diff --git a/.github/RELEASE_TEMPLATE.md b/.github/RELEASE_TEMPLATE.md index cf4ceb7c5..bbf6c7394 100644 --- a/.github/RELEASE_TEMPLATE.md +++ b/.github/RELEASE_TEMPLATE.md @@ -3,5 +3,4 @@ - [ ] Checked that the release is on the correct branch name of the form `release-vX.Y.Z` and the PR title matches `Release vX.Y.Z` - [ ] Added the `changelog:skip` label and the relevant `release` label to this PR - [ ] Updated the [`CHANGELOG.md`](https://github.com/manta-network/manta-rs/blob/main/CHANGELOG.md) -- [ ] Updated the version numbers in the `Cargo.toml` for each crate in the workspace -- [ ] Ran `cargo hakari disable` to disable the `workspace-hack` system and checked that `workspace-hack/Cargo.toml` has no dependencies +- [ ] Updated the version number in the workspace `Cargo.toml` diff --git a/CHANGELOG.md b/CHANGELOG.md index 85039811c..f5577a4ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,14 +5,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] ### Added +- [\#238](https://github.com/Manta-Network/manta-rs/pull/238) Add trusted setup ceremony primitives for server and client - [\#237](https://github.com/Manta-Network/manta-rs/pull/237) Public input fuzzing tests for transfer protocol - [\#215](https://github.com/Manta-Network/manta-rs/pull/215) Add windowed multiplication algorithm for groups - [\#213](https://github.com/Manta-Network/manta-rs/pull/197) Add Ceremony Utilities - [\#206](https://github.com/Manta-Network/manta-rs/pull/206) Move Poseidon sage script to test the hardcoded round constant values -- [\#172](https://github.com/Manta-Network/manta-rs/pull/172) Add abstract Phase 2 for Groth16 trusted setup -- [\#193](https://github.com/Manta-Network/manta-rs/pull/193) Add Bn254 curve backend for Groth16 trusted setup -- [\#196](https://github.com/Manta-Network/manta-rs/pull/172) Add fixed base scalar multiplication using precomputed bases - [\#197](https://github.com/Manta-Network/manta-rs/pull/197) Add ECLAIR utilities for next circuit upgrade +- [\#196](https://github.com/Manta-Network/manta-rs/pull/172) Add fixed base scalar multiplication using precomputed bases +- [\#193](https://github.com/Manta-Network/manta-rs/pull/193) Add Bn254 curve backend for Groth16 trusted setup +- [\#172](https://github.com/Manta-Network/manta-rs/pull/172) Add abstract Phase 2 for Groth16 trusted setup ### Changed - [\#247](https://github.com/Manta-Network/manta-rs/pull/247) Moved BLS12-381 and BN254 curves (and Edwards counterparts) to `manta-crypto` diff --git a/Cargo.toml b/Cargo.toml index dc1d3dbe5..f6db9e878 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,15 @@ +[workspace.package] +version = "0.5.4" +edition = "2021" +authors = ["Manta Network "] +repository = "https://github.com/Manta-Network/manta-rs" +homepage = "https://github.com/Manta-Network" +documentation = "https://github.com/Manta-Network/manta-rs" +license-file = "LICENSE" +categories = [""] +keywords = [""] +publish = false + [workspace] resolver = "2" members = ["manta-*"] diff --git a/manta-benchmark/Cargo.toml b/manta-benchmark/Cargo.toml index b6488a55c..13fa290f6 100644 --- a/manta-benchmark/Cargo.toml +++ b/manta-benchmark/Cargo.toml @@ -44,15 +44,13 @@ name = "reclaim" harness = false [dependencies] -ark-ec = { version = "0.3.0", default-features = false } -ark-ff = { version = "0.3.0", default-features = false } getrandom = { version = "0.2.6", default-features = false, features = ["js"] } instant = { version = "0.1.12", default-features = false, features = [ "wasm-bindgen" ] } manta-accounting = { path = "../manta-accounting", default-features = false, features = ["test"] } manta-crypto = { path = "../manta-crypto", default-features = false, features = ["ark-bls12-381", "getrandom", "test"] } manta-pay = { path = "../manta-pay", default-features = false, features = ["groth16", "test"] } -wasm-bindgen = { version = "0.2.82", default-features = false } -wasm-bindgen-test = { version = "0.3.30", default-features = false } +wasm-bindgen = { version = "0.2.83", default-features = false } +wasm-bindgen-test = { version = "0.3.33", default-features = false } web-sys = { version = "0.3.59", default-features = false, features = ["console"] } [dev-dependencies] diff --git a/manta-benchmark/src/ecc.rs b/manta-benchmark/src/ecc.rs index 6a4c9f8de..dd67779a8 100644 --- a/manta-benchmark/src/ecc.rs +++ b/manta-benchmark/src/ecc.rs @@ -16,10 +16,14 @@ //! Elliptic Curve Cryptography Utilities -use ark_ec::{AffineCurve, ProjectiveCurve}; -use ark_ff::UniformRand; use core::ops::AddAssign; -use manta_crypto::rand::RngCore; +use manta_crypto::{ + arkworks::{ + ec::{AffineCurve, ProjectiveCurve}, + ff::UniformRand, + }, + rand::RngCore, +}; /// Samples an affine point. #[inline] diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index 4366bd1e5..d9228cb92 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -42,9 +42,10 @@ getrandom = ["rand_core/getrandom"] # Serde Serialization serde = [ + "ed25519-dalek?/serde", "manta-util/serde-alloc", "manta-util/serde-array", - "rand_chacha?/serde1" + "rand_chacha?/serde1", ] # Standard Library diff --git a/manta-crypto/src/arkworks/mod.rs b/manta-crypto/src/arkworks/mod.rs index ab1c075e1..b3642b655 100644 --- a/manta-crypto/src/arkworks/mod.rs +++ b/manta-crypto/src/arkworks/mod.rs @@ -19,7 +19,6 @@ pub use ark_ec as ec; pub use ark_r1cs_std as r1cs_std; pub use ark_relations as relations; -pub use ark_serialize as serialize; #[cfg(feature = "ark-bls12-381")] pub use ark_bls12_381 as bls12_381; @@ -39,3 +38,4 @@ pub mod ff; pub mod pairing; pub mod rand; pub mod ratio; +pub mod serialize; diff --git a/manta-crypto/src/arkworks/serialize.rs b/manta-crypto/src/arkworks/serialize.rs new file mode 100644 index 000000000..084602117 --- /dev/null +++ b/manta-crypto/src/arkworks/serialize.rs @@ -0,0 +1,54 @@ +// Copyright 2019-2022 Manta Network. +// This file is part of manta-rs. +// +// manta-rs is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// manta-rs is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with manta-rs. If not, see . + +//! Arkworks Canonical Serialize and Deserialize Backend + +#[cfg(feature = "serde")] +use { + alloc::vec::Vec, + manta_util::serde::{de, ser, Deserialize, Deserializer, Serialize, Serializer}, +}; + +#[doc(inline)] +pub use ark_serialize::*; + +/// Serializes `data` using the [`CanonicalSerialize`] format with `S` as the [`Serializer`]. +#[cfg(feature = "serde")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] +#[inline] +pub fn canonical_serialize(data: &T, serializer: S) -> Result +where + T: CanonicalSerialize, + S: Serializer, +{ + let mut bytes = Vec::new(); + data.serialize(&mut bytes).map_err(ser::Error::custom)?; + Serialize::serialize(&bytes, serializer) +} + +/// Deserializes data of type `T` using the [`CanonicalDeserialize`] format with `D` as the +/// [`Deserializer`]. +#[cfg(feature = "serde")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] +#[inline] +pub fn canonical_deserialize<'de, D, T>(deserializer: D) -> Result +where + D: Deserializer<'de>, + T: CanonicalDeserialize, +{ + let bytes: Vec = Deserialize::deserialize(deserializer)?; + CanonicalDeserialize::deserialize(bytes.as_slice()).map_err(de::Error::custom) +} diff --git a/manta-crypto/src/dalek/ed25519.rs b/manta-crypto/src/dalek/ed25519.rs index 0b7b39521..ef3c621c0 100644 --- a/manta-crypto/src/dalek/ed25519.rs +++ b/manta-crypto/src/dalek/ed25519.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see . -//! Dalek Cryptography `ed25519` Backend +//! Dalek Cryptography [`ed25519`](ed25519_dalek) Backend use crate::{ rand::{CryptoRng, Rand, RngCore}, @@ -27,17 +27,36 @@ use manta_util::AsBytes; pub use ed25519_dalek::*; -/// Converts `bytes` into a [`SecretKey`]. -#[inline] -pub fn secret_key_from_bytes(bytes: [u8; SECRET_KEY_LENGTH]) -> SecretKey { - match SecretKey::from_bytes(&bytes) { - Ok(secret_key) => secret_key, - _ => { - unreachable!("We are guaranteed the correct number of bytes from `SECRET_KEY_LENGTH`.") +/// Implements byte conversion from an array of bytes of length `$len` into the given `$type`. These +/// implementations are prefered over the ones provided by [`ed25519_dalek`] because they have no +/// error branch. +macro_rules! byte_conversion { + ($name:ident, $type:tt, $len:ident) => { + #[doc = "Converts the `bytes` fixed-length array into [`"] + #[doc = stringify!($type)] + #[doc = "`]."] + /// + /// # Note + /// + /// We don't need to return an error here because `bytes` already has the correct length. + #[inline] + pub fn $name(bytes: [u8; $len]) -> $type { + match $type::from_bytes(&bytes) { + Ok(value) => value, + _ => unreachable!(concat!( + "We are guaranteed the correct number of bytes from `", + stringify!($len), + "`." + )), + } } - } + }; } +byte_conversion!(secret_key_from_bytes, SecretKey, SECRET_KEY_LENGTH); +byte_conversion!(public_key_from_bytes, PublicKey, PUBLIC_KEY_LENGTH); +byte_conversion!(signature_from_bytes, Signature, SIGNATURE_LENGTH); + /// Clones the `secret_key` by serializing and then deserializing. #[inline] pub fn clone_secret_key(secret_key: &SecretKey) -> SecretKey { @@ -85,7 +104,9 @@ impl MessageType for Ed25519 { } impl RandomnessType for Ed25519 { - /// The `ed25519_dalek` crate provides randomness internally so we set it as `()` here. + /// Empty Randomness + /// + /// The [`ed25519_dalek`] crate provides randomness internally so we set it as `()` here. type Randomness = (); } diff --git a/manta-parameters/Cargo.toml b/manta-parameters/Cargo.toml index 059ec8dea..e0c238b0e 100644 --- a/manta-parameters/Cargo.toml +++ b/manta-parameters/Cargo.toml @@ -32,7 +32,7 @@ download = ["anyhow", "attohttpc", "std"] std = ["anyhow?/std"] [dependencies] -anyhow = { version = "1.0.64", optional = true, default-features = false } +anyhow = { version = "1.0.65", optional = true, default-features = false } attohttpc = { version = "0.22.0", optional = true } blake3 = { version = "1.3.1", default-features = false } @@ -44,7 +44,7 @@ tempfile = { version = "3.3.0", default-features = false } walkdir = { version = "2.3.2", default-features = false } [build-dependencies] -anyhow = { version = "1.0.64", default-features = false, features = ["std"] } +anyhow = { version = "1.0.65", default-features = false, features = ["std"] } blake3 = { version = "1.3.1", default-features = false, features = ["std"] } gitignore = { version = "1.0.7", default-features = false } hex = { version = "0.4.3", default-features = false, features = ["std"] } diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 612a7d7b9..2f11a9599 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -106,7 +106,7 @@ ark-std = { version = "0.3.0", optional = true, default-features = false } bip32 = { version = "0.3.0", optional = true, default-features = false, features = ["bip39", "secp256k1"] } blake2 = { version = "0.10.4", default-features = false } bs58 = { version = "0.4.0", optional = true, default-features = false, features = ["alloc"] } -clap = { version = "3.2.20", optional = true, default-features = false, features = ["color", "derive", "std", "suggestions", "unicode", "wrap_help"] } +clap = { version = "3.2.22", optional = true, default-features = false, features = ["color", "derive", "std", "suggestions", "unicode", "wrap_help"] } derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } futures = { version = "0.3.24", optional = true, default-features = false } indexmap = { version = "1.9.1", optional = true, default-features = false } @@ -119,7 +119,7 @@ scale-codec = { package = "parity-scale-codec", version = "3.1.2", optional = tr scale-info = { version = "2.1.2", optional = true, default-features = false, features = ["derive"] } serde_json = { version = "1.0.85", optional = true, default-features = false, features = ["alloc"] } tempfile = { version = "3.3.0", optional = true, default-features = false } -tokio = { version = "1.21.0", optional = true, default-features = false } +tokio = { version = "1.21.1", optional = true, default-features = false } tokio-tungstenite = { version = "0.17.2", optional = true, default-features = false, features = ["native-tls"] } ws_stream_wasm = { version = "0.7.3", optional = true, default-features = false } diff --git a/manta-trusted-setup/Cargo.toml b/manta-trusted-setup/Cargo.toml index 7a9226c18..62b280bc4 100644 --- a/manta-trusted-setup/Cargo.toml +++ b/manta-trusted-setup/Cargo.toml @@ -25,6 +25,12 @@ is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } [features] +# Bincode for Message Signing +bincode = ["dep:bincode", "serde"] + +# CSV for Ceremony Registries +csv = ["dep:csv", "serde", "std"] + # Perpetual Powers of Tau Ceremony ppot = ["manta-crypto/ark-bn254"] @@ -33,6 +39,7 @@ rayon = ["manta-util/rayon"] # Serde Serialization serde = [ + "manta-crypto/serde", "manta-util/serde", "manta-util/serde-alloc", "manta-util/serde-array" @@ -50,9 +57,16 @@ ark-poly = { version = "0.3.0", default-features = false } ark-std = { version = "0.3.0", default-features = false } bincode = { version = "1.3.3", optional = true, default-features = false } blake2 = { version = "0.10.4", default-features = false } +bs58 = { version = "0.4", default-features = false, features = ["alloc"] } +colored = { version = "2.0.0", default-features = false } +console = { version = "0.15.1", default-features = false } +csv = { version = "1.1.6", optional = true, default-features = false } derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } -manta-crypto = { path = "../manta-crypto", default-features = false, features = ["arkworks", "getrandom", "rand_chacha"] } -manta-util = { path = "../manta-util", default-features = false } +dialoguer = { version = "0.10.2", default-features = false } +manta-crypto = { path = "../manta-crypto", default-features = false, features = ["arkworks", "getrandom", "rand_chacha", "dalek"] } +manta-util = { path = "../manta-util", default-features = false, features = ["reqwest"] } +parking_lot = { version = "0.12.1", default-features = false } +tiny-bip39 = { version = "1.0.0", default-features = false } [dev-dependencies] ark-snark = { version = "0.3.0", default-features = false } diff --git a/manta-trusted-setup/src/ceremony/mod.rs b/manta-trusted-setup/src/ceremony/mod.rs new file mode 100644 index 000000000..1a2a759bd --- /dev/null +++ b/manta-trusted-setup/src/ceremony/mod.rs @@ -0,0 +1,25 @@ +// Copyright 2019-2022 Manta Network. +// This file is part of manta-rs. +// +// manta-rs is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// manta-rs is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with manta-rs. If not, see . + +//! Trusted Setup Ceremonies + +pub mod participant; +pub mod registry; +pub mod signature; + +#[cfg(all(feature = "bincode", feature = "std"))] +#[cfg_attr(doc_cfg, doc(cfg(all(feature = "bincode", feature = "std"))))] +pub mod util; diff --git a/manta-trusted-setup/src/ceremony/participant.rs b/manta-trusted-setup/src/ceremony/participant.rs new file mode 100644 index 000000000..77c325df4 --- /dev/null +++ b/manta-trusted-setup/src/ceremony/participant.rs @@ -0,0 +1,59 @@ +// Copyright 2019-2022 Manta Network. +// This file is part of manta-rs. +// +// manta-rs is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// manta-rs is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with manta-rs. If not, see . + +//! Trusted Setup Ceremony Participants + +/// Participant +pub trait Participant { + /// Identifier Type + type Identifier; + + /// Verifying Key Type + type VerifyingKey; + + /// Nonce Type + type Nonce; + + /// Returns the [`Identifier`](Self::Identifier) for `self`. + fn id(&self) -> &Self::Identifier; + + /// Returns the [`VerifyingKey`](Self::VerifyingKey) for `self`. + fn verifying_key(&self) -> &Self::VerifyingKey; + + /// Checks if the participant has contributed. + fn has_contributed(&self) -> bool; + + /// Sets contributed. + fn set_contributed(&mut self); + + /// Returns the current nonce for `self`. + fn nonce(&self) -> &Self::Nonce; + + /// Increments the current nonce of `self` by one. + fn increment_nonce(&mut self); +} + +/// Priority +pub trait Priority { + /// Priority Type + type Priority; + + /// Returns the priority level for `self`. + fn priority(&self) -> Self::Priority; + + /// Reduces the priority. + fn reduce_priority(&mut self); +} diff --git a/manta-trusted-setup/src/ceremony/registry/csv.rs b/manta-trusted-setup/src/ceremony/registry/csv.rs new file mode 100644 index 000000000..ffa2d2ee7 --- /dev/null +++ b/manta-trusted-setup/src/ceremony/registry/csv.rs @@ -0,0 +1,66 @@ +// Copyright 2019-2022 Manta Network. +// This file is part of manta-rs. +// +// manta-rs is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// manta-rs is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with manta-rs. If not, see . + +//! Trusted Setup Ceremony Registry CSV Compatibility + +use crate::ceremony::registry::Registry; +use manta_util::serde::de::DeserializeOwned; +use std::{fs::File, path::Path}; + +/// CSV Record +pub trait Record: DeserializeOwned { + /// Error Type + type Error; + + /// Parses a registry entry of type `(I, V)` from `self`. + fn parse(self) -> Result<(I, V), Self::Error>; +} + +/// Record Error +#[derive(Debug)] +pub enum Error { + /// Parsing Error + Parse(E), + + /// CSV Reading Error + Csv(csv::Error), +} + +impl From for Error +where + T: Into, +{ + #[inline] + fn from(err: T) -> Self { + Self::Csv(err.into()) + } +} + +/// Loads a registry of type `R` from `path` using `T` as the record type. +#[inline] +pub fn load(path: P) -> Result> +where + T: Record, + R: Registry, + P: AsRef, +{ + let mut registry = R::new(); + for record in csv::Reader::from_reader(File::open(path)?).deserialize() { + let (identifier, participant) = T::parse(record?).map_err(Error::Parse)?; + registry.insert(identifier, participant); + } + Ok(registry) +} diff --git a/manta-trusted-setup/src/groth16/ceremony/registry.rs b/manta-trusted-setup/src/ceremony/registry/mod.rs similarity index 70% rename from manta-trusted-setup/src/groth16/ceremony/registry.rs rename to manta-trusted-setup/src/ceremony/registry/mod.rs index 7637086d2..2286d4c39 100644 --- a/manta-trusted-setup/src/groth16/ceremony/registry.rs +++ b/manta-trusted-setup/src/ceremony/registry/mod.rs @@ -14,21 +14,24 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see . -//! Groth16 Trusted Setup Ceremony Registry +//! Trusted Setup Ceremony Registry + +#[cfg(feature = "csv")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "csv")))] +pub mod csv; /// Participant Registry pub trait Registry { - /// Registers the `participant` into `self` returning `false` if the `participant` is already - /// registered or their registration would conflict with another existing participant. - fn register(&mut self, participant: P) -> bool; + /// Builds a new [`Registry`]. + fn new() -> Self; + + /// Registers the `id` and `participant` into `self` returning `false` if the `participant` is + /// already registered or their registration would conflict with another existing participant. + fn insert(&mut self, id: I, participant: P) -> bool; /// Returns a shared reference to the participant with the given `id` if they are registered. fn get(&self, id: &I) -> Option<&P>; /// Returns a mutable reference to the participant with the given `id` if they are registered. fn get_mut(&mut self, id: &I) -> Option<&mut P>; - - /// Returns `true` if the participant with the given `id` has already contributed to the - /// ceremony. - fn has_contributed(&self, id: &I) -> bool; } diff --git a/manta-trusted-setup/src/ceremony/signature.rs b/manta-trusted-setup/src/ceremony/signature.rs new file mode 100644 index 000000000..753c36f4a --- /dev/null +++ b/manta-trusted-setup/src/ceremony/signature.rs @@ -0,0 +1,379 @@ +// Copyright 2019-2022 Manta Network. +// This file is part of manta-rs. +// +// manta-rs is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// manta-rs is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with manta-rs. If not, see . + +//! Trusted Setup Ceremony Signatures + +use alloc::vec::Vec; +use manta_crypto::{ + dalek::ed25519::{Ed25519, SignatureError}, + signature, +}; +use manta_util::AsBytes; + +#[cfg(feature = "serde")] +use manta_util::serde::{Deserialize, Serialize}; + +/// Nonce +pub trait Nonce: Default + PartialEq { + /// Increments the current nonce by one. + fn increment(&mut self); + + /// Checks if the current nonce is valid. + fn is_valid(&self) -> bool; + + /// Checks that `self` and `rhs` are valid and are both equal. + #[inline] + fn matches(&self, rhs: &Self) -> bool { + self.is_valid() && rhs.is_valid() && self == rhs + } +} + +impl Nonce for u64 { + #[inline] + fn increment(&mut self) { + *self = self.saturating_add(1); + } + + #[inline] + fn is_valid(&self) -> bool { + *self != Self::MAX + } +} + +/// Message with Nonce +#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] +pub struct RawMessage { + /// Nonce + nonce: N, + + /// Encoded Message + encoded_message: Vec, +} + +impl RawMessage { + /// Builds a new [`RawMessage`] from `nonce` and `encoded_message`. + #[inline] + pub fn new(nonce: N, encoded_message: Vec) -> Self { + Self { + nonce, + encoded_message, + } + } +} + +impl AsBytes for RawMessage +where + N: AsBytes, +{ + #[inline] + fn as_bytes(&self) -> Vec { + let mut bytes = self.nonce.as_bytes(); + bytes.extend_from_slice(&self.encoded_message); + bytes + } +} + +/// Signature Scheme +pub trait SignatureScheme: + Default + + signature::Sign, Randomness = ()> + + signature::Verify> +{ + /// Message Nonce Type + type Nonce: Clone + Nonce; + + /// Verification Error Type + type Error; +} + +impl SignatureScheme for Ed25519> +where + N: AsBytes + Clone + Default + Nonce, +{ + type Nonce = N; + type Error = SignatureError; +} + +/// Signs the `message` with the `nonce` attached using the `signing_key`. +#[cfg(feature = "bincode")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "bincode")))] +#[inline] +pub fn sign( + signing_key: &S::SigningKey, + nonce: S::Nonce, + message: &T, +) -> Result +where + S: SignatureScheme, + T: Serialize, +{ + Ok(S::default().sign( + signing_key, + &(), + &RawMessage::new(nonce, bincode::serialize(message)?), + &mut (), + )) +} + +/// Verification Error +#[cfg(feature = "bincode")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "bincode")))] +#[derive(Debug)] +pub enum VerificationError { + /// Serialization + Serialization(bincode::Error), + + /// Base Verification Error Type + Error(E), +} + +#[cfg(feature = "bincode")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "bincode")))] +impl From for VerificationError { + #[inline] + fn from(err: bincode::Error) -> Self { + Self::Serialization(err) + } +} + +/// Verifies the `signature` of `message` with `nonce` attached using `verifying_key`. +#[cfg(feature = "bincode")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "bincode")))] +#[inline] +pub fn verify( + verifying_key: &S::VerifyingKey, + nonce: S::Nonce, + message: &T, + signature: &S::Signature, +) -> Result<(), VerificationError> +where + S: SignatureScheme, + T: Serialize, +{ + S::default() + .verify( + verifying_key, + &RawMessage::new(nonce, bincode::serialize(message)?), + signature, + &mut (), + ) + .map_err(VerificationError::Error) +} + +/// Signed Message +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = r" + S::Signature: Deserialize<'de>, + I: Deserialize<'de>, + T: Deserialize<'de>, + ", + serialize = r" + S::Signature: Serialize, + I: Serialize, + T: Serialize, + ", + ), + crate = "manta_util::serde", + deny_unknown_fields, + ) +)] +pub struct SignedMessage +where + S: SignatureScheme, +{ + /// Signature + signature: S::Signature, + + /// Participant Identifier + identifier: I, + + /// Message + message: T, +} + +impl SignedMessage +where + S: SignatureScheme, +{ + /// Builds a new [`SignedMessage`] without checking that the `signature` actually attests to the + /// `message`. + #[inline] + pub fn new_unchecked(signature: S::Signature, identifier: I, message: T) -> Self { + Self { + signature, + identifier, + message, + } + } + + /// Generates a signed message with `signing_key` on `message` and `nonce`. + #[cfg(feature = "bincode")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "bincode")))] + #[inline] + pub fn generate( + signing_key: &S::SigningKey, + nonce: S::Nonce, + identifier: I, + message: T, + ) -> Result + where + T: Serialize, + { + Ok(Self::new_unchecked( + sign::(signing_key, nonce, &message)?, + identifier, + message, + )) + } + + /// Returns a shared reference to the signature for this message. + #[inline] + pub fn signature(&self) -> &S::Signature { + &self.signature + } + + /// Returns a shared reference to the identifier for this message. + #[inline] + pub fn identifier(&self) -> &I { + &self.identifier + } + + /// Returns a shared reference the underlying message. + #[inline] + pub fn message(&self) -> &T { + &self.message + } + + /// Verifies `self` against the `verifying_key`. + #[cfg(feature = "bincode")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "bincode")))] + #[inline] + pub fn verify( + &self, + nonce: S::Nonce, + verifying_key: &S::VerifyingKey, + ) -> Result<(), VerificationError> + where + T: Serialize, + { + verify::(verifying_key, nonce, &self.message, &self.signature) + } + + /// Converts `self` into its underlying identifier. + #[inline] + pub fn into_identifier(self) -> I { + self.identifier + } + + /// Converts `self` into its underlying message. + #[inline] + pub fn into_message(self) -> T { + self.message + } + + /// Converts `self` into its identifier and message. + #[inline] + pub fn into_inner(self) -> (I, T) { + (self.identifier, self.message) + } +} + +/// Signer +pub struct Signer +where + S: SignatureScheme, +{ + /// Nonce + nonce: S::Nonce, + + /// Signing Key + signing_key: S::SigningKey, + + /// Identifier + identifier: I, +} + +impl Signer +where + S: SignatureScheme, +{ + /// Builds a new [`Signer`] from `nonce`, `signing_key`, and `identifier`. + #[inline] + pub fn new(nonce: S::Nonce, signing_key: S::SigningKey, identifier: I) -> Self { + Self { + nonce, + signing_key, + identifier, + } + } + + /// Returns the nonce for `self`. + #[inline] + pub fn nonce(&self) -> &S::Nonce { + &self.nonce + } + + /// Increments the nonce for `self`. + #[inline] + pub fn increment_nonce(&mut self) { + self.nonce.increment() + } + + /// Sets the current nonce for `self` to `nonce` if it is a valid nonce. If `nonce` is valid, + /// `true` is returned and `false` otherwise. + #[inline] + pub fn set_valid_nonce(&mut self, nonce: S::Nonce) -> bool { + if nonce.is_valid() { + self.nonce = nonce; + true + } else { + false + } + } + + /// Returns the signing key for `self`. + #[inline] + pub fn signing_key(&self) -> &S::SigningKey { + &self.signing_key + } + + /// Returns the identifier for `self`. + #[inline] + pub fn identifier(&self) -> &I { + &self.identifier + } + + /// Signs `message` using the internal signing key and + #[cfg(feature = "bincode")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "bincode")))] + #[inline] + pub fn sign(&self, message: T) -> Result, bincode::Error> + where + I: Clone, + T: Serialize, + { + SignedMessage::generate( + &self.signing_key, + self.nonce.clone(), + self.identifier.clone(), + message, + ) + } +} diff --git a/manta-trusted-setup/src/ceremony/util.rs b/manta-trusted-setup/src/ceremony/util.rs new file mode 100644 index 000000000..f347ea09f --- /dev/null +++ b/manta-trusted-setup/src/ceremony/util.rs @@ -0,0 +1,47 @@ +// Copyright 2019-2022 Manta Network. +// This file is part of manta-rs. +// +// manta-rs is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// manta-rs is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with manta-rs. If not, see . + +//! Trusted Setup Ceremony Utilities + +use manta_util::serde::{de::DeserializeOwned, Serialize}; +use std::{ + fs::{File, OpenOptions}, + path::Path, +}; + +/// Serializes `data` to a file at `path` with the given `open_options`. +#[inline] +pub fn serialize_into_file( + open_options: &mut OpenOptions, + path: &P, + data: &T, +) -> bincode::Result<()> +where + P: AsRef, + T: Serialize, +{ + bincode::serialize_into(open_options.open(path)?, data) +} + +/// Deserializes an element of type `T` from the file at `path`. +#[inline] +pub fn deserialize_from_file(path: P) -> bincode::Result +where + P: AsRef, + T: DeserializeOwned, +{ + bincode::deserialize_from(File::open(path)?) +} diff --git a/manta-trusted-setup/src/groth16/ceremony/client.rs b/manta-trusted-setup/src/groth16/ceremony/client.rs new file mode 100644 index 000000000..d8b1c7133 --- /dev/null +++ b/manta-trusted-setup/src/groth16/ceremony/client.rs @@ -0,0 +1,266 @@ +// Copyright 2019-2022 Manta Network. +// This file is part of manta-rs. +// +// manta-rs is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// manta-rs is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with manta-rs. If not, see . + +//! Trusted Setup Client + +use crate::{ + ceremony::signature::{SignedMessage, Signer}, + groth16::{ + ceremony::{ + message::{ContributeRequest, QueryRequest, QueryResponse}, + Ceremony, CeremonyError, Metadata, Round, UnexpectedError, + }, + mpc, + }, +}; +use alloc::vec::Vec; +use manta_crypto::rand::OsRng; +use manta_util::{ + http::reqwest::{self, IntoUrl, KnownUrlClient}, + serde::{de::DeserializeOwned, Serialize}, +}; + +/// Converts the [`reqwest`] error `err` into a [`CeremonyError`] depending on whether it comes from +/// a timeout or other network error. +#[inline] +fn into_ceremony_error(err: reqwest::Error) -> CeremonyError +where + C: Ceremony, +{ + if err.is_timeout() { + CeremonyError::Timeout + } else { + CeremonyError::Network + } +} + +/// Client Update States +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum Update { + /// Position Updated + Position(u64), + + /// Timeout + Timeout, +} + +/// Client +pub struct Client +where + C: Ceremony, +{ + /// Signer + signer: Signer, + + /// HTTP Client + client: KnownUrlClient, + + /// Ceremony Metadata + metadata: Metadata, +} + +impl Client +where + C: Ceremony, +{ + /// Builds a new [`Client`] from `signer`, `client`, and `ceremony_size`. + #[inline] + fn new_unchecked( + signer: Signer, + client: KnownUrlClient, + metadata: Metadata, + ) -> Self { + Self { + signer, + client, + metadata, + } + } + + /// Updates the client's nonce to the `expected_nonce` returned by the server. + #[inline] + fn update_nonce(&mut self, expected_nonce: C::Nonce) -> Result<(), CeremonyError> { + self.signer + .set_valid_nonce(expected_nonce) + .then_some(()) + .ok_or(CeremonyError::Unexpected(UnexpectedError::AllNoncesUsed)) + } + + /// Signs the `message` with the signer in `self`, incrementing its nonce if the signing was + /// successful. + #[inline] + fn sign( + &mut self, + message: T, + ) -> Result, CeremonyError> + where + T: Serialize, + { + let signed_message = self + .signer + .sign(message) + .map_err(|_| CeremonyError::Unexpected(UnexpectedError::Serialization))?; + self.signer.increment_nonce(); + Ok(signed_message) + } + + /// Builds a new [`Client`] from `signing_key`, `identifier`, and `client` and performs the + /// initial synchronization procedure with the ceremony server to establish the correct ceremony + /// parameters and registration status. + #[inline] + pub async fn build( + signing_key: C::SigningKey, + identifier: C::Identifier, + client: KnownUrlClient, + ) -> Result> + where + C::Identifier: Serialize, + C::Nonce: DeserializeOwned, + { + let (metadata, nonce) = client + .post("start", &identifier) + .await + .map_err(into_ceremony_error)?; + Ok(Self::new_unchecked( + Signer::new(nonce, signing_key, identifier), + client, + metadata, + )) + } + + /// Queries for the state of the ceremony, returning the queue position if the participant is + /// not at the front of the queue. + #[inline] + async fn query(&mut self) -> Result, CeremonyError> + where + C::Identifier: Serialize, + C::Nonce: Serialize, + C::Signature: Serialize, + QueryResponse: DeserializeOwned, + { + let signed_message = self.sign(QueryRequest)?; + match self.client.post("query", &signed_message).await { + Ok(QueryResponse::State(state)) => match state.with_valid_shape() { + Some(state) if self.metadata.ceremony_size.matches(&state.state) => { + Ok(QueryResponse::State(state)) + } + _ => Err(CeremonyError::Unexpected( + UnexpectedError::IncorrectStateSize, + )), + }, + Ok(response) => Ok(response), + Err(err) => Err(into_ceremony_error(err)), + } + } + + /// Performs the ceremony contribution over `round`, sending the result to the ceremony server. + #[inline] + async fn contribute( + &mut self, + hasher: &C::Hasher, + mut round: Round, + ) -> Result<(), CeremonyError> + where + C::Identifier: Serialize, + C::Nonce: Serialize, + C::Signature: Serialize, + ContributeRequest: Serialize, + { + let mut rng = OsRng; + let mut proof = Vec::new(); + for i in 0..round.state.len() { + proof.push( + mpc::contribute(hasher, &round.challenge[i], &mut round.state[i], &mut rng).ok_or( + CeremonyError::Unexpected(UnexpectedError::FailedContribution), + )?, + ); + } + let signed_message = self.sign(ContributeRequest { + state: round.state.into(), + proof, + })?; + self.client + .post("update", &signed_message) + .await + .map_err(into_ceremony_error) + } + + /// Tries to contribute to the ceremony if at the front of the queue. This method returns an + /// optional [`Update`] if the status of the unfinalized participant has changed. If the result + /// is `Ok(None)` then the ceremony contribution was successful. + #[inline] + pub async fn try_contribute(&mut self) -> Result, CeremonyError> + where + C::Identifier: Serialize, + C::Nonce: Serialize, + C::Signature: Serialize, + ContributeRequest: Serialize, + QueryResponse: DeserializeOwned, + { + let state = match self.query().await { + Ok(QueryResponse::State(state)) => state, + Ok(QueryResponse::QueuePosition(position)) => { + return Ok(Some(Update::Position(position))) + } + Err(CeremonyError::Timeout) => return Ok(Some(Update::Timeout)), + Err(err) => return Err(err), + }; + match self.contribute(&C::Hasher::default(), state).await { + Ok(_) => Ok(None), + Err(CeremonyError::Timeout) | Err(CeremonyError::NotYourTurn) => { + Ok(Some(Update::Timeout)) + } + Err(err) => Err(err), + } + } +} + +/// Runs the contribution protocol for `signing_key`, `identifier`, and `server_url`, using +/// `process_update` as the callback for processing [`Update`] messages from the client. +#[inline] +pub async fn contribute( + signing_key: C::SigningKey, + identifier: C::Identifier, + server_url: U, + mut process_update: F, +) -> Result<(), CeremonyError> +where + C: Ceremony, + C::Identifier: Serialize, + C::Nonce: DeserializeOwned + Serialize, + C::Signature: Serialize, + ContributeRequest: Serialize, + QueryResponse: DeserializeOwned, + U: IntoUrl, + F: FnMut(Update), +{ + let mut client = Client::build( + signing_key, + identifier, + KnownUrlClient::new(server_url).map_err(into_ceremony_error)?, + ) + .await?; + loop { + match client.try_contribute().await { + Ok(Some(update)) => process_update(update), + Ok(None) => return Ok(()), + Err(CeremonyError::InvalidSignature { expected_nonce }) => { + client.update_nonce(expected_nonce)?; + } + Err(err) => return Err(err), + } + } +} diff --git a/manta-trusted-setup/src/groth16/ceremony/config/mod.rs b/manta-trusted-setup/src/groth16/ceremony/config/mod.rs new file mode 100644 index 000000000..6a766d741 --- /dev/null +++ b/manta-trusted-setup/src/groth16/ceremony/config/mod.rs @@ -0,0 +1,21 @@ +// Copyright 2019-2022 Manta Network. +// This file is part of manta-rs. +// +// manta-rs is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// manta-rs is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with manta-rs. If not, see . + +//! Groth16 Trusted Setup Ceremony Configurations + +#[cfg(feature = "csv")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "csv")))] +pub mod ppot; diff --git a/manta-trusted-setup/src/groth16/ceremony/config/ppot.rs b/manta-trusted-setup/src/groth16/ceremony/config/ppot.rs new file mode 100644 index 000000000..17b1776a4 --- /dev/null +++ b/manta-trusted-setup/src/groth16/ceremony/config/ppot.rs @@ -0,0 +1,387 @@ +// Copyright 2019-2022 Manta Network. +// This file is part of manta-rs. +// +// manta-rs is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// manta-rs is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with manta-rs. If not, see . + +//! Groth16 Trusted Setup Ceremony Perpetual Powers of Tau Configuration + +use crate::{ + ceremony::{ + participant, + registry::csv, + signature::{sign, verify, Nonce as _, RawMessage, SignatureScheme}, + }, + groth16::ceremony::{ + client::{self, Update}, + Ceremony, CeremonyError, + }, +}; +use bip39::{Language, Mnemonic, MnemonicType, Seed}; +use colored::Colorize; +use console::{style, Term}; +use core::fmt::Debug; +use dialoguer::{theme::ColorfulTheme, Input}; +use manta_crypto::{ + dalek::ed25519::{self, generate_keypair, Ed25519, SECRET_KEY_LENGTH}, + rand::{ChaCha20Rng, OsRng, Rand, SeedableRng}, + signature::VerifyingKeyType, +}; +use manta_util::serde::{de::DeserializeOwned, Deserialize, Serialize}; + +type Signature = Ed25519>; +type VerifyingKey = ::VerifyingKey; +type Nonce = ::Nonce; + +/// Priority +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[serde( + bound(deserialize = "", serialize = ""), + crate = "manta_util::serde", + deny_unknown_fields +)] +pub enum Priority { + /// High Priority + High, + + /// Normal Priority + Normal, +} + +impl From for usize { + #[inline] + fn from(priority: Priority) -> Self { + match priority { + Priority::High => 0, + Priority::Normal => 1, + } + } +} + +/// Participant +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde( + bound( + deserialize = r" + VerifyingKey: Deserialize<'de>, + Nonce: Deserialize<'de>, + ", + serialize = r" + VerifyingKey: Serialize, + Nonce: Serialize, + " + ), + crate = "manta_util::serde", + deny_unknown_fields +)] +pub struct Participant { + /// Twitter Account + twitter: String, + + /// Priority + priority: Priority, + + /// Verifying Key + verifying_key: VerifyingKey, + + /// Nonce + nonce: Nonce, + + /// Boolean on whether this participant has contributed + contributed: bool, +} + +impl Participant { + /// Builds a new [`Participant`]. + #[inline] + pub fn new( + verifying_key: VerifyingKey, + twitter: String, + priority: Priority, + nonce: Nonce, + contributed: bool, + ) -> Self { + Self { + verifying_key, + twitter, + priority, + nonce, + contributed, + } + } + + /// Gets `twitter`. + #[inline] + pub fn twitter(&self) -> &str { + &self.twitter + } +} + +impl participant::Participant for Participant { + type Identifier = VerifyingKey; + type VerifyingKey = VerifyingKey; + type Nonce = Nonce; + + #[inline] + fn id(&self) -> &Self::Identifier { + &self.verifying_key + } + + #[inline] + fn verifying_key(&self) -> &Self::VerifyingKey { + &self.verifying_key + } + + #[inline] + fn has_contributed(&self) -> bool { + self.contributed + } + + #[inline] + fn set_contributed(&mut self) { + self.contributed = true + } + + #[inline] + fn nonce(&self) -> &Self::Nonce { + &self.nonce + } + + #[inline] + fn increment_nonce(&mut self) { + self.nonce.increment(); + } +} + +impl participant::Priority for Participant { + type Priority = Priority; + + #[inline] + fn priority(&self) -> Self::Priority { + self.priority + } + + #[inline] + fn reduce_priority(&mut self) { + self.priority = Priority::Normal; + } +} + +/// Record +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[serde( + bound(deserialize = "", serialize = ""), + crate = "manta_util::serde", + deny_unknown_fields +)] +pub struct Record { + /// Twitter Account + twitter: String, + + /// Email Account + email: String, + + /// Priority Level + priority: String, + + /// Verifying Key + verifying_key: String, + + /// Signature + signature: String, +} + +impl csv::Record for Record { + type Error = String; + + #[inline] + fn parse(self) -> Result<(VerifyingKey, Participant), Self::Error> { + let verifying_key = ed25519::public_key_from_bytes( + bs58::decode(self.verifying_key) + .into_vec() + .map_err(|_| "Cannot decode verifying key.".to_string())? + .try_into() + .map_err(|_| "Cannot decode to array.".to_string())?, + ); + let signature: ed25519::Signature = ed25519::signature_from_bytes( + bs58::decode(self.signature) + .into_vec() + .map_err(|_| "Cannot decode signature.".to_string())? + .try_into() + .map_err(|_| "Cannot decode to array.".to_string())?, + ); + verify::( + &verifying_key, + 0, + &format!( + "manta-trusted-setup-twitter:{}, manta-trusted-setup-email:{}", + self.twitter, self.email + ), + &signature, + ) + .map_err(|_| "Cannot verify signature.".to_string())?; + Ok(( + verifying_key, + Participant::new( + verifying_key, + self.twitter, + match self + .priority + .parse::() + .map_err(|_| "Cannot parse priority.".to_string())? + { + true => Priority::High, + false => Priority::Normal, + }, + OsRng.gen::<_, u16>() as u64, + false, + ), + )) + } +} + +/// Generates an ed25519 keypair with `bytes` as seed. +#[inline] +pub fn generate_keys(bytes: &[u8]) -> Option<(ed25519::SecretKey, ed25519::PublicKey)> { + if ed25519::SECRET_KEY_LENGTH > bytes.len() { + return None; + } + let keypair = generate_keypair(&mut ChaCha20Rng::from_seed( + bytes[0..SECRET_KEY_LENGTH].try_into().ok()?, + )); + Some((keypair.secret, keypair.public)) +} + +/// Registers a participant. +#[inline] +pub fn register(twitter_account: String, email: String) { + println!( + "Your {}: \nCopy the following text to \"Twitter\" Section in Google Sheet:\n {}\n", + "Twitter Account".italic(), + twitter_account.blue(), + ); + println!( + "Your {}: \nCopy the following text to \"Email\" Section in Google Sheet:\n {}\n", + "Email".italic(), + email.blue(), + ); + let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English); + let seed = Seed::new(&mnemonic, "manta-trusted-setup"); + let keypair = generate_keys(seed.as_bytes()).expect("Should generate a key pair."); + println!( + "Your {}: \nCopy the following text to \"Public Key\" Section in Google Sheet:\n {}\n", + "Public Key".italic(), + bs58::encode(keypair.1).into_string().blue(), + ); + let signature = sign::>, _>( + &keypair.0, + Default::default(), + &format!( + "manta-trusted-setup-twitter:{}, manta-trusted-setup-email:{}", + twitter_account, email + ), + ) + .expect("Signing message should succeed."); + println!( + "Your {}: \nCopy the following text to \"Signature\" Section in Google Sheet: \n {}\n", + "Signature".italic(), + bs58::encode(signature).into_string().blue() + ); + println!( + "Your {}: \nThe following text stores your secret for trusted setup. \ + Save the following text somewhere safe. \n DO NOT share this to anyone else! \ + Please discard this data after the trusted setup ceremony.\n {}", + "Secret".italic(), + mnemonic.phrase().red(), + ); +} + +/// Prompts the client information and get client keys. +#[inline] +pub fn get_client_keys() -> Option<(ed25519::SecretKey, ed25519::PublicKey)> { + println!( + "Please enter the {} you received when you registered yourself using this tool.", + "Secret".italic() + ); + let seed_bytes = Seed::new( + &Mnemonic::from_phrase( + Input::with_theme(&ColorfulTheme::default()) + .with_prompt("Your Secret") + .validate_with(|input: &String| -> Result<(), &str> { + Mnemonic::validate(input, Language::English) + .map_err(|_| "This is not a valid secret.") + }) + .interact_text() + .expect("Please enter your secret received during `Register`.") + .as_str(), + Language::English, + ) + .expect("Should produce a mnemonic from the secret."), + "manta-trusted-setup", + ) + .as_bytes() + .to_vec(); + generate_keys(&seed_bytes) +} + +/// Contributes to the server. +#[inline] +pub async fn client_contribute( + signing_key: C::SigningKey, + identifier: C::Identifier, +) -> Result<(), CeremonyError> +where + C: Ceremony, + C::Challenge: DeserializeOwned, + C::Identifier: Serialize, + C::Nonce: Clone + Debug + DeserializeOwned + Serialize, + C::Signature: Serialize, +{ + const LOCK_TIME: u64 = 5; + let term = Term::stdout(); + client::contribute( + signing_key, + identifier, + "http://localhost:8080", + |state| match state { + Update::Timeout => { + let _ = term.clear_last_lines(1); + println!("You have timed out. Waiting in queue again ..."); + }, + Update::Position(position) => { + let _ = term.clear_last_lines(1); + println!( + "Waiting in queue... There are {} people ahead of you. Estimated Waiting Time: {} minutes.", + style(position).bold().red(), + style(LOCK_TIME * position).bold().blue(), + ); + }, + }, + ) + .await +} + +/// Testing Suite +#[cfg(test)] +mod test { + use super::*; + + /// Tests if register is visually correct. + #[test] + fn register_is_visually_correct() { + register( + "Mantalorian".to_string(), + "mantalorian@manta.network".to_string(), + ); + } +} diff --git a/manta-trusted-setup/src/groth16/ceremony/coordinator.rs b/manta-trusted-setup/src/groth16/ceremony/coordinator.rs index dcd4f9de7..f3b7b7e76 100644 --- a/manta-trusted-setup/src/groth16/ceremony/coordinator.rs +++ b/manta-trusted-setup/src/groth16/ceremony/coordinator.rs @@ -14,57 +14,41 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see . -//! Groth16 Trusted Setup Ceremony Coordinator +//! Coordinator use crate::{ + ceremony::{ + participant::{Participant, Priority}, + registry::Registry, + signature::{Nonce, SignedMessage}, + }, groth16::{ - ceremony::{registry::Registry, Ceremony, Participant}, - mpc::StateSize, + ceremony::{Ceremony, CeremonyError, Metadata, Queue, Round, UnexpectedError}, + mpc::{verify_transform, Proof, State}, }, - mpc::{Challenge, Proof, State}, }; -use manta_util::{collections::vec_deque::MultiVecDeque, time::lock::Timed, Array, BoxArray}; +use core::mem; +use manta_util::{time::lock::Timed, BoxArray}; #[cfg(feature = "serde")] use manta_util::serde::{Deserialize, Serialize}; -/// Proof Array Type -pub type ProofArray = BoxArray, N>; - -/// State Array Type -pub type StateArray = BoxArray, N>; - -/// Challenge Array Type -pub type ChallengeArray = BoxArray, N>; - -/// Participant Queue Type -pub type Queue = - MultiVecDeque<::Identifier, LEVEL_COUNT>; - /// Ceremony Coordinator #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), serde( bound( - deserialize = r" - R: Deserialize<'de>, - Queue: Deserialize<'de>, - C::Identifier: Deserialize<'de>, - State: Deserialize<'de>, - Challenge: Deserialize<'de>, - Proof: Deserialize<'de>, - C::Participant: Deserialize<'de>, - ", serialize = r" - R: Serialize, - Queue: Serialize, - C::Identifier: Serialize, - State: Serialize, - Challenge: Serialize, - Proof: Serialize, - C::Participant: Serialize, - " + R: Serialize, + C::Challenge: Serialize, + C::Participant: Serialize, + ", + deserialize = r" + R: Deserialize<'de>, + C::Challenge: Deserialize<'de>, + C::Participant: Deserialize<'de>, + " ), crate = "manta_util::serde", deny_unknown_fields @@ -76,33 +60,35 @@ where R: Registry, { /// Participant Registry - pub registry: R, - - /// Participant Queue - pub queue: Queue, - - /// Participant Lock - pub participant_lock: Timed>, + registry: R, /// State - pub state: StateArray, + state: BoxArray, CIRCUIT_COUNT>, /// Challenge - pub challenge: ChallengeArray, + challenge: BoxArray, /// Latest Contributor /// /// This participant was the last one to perform a successful contribution to the ceremony. - pub latest_contributor: Option, + latest_contributor: Option, /// Latest Proof - pub latest_proof: Option>, + latest_proof: Option, CIRCUIT_COUNT>>, - /// State Sizes - pub size: Array, + /// Ceremony Metadata + metadata: Metadata, /// Current Round Number - pub round: usize, + round: usize, + + /// Participant Queue + #[cfg_attr(feature = "serde", serde(skip))] + queue: Queue, + + /// Participant Lock + #[cfg_attr(feature = "serde", serde(skip))] + participant_lock: Timed>, } impl @@ -111,12 +97,56 @@ where C: Ceremony, R: Registry, { + /// Builds a new [`Coordinator`]. + #[inline] + pub fn new( + registry: R, + state: BoxArray, CIRCUIT_COUNT>, + challenge: BoxArray, + metadata: Metadata, + ) -> Self { + assert!( + metadata.ceremony_size.matches(state.as_slice()), + "Mismatch of metadata `{:?}` and state.", + metadata, + ); + Self { + registry, + state, + challenge, + latest_contributor: None, + latest_proof: None, + metadata, + round: 0, + queue: Default::default(), + participant_lock: Default::default(), + } + } + /// Returns the current round number. #[inline] pub fn round(&self) -> usize { self.round } + /// Increments the round number. + #[inline] + pub fn increment_round(&mut self) { + self.round += 1; + } + + /// Returns the metadata for this ceremony. + #[inline] + pub fn metadata(&self) -> &Metadata { + &self.metadata + } + + /// Returns the registry. + #[inline] + pub fn registry(&self) -> &R { + &self.registry + } + /// Returns a shared reference to the participant data for `id` from the registry. #[inline] pub fn participant(&self, id: &C::Identifier) -> Option<&C::Participant> { @@ -129,16 +159,125 @@ where self.registry.get_mut(id) } - /// Returns the current position for a `participant` in the queue. + /// Returns a mutable reference to `queue`. + #[inline] + pub fn queue_mut(&mut self) -> &mut Queue { + &mut self.queue + } + + /// Returns the current round state. + #[inline] + pub fn round_state(&self) -> Round + where + C::Challenge: Clone, + { + Round::new(self.state.to_vec().into(), self.challenge.to_vec().into()) + } + + /// Preprocesses a request by checking the nonce and verifying the signature. #[inline] - pub fn position(&self, participant: &C::Participant) -> Option { - self.queue.position(participant.level(), participant.id()) + pub fn preprocess_request( + &mut self, + request: &SignedMessage, + ) -> Result> + where + T: Serialize, + { + let participant = self + .registry + .get_mut(request.identifier()) + .ok_or(CeremonyError::NotRegistered)?; + if participant.has_contributed() { + return Err(CeremonyError::AlreadyContributed); + } + let participant_nonce = participant.nonce(); + if !participant_nonce.is_valid() { + return Err(CeremonyError::Unexpected(UnexpectedError::AllNoncesUsed)); + } + request + .verify(participant_nonce.clone(), participant.verifying_key()) + .map_err(|_| CeremonyError::InvalidSignature { + expected_nonce: participant_nonce.clone(), + })?; + participant.increment_nonce(); + Ok(participant.priority()) } - /// Inserts `participant` into the queue. + /// Checks the lock update errors for the [`Coordinator::update`] method. + #[inline] + pub fn check_lock_update_errors( + has_expired: bool, + lhs: &Option, + rhs: &C::Identifier, + ) -> Result<(), CeremonyError> { + match lhs { + Some(lhs) if lhs == rhs && has_expired => Err(CeremonyError::Timeout), + Some(lhs) if lhs != rhs => Err(CeremonyError::NotYourTurn), + _ => Ok(()), + } + } + + /// Updates the expired lock by reducing the priority of its participant and setting its + /// contained value to the new front of the queue. The previous participant in the lock is + /// returned. + #[inline] + pub fn update_expired_lock(&mut self) -> Option { + self.participant_lock.mutate(|p| { + if let Some(identifier) = p { + if let Some(participant) = self.registry.get_mut(identifier) { + participant.reduce_priority(); + } + } + mem::replace(p, self.queue.pop_front()) + }) + } + + /// Checks the lock for `participant`. + #[inline] + pub fn check_lock(&mut self, participant: &C::Identifier) -> Result<(), CeremonyError> { + if self + .participant_lock + .has_expired(self.metadata.contribution_time_limit) + { + Self::check_lock_update_errors(true, &self.update_expired_lock(), participant) + } else { + Self::check_lock_update_errors(false, self.participant_lock.get(), participant) + } + } + + /// Updates the MPC state and challenge using client's contribution. If the contribution is + /// valid, the participant will be removed from the waiting queue, and cannot participate in + /// this ceremony again. + /// + /// # Registration + /// + /// This method requires that `participant` is already registered. #[inline] - pub fn insert_participant(&mut self, participant: &C::Participant) { - self.queue - .push_back_at(participant.level(), participant.id().clone()); + pub fn update( + &mut self, + participant: &C::Identifier, + state: BoxArray, CIRCUIT_COUNT>, + proof: BoxArray, CIRCUIT_COUNT>, + ) -> Result<(), CeremonyError> { + self.check_lock(participant)?; + for (i, (state, proof)) in state.into_iter().zip(proof.clone().into_iter()).enumerate() { + let next_challenge = C::challenge(&self.challenge[i], &self.state[i], &state, &proof); + self.state[i] = verify_transform(&self.challenge[i], &self.state[i], state, proof) + .map_err(|_| CeremonyError::BadRequest)? + .1; + self.challenge[i] = next_challenge; + } + self.latest_proof = Some(proof); + self.participant_lock.set(self.queue.pop_front()); + match self.participant_mut(participant) { + Some(participant) => participant.set_contributed(), + _ => { + return Err(CeremonyError::Unexpected( + UnexpectedError::MissingRegisteredParticipant, + )); + } + }; + self.increment_round(); + Ok(()) } } diff --git a/manta-trusted-setup/src/groth16/ceremony/message.rs b/manta-trusted-setup/src/groth16/ceremony/message.rs new file mode 100644 index 000000000..ab1385362 --- /dev/null +++ b/manta-trusted-setup/src/groth16/ceremony/message.rs @@ -0,0 +1,80 @@ +// Copyright 2019-2022 Manta Network. +// This file is part of manta-rs. +// +// manta-rs is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// manta-rs is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with manta-rs. If not, see . + +//! Groth16 Trusted Setup Ceremony Messaging Protocol + +use crate::groth16::{ + ceremony::{Ceremony, Round}, + mpc::{Proof, State}, +}; +use alloc::vec::Vec; + +#[cfg(feature = "serde")] +use manta_util::serde::{Deserialize, Serialize}; + +/// Query Request +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct QueryRequest; + +/// Response for [`QueryRequest`] +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "Round: Deserialize<'de>", + serialize = "Round: Serialize", + ), + crate = "manta_util::serde", + deny_unknown_fields + ) +)] +pub enum QueryResponse +where + C: Ceremony, +{ + /// Queue Position + QueuePosition(u64), + + /// MPC Round State + State(Round), +} + +/// Contribute Request +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound(deserialize = "", serialize = "",), + crate = "manta_util::serde", + deny_unknown_fields + ) +)] +pub struct ContributeRequest +where + C: Ceremony, +{ + /// State + pub state: Vec>, + + /// Proof + pub proof: Vec>, +} diff --git a/manta-trusted-setup/src/groth16/ceremony/mod.rs b/manta-trusted-setup/src/groth16/ceremony/mod.rs index d03db33d7..ec7089b00 100644 --- a/manta-trusted-setup/src/groth16/ceremony/mod.rs +++ b/manta-trusted-setup/src/groth16/ceremony/mod.rs @@ -16,39 +16,160 @@ //! Groth16 Trusted Setup Ceremony -use crate::mpc; +use crate::{ + ceremony::{ + participant::{Participant, Priority}, + signature::SignatureScheme, + }, + groth16::mpc::{Configuration, State, StateSize}, + mpc, +}; +use core::{fmt::Debug, time::Duration}; +use manta_crypto::arkworks::pairing::Pairing; +use manta_util::{ + collections::vec_deque::MultiVecDeque, + serde::{Deserialize, Serialize}, +}; -pub mod registry; +pub mod client; +pub mod config; +pub mod message; #[cfg(feature = "std")] #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] pub mod coordinator; -#[cfg(all(feature = "bincode", feature = "serde"))] -#[cfg_attr(doc_cfg, doc(cfg(all(feature = "bincode", feature = "serde"))))] -pub mod signature; - -/// Participant -pub trait Participant { - /// Participant Identifier Type - type Identifier; - - /// Returns the [`Identifier`](Self::Identifier) for `self`. - fn id(&self) -> &Self::Identifier; +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +pub mod server; - /// Returns the priority level for `self`. - /// - /// # Note - /// - /// Lower level indicates a higher priority. - fn level(&self) -> usize; -} +/// Participant Queue Type +pub type Queue = + MultiVecDeque<::Identifier, LEVEL_COUNT>; /// Ceremony Configuration -pub trait Ceremony: mpc::Types { +pub trait Ceremony: Configuration + SignatureScheme { /// Participant Identifier Type type Identifier: Clone + PartialEq; + /// Participant Priority Type + type Priority: Into; + /// Participant Type - type Participant: Participant; + type Participant: Participant< + Identifier = Self::Identifier, + VerifyingKey = Self::VerifyingKey, + Nonce = Self::Nonce, + > + Priority; +} + +/// Parallel Round Alias +/// +/// In the ceremony we always use parallel round structures to support multiple Groth16 circuits at +/// the same time. +pub type Round = mpc::ParallelRound; + +/// Ceremony Size Alias +/// +/// In the ceremony we always use parallel round structures to support multiple Groth16 circuits at +/// the same time. +pub type CeremonySize = mpc::Parallel; + +impl CeremonySize { + /// Checks that each size in `self` matches each [`State`] in `states`. + #[inline] + pub fn matches

(&self, states: &[State

]) -> bool + where + P: Pairing, + { + self.len() == states.len() && self.iter().zip(states).all(|(l, r)| l.matches(&r.0)) + } +} + +/// Ceremony Metadata +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] +#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] +pub struct Metadata { + /// Ceremony Size + pub ceremony_size: CeremonySize, + + /// Contribution Time Limit + pub contribution_time_limit: Duration, +} + +/// Ceremony Error +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + serialize = "C::Nonce: Serialize", + deserialize = "C::Nonce: Deserialize<'de>", + ), + crate = "manta_util::serde", + deny_unknown_fields + ) +)] +#[derive(derivative::Derivative)] +#[derivative(Debug(bound = "C::Nonce: Debug"))] +pub enum CeremonyError +where + C: Ceremony, +{ + /// Malformed request that should not come from official client + BadRequest, + + /// Invalid Signature + InvalidSignature { + /// Expected Nonce + /// + /// We also return the nonce here in case the client has gotten out of sync with the server. + expected_nonce: C::Nonce, + }, + + /// Not Registered + NotRegistered, + + /// Already Contributed + AlreadyContributed, + + /// Not Your Turn + NotYourTurn, + + /// Timed out + Timeout, + + /// Network Error + Network, + + /// Unexpected Server Error + Unexpected(UnexpectedError), +} + +/// Unexpected Error +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] +#[derive(Debug)] +pub enum UnexpectedError { + /// Serialization Error + Serialization, + + /// Failed to generate a valid Contribution + FailedContribution, + + /// Missing Registered Participant + MissingRegisteredParticipant, + + /// Incorrect State Size + IncorrectStateSize, + + /// All Nonces were Used + AllNoncesUsed, } diff --git a/manta-trusted-setup/src/groth16/ceremony/server.rs b/manta-trusted-setup/src/groth16/ceremony/server.rs new file mode 100644 index 000000000..0acfbc731 --- /dev/null +++ b/manta-trusted-setup/src/groth16/ceremony/server.rs @@ -0,0 +1,169 @@ +// Copyright 2019-2022 Manta Network. +// This file is part of manta-rs. +// +// manta-rs is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// manta-rs is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with manta-rs. If not, see . + +//! Trusted Setup Server + +use crate::{ + ceremony::{ + registry::Registry, + signature::SignedMessage, + util::{deserialize_from_file, serialize_into_file}, + }, + groth16::{ + ceremony::{ + coordinator::Coordinator, + message::{ContributeRequest, QueryRequest, QueryResponse}, + Ceremony, CeremonyError, Metadata, Participant as _, UnexpectedError, + }, + mpc::State, + }, +}; +use alloc::sync::Arc; +use core::ops::Deref; +use manta_util::{ + serde::{de::DeserializeOwned, Serialize}, + BoxArray, +}; +use parking_lot::Mutex; +use std::{ + fs::OpenOptions, + path::{Path, PathBuf}, +}; + +/// Server +pub struct Server +where + C: Ceremony, + R: Registry, +{ + /// Coordinator + coordinator: Arc>>, + + /// Recovery directory path + recovery_directory: PathBuf, +} + +impl + Server +where + C: Ceremony, + R: Registry, +{ + /// Builds a ['Server`] with initial `state`, `challenge`, a loaded `registry`, and a + /// `recovery_directory`. + #[inline] + pub fn new( + state: BoxArray, CIRCUIT_COUNT>, + challenge: BoxArray, + registry: R, + recovery_directory: PathBuf, + metadata: Metadata, + ) -> Self { + Self { + coordinator: Arc::new(Mutex::new(Coordinator::new( + registry, state, challenge, metadata, + ))), + recovery_directory, + } + } + + /// Recovers from a disk file at `path` and use `recovery_directory` as the backup directory. + #[inline] + pub fn recover

(path: P, recovery_directory: PathBuf) -> Result> + where + P: AsRef, + Coordinator: DeserializeOwned, + { + Ok(Self { + coordinator: Arc::new(Mutex::new( + deserialize_from_file(path) + .map_err(|_| CeremonyError::Unexpected(UnexpectedError::Serialization))?, + )), + recovery_directory, + }) + } + + /// Gets the server state size and the current nonce of the participant. + #[inline] + pub async fn start( + self, + request: C::Identifier, + ) -> Result<(Metadata, C::Nonce), CeremonyError> + where + C::Nonce: Clone, + { + let coordinator = self.coordinator.lock(); + Ok(( + coordinator.metadata().clone(), + coordinator + .registry() + .get(&request) + .ok_or(CeremonyError::NotRegistered)? + .nonce() + .clone(), + )) + } + + /// Queries the server state + #[inline] + pub async fn query( + self, + request: SignedMessage, + ) -> Result, CeremonyError> + where + C::Challenge: Clone, + { + let mut coordinator = self.coordinator.lock(); + let priority = coordinator.preprocess_request(&request)?; + let position = coordinator + .queue_mut() + .push_back_if_missing(priority.into(), request.into_identifier()); + if position == 0 { + Ok(QueryResponse::State(coordinator.round_state())) + } else { + Ok(QueryResponse::QueuePosition(position as u64)) + } + } + + /// Processes a request to update the MPC state and removes the participant if the state was + /// updated successfully. If the update succeeds, the current coordinator is saved to disk. + #[inline] + pub async fn update( + self, + request: SignedMessage>, + ) -> Result<(), CeremonyError> + where + Coordinator: Serialize, + { + let mut coordinator = self.coordinator.lock(); + coordinator.preprocess_request(&request)?; + let (identifier, message) = request.into_inner(); + coordinator.update( + &identifier, + BoxArray::from_vec(message.state), + BoxArray::from_vec(message.proof), + )?; + serialize_into_file( + OpenOptions::new().write(true).create_new(true), + &Path::new(&self.recovery_directory) + .join(format!("transcript{}.data", coordinator.round())), + &coordinator.deref(), + ) + .map_err(|_| CeremonyError::Unexpected(UnexpectedError::Serialization))?; + println!("{} participants have contributed.", coordinator.round()); + Ok(()) + } +} diff --git a/manta-trusted-setup/src/groth16/ceremony/signature.rs b/manta-trusted-setup/src/groth16/ceremony/signature.rs deleted file mode 100644 index 5fe9d3751..000000000 --- a/manta-trusted-setup/src/groth16/ceremony/signature.rs +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2019-2022 Manta Network. -// This file is part of manta-rs. -// -// manta-rs is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// manta-rs is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with manta-rs. If not, see . - -//! Groth16 Trusted Setup Ceremony Signatures - -use alloc::vec::Vec; -use manta_crypto::signature; -use manta_util::{serde::Serialize, AsBytes}; - -/// Nonce -pub trait Nonce: PartialEq { - /// Increments the current nonce by one. - fn increment(&self) -> Self; - - /// Checks if the current nonce is valid. - fn is_valid(&self) -> bool; -} - -impl Nonce for u64 { - #[inline] - fn increment(&self) -> Self { - self.saturating_add(1) - } - - #[inline] - fn is_valid(&self) -> bool { - *self != Self::MAX - } -} - -/// Checks if the two nonces, `current` and `user_nonce`, are both valid and equal to each other. -#[inline] -pub fn check_nonce(current: &N, user_nonce: &N) -> bool -where - N: Nonce, -{ - current.is_valid() && user_nonce.is_valid() && current == user_nonce -} - -/// Message with Nonce -#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] -pub struct Message { - /// Nonce - pub nonce: N, - - /// Encoded Message - pub encoded_message: Vec, -} - -impl AsBytes for Message -where - N: AsBytes, -{ - #[inline] - fn as_bytes(&self) -> Vec { - let mut bytes = self.nonce.as_bytes(); - bytes.extend_from_slice(&self.encoded_message); - bytes - } -} - -/// Signature Scheme -pub trait SignatureScheme: - Default - + signature::Sign, Randomness = ()> - + signature::Verify> -{ - /// Message Nonce - type Nonce: Nonce; - - /// Verification Error Type - type Error; -} - -/// Signs the `message` with the `nonce` attached using the `signing_key`. -#[inline] -pub fn sign( - signing_key: &S::SigningKey, - nonce: S::Nonce, - message: &T, -) -> Result -where - T: Serialize, - S: SignatureScheme, -{ - Ok(S::default().sign( - signing_key, - &(), - &Message { - nonce, - encoded_message: bincode::serialize(message)?, - }, - &mut (), - )) -} - -/// Verification Error -#[derive(Debug)] -pub enum VerificationError { - /// Serialization - Serialization(bincode::Error), - - /// Base Verification Error Type - Error(E), -} - -impl From for VerificationError { - #[inline] - fn from(err: bincode::Error) -> Self { - Self::Serialization(err) - } -} - -/// Verifies the `signature` of `message` with `nonce` attached using `verifying_key`. -#[inline] -pub fn verify( - verifying_key: &S::VerifyingKey, - nonce: S::Nonce, - message: &T, - signature: &S::Signature, -) -> Result<(), VerificationError> -where - T: Serialize, - S: SignatureScheme, -{ - S::default() - .verify( - verifying_key, - &Message { - nonce, - encoded_message: bincode::serialize(message)?, - }, - signature, - &mut (), - ) - .map_err(VerificationError::Error) -} diff --git a/manta-trusted-setup/src/groth16/mod.rs b/manta-trusted-setup/src/groth16/mod.rs index c5114f814..7f6db0096 100644 --- a/manta-trusted-setup/src/groth16/mod.rs +++ b/manta-trusted-setup/src/groth16/mod.rs @@ -16,10 +16,13 @@ //! Groth16 Trusted Setup -pub mod ceremony; pub mod kzg; pub mod mpc; +#[cfg(all(feature = "bincode", feature = "serde"))] +#[cfg_attr(doc_cfg, doc(cfg(all(feature = "bincode", feature = "serde"))))] +pub mod ceremony; + #[cfg(feature = "ppot")] #[cfg_attr(doc_cfg, doc(cfg(feature = "ppot")))] pub mod ppot; diff --git a/manta-trusted-setup/src/groth16/mpc.rs b/manta-trusted-setup/src/groth16/mpc.rs index 29a15a909..2b4ce97c6 100644 --- a/manta-trusted-setup/src/groth16/mpc.rs +++ b/manta-trusted-setup/src/groth16/mpc.rs @@ -18,6 +18,7 @@ use crate::{ groth16::kzg::{self, Accumulator}, + mpc, util::{batch_into_projective, batch_mul_fixed_scalar, merge_pairs_affine}, }; use alloc::{vec, vec::Vec}; @@ -35,7 +36,52 @@ use manta_crypto::{ }; #[cfg(feature = "serde")] -use manta_util::serde::{Deserialize, Serialize}; +use { + manta_crypto::arkworks::serialize::{canonical_deserialize, canonical_serialize}, + manta_util::serde::{Deserialize, Serialize}, +}; + +/// MPC State +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""))] +pub struct State

( + #[cfg_attr( + feature = "serde", + serde( + serialize_with = "canonical_serialize::, _>", + deserialize_with = "canonical_deserialize::<'de, _, ProvingKey>" + ) + )] + pub ProvingKey, +) +where + P: Pairing + ?Sized; + +/// MPC Proof +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""))] +pub struct Proof

( + #[cfg_attr( + feature = "serde", + serde( + serialize_with = "canonical_serialize::, _>", + deserialize_with = "canonical_deserialize::<'de, _, RatioProof

>" + ) + )] + pub RatioProof

, +) +where + P: Pairing + ?Sized; /// Proving Key Hasher pub trait ProvingKeyHasher

@@ -49,12 +95,6 @@ where fn hash(proving_key: &ProvingKey) -> Self::Output; } -/// MPC State -pub type State

= ProvingKey<

::Pairing>; - -/// MPC Proof -pub type Proof

= RatioProof

; - /// MPC State Size #[cfg_attr( feature = "serde", @@ -217,34 +257,34 @@ pub fn check_invariants

(prev: &State

, next: &State

) -> Result<(), Error where P: Pairing, { - if prev.h_query.len() != next.h_query.len() { + if prev.0.h_query.len() != next.0.h_query.len() { return Err(Error::InvariantViolated("H length changed")); } - if prev.l_query.len() != next.l_query.len() { + if prev.0.l_query.len() != next.0.l_query.len() { return Err(Error::InvariantViolated("L length changed")); } - if prev.a_query != next.a_query { + if prev.0.a_query != next.0.a_query { return Err(Error::InvariantViolated("A query changed")); } - if prev.b_g1_query != next.b_g1_query { + if prev.0.b_g1_query != next.0.b_g1_query { return Err(Error::InvariantViolated("B_G1 query changed")); } - if prev.b_g2_query != next.b_g2_query { + if prev.0.b_g2_query != next.0.b_g2_query { return Err(Error::InvariantViolated("B_G2 query changed")); } - if prev.vk.alpha_g1 != next.vk.alpha_g1 { + if prev.0.vk.alpha_g1 != next.0.vk.alpha_g1 { return Err(Error::InvariantViolated("alpha_G1 changed")); } - if prev.beta_g1 != next.beta_g1 { + if prev.0.beta_g1 != next.0.beta_g1 { return Err(Error::InvariantViolated("beta_G1 changed")); } - if prev.vk.beta_g2 != next.vk.beta_g2 { + if prev.0.vk.beta_g2 != next.0.vk.beta_g2 { return Err(Error::InvariantViolated("beta_G2 changed")); } - if prev.vk.gamma_g2 != next.vk.gamma_g2 { + if prev.0.vk.gamma_g2 != next.0.vk.gamma_g2 { return Err(Error::InvariantViolated("gamma_G2 changed")); } - if prev.vk.gamma_abc_g1 != next.vk.gamma_abc_g1 { + if prev.0.vk.gamma_abc_g1 != next.0.vk.gamma_abc_g1 { return Err(Error::InvariantViolated("Public input cross terms changed")); } Ok(()) @@ -312,7 +352,7 @@ where let ext = ProjectiveCurve::batch_normalization_into_affine(&ext); let public_cross_terms = Vec::from(&ext[..constraint_matrices.num_instance_variables]); let private_cross_terms = Vec::from(&ext[constraint_matrices.num_instance_variables..]); - Ok(ProvingKey { + Ok(State(ProvingKey { vk: VerifyingKey { alpha_g1: powers.alpha_tau_powers_g1[0], beta_g2: powers.beta_g2, @@ -327,19 +367,17 @@ where b_g2_query, h_query, l_query: private_cross_terms, - }) + })) } /// Configuration -pub trait Configuration: Pairing { - /// Challenge Type - type Challenge; - +pub trait Configuration: Pairing + mpc::Types, State = State> { /// Hasher Type type Hasher: Default + HashToGroup; - /// Generates the next [`Challenge`](Self::Challenge) from `challenge`, `prev` state, `next` state, - /// and `proof`. + /// Generates the next [`Challenge`] from `challenge`, `prev` state, `next` state, and `proof`. + /// + /// [`Challenge`]: mpc::ChallengeType::Challenge fn challenge( challenge: &Self::Challenge, prev: &State, @@ -362,56 +400,59 @@ where { let delta = C::Scalar::rand(rng); let delta_inverse = delta.inverse()?; - batch_mul_fixed_scalar(&mut state.l_query, delta_inverse); - batch_mul_fixed_scalar(&mut state.h_query, delta_inverse); - state.delta_g1 = state.delta_g1.mul(delta).into_affine(); - state.vk.delta_g2 = state.vk.delta_g2.mul(delta).into_affine(); - Proof::prove(hasher, challenge, &delta, rng) + batch_mul_fixed_scalar(&mut state.0.l_query, delta_inverse); + batch_mul_fixed_scalar(&mut state.0.h_query, delta_inverse); + state.0.delta_g1 = state.0.delta_g1.mul(delta).into_affine(); + state.0.vk.delta_g2 = state.0.vk.delta_g2.mul(delta).into_affine(); + RatioProof::prove(hasher, challenge, &delta, rng).map(Proof) } /// Verifies transforming from `prev` to `next` is correct given `challenge` and `proof`. #[inline] pub fn verify_transform( challenge: &C::Challenge, - prev: State, + prev: &State, next: State, proof: Proof, ) -> Result<(C::Challenge, State), Error> where C: Configuration, { - check_invariants::(&prev, &next)?; - let next_challenge = C::challenge(challenge, &prev, &next, &proof); + check_invariants::(prev, &next)?; + let next_challenge = C::challenge(challenge, prev, &next, &proof); let ((ratio_0, ratio_1), _) = proof + .0 .verify(&C::Hasher::default(), challenge) .ok_or(Error::InvalidRatioProof)?; - if !C::Pairing::same_ratio((ratio_0, ratio_1), (prev.vk.delta_g2, next.vk.delta_g2)) { + if !C::Pairing::same_ratio((ratio_0, ratio_1), (prev.0.vk.delta_g2, next.0.vk.delta_g2)) { return Err(Error::InconsistentDeltaChange); } if !C::Pairing::same_ratio( - (prev.delta_g1, next.delta_g1), - (prev.vk.delta_g2, next.vk.delta_g2), + (prev.0.delta_g1, next.0.delta_g1), + (prev.0.vk.delta_g2, next.0.vk.delta_g2), ) { return Err(Error::InconsistentDeltaChange); } if !C::Pairing::same_ratio( - merge_pairs_affine(&next.h_query, &prev.h_query), - (prev.vk.delta_g2, next.vk.delta_g2), + merge_pairs_affine(&next.0.h_query, &prev.0.h_query), + (prev.0.vk.delta_g2, next.0.vk.delta_g2), ) { return Err(Error::InconsistentHChange); } if !C::Pairing::same_ratio( - merge_pairs_affine(&next.l_query, &prev.l_query), - (prev.vk.delta_g2, next.vk.delta_g2), + merge_pairs_affine(&next.0.l_query, &prev.0.l_query), + (prev.0.vk.delta_g2, next.0.vk.delta_g2), ) { return Err(Error::InconsistentLChange); } Ok((next_challenge, next)) } -/// Verifies all contributions in `iter` chaining from an initial `state` and `challenge` -/// returning the newest [`State`](State) and [`Challenge`](Configuration::Challenge) if all the -/// contributions in the chain had valid transitions. +/// Verifies all contributions in `iter` chaining from an initial `state` and `challenge` returning +/// the newest [`State`] and [`Challenge`] if all the contributions in the chain had valid +/// transitions. +/// +/// [`Challenge`]: mpc::ChallengeType::Challenge #[inline] pub fn verify_transform_all( mut challenge: C::Challenge, @@ -426,11 +467,12 @@ where for (next_state, next_proof) in iter { let next_challenge = C::challenge(&challenge, &state, &next_state, &next_proof); let ((ratio_0, ratio_1), _) = next_proof + .0 .verify(&C::Hasher::default(), &challenge) .ok_or(Error::InvalidRatioProof)?; if !C::Pairing::same_ratio( (ratio_0, ratio_1), - (state.vk.delta_g2, next_state.vk.delta_g2), + (state.0.vk.delta_g2, next_state.0.vk.delta_g2), ) { return Err(Error::InconsistentDeltaChange); } @@ -438,20 +480,20 @@ where } check_invariants::(&initial_state, &state)?; if !C::Pairing::same_ratio( - (initial_state.delta_g1, state.delta_g1), - (initial_state.vk.delta_g2, state.vk.delta_g2), + (initial_state.0.delta_g1, state.0.delta_g1), + (initial_state.0.vk.delta_g2, state.0.vk.delta_g2), ) { return Err(Error::InconsistentDeltaChange); } if !C::Pairing::same_ratio( - merge_pairs_affine(&state.h_query, &initial_state.h_query), - (initial_state.vk.delta_g2, state.vk.delta_g2), + merge_pairs_affine(&state.0.h_query, &initial_state.0.h_query), + (initial_state.0.vk.delta_g2, state.0.vk.delta_g2), ) { return Err(Error::InconsistentHChange); } if !C::Pairing::same_ratio( - merge_pairs_affine(&state.l_query, &initial_state.l_query), - (initial_state.vk.delta_g2, state.vk.delta_g2), + merge_pairs_affine(&state.0.l_query, &initial_state.0.l_query), + (initial_state.0.vk.delta_g2, state.0.vk.delta_g2), ) { return Err(Error::InconsistentLChange); } diff --git a/manta-trusted-setup/src/groth16/ppot/mpc.rs b/manta-trusted-setup/src/groth16/ppot/mpc.rs index 29d064f08..d86eea15d 100644 --- a/manta-trusted-setup/src/groth16/ppot/mpc.rs +++ b/manta-trusted-setup/src/groth16/ppot/mpc.rs @@ -29,8 +29,11 @@ use blake2::Digest; use manta_crypto::arkworks::{pairing::Pairing, serialize::CanonicalSerialize}; use manta_util::into_array_unchecked; -impl Configuration for PerpetualPowersOfTauCeremony { +impl ChallengeType for PerpetualPowersOfTauCeremony { type Challenge = [u8; 64]; +} + +impl Configuration for PerpetualPowersOfTauCeremony { type Hasher = BlakeHasher; #[inline] @@ -42,33 +45,28 @@ impl Configuration for PerpetualPowersOfTauCeremony Self::Challenge { let mut hasher = Self::Hasher::default(); hasher.0.update(challenge); - prev.serialize(&mut hasher) + prev.0 + .serialize(&mut hasher) .expect("Consuming the previous state failed."); - next.serialize(&mut hasher) + next.0 + .serialize(&mut hasher) .expect("Consuming the current state failed."); proof + .0 .serialize(&mut hasher) .expect("Consuming proof failed"); into_array_unchecked(hasher.0.finalize()) } } -impl StateType for PerpetualPowersOfTauCeremony { - type State = State; -} - -impl ChallengeType for PerpetualPowersOfTauCeremony { - type Challenge = [u8; 64]; +impl ContributionType for PerpetualPowersOfTauCeremony { + type Contribution = ::Scalar; } impl ProofType for PerpetualPowersOfTauCeremony { type Proof = Proof; } -impl ContributionType for PerpetualPowersOfTauCeremony { - type Contribution = ::Scalar; -} - impl ProvingKeyHasher for PerpetualPowersOfTauCeremony { type Output = [u8; 64]; @@ -81,3 +79,7 @@ impl ProvingKeyHasher for PerpetualPowersOfTauCere into_array_unchecked(hasher.0.finalize()) } } + +impl StateType for PerpetualPowersOfTauCeremony { + type State = State; +} diff --git a/manta-trusted-setup/src/groth16/test/mod.rs b/manta-trusted-setup/src/groth16/test/mod.rs index d3233519e..771e6c564 100644 --- a/manta-trusted-setup/src/groth16/test/mod.rs +++ b/manta-trusted-setup/src/groth16/test/mod.rs @@ -21,7 +21,7 @@ use crate::{ kzg::{self, Accumulator, Configuration, Contribution, Size}, mpc::{self, contribute, initialize, verify_transform, verify_transform_all, Proof, State}, }, - mpc::{ChallengeType, ProofType, StateType, Transcript}, + mpc::{ChallengeType, ContributionType, ProofType, StateType, Transcript}, util::{BlakeHasher, HasDistribution, KZGBlakeHasher}, }; use alloc::vec::Vec; @@ -132,7 +132,6 @@ impl kzg::Configuration for Test { } impl mpc::Configuration for Test { - type Challenge = [u8; 64]; type Hasher = BlakeHasher; #[inline] @@ -144,11 +143,14 @@ impl mpc::Configuration for Test { ) -> Self::Challenge { let mut hasher = Self::Hasher::default(); hasher.0.update(challenge); - prev.serialize(&mut hasher) + prev.0 + .serialize(&mut hasher) .expect("Consuming the previous state failed."); - next.serialize(&mut hasher) + next.0 + .serialize(&mut hasher) .expect("Consuming the current state failed."); proof + .0 .serialize(&mut hasher) .expect("Consuming proof failed"); into_array_unchecked(hasher.0.finalize()) @@ -171,16 +173,20 @@ where } } -impl StateType for Test { - type State = State; -} - impl ChallengeType for Test { type Challenge = [u8; 64]; } +impl ContributionType for Test { + type Contribution = Contribution; +} + impl ProofType for Test { - type Proof = Proof; + type Proof = Proof; +} + +impl StateType for Test { + type State = State; } /// Conducts a dummy phase one trusted setup. @@ -215,7 +221,7 @@ pub fn dummy_circuit(cs: &mut R1CS) { pub fn dummy_prover_key() -> ProvingKey { let mut cs = R1CS::for_contexts(); dummy_circuit(&mut cs); - initialize(dummy_phase_one_trusted_setup(), cs).unwrap() + initialize(dummy_phase_one_trusted_setup(), cs).unwrap().0 } /// Proves and verifies a R1CS circuit with proving key `pk` and a random number generator `rng`. @@ -252,9 +258,9 @@ fn proving_and_verifying_ratio_proof_is_correct() { #[test] fn trusted_setup_phase_two_is_valid() { let mut rng = OsRng; - let mut state = dummy_prover_key(); + let mut state = State(dummy_prover_key()); let mut transcript = Transcript:: { - initial_challenge: >::hash(&state), + initial_challenge: >::hash(&state.0), initial_state: state.clone(), rounds: Vec::new(), }; @@ -264,7 +270,7 @@ fn trusted_setup_phase_two_is_valid() { for _ in 0..5 { prev_state = state.clone(); proof = contribute(&hasher, &challenge, &mut state, &mut rng).unwrap(); - (challenge, state) = verify_transform(&challenge, prev_state, state, proof.clone()) + (challenge, state) = verify_transform(&challenge, &prev_state, state, proof.clone()) .expect("Verify transform failed"); transcript.rounds.push((state.clone(), proof)); } @@ -276,5 +282,5 @@ fn trusted_setup_phase_two_is_valid() { .expect("Verifying all transformations failed."); let mut cs = R1CS::for_proofs(); dummy_circuit(&mut cs); - prove_and_verify_circuit(state, cs, &mut rng); + prove_and_verify_circuit(state.0, cs, &mut rng); } diff --git a/manta-trusted-setup/src/lib.rs b/manta-trusted-setup/src/lib.rs index 84dabb22a..8bf82c1a2 100644 --- a/manta-trusted-setup/src/lib.rs +++ b/manta-trusted-setup/src/lib.rs @@ -23,6 +23,7 @@ extern crate alloc; +pub mod ceremony; pub mod groth16; pub mod mpc; pub mod util; diff --git a/manta-trusted-setup/src/mpc.rs b/manta-trusted-setup/src/mpc.rs index c5ea32390..200e6e5e6 100644 --- a/manta-trusted-setup/src/mpc.rs +++ b/manta-trusted-setup/src/mpc.rs @@ -16,7 +16,17 @@ //! Secure Multi-Party Computation Primitives -use alloc::vec::Vec; +use alloc::vec::{self, Vec}; +use core::{ + fmt::Debug, + hash::Hash, + ops::{Deref, DerefMut}, + slice, +}; +use manta_util::assert_all_eq_len; + +#[cfg(feature = "serde")] +use manta_util::serde::{Deserialize, Serialize}; /// State pub trait StateType { @@ -103,12 +113,12 @@ pub trait Verify: ChallengeType + ProofType + StateType { /// Verification Error Type type Error; - /// Computes the challenge associated to `challenge`, `prev`, `next`, and `proof` for the next + /// Computes the challenge associated to `challenge`, `last`, `next`, and `proof` for the next /// player. fn challenge( &self, challenge: &Self::Challenge, - prev: &Self::State, + last: &Self::State, next: &Self::State, proof: &Self::Proof, ) -> Self::Challenge; @@ -148,7 +158,7 @@ pub trait Verify: ChallengeType + ProofType + StateType { } } -/// MPC Transcript +/// Secure Multi-Party Contribution Transcript pub struct Transcript where T: ChallengeType + StateType + ProofType, @@ -162,3 +172,311 @@ where /// Rounds pub rounds: Vec<(T::State, T::Proof)>, } + +impl Transcript +where + T: ChallengeType + StateType + ProofType, +{ + /// Verifies the transcript `self` against `parameters` returning the final challenge and state. + #[inline] + pub fn verify(self, parameters: &T) -> Result<(T::Challenge, T::State), T::Error> + where + T: Verify, + { + parameters.verify_transform_all::( + self.initial_challenge, + self.initial_state, + self.rounds.into_iter().map(Ok), + ) + } +} + +/// Secure Multi-Party Computation Round +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "T::State: Deserialize<'de>, T::Challenge: Deserialize<'de>", + serialize = "T::State: Serialize, T::Challenge: Serialize" + ), + crate = "manta_util::serde", + deny_unknown_fields + ) +)] +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "T::State: Clone, T::Challenge: Clone"), + Copy(bound = "T::State: Copy, T::Challenge: Copy"), + Debug(bound = "T::State: Debug, T::Challenge: Debug"), + Default(bound = "T::State: Default, T::Challenge: Default"), + Eq(bound = "T::State: Eq, T::Challenge: Eq"), + Hash(bound = "T::State: Hash, T::Challenge: Hash"), + PartialEq(bound = "T::State: PartialEq, T::Challenge: PartialEq") +)] +pub struct Round +where + T: ChallengeType + StateType, +{ + /// State + pub state: T::State, + + /// Challenge + pub challenge: T::Challenge, +} + +impl Round +where + T: ChallengeType + StateType, +{ + /// Builds a new [`Round`] from `state` and `challenge`. + #[inline] + pub fn new(state: T::State, challenge: T::Challenge) -> Self { + Self { state, challenge } + } + + /// Computes the contribution proof using [`Contribute::contribute`] over `parameters`. + #[inline] + pub fn contribute(&mut self, parameters: &T, contribution: &T::Contribution) -> T::Proof + where + T: Contribute, + { + parameters.contribute(&mut self.state, &self.challenge, contribution) + } + + /// Computes the round challenge using [`Verify::challenge`] over `parameters`. + #[inline] + pub fn challenge(&self, parameters: &T, next: &T::State, proof: &T::Proof) -> T::Challenge + where + T: Verify, + { + parameters.challenge(&self.challenge, &self.state, next, proof) + } + + /// Verifies that the transformation from `self` to `next` is valid, returning the next state + /// using [`Verify::verify_transform`] over `parameters`. + #[inline] + pub fn verify_transform( + self, + parameters: &T, + next: T::State, + proof: T::Proof, + ) -> Result + where + T: Verify, + { + parameters.verify_transform(&self.challenge, self.state, next, proof) + } +} + +/// Parallel MPC Wrapper +/// +/// Multiple secure multi-party computation protocols with the same round structure can be run in +/// parallel, i.e. both the contribution and verification of each protocol can be run in parallel. +/// This increases the latency of any particular instance of the protocol but reduces the overhead +/// of running multiple protocols. +/// +/// This type [`Parallel`] can be used for any component of the protocol, including types defined by +/// [`Types`] instances or the [`Contribute`] or [`Verify`] instances. +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] +#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] +pub struct Parallel(Vec); + +impl Deref for Parallel { + type Target = [T]; + + #[inline] + fn deref(&self) -> &[T] { + &self.0 + } +} + +impl DerefMut for Parallel { + #[inline] + fn deref_mut(&mut self) -> &mut [T] { + &mut self.0 + } +} + +impl From> for Parallel { + #[inline] + fn from(vec: Vec) -> Self { + Self(vec) + } +} + +impl From> for Vec { + #[inline] + fn from(parallel: Parallel) -> Self { + parallel.0 + } +} + +impl FromIterator for Parallel { + #[inline] + fn from_iter(iter: I) -> Self + where + I: IntoIterator, + { + Self(iter.into_iter().collect()) + } +} + +impl IntoIterator for Parallel { + type Item = T; + type IntoIter = vec::IntoIter; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'p, T> IntoIterator for &'p Parallel { + type Item = &'p T; + type IntoIter = slice::Iter<'p, T>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } +} + +impl<'p, T> IntoIterator for &'p mut Parallel { + type Item = &'p mut T; + type IntoIter = slice::IterMut<'p, T>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.0.iter_mut() + } +} + +impl ChallengeType for Parallel +where + T: ChallengeType, +{ + type Challenge = Parallel; +} + +impl ContributionType for Parallel +where + T: ContributionType, +{ + type Contribution = Parallel; +} + +impl ProofType for Parallel +where + T: ProofType, +{ + type Proof = Parallel; +} + +impl StateType for Parallel +where + T: StateType, +{ + type State = Parallel; +} + +impl Contribute for Parallel +where + C: Contribute, +{ + #[inline] + fn contribute( + &self, + state: &mut Self::State, + challenge: &Self::Challenge, + contribution: &Self::Contribution, + ) -> Self::Proof { + assert_all_eq_len!( + [self, state, challenge, contribution], + "Length mismatch in parallel `Contribute::contribute`." + ); + self.0 + .iter() + .zip(state) + .zip(challenge) + .zip(contribution) + .map(|(((this, state), challenge), contribution)| { + this.contribute(state, challenge, contribution) + }) + .collect() + } +} + +impl Verify for Parallel +where + V: Verify, +{ + type Error = V::Error; + + #[inline] + fn challenge( + &self, + challenge: &Self::Challenge, + last: &Self::State, + next: &Self::State, + proof: &Self::Proof, + ) -> Self::Challenge { + assert_all_eq_len!( + [self, challenge, last, next, proof], + "Length mismatch in parallel `Verify::challlenge`." + ); + self.0 + .iter() + .zip(challenge) + .zip(last) + .zip(next) + .zip(proof) + .map(|((((this, challenge), last), next), proof)| { + this.challenge(challenge, last, next, proof) + }) + .collect() + } + + #[inline] + fn verify_transform( + &self, + challenge: &Self::Challenge, + last: Self::State, + next: Self::State, + proof: Self::Proof, + ) -> Result { + assert_all_eq_len!( + [self, challenge, last, next, proof], + "Length mismatch in parallel `Verify::verify_transform`." + ); + self.0 + .iter() + .zip(challenge) + .zip(last) + .zip(next) + .zip(proof) + .map(|((((this, challenge), last), next), proof)| { + this.verify_transform(challenge, last, next, proof) + }) + .collect() + } +} + +/// Parallel Round Alias +pub type ParallelRound = Round>; + +impl ParallelRound +where + T: ChallengeType + StateType, +{ + /// Returns `self` if it has the correct shape expected for a [`Round`] over a [`Parallel`] + /// ceremony, i.e. that `self.state` has the same length as `self.challenge`. + #[inline] + pub fn with_valid_shape(self) -> Option { + (self.state.len() == self.challenge.len()).then_some(self) + } +} diff --git a/manta-util/Cargo.toml b/manta-util/Cargo.toml index 0a51ddcf8..5605378c3 100644 --- a/manta-util/Cargo.toml +++ b/manta-util/Cargo.toml @@ -40,7 +40,7 @@ std = ["alloc", "crossbeam-channel?/std", "serde?/std"] [dependencies] crossbeam-channel = { version = "0.5.6", optional = true, default-features = false } rayon = { version = "1.5.3", optional = true, default-features = false } -reqwest = { version = "0.11.11", optional = true, default-features = false, features = ["json"] } +reqwest = { version = "0.11.12", optional = true, default-features = false, features = ["json"] } serde = { version = "1.0.144", optional = true, default-features = false, features = ["derive"] } serde_with = { version = "1.14.0", optional = true, default-features = false, features = ["macros"] } tide = { version = "0.16.0", optional = true, default-features = false } diff --git a/manta-util/src/bytes.rs b/manta-util/src/bytes.rs index 592bc0c98..021724bb4 100644 --- a/manta-util/src/bytes.rs +++ b/manta-util/src/bytes.rs @@ -51,8 +51,16 @@ pub trait Bytes: FromBytes + IntoBytes {} impl Bytes for B where B: FromBytes + IntoBytes {} -/// Implements [`Bytes`] for the primitive `$type` of a given `$size` using `from_le_bytes` and -/// `to_le_bytes` for little-endian conversion. +/// Byte Vector Conversion +#[cfg(feature = "alloc")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] +pub trait AsBytes { + /// Returns an owned byte representation of `self`. + fn as_bytes(&self) -> Vec; +} + +/// Implements [`Bytes`] and [`AsBytes`] for the primitive `$type` of a given `$size` using +/// `from_le_bytes` and `to_le_bytes` for little-endian conversion. macro_rules! impl_bytes_primitive { ($type:tt, $size:expr) => { impl FromBytes<$size> for $type { @@ -68,6 +76,15 @@ macro_rules! impl_bytes_primitive { self.to_le_bytes() } } + + #[cfg(feature = "alloc")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] + impl AsBytes for $type { + #[inline] + fn as_bytes(&self) -> Vec { + self.to_le_bytes().to_vec() + } + } }; ($($type:tt),* $(,)?) => { $(impl_bytes_primitive!($type, { ($type::BITS / 8) as usize });)* @@ -98,11 +115,3 @@ impl IntoBytes for [u8; N] { self } } - -/// Byte Vector Conversion -#[cfg(feature = "alloc")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] -pub trait AsBytes { - /// Returns an owned byte representation of `self`. - fn as_bytes(&self) -> Vec; -} diff --git a/manta-util/src/collections/vec_deque.rs b/manta-util/src/collections/vec_deque.rs index 4ce9e3806..be0d48f26 100644 --- a/manta-util/src/collections/vec_deque.rs +++ b/manta-util/src/collections/vec_deque.rs @@ -128,6 +128,12 @@ impl MultiVecDeque { } } + /// Returns the number of elements before the [`VecDeque`] at the given `level`. + #[inline] + fn leading_element_count(&self, level: usize) -> usize { + self.0[0..level].iter().map(VecDeque::len).sum::() + } + /// Finds the position of `item` assuming it was inserted at the given `level`. #[inline] pub fn position(&self, level: usize, item: &T) -> Option @@ -144,10 +150,7 @@ impl MultiVecDeque { where F: FnMut(&T, &T) -> bool, { - Some( - self.0[level].iter().position(|x| eq(x, item))? - + self.0[0..level].iter().map(VecDeque::len).sum::(), - ) + Some(self.0[level].iter().position(|x| eq(x, item))? + self.leading_element_count(level)) } /// Pushes `item` to the back of the deque at the given `level`. @@ -156,7 +159,7 @@ impl MultiVecDeque { /// /// This method is an alias for `self.at_level_mut(level).push_back(item)`. #[inline] - pub fn push_back_at(&mut self, level: usize, item: T) { + pub fn push_back(&mut self, level: usize, item: T) { self.0[level].push_back(item) } @@ -165,6 +168,22 @@ impl MultiVecDeque { pub fn pop_front(&mut self) -> Option { self.0.iter_mut().find_map(VecDeque::pop_front) } + + /// Pushes back `item` at `level` if `item` is missing. Returns the position + /// of `item` in both cases. + #[inline] + pub fn push_back_if_missing(&mut self, level: usize, item: T) -> usize + where + T: PartialEq, + { + match self.position(level, &item) { + Some(position) => position, + None => { + self.push_back(level, item); + self.leading_element_count(level) + self.0[level].len() - 1 + } + } + } } impl Default for MultiVecDeque { diff --git a/manta-util/src/macros.rs b/manta-util/src/macros.rs index e04266aba..9fc25c497 100644 --- a/manta-util/src/macros.rs +++ b/manta-util/src/macros.rs @@ -16,6 +16,29 @@ //! Utility Macros +/// Asserts that all the elements in `$tail` have the same length as `$head`. +#[macro_export] +macro_rules! assert_all_eq_len { + ([$head:expr, $($tail:expr),+ $(,)?]) => {{ + $( + assert_eq!( + $head.len(), + $tail.len(), + ); + )+ + }}; + ([$head:expr, $($tail:expr),+], $($arg:tt)+) => {{ + let __format = core::format_args!($($arg)+); + $( + assert_eq!( + $head.len(), + $tail.len(), + "{}", __format, + ); + )+ + }}; +} + /// Implements [`From`]`<$from>` for an enum `$to`, choosing the `$kind` variant. #[macro_export] macro_rules! from_variant {