From 833eaa450ff1aa79c6be5f1e4fb98c40d72eb92f Mon Sep 17 00:00:00 2001 From: CPerezz Date: Tue, 10 Nov 2020 09:27:19 +0100 Subject: [PATCH 01/11] init --- Cargo.toml | 9 +++++++-- src/cipher/cipher.rs | 9 +++++---- src/cipher/error.rs | 1 + src/cipher/mod.rs | 1 + src/cipher/tests.rs | 1 + src/cipher/zk.rs | 1 + src/lib.rs | 5 ++++- src/tree/mod.rs | 1 + 8 files changed, 21 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6d547d3..908218e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,8 +8,10 @@ edition = "2018" [dependencies] lazy_static = "1.3.0" -hades252 = { git = "https://github.com/dusk-network/hades252", tag = "v0.10.0" } -dusk-plonk = {version = "0.3.3", features = ["trace-print"]} +hades252 = { git = "https://github.com/dusk-network/hades252", tag = "v0.10.1", default-features = false } +dusk-plonk = {version = "0.3", default-features = false} +dusk-bls12_381 = {version = "0.3", default-features = false} +dusk-jubjub = {version = "0.5", default-features = false} anyhow = "1.0" thiserror = "1.0" canonical = {version = "0.4", optional = true} @@ -24,9 +26,12 @@ rand_core = "0.5" criterion = "0.3" [features] +default = ["std"] +std = ["hades252/default", "dusk-bls12_381/default", "dusk-jubjub/std"] canon = ["canonical", "canonical_derive", "nstack", "microkelvin", "dusk-plonk/canon"] canon_host = ["canon", "canonical/host"] + [profile.dev] opt-level = 3 debug = false diff --git a/src/cipher/cipher.rs b/src/cipher/cipher.rs index 9ec9f5b..9bf53df 100644 --- a/src/cipher/cipher.rs +++ b/src/cipher/cipher.rs @@ -8,7 +8,7 @@ use canonical::Canon; #[cfg(feature = "canon")] use canonical_derive::Canon; -use dusk_plonk::jubjub::AffinePoint; +use dusk_jubjub::JubJubAffine; use dusk_plonk::prelude::*; use hades252::{ScalarStrategy, Strategy, WIDTH}; @@ -96,6 +96,7 @@ pub struct PoseidonCipher { cipher: [BlsScalar; CIPHER_SIZE], } +#[cfg(feature = "std")] impl PoseidonCipher { /// [`PoseidonCipher`] constructor pub fn new(cipher: [BlsScalar; CIPHER_SIZE]) -> Self { @@ -155,7 +156,7 @@ impl PoseidonCipher { /// The message size will be truncated to [`MESSAGE_CAPACITY`] bits pub fn encrypt( message: &[BlsScalar], - secret: &AffinePoint, + secret: &JubJubAffine, nonce: &BlsScalar, ) -> Self { let zero = BlsScalar::zero(); @@ -187,7 +188,7 @@ impl PoseidonCipher { /// Will return `None` if the decryption fails. pub fn decrypt( &self, - secret: &AffinePoint, + secret: &JubJubAffine, nonce: &BlsScalar, ) -> Result<[BlsScalar; MESSAGE_CAPACITY], CipherError> { let zero = BlsScalar::zero(); @@ -219,7 +220,7 @@ impl PoseidonCipher { /// Returns the initial state of the encryption pub fn initial_state( - secret: &AffinePoint, + secret: &JubJubAffine, nonce: BlsScalar, ) -> [BlsScalar; WIDTH] { [ diff --git a/src/cipher/error.rs b/src/cipher/error.rs index 9637264..bffeca8 100644 --- a/src/cipher/error.rs +++ b/src/cipher/error.rs @@ -4,6 +4,7 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. +#![cfg(feature = "std")] use thiserror::Error; /// Error definitions for the decription process diff --git a/src/cipher/mod.rs b/src/cipher/mod.rs index 1cbad7c..7b0d769 100644 --- a/src/cipher/mod.rs +++ b/src/cipher/mod.rs @@ -4,6 +4,7 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. +#![cfg(feature = "std")] pub use cipher::PoseidonCipher; pub use error::CipherError; diff --git a/src/cipher/tests.rs b/src/cipher/tests.rs index f5d92a7..7798222 100644 --- a/src/cipher/tests.rs +++ b/src/cipher/tests.rs @@ -4,6 +4,7 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. +#![cfg(feature = "std")] use super::{PoseidonCipher, CIPHER_SIZE, MESSAGE_CAPACITY}; use anyhow::Result; use dusk_plonk::jubjub::{AffinePoint, Fr, GENERATOR}; diff --git a/src/cipher/zk.rs b/src/cipher/zk.rs index 6ce3078..8764210 100644 --- a/src/cipher/zk.rs +++ b/src/cipher/zk.rs @@ -4,6 +4,7 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. +#![cfg(feature = "std")] use super::{PoseidonCipher, CIPHER_SIZE, MESSAGE_CAPACITY}; use dusk_plonk::constraint_system::ecc::Point; use dusk_plonk::prelude::*; diff --git a/src/lib.rs b/src/lib.rs index c6898f4..65cb3fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,13 +8,16 @@ #![feature(min_const_generics)] #![feature(external_doc)] #![doc(include = "../README.md")] +#![cfg_attr(not(feature = "std"), no_std)] /// Encryption and decryption implementation over a Poseidon cipher pub mod cipher; +#[cfg(feature = "std")] /// Module containing a fixed-length Poseidon hash implementation pub mod perm_uses; +#[cfg(feature = "std")] /// Reference implementation for the Poseidon Sponge hash function pub mod sponge; /// The module handling posedion-trees. -#[cfg(feature = "canon")] +#[cfg(all(feature = "canon", feature = "std"))] pub mod tree; diff --git a/src/tree/mod.rs b/src/tree/mod.rs index 4e48c94..4d11b92 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -87,6 +87,7 @@ where } } +#[cfg(feature = "std")] impl PoseidonTree where L: PoseidonLeaf, From bd178490b836359492b846cd5d8807c29cc11738 Mon Sep 17 00:00:00 2001 From: CPerezz Date: Tue, 10 Nov 2020 09:48:28 +0100 Subject: [PATCH 02/11] add no_std_min --- Cargo.toml | 2 +- src/cipher/cipher.rs | 1 + src/cipher/mod.rs | 4 +++- src/lib.rs | 2 +- src/tree/mod.rs | 4 ++++ 5 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 908218e..a1ac5f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ criterion = "0.3" [features] default = ["std"] std = ["hades252/default", "dusk-bls12_381/default", "dusk-jubjub/std"] -canon = ["canonical", "canonical_derive", "nstack", "microkelvin", "dusk-plonk/canon"] +canon = ["canonical", "canonical_derive", "nstack", "microkelvin", "dusk-bls12_381/canon", "dusk-jubjub/canon"] canon_host = ["canon", "canonical/host"] diff --git a/src/cipher/cipher.rs b/src/cipher/cipher.rs index 9bf53df..cde8678 100644 --- a/src/cipher/cipher.rs +++ b/src/cipher/cipher.rs @@ -16,6 +16,7 @@ use super::{ CIPHER_BYTES_SIZE, CIPHER_SIZE, ENCRYPTED_DATA_SIZE, MESSAGE_CAPACITY, }; +#[cfg(feature = "std")] pub use super::CipherError; /// ```ignore diff --git a/src/cipher/mod.rs b/src/cipher/mod.rs index 7b0d769..0dddad8 100644 --- a/src/cipher/mod.rs +++ b/src/cipher/mod.rs @@ -4,8 +4,8 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -#![cfg(feature = "std")] pub use cipher::PoseidonCipher; +#[cfg(feature = "std")] pub use error::CipherError; /// Maximum number of scalars allowed per message @@ -26,11 +26,13 @@ pub const ENCRYPTED_DATA_SIZE: usize = CIPHER_SIZE * 32; /// [`PoseidonCipher`] definition pub mod cipher; +#[cfg(feature = "std")] /// Error definition for the cipher generation process pub mod error; #[cfg(test)] mod tests; +#[cfg(feature = "std")] /// Plonk gadget for Poseidon encryption pub mod zk; diff --git a/src/lib.rs b/src/lib.rs index 65cb3fd..cbe7007 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,5 +19,5 @@ pub mod perm_uses; /// Reference implementation for the Poseidon Sponge hash function pub mod sponge; /// The module handling posedion-trees. -#[cfg(all(feature = "canon", feature = "std"))] +#[cfg(feature = "canon")] pub mod tree; diff --git a/src/tree/mod.rs b/src/tree/mod.rs index 4d11b92..8400cf6 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -20,6 +20,7 @@ pub use branch::{PoseidonBranch, PoseidonLevel}; mod annotation; mod branch; +#[cfg(feature = "std")] /// Zero-Knowledge implementations for the poseidon tree pub mod zk; @@ -171,6 +172,7 @@ where } } +#[cfg(feature = "std")] /// Main iterator of the poseidon tree. /// /// Depends on an implementation of `PoseidonWalkableAnnotation` for the tree annotation @@ -192,6 +194,7 @@ where data: D, } +#[cfg(feature = "std")] impl PoseidonTreeIterator where L: PoseidonLeaf, @@ -217,6 +220,7 @@ where } } +#[cfg(feature = "std")] impl Iterator for PoseidonTreeIterator where From e535e9d2418782cbe6638200d47a16dbce44ae6b Mon Sep 17 00:00:00 2001 From: CPerezz Date: Tue, 10 Nov 2020 10:58:38 +0100 Subject: [PATCH 03/11] Enable new --- src/tree/mod.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/tree/mod.rs b/src/tree/mod.rs index 8400cf6..87804f5 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -88,7 +88,6 @@ where } } -#[cfg(feature = "std")] impl PoseidonTree where L: PoseidonLeaf, @@ -101,7 +100,7 @@ where Self { inner } } - + #[cfg(feature = "std")] /// Append a leaf to the tree. Return the index of the appended leaf. /// /// Will call the `tree_pos_mut` implementation of the leaf to @@ -125,14 +124,14 @@ where Ok(size) } - + #[cfg(feature = "std")] /// Fetch, remove and return the last inserted leaf, if present. pub fn pop(&mut self) -> Result> { self.inner .pop() .map_err(|e| anyhow!("Error pop from the tree: {:?}", e)) } - + #[cfg(feature = "std")] /// Fetch a leaf on a provided index. pub fn get(&self, n: usize) -> Result> { self.inner @@ -142,7 +141,7 @@ where anyhow!("Error fetching the Nth item from the tree: {:?}", e) }) } - + #[cfg(feature = "std")] /// Return a full merkle opening for this poseidon tree for a given index. pub fn branch(&self, n: usize) -> Result>> { let branch = self.inner.nth::(n as u64).map_err(|e| { @@ -154,12 +153,12 @@ where None => Ok(None), } } - + #[cfg(feature = "std")] /// Return the current root/state of the tree. pub fn root(&self) -> Result { self.branch(0).map(|b| b.unwrap_or_default().root()) } - + #[cfg(feature = "std")] /// Iterates over the tree, provided its annotation implements [`PoseidonWalkableAnnotation`] pub fn iter_walk( &self, From 2ad55a6971beb6c202e82e3c6cc6519c696b171c Mon Sep 17 00:00:00 2001 From: CPerezz Date: Thu, 12 Nov 2020 16:16:37 +0100 Subject: [PATCH 04/11] Custom canonical version --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a1ac5f1..1a97795 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,13 +14,13 @@ dusk-bls12_381 = {version = "0.3", default-features = false} dusk-jubjub = {version = "0.5", default-features = false} anyhow = "1.0" thiserror = "1.0" -canonical = {version = "0.4", optional = true} -canonical_derive = {version = "0.4", optional = true} +canonical = {git = "https://github.com/dusk-network/canonical", branch = "hashing_resolv", optional = true} +canonical_derive = {git = "https://github.com/dusk-network/canonical", branch = "hashing_resolv", optional = true} microkelvin = {version = "0.5", optional = true} nstack = {version = "0.6", optional = true} [dev-dependencies] -canonical_host = "0.4" +canonical_host = {git = "https://github.com/dusk-network/canonical", branch = "hashing_resolv"} rand = "0.7" rand_core = "0.5" criterion = "0.3" From 8f64bf3446d19d28dea5094415d5ba0e37b52c09 Mon Sep 17 00:00:00 2001 From: CPerezz Date: Thu, 12 Nov 2020 16:57:14 +0100 Subject: [PATCH 05/11] Update --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1a97795..85a030f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,13 +14,13 @@ dusk-bls12_381 = {version = "0.3", default-features = false} dusk-jubjub = {version = "0.5", default-features = false} anyhow = "1.0" thiserror = "1.0" -canonical = {git = "https://github.com/dusk-network/canonical", branch = "hashing_resolv", optional = true} -canonical_derive = {git = "https://github.com/dusk-network/canonical", branch = "hashing_resolv", optional = true} +canonical = {version = "0.4", optional = true} +canonical_derive = {version = "0.4", optional = true} microkelvin = {version = "0.5", optional = true} nstack = {version = "0.6", optional = true} [dev-dependencies] -canonical_host = {git = "https://github.com/dusk-network/canonical", branch = "hashing_resolv"} +canonical_host = {version = "0.4"} rand = "0.7" rand_core = "0.5" criterion = "0.3" From e5462debbe3d26224af9138c0d7eec70b7e1d0a2 Mon Sep 17 00:00:00 2001 From: CPerezz Date: Fri, 13 Nov 2020 13:49:07 +0100 Subject: [PATCH 06/11] Update CI --- .github/workflows/dusk_ci.yml | 65 +++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/.github/workflows/dusk_ci.yml b/.github/workflows/dusk_ci.yml index 0fc30b9..096e3af 100644 --- a/.github/workflows/dusk_ci.yml +++ b/.github/workflows/dusk_ci.yml @@ -35,8 +35,8 @@ jobs: with: command: check - test_nightly: - name: Nightly tests + test_nightly_std: + name: Nightly tests std runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -49,9 +49,22 @@ jobs: with: command: test args: --release - - test_nightly_canon: - name: Nightly tests + test_nightly_no_std: + name: Nightly tests no_std + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + override: true + - uses: actions-rs/cargo@v1 + with: + command: test + args: --release --no-default-features + test_nightly_canon_std: + name: Nightly tests canon std runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -64,9 +77,43 @@ jobs: with: command: test args: --release --features canon + + test_nightly_canon_no_std: + name: Nightly tests canon no_std + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + override: true + - uses: actions-rs/cargo@v1 + with: + command: test + args: --release --no-default-features --features canon + + test_nightly_canon_host_std: + name: Nightly tests canon_host std + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + override: true + - uses: actions-rs/cargo@v1 + with: + command: test + args: --release --features canon_host + - uses: actions-rs/cargo@v1 + with: + comand: test + args: --release --features canon_host - test_nightly_canon_host: - name: Nightly tests + test_nightly_canon_host_no_std: + name: Nightly tests canon_host no_std runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -79,6 +126,10 @@ jobs: with: command: test args: --release --features canon_host + - uses: actions-rs/cargo@v1 + with: + comand: test + args: --release --no-default-features --features canon_host fmt: name: Rustfmt From e1a93e9596960f6c0cb8b9c3e624dfa6863194cd Mon Sep 17 00:00:00 2001 From: Victor Lopez Date: Tue, 17 Nov 2020 02:23:51 +0100 Subject: [PATCH 07/11] No-std compatibility --- Cargo.toml | 38 +++-- README.md | 2 +- src/cipher/cipher.rs | 167 ++++++--------------- src/cipher/error.rs | 16 -- src/cipher/mod.rs | 32 +--- src/cipher/tests.rs | 46 +++--- src/cipher/zk.rs | 60 +++++--- src/lib.rs | 4 +- src/tree/annotation/max.rs | 2 +- src/tree/annotation/mod.rs | 4 +- src/tree/annotation/poseidon.rs | 21 ++- src/tree/branch.rs | 2 +- src/tree/hash.rs | 174 ++++++++++++++++++++++ src/tree/leaf.rs | 25 ++++ src/tree/mod.rs | 251 ++------------------------------ src/tree/tests.rs | 90 +++--------- src/tree/tree.rs | 223 ++++++++++++++++++++++++++++ src/tree/zk.rs | 65 +++++++++ 18 files changed, 676 insertions(+), 546 deletions(-) delete mode 100644 src/cipher/error.rs create mode 100644 src/tree/hash.rs create mode 100644 src/tree/leaf.rs create mode 100644 src/tree/tree.rs diff --git a/Cargo.toml b/Cargo.toml index 071618d..bd61be6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,31 +7,45 @@ authors = [ edition = "2018" [dependencies] -lazy_static = "1.3.0" -hades252 = { git = "https://github.com/dusk-network/hades252", tag = "v0.10.1", default-features = false } -dusk-plonk = {version = "0.3", default-features = false} dusk-bls12_381 = {version = "0.3", default-features = false} dusk-jubjub = {version = "0.5", default-features = false} - -anyhow = "1.0" -thiserror = "1.0" +hades252 = { git = "https://github.com/dusk-network/hades252", tag = "v0.10.1", default-features = false } canonical = {version = "0.4", optional = true} canonical_derive = {version = "0.4", optional = true} microkelvin = {version = "0.5", optional = true} nstack = {version = "0.6", optional = true} +dusk-plonk = {version = "0.3", default-features = false, optional = true} +anyhow = { version = "1.0", optional = true } +thiserror = { version = "1.0", optional = true } + [dev-dependencies] -canonical_host = {version = "0.4"} +canonical_host = "0.4" rand = "0.7" -rand_core = "0.5" criterion = "0.3" [features] default = ["std"] -std = ["hades252/default", "dusk-bls12_381/default", "dusk-jubjub/std"] -canon = ["canonical", "canonical_derive", "nstack", "microkelvin", "dusk-bls12_381/canon", "dusk-jubjub/canon"] -canon_host = ["canon", "canonical/host"] - +std = [ + "hades252/default", + "dusk-bls12_381/default", + "dusk-jubjub/std", + "dusk-plonk", + "anyhow", + "thiserror" +] +canon = [ + "dusk-bls12_381/canon", + "dusk-jubjub/canon", + "canonical", + "canonical_derive", + "microkelvin", + "nstack" +] +canon_host = [ + "canon", + "canonical/host" +] [profile.dev] opt-level = 3 diff --git a/README.md b/README.md index d41c61c..6090cd8 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ computed and placed in the first Level position. ### Zero Knowledge Merkle Opening Proof example: ```no_run -#[cfg(feature = "canon")] +#[cfg(all(feature = "canon", feature = "std"))] { use anyhow::Result; diff --git a/src/cipher/cipher.rs b/src/cipher/cipher.rs index cde8678..85c2219 100644 --- a/src/cipher/cipher.rs +++ b/src/cipher/cipher.rs @@ -8,99 +8,25 @@ use canonical::Canon; #[cfg(feature = "canon")] use canonical_derive::Canon; -use dusk_jubjub::JubJubAffine; -use dusk_plonk::prelude::*; -use hades252::{ScalarStrategy, Strategy, WIDTH}; -use super::{ - CIPHER_BYTES_SIZE, CIPHER_SIZE, ENCRYPTED_DATA_SIZE, MESSAGE_CAPACITY, -}; +use dusk_bls12_381::BlsScalar; +use dusk_jubjub::JubJubAffine; +use hades252::strategies::{ScalarStrategy, Strategy}; -#[cfg(feature = "std")] -pub use super::CipherError; +const MESSAGE_CAPACITY: usize = 2; +const CIPHER_SIZE: usize = MESSAGE_CAPACITY + 1; +const CIPHER_BYTES_SIZE: usize = CIPHER_SIZE * 32; -/// ```ignore -/// Encapsulates an encrypted data -/// -/// This implementation is optimized for a message containing 2 scalars -/// -/// # Examples -/// use dusk_plonk::jubjub::{dhke, ExtendedPoint, GENERATOR}; -/// use dusk_plonk::prelude::*; -/// use poseidon252::cipher::{PoseidonCipher, MESSAGE_CAPACITY}; -/// -/// use std::ops::Mul; -/// -/// fn main() { -/// let mut rng = rand::thread_rng(); -/// -/// // Generate a secret and a public key for Bob -/// let bob_secret = JubJubScalar::random(&mut rng); -/// let bob_public = GENERATOR.to_niels().mul(&bob_secret); -/// -/// // Generate a secret and a public key for Alice -/// let alice_secret = JubJubScalar::random(&mut rng); -/// let alice_public = GENERATOR.to_niels().mul(&alice_secret); -/// -/// // Generate a secret message -/// let a = BlsScalar::random(&mut rng); -/// let b = BlsScalar::random(&mut rng); -/// let message = [a, b]; -/// -/// // Bob's view (sender) -/// // The cipher and nonce are safe to be broadcasted publicly -/// let (cipher, nonce) = sender(&bob_secret, &alice_public, &message); -/// -/// // Alice's view (receiver) -/// let decrypted_message = -/// receiver(&alice_secret, &bob_public, &cipher, &nonce); -/// -/// // Successful communication -/// assert_eq!(decrypted_message, message); -/// } -/// -/// fn sender( -/// sender_secret: &JubJubScalar, -/// receiver_public: &ExtendedPoint, -/// message: &[BlsScalar], -/// ) -> (PoseidonCipher, BlsScalar) { -/// // Use the Diffie-Hellman protocol to generate a shared secret -/// let shared_secret = dhke(sender_secret, receiver_public); -/// -/// // Generate a random nonce that will be public -/// let nonce = BlsScalar::random(&mut rand::thread_rng()); -/// -/// // Encrypt the message -/// let cipher = PoseidonCipher::encrypt(&message, &shared_secret, &nonce); -/// -/// (cipher, nonce) -/// } -/// -/// fn receiver( -/// receiver_secret: &JubJubScalar, -/// sender_public: &ExtendedPoint, -/// cipher: &PoseidonCipher, -/// nonce: &BlsScalar, -/// ) -> [BlsScalar; MESSAGE_CAPACITY] { -/// // Use the Diffie-Hellman protocol to generate a shared secret -/// let shared_secret = dhke(receiver_secret, sender_public); -/// -/// // Decrypt the message -/// cipher -/// .decrypt(&shared_secret, &nonce) -/// .expect("Failed to decrypt!") -/// } -/// ``` #[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Default)] #[cfg_attr(feature = "canon", derive(Canon))] +/// Encapsulates an encrypted data pub struct PoseidonCipher { cipher: [BlsScalar; CIPHER_SIZE], } -#[cfg(feature = "std")] impl PoseidonCipher { /// [`PoseidonCipher`] constructor - pub fn new(cipher: [BlsScalar; CIPHER_SIZE]) -> Self { + pub const fn new(cipher: [BlsScalar; CIPHER_SIZE]) -> Self { Self { cipher } } @@ -143,18 +69,44 @@ impl PoseidonCipher { } /// Maximum number of scalars allowed per message - pub fn capacity() -> usize { + pub const fn capacity() -> usize { MESSAGE_CAPACITY } - /// Bytes consumed on serialization of the poseidon cipher - pub const fn serialized_size() -> usize { - ENCRYPTED_DATA_SIZE + /// Number of scalars used in a cipher + pub const fn cipher_size() -> usize { + CIPHER_SIZE + } + + /// Number of bytes used by from/to bytes `PoseidonCipher` function + pub const fn cipher_size_bytes() -> usize { + CIPHER_BYTES_SIZE + } + + /// Returns the initial state of the encryption + pub fn initial_state( + secret: &JubJubAffine, + nonce: BlsScalar, + ) -> [BlsScalar; hades252::WIDTH] { + [ + // Domain - Maximum plaintext length of the elements of Fq, as defined in the paper + BlsScalar::from_raw([0x100000000u64, 0, 0, 0]), + // The size of the message is constant because any absent input is replaced by zero + BlsScalar::from_raw([MESSAGE_CAPACITY as u64, 0, 0, 0]), + secret.get_x(), + secret.get_y(), + nonce, + ] + } + + /// Getter for the cipher + pub const fn cipher(&self) -> &[BlsScalar; CIPHER_SIZE] { + &self.cipher } /// Encrypt a slice of scalars into an internal cipher representation /// - /// The message size will be truncated to [`MESSAGE_CAPACITY`] bits + /// The message size will be truncated to [`PoseidonCipher::capacity()`] bits pub fn encrypt( message: &[BlsScalar], secret: &JubJubAffine, @@ -191,7 +143,7 @@ impl PoseidonCipher { &self, secret: &JubJubAffine, nonce: &BlsScalar, - ) -> Result<[BlsScalar; MESSAGE_CAPACITY], CipherError> { + ) -> Result<[BlsScalar; MESSAGE_CAPACITY], &'static str> { let zero = BlsScalar::zero(); let mut strategy = ScalarStrategy::new(); @@ -208,46 +160,9 @@ impl PoseidonCipher { strategy.perm(&mut state); if self.cipher[MESSAGE_CAPACITY] != state[1] { - return Err(CipherError::FailedDecrypt); + return Err("Decryption failed for the provided secret+nonce"); } Ok(message) } - - /// Getter for the cipher - pub fn cipher(&self) -> &[BlsScalar; CIPHER_SIZE] { - &self.cipher - } - - /// Returns the initial state of the encryption - pub fn initial_state( - secret: &JubJubAffine, - nonce: BlsScalar, - ) -> [BlsScalar; WIDTH] { - [ - // Domain - Maximum plaintext length of the elements of Fq, as defined in the paper - BlsScalar::from_raw([0x100000000u64, 0, 0, 0]), - // The size of the message is constant because any absent input is replaced by zero - BlsScalar::from_raw([MESSAGE_CAPACITY as u64, 0, 0, 0]), - secret.get_x(), - secret.get_y(), - nonce, - ] - } - - /// Returns the initial state of the encryption within a composer circuit - pub fn initial_state_circuit( - composer: &mut StandardComposer, - ks0: Variable, - ks1: Variable, - nonce: Variable, - ) -> [Variable; WIDTH] { - let domain = BlsScalar::from_raw([0x100000000u64, 0, 0, 0]); - let domain = composer.add_witness_to_circuit_description(domain); - - let length = BlsScalar::from_raw([MESSAGE_CAPACITY as u64, 0, 0, 0]); - let length = composer.add_witness_to_circuit_description(length); - - [domain, length, ks0, ks1, nonce] - } } diff --git a/src/cipher/error.rs b/src/cipher/error.rs deleted file mode 100644 index bffeca8..0000000 --- a/src/cipher/error.rs +++ /dev/null @@ -1,16 +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. - -#![cfg(feature = "std")] -use thiserror::Error; - -/// Error definitions for the decription process -#[derive(Error, Debug)] -pub enum CipherError { - /// Error spawned when there is an inconsistency on the decryption - #[error("Decription failed for the provided secret+nonce")] - FailedDecrypt, -} diff --git a/src/cipher/mod.rs b/src/cipher/mod.rs index 0dddad8..8e34c72 100644 --- a/src/cipher/mod.rs +++ b/src/cipher/mod.rs @@ -4,35 +4,15 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -pub use cipher::PoseidonCipher; -#[cfg(feature = "std")] -pub use error::CipherError; - -/// Maximum number of scalars allowed per message -pub const MESSAGE_CAPACITY: usize = 2; - -/// Number of scalars used in a cipher -pub const CIPHER_SIZE: usize = MESSAGE_CAPACITY + 1; - -/// Number of bytes used by from/to bytes `PoseidonCipher` function -pub const CIPHER_BYTES_SIZE: usize = CIPHER_SIZE * 32; - -/// Bytes consumed on serialization of the poseidon cipher -/// -/// This is kept for backwards compatibility since the constant definition is -/// redundant to [`CIPHER_BYTES_SIZE`] -pub const ENCRYPTED_DATA_SIZE: usize = CIPHER_SIZE * 32; +mod cipher; -/// [`PoseidonCipher`] definition -pub mod cipher; +#[cfg(test)] +mod tests; #[cfg(feature = "std")] -/// Error definition for the cipher generation process -pub mod error; +mod zk; -#[cfg(test)] -mod tests; +pub use cipher::PoseidonCipher; #[cfg(feature = "std")] -/// Plonk gadget for Poseidon encryption -pub mod zk; +pub use zk::{poseidon_cipher_decrypt, poseidon_cipher_encrypt}; diff --git a/src/cipher/tests.rs b/src/cipher/tests.rs index 49d6042..d537364 100644 --- a/src/cipher/tests.rs +++ b/src/cipher/tests.rs @@ -4,28 +4,27 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -#![cfg(feature = "std")] -use super::{PoseidonCipher, CIPHER_SIZE, MESSAGE_CAPACITY}; -use anyhow::Result; -use dusk_plonk::jubjub::{ - JubJubAffine as AffinePoint, JubJubScalar as Fr, GENERATOR, -}; -use dusk_plonk::prelude::*; -use hades252::WIDTH; +use crate::cipher::PoseidonCipher; +use core::ops::Mul; +use dusk_bls12_381::BlsScalar; +use dusk_jubjub::{JubJubAffine, JubJubScalar, GENERATOR}; use rand::RngCore; -use std::ops::Mul; -fn gen() -> ([BlsScalar; MESSAGE_CAPACITY], AffinePoint, BlsScalar) { +fn gen() -> ( + [BlsScalar; PoseidonCipher::capacity()], + JubJubAffine, + BlsScalar, +) { let mut rng = rand::thread_rng(); - let mut message = [BlsScalar::zero(); MESSAGE_CAPACITY]; + let mut message = [BlsScalar::zero(); PoseidonCipher::capacity()]; message .iter_mut() .for_each(|m| *m = BlsScalar::random(&mut rng)); let mut secret = [0u8; 64]; rng.fill_bytes(&mut secret); - let secret = Fr::from_bytes_wide(&secret); + let secret = JubJubScalar::from_bytes_wide(&secret); let secret = GENERATOR.to_niels().mul(&secret).into(); let nonce = BlsScalar::random(&mut rng); @@ -36,17 +35,20 @@ fn gen() -> ([BlsScalar; MESSAGE_CAPACITY], AffinePoint, BlsScalar) { #[test] fn sanity() { // The secret is always a pair with nonce, so the message capacity should be at least 2 - assert!(MESSAGE_CAPACITY > 1); + assert!(PoseidonCipher::capacity() > 1); // The cipher size only makes sense to be `capacity + 1` - assert_eq!(CIPHER_SIZE, MESSAGE_CAPACITY + 1); + assert_eq!( + PoseidonCipher::cipher_size(), + PoseidonCipher::capacity() + 1 + ); // The hades permutation cannot be performed if the cipher is bigger than hades width - assert!(WIDTH >= CIPHER_SIZE); + assert!(hades252::WIDTH >= PoseidonCipher::cipher_size()); } #[test] -fn encrypt() -> Result<()> { +fn encrypt() -> Result<(), &'static str> { let (message, secret, nonce) = gen(); let cipher = PoseidonCipher::encrypt(&message, &secret, &nonce); @@ -58,7 +60,7 @@ fn encrypt() -> Result<()> { } #[test] -fn single_bit() -> Result<()> { +fn single_bit() -> Result<(), &'static str> { let (_, secret, nonce) = gen(); let message = BlsScalar::random(&mut rand::thread_rng()); @@ -71,15 +73,15 @@ fn single_bit() -> Result<()> { } #[test] -fn overflow() -> Result<()> { +fn overflow() -> Result<(), &'static str> { let (_, secret, nonce) = gen(); - let message = - [BlsScalar::random(&mut rand::thread_rng()); MESSAGE_CAPACITY + 1]; + let message = [BlsScalar::random(&mut rand::thread_rng()); + PoseidonCipher::capacity() + 1]; let cipher = PoseidonCipher::encrypt(&message, &secret, &nonce); let decrypt = cipher.decrypt(&secret, &nonce)?; - assert_eq!(message[0..MESSAGE_CAPACITY], decrypt); + assert_eq!(message[0..PoseidonCipher::capacity()], decrypt); Ok(()) } @@ -94,7 +96,7 @@ fn wrong_key_fail() { } #[test] -fn bytes() -> Result<()> { +fn bytes() -> Result<(), &'static str> { let (message, secret, nonce) = gen(); let cipher = PoseidonCipher::encrypt(&message, &secret, &nonce); diff --git a/src/cipher/zk.rs b/src/cipher/zk.rs index fd0406e..7151f1d 100644 --- a/src/cipher/zk.rs +++ b/src/cipher/zk.rs @@ -4,11 +4,30 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -#![cfg(feature = "std")] -use super::{PoseidonCipher, CIPHER_SIZE, MESSAGE_CAPACITY}; +use crate::cipher::PoseidonCipher; +use dusk_bls12_381::BlsScalar; use dusk_plonk::constraint_system::ecc::Point; use dusk_plonk::prelude::*; -use hades252::{GadgetStrategy, Strategy}; +use hades252::strategies::{GadgetStrategy, Strategy}; + +impl PoseidonCipher { + /// Returns the initial state of the encryption within a composer circuit + pub fn initial_state_circuit( + composer: &mut StandardComposer, + ks0: Variable, + ks1: Variable, + nonce: Variable, + ) -> [Variable; hades252::WIDTH] { + let domain = BlsScalar::from_raw([0x100000000u64, 0, 0, 0]); + let domain = composer.add_witness_to_circuit_description(domain); + + let length = + BlsScalar::from_raw([PoseidonCipher::capacity() as u64, 0, 0, 0]); + let length = composer.add_witness_to_circuit_description(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. @@ -19,19 +38,19 @@ pub fn poseidon_cipher_encrypt( shared_secret: &Point, nonce: Variable, message: &[Variable], -) -> [Variable; CIPHER_SIZE] { +) -> [Variable; PoseidonCipher::cipher_size()] { let zero = composer.add_witness_to_circuit_description(BlsScalar::zero()); let ks0 = *shared_secret.x(); let ks1 = *shared_secret.y(); - let mut cipher = [zero; CIPHER_SIZE]; + let mut cipher = [zero; PoseidonCipher::cipher_size()]; let mut state = PoseidonCipher::initial_state_circuit(composer, ks0, ks1, nonce); GadgetStrategy::new(composer).perm(&mut state); - (0..MESSAGE_CAPACITY).for_each(|i| { + (0..PoseidonCipher::capacity()).for_each(|i| { let x = if i < message.len() { message[i] } else { zero }; state[i + 1] = composer.add( @@ -45,7 +64,7 @@ pub fn poseidon_cipher_encrypt( }); GadgetStrategy::new(composer).perm(&mut state); - cipher[MESSAGE_CAPACITY] = state[1]; + cipher[PoseidonCipher::capacity()] = state[1]; cipher } @@ -59,19 +78,19 @@ pub fn poseidon_cipher_decrypt( shared_secret: &Point, nonce: Variable, cipher: &[Variable], -) -> [Variable; MESSAGE_CAPACITY] { +) -> [Variable; PoseidonCipher::capacity()] { let zero = composer.add_witness_to_circuit_description(BlsScalar::zero()); let ks0 = *shared_secret.x(); let ks1 = *shared_secret.y(); - let mut message = [zero; MESSAGE_CAPACITY]; + let mut message = [zero; PoseidonCipher::capacity()]; let mut state = PoseidonCipher::initial_state_circuit(composer, ks0, ks1, nonce); GadgetStrategy::new(composer).perm(&mut state); - (0..MESSAGE_CAPACITY).for_each(|i| { + (0..PoseidonCipher::capacity()).for_each(|i| { message[i] = composer.add( (BlsScalar::one(), cipher[i]), (-BlsScalar::one(), state[i + 1]), @@ -84,19 +103,22 @@ pub fn poseidon_cipher_decrypt( GadgetStrategy::new(composer).perm(&mut state); - composer.assert_equal(cipher[MESSAGE_CAPACITY], state[1]); + composer.assert_equal(cipher[PoseidonCipher::capacity()], state[1]); message } #[cfg(test)] mod tests { - use super::*; + use crate::cipher::{ + poseidon_cipher_decrypt, poseidon_cipher_encrypt, PoseidonCipher, + }; use anyhow::Result; + use dusk_bls12_381::BlsScalar; + use dusk_jubjub::{dhke, JubJubExtended, GENERATOR_EXTENDED}; use dusk_plonk::constraint_system::ecc::scalar_mul::variable_base::variable_base_scalar_mul; - use dusk_plonk::jubjub::{ - dhke, JubJubExtended as ExtendedPoint, GENERATOR_EXTENDED, - }; + use dusk_plonk::constraint_system::ecc::Point; + use dusk_plonk::prelude::*; #[test] fn gadget() -> Result<()> { @@ -121,7 +143,7 @@ mod tests { let nonce = BlsScalar::random(&mut rng); let cipher = PoseidonCipher::encrypt(&message, &shared_secret, &nonce); - let size = 14; + let size = 13; let pp = PublicParameters::setup(1 << size, &mut rng)?; let (ck, vk) = pp.trim(1 << size)?; @@ -129,7 +151,7 @@ mod tests { let circuit = |composer: &mut StandardComposer, secret: JubJubScalar, - public: ExtendedPoint, + public: JubJubExtended, nonce: BlsScalar, message: &[BlsScalar], cipher: &[BlsScalar]| { @@ -142,7 +164,7 @@ mod tests { let shared = variable_base_scalar_mul(composer, secret, public); - let mut message_circuit = [zero; MESSAGE_CAPACITY]; + let mut message_circuit = [zero; PoseidonCipher::capacity()]; message.iter().zip(message_circuit.iter_mut()).for_each( |(m, v)| { *v = composer.add_input(*m); @@ -211,7 +233,7 @@ mod tests { JubJubScalar::zero(), GENERATOR_EXTENDED, nonce, - &[BlsScalar::zero(); MESSAGE_CAPACITY], + &[BlsScalar::zero(); PoseidonCipher::capacity()], cipher.cipher(), ); verifier.preprocess(&ck)?; diff --git a/src/lib.rs b/src/lib.rs index cbe7007..955df45 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,11 +4,11 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -#![deny(missing_docs)] +#![cfg_attr(not(feature = "std"), no_std)] #![feature(min_const_generics)] #![feature(external_doc)] #![doc(include = "../README.md")] -#![cfg_attr(not(feature = "std"), no_std)] +#![deny(missing_docs)] /// Encryption and decryption implementation over a Poseidon cipher pub mod cipher; diff --git a/src/tree/annotation/max.rs b/src/tree/annotation/max.rs index 915f91a..d738196 100644 --- a/src/tree/annotation/max.rs +++ b/src/tree/annotation/max.rs @@ -11,7 +11,7 @@ use crate::tree::PoseidonLeaf; use canonical::{Canon, Store}; use canonical_derive::Canon; use core::borrow::Borrow; -use dusk_plonk::prelude::*; +use dusk_bls12_381::BlsScalar; use microkelvin::{Annotation, Cardinality, Compound, Max, Step, Walk}; use nstack::NStack; diff --git a/src/tree/annotation/mod.rs b/src/tree/annotation/mod.rs index 07b25ea..c908442 100644 --- a/src/tree/annotation/mod.rs +++ b/src/tree/annotation/mod.rs @@ -4,10 +4,10 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -use super::PoseidonLeaf; +use crate::tree::PoseidonLeaf; use canonical::{Canon, Store}; use core::borrow::Borrow; -use dusk_plonk::prelude::*; +use dusk_bls12_381::BlsScalar; use microkelvin::{Annotation, Cardinality, Compound, Step, Walk}; use nstack::NStack; diff --git a/src/tree/annotation/poseidon.rs b/src/tree/annotation/poseidon.rs index 91e951d..13cc35d 100644 --- a/src/tree/annotation/poseidon.rs +++ b/src/tree/annotation/poseidon.rs @@ -5,12 +5,12 @@ // Copyright (c) DUSK NETWORK. All rights reserved. use super::PoseidonTreeAnnotation; +use crate::tree::hash; use crate::tree::PoseidonLeaf; use canonical::{Canon, Store}; use canonical_derive::Canon; use core::borrow::Borrow; -use dusk_plonk::prelude::*; -use hades252::{ScalarStrategy, Strategy}; +use dusk_bls12_381::BlsScalar; use microkelvin::{Annotation, Cardinality}; use nstack::NStack; @@ -65,8 +65,7 @@ impl PoseidonAnnotation { } perm[0] = BlsScalar::from(mask); - ScalarStrategy::new().perm(&mut perm); - let poseidon_root = perm[1]; + let poseidon_root = hash::permutate(&mut perm); Self { cardinality, @@ -92,13 +91,6 @@ impl Borrow for PoseidonAnnotation { } } -impl PoseidonTreeAnnotation for PoseidonAnnotation -where - L: PoseidonLeaf, - S: Store, -{ -} - impl Annotation, S> for PoseidonAnnotation where @@ -135,3 +127,10 @@ where Self::from_generic_node(node) } } + +impl PoseidonTreeAnnotation for PoseidonAnnotation +where + L: PoseidonLeaf, + S: Store, +{ +} diff --git a/src/tree/branch.rs b/src/tree/branch.rs index e91b01c..2afb9ec 100644 --- a/src/tree/branch.rs +++ b/src/tree/branch.rs @@ -7,7 +7,7 @@ use super::{PoseidonLeaf, PoseidonTreeAnnotation}; use canonical::Store; use core::ops::Deref; -use dusk_plonk::prelude::BlsScalar; +use dusk_bls12_381::BlsScalar; use hades252::{ScalarStrategy, Strategy}; use microkelvin::Branch; use nstack::NStack; diff --git a/src/tree/hash.rs b/src/tree/hash.rs new file mode 100644 index 0000000..05a17b4 --- /dev/null +++ b/src/tree/hash.rs @@ -0,0 +1,174 @@ +// 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 hades252::strategies::{ScalarStrategy, Strategy}; + +#[cfg(feature = "std")] +use dusk_plonk::prelude::*; +#[cfg(feature = "std")] +use hades252::strategies::GadgetStrategy; + +/// Perform the Hades252 permutation +pub fn permutate(input: &mut [BlsScalar; hades252::WIDTH]) -> BlsScalar { + ScalarStrategy::new().perm(input); + + input[1] +} + +/// Truncate a set of messages to [`width()`] and set the first element as the bitflags +/// representing the provided input +/// +/// Mirror implementation of [`prepare_input`] for a given plonk circuit +#[cfg(feature = "std")] +pub fn prepare_input_gadget( + composer: &mut StandardComposer, + input: &[BlsScalar], + perm: &mut [Variable; hades252::WIDTH], +) { + let n = cmp::min(input.len(), hades252::WIDTH - 1); + + let mut mask = 0; + (0..n).fold(1, |flag, _| { + mask |= flag; + flag << 1 + }); + + let flag = BlsScalar::from(mask); + let flag = composer.add_input(flag); + perm[0] = flag; + + perm.iter_mut() + .skip(1) + .zip(input.iter()) + .for_each(|(p, i)| *p = composer.add_input(*i)); +} + +/// Perform the Hades252 permutation inside of a circuit +/// +/// Mirror implementation of [`permutate`] +#[cfg(feature = "std")] +pub fn permutate_gadget( + composer: &mut StandardComposer, + input: &mut [Variable; hades252::WIDTH], +) -> Variable { + GadgetStrategy::new(composer).perm(input); + + input[1] +} + +/// Perform the poseidon hash of a provided set of circuit variables. +/// +/// Mirror the implementation of [`hash`] for a circuit +#[cfg(feature = "std")] +pub fn hash_gadget( + composer: &mut StandardComposer, + input: &[BlsScalar], +) -> Variable { + let zero = composer.add_witness_to_circuit_description(BlsScalar::zero()); + + let mut perm = [zero; hades252::WIDTH]; + prepare_input_gadget(composer, input, &mut perm); + + permutate_gadget(composer, &mut perm) +} + +#[cfg(feature = "std")] +#[cfg(test)] +mod tests { + //use super::{hash, prepare_input, width}; + use dusk_bls12_381::BlsScalar; + + use crate::merkle::hash_gadget; + use anyhow::Result; + use dusk_plonk::prelude::*; + + #[test] + fn merkle_preimage() -> Result<()> { + const CAPACITY: usize = 1 << 10; + + let pub_params = + PublicParameters::setup(CAPACITY, &mut rand::thread_rng())?; + let (ck, ok) = pub_params.trim(CAPACITY)?; + + let label = b"merkle_hash_gadget"; + fn execute( + label: &'static [u8], + ck: &CommitKey, + ok: &OpeningKey, + input: &[BlsScalar], + ) -> Result<()> { + let gadget_tester = + |composer: &mut StandardComposer, input: &[BlsScalar]| { + let hash = hash(input); + let hash_p = hash_gadget(composer, input); + + composer.constrain_to_constant( + hash_p, + BlsScalar::zero(), + -hash, + ); + }; + + let mut prover = Prover::new(label); + gadget_tester(prover.mut_cs(), &input); + prover.preprocess(ck)?; + let proof = prover.prove(ck)?; + + let mut verifier = Verifier::new(label); + gadget_tester(verifier.mut_cs(), &input); + verifier.preprocess(ck)?; + let pi = verifier.mut_cs().public_inputs.clone(); + verifier.verify(&proof, ok, &pi).unwrap(); + + Ok(()) + } + + execute(label, &ck, &ok, &[])?; + execute(label, &ck, &ok, &[BlsScalar::from(25)])?; + execute( + label, + &ck, + &ok, + &[BlsScalar::from(54), BlsScalar::from(43728)], + )?; + execute( + label, + &ck, + &ok, + &[ + BlsScalar::from(54), + BlsScalar::from(43728), + BlsScalar::from(123), + ], + )?; + execute( + label, + &ck, + &ok, + &[ + BlsScalar::from(54), + BlsScalar::from(43728), + BlsScalar::from(5846), + BlsScalar::from(9834), + ], + )?; + execute( + label, + &ck, + &ok, + &[ + BlsScalar::from(54), + BlsScalar::from(43728), + BlsScalar::from(5846), + BlsScalar::from(9834), + BlsScalar::from(23984), + ], + )?; + + Ok(()) + } +} diff --git a/src/tree/leaf.rs b/src/tree/leaf.rs new file mode 100644 index 0000000..7db1060 --- /dev/null +++ b/src/tree/leaf.rs @@ -0,0 +1,25 @@ +use canonical::{Canon, Store}; +use dusk_bls12_381::BlsScalar; + +/// A struct that will be used as a poseidon tree leaf must implement this trait +/// +/// After [`PoseidonTree::push`], `tree_pos_mut` will be called to set the +/// index of the leaf on the tree +pub trait PoseidonLeaf: Canon + Clone +where + S: Store, +{ + /// Poseidon hash implementation of the leaf structure. + /// + /// The result of this function will be used as opening for the merkle tree. + fn poseidon_hash(&self) -> BlsScalar; + + /// Index of the leaf structure on the merkle tree. + fn pos(&self) -> u64; + + /// Index of the leaf structure on the merkle tree. + /// + /// This method is internally used to set the index after the data has been inserted in the + /// merkle tree. + fn set_pos(&mut self, pos: u64); +} diff --git a/src/tree/mod.rs b/src/tree/mod.rs index 87804f5..7395b66 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -4,251 +4,26 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -use anyhow::{anyhow, Result}; -use canonical::{Canon, Store}; -use canonical_derive::Canon; -use dusk_plonk::prelude::BlsScalar; -use microkelvin::{Branch, Cardinality, Nth}; -use nstack::NStack; - -pub use annotation::{ - PoseidonAnnotation, PoseidonMaxAnnotation, PoseidonTreeAnnotation, - PoseidonWalkableAnnotation, -}; -pub use branch::{PoseidonBranch, PoseidonLevel}; - mod annotation; mod branch; +mod leaf; +mod tree; + +pub(crate) mod hash; #[cfg(feature = "std")] -/// Zero-Knowledge implementations for the poseidon tree -pub mod zk; +mod zk; #[cfg(test)] mod tests; -/// A struct that will be used as a poseidon tree leaf must implement this trait -/// -/// After [`PoseidonTree::push`], `tree_pos_mut` will be called to set the -/// index of the leaf on the tree -pub trait PoseidonLeaf: Canon + Clone -where - S: Store, -{ - /// Poseidon hash implementation of the leaf structure. - /// - /// The result of this function will be used as opening for the merkle tree. - fn poseidon_hash(&self) -> BlsScalar; - - /// Index of the leaf structure on the merkle tree. - fn pos(&self) -> u64; - - /// Index of the leaf structure on the merkle tree. - /// - /// This method is internally used to set the index after the data has been inserted in the - /// merkle tree. - fn set_pos(&mut self, pos: u64); -} - -/// Represents a Merkle Tree with a given depth that will be calculated using poseidon hash -/// -/// The `BlsScalar` borrow of the annotation must represent the root poseidon merkle opening -/// for the annotated subtree -#[derive(Debug, Clone, Canon)] -pub struct PoseidonTree -where - L: PoseidonLeaf, - A: PoseidonTreeAnnotation, - S: Store, -{ - inner: NStack, -} - -impl AsRef> - for PoseidonTree -where - L: PoseidonLeaf, - A: PoseidonTreeAnnotation, - S: Store, -{ - fn as_ref(&self) -> &NStack { - &self.inner - } -} - -impl AsMut> - for PoseidonTree -where - L: PoseidonLeaf, - A: PoseidonTreeAnnotation, - S: Store, -{ - fn as_mut(&mut self) -> &mut NStack { - &mut self.inner - } -} - -impl PoseidonTree -where - L: PoseidonLeaf, - A: PoseidonTreeAnnotation, - S: Store, -{ - /// Creates a new poseidon tree - pub fn new() -> Self { - let inner = NStack::new(); - - Self { inner } - } - #[cfg(feature = "std")] - /// Append a leaf to the tree. Return the index of the appended leaf. - /// - /// Will call the `tree_pos_mut` implementation of the leaf to - /// set its index - pub fn push(&mut self, mut leaf: L) -> Result { - let size = match &self.inner { - NStack::Leaf(l) => l.iter().filter(|l| l.is_some()).count(), - NStack::Node(n) => n - .iter() - .filter_map(|n| n.as_ref()) - .map::<&Cardinality, _>(|n| n.annotation().borrow()) - .map::(|c| c.into()) - .map(|n| n as usize) - .sum(), - }; - - leaf.set_pos(size as u64); - self.inner - .push(leaf) - .map_err(|e| anyhow!("Error pushing to the tree: {:?}", e))?; - - Ok(size) - } - #[cfg(feature = "std")] - /// Fetch, remove and return the last inserted leaf, if present. - pub fn pop(&mut self) -> Result> { - self.inner - .pop() - .map_err(|e| anyhow!("Error pop from the tree: {:?}", e)) - } - #[cfg(feature = "std")] - /// Fetch a leaf on a provided index. - pub fn get(&self, n: usize) -> Result> { - self.inner - .nth::(n as u64) - .map(|o| o.map(|l| l.clone())) - .map_err(|e| { - anyhow!("Error fetching the Nth item from the tree: {:?}", e) - }) - } - #[cfg(feature = "std")] - /// Return a full merkle opening for this poseidon tree for a given index. - pub fn branch(&self, n: usize) -> Result>> { - let branch = self.inner.nth::(n as u64).map_err(|e| { - anyhow!("Error fetching the Nth item from the tree: {:?}", e) - })?; - - match branch { - Some(b) => Ok(Some(PoseidonBranch::from(&b))), - None => Ok(None), - } - } - #[cfg(feature = "std")] - /// Return the current root/state of the tree. - pub fn root(&self) -> Result { - self.branch(0).map(|b| b.unwrap_or_default().root()) - } - #[cfg(feature = "std")] - /// Iterates over the tree, provided its annotation implements [`PoseidonWalkableAnnotation`] - pub fn iter_walk( - &self, - data: D, - ) -> Result> - where - A: PoseidonWalkableAnnotation, D, L, S>, - { - PoseidonTreeIterator::new(&self, data) - } -} - -#[cfg(feature = "std")] -/// Main iterator of the poseidon tree. -/// -/// Depends on an implementation of `PoseidonWalkableAnnotation` for the tree annotation -/// -/// Every iteration will check for a valid `PoseidonWalkableAnnotation::poseidon_walk` call and -/// return a next leaf if the provided data holds true for the implemented logic -/// -/// The data can be any struct that implements `Clone`, and will be used to define the traversal -/// path over the tree. -pub struct PoseidonTreeIterator -where - L: PoseidonLeaf, - A: PoseidonTreeAnnotation, - S: Store, - D: Clone, -{ - tree: PoseidonTree, - pos: usize, - data: D, -} - -#[cfg(feature = "std")] -impl PoseidonTreeIterator -where - L: PoseidonLeaf, - A: PoseidonTreeAnnotation, - A: PoseidonWalkableAnnotation, D, L, S>, - S: Store, - D: Clone, -{ - /// Iterator constructor - pub fn new(tree: &PoseidonTree, data: D) -> Result { - let tree = tree.clone(); - - // TODO - Naive implementation until iterable branch is implemented - // https://github.com/dusk-network/microkelvin/issues/23 - let pos = , S, DEPTH>>::walk(&tree.inner, |w| { - A::poseidon_walk(w, data.clone()) - }) - .map_err(|e| anyhow!("Error fetching the branch: {:?}", e))? - .map(|l| l.pos()) - .unwrap_or(u64::max_value()) as usize; - - Ok(Self { tree, pos, data }) - } -} +pub use annotation::{ + PoseidonAnnotation, PoseidonMaxAnnotation, PoseidonTreeAnnotation, + PoseidonWalkableAnnotation, +}; +pub use branch::{PoseidonBranch, PoseidonLevel}; +pub use leaf::PoseidonLeaf; +pub use tree::{PoseidonTree, PoseidonTreeIterator}; #[cfg(feature = "std")] -impl Iterator - for PoseidonTreeIterator -where - L: PoseidonLeaf, - A: PoseidonTreeAnnotation, - A: PoseidonWalkableAnnotation, D, L, S>, - S: Store, - D: Clone, -{ - type Item = Result; - - fn next(&mut self) -> Option { - let pos = self.pos; - let (pos_p, overflow) = self.pos.overflowing_add(1); - if overflow { - return None; - } - self.pos = pos_p; - - match self.tree.get(pos) { - // Hack until iterable branch is available - // This will prevent the iteration over non-filtered data - // https://github.com/dusk-network/microkelvin/issues/23 - Ok(Some(l)) if A::poseidon_leaf_found(&l, self.data.clone()) => { - Some(Ok(l)) - } - Ok(Some(_)) => self.next(), - Err(e) => Some(Err(e)), - _ => None, - } - } -} +pub use zk::merkle_opening; diff --git a/src/tree/tests.rs b/src/tree/tests.rs index a0cf5dd..4132511 100644 --- a/src/tree/tests.rs +++ b/src/tree/tests.rs @@ -4,20 +4,18 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -use crate::tree::zk::merkle_opening; use crate::tree::{ PoseidonAnnotation, PoseidonLeaf, PoseidonMaxAnnotation, PoseidonTree, }; -use anyhow::Result; use canonical::Canon; use canonical_derive::Canon; use canonical_host::MemStore; use core::borrow::Borrow; -use dusk_plonk::prelude::*; +use dusk_bls12_381::BlsScalar; use hades252::{ScalarStrategy, Strategy}; #[derive(Debug, Default, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Canon)] -struct MockLeaf { +pub struct MockLeaf { s: BlsScalar, pub pos: u64, pub expiration: u64, @@ -55,17 +53,17 @@ impl Borrow for MockLeaf { #[test] fn tree_append_fetch() { + const MAX: usize = 4097; + let mut v = [MockLeaf::default(); MAX]; + let mut tree: PoseidonTree = PoseidonTree::new(); - let mut v = vec![]; - let max = 4097; - - for i in 0..max { + for i in 0..MAX { let mut s = MockLeaf::from(i as u64); let pos = tree.push(s).unwrap(); assert_eq!(i, pos); s.pos = i as u64; - v.push(s); + v[i] = s; } v.iter().enumerate().for_each(|(i, s)| { @@ -73,25 +71,25 @@ fn tree_append_fetch() { assert_eq!(s, &l); }); - v.into_iter().rev().for_each(|s| { + v.iter().rev().for_each(|s| { let t = tree.pop().unwrap().unwrap(); - assert_eq!(s, t); + assert_eq!(s, &t); }); } #[test] fn tree_max_walk() { + const MAX: usize = 1025; + let mut v = [MockLeaf::default(); MAX]; + let mut tree: PoseidonTree = PoseidonTree::new(); - let mut v = vec![]; - let max = 1025; - - for i in 0..max { + for i in 0..MAX { let mut s = MockLeaf::from(i as u64); let pos = tree.push(s).unwrap(); assert_eq!(i, pos); s.pos = i as u64; - v.push(s); + v[i] = s; } let w = 170; @@ -104,17 +102,18 @@ fn tree_max_walk() { assert_eq!(pos + i as u64, leaf.pos()); }); - assert!(tree.iter_walk((max + 1) as u64).unwrap().next().is_none()); + assert!(tree.iter_walk((MAX + 1) as u64).unwrap().next().is_none()); } #[test] fn tree_max_walk_non_continuous() { + const MAX: usize = 1025; + let mut v = [MockLeaf::default(); MAX]; + let mut tree: PoseidonTree = PoseidonTree::new(); - let mut v = vec![]; - let max = 1025; - for i in 0..max { + for i in 0..MAX { let mut s = MockLeaf::from(i as u64); if i % 4 == 0 { @@ -124,7 +123,7 @@ fn tree_max_walk_non_continuous() { let pos = tree.push(s).unwrap(); assert_eq!(i, pos); s.pos = i as u64; - v.push(s); + v[i] = s; } let w = 170; @@ -140,7 +139,7 @@ fn tree_max_walk_non_continuous() { pos += 1; }); - assert!(tree.iter_walk((max + 1) as u64).unwrap().next().is_none()); + assert!(tree.iter_walk((MAX + 1) as u64).unwrap().next().is_none()); } #[test] @@ -193,50 +192,3 @@ fn tree_branch_leaf() { } }); } - -#[test] -fn tree_merkle_opening() -> Result<()> { - const DEPTH: usize = 17; - - let pub_params = PublicParameters::setup(1 << 15, &mut rand::thread_rng())?; - let (ck, ok) = pub_params.trim(1 << 15)?; - - let mut tree: PoseidonTree = - PoseidonTree::new(); - - for i in 0..1024 { - let l = MockLeaf::from(i as u64); - tree.push(l)?; - } - - let gadget_tester = - |composer: &mut StandardComposer, - tree: &PoseidonTree, - n: usize| { - let branch = tree.branch(n).unwrap().unwrap(); - let root = tree.root().unwrap(); - - let leaf = BlsScalar::from(n as u64); - let leaf = composer.add_input(leaf); - - let root_p = merkle_opening::(composer, &branch, leaf); - composer.constrain_to_constant(root_p, BlsScalar::zero(), -root); - }; - - let label = b"opening_gadget"; - - for i in [0, 567, 1023].iter() { - let mut prover = Prover::new(label); - gadget_tester(prover.mut_cs(), &tree, *i); - prover.preprocess(&ck)?; - let proof = prover.prove(&ck)?; - - let mut verifier = Verifier::new(label); - gadget_tester(verifier.mut_cs(), &tree, *i); - verifier.preprocess(&ck)?; - let pi = verifier.mut_cs().public_inputs.clone(); - verifier.verify(&proof, &ok, &pi).unwrap(); - } - - Ok(()) -} diff --git a/src/tree/tree.rs b/src/tree/tree.rs new file mode 100644 index 0000000..fc164de --- /dev/null +++ b/src/tree/tree.rs @@ -0,0 +1,223 @@ +use super::{ + PoseidonBranch, PoseidonLeaf, PoseidonTreeAnnotation, + PoseidonWalkableAnnotation, +}; +use canonical::{Canon, Store}; +use canonical_derive::Canon; +use dusk_bls12_381::BlsScalar; +use microkelvin::{Branch, Cardinality, Nth}; +use nstack::NStack; + +/// Represents a Merkle Tree with a given depth that will be calculated using poseidon hash +/// +/// The `BlsScalar` borrow of the annotation must represent the root poseidon merkle opening +/// for the annotated subtree +#[derive(Debug, Clone, Canon)] +pub struct PoseidonTree +where + L: PoseidonLeaf, + A: PoseidonTreeAnnotation, + S: Store, +{ + inner: NStack, +} + +impl AsRef> + for PoseidonTree +where + L: PoseidonLeaf, + A: PoseidonTreeAnnotation, + S: Store, +{ + fn as_ref(&self) -> &NStack { + &self.inner + } +} + +impl AsMut> + for PoseidonTree +where + L: PoseidonLeaf, + A: PoseidonTreeAnnotation, + S: Store, +{ + fn as_mut(&mut self) -> &mut NStack { + &mut self.inner + } +} + +impl Default for PoseidonTree +where + L: PoseidonLeaf, + A: PoseidonTreeAnnotation, + S: Store, +{ + fn default() -> Self { + PoseidonTree::new() + } +} + +impl PoseidonTree +where + L: PoseidonLeaf, + A: PoseidonTreeAnnotation, + S: Store, +{ + /// Creates a new poseidon tree + pub fn new() -> Self { + let inner = NStack::new(); + + Self { inner } + } + + /// Append a leaf to the tree. Return the index of the appended leaf. + /// + /// Will call the `tree_pos_mut` implementation of the leaf to + /// set its index + pub fn push(&mut self, mut leaf: L) -> Result { + let size = match &self.inner { + NStack::Leaf(l) => l.iter().filter(|l| l.is_some()).count(), + NStack::Node(n) => n + .iter() + .filter_map(|n| n.as_ref()) + .map::<&Cardinality, _>(|n| n.annotation().borrow()) + .map::(|c| c.into()) + .map(|n| n as usize) + .sum(), + }; + + leaf.set_pos(size as u64); + self.inner + .push(leaf) + .map_err(|_| "Error pushing to the poseidon tree!")?; + + Ok(size) + } + + /// Fetch, remove and return the last inserted leaf, if present. + pub fn pop(&mut self) -> Result, &'static str> { + self.inner.pop().map_err(|_| "Error on pop of the tree!") + } + + /// Fetch a leaf on a provided index. + pub fn get(&self, n: usize) -> Result, &'static str> { + self.inner + .nth::(n as u64) + .map(|o| o.map(|l| l.clone())) + .map_err(|_| "Error fetching the Nth item from the tree!") + } + + /// Return a full merkle opening for this poseidon tree for a given index. + pub fn branch( + &self, + n: usize, + ) -> Result>, &'static str> { + let branch = self + .inner + .nth::(n as u64) + .map_err(|_| "Error fetching the Nth item from the tree!")?; + + match branch { + Some(b) => Ok(Some(PoseidonBranch::from(&b))), + None => Ok(None), + } + } + + /// Return the current root/state of the tree. + pub fn root(&self) -> Result { + self.branch(0).map(|b| b.unwrap_or_default().root()) + } + + /// Iterates over the tree, provided its annotation implements [`PoseidonWalkableAnnotation`] + pub fn iter_walk( + &self, + data: D, + ) -> Result, &'static str> + where + A: PoseidonWalkableAnnotation, D, L, S>, + { + PoseidonTreeIterator::new(&self, data) + } +} + +/// Main iterator of the poseidon tree. +/// +/// Depends on an implementation of `PoseidonWalkableAnnotation` for the tree annotation +/// +/// Every iteration will check for a valid `PoseidonWalkableAnnotation::poseidon_walk` call and +/// return a next leaf if the provided data holds true for the implemented logic +/// +/// The data can be any struct that implements `Clone`, and will be used to define the traversal +/// path over the tree. +pub struct PoseidonTreeIterator +where + L: PoseidonLeaf, + A: PoseidonTreeAnnotation, + S: Store, + D: Clone, +{ + tree: PoseidonTree, + pos: usize, + data: D, +} + +impl PoseidonTreeIterator +where + L: PoseidonLeaf, + A: PoseidonTreeAnnotation, + A: PoseidonWalkableAnnotation, D, L, S>, + S: Store, + D: Clone, +{ + /// Iterator constructor + pub fn new( + tree: &PoseidonTree, + data: D, + ) -> Result { + let tree = tree.clone(); + + // TODO - Naive implementation until iterable branch is implemented + // https://github.com/dusk-network/microkelvin/issues/23 + let pos = , S, DEPTH>>::walk(&tree.inner, |w| { + A::poseidon_walk(w, data.clone()) + }) + .map_err(|_| "Error fetching the branch!")? + .map(|l| l.pos()) + .unwrap_or(u64::max_value()) as usize; + + Ok(Self { tree, pos, data }) + } +} + +impl Iterator + for PoseidonTreeIterator +where + L: PoseidonLeaf, + A: PoseidonTreeAnnotation, + A: PoseidonWalkableAnnotation, D, L, S>, + S: Store, + D: Clone, +{ + type Item = Result; + + fn next(&mut self) -> Option { + let pos = self.pos; + let (pos_p, overflow) = self.pos.overflowing_add(1); + if overflow { + return None; + } + self.pos = pos_p; + + match self.tree.get(pos) { + // Hack until iterable branch is available + // This will prevent the iteration over non-filtered data + // https://github.com/dusk-network/microkelvin/issues/23 + Ok(Some(l)) if A::poseidon_leaf_found(&l, self.data.clone()) => { + Some(Ok(l)) + } + Ok(Some(_)) => self.next(), + Err(e) => Some(Err(e)), + _ => None, + } + } +} diff --git a/src/tree/zk.rs b/src/tree/zk.rs index 0859a59..9e8594b 100644 --- a/src/tree/zk.rs +++ b/src/tree/zk.rs @@ -43,3 +43,68 @@ pub fn merkle_opening( root } + +#[cfg(test)] +mod tests { + use crate::tests::MockLeaf; + use crate::{merkle_opening, PoseidonAnnotation, PoseidonTree}; + use anyhow::Result; + use canonical_host::MemStore; + use dusk_plonk::prelude::*; + + #[test] + fn tree_merkle_opening() -> Result<()> { + const DEPTH: usize = 17; + + let pub_params = + PublicParameters::setup(1 << 15, &mut rand::thread_rng())?; + let (ck, ok) = pub_params.trim(1 << 15)?; + + let mut tree: PoseidonTree< + MockLeaf, + PoseidonAnnotation, + MemStore, + DEPTH, + > = PoseidonTree::new(); + + for i in 0..1024 { + let l = MockLeaf::from(i as u64); + tree.push(l).unwrap(); + } + + let gadget_tester = |composer: &mut StandardComposer, + tree: &PoseidonTree< + MockLeaf, + PoseidonAnnotation, + MemStore, + DEPTH, + >, + n: usize| { + let branch = tree.branch(n).unwrap().unwrap(); + let root = tree.root().unwrap(); + + let leaf = BlsScalar::from(n as u64); + let leaf = composer.add_input(leaf); + + let root_p = merkle_opening::(composer, &branch, leaf); + composer.constrain_to_constant(root_p, BlsScalar::zero(), -root); + }; + + let label = b"opening_gadget"; + + for i in [0, 567, 1023].iter() { + let mut prover = Prover::new(label); + gadget_tester(prover.mut_cs(), &tree, *i); + prover.preprocess(&ck)?; + let proof = prover.prove(&ck)?; + + let mut verifier = Verifier::new(label); + gadget_tester(verifier.mut_cs(), &tree, *i); + verifier.preprocess(&ck)?; + let pi = verifier.mut_cs().public_inputs.clone(); + verifier.verify(&proof, &ok, &pi).unwrap(); + } + + Ok(()) + } +} From 9b5018403790385afb8c53c9f053fe41a0f0ed4a Mon Sep 17 00:00:00 2001 From: CPerezz Date: Tue, 17 Nov 2020 09:07:15 +0100 Subject: [PATCH 08/11] Include `std` in `cmp` path. CI complains about not having this module with it's full path. --- src/tree/hash.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tree/hash.rs b/src/tree/hash.rs index 05a17b4..19e02f3 100644 --- a/src/tree/hash.rs +++ b/src/tree/hash.rs @@ -29,7 +29,7 @@ pub fn prepare_input_gadget( input: &[BlsScalar], perm: &mut [Variable; hades252::WIDTH], ) { - let n = cmp::min(input.len(), hades252::WIDTH - 1); + let n = std::cmp::min(input.len(), hades252::WIDTH - 1); let mut mask = 0; (0..n).fold(1, |flag, _| { From 74e567addadc63b999c3268df5ec7a464bb954db Mon Sep 17 00:00:00 2001 From: CPerezz Date: Tue, 17 Nov 2020 09:09:05 +0100 Subject: [PATCH 09/11] Add license headers --- src/tree/leaf.rs | 6 ++++++ src/tree/tree.rs | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/tree/leaf.rs b/src/tree/leaf.rs index 7db1060..43caac8 100644 --- a/src/tree/leaf.rs +++ b/src/tree/leaf.rs @@ -1,3 +1,9 @@ +// 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 canonical::{Canon, Store}; use dusk_bls12_381::BlsScalar; diff --git a/src/tree/tree.rs b/src/tree/tree.rs index fc164de..717c2b3 100644 --- a/src/tree/tree.rs +++ b/src/tree/tree.rs @@ -1,3 +1,9 @@ +// 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::{ PoseidonBranch, PoseidonLeaf, PoseidonTreeAnnotation, PoseidonWalkableAnnotation, From a24cb5ff8352bb55e2b38ecd998d22516133b799 Mon Sep 17 00:00:00 2001 From: Victor Lopez Date: Tue, 17 Nov 2020 14:58:48 +0100 Subject: [PATCH 10/11] No-Std compatibility --- CHANGELOG.md | 4 ++ Cargo.toml | 2 +- README.md | 2 +- src/cipher/cipher.rs | 6 ++- src/cipher/mod.rs | 82 +++++++++++++++++++++++++++++++++- src/cipher/tests.rs | 9 ++-- src/cipher/zk.rs | 24 +++------- src/error.rs | 42 +++++++++++++++++ src/lib.rs | 6 ++- src/tree/hash.rs | 63 -------------------------- src/tree/leaf.rs | 2 +- src/tree/mod.rs | 104 +++++++++++++++++++++++++++++++++++++++++++ src/tree/tree.rs | 21 ++++----- 13 files changed, 266 insertions(+), 101 deletions(-) create mode 100644 src/error.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 61cdff2..c329112 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] +## [0.14.0] - 17-11-20 +### Changed +- No-Std compatibility. + ## [0.13.1] - 06-11-20 ### Changed - Feature split between `canon` and `canon_host` for constrained and unconstrained environments. diff --git a/Cargo.toml b/Cargo.toml index bd61be6..a8c050f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "poseidon252" -version = "0.13.2" +version = "0.14.0" authors = [ "zer0 ", "vlopes11 ", "CPerezz ", "Kristoffer Ström " ] diff --git a/README.md b/README.md index 6090cd8..a47a602 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ The sponge hash techniqe requires a padding to be applied before the data can be hashed. This is done to avoid hash collitions as stated in the paper of the Poseidon Hash -algorithm. See: (https://eprint.iacr.org/2019/458.pdf)[https://eprint.iacr.org/2019/458.pdf]. +algorithm. See: . The inputs of the `sponge_hash` are always `Scalar` or need to be capable of being represented as it. diff --git a/src/cipher/cipher.rs b/src/cipher/cipher.rs index 85c2219..e4b7948 100644 --- a/src/cipher/cipher.rs +++ b/src/cipher/cipher.rs @@ -4,6 +4,8 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. +use crate::Error; + #[cfg(feature = "canon")] use canonical::Canon; #[cfg(feature = "canon")] @@ -143,7 +145,7 @@ impl PoseidonCipher { &self, secret: &JubJubAffine, nonce: &BlsScalar, - ) -> Result<[BlsScalar; MESSAGE_CAPACITY], &'static str> { + ) -> Result<[BlsScalar; MESSAGE_CAPACITY], Error<()>> { let zero = BlsScalar::zero(); let mut strategy = ScalarStrategy::new(); @@ -160,7 +162,7 @@ impl PoseidonCipher { strategy.perm(&mut state); if self.cipher[MESSAGE_CAPACITY] != state[1] { - return Err("Decryption failed for the provided secret+nonce"); + return Err(Error::CipherDecryptionFailed); } Ok(message) diff --git a/src/cipher/mod.rs b/src/cipher/mod.rs index 8e34c72..5789e6c 100644 --- a/src/cipher/mod.rs +++ b/src/cipher/mod.rs @@ -4,6 +4,86 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. +//! # Poseidon 252 Cipher +//! +//! Encryption/decryption implementation with Poseidon252 backend. +//! +//! This implementation is optimized for a message containing 2 scalars. +//! +//! ## Shared secret +//! +//! The shared secret is a point on the JubJub curve on the affine form. +//! +//! This implementation does not cover the shared secret derivation strategy. +//! +//! The suggestion is to use a Diffie-Hellman key exchange, as shown in the example. Check [dusk-jubjub](https://github.com/dusk-network/jubjub) for further reference. +//! +//! ## Example +//! +//! ```rust +//! use core::ops::Mul; +//! use dusk_bls12_381::BlsScalar; +//! use dusk_jubjub::{dhke, JubJubExtended, JubJubScalar, GENERATOR}; +//! use poseidon252::cipher::PoseidonCipher; +//! +//! fn sender( +//! sender_secret: &JubJubScalar, +//! receiver_public: &JubJubExtended, +//! message: &[BlsScalar], +//! ) -> (PoseidonCipher, BlsScalar) { +//! // Use the Diffie-Hellman protocol to generate a shared secret +//! let shared_secret = dhke(sender_secret, receiver_public); +//! +//! // Generate a random nonce that will be public +//! let nonce = BlsScalar::random(&mut rand::thread_rng()); +//! +//! // Encrypt the message +//! let cipher = PoseidonCipher::encrypt(&message, &shared_secret, &nonce); +//! +//! (cipher, nonce) +//! } +//! +//! fn receiver( +//! receiver_secret: &JubJubScalar, +//! sender_public: &JubJubExtended, +//! cipher: &PoseidonCipher, +//! nonce: &BlsScalar, +//! ) -> [BlsScalar; PoseidonCipher::capacity()] { +//! // Use the Diffie-Hellman protocol to generate a shared secret +//! let shared_secret = dhke(receiver_secret, sender_public); +//! +//! // Decrypt the message +//! cipher +//! .decrypt(&shared_secret, &nonce) +//! .expect("Failed to decrypt!") +//! } +//! +//! let mut rng = rand::thread_rng(); +//! +//! // Generate a secret and a public key for Bob +//! let bob_secret = JubJubScalar::random(&mut rng); +//! let bob_public = GENERATOR.to_niels().mul(&bob_secret); +//! +//! // Generate a secret and a public key for Alice +//! let alice_secret = JubJubScalar::random(&mut rng); +//! let alice_public = GENERATOR.to_niels().mul(&alice_secret); +//! +//! // Generate a secret message +//! let a = BlsScalar::random(&mut rng); +//! let b = BlsScalar::random(&mut rng); +//! let message = [a, b]; +//! +//! // Bob's view (sender) +//! // The cipher and nonce are safe to be broadcasted publicly +//! let (cipher, nonce) = sender(&bob_secret, &alice_public, &message); +//! +//! // Alice's view (receiver) +//! let decrypted_message = receiver(&alice_secret, &bob_public, &cipher, &nonce); +//! +//! // Successful communication +//! assert_eq!(decrypted_message, message); +//! ``` + mod cipher; #[cfg(test)] @@ -15,4 +95,4 @@ mod zk; pub use cipher::PoseidonCipher; #[cfg(feature = "std")] -pub use zk::{poseidon_cipher_decrypt, poseidon_cipher_encrypt}; +pub use zk::{decrypt, encrypt}; diff --git a/src/cipher/tests.rs b/src/cipher/tests.rs index d537364..58b6484 100644 --- a/src/cipher/tests.rs +++ b/src/cipher/tests.rs @@ -5,6 +5,7 @@ // Copyright (c) DUSK NETWORK. All rights reserved. use crate::cipher::PoseidonCipher; +use crate::Error; use core::ops::Mul; use dusk_bls12_381::BlsScalar; use dusk_jubjub::{JubJubAffine, JubJubScalar, GENERATOR}; @@ -48,7 +49,7 @@ fn sanity() { } #[test] -fn encrypt() -> Result<(), &'static str> { +fn encrypt() -> Result<(), Error<()>> { let (message, secret, nonce) = gen(); let cipher = PoseidonCipher::encrypt(&message, &secret, &nonce); @@ -60,7 +61,7 @@ fn encrypt() -> Result<(), &'static str> { } #[test] -fn single_bit() -> Result<(), &'static str> { +fn single_bit() -> Result<(), Error<()>> { let (_, secret, nonce) = gen(); let message = BlsScalar::random(&mut rand::thread_rng()); @@ -73,7 +74,7 @@ fn single_bit() -> Result<(), &'static str> { } #[test] -fn overflow() -> Result<(), &'static str> { +fn overflow() -> Result<(), Error<()>> { let (_, secret, nonce) = gen(); let message = [BlsScalar::random(&mut rand::thread_rng()); PoseidonCipher::capacity() + 1]; @@ -96,7 +97,7 @@ fn wrong_key_fail() { } #[test] -fn bytes() -> Result<(), &'static str> { +fn bytes() -> Result<(), Error<()>> { let (message, secret, nonce) = gen(); let cipher = PoseidonCipher::encrypt(&message, &secret, &nonce); diff --git a/src/cipher/zk.rs b/src/cipher/zk.rs index 7151f1d..f0b2c03 100644 --- a/src/cipher/zk.rs +++ b/src/cipher/zk.rs @@ -33,7 +33,7 @@ impl PoseidonCipher { /// the encryption of the message. /// /// The returned set of variables is the cipher text -pub fn poseidon_cipher_encrypt( +pub fn encrypt( composer: &mut StandardComposer, shared_secret: &Point, nonce: Variable, @@ -73,7 +73,7 @@ pub fn poseidon_cipher_encrypt( /// the decryption of the cipher. /// /// The returned set of variables is the original message -pub fn poseidon_cipher_decrypt( +pub fn decrypt( composer: &mut StandardComposer, shared_secret: &Point, nonce: Variable, @@ -110,9 +110,7 @@ pub fn poseidon_cipher_decrypt( #[cfg(test)] mod tests { - use crate::cipher::{ - poseidon_cipher_decrypt, poseidon_cipher_encrypt, PoseidonCipher, - }; + use crate::cipher::{decrypt, encrypt, PoseidonCipher}; use anyhow::Result; use dusk_bls12_381::BlsScalar; use dusk_jubjub::{dhke, JubJubExtended, GENERATOR_EXTENDED}; @@ -171,24 +169,16 @@ mod tests { }, ); - let cipher_gadget = poseidon_cipher_encrypt( - composer, - shared.point(), - nonce, - &message_circuit, - ); + let cipher_gadget = + encrypt(composer, shared.point(), nonce, &message_circuit); cipher.iter().zip(cipher_gadget.iter()).for_each(|(c, g)| { let x = composer.add_input(*c); composer.assert_equal(x, *g); }); - let message_gadget = poseidon_cipher_decrypt( - composer, - shared.point(), - nonce, - &cipher_gadget, - ); + let message_gadget = + decrypt(composer, shared.point(), nonce, &cipher_gadget); message .iter() diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..dc07437 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,42 @@ +// 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 core::fmt; + +#[cfg(feature = "std")] +use std::{error as std_error, fmt as std_fmt}; + +/// Poseidon error variants +#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum Error { + /// Error pushing to the poseidon tree + TreePushFailed(E), + /// Error on pop of the tree + TreePopFailed(E), + /// Error fetching the Nth item from the tree + TreeGetFailed(E), + /// Decryption failed for the provided secret+nonce + CipherDecryptionFailed, +} + +#[cfg(feature = "std")] +impl std_fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std_fmt::Result { + write!(f, "Poseidon252 Error: {:?}", &self) + } +} + +#[cfg(feature = "std")] +impl std_error::Error for Error { + fn source(&self) -> Option<&(dyn std_error::Error + 'static)> { + match &self { + Self::TreePushFailed(e) => Some(e), + Self::TreePopFailed(e) => Some(e), + Self::TreeGetFailed(e) => Some(e), + _ => None, + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 955df45..9bb2edd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ #![feature(min_const_generics)] #![feature(external_doc)] #![doc(include = "../README.md")] -#![deny(missing_docs)] +#![warn(missing_docs)] /// Encryption and decryption implementation over a Poseidon cipher pub mod cipher; @@ -21,3 +21,7 @@ pub mod sponge; /// The module handling posedion-trees. #[cfg(feature = "canon")] pub mod tree; + +mod error; + +pub use error::Error; diff --git a/src/tree/hash.rs b/src/tree/hash.rs index 19e02f3..56f7001 100644 --- a/src/tree/hash.rs +++ b/src/tree/hash.rs @@ -7,11 +7,6 @@ use dusk_bls12_381::BlsScalar; use hades252::strategies::{ScalarStrategy, Strategy}; -#[cfg(feature = "std")] -use dusk_plonk::prelude::*; -#[cfg(feature = "std")] -use hades252::strategies::GadgetStrategy; - /// Perform the Hades252 permutation pub fn permutate(input: &mut [BlsScalar; hades252::WIDTH]) -> BlsScalar { ScalarStrategy::new().perm(input); @@ -19,67 +14,9 @@ pub fn permutate(input: &mut [BlsScalar; hades252::WIDTH]) -> BlsScalar { input[1] } -/// Truncate a set of messages to [`width()`] and set the first element as the bitflags -/// representing the provided input -/// -/// Mirror implementation of [`prepare_input`] for a given plonk circuit -#[cfg(feature = "std")] -pub fn prepare_input_gadget( - composer: &mut StandardComposer, - input: &[BlsScalar], - perm: &mut [Variable; hades252::WIDTH], -) { - let n = std::cmp::min(input.len(), hades252::WIDTH - 1); - - let mut mask = 0; - (0..n).fold(1, |flag, _| { - mask |= flag; - flag << 1 - }); - - let flag = BlsScalar::from(mask); - let flag = composer.add_input(flag); - perm[0] = flag; - - perm.iter_mut() - .skip(1) - .zip(input.iter()) - .for_each(|(p, i)| *p = composer.add_input(*i)); -} - -/// Perform the Hades252 permutation inside of a circuit -/// -/// Mirror implementation of [`permutate`] -#[cfg(feature = "std")] -pub fn permutate_gadget( - composer: &mut StandardComposer, - input: &mut [Variable; hades252::WIDTH], -) -> Variable { - GadgetStrategy::new(composer).perm(input); - - input[1] -} - -/// Perform the poseidon hash of a provided set of circuit variables. -/// -/// Mirror the implementation of [`hash`] for a circuit -#[cfg(feature = "std")] -pub fn hash_gadget( - composer: &mut StandardComposer, - input: &[BlsScalar], -) -> Variable { - let zero = composer.add_witness_to_circuit_description(BlsScalar::zero()); - - let mut perm = [zero; hades252::WIDTH]; - prepare_input_gadget(composer, input, &mut perm); - - permutate_gadget(composer, &mut perm) -} - #[cfg(feature = "std")] #[cfg(test)] mod tests { - //use super::{hash, prepare_input, width}; use dusk_bls12_381::BlsScalar; use crate::merkle::hash_gadget; diff --git a/src/tree/leaf.rs b/src/tree/leaf.rs index 43caac8..ec6cd8a 100644 --- a/src/tree/leaf.rs +++ b/src/tree/leaf.rs @@ -9,7 +9,7 @@ use dusk_bls12_381::BlsScalar; /// A struct that will be used as a poseidon tree leaf must implement this trait /// -/// After [`PoseidonTree::push`], `tree_pos_mut` will be called to set the +/// After `PoseidonTree::push`, `tree_pos_mut` will be called to set the /// index of the leaf on the tree pub trait PoseidonLeaf: Canon + Clone where diff --git a/src/tree/mod.rs b/src/tree/mod.rs index 7395b66..16784c8 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -4,6 +4,110 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. +//! # Poseidon252 Merkle Tree +//! +//! Implementation of a Merkle Tree with a Poseidon252 backend and zero-knowledge opening proof powered by PLONK. +//! +//! ### Example +//! +//! ```rust +//! #[cfg(feature = "std")] +//! { +//! use anyhow::Result; +//! use canonical::Canon; +//! use canonical_derive::Canon; +//! use canonical_host::MemStore; +//! use dusk_plonk::prelude::*; +//! use poseidon252_tree::{merkle_opening, PoseidonAnnotation, PoseidonLeaf, PoseidonTree}; +//! +//! // Constant depth of the merkle tree +//! const DEPTH: usize = 17; +//! +//! // Leaf representation +//! #[derive(Debug, Default, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Canon)] +//! struct DataLeaf { +//! data: BlsScalar, +//! pos: u64, +//! } +//! +//! // Example helper +//! impl From for DataLeaf { +//! fn from(n: u64) -> DataLeaf { +//! DataLeaf { +//! data: BlsScalar::from(n), +//! pos: n, +//! } +//! } +//! } +//! +//! // Any leaf of the poseidon tree must implement `PoseidonLeaf` +//! impl PoseidonLeaf for DataLeaf { +//! // Cryptographic hash of the data leaf +//! fn poseidon_hash(&self) -> BlsScalar { +//! self.data +//! } +//! +//! // Position on the tree +//! fn pos(&self) -> u64 { +//! self.pos +//! } +//! +//! // Method used to set the position on the tree after the `PoseidonTree::push` call +//! fn set_pos(&mut self, pos: u64) { +//! self.pos = pos; +//! } +//! } +//! +//! fn main() -> Result<()> { +//! // Create the ZK keys +//! let pub_params = PublicParameters::setup(1 << 15, &mut rand::thread_rng())?; +//! let (ck, ok) = pub_params.trim(1 << 15)?; +//! +//! // Instantiate a new tree with the MemStore implementation +//! let mut tree: PoseidonTree = PoseidonTree::new(); +//! +//! // Append 1024 elements to the tree +//! for i in 0..1024 { +//! let l = DataLeaf::from(i as u64); +//! tree.push(l).unwrap(); +//! } +//! +//! // Create a merkle opening tester gadget +//! let gadget_tester = |composer: &mut StandardComposer, +//! tree: &PoseidonTree, +//! n: usize| { +//! let branch = tree.branch(n).unwrap().unwrap(); +//! let root = tree.root().unwrap(); +//! +//! let leaf = BlsScalar::from(n as u64); +//! let leaf = composer.add_input(leaf); +//! +//! let root_p = merkle_opening::(composer, &branch, leaf); +//! composer.constrain_to_constant(root_p, BlsScalar::zero(), -root); +//! }; +//! +//! // Define the transcript initializer for the ZK backend +//! let label = b"opening_gadget"; +//! let pos = 0; +//! +//! // Create a merkle opening ZK proof +//! let mut prover = Prover::new(label); +//! gadget_tester(prover.mut_cs(), &tree, pos); +//! prover.preprocess(&ck)?; +//! let proof = prover.prove(&ck)?; +//! +//! // Verify the merkle opening proof +//! let mut verifier = Verifier::new(label); +//! gadget_tester(verifier.mut_cs(), &tree, pos); +//! verifier.preprocess(&ck)?; +//! let pi = verifier.mut_cs().public_inputs.clone(); +//! verifier.verify(&proof, &ok, &pi).unwrap(); +//! +//! Ok(()) +//! } +//! } +//! ``` + mod annotation; mod branch; mod leaf; diff --git a/src/tree/tree.rs b/src/tree/tree.rs index 717c2b3..d2957d5 100644 --- a/src/tree/tree.rs +++ b/src/tree/tree.rs @@ -8,6 +8,7 @@ use super::{ PoseidonBranch, PoseidonLeaf, PoseidonTreeAnnotation, PoseidonWalkableAnnotation, }; +use crate::Error; use canonical::{Canon, Store}; use canonical_derive::Canon; use dusk_bls12_381::BlsScalar; @@ -80,7 +81,7 @@ where /// /// Will call the `tree_pos_mut` implementation of the leaf to /// set its index - pub fn push(&mut self, mut leaf: L) -> Result { + pub fn push(&mut self, mut leaf: L) -> Result> { let size = match &self.inner { NStack::Leaf(l) => l.iter().filter(|l| l.is_some()).count(), NStack::Node(n) => n @@ -95,33 +96,33 @@ where leaf.set_pos(size as u64); self.inner .push(leaf) - .map_err(|_| "Error pushing to the poseidon tree!")?; + .map_err(|e| Error::TreePushFailed(e))?; Ok(size) } /// Fetch, remove and return the last inserted leaf, if present. - pub fn pop(&mut self) -> Result, &'static str> { - self.inner.pop().map_err(|_| "Error on pop of the tree!") + pub fn pop(&mut self) -> Result, Error> { + self.inner.pop().map_err(|e| Error::TreePopFailed(e)) } /// Fetch a leaf on a provided index. - pub fn get(&self, n: usize) -> Result, &'static str> { + pub fn get(&self, n: usize) -> Result, Error> { self.inner .nth::(n as u64) .map(|o| o.map(|l| l.clone())) - .map_err(|_| "Error fetching the Nth item from the tree!") + .map_err(|e| Error::TreeGetFailed(e)) } /// Return a full merkle opening for this poseidon tree for a given index. pub fn branch( &self, n: usize, - ) -> Result>, &'static str> { + ) -> Result>, Error> { let branch = self .inner .nth::(n as u64) - .map_err(|_| "Error fetching the Nth item from the tree!")?; + .map_err(|e| Error::TreeGetFailed(e))?; match branch { Some(b) => Ok(Some(PoseidonBranch::from(&b))), @@ -130,7 +131,7 @@ where } /// Return the current root/state of the tree. - pub fn root(&self) -> Result { + pub fn root(&self) -> Result> { self.branch(0).map(|b| b.unwrap_or_default().root()) } @@ -204,7 +205,7 @@ where S: Store, D: Clone, { - type Item = Result; + type Item = Result>; fn next(&mut self) -> Option { let pos = self.pos; From 3635c3c0cd8bc89331343a7e9600a8b6ab027873 Mon Sep 17 00:00:00 2001 From: Victor Lopez Date: Tue, 17 Nov 2020 15:21:48 +0100 Subject: [PATCH 11/11] Documentation nits --- README.md | 5 +- src/tree/annotation/poseidon.rs | 5 +- src/tree/hash.rs | 111 -------------------------------- src/tree/mod.rs | 4 +- src/tree/zk.rs | 4 +- 5 files changed, 8 insertions(+), 121 deletions(-) delete mode 100644 src/tree/hash.rs diff --git a/README.md b/README.md index a47a602..b3e54f9 100644 --- a/README.md +++ b/README.md @@ -76,8 +76,7 @@ use canonical::Canon; use canonical_derive::Canon; use canonical_host::MemStore; use dusk_plonk::prelude::*; -use poseidon252::tree::zk::merkle_opening; -use poseidon252::tree::{PoseidonAnnotation, PoseidonLeaf, PoseidonTree}; +use poseidon252::tree::{PoseidonAnnotation, PoseidonLeaf, PoseidonTree, merkle_opening}; // Constant depth of the merkle tree const DEPTH: usize = 17; @@ -129,7 +128,7 @@ fn main() -> Result<()> { // Append 1024 elements to the tree for i in 0..1024 { let l = DataLeaf::from(i as u64); - tree.push(l)?; + tree.push(l).unwrap(); } // Create a merkle opening tester gadget diff --git a/src/tree/annotation/poseidon.rs b/src/tree/annotation/poseidon.rs index 13cc35d..1dd6035 100644 --- a/src/tree/annotation/poseidon.rs +++ b/src/tree/annotation/poseidon.rs @@ -5,12 +5,12 @@ // Copyright (c) DUSK NETWORK. All rights reserved. use super::PoseidonTreeAnnotation; -use crate::tree::hash; use crate::tree::PoseidonLeaf; use canonical::{Canon, Store}; use canonical_derive::Canon; use core::borrow::Borrow; use dusk_bls12_381::BlsScalar; +use hades252::{ScalarStrategy, Strategy}; use microkelvin::{Annotation, Cardinality}; use nstack::NStack; @@ -65,7 +65,8 @@ impl PoseidonAnnotation { } perm[0] = BlsScalar::from(mask); - let poseidon_root = hash::permutate(&mut perm); + ScalarStrategy::new().perm(&mut perm); + let poseidon_root = perm[1]; Self { cardinality, diff --git a/src/tree/hash.rs b/src/tree/hash.rs deleted file mode 100644 index 56f7001..0000000 --- a/src/tree/hash.rs +++ /dev/null @@ -1,111 +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_bls12_381::BlsScalar; -use hades252::strategies::{ScalarStrategy, Strategy}; - -/// Perform the Hades252 permutation -pub fn permutate(input: &mut [BlsScalar; hades252::WIDTH]) -> BlsScalar { - ScalarStrategy::new().perm(input); - - input[1] -} - -#[cfg(feature = "std")] -#[cfg(test)] -mod tests { - use dusk_bls12_381::BlsScalar; - - use crate::merkle::hash_gadget; - use anyhow::Result; - use dusk_plonk::prelude::*; - - #[test] - fn merkle_preimage() -> Result<()> { - const CAPACITY: usize = 1 << 10; - - let pub_params = - PublicParameters::setup(CAPACITY, &mut rand::thread_rng())?; - let (ck, ok) = pub_params.trim(CAPACITY)?; - - let label = b"merkle_hash_gadget"; - fn execute( - label: &'static [u8], - ck: &CommitKey, - ok: &OpeningKey, - input: &[BlsScalar], - ) -> Result<()> { - let gadget_tester = - |composer: &mut StandardComposer, input: &[BlsScalar]| { - let hash = hash(input); - let hash_p = hash_gadget(composer, input); - - composer.constrain_to_constant( - hash_p, - BlsScalar::zero(), - -hash, - ); - }; - - let mut prover = Prover::new(label); - gadget_tester(prover.mut_cs(), &input); - prover.preprocess(ck)?; - let proof = prover.prove(ck)?; - - let mut verifier = Verifier::new(label); - gadget_tester(verifier.mut_cs(), &input); - verifier.preprocess(ck)?; - let pi = verifier.mut_cs().public_inputs.clone(); - verifier.verify(&proof, ok, &pi).unwrap(); - - Ok(()) - } - - execute(label, &ck, &ok, &[])?; - execute(label, &ck, &ok, &[BlsScalar::from(25)])?; - execute( - label, - &ck, - &ok, - &[BlsScalar::from(54), BlsScalar::from(43728)], - )?; - execute( - label, - &ck, - &ok, - &[ - BlsScalar::from(54), - BlsScalar::from(43728), - BlsScalar::from(123), - ], - )?; - execute( - label, - &ck, - &ok, - &[ - BlsScalar::from(54), - BlsScalar::from(43728), - BlsScalar::from(5846), - BlsScalar::from(9834), - ], - )?; - execute( - label, - &ck, - &ok, - &[ - BlsScalar::from(54), - BlsScalar::from(43728), - BlsScalar::from(5846), - BlsScalar::from(9834), - BlsScalar::from(23984), - ], - )?; - - Ok(()) - } -} diff --git a/src/tree/mod.rs b/src/tree/mod.rs index 16784c8..3c75f06 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -18,7 +18,7 @@ //! use canonical_derive::Canon; //! use canonical_host::MemStore; //! use dusk_plonk::prelude::*; -//! use poseidon252_tree::{merkle_opening, PoseidonAnnotation, PoseidonLeaf, PoseidonTree}; +//! use poseidon252::tree::{merkle_opening, PoseidonAnnotation, PoseidonLeaf, PoseidonTree}; //! //! // Constant depth of the merkle tree //! const DEPTH: usize = 17; @@ -113,8 +113,6 @@ mod branch; mod leaf; mod tree; -pub(crate) mod hash; - #[cfg(feature = "std")] mod zk; diff --git a/src/tree/zk.rs b/src/tree/zk.rs index 9e8594b..f3f2626 100644 --- a/src/tree/zk.rs +++ b/src/tree/zk.rs @@ -46,8 +46,8 @@ pub fn merkle_opening( #[cfg(test)] mod tests { - use crate::tests::MockLeaf; - use crate::{merkle_opening, PoseidonAnnotation, PoseidonTree}; + use crate::tree::tests::MockLeaf; + use crate::tree::{merkle_opening, PoseidonAnnotation, PoseidonTree}; use anyhow::Result; use canonical_host::MemStore; use dusk_plonk::prelude::*;