From 79f912d11f6b8479541014706cb6d1646a3963ed Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" Date: Tue, 31 Aug 2021 20:45:12 -0400 Subject: [PATCH 001/275] first draft of manta-codec --- manta-codec/Cargo.toml | 35 +++++++++++ manta-codec/LICENSE | 1 + manta-codec/README.md | 1 + manta-codec/src/lib.rs | 131 +++++++++++++++++++++++++++++++++++++++++ manta-util/Cargo.toml | 23 ++++++++ manta-util/LICENSE | 1 + manta-util/README.md | 1 + manta-util/src/lib.rs | 83 ++++++++++++++++++++++++++ 8 files changed, 276 insertions(+) create mode 100644 manta-codec/Cargo.toml create mode 120000 manta-codec/LICENSE create mode 100644 manta-codec/README.md create mode 100644 manta-codec/src/lib.rs create mode 100644 manta-util/Cargo.toml create mode 120000 manta-util/LICENSE create mode 100644 manta-util/README.md create mode 100644 manta-util/src/lib.rs diff --git a/manta-codec/Cargo.toml b/manta-codec/Cargo.toml new file mode 100644 index 000000000..5bf1a790c --- /dev/null +++ b/manta-codec/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "manta-codec" +edition = "2018" +version = "0.3.0" +authors = ["Manta Network "] +readme = "README.md" +license-file = "LICENSE" +repository = "https://github.com/Manta-Network/manta-rs" +homepage = "https://github.com/Manta-Network" +documentation = "https://github.com/Manta-Network/manta-rs" +categories = [""] +keywords = [""] +description = "Serialization and Deserialization tools for manta-rs." +publish = false + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[badges] +is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } +is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } +maintenance = { status = "actively-developed" } + +[features] +default = [] +std = [] +derive = ["scale-codec/derive"] + +[dependencies] +ark-serialize = { version = "0.3.0", default-features = false } +ark-std = { version = "0.3.0", default-features = false } +scale-codec = { package = "parity-scale-codec", version = "2.2.0", default-features = false } +displaydoc = { version = "0.2.3", default-features = false } +manta-util = { path = "../manta-util", default-features = false } diff --git a/manta-codec/LICENSE b/manta-codec/LICENSE new file mode 120000 index 000000000..ea5b60640 --- /dev/null +++ b/manta-codec/LICENSE @@ -0,0 +1 @@ +../LICENSE \ No newline at end of file diff --git a/manta-codec/README.md b/manta-codec/README.md new file mode 100644 index 000000000..b96db7f8d --- /dev/null +++ b/manta-codec/README.md @@ -0,0 +1 @@ +# manta-codec diff --git a/manta-codec/src/lib.rs b/manta-codec/src/lib.rs new file mode 100644 index 000000000..94e9117d7 --- /dev/null +++ b/manta-codec/src/lib.rs @@ -0,0 +1,131 @@ +// Copyright 2019-2021 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 . + +//! Parity SCALE + Arkworks Codec System + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(docsrs, feature(doc_cfg), forbid(broken_intra_doc_links))] + +use displaydoc::Display; +use manta_util::from_variant_impl; + +pub use ark_serialize::{Read, SerializationError as ArkCodecError, Write}; +pub use scale_codec::{ + Decode as ScaleDecode, Encode as ScaleEncode, Error as ScaleCodecError, Input, Output, +}; + +/// Codec Error Type +#[derive(Debug, Display)] +pub enum Error { + /// Arkworks Codec Error + #[displaydoc("Arkworks Codec Error: {0}")] + ArkCodecError(ArkCodecError), + + /// SCALE Codec Error + #[displaydoc("SCALE Codec Error: {0}")] + ScaleCodecError(ScaleCodecError), +} + +from_variant_impl!(Error, ArkCodecError, ArkCodecError); +from_variant_impl!(Error, ScaleCodecError, ScaleCodecError); + +impl From for Error { + #[inline] + fn from(err: ark_std::io::Error) -> Self { + Self::ArkCodecError(ArkCodecError::IoError(err)) + } +} + +impl ark_std::error::Error for Error {} + +/// Decoding Result Alias +pub type DecodeResult = Result; + +/// Encoding Result Alias +pub type EncodeResult = Result<(), Error>; + +/// Default Decode Implementation Marker Trait +pub trait DefaultDecode: ScaleDecode {} + +/// Decoding Trait +pub trait Decode: Sized { + /// Decodes `Self` from the reader. + fn decode(reader: &mut R) -> DecodeResult + where + R: Input + Read; +} + +impl Decode for D +where + D: DefaultDecode, +{ + #[inline] + fn decode(reader: &mut R) -> DecodeResult + where + R: Input + Read, + { + scale_decode(reader) + } +} + +/// Default Encode Implementation Marker Trait +pub trait DefaultEncode: ScaleEncode {} + +/// Encoding Trait +pub trait Encode { + /// Encodes `self` to the writer. + fn encode(&self, writer: &mut W) -> EncodeResult + where + W: Output + Write; +} + +impl Encode for E +where + E: DefaultEncode, +{ + #[inline] + fn encode(&self, writer: &mut W) -> EncodeResult + where + W: Output + Write, + { + scale_encode(self, writer) + } +} + +/// Codec Trait +pub trait Codec: Decode + Encode {} + +impl Codec for S where S: Decode + Encode {} + +/// Decodes a value of type `T` from `reader` using the SCALE codec. +#[inline] +pub fn scale_decode(reader: &mut R) -> DecodeResult +where + T: ScaleDecode, + R: Input, +{ + Ok(T::decode(reader)?) +} + +/// Encodes a value `t` to `writer` using the SCALE codec. +#[inline] +pub fn scale_encode(t: &T, writer: &mut W) -> EncodeResult +where + T: ScaleEncode, + W: Write, +{ + Ok(writer.write_all(t.encode().as_ref())?) +} diff --git a/manta-util/Cargo.toml b/manta-util/Cargo.toml new file mode 100644 index 000000000..78cb7ccfb --- /dev/null +++ b/manta-util/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "manta-util" +edition = "2018" +version = "0.3.0" +authors = ["Manta Network "] +readme = "README.md" +license-file = "LICENSE" +repository = "https://github.com/Manta-Network/manta-rs" +homepage = "https://github.com/Manta-Network" +documentation = "https://github.com/Manta-Network/manta-rs" +categories = [""] +keywords = ["utilities"] +description = "Basic utilities for manta-rs crates." +publish = false + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[badges] +is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } +is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } +maintenance = { status = "actively-developed" } diff --git a/manta-util/LICENSE b/manta-util/LICENSE new file mode 120000 index 000000000..ea5b60640 --- /dev/null +++ b/manta-util/LICENSE @@ -0,0 +1 @@ +../LICENSE \ No newline at end of file diff --git a/manta-util/README.md b/manta-util/README.md new file mode 100644 index 000000000..feeb1820e --- /dev/null +++ b/manta-util/README.md @@ -0,0 +1 @@ +# manta-util diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs new file mode 100644 index 000000000..919bf379c --- /dev/null +++ b/manta-util/src/lib.rs @@ -0,0 +1,83 @@ +// Copyright 2019-2021 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 . + +//! Utilities + +#![no_std] +#![cfg_attr(docsrs, feature(doc_cfg), forbid(broken_intra_doc_links))] + +extern crate alloc; + +use alloc::vec::Vec; +use core::convert::TryInto; + +/// Implements [`From`]`<$from>` for an enum `$to`, choosing the `$kind` variant. +#[macro_export] +macro_rules! from_variant_impl { + ($to:tt, $kind:ident, $from:tt) => { + impl From<$from> for $to { + #[inline] + fn from(t: $from) -> Self { + Self::$kind(t) + } + } + }; +} + +/// Performs the [`TryInto`] conversion into an array without checking if the conversion succeeded. +#[inline] +pub fn try_into_array_unchecked(v: V) -> [T; N] +where + V: TryInto<[T; N]>, +{ + match v.try_into() { + Ok(array) => array, + _ => unreachable!(), + } +} + +/// Maps `f` over the `array`. +#[inline] +pub fn array_map(array: [T; N], f: F) -> [U; N] +where + F: FnMut(T) -> U, +{ + // TODO: get rid of this function when `array::map` is stabilized + try_into_array_unchecked(IntoIterator::into_iter(array).map(f).collect::>()) +} + +/// Maps `f` over the `array` by reference. +#[inline] +pub fn array_map_ref(array: &[T; N], f: F) -> [U; N] +where + F: FnMut(&T) -> U, +{ + try_into_array_unchecked(array.iter().map(f).collect::>()) +} + +/// Maps `f` over the `array` returning the target array if all of the mappings succeeded, or +/// returning the first error that occurs. +#[inline] +pub fn fallible_array_map(array: [T; N], f: F) -> Result<[U; N], E> +where + F: FnMut(T) -> Result, +{ + Ok(try_into_array_unchecked( + IntoIterator::into_iter(array) + .map(f) + .collect::, _>>()?, + )) +} From 235edd902b2e6f874537d1fa8a4612fde48c7ca0 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" Date: Tue, 31 Aug 2021 20:53:09 -0400 Subject: [PATCH 002/275] add note about scale codec --- manta-codec/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/manta-codec/src/lib.rs b/manta-codec/src/lib.rs index 94e9117d7..7376ce145 100644 --- a/manta-codec/src/lib.rs +++ b/manta-codec/src/lib.rs @@ -16,6 +16,9 @@ //! Parity SCALE + Arkworks Codec System +// FIXME: figure out how to "re-export" the `parity_scale_codec` crate so we don't have to explicitly +// depend on it downstream + #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(docsrs, feature(doc_cfg), forbid(broken_intra_doc_links))] From 164484d08a02be7e642714d937400822a541481a Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" Date: Tue, 31 Aug 2021 21:01:03 -0400 Subject: [PATCH 003/275] first draft of manta-crypto --- manta-crypto/Cargo.toml | 34 ++++++ manta-crypto/LICENSE | 1 + manta-crypto/README.md | 1 + manta-crypto/src/checksum.rs | 48 +++++++++ manta-crypto/src/commitment.rs | 51 +++++++++ manta-crypto/src/concat.rs | 170 ++++++++++++++++++++++++++++++ manta-crypto/src/constraints.rs | 17 +++ manta-crypto/src/ies.rs | 178 ++++++++++++++++++++++++++++++++ manta-crypto/src/lib.rs | 35 +++++++ manta-crypto/src/prf.rs | 46 +++++++++ manta-crypto/src/set.rs | 119 +++++++++++++++++++++ 11 files changed, 700 insertions(+) create mode 100644 manta-crypto/Cargo.toml create mode 120000 manta-crypto/LICENSE create mode 100644 manta-crypto/README.md create mode 100644 manta-crypto/src/checksum.rs create mode 100644 manta-crypto/src/commitment.rs create mode 100644 manta-crypto/src/concat.rs create mode 100644 manta-crypto/src/constraints.rs create mode 100644 manta-crypto/src/ies.rs create mode 100644 manta-crypto/src/lib.rs create mode 100644 manta-crypto/src/prf.rs create mode 100644 manta-crypto/src/set.rs diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml new file mode 100644 index 000000000..52ea5a6a4 --- /dev/null +++ b/manta-crypto/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "manta-crypto" +edition = "2018" +version = "0.3.0" +authors = ["Manta Network "] +readme = "README.md" +license-file = "LICENSE" +repository = "https://github.com/Manta-Network/manta-rs" +homepage = "https://github.com/Manta-Network" +documentation = "https://github.com/Manta-Network/manta-rs" +categories = [""] +keywords = [""] +description = "Cryptographic Primitives and Interfaces for manta-rs." +publish = false + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[badges] +is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } +is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } +maintenance = { status = "actively-developed" } + +[features] +default = [] +std = [] + +[dependencies] +ark-std = { version = "0.3.0", default-features = false } +blake2 = { version = "0.9.2", default-features = false } +derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } +manta-codec = { path = "../manta-codec", default-features = false, features = ["derive"] } +parity-scale-codec = { version = "2.2.0", default-features = false } diff --git a/manta-crypto/LICENSE b/manta-crypto/LICENSE new file mode 120000 index 000000000..ea5b60640 --- /dev/null +++ b/manta-crypto/LICENSE @@ -0,0 +1 @@ +../LICENSE \ No newline at end of file diff --git a/manta-crypto/README.md b/manta-crypto/README.md new file mode 100644 index 000000000..15a7cef4b --- /dev/null +++ b/manta-crypto/README.md @@ -0,0 +1 @@ +# manta-crypto diff --git a/manta-crypto/src/checksum.rs b/manta-crypto/src/checksum.rs new file mode 100644 index 000000000..0cafbb008 --- /dev/null +++ b/manta-crypto/src/checksum.rs @@ -0,0 +1,48 @@ +// Copyright 2019-2021 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 . + +//! Checksums + +// TODO: move this out of this crate, this is for `pallet-manta-pay` when we check local parameters +// against stored ones. maybe this can go in `manta-pay`? + +use ark_std::vec::Vec; +use blake2::{Blake2s, Digest}; + +/// Checksum Error Type +pub struct Error; + +/// Checksum Trait +pub trait Checksum { + /// Computes the checksum of `self`. + fn get_checksum(&self) -> Result<[u8; N], Error>; +} + +impl Checksum<4> for Vec { + fn get_checksum(&self) -> Result<[u8; 4], Error> { + let mut hasher = Blake2s::new(); + hasher.update(&self); + let digest = hasher.finalize(); + let mut int_res = [0; 32]; + int_res.copy_from_slice(digest.as_slice()); + let mut hasher_two = Blake2s::new(); + hasher_two.update(int_res); + let final_digest = hasher_two.finalize(); + let mut res = [0; 4]; + res.copy_from_slice(&final_digest.as_slice()[0..4]); + Ok(res) + } +} diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs new file mode 100644 index 000000000..e2d02bb77 --- /dev/null +++ b/manta-crypto/src/commitment.rs @@ -0,0 +1,51 @@ +// Copyright 2019-2021 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 . + +//! Commitments + +use ark_std::{borrow::Borrow, rand::Rng}; + +/// Commitment Scheme +pub trait CommitmentScheme { + /// Commitment Randomness Parameter Type + type Randomness; + + /// Commitment Output Type + type Output: PartialEq; + + /// Samples random commitment paramters. + fn setup(rng: &mut R) -> Self + where + R: Rng; + + /// Commits the `input` with the given `randomness` parameter. + fn commit(&self, input: I, randomness: R) -> Self::Output + where + I: Borrow<[u8]>, + R: Borrow; + + /// Checks that the `output` matches the commitment of the `input` with the given `randomness` + /// parameter. + #[inline] + fn check_commitment(&self, input: I, randomness: R, output: O) -> bool + where + I: Borrow<[u8]>, + R: Borrow, + O: Borrow, + { + &self.commit(input, randomness) == output.borrow() + } +} diff --git a/manta-crypto/src/concat.rs b/manta-crypto/src/concat.rs new file mode 100644 index 000000000..896e87959 --- /dev/null +++ b/manta-crypto/src/concat.rs @@ -0,0 +1,170 @@ +// Copyright 2019-2021 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 . + +//! Concatenation and Byte Accumulators + +use ark_std::{borrow::Borrow, vec::Vec}; + +/// Accumulation Trait +pub trait Accumulator { + /// Extends the current accumulator by a `buffer` of elements. + fn extend(&mut self, buffer: &[T]); + + /// Reserves space in the accumulator for `additional` more elements. + #[inline] + fn reserve(&mut self, additional: usize) { + let _ = additional; + } + + /// Drops extra capacity in the accumulator. + #[inline] + fn shrink_to_fit(&mut self) {} + + /// Captures the accumulator and drops extra capacity before returning an owned copy. + #[inline] + fn finish(mut self) -> Self + where + Self: Sized, + { + self.shrink_to_fit(); + self + } + + /// Creates a "by mutable reference" adaptor for this instance of [`Accumulator`]. + #[inline] + fn by_ref(&mut self) -> &mut Self + where + Self: Sized, + { + self + } +} + +impl Accumulator for &mut A +where + A: Accumulator + ?Sized, +{ + #[inline] + fn extend(&mut self, buffer: &[T]) { + (**self).extend(buffer) + } + + #[inline] + fn reserve(&mut self, additional: usize) { + (**self).reserve(additional) + } + + #[inline] + fn shrink_to_fit(&mut self) { + (**self).shrink_to_fit() + } +} + +impl Accumulator for Vec +where + T: Clone, +{ + #[inline] + fn extend(&mut self, buffer: &[T]) { + self.extend_from_slice(buffer) + } + + #[inline] + fn reserve(&mut self, additional: usize) { + self.reserve(additional) + } + + #[inline] + fn shrink_to_fit(&mut self) { + self.shrink_to_fit() + } +} + +/// Byte Concatenation Trait +pub trait ConcatBytes { + /// Concatenates `self` on the end of the accumulator. + /// + /// # Note + /// + /// Implementations should not ask to reserve additional space for elements in this method. + /// Instead, reimplement [`reserve_concat`](Self::reserve_concat) if the default implementation + /// is not efficient. + fn concat(&self, accumulator: &mut A) + where + A: Accumulator; + + /// Returns a hint to the possible number of bytes that will be accumulated when concatenating + /// `self`. + #[inline] + fn size_hint(&self) -> Option { + None + } + + /// Concatenates `self` on the end of the accumulator after trying to reserve space for it. + #[inline] + fn reserve_concat(&self, accumulator: &mut A) + where + A: Accumulator, + { + if let Some(capacity) = self.size_hint() { + accumulator.reserve(capacity); + } + self.concat(accumulator) + } + + /// Constructs a default accumulator and accumulates over `self`, reserving the appropriate + /// capacity. + #[inline] + fn as_bytes(&self) -> A + where + A: Default + Accumulator, + { + let mut accumulator = A::default(); + self.reserve_concat(&mut accumulator); + accumulator.finish() + } +} + +impl ConcatBytes for T +where + T: Borrow<[u8]>, +{ + #[inline] + fn concat(&self, accumulator: &mut A) + where + A: Accumulator, + { + accumulator.extend(self.borrow()) + } + + #[inline] + fn size_hint(&self) -> Option { + Some(self.borrow().len()) + } +} + +/// Concatenates `$item`s together by building a [`Accumulator`] and running +/// [`ConcatBytes::concat`] over each `$item`. +#[macro_export] +macro_rules! concatenate { + ($($item:expr),*) => { + { + let mut accumulator = ::ark_std::vec::Vec::new(); + $($crate::ConcatBytes::reserve_concat($item, &mut accumulator);)* + $crate::Accumulator::finish(accumulator) + } + } +} diff --git a/manta-crypto/src/constraints.rs b/manta-crypto/src/constraints.rs new file mode 100644 index 000000000..d7eb73b15 --- /dev/null +++ b/manta-crypto/src/constraints.rs @@ -0,0 +1,17 @@ +// Copyright 2019-2021 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 . + +//! Constraint Systems and Zero Knowledge Proofs diff --git a/manta-crypto/src/ies.rs b/manta-crypto/src/ies.rs new file mode 100644 index 000000000..a818d7c90 --- /dev/null +++ b/manta-crypto/src/ies.rs @@ -0,0 +1,178 @@ +// Copyright 2019-2021 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 . + +//! Integrated Encryption Schemes and Encrypted Messages + +use ark_std::rand::{CryptoRng, RngCore}; +use core::{fmt::Debug, hash::Hash}; +use manta_codec::{ScaleDecode, ScaleEncode}; + +/// [`IntegratedEncryptionScheme`] Key Pair +#[derive(derivative::Derivative, ScaleDecode, ScaleEncode)] +#[derivative( + Clone(bound = "I::PublicKey: Clone, I::SecretKey: Clone"), + Copy(bound = "I::PublicKey: Copy, I::SecretKey: Copy"), + Debug(bound = "I::PublicKey: Debug, I::SecretKey: Debug"), + Default(bound = "I::PublicKey: Default, I::SecretKey: Default"), + Eq(bound = "I::PublicKey: Eq, I::SecretKey: Eq"), + Hash(bound = "I::PublicKey: Hash, I::SecretKey: Hash"), + PartialEq(bound = "I::PublicKey: PartialEq, I::SecretKey: PartialEq") +)] +pub struct KeyPair +where + I: IntegratedEncryptionScheme, +{ + /// Public Key + pub public: I::PublicKey, + + /// Secret Key + pub secret: I::SecretKey, +} + +impl KeyPair +where + I: IntegratedEncryptionScheme, +{ + /// Builds a new [`KeyPair`] from a `public` key and a `secret` key. + #[inline] + pub fn new(public: I::PublicKey, secret: I::SecretKey) -> Self { + Self { public, secret } + } +} + +/// Integrated Encryption Scheme Trait +pub trait IntegratedEncryptionScheme { + /// Public Key Type + type PublicKey; + + /// Secret Key Type + type SecretKey; + + /// Plaintext Type + type Plaintext; + + /// Ciphertext Type + type Ciphertext; + + /// Encryption/Decryption Error Type + type Error; + + /// Generates public/secret keypair. + fn keygen(rng: &mut R) -> KeyPair + where + R: CryptoRng + RngCore; + + /// Generates a new keypair and encrypts the `message`, generating an [`EncryptedMessage`], + /// and returning the keypair. + #[inline] + fn keygen_encrypt( + message: &Self::Plaintext, + rng: &mut R, + ) -> Result<(KeyPair, EncryptedMessage), Self::Error> + where + R: CryptoRng + RngCore, + { + let keypair = Self::keygen(rng); + let encrypted_message = Self::encrypt(message, &keypair.public, rng)?; + Ok((keypair, encrypted_message)) + } + + /// Encrypts the `message` with `pk`, generating an [`EncryptedMessage`]. + fn encrypt( + message: &Self::Plaintext, + pk: &Self::PublicKey, + rng: &mut R, + ) -> Result, Self::Error> + where + R: CryptoRng + RngCore; + + /// Decrypts the `message` with `sk`. + fn decrypt( + message: &EncryptedMessage, + sk: &Self::SecretKey, + ) -> Result; +} + +/// Encrypted Message +#[derive(derivative::Derivative, ScaleDecode, ScaleEncode)] +#[derivative( + Clone(bound = "I::Ciphertext: Clone, I::PublicKey: Clone"), + Copy(bound = "I::Ciphertext: Copy, I::PublicKey: Copy"), + Debug(bound = "I::Ciphertext: Debug, I::PublicKey: Debug"), + Default(bound = "I::Ciphertext: Default, I::PublicKey: Default"), + Eq(bound = "I::Ciphertext: Eq, I::PublicKey: Eq"), + Hash(bound = "I::Ciphertext: Hash, I::PublicKey: Hash"), + PartialEq(bound = "I::Ciphertext: PartialEq, I::PublicKey: PartialEq") +)] +pub struct EncryptedMessage +where + I: IntegratedEncryptionScheme, +{ + /// Ciphertext of the Message + pub ciphertext: I::Ciphertext, + + /// Ephemeral Public Key + pub ephemeral_public_key: I::PublicKey, +} + +impl EncryptedMessage +where + I: IntegratedEncryptionScheme, +{ + /// Builds a new [`EncryptedMessage`] from [`I::Ciphertext`] and an ephemeral [`I::PublicKey`]. + /// + /// [`I::Ciphertext`]: IntegratedEncryptionScheme::Ciphertext + /// [`I::PublicKey`]: IntegratedEncryptionScheme::PublicKey + #[inline] + pub fn new(ciphertext: I::Ciphertext, ephemeral_public_key: I::PublicKey) -> Self { + Self { + ciphertext, + ephemeral_public_key, + } + } + + /// Generates a new keypair and encrypts the `message`, generating an [`EncryptedMessage`], + /// and returning the keypair. + #[inline] + pub fn keygen_encrypt( + message: &I::Plaintext, + rng: &mut R, + ) -> Result<(KeyPair, Self), I::Error> + where + R: CryptoRng + RngCore, + { + I::keygen_encrypt(message, rng) + } + + /// Encrypts the `message` with `pk`, generating an [`EncryptedMessage`]. + #[inline] + pub fn encrypt( + message: &I::Plaintext, + pk: &I::PublicKey, + rng: &mut R, + ) -> Result + where + R: CryptoRng + RngCore, + { + I::encrypt(message, pk, rng) + } + + /// Decrypts the `message` with `sk`. + #[inline] + pub fn decrypt(&self, sk: &I::SecretKey) -> Result { + I::decrypt(self, sk) + } +} diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs new file mode 100644 index 000000000..7f59e7e27 --- /dev/null +++ b/manta-crypto/src/lib.rs @@ -0,0 +1,35 @@ +// Copyright 2019-2021 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 . + +//! Cryptographic Primitives Library + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(docsrs, feature(doc_cfg), forbid(broken_intra_doc_links))] + +mod commitment; +mod concat; +mod ies; +mod prf; +mod set; + +pub mod checksum; +pub mod constraints; + +pub use commitment::*; +pub use concat::*; +pub use ies::*; +pub use prf::*; +pub use set::*; diff --git a/manta-crypto/src/prf.rs b/manta-crypto/src/prf.rs new file mode 100644 index 000000000..d70f00855 --- /dev/null +++ b/manta-crypto/src/prf.rs @@ -0,0 +1,46 @@ +// Copyright 2019-2021 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 . + +//! Pseudorandom Function Families + +use core::borrow::Borrow; + +/// Pseudorandom Function Families (PRF) Trait +pub trait PseudorandomFunctionFamily { + /// PRF Seed Type + type Seed: ?Sized; + + /// PRF Input Type + type Input: Default; + + /// PRF Output Type + type Output; + + /// Evaluates the PRF at the `seed` and `input`. + fn evaluate(seed: S, input: I) -> Self::Output + where + S: Borrow, + I: Borrow; + + /// Evaluates the PRF at the `seed` with the default input. + #[inline] + fn evaluate_zero(seed: S) -> Self::Output + where + S: Borrow, + { + Self::evaluate(seed, Self::Input::default()) + } +} diff --git a/manta-crypto/src/set.rs b/manta-crypto/src/set.rs new file mode 100644 index 000000000..b5f8f915b --- /dev/null +++ b/manta-crypto/src/set.rs @@ -0,0 +1,119 @@ +// Copyright 2019-2021 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 . + +//! Sets and Verified Sets + +use core::{fmt::Debug, hash::Hash}; +use manta_codec::{ScaleDecode, ScaleEncode}; + +/// Set Trait +pub trait Set { + /// Item Stored in the [`Set`] + type Item; + + /// Returns `true` if `item` is stored in `self`. + fn contains(&self, item: &Self::Item) -> bool; + + /// Tries to insert the `item` into `self`, returning the item back if it was already + /// contained in `self`. + fn try_insert(&mut self, item: Self::Item) -> Result<(), Self::Item>; + + /// Inserts the `item` into `self`, returning `true` if the item was already contained in + /// `self`. + #[inline] + fn insert(&mut self, item: Self::Item) -> bool { + self.try_insert(item).is_err() + } +} + +/// Verified Containment Trait +pub trait VerifyContainment { + /// Verifies that `self` is a proof that `item` is contained in some [`VerifiedSet`]. + fn verify(&self, public: &Public, item: &Item) -> bool; +} + +/// Containment Proof for a [`VerifiedSet`] +#[derive(derivative::Derivative, ScaleDecode, ScaleEncode)] +#[derivative( + Clone(bound = "S::Public: Clone, S::Secret: Clone"), + Debug(bound = "S::Public: Debug, S::Secret: Debug"), + Default(bound = "S::Public: Default, S::Secret: Default"), + Eq(bound = "S::Public: Eq, S::Secret: Eq"), + Hash(bound = "S::Public: Hash, S::Secret: Hash"), + PartialEq(bound = "S::Public: PartialEq, S::Secret: PartialEq") +)] +pub struct ContainmentProof +where + S: VerifiedSet, +{ + /// Public Input + pub input: S::Public, + + /// Secret Witness + pub witness: S::Secret, +} + +impl ContainmentProof +where + S: VerifiedSet, +{ + /// Builds a new [`ContainmentProof`] from public `input` and secret `witness`. + #[inline] + pub fn new(input: S::Public, witness: S::Secret) -> Self { + Self { input, witness } + } + + /// Verifies that the `item` is contained in some [`VerifiedSet`]. + #[inline] + pub fn verify(&self, item: &S::Item) -> bool { + self.witness.verify(&self.input, item) + } +} + +/// Verified Set Trait +pub trait VerifiedSet { + /// Item Stored in the [`VerifiedSet`] + type Item; + + /// Public Input for [`Item`](Self::Item) Containment + type Public; + + /// Secret Witness for [`Item`](Self::Item) Containment + type Secret: VerifyContainment; + + /// Returns `true` if `public` is a valid input for the current state of `self`. + fn check_public_input(&self, public: &Self::Public) -> bool; + + /// Generates a proof that the given `item` is stored in `self`. + fn get_containment_proof(&self, item: &Self::Item) -> Option>; + + /// Returns `true` if there exists a proof that `item` is stored in `self`. + #[inline] + fn contains(&self, item: &Self::Item) -> bool { + self.get_containment_proof(item).is_some() + } + + /// Tries to insert the `item` into `self`, returning the item back if it was already + /// contained in `self`. + fn try_insert(&mut self, item: Self::Item) -> Result<(), Self::Item>; + + /// Inserts the `item` into `self`, returning `true` if the item was already contained in + /// `self`. + #[inline] + fn insert(&mut self, item: Self::Item) -> bool { + self.try_insert(item).is_err() + } +} From 3f73b87406503309bc15eeb5fb41693328dcf3f5 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" Date: Thu, 2 Sep 2021 19:42:09 -0400 Subject: [PATCH 004/275] add draft accounting primitives --- .editorconfig | 21 +- .rustfmt.toml | 1 - manta-accounting/Cargo.toml | 32 ++ manta-accounting/LICENSE | 1 + manta-accounting/README.md | 1 + manta-accounting/src/account.rs | 699 +++++++++++++++++++++++++++++++ manta-accounting/src/asset.rs | 317 ++++++++++++++ manta-accounting/src/lib.rs | 31 ++ manta-accounting/src/transfer.rs | 44 ++ manta-codec/src/lib.rs | 82 ++-- manta-crypto/Cargo.toml | 7 +- manta-crypto/src/checksum.rs | 36 +- manta-crypto/src/commitment.rs | 51 +-- manta-crypto/src/concat.rs | 235 +++++------ manta-crypto/src/constraints.rs | 155 +++++++ manta-crypto/src/ies.rs | 248 +++++------ manta-crypto/src/lib.rs | 4 +- manta-crypto/src/prf.rs | 38 +- manta-crypto/src/set.rs | 170 ++++---- manta-util/src/lib.rs | 48 +-- 20 files changed, 1751 insertions(+), 470 deletions(-) create mode 100644 manta-accounting/Cargo.toml create mode 120000 manta-accounting/LICENSE create mode 100644 manta-accounting/README.md create mode 100644 manta-accounting/src/account.rs create mode 100644 manta-accounting/src/asset.rs create mode 100644 manta-accounting/src/lib.rs create mode 100644 manta-accounting/src/transfer.rs diff --git a/.editorconfig b/.editorconfig index c504cd0f4..b4aafcadc 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,23 +1,12 @@ root = true [*.rs] -indent_style=tab +charset=utf-8 +end_of_line=lf indent_size=tab +indent_style=tab +insert_final_newline=true +max_line_length=100 tab_width=4 -end_of_line=lf -charset=utf-8 trim_trailing_whitespace=true -max_line_length=100 -insert_final_newline=true -[*.yml] -indent_style=space -indent_size=2 -tab_width=8 -end_of_line=lf - -[*.sh] -indent_style=space -indent_size=2 -tab_width=8 -end_of_line=lf diff --git a/.rustfmt.toml b/.rustfmt.toml index 59845723d..4aefbd7c0 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -1,3 +1,2 @@ -hard_tabs=true imports_granularity="Crate" license_template_path = "FILE_TEMPLATE" diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml new file mode 100644 index 000000000..08b6b058c --- /dev/null +++ b/manta-accounting/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "manta-accounting" +edition = "2018" +version = "0.3.0" +authors = ["Manta Network "] +readme = "README.md" +license-file = "LICENSE" +repository = "https://github.com/Manta-Network/manta-rs" +homepage = "https://github.com/Manta-Network" +documentation = "https://github.com/Manta-Network/manta-rs" +categories = [""] +keywords = [""] +description = "Accounting Primitives for manta-rs." +publish = false + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[badges] +is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } +is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } +maintenance = { status = "actively-developed" } + +[dependencies] +derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } +derive_more = { version = "0.99.16", default-features = false, features = ["add", "add_assign", "display", "from", "mul", "mul_assign", "sum"] } +manta-codec = { path = "../manta-codec", default-features = false, features = ["derive"] } +manta-crypto = { path = "../manta-crypto", default-features = false } +manta-util = { path = "../manta-util", default-features = false } +parity-scale-codec = { version = "2.2.0", default-features = false } +rand = { version = "0.8.4", default-features = false } diff --git a/manta-accounting/LICENSE b/manta-accounting/LICENSE new file mode 120000 index 000000000..ea5b60640 --- /dev/null +++ b/manta-accounting/LICENSE @@ -0,0 +1 @@ +../LICENSE \ No newline at end of file diff --git a/manta-accounting/README.md b/manta-accounting/README.md new file mode 100644 index 000000000..ac7b03154 --- /dev/null +++ b/manta-accounting/README.md @@ -0,0 +1 @@ +# manta-accounting diff --git a/manta-accounting/src/account.rs b/manta-accounting/src/account.rs new file mode 100644 index 000000000..b54b09848 --- /dev/null +++ b/manta-accounting/src/account.rs @@ -0,0 +1,699 @@ +// Copyright 2019-2021 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 . + +//! Identities, Senders, and Receivers + +// FIXME: tweak the secret key api so that it matches with a wallet implementation (i.e. it should +// not necessarily be generated by an `Rng`, but something like a `SecreKeySource` +// FIXME: ensure secret keys cannot be made public by some API call + +use crate::asset::Asset; +use core::{convert::Infallible, fmt::Debug, hash::Hash, marker::PhantomData}; +use manta_codec::{ScaleDecode, ScaleEncode}; +use manta_crypto::{ + concatenate, CommitmentScheme, ConcatBytes, ContainmentProof, EncryptedMessage, + IntegratedEncryptionScheme, KeyPair, PseudorandomFunctionFamily, VerifiedSet, +}; +use rand::{ + distributions::{Distribution, Standard}, + CryptoRng, Rng, RngCore, SeedableRng, +}; + +/// [`Identity`] Configuration Trait +pub trait IdentityConfiguration { + /// Secret Key Type + type SecretKey: Clone; + + /// Pseudorandom Function Family Type + type PseudorandomFunctionFamily: PseudorandomFunctionFamily; + + /// Commitment Scheme Type + type CommitmentScheme: CommitmentScheme; + + /// Seedable Cryptographic Random Number Generator Type + type Rng: CryptoRng + RngCore + SeedableRng; +} + +/// [`PseudorandomFunctionFamily::Input`] Type Alias +pub type PseudorandomFunctionFamilyInput = + <::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Input; + +/// [`PseudorandomFunctionFamily::Output`] Type Alias +pub type PseudorandomFunctionFamilyOutput = + <::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Output; + +/// [`CommitmentScheme::Randomness`] Type Alias +pub type CommitmentSchemeRandomness = + <::CommitmentScheme as CommitmentScheme>::Randomness; + +/// [`CommitmentScheme::Output`] Type Alias +pub type CommitmentSchemeOutput = + <::CommitmentScheme as CommitmentScheme>::Output; + +/// Public Key Type Alias +pub type PublicKey = PseudorandomFunctionFamilyOutput; + +/// Void Number Generator Type Alias +pub type VoidNumberGenerator = PseudorandomFunctionFamilyInput; + +/// Void Number Type Alias +pub type VoidNumber = PseudorandomFunctionFamilyOutput; + +/// Void Number Commitment Randomness Type Alias +pub type VoidNumberCommitmentRandomness = CommitmentSchemeRandomness; + +/// Void Number Commitment Type Alias +pub type VoidNumberCommitment = CommitmentSchemeOutput; + +/// UTXO Randomness Type Alias +pub type UtxoRandomness = CommitmentSchemeRandomness; + +/// UTXO Type Alias +pub type Utxo = CommitmentSchemeOutput; + +/// Public Parameters for using an [`Asset`] +#[derive(derivative::Derivative, ScaleDecode, ScaleEncode)] +#[derivative( + Clone( + bound = "VoidNumberGenerator: Clone, VoidNumberCommitmentRandomness: Clone, UtxoRandomness: Copy" + ), + Copy( + bound = "VoidNumberGenerator: Copy, VoidNumberCommitmentRandomness: Copy, UtxoRandomness: Copy" + ), + Debug( + bound = "VoidNumberGenerator: Debug, VoidNumberCommitmentRandomness: Debug, UtxoRandomness: Debug" + ), + Default( + bound = "VoidNumberGenerator: Default, VoidNumberCommitmentRandomness: Default, UtxoRandomness: Default" + ), + Eq( + bound = "VoidNumberGenerator: Eq, VoidNumberCommitmentRandomness: Eq, UtxoRandomness: Eq" + ), + Hash( + bound = "VoidNumberGenerator: Hash, VoidNumberCommitmentRandomness: Hash, UtxoRandomness: Hash" + ), + PartialEq( + bound = "VoidNumberGenerator: PartialEq, VoidNumberCommitmentRandomness: PartialEq, UtxoRandomness: PartialEq" + ) +)] +pub struct AssetParameters +where + C: IdentityConfiguration, +{ + /// Void Number Generation Parameter + pub void_number_generator: VoidNumberGenerator, + + /// Void Number Commitment Randomness + pub void_number_commitment_randomness: VoidNumberCommitmentRandomness, + + /// UTXO Randomness + pub utxo_randomness: UtxoRandomness, +} + +impl AssetParameters +where + C: IdentityConfiguration, +{ + /// Builds a new [`AssetParameters`]. + #[inline] + pub fn new( + void_number_generator: VoidNumberGenerator, + void_number_commitment_randomness: VoidNumberCommitmentRandomness, + utxo_randomness: UtxoRandomness, + ) -> Self { + Self { + void_number_generator, + void_number_commitment_randomness, + utxo_randomness, + } + } +} + +impl Distribution> for Standard +where + C: IdentityConfiguration, + Standard: Distribution> + + Distribution> + + Distribution>, +{ + #[inline] + fn sample(&self, rng: &mut R) -> AssetParameters { + AssetParameters::new(rng.gen(), rng.gen(), rng.gen()) + } +} + +/// Account Identity +#[derive(derivative::Derivative, ScaleDecode, ScaleEncode)] +#[derivative( + Clone(bound = "C::SecretKey: Clone"), + Copy(bound = "C::SecretKey: Copy"), + Debug(bound = "C::SecretKey: Debug"), + Default(bound = "C::SecretKey: Default"), + Eq(bound = "C::SecretKey: Eq"), + Hash(bound = "C::SecretKey: Hash"), + PartialEq(bound = "C::SecretKey: PartialEq") +)] +pub struct Identity +where + C: IdentityConfiguration, +{ + /// Secret Key + secret_key: C::SecretKey, +} + +impl Identity +where + C: IdentityConfiguration, +{ + /// Generates a new `Identity` from a `C::SecretKey`. + /// + /// # API Note + /// + /// This function is intentionally private so that secret keys are not part of the + /// public interface. + #[inline] + fn new(secret_key: C::SecretKey) -> Self { + Self { secret_key } + } + + /// Returns the public key associated with this identity. + #[inline] + pub fn public_key(&self) -> PublicKey { + C::PseudorandomFunctionFamily::evaluate_zero(&self.secret_key) + } + + /// Generates the associated `C::Rng` and a `AssetParameters` for this identity. + /// + /// # API Note + /// + /// This function is intentionally private so that random number generators are not part of + /// the public interface. See [`Self::parameters`] for access to the associated + /// `parameters`. + /// + /// # Implementation Note + /// + /// Contributors should always use this function when generating an `rng` or a + /// `parameters` in the folowing ways: + /// + /// ```text + /// 1. [BOTH] let (mut rng, parameters) = self.rng_and_parameters(); + /// 2. [RNG] let (mut rng, _) = self.rng_and_parameters(); + /// 2. [PAIR] let parameters = self.parameters(); + /// ``` + /// + /// This is important because we need to preserve the order in which objects are randomly + /// generated across different functions. The `parameters` is always generated immediately + /// after creation of the random number generator. + #[inline] + fn rng_and_parameters(&self) -> (C::Rng, AssetParameters) + where + Standard: Distribution>, + { + let mut rng = C::Rng::from_seed(self.secret_key.clone()); + let parameters = rng.gen(); + (rng, parameters) + } + + /// Generates [`AssetParameters`] for assets that are used by this identity. + #[inline] + pub fn parameters(&self) -> AssetParameters + where + Standard: Distribution>, + { + let (_, parameters) = self.rng_and_parameters(); + parameters + } + + /// Generates the associated `C::Rng`, `AssetParameters`, and `KeyPair` for + /// this identity. + /// + /// # API Note + /// + /// This function is intentionally private so that random number generators are not part of + /// the public interface. See [`Self::parameters_and_asset_keypair`] for access to the + /// associated `parameters` and `asset_keypair`. + /// + /// # Implementation Note + /// + /// See [`Self::rng_and_parameters`]. + #[inline] + fn rng_and_parameters_and_asset_keypair(&self) -> (C::Rng, AssetParameters, KeyPair) + where + I: IntegratedEncryptionScheme, + Standard: Distribution<AssetParameters<C>>, + { + let (mut rng, parameters) = self.rng_and_parameters(); + let asset_keypair = I::keygen(&mut rng); + (rng, parameters, asset_keypair) + } + + /// Generates [`AssetParameters`] and a [`KeyPair`] for assets that are used by this identity. + #[inline] + pub fn parameters_and_asset_keypair<I>(&self) -> (AssetParameters<C>, KeyPair<I>) + where + I: IntegratedEncryptionScheme<Plaintext = Asset>, + Standard: Distribution<AssetParameters<C>>, + { + let (_, parameters, asset_keypair) = self.rng_and_parameters_and_asset_keypair::<I>(); + (parameters, asset_keypair) + } + + /// Generates a [`KeyPair`] for assets that are used by this identity. + #[inline] + pub fn asset_keypair<I>(&self) -> KeyPair<I> + where + I: IntegratedEncryptionScheme<Plaintext = Asset>, + Standard: Distribution<AssetParameters<C>>, + { + let (_, asset_keypair) = self.parameters_and_asset_keypair(); + asset_keypair + } + + /// Generates a new void number using the `void_number_generator` parameter. + #[inline] + pub fn void_number(&self, void_number_generator: &VoidNumberGenerator<C>) -> VoidNumber<C> { + C::PseudorandomFunctionFamily::evaluate(&self.secret_key, void_number_generator) + } + + /// Generates a new void number commitment using the `void_number_generator` and + /// `void_number_commitment_randomness`. + #[inline] + pub fn void_number_commitment( + &self, + commitment_scheme: &C::CommitmentScheme, + void_number_generator: &VoidNumberGenerator<C>, + void_number_commitment_randomness: &VoidNumberCommitmentRandomness<C>, + ) -> VoidNumberCommitment<C> + where + PublicKey<C>: ConcatBytes, + VoidNumberGenerator<C>: ConcatBytes, + { + generate_void_number_commitment::<C>( + commitment_scheme, + &self.public_key(), + void_number_generator, + void_number_commitment_randomness, + ) + } + + /// Generates a [`Utxo`] for an `asset` using the `parameters`. + #[inline] + pub fn utxo( + &self, + commitment_scheme: &C::CommitmentScheme, + asset: &Asset, + parameters: &AssetParameters<C>, + ) -> Utxo<C> + where + PublicKey<C>: ConcatBytes, + VoidNumberGenerator<C>: ConcatBytes, + VoidNumberCommitment<C>: ConcatBytes, + { + generate_utxo::<C>( + commitment_scheme, + asset, + &self.void_number_commitment( + commitment_scheme, + &parameters.void_number_generator, + &parameters.void_number_commitment_randomness, + ), + &parameters.utxo_randomness, + ) + } + + /// Builds a new [`Sender`] for the given `asset`. + pub fn into_sender<S>( + self, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + utxo_set: &S, + ) -> Result<Sender<C, S>, S::ContainmentError> + where + S: VerifiedSet<Item = Utxo<C>>, + Standard: Distribution<AssetParameters<C>>, + PublicKey<C>: ConcatBytes, + VoidNumberGenerator<C>: ConcatBytes, + VoidNumberCommitment<C>: ConcatBytes, + { + let parameters = self.parameters(); + let utxo = self.utxo(commitment_scheme, &asset, &parameters); + let utxo_containment_proof = utxo_set.get_containment_proof(&utxo)?; + Ok(Sender { + asset, + void_number: self.void_number(&parameters.void_number_generator), + parameters, + utxo, + utxo_containment_proof, + identity: self, + }) + } + + /// Builds a new [`ShieldedIdentity`]-[`Spend`] pair for the given `asset`. + pub fn into_receiver<I>( + self, + commitment_scheme: &C::CommitmentScheme, + ) -> (ShieldedIdentity<C, I>, Spend<C, I>) + where + I: IntegratedEncryptionScheme<Plaintext = Asset>, + Standard: Distribution<AssetParameters<C>>, + PublicKey<C>: ConcatBytes, + VoidNumberGenerator<C>: ConcatBytes, + { + let (parameters, asset_keypair) = self.parameters_and_asset_keypair::<I>(); + let void_number_commitment = self.void_number_commitment( + commitment_scheme, + &parameters.void_number_generator, + &parameters.void_number_commitment_randomness, + ); + ( + ShieldedIdentity { + utxo_randomness: parameters.utxo_randomness, + void_number_commitment, + asset_public_key: asset_keypair.public, + }, + Spend { + identity: self, + asset_secret_key: asset_keypair.secret, + }, + ) + } +} + +impl<C> Distribution<Identity<C>> for Standard +where + C: IdentityConfiguration, + Standard: Distribution<C::SecretKey>, +{ + #[inline] + fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> Identity<C> { + Identity::new(rng.gen()) + } +} + +/// Shielded Identity +pub struct ShieldedIdentity<C, I> +where + C: IdentityConfiguration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, +{ + /// UTXO Randomness + pub utxo_randomness: UtxoRandomness<C>, + + /// Void Number Commitment + pub void_number_commitment: VoidNumberCommitment<C>, + + /// Encrypted [`Asset`] Public Key + pub asset_public_key: I::PublicKey, +} + +impl<C, I> ShieldedIdentity<C, I> +where + C: IdentityConfiguration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, +{ + /// Generates a [`Utxo`] for an `asset` using the pre-computed + /// [`void_number_commitment`](Self::void_number_commitment) and + /// [`utxo_randomness`](Self::utxo_randomness). + #[inline] + pub fn utxo(&self, commitment_scheme: &C::CommitmentScheme, asset: &Asset) -> Utxo<C> + where + VoidNumberCommitment<C>: ConcatBytes, + { + generate_utxo::<C>( + commitment_scheme, + asset, + &self.void_number_commitment, + &self.utxo_randomness, + ) + } + + /// Generates a [`Receiver`] from a [`ShieldedIdentity`]. + #[inline] + pub fn into_receiver<R>( + self, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + rng: &mut R, + ) -> Result<Receiver<C, I>, I::Error> + where + R: CryptoRng + RngCore, + VoidNumberCommitment<C>: ConcatBytes, + { + Ok(Receiver { + encrypted_asset: I::encrypt(&asset, &self.asset_public_key, rng)?, + utxo: self.utxo(commitment_scheme, &asset), + asset, + utxo_randomness: self.utxo_randomness, + void_number_commitment: self.void_number_commitment, + }) + } +} + +/// [`Spend`] Error +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "I::Error: Clone, S::ContainmentError: Clone"), + Copy(bound = "I::Error: Copy, S::ContainmentError: Copy"), + Debug(bound = "I::Error: Debug, S::ContainmentError: Debug"), + Eq(bound = "I::Error: Eq, S::ContainmentError: Eq"), + Hash(bound = "I::Error: Hash, S::ContainmentError: Hash"), + PartialEq(bound = "I::Error: PartialEq, S::ContainmentError: PartialEq") +)] +pub enum SpendError<C, I, S> +where + C: IdentityConfiguration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + S: VerifiedSet<Item = Utxo<C>>, +{ + /// Encryption Error + EncryptionError(I::Error), + + /// Missing UTXO Containment Proof + MissingUtxo(S::ContainmentError), + + /// Parameter Marker + #[doc(hidden)] + __(Infallible, PhantomData<C>), +} + +/// Spending Information +pub struct Spend<C, I> +where + C: IdentityConfiguration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, +{ + /// Spender Identity + identity: Identity<C>, + + /// Encrypted [`Asset`] Secret Key + asset_secret_key: I::SecretKey, +} + +impl<C, I> Spend<C, I> +where + C: IdentityConfiguration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, +{ + /// Returns the public key associated with this spend. + #[inline] + pub fn public_key(&self) -> PublicKey<C> { + self.identity.public_key() + } + + /// Builds a new [`Sender`] for the given `encrypted_asset`. + #[inline] + pub fn into_sender<S>( + self, + commitment_scheme: &C::CommitmentScheme, + encrypted_asset: EncryptedMessage<I>, + utxo_set: &S, + ) -> Result<Sender<C, S>, SpendError<C, I, S>> + where + S: VerifiedSet<Item = Utxo<C>>, + Standard: Distribution<AssetParameters<C>>, + PublicKey<C>: ConcatBytes, + VoidNumberGenerator<C>: ConcatBytes, + VoidNumberCommitment<C>: ConcatBytes, + { + self.identity + .into_sender( + commitment_scheme, + encrypted_asset + .decrypt(&self.asset_secret_key) + .map_err(SpendError::EncryptionError)?, + utxo_set, + ) + .map_err(SpendError::MissingUtxo) + } +} + +/* TODO: +/// Sender Public Data +pub struct SenderPublicData<C, S> +where + C: IdentityConfiguration, + S: VerifiedSet<Item = Utxo<C>>, +{ + /// Void Number + pub void_number: VoidNumber<C>, + + /// Void Number Commitment + pub void_number_commitment: VoidNumberCommitment<C>, + + /// UTXO Containment Public Input + pub utxo_containment_public_input: S::Public, +} + +/// Sender Circuit Data +pub struct SenderCircuitData<C, S> +where + C: IdentityConfiguration, + S: VerifiedSet<Item = Utxo<C>>, +{ + /// Sender Identity + pub identity: Identity<C>, + + /// Asset + pub asset: Asset, + + /// Asset Generation Parameters + pub parameters: AssetParameters<C>, + + /// Unspent Transaction Output + pub utxo: Utxo<C>, + + /// UTXO Containment Proof + pub utxo_containment_proof: ContainmentProof<S>, +} +*/ + +/// Sender +pub struct Sender<C, S> +where + C: IdentityConfiguration, + S: VerifiedSet<Item = Utxo<C>>, +{ + /// Sender Identity + pub identity: Identity<C>, + + /// Asset + pub asset: Asset, + + /// Asset Generation Parameters + pub parameters: AssetParameters<C>, + + /// Void Number + pub void_number: VoidNumber<C>, + + /// Unspent Transaction Output + pub utxo: Utxo<C>, + + /// UTXO Containment Proof + pub utxo_containment_proof: ContainmentProof<S>, +} + +/* TODO: +/// Receiver Public Data +pub struct ReceiverPublicData<C, I> +where + C: IdentityConfiguration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, +{ + /// Void Number Commitment + pub void_number_commitment: VoidNumberCommitment<C>, + + /// Unspent Transaction Output + pub utxo: Utxo<C>, + + /// Encrypted [`Asset`] + pub encrypted_asset: EncryptedMessage<I>, +} + +/// Receiver Circuit Data +pub struct ReceiverCircuitData<C> +where + C: IdentityConfiguration, +{ + /// Asset + pub asset: Asset, + + /// UTXO Randomness + pub utxo_randomness: UtxoRandomness<C>, + + /// Void Number Commitment + pub void_number_commitment: VoidNumberCommitment<C>, + + /// Unspent Transaction Output + pub utxo: Utxo<C>, +} +*/ + +/// Receiver +pub struct Receiver<C, I> +where + C: IdentityConfiguration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, +{ + /// Asset + pub asset: Asset, + + /// UTXO Randomness + pub utxo_randomness: UtxoRandomness<C>, + + /// Void Number Commitment + pub void_number_commitment: VoidNumberCommitment<C>, + + /// Unspent Transaction Output + pub utxo: Utxo<C>, + + /// Encrypted [`Asset`] + pub encrypted_asset: EncryptedMessage<I>, +} + +/// Generates a [`VoidNumberCommitment`] from a given `public_key`, `void_number_generator`, and +/// `void_number_commitment_randomness`. +#[inline] +pub fn generate_void_number_commitment<C>( + commitment_scheme: &C::CommitmentScheme, + public_key: &PublicKey<C>, + void_number_generator: &VoidNumberGenerator<C>, + void_number_commitment_randomness: &VoidNumberCommitmentRandomness<C>, +) -> VoidNumberCommitment<C> +where + C: IdentityConfiguration, + PublicKey<C>: ConcatBytes, + VoidNumberGenerator<C>: ConcatBytes, +{ + commitment_scheme.commit( + concatenate!(public_key, void_number_generator), + void_number_commitment_randomness, + ) +} + +/// Generates a [`Utxo`] from a given `asset`, `void_number_commitment`, and `utxo_randomness`. +#[inline] +pub fn generate_utxo<C>( + commitment_scheme: &C::CommitmentScheme, + asset: &Asset, + void_number_commitment: &VoidNumberCommitment<C>, + utxo_randomness: &UtxoRandomness<C>, +) -> Utxo<C> +where + C: IdentityConfiguration, + VoidNumberCommitment<C>: ConcatBytes, +{ + commitment_scheme.commit(concatenate!(asset, void_number_commitment), utxo_randomness) +} diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs new file mode 100644 index 000000000..f74d5042e --- /dev/null +++ b/manta-accounting/src/asset.rs @@ -0,0 +1,317 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Assets + +// TODO: add macro to build `AssetId` and `AssetBalance` + +use alloc::vec::Vec; +use core::{ + fmt::Debug, + hash::Hash, + ops::{Add, AddAssign, Mul, Sub, SubAssign}, +}; +use derive_more::{ + Add, AddAssign, Display, Div, DivAssign, From, Mul, MulAssign, Product, Sub, SubAssign, Sum, +}; +use manta_codec::{ScaleDecode, ScaleEncode}; +use manta_crypto::{Accumulator, ConcatBytes}; +use manta_util::try_into_array_unchecked; +use rand::{ + distributions::{Distribution, Standard}, + Rng, RngCore, +}; + +type AssetIdType = u32; + +/// Asset Id Type +#[derive( + Add, + AddAssign, + Clone, + Copy, + Debug, + Default, + Display, + Div, + DivAssign, + Eq, + From, + Hash, + Mul, + MulAssign, + Ord, + PartialEq, + PartialOrd, + Product, + ScaleDecode, + ScaleEncode, + Sub, + SubAssign, + Sum, +)] +#[from(forward)] +pub struct AssetId( + /// [`Asset`] Id + pub AssetIdType, +); + +impl AssetId { + /// The size of this type in bits. + pub const BITS: u32 = AssetIdType::BITS; + + /// The size of this type in bytes. + pub const SIZE: usize = (Self::BITS / 8) as usize; + + /// Converts `self` into a byte array. + #[inline] + pub const fn into_bytes(self) -> [u8; Self::SIZE] { + self.0.to_le_bytes() + } + + /// Converts a byte array into `self`. + #[inline] + pub const fn from_bytes(bytes: [u8; Self::SIZE]) -> Self { + Self(AssetIdType::from_le_bytes(bytes)) + } +} + +impl Distribution<AssetId> for Standard { + #[inline] + fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> AssetId { + AssetId(rng.gen()) + } +} + +impl From<AssetId> for [u8; AssetId::SIZE] { + #[inline] + fn from(entry: AssetId) -> Self { + entry.into_bytes() + } +} + +impl Mul<AssetId> for AssetIdType { + type Output = AssetIdType; + + #[inline] + fn mul(self, rhs: AssetId) -> Self::Output { + self * rhs.0 + } +} + +type AssetBalanceType = u128; + +/// Asset Balance Type +#[derive( + Add, + AddAssign, + Clone, + Copy, + Debug, + Default, + Display, + Div, + DivAssign, + Eq, + From, + Hash, + Mul, + MulAssign, + Ord, + PartialEq, + PartialOrd, + Product, + ScaleDecode, + ScaleEncode, + Sub, + SubAssign, + Sum, +)] +#[from(forward)] +pub struct AssetBalance( + /// [`Asset`] Balance + pub AssetBalanceType, +); + +impl AssetBalance { + /// The size of this type in bits. + pub const BITS: u32 = AssetBalanceType::BITS; + + /// The size of this type in bytes. + pub const SIZE: usize = (Self::BITS / 8) as usize; + + /// Converts `self` into a byte array. + #[inline] + pub const fn into_bytes(self) -> [u8; Self::SIZE] { + self.0.to_le_bytes() + } + + /// Converts a byte array into `self`. + #[inline] + pub const fn from_bytes(bytes: [u8; Self::SIZE]) -> Self { + Self(AssetBalanceType::from_le_bytes(bytes)) + } +} + +impl Distribution<AssetBalance> for Standard { + #[inline] + fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> AssetBalance { + AssetBalance(rng.gen()) + } +} + +impl From<AssetBalance> for [u8; AssetBalance::SIZE] { + #[inline] + fn from(entry: AssetBalance) -> Self { + entry.into_bytes() + } +} + +impl Mul<AssetBalance> for AssetBalanceType { + type Output = AssetBalanceType; + + #[inline] + fn mul(self, rhs: AssetBalance) -> Self::Output { + self * rhs.0 + } +} + +/// Asset +#[derive( + Clone, Copy, Debug, Default, Display, Eq, From, Hash, PartialEq, ScaleDecode, ScaleEncode, +)] +#[display(fmt = "{{id: {}, value: {}}}", id, value)] +pub struct Asset { + /// Asset Id + pub id: AssetId, + + /// Asset Value + pub value: AssetBalance, +} + +impl Asset { + /// The size of the data in this type in bits. + pub const BITS: u32 = AssetId::BITS + AssetBalance::BITS; + + /// The size of the data in this type in bytes. + pub const SIZE: usize = (Self::BITS / 8) as usize; + + /// Builds a new [`Asset`] from an `id` and a `value`. + #[inline] + pub const fn new(id: AssetId, value: AssetBalance) -> Self { + Self { id, value } + } + + /// Builds a new [`Asset`] from an existing one with a new `value`. + #[inline] + pub const fn with_value(&self, value: AssetBalance) -> Self { + Self::new(self.id, value) + } + + /// Converts `self` into a byte array. + #[inline] + pub fn into_bytes(self) -> [u8; Self::SIZE] { + try_into_array_unchecked(self.as_bytes::<Vec<u8>>()) + } + + /// Converts a byte array into `self`. + #[inline] + pub fn from_bytes(bytes: [u8; Self::SIZE]) -> Self { + let split = (AssetId::BITS / 8) as usize; + Self::new( + AssetId::from_bytes(try_into_array_unchecked(&bytes[..split])), + AssetBalance::from_bytes(try_into_array_unchecked(&bytes[split..])), + ) + } +} + +impl Add<AssetBalance> for Asset { + type Output = Self; + + #[inline] + fn add(mut self, rhs: AssetBalance) -> Self::Output { + self += rhs; + self + } +} + +impl AddAssign<AssetBalance> for Asset { + #[inline] + fn add_assign(&mut self, rhs: AssetBalance) { + self.value += rhs; + } +} + +impl Sub<AssetBalance> for Asset { + type Output = Self; + + #[inline] + fn sub(mut self, rhs: AssetBalance) -> Self::Output { + self -= rhs; + self + } +} + +impl SubAssign<AssetBalance> for Asset { + #[inline] + fn sub_assign(&mut self, rhs: AssetBalance) { + self.value -= rhs; + } +} + +impl ConcatBytes for Asset { + #[inline] + fn concat<A>(&self, accumulator: &mut A) + where + A: Accumulator<u8>, + { + accumulator.extend(&self.id.into_bytes()); + accumulator.extend(&self.value.into_bytes()); + } + + #[inline] + fn size_hint(&self) -> Option<usize> { + Some(Self::SIZE) + } +} + +impl From<[u8; Self::SIZE]> for Asset { + #[inline] + fn from(array: [u8; Self::SIZE]) -> Self { + Self::from_bytes(array) + } +} + +impl From<Asset> for [u8; Asset::SIZE] { + #[inline] + fn from(entry: Asset) -> Self { + entry.into_bytes() + } +} + +impl From<Asset> for (AssetId, AssetBalance) { + #[inline] + fn from(asset: Asset) -> Self { + (asset.id, asset.value) + } +} + +impl Distribution<Asset> for Standard { + #[inline] + fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> Asset { + Asset::new(rng.gen(), rng.gen()) + } +} diff --git a/manta-accounting/src/lib.rs b/manta-accounting/src/lib.rs new file mode 100644 index 000000000..afc194be9 --- /dev/null +++ b/manta-accounting/src/lib.rs @@ -0,0 +1,31 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Accounting Primitives + +#![no_std] +#![cfg_attr(docsrs, feature(doc_cfg), forbid(broken_intra_doc_links))] + +extern crate alloc; +extern crate derive_more; + +mod account; +mod asset; +mod transfer; + +pub use account::*; +pub use asset::*; +pub use transfer::*; diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs new file mode 100644 index 000000000..8e0a5c01b --- /dev/null +++ b/manta-accounting/src/transfer.rs @@ -0,0 +1,44 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Transfer Protocol + +use crate::{ + account::{IdentityConfiguration, Receiver, Sender, Utxo}, + asset::Asset, +}; +use manta_crypto::{IntegratedEncryptionScheme, VerifiedSet}; + +/// Transfer Configuration Trait +pub trait TransferConfiguration: IdentityConfiguration { + /// Integrated Encryption Scheme for [`Asset`] + type IntegratedEncryptionScheme: IntegratedEncryptionScheme<Plaintext = Asset>; + + /// Verified Set for [`Utxo`] + type UtxoSet: VerifiedSet<Item = Utxo<Self>>; +} + +/// Private Transfer +pub struct PrivateTransfer<T, const SENDERS: usize, const RECEIVERS: usize> +where + T: TransferConfiguration, +{ + /// Secret Senders + pub senders: [Sender<T, T::UtxoSet>; SENDERS], + + /// Secret Receivers + pub receivers: [Receiver<T, T::IntegratedEncryptionScheme>; RECEIVERS], +} diff --git a/manta-codec/src/lib.rs b/manta-codec/src/lib.rs index 7376ce145..35962ce42 100644 --- a/manta-codec/src/lib.rs +++ b/manta-codec/src/lib.rs @@ -27,29 +27,29 @@ use manta_util::from_variant_impl; pub use ark_serialize::{Read, SerializationError as ArkCodecError, Write}; pub use scale_codec::{ - Decode as ScaleDecode, Encode as ScaleEncode, Error as ScaleCodecError, Input, Output, + Decode as ScaleDecode, Encode as ScaleEncode, Error as ScaleCodecError, Input, Output, }; /// Codec Error Type #[derive(Debug, Display)] pub enum Error { - /// Arkworks Codec Error - #[displaydoc("Arkworks Codec Error: {0}")] - ArkCodecError(ArkCodecError), + /// Arkworks Codec Error + #[displaydoc("Arkworks Codec Error: {0}")] + ArkCodecError(ArkCodecError), - /// SCALE Codec Error - #[displaydoc("SCALE Codec Error: {0}")] - ScaleCodecError(ScaleCodecError), + /// SCALE Codec Error + #[displaydoc("SCALE Codec Error: {0}")] + ScaleCodecError(ScaleCodecError), } from_variant_impl!(Error, ArkCodecError, ArkCodecError); from_variant_impl!(Error, ScaleCodecError, ScaleCodecError); impl From<ark_std::io::Error> for Error { - #[inline] - fn from(err: ark_std::io::Error) -> Self { - Self::ArkCodecError(ArkCodecError::IoError(err)) - } + #[inline] + fn from(err: ark_std::io::Error) -> Self { + Self::ArkCodecError(ArkCodecError::IoError(err)) + } } impl ark_std::error::Error for Error {} @@ -65,23 +65,23 @@ pub trait DefaultDecode: ScaleDecode {} /// Decoding Trait pub trait Decode: Sized { - /// Decodes `Self` from the reader. - fn decode<R>(reader: &mut R) -> DecodeResult<Self> - where - R: Input + Read; + /// Decodes `Self` from the reader. + fn decode<R>(reader: &mut R) -> DecodeResult<Self> + where + R: Input + Read; } impl<D> Decode for D where - D: DefaultDecode, + D: DefaultDecode, { - #[inline] - fn decode<R>(reader: &mut R) -> DecodeResult<Self> - where - R: Input + Read, - { - scale_decode(reader) - } + #[inline] + fn decode<R>(reader: &mut R) -> DecodeResult<Self> + where + R: Input + Read, + { + scale_decode(reader) + } } /// Default Encode Implementation Marker Trait @@ -89,23 +89,23 @@ pub trait DefaultEncode: ScaleEncode {} /// Encoding Trait pub trait Encode { - /// Encodes `self` to the writer. - fn encode<W>(&self, writer: &mut W) -> EncodeResult - where - W: Output + Write; + /// Encodes `self` to the writer. + fn encode<W>(&self, writer: &mut W) -> EncodeResult + where + W: Output + Write; } impl<E> Encode for E where - E: DefaultEncode, + E: DefaultEncode, { - #[inline] - fn encode<W>(&self, writer: &mut W) -> EncodeResult - where - W: Output + Write, - { - scale_encode(self, writer) - } + #[inline] + fn encode<W>(&self, writer: &mut W) -> EncodeResult + where + W: Output + Write, + { + scale_encode(self, writer) + } } /// Codec Trait @@ -117,18 +117,18 @@ impl<S> Codec for S where S: Decode + Encode {} #[inline] pub fn scale_decode<T, R>(reader: &mut R) -> DecodeResult<T> where - T: ScaleDecode, - R: Input, + T: ScaleDecode, + R: Input, { - Ok(T::decode(reader)?) + Ok(T::decode(reader)?) } /// Encodes a value `t` to `writer` using the SCALE codec. #[inline] pub fn scale_encode<T, W>(t: &T, writer: &mut W) -> EncodeResult where - T: ScaleEncode, - W: Write, + T: ScaleEncode, + W: Write, { - Ok(writer.write_all(t.encode().as_ref())?) + Ok(writer.write_all(t.encode().as_ref())?) } diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index 52ea5a6a4..a1272f0aa 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -22,13 +22,8 @@ is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } -[features] -default = [] -std = [] - [dependencies] -ark-std = { version = "0.3.0", default-features = false } -blake2 = { version = "0.9.2", default-features = false } derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } manta-codec = { path = "../manta-codec", default-features = false, features = ["derive"] } parity-scale-codec = { version = "2.2.0", default-features = false } +rand = { version = "0.8.4", default-features = false } diff --git a/manta-crypto/src/checksum.rs b/manta-crypto/src/checksum.rs index 0cafbb008..5deb133ef 100644 --- a/manta-crypto/src/checksum.rs +++ b/manta-crypto/src/checksum.rs @@ -19,30 +19,16 @@ // TODO: move this out of this crate, this is for `pallet-manta-pay` when we check local parameters // against stored ones. maybe this can go in `manta-pay`? -use ark_std::vec::Vec; -use blake2::{Blake2s, Digest}; +/// Checksum Equality +pub trait ChecksumEq<SumAlg, Rhs: ?Sized = Self> { + /// Returns `true` if `self` and `other` have the same checksum. + #[must_use] + fn checksum_eq(&self, other: &Rhs) -> bool; -/// Checksum Error Type -pub struct Error; - -/// Checksum Trait -pub trait Checksum<const N: usize> { - /// Computes the checksum of `self`. - fn get_checksum(&self) -> Result<[u8; N], Error>; -} - -impl Checksum<4> for Vec<u8> { - fn get_checksum(&self) -> Result<[u8; 4], Error> { - let mut hasher = Blake2s::new(); - hasher.update(&self); - let digest = hasher.finalize(); - let mut int_res = [0; 32]; - int_res.copy_from_slice(digest.as_slice()); - let mut hasher_two = Blake2s::new(); - hasher_two.update(int_res); - let final_digest = hasher_two.finalize(); - let mut res = [0; 4]; - res.copy_from_slice(&final_digest.as_slice()[0..4]); - Ok(res) - } + /// Returns `true` if `self` and `other` have different checksums. + #[inline] + #[must_use] + fn checksum_ne(&self, other: &Rhs) -> bool { + !self.checksum_eq(other) + } } diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index e2d02bb77..debdb2cd1 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -16,36 +16,37 @@ //! Commitments -use ark_std::{borrow::Borrow, rand::Rng}; +use core::borrow::Borrow; +use rand::RngCore; /// Commitment Scheme pub trait CommitmentScheme { - /// Commitment Randomness Parameter Type - type Randomness; + /// Commitment Randomness Parameter Type + type Randomness; - /// Commitment Output Type - type Output: PartialEq; + /// Commitment Output Type + type Output: PartialEq; - /// Samples random commitment paramters. - fn setup<R>(rng: &mut R) -> Self - where - R: Rng; + /// Samples random commitment paramters. + fn setup<R>(rng: &mut R) -> Self + where + R: RngCore; - /// Commits the `input` with the given `randomness` parameter. - fn commit<I, R>(&self, input: I, randomness: R) -> Self::Output - where - I: Borrow<[u8]>, - R: Borrow<Self::Randomness>; + /// Commits the `input` with the given `randomness` parameter. + fn commit<I, R>(&self, input: I, randomness: R) -> Self::Output + where + I: Borrow<[u8]>, + R: Borrow<Self::Randomness>; - /// Checks that the `output` matches the commitment of the `input` with the given `randomness` - /// parameter. - #[inline] - fn check_commitment<I, R, O>(&self, input: I, randomness: R, output: O) -> bool - where - I: Borrow<[u8]>, - R: Borrow<Self::Randomness>, - O: Borrow<Self::Output>, - { - &self.commit(input, randomness) == output.borrow() - } + /// Checks that the `output` matches the commitment of the `input` with the given `randomness` + /// parameter. + #[inline] + fn check_commitment<I, R, O>(&self, input: I, randomness: R, output: O) -> bool + where + I: Borrow<[u8]>, + R: Borrow<Self::Randomness>, + O: Borrow<Self::Output>, + { + &self.commit(input, randomness) == output.borrow() + } } diff --git a/manta-crypto/src/concat.rs b/manta-crypto/src/concat.rs index 896e87959..c6b2c0059 100644 --- a/manta-crypto/src/concat.rs +++ b/manta-crypto/src/concat.rs @@ -16,144 +16,145 @@ //! Concatenation and Byte Accumulators -use ark_std::{borrow::Borrow, vec::Vec}; +use alloc::vec::Vec; +use core::borrow::Borrow; /// Accumulation Trait pub trait Accumulator<T> { - /// Extends the current accumulator by a `buffer` of elements. - fn extend(&mut self, buffer: &[T]); - - /// Reserves space in the accumulator for `additional` more elements. - #[inline] - fn reserve(&mut self, additional: usize) { - let _ = additional; - } - - /// Drops extra capacity in the accumulator. - #[inline] - fn shrink_to_fit(&mut self) {} - - /// Captures the accumulator and drops extra capacity before returning an owned copy. - #[inline] - fn finish(mut self) -> Self - where - Self: Sized, - { - self.shrink_to_fit(); - self - } - - /// Creates a "by mutable reference" adaptor for this instance of [`Accumulator`]. - #[inline] - fn by_ref(&mut self) -> &mut Self - where - Self: Sized, - { - self - } + /// Extends the current accumulator by a `buffer` of elements. + fn extend(&mut self, buffer: &[T]); + + /// Reserves space in the accumulator for `additional` more elements. + #[inline] + fn reserve(&mut self, additional: usize) { + let _ = additional; + } + + /// Drops extra capacity in the accumulator. + #[inline] + fn shrink_to_fit(&mut self) {} + + /// Captures the accumulator and drops extra capacity before returning an owned copy. + #[inline] + fn finish(mut self) -> Self + where + Self: Sized, + { + self.shrink_to_fit(); + self + } + + /// Creates a "by mutable reference" adaptor for this instance of [`Accumulator`]. + #[inline] + fn by_ref(&mut self) -> &mut Self + where + Self: Sized, + { + self + } } impl<T, A> Accumulator<T> for &mut A where - A: Accumulator<T> + ?Sized, + A: Accumulator<T> + ?Sized, { - #[inline] - fn extend(&mut self, buffer: &[T]) { - (**self).extend(buffer) - } - - #[inline] - fn reserve(&mut self, additional: usize) { - (**self).reserve(additional) - } - - #[inline] - fn shrink_to_fit(&mut self) { - (**self).shrink_to_fit() - } + #[inline] + fn extend(&mut self, buffer: &[T]) { + (**self).extend(buffer) + } + + #[inline] + fn reserve(&mut self, additional: usize) { + (**self).reserve(additional) + } + + #[inline] + fn shrink_to_fit(&mut self) { + (**self).shrink_to_fit() + } } impl<T> Accumulator<T> for Vec<T> where - T: Clone, + T: Clone, { - #[inline] - fn extend(&mut self, buffer: &[T]) { - self.extend_from_slice(buffer) - } - - #[inline] - fn reserve(&mut self, additional: usize) { - self.reserve(additional) - } - - #[inline] - fn shrink_to_fit(&mut self) { - self.shrink_to_fit() - } + #[inline] + fn extend(&mut self, buffer: &[T]) { + self.extend_from_slice(buffer) + } + + #[inline] + fn reserve(&mut self, additional: usize) { + self.reserve(additional) + } + + #[inline] + fn shrink_to_fit(&mut self) { + self.shrink_to_fit() + } } /// Byte Concatenation Trait pub trait ConcatBytes { - /// Concatenates `self` on the end of the accumulator. - /// - /// # Note - /// - /// Implementations should not ask to reserve additional space for elements in this method. - /// Instead, reimplement [`reserve_concat`](Self::reserve_concat) if the default implementation - /// is not efficient. - fn concat<A>(&self, accumulator: &mut A) - where - A: Accumulator<u8>; - - /// Returns a hint to the possible number of bytes that will be accumulated when concatenating - /// `self`. - #[inline] - fn size_hint(&self) -> Option<usize> { - None - } - - /// Concatenates `self` on the end of the accumulator after trying to reserve space for it. - #[inline] - fn reserve_concat<A>(&self, accumulator: &mut A) - where - A: Accumulator<u8>, - { - if let Some(capacity) = self.size_hint() { - accumulator.reserve(capacity); - } - self.concat(accumulator) - } - - /// Constructs a default accumulator and accumulates over `self`, reserving the appropriate - /// capacity. - #[inline] - fn as_bytes<A>(&self) -> A - where - A: Default + Accumulator<u8>, - { - let mut accumulator = A::default(); - self.reserve_concat(&mut accumulator); - accumulator.finish() - } + /// Concatenates `self` on the end of the accumulator. + /// + /// # Note + /// + /// Implementations should not ask to reserve additional space for elements in this method. + /// Instead, reimplement [`reserve_concat`](Self::reserve_concat) if the default implementation + /// is not efficient. + fn concat<A>(&self, accumulator: &mut A) + where + A: Accumulator<u8>; + + /// Returns a hint to the possible number of bytes that will be accumulated when concatenating + /// `self`. + #[inline] + fn size_hint(&self) -> Option<usize> { + None + } + + /// Concatenates `self` on the end of the accumulator after trying to reserve space for it. + #[inline] + fn reserve_concat<A>(&self, accumulator: &mut A) + where + A: Accumulator<u8>, + { + if let Some(capacity) = self.size_hint() { + accumulator.reserve(capacity); + } + self.concat(accumulator) + } + + /// Constructs a default accumulator and accumulates over `self`, reserving the appropriate + /// capacity. + #[inline] + fn as_bytes<A>(&self) -> A + where + A: Default + Accumulator<u8>, + { + let mut accumulator = A::default(); + self.reserve_concat(&mut accumulator); + accumulator.finish() + } } impl<T> ConcatBytes for T where - T: Borrow<[u8]>, + T: Borrow<[u8]>, { - #[inline] - fn concat<A>(&self, accumulator: &mut A) - where - A: Accumulator<u8>, - { - accumulator.extend(self.borrow()) - } - - #[inline] - fn size_hint(&self) -> Option<usize> { - Some(self.borrow().len()) - } + #[inline] + fn concat<A>(&self, accumulator: &mut A) + where + A: Accumulator<u8>, + { + accumulator.extend(self.borrow()) + } + + #[inline] + fn size_hint(&self) -> Option<usize> { + Some(self.borrow().len()) + } } /// Concatenates `$item`s together by building a [`Accumulator`] and running @@ -162,7 +163,7 @@ where macro_rules! concatenate { ($($item:expr),*) => { { - let mut accumulator = ::ark_std::vec::Vec::new(); + let mut accumulator = ::alloc::vec::Vec::new(); $($crate::ConcatBytes::reserve_concat($item, &mut accumulator);)* $crate::Accumulator::finish(accumulator) } diff --git a/manta-crypto/src/constraints.rs b/manta-crypto/src/constraints.rs index d7eb73b15..65f675852 100644 --- a/manta-crypto/src/constraints.rs +++ b/manta-crypto/src/constraints.rs @@ -15,3 +15,158 @@ // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. //! Constraint Systems and Zero Knowledge Proofs + +use alloc::vec::Vec; + +/* TODO: +use alloc::borrow::Cow; + +/// Variable Trait +pub trait Variable<T> +where + T: ?Sized + ToOwned, +{ + /// Builds a new variable from a borrowed value. + fn from_borrowed(t: &T) -> Self; + + /// Builds a new variable from an owned value. + fn from_owned(t: T::Owned) -> Self; + + /// Builds a new variable from no value. + fn variable() -> Self; +} + +/// Variable CoW +pub enum VariableCow<'t, T, V> +where + T: 't + ?Sized + ToOwned, + V: Variable<T>, +{ + /// Borrowed data + Borrowed(&'t T), + + /// Owned data + Owned(T::Owned), + + /// Variable + Variable(V), +} + +impl<T, V> VariableCow<'_, T, V> +where + T: ?Sized + ToOwned, + V: Variable<T>, +{ + /// Extracts the owned data. + /// + /// Clones the data if it is not already owned. + #[inline] + pub fn try_into_owned(self) -> Option<T::Owned> { + match self { + Self::Borrowed(borrowed) => Some(borrowed.to_owned()), + Self::Owned(owned) => Some(owned), + _ => None, + } + } + + /// Converts into a variable. + #[inline] + pub fn into_variable(self) -> V { + match self { + Self::Borrowed(borrowed) => V::from_borrowed(borrowed), + Self::Owned(owned) => V::from_owned(owned), + Self::Variable(variable) => variable, + } + } +} + +impl<'t, T, V> From<Cow<'t, T>> for VariableCow<'t, T, V> +where + T: ?Sized + ToOwned, + V: Variable<T>, +{ + #[inline] + fn from(cow: Cow<'t, T>) -> Self { + match cow { + Cow::Borrowed(borrowed) => Self::Borrowed(borrowed), + Cow::Owned(owned) => Self::Owned(owned), + } + } +} +*/ + +/// +pub enum Variable<T = usize> { + /// + Input(T), + + /// + Witness(T), +} + +/// +pub trait Constraint<T> { + /// + fn is_satisfied<C>(&self, cs: &C) -> Option<bool> + where + C: ConstraintSystem<T>; +} + +/// +pub trait ConstraintSystem<T> { + /// + type Constraint: Constraint<T>; + + /// + fn new(shape: bool) -> Self; + + /// + fn new_input<F>(&mut self, f: F) -> Variable + where + F: FnOnce() -> T; + + /// + fn new_witness<F>(&mut self, f: F) -> Variable + where + F: FnOnce() -> T; + + /// + fn new_variable<F>(&mut self, f: F) -> Variable + where + F: FnOnce() -> Variable<T>; + + /// + fn value(&self, variable: Variable) -> Option<T>; + + /// + fn add(&mut self, constraint: Self::Constraint); + + /// + fn input_count(&self) -> usize; + + /// + fn witness_count(&self) -> usize; + + /// + #[inline] + fn variable_count(&self) -> usize { + self.input_count() + self.witness_count() + } + + /// + fn constraint_count(&self) -> usize; + + /// + fn is_satisfied(&self) -> Option<bool>; +} + +/// +pub trait ConstraintSynthesizer<T> { + /// + fn synthesize<C>(&self, cs: &mut C) + where + C: ConstraintSystem<T>; + + /// + fn input(&self) -> Vec<&T>; +} diff --git a/manta-crypto/src/ies.rs b/manta-crypto/src/ies.rs index a818d7c90..951118b9c 100644 --- a/manta-crypto/src/ies.rs +++ b/manta-crypto/src/ies.rs @@ -16,163 +16,163 @@ //! Integrated Encryption Schemes and Encrypted Messages -use ark_std::rand::{CryptoRng, RngCore}; use core::{fmt::Debug, hash::Hash}; use manta_codec::{ScaleDecode, ScaleEncode}; +use rand::{CryptoRng, RngCore}; /// [`IntegratedEncryptionScheme`] Key Pair #[derive(derivative::Derivative, ScaleDecode, ScaleEncode)] #[derivative( - Clone(bound = "I::PublicKey: Clone, I::SecretKey: Clone"), - Copy(bound = "I::PublicKey: Copy, I::SecretKey: Copy"), - Debug(bound = "I::PublicKey: Debug, I::SecretKey: Debug"), - Default(bound = "I::PublicKey: Default, I::SecretKey: Default"), - Eq(bound = "I::PublicKey: Eq, I::SecretKey: Eq"), - Hash(bound = "I::PublicKey: Hash, I::SecretKey: Hash"), - PartialEq(bound = "I::PublicKey: PartialEq, I::SecretKey: PartialEq") + Clone(bound = "I::PublicKey: Clone, I::SecretKey: Clone"), + Copy(bound = "I::PublicKey: Copy, I::SecretKey: Copy"), + Debug(bound = "I::PublicKey: Debug, I::SecretKey: Debug"), + Default(bound = "I::PublicKey: Default, I::SecretKey: Default"), + Eq(bound = "I::PublicKey: Eq, I::SecretKey: Eq"), + Hash(bound = "I::PublicKey: Hash, I::SecretKey: Hash"), + PartialEq(bound = "I::PublicKey: PartialEq, I::SecretKey: PartialEq") )] pub struct KeyPair<I: ?Sized> where - I: IntegratedEncryptionScheme, + I: IntegratedEncryptionScheme, { - /// Public Key - pub public: I::PublicKey, + /// Public Key + pub public: I::PublicKey, - /// Secret Key - pub secret: I::SecretKey, + /// Secret Key + pub secret: I::SecretKey, } impl<I> KeyPair<I> where - I: IntegratedEncryptionScheme, + I: IntegratedEncryptionScheme, { - /// Builds a new [`KeyPair`] from a `public` key and a `secret` key. - #[inline] - pub fn new(public: I::PublicKey, secret: I::SecretKey) -> Self { - Self { public, secret } - } + /// Builds a new [`KeyPair`] from a `public` key and a `secret` key. + #[inline] + pub fn new(public: I::PublicKey, secret: I::SecretKey) -> Self { + Self { public, secret } + } } /// Integrated Encryption Scheme Trait pub trait IntegratedEncryptionScheme { - /// Public Key Type - type PublicKey; - - /// Secret Key Type - type SecretKey; - - /// Plaintext Type - type Plaintext; - - /// Ciphertext Type - type Ciphertext; - - /// Encryption/Decryption Error Type - type Error; - - /// Generates public/secret keypair. - fn keygen<R>(rng: &mut R) -> KeyPair<Self> - where - R: CryptoRng + RngCore; - - /// Generates a new keypair and encrypts the `message`, generating an [`EncryptedMessage`], - /// and returning the keypair. - #[inline] - fn keygen_encrypt<R>( - message: &Self::Plaintext, - rng: &mut R, - ) -> Result<(KeyPair<Self>, EncryptedMessage<Self>), Self::Error> - where - R: CryptoRng + RngCore, - { - let keypair = Self::keygen(rng); - let encrypted_message = Self::encrypt(message, &keypair.public, rng)?; - Ok((keypair, encrypted_message)) - } - - /// Encrypts the `message` with `pk`, generating an [`EncryptedMessage`]. - fn encrypt<R>( - message: &Self::Plaintext, - pk: &Self::PublicKey, - rng: &mut R, - ) -> Result<EncryptedMessage<Self>, Self::Error> - where - R: CryptoRng + RngCore; - - /// Decrypts the `message` with `sk`. - fn decrypt( - message: &EncryptedMessage<Self>, - sk: &Self::SecretKey, - ) -> Result<Self::Plaintext, Self::Error>; + /// Public Key Type + type PublicKey; + + /// Secret Key Type + type SecretKey; + + /// Plaintext Type + type Plaintext; + + /// Ciphertext Type + type Ciphertext; + + /// Encryption/Decryption Error Type + type Error; + + /// Generates public/secret keypair. + fn keygen<R>(rng: &mut R) -> KeyPair<Self> + where + R: CryptoRng + RngCore; + + /// Generates a new keypair and encrypts the `message`, generating an [`EncryptedMessage`], + /// and returning the keypair. + #[inline] + fn keygen_encrypt<R>( + message: &Self::Plaintext, + rng: &mut R, + ) -> Result<(KeyPair<Self>, EncryptedMessage<Self>), Self::Error> + where + R: CryptoRng + RngCore, + { + let keypair = Self::keygen(rng); + let encrypted_message = Self::encrypt(message, &keypair.public, rng)?; + Ok((keypair, encrypted_message)) + } + + /// Encrypts the `message` with `pk`, generating an [`EncryptedMessage`]. + fn encrypt<R>( + message: &Self::Plaintext, + pk: &Self::PublicKey, + rng: &mut R, + ) -> Result<EncryptedMessage<Self>, Self::Error> + where + R: CryptoRng + RngCore; + + /// Decrypts the `message` with `sk`. + fn decrypt( + message: &EncryptedMessage<Self>, + sk: &Self::SecretKey, + ) -> Result<Self::Plaintext, Self::Error>; } /// Encrypted Message #[derive(derivative::Derivative, ScaleDecode, ScaleEncode)] #[derivative( - Clone(bound = "I::Ciphertext: Clone, I::PublicKey: Clone"), - Copy(bound = "I::Ciphertext: Copy, I::PublicKey: Copy"), - Debug(bound = "I::Ciphertext: Debug, I::PublicKey: Debug"), - Default(bound = "I::Ciphertext: Default, I::PublicKey: Default"), - Eq(bound = "I::Ciphertext: Eq, I::PublicKey: Eq"), - Hash(bound = "I::Ciphertext: Hash, I::PublicKey: Hash"), - PartialEq(bound = "I::Ciphertext: PartialEq, I::PublicKey: PartialEq") + Clone(bound = "I::Ciphertext: Clone, I::PublicKey: Clone"), + Copy(bound = "I::Ciphertext: Copy, I::PublicKey: Copy"), + Debug(bound = "I::Ciphertext: Debug, I::PublicKey: Debug"), + Default(bound = "I::Ciphertext: Default, I::PublicKey: Default"), + Eq(bound = "I::Ciphertext: Eq, I::PublicKey: Eq"), + Hash(bound = "I::Ciphertext: Hash, I::PublicKey: Hash"), + PartialEq(bound = "I::Ciphertext: PartialEq, I::PublicKey: PartialEq") )] pub struct EncryptedMessage<I: ?Sized> where - I: IntegratedEncryptionScheme, + I: IntegratedEncryptionScheme, { - /// Ciphertext of the Message - pub ciphertext: I::Ciphertext, + /// Ciphertext of the Message + pub ciphertext: I::Ciphertext, - /// Ephemeral Public Key - pub ephemeral_public_key: I::PublicKey, + /// Ephemeral Public Key + pub ephemeral_public_key: I::PublicKey, } impl<I> EncryptedMessage<I> where - I: IntegratedEncryptionScheme, + I: IntegratedEncryptionScheme, { - /// Builds a new [`EncryptedMessage`] from [`I::Ciphertext`] and an ephemeral [`I::PublicKey`]. - /// - /// [`I::Ciphertext`]: IntegratedEncryptionScheme::Ciphertext - /// [`I::PublicKey`]: IntegratedEncryptionScheme::PublicKey - #[inline] - pub fn new(ciphertext: I::Ciphertext, ephemeral_public_key: I::PublicKey) -> Self { - Self { - ciphertext, - ephemeral_public_key, - } - } - - /// Generates a new keypair and encrypts the `message`, generating an [`EncryptedMessage`], - /// and returning the keypair. - #[inline] - pub fn keygen_encrypt<R>( - message: &I::Plaintext, - rng: &mut R, - ) -> Result<(KeyPair<I>, Self), I::Error> - where - R: CryptoRng + RngCore, - { - I::keygen_encrypt(message, rng) - } - - /// Encrypts the `message` with `pk`, generating an [`EncryptedMessage`]. - #[inline] - pub fn encrypt<R>( - message: &I::Plaintext, - pk: &I::PublicKey, - rng: &mut R, - ) -> Result<Self, I::Error> - where - R: CryptoRng + RngCore, - { - I::encrypt(message, pk, rng) - } - - /// Decrypts the `message` with `sk`. - #[inline] - pub fn decrypt(&self, sk: &I::SecretKey) -> Result<I::Plaintext, I::Error> { - I::decrypt(self, sk) - } + /// Builds a new [`EncryptedMessage`] from [`I::Ciphertext`] and an ephemeral [`I::PublicKey`]. + /// + /// [`I::Ciphertext`]: IntegratedEncryptionScheme::Ciphertext + /// [`I::PublicKey`]: IntegratedEncryptionScheme::PublicKey + #[inline] + pub fn new(ciphertext: I::Ciphertext, ephemeral_public_key: I::PublicKey) -> Self { + Self { + ciphertext, + ephemeral_public_key, + } + } + + /// Generates a new keypair and encrypts the `message`, generating an [`EncryptedMessage`], + /// and returning the keypair. + #[inline] + pub fn keygen_encrypt<R>( + message: &I::Plaintext, + rng: &mut R, + ) -> Result<(KeyPair<I>, Self), I::Error> + where + R: CryptoRng + RngCore, + { + I::keygen_encrypt(message, rng) + } + + /// Encrypts the `message` with `pk`, generating an [`EncryptedMessage`]. + #[inline] + pub fn encrypt<R>( + message: &I::Plaintext, + pk: &I::PublicKey, + rng: &mut R, + ) -> Result<Self, I::Error> + where + R: CryptoRng + RngCore, + { + I::encrypt(message, pk, rng) + } + + /// Decrypts the `message` with `sk`. + #[inline] + pub fn decrypt(&self, sk: &I::SecretKey) -> Result<I::Plaintext, I::Error> { + I::decrypt(self, sk) + } } diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index 7f59e7e27..b0e3b6681 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -16,9 +16,11 @@ //! Cryptographic Primitives Library -#![cfg_attr(not(feature = "std"), no_std)] +#![no_std] #![cfg_attr(docsrs, feature(doc_cfg), forbid(broken_intra_doc_links))] +extern crate alloc; + mod commitment; mod concat; mod ies; diff --git a/manta-crypto/src/prf.rs b/manta-crypto/src/prf.rs index d70f00855..18654bce9 100644 --- a/manta-crypto/src/prf.rs +++ b/manta-crypto/src/prf.rs @@ -20,27 +20,27 @@ use core::borrow::Borrow; /// Pseudorandom Function Families (PRF) Trait pub trait PseudorandomFunctionFamily { - /// PRF Seed Type - type Seed: ?Sized; + /// PRF Seed Type + type Seed: ?Sized; - /// PRF Input Type - type Input: Default; + /// PRF Input Type + type Input: Default; - /// PRF Output Type - type Output; + /// PRF Output Type + type Output; - /// Evaluates the PRF at the `seed` and `input`. - fn evaluate<S, I>(seed: S, input: I) -> Self::Output - where - S: Borrow<Self::Seed>, - I: Borrow<Self::Input>; + /// Evaluates the PRF at the `seed` and `input`. + fn evaluate<S, I>(seed: S, input: I) -> Self::Output + where + S: Borrow<Self::Seed>, + I: Borrow<Self::Input>; - /// Evaluates the PRF at the `seed` with the default input. - #[inline] - fn evaluate_zero<S>(seed: S) -> Self::Output - where - S: Borrow<Self::Seed>, - { - Self::evaluate(seed, Self::Input::default()) - } + /// Evaluates the PRF at the `seed` with the default input. + #[inline] + fn evaluate_zero<S>(seed: S) -> Self::Output + where + S: Borrow<Self::Seed>, + { + Self::evaluate(seed, Self::Input::default()) + } } diff --git a/manta-crypto/src/set.rs b/manta-crypto/src/set.rs index b5f8f915b..bd00493ae 100644 --- a/manta-crypto/src/set.rs +++ b/manta-crypto/src/set.rs @@ -21,99 +21,127 @@ use manta_codec::{ScaleDecode, ScaleEncode}; /// Set Trait pub trait Set { - /// Item Stored in the [`Set`] - type Item; - - /// Returns `true` if `item` is stored in `self`. - fn contains(&self, item: &Self::Item) -> bool; - - /// Tries to insert the `item` into `self`, returning the item back if it was already - /// contained in `self`. - fn try_insert(&mut self, item: Self::Item) -> Result<(), Self::Item>; - - /// Inserts the `item` into `self`, returning `true` if the item was already contained in - /// `self`. - #[inline] - fn insert(&mut self, item: Self::Item) -> bool { - self.try_insert(item).is_err() - } + /// Item Stored in the [`Set`] + type Item; + + /// Returns `true` if `item` is stored in `self`. + fn contains(&self, item: &Self::Item) -> bool; + + /// Tries to insert the `item` into `self`, returning the item back if it was already + /// contained in `self`. + fn try_insert(&mut self, item: Self::Item) -> Result<(), Self::Item>; + + /// Inserts the `item` into `self`, returning `true` if the item was already contained in + /// `self`. + #[inline] + fn insert(&mut self, item: Self::Item) -> bool { + self.try_insert(item).is_err() + } } /// Verified Containment Trait pub trait VerifyContainment<Public, Item> { - /// Verifies that `self` is a proof that `item` is contained in some [`VerifiedSet`]. - fn verify(&self, public: &Public, item: &Item) -> bool; + /// Verifies that `self` is a proof that `item` is contained in some [`VerifiedSet`]. + fn verify(&self, public: &Public, item: &Item) -> bool; } /// Containment Proof for a [`VerifiedSet`] #[derive(derivative::Derivative, ScaleDecode, ScaleEncode)] #[derivative( - Clone(bound = "S::Public: Clone, S::Secret: Clone"), - Debug(bound = "S::Public: Debug, S::Secret: Debug"), - Default(bound = "S::Public: Default, S::Secret: Default"), - Eq(bound = "S::Public: Eq, S::Secret: Eq"), - Hash(bound = "S::Public: Hash, S::Secret: Hash"), - PartialEq(bound = "S::Public: PartialEq, S::Secret: PartialEq") + Clone(bound = "S::Public: Clone, S::Secret: Clone"), + Debug(bound = "S::Public: Debug, S::Secret: Debug"), + Default(bound = "S::Public: Default, S::Secret: Default"), + Eq(bound = "S::Public: Eq, S::Secret: Eq"), + Hash(bound = "S::Public: Hash, S::Secret: Hash"), + PartialEq(bound = "S::Public: PartialEq, S::Secret: PartialEq") )] pub struct ContainmentProof<S: ?Sized> where - S: VerifiedSet, + S: VerifiedSet, { - /// Public Input - pub input: S::Public, + /// Public Input + pub input: S::Public, - /// Secret Witness - pub witness: S::Secret, + /// Secret Witness + pub witness: S::Secret, } impl<S> ContainmentProof<S> where - S: VerifiedSet, + S: VerifiedSet, { - /// Builds a new [`ContainmentProof`] from public `input` and secret `witness`. - #[inline] - pub fn new(input: S::Public, witness: S::Secret) -> Self { - Self { input, witness } - } - - /// Verifies that the `item` is contained in some [`VerifiedSet`]. - #[inline] - pub fn verify(&self, item: &S::Item) -> bool { - self.witness.verify(&self.input, item) - } + /// Builds a new [`ContainmentProof`] from public `input` and secret `witness`. + #[inline] + pub fn new(input: S::Public, witness: S::Secret) -> Self { + Self { input, witness } + } + + /// Verifies that the `item` is contained in some [`VerifiedSet`]. + #[inline] + pub fn verify(&self, item: &S::Item) -> bool { + self.witness.verify(&self.input, item) + } } /// Verified Set Trait pub trait VerifiedSet { - /// Item Stored in the [`VerifiedSet`] - type Item; - - /// Public Input for [`Item`](Self::Item) Containment - type Public; - - /// Secret Witness for [`Item`](Self::Item) Containment - type Secret: VerifyContainment<Self::Public, Self::Item>; - - /// Returns `true` if `public` is a valid input for the current state of `self`. - fn check_public_input(&self, public: &Self::Public) -> bool; - - /// Generates a proof that the given `item` is stored in `self`. - fn get_containment_proof(&self, item: &Self::Item) -> Option<ContainmentProof<Self>>; - - /// Returns `true` if there exists a proof that `item` is stored in `self`. - #[inline] - fn contains(&self, item: &Self::Item) -> bool { - self.get_containment_proof(item).is_some() - } - - /// Tries to insert the `item` into `self`, returning the item back if it was already - /// contained in `self`. - fn try_insert(&mut self, item: Self::Item) -> Result<(), Self::Item>; + /// Item Stored in the [`VerifiedSet`] + type Item; + + /// Public Input for [`Item`](Self::Item) Containment + type Public; + + /// Secret Witness for [`Item`](Self::Item) Containment + type Secret: VerifyContainment<Self::Public, Self::Item>; + + /// Error Generating a [`ContainmentProof`] + type ContainmentError; + + /// Returns `true` if `public` is a valid input for the current state of `self`. + fn check_public_input(&self, public: &Self::Public) -> bool; + + /// Generates a proof that the given `item` is stored in `self`. + fn get_containment_proof( + &self, + item: &Self::Item, + ) -> Result<ContainmentProof<Self>, Self::ContainmentError>; + + /// Returns `true` if there exists a proof that `item` is stored in `self`. + #[inline] + fn contains(&self, item: &Self::Item) -> bool { + self.get_containment_proof(item).is_ok() + } + + /// Tries to insert the `item` into `self`, returning the item back if it was already + /// contained in `self`. + fn try_insert(&mut self, item: Self::Item) -> Result<(), Self::Item>; + + /// Inserts the `item` into `self`, returning `true` if the item was already contained in + /// `self`. + #[inline] + fn insert(&mut self, item: Self::Item) -> bool { + self.try_insert(item).is_err() + } +} - /// Inserts the `item` into `self`, returning `true` if the item was already contained in - /// `self`. - #[inline] - fn insert(&mut self, item: Self::Item) -> bool { - self.try_insert(item).is_err() - } +impl<S> Set for S +where + S: VerifiedSet, +{ + type Item = S::Item; + + #[inline] + fn contains(&self, item: &Self::Item) -> bool { + self.contains(item) + } + + #[inline] + fn try_insert(&mut self, item: Self::Item) -> Result<(), Self::Item> { + self.try_insert(item) + } + + #[inline] + fn insert(&mut self, item: Self::Item) -> bool { + self.insert(item) + } } diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index 919bf379c..5259df74e 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -27,45 +27,45 @@ use core::convert::TryInto; /// Implements [`From`]`<$from>` for an enum `$to`, choosing the `$kind` variant. #[macro_export] macro_rules! from_variant_impl { - ($to:tt, $kind:ident, $from:tt) => { - impl From<$from> for $to { - #[inline] - fn from(t: $from) -> Self { - Self::$kind(t) - } - } - }; + ($to:tt, $kind:ident, $from:tt) => { + impl From<$from> for $to { + #[inline] + fn from(t: $from) -> Self { + Self::$kind(t) + } + } + }; } /// Performs the [`TryInto`] conversion into an array without checking if the conversion succeeded. #[inline] pub fn try_into_array_unchecked<T, V, const N: usize>(v: V) -> [T; N] where - V: TryInto<[T; N]>, + V: TryInto<[T; N]>, { - match v.try_into() { - Ok(array) => array, - _ => unreachable!(), - } + match v.try_into() { + Ok(array) => array, + _ => unreachable!(), + } } /// Maps `f` over the `array`. #[inline] pub fn array_map<T, U, F, const N: usize>(array: [T; N], f: F) -> [U; N] where - F: FnMut(T) -> U, + F: FnMut(T) -> U, { - // TODO: get rid of this function when `array::map` is stabilized - try_into_array_unchecked(IntoIterator::into_iter(array).map(f).collect::<Vec<_>>()) + // TODO: get rid of this function when `array::map` is stabilized + try_into_array_unchecked(IntoIterator::into_iter(array).map(f).collect::<Vec<_>>()) } /// Maps `f` over the `array` by reference. #[inline] pub fn array_map_ref<T, U, F, const N: usize>(array: &[T; N], f: F) -> [U; N] where - F: FnMut(&T) -> U, + F: FnMut(&T) -> U, { - try_into_array_unchecked(array.iter().map(f).collect::<Vec<_>>()) + try_into_array_unchecked(array.iter().map(f).collect::<Vec<_>>()) } /// Maps `f` over the `array` returning the target array if all of the mappings succeeded, or @@ -73,11 +73,11 @@ where #[inline] pub fn fallible_array_map<T, U, E, F, const N: usize>(array: [T; N], f: F) -> Result<[U; N], E> where - F: FnMut(T) -> Result<U, E>, + F: FnMut(T) -> Result<U, E>, { - Ok(try_into_array_unchecked( - IntoIterator::into_iter(array) - .map(f) - .collect::<Result<Vec<_>, _>>()?, - )) + Ok(try_into_array_unchecked( + IntoIterator::into_iter(array) + .map(f) + .collect::<Result<Vec<_>, _>>()?, + )) } From 0628a77aa5b22e6614ab8b58ad1f24078b64c879 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 3 Sep 2021 03:52:59 -0400 Subject: [PATCH 005/275] improve secret key APIs and finish spend spec --- Cargo.toml | 9 ++ manta-accounting/Cargo.toml | 2 +- manta-accounting/src/account.rs | 236 +++++++++++++---------------- manta-accounting/src/asset.rs | 19 ++- manta-accounting/src/transfer.rs | 29 +++- manta-codec/Cargo.toml | 2 +- manta-crypto/Cargo.toml | 2 +- manta-crypto/src/commitment.rs | 16 +- manta-crypto/src/concat.rs | 11 +- manta-crypto/src/constraints.rs | 77 ---------- manta-crypto/src/ies.rs | 250 +++++++++++++++++++++---------- manta-crypto/src/lib.rs | 8 +- manta-crypto/src/prf.rs | 14 +- manta-crypto/src/set.rs | 31 ++-- manta-pay/Cargo.toml | 39 +++++ manta-pay/LICENSE | 1 + manta-pay/README.md | 1 + manta-pay/build.rs | 19 +++ manta-pay/src/ies.rs | 159 ++++++++++++++++++++ manta-pay/src/lib.rs | 22 +++ manta-util/Cargo.toml | 2 +- manta/Cargo.toml | 34 +++++ manta/LICENSE | 1 + manta/README.md | 1 + manta/src/lib.rs | 32 ++++ 25 files changed, 671 insertions(+), 346 deletions(-) create mode 100644 Cargo.toml create mode 100644 manta-pay/Cargo.toml create mode 120000 manta-pay/LICENSE create mode 100644 manta-pay/README.md create mode 100644 manta-pay/build.rs create mode 100644 manta-pay/src/ies.rs create mode 100644 manta-pay/src/lib.rs create mode 100644 manta/Cargo.toml create mode 120000 manta/LICENSE create mode 100644 manta/README.md create mode 100644 manta/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..fdb51002f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] +members = [ + "manta", + "manta-accounting", + "manta-codec", + "manta-crypto", + "manta-pay", + "manta-util", +] diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 08b6b058c..5abb900ac 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -10,7 +10,7 @@ homepage = "https://github.com/Manta-Network" documentation = "https://github.com/Manta-Network/manta-rs" categories = [""] keywords = [""] -description = "Accounting Primitives for manta-rs." +description = "Accounting Primitives for Manta." publish = false [package.metadata.docs.rs] diff --git a/manta-accounting/src/account.rs b/manta-accounting/src/account.rs index b54b09848..25b4870da 100644 --- a/manta-accounting/src/account.rs +++ b/manta-accounting/src/account.rs @@ -16,27 +16,43 @@ //! Identities, Senders, and Receivers -// FIXME: tweak the secret key api so that it matches with a wallet implementation (i.e. it should -// not necessarily be generated by an `Rng`, but something like a `SecreKeySource` // FIXME: ensure secret keys cannot be made public by some API call use crate::asset::Asset; use core::{convert::Infallible, fmt::Debug, hash::Hash, marker::PhantomData}; use manta_codec::{ScaleDecode, ScaleEncode}; use manta_crypto::{ - concatenate, CommitmentScheme, ConcatBytes, ContainmentProof, EncryptedMessage, - IntegratedEncryptionScheme, KeyPair, PseudorandomFunctionFamily, VerifiedSet, + concatenate, + ies::{self, EncryptedMessage}, + set::ContainmentProof, + CommitmentScheme, ConcatBytes, IntegratedEncryptionScheme, PseudorandomFunctionFamily, + VerifiedSet, }; use rand::{ distributions::{Distribution, Standard}, CryptoRng, Rng, RngCore, SeedableRng, }; +/// Secret Key Generator Trait +pub trait SecretKeyGenerator<SecretKey> { + /// Index Type + type Index; + + /// Key Generation Error + type Error; + + /// Generates a secret key for a given index starting from some root key. + fn generate_key(&self, index: &Self::Index) -> Result<SecretKey, Self::Error>; +} + /// [`Identity`] Configuration Trait pub trait IdentityConfiguration { /// Secret Key Type type SecretKey: Clone; + /// Secret Key Generator Type + type SecretKeyGenerator: SecretKeyGenerator<Self::SecretKey>; + /// Pseudorandom Function Family Type type PseudorandomFunctionFamily: PseudorandomFunctionFamily<Seed = Self::SecretKey>; @@ -47,6 +63,18 @@ pub trait IdentityConfiguration { type Rng: CryptoRng + RngCore + SeedableRng<Seed = Self::SecretKey>; } +/// [`SecretKeyGenerator::Index`] Type Alias +pub type SecretKeyGeneratorIndex<C> = + <<C as IdentityConfiguration>::SecretKeyGenerator as SecretKeyGenerator< + <C as IdentityConfiguration>::SecretKey, + >>::Index; + +/// [`SecretKeyGenerator::Error`] Type Alias +pub type SecretKeyGeneratorError<C> = + <<C as IdentityConfiguration>::SecretKeyGenerator as SecretKeyGenerator< + <C as IdentityConfiguration>::SecretKey, + >>::Error; + /// [`PseudorandomFunctionFamily::Input`] Type Alias pub type PseudorandomFunctionFamilyInput<C> = <<C as IdentityConfiguration>::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Input; @@ -63,6 +91,9 @@ pub type CommitmentSchemeRandomness<C> = pub type CommitmentSchemeOutput<C> = <<C as IdentityConfiguration>::CommitmentScheme as CommitmentScheme>::Output; +/// Secret Key Type Alias +pub type SecretKey<C> = <C as IdentityConfiguration>::SecretKey; + /// Public Key Type Alias pub type PublicKey<C> = PseudorandomFunctionFamilyOutput<C>; @@ -156,16 +187,6 @@ where } /// Account Identity -#[derive(derivative::Derivative, ScaleDecode, ScaleEncode)] -#[derivative( - Clone(bound = "C::SecretKey: Clone"), - Copy(bound = "C::SecretKey: Copy"), - Debug(bound = "C::SecretKey: Debug"), - Default(bound = "C::SecretKey: Default"), - Eq(bound = "C::SecretKey: Eq"), - Hash(bound = "C::SecretKey: Hash"), - PartialEq(bound = "C::SecretKey: PartialEq") -)] pub struct Identity<C> where C: IdentityConfiguration, @@ -189,6 +210,15 @@ where Self { secret_key } } + /// Generates a new [`Identity`] from a secret key generation source. + #[inline] + pub fn generate( + source: &C::SecretKeyGenerator, + index: &SecretKeyGeneratorIndex<C>, + ) -> Result<Self, SecretKeyGeneratorError<C>> { + source.generate_key(index).map(Self::new) + } + /// Returns the public key associated with this identity. #[inline] pub fn public_key(&self) -> PublicKey<C> { @@ -237,7 +267,7 @@ where parameters } - /// Generates the associated `C::Rng`, `AssetParameters<C>`, and `KeyPair<I>` for + /// Generates the associated `C::Rng`, `AssetParameters<C>`, and `ies::KeyPair<I>` for /// this identity. /// /// # API Note @@ -250,7 +280,9 @@ where /// /// See [`Self::rng_and_parameters`]. #[inline] - fn rng_and_parameters_and_asset_keypair<I>(&self) -> (C::Rng, AssetParameters<C>, KeyPair<I>) + fn rng_and_parameters_and_asset_keypair<I>( + &self, + ) -> (C::Rng, AssetParameters<C>, ies::KeyPair<I>) where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, @@ -260,9 +292,10 @@ where (rng, parameters, asset_keypair) } - /// Generates [`AssetParameters`] and a [`KeyPair`] for assets that are used by this identity. + /// Generates [`AssetParameters`] and a [`KeyPair`](ies::KeyPair) for assets that are used by + /// this identity. #[inline] - pub fn parameters_and_asset_keypair<I>(&self) -> (AssetParameters<C>, KeyPair<I>) + pub fn parameters_and_asset_keypair<I>(&self) -> (AssetParameters<C>, ies::KeyPair<I>) where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, @@ -271,9 +304,9 @@ where (parameters, asset_keypair) } - /// Generates a [`KeyPair`] for assets that are used by this identity. + /// Generates a [`KeyPair`](ies::KeyPair) for assets that are used by this identity. #[inline] - pub fn asset_keypair<I>(&self) -> KeyPair<I> + pub fn asset_keypair<I>(&self) -> ies::KeyPair<I> where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, @@ -309,6 +342,24 @@ where ) } + /// Generates a new void number commitment using `parameters`. + #[inline] + pub fn void_number_commitment_from_parameters( + &self, + commitment_scheme: &C::CommitmentScheme, + parameters: &AssetParameters<C>, + ) -> VoidNumberCommitment<C> + where + PublicKey<C>: ConcatBytes, + VoidNumberGenerator<C>: ConcatBytes, + { + self.void_number_commitment( + commitment_scheme, + &parameters.void_number_generator, + &parameters.void_number_commitment_randomness, + ) + } + /// Generates a [`Utxo`] for an `asset` using the `parameters`. #[inline] pub fn utxo( @@ -325,16 +376,13 @@ where generate_utxo::<C>( commitment_scheme, asset, - &self.void_number_commitment( - commitment_scheme, - &parameters.void_number_generator, - &parameters.void_number_commitment_randomness, - ), + &self.void_number_commitment_from_parameters(commitment_scheme, parameters), &parameters.utxo_randomness, ) } /// Builds a new [`Sender`] for the given `asset`. + #[inline] pub fn into_sender<S>( self, commitment_scheme: &C::CommitmentScheme, @@ -362,6 +410,7 @@ where } /// Builds a new [`ShieldedIdentity`]-[`Spend`] pair for the given `asset`. + #[inline] pub fn into_receiver<I>( self, commitment_scheme: &C::CommitmentScheme, @@ -373,20 +422,17 @@ where VoidNumberGenerator<C>: ConcatBytes, { let (parameters, asset_keypair) = self.parameters_and_asset_keypair::<I>(); - let void_number_commitment = self.void_number_commitment( - commitment_scheme, - &parameters.void_number_generator, - &parameters.void_number_commitment_randomness, - ); + let (asset_public_key, asset_secret_key) = asset_keypair.split(); ( ShieldedIdentity { + void_number_commitment: self + .void_number_commitment_from_parameters(commitment_scheme, &parameters), utxo_randomness: parameters.utxo_randomness, - void_number_commitment, - asset_public_key: asset_keypair.public, + asset_public_key, }, Spend { identity: self, - asset_secret_key: asset_keypair.secret, + asset_secret_key, }, ) } @@ -416,7 +462,7 @@ where pub void_number_commitment: VoidNumberCommitment<C>, /// Encrypted [`Asset`] Public Key - pub asset_public_key: I::PublicKey, + pub asset_public_key: ies::PublicKey<I>, } impl<C, I> ShieldedIdentity<C, I> @@ -424,22 +470,6 @@ where C: IdentityConfiguration, I: IntegratedEncryptionScheme<Plaintext = Asset>, { - /// Generates a [`Utxo`] for an `asset` using the pre-computed - /// [`void_number_commitment`](Self::void_number_commitment) and - /// [`utxo_randomness`](Self::utxo_randomness). - #[inline] - pub fn utxo(&self, commitment_scheme: &C::CommitmentScheme, asset: &Asset) -> Utxo<C> - where - VoidNumberCommitment<C>: ConcatBytes, - { - generate_utxo::<C>( - commitment_scheme, - asset, - &self.void_number_commitment, - &self.utxo_randomness, - ) - } - /// Generates a [`Receiver`] from a [`ShieldedIdentity`]. #[inline] pub fn into_receiver<R>( @@ -449,15 +479,25 @@ where rng: &mut R, ) -> Result<Receiver<C, I>, I::Error> where - R: CryptoRng + RngCore, + R: CryptoRng + RngCore + ?Sized, VoidNumberCommitment<C>: ConcatBytes, { + let Self { + utxo_randomness, + void_number_commitment, + asset_public_key, + } = self; Ok(Receiver { - encrypted_asset: I::encrypt(&asset, &self.asset_public_key, rng)?, - utxo: self.utxo(commitment_scheme, &asset), + encrypted_asset: asset_public_key.encrypt(asset, rng)?, + utxo: generate_utxo::<C>( + commitment_scheme, + &asset, + &void_number_commitment, + &utxo_randomness, + ), asset, - utxo_randomness: self.utxo_randomness, - void_number_commitment: self.void_number_commitment, + utxo_randomness, + void_number_commitment, }) } } @@ -499,7 +539,7 @@ where identity: Identity<C>, /// Encrypted [`Asset`] Secret Key - asset_secret_key: I::SecretKey, + asset_secret_key: ies::SecretKey<I>, } impl<C, I> Spend<C, I> @@ -531,8 +571,8 @@ where self.identity .into_sender( commitment_scheme, - encrypted_asset - .decrypt(&self.asset_secret_key) + self.asset_secret_key + .decrypt(encrypted_asset) .map_err(SpendError::EncryptionError)?, utxo_set, ) @@ -540,46 +580,6 @@ where } } -/* TODO: -/// Sender Public Data -pub struct SenderPublicData<C, S> -where - C: IdentityConfiguration, - S: VerifiedSet<Item = Utxo<C>>, -{ - /// Void Number - pub void_number: VoidNumber<C>, - - /// Void Number Commitment - pub void_number_commitment: VoidNumberCommitment<C>, - - /// UTXO Containment Public Input - pub utxo_containment_public_input: S::Public, -} - -/// Sender Circuit Data -pub struct SenderCircuitData<C, S> -where - C: IdentityConfiguration, - S: VerifiedSet<Item = Utxo<C>>, -{ - /// Sender Identity - pub identity: Identity<C>, - - /// Asset - pub asset: Asset, - - /// Asset Generation Parameters - pub parameters: AssetParameters<C>, - - /// Unspent Transaction Output - pub utxo: Utxo<C>, - - /// UTXO Containment Proof - pub utxo_containment_proof: ContainmentProof<S>, -} -*/ - /// Sender pub struct Sender<C, S> where @@ -587,12 +587,12 @@ where S: VerifiedSet<Item = Utxo<C>>, { /// Sender Identity - pub identity: Identity<C>, + identity: Identity<C>, /// Asset pub asset: Asset, - /// Asset Generation Parameters + /// Asset Parameters pub parameters: AssetParameters<C>, /// Void Number @@ -605,42 +605,6 @@ where pub utxo_containment_proof: ContainmentProof<S>, } -/* TODO: -/// Receiver Public Data -pub struct ReceiverPublicData<C, I> -where - C: IdentityConfiguration, - I: IntegratedEncryptionScheme<Plaintext = Asset>, -{ - /// Void Number Commitment - pub void_number_commitment: VoidNumberCommitment<C>, - - /// Unspent Transaction Output - pub utxo: Utxo<C>, - - /// Encrypted [`Asset`] - pub encrypted_asset: EncryptedMessage<I>, -} - -/// Receiver Circuit Data -pub struct ReceiverCircuitData<C> -where - C: IdentityConfiguration, -{ - /// Asset - pub asset: Asset, - - /// UTXO Randomness - pub utxo_randomness: UtxoRandomness<C>, - - /// Void Number Commitment - pub void_number_commitment: VoidNumberCommitment<C>, - - /// Unspent Transaction Output - pub utxo: Utxo<C>, -} -*/ - /// Receiver pub struct Receiver<C, I> where diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index f74d5042e..a158a9848 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -276,7 +276,7 @@ impl ConcatBytes for Asset { #[inline] fn concat<A>(&self, accumulator: &mut A) where - A: Accumulator<u8>, + A: Accumulator<u8> + ?Sized, { accumulator.extend(&self.id.into_bytes()); accumulator.extend(&self.value.into_bytes()); @@ -315,3 +315,20 @@ impl Distribution<Asset> for Standard { Asset::new(rng.gen(), rng.gen()) } } + +/// Asset Collection +pub struct AssetCollection<const N: usize> { + /// Asset Id + pub id: AssetId, + + /// Asset Values + pub values: [AssetBalance; N], +} + +impl<const N: usize> AssetCollection<N> { + /// Generates a collection of assets with matching [`AssetId`]. + #[inline] + pub const fn new(id: AssetId, values: [AssetBalance; N]) -> Self { + Self { id, values } + } +} diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 8e0a5c01b..6cbf0d251 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -14,11 +14,11 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Transfer Protocol +//! Transfer Protocols use crate::{ account::{IdentityConfiguration, Receiver, Sender, Utxo}, - asset::Asset, + asset::{Asset, AssetCollection}, }; use manta_crypto::{IntegratedEncryptionScheme, VerifiedSet}; @@ -31,7 +31,30 @@ pub trait TransferConfiguration: IdentityConfiguration { type UtxoSet: VerifiedSet<Item = Utxo<Self>>; } -/// Private Transfer +/// Transfer Protocol +pub struct Transfer< + T, + const SOURCES: usize, + const SENDERS: usize, + const RECEIVERS: usize, + const SINKS: usize, +> where + T: TransferConfiguration, +{ + /// Public Asset Sources + pub sources: AssetCollection<SOURCES>, + + /// Secret Senders + pub senders: [Sender<T, T::UtxoSet>; SENDERS], + + /// Secret Receivers + pub receivers: [Receiver<T, T::IntegratedEncryptionScheme>; RECEIVERS], + + /// Public Asset Sinks + pub sinks: AssetCollection<SINKS>, +} + +/// Private Transfer Protocol pub struct PrivateTransfer<T, const SENDERS: usize, const RECEIVERS: usize> where T: TransferConfiguration, diff --git a/manta-codec/Cargo.toml b/manta-codec/Cargo.toml index 5bf1a790c..23a888b69 100644 --- a/manta-codec/Cargo.toml +++ b/manta-codec/Cargo.toml @@ -10,7 +10,7 @@ homepage = "https://github.com/Manta-Network" documentation = "https://github.com/Manta-Network/manta-rs" categories = [""] keywords = [""] -description = "Serialization and Deserialization tools for manta-rs." +description = "Serialization and Deserialization tools for Manta." publish = false [package.metadata.docs.rs] diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index a1272f0aa..73ca89912 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -10,7 +10,7 @@ homepage = "https://github.com/Manta-Network" documentation = "https://github.com/Manta-Network/manta-rs" categories = [""] keywords = [""] -description = "Cryptographic Primitives and Interfaces for manta-rs." +description = "Cryptographic Primitives and Interfaces for Manta." publish = false [package.metadata.docs.rs] diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index debdb2cd1..9188fe8ea 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -30,22 +30,24 @@ pub trait CommitmentScheme { /// Samples random commitment paramters. fn setup<R>(rng: &mut R) -> Self where - R: RngCore; + R: RngCore + ?Sized; /// Commits the `input` with the given `randomness` parameter. - fn commit<I, R>(&self, input: I, randomness: R) -> Self::Output + fn commit<I>(&self, input: I, randomness: &Self::Randomness) -> Self::Output where - I: Borrow<[u8]>, - R: Borrow<Self::Randomness>; + I: Borrow<[u8]>; /// Checks that the `output` matches the commitment of the `input` with the given `randomness` /// parameter. #[inline] - fn check_commitment<I, R, O>(&self, input: I, randomness: R, output: O) -> bool + fn check_commitment<I>( + &self, + input: I, + randomness: &Self::Randomness, + output: &Self::Output, + ) -> bool where I: Borrow<[u8]>, - R: Borrow<Self::Randomness>, - O: Borrow<Self::Output>, { &self.commit(input, randomness) == output.borrow() } diff --git a/manta-crypto/src/concat.rs b/manta-crypto/src/concat.rs index c6b2c0059..fe6179375 100644 --- a/manta-crypto/src/concat.rs +++ b/manta-crypto/src/concat.rs @@ -76,7 +76,7 @@ where impl<T> Accumulator<T> for Vec<T> where - T: Clone, + T: Clone + ?Sized, { #[inline] fn extend(&mut self, buffer: &[T]) { @@ -105,7 +105,7 @@ pub trait ConcatBytes { /// is not efficient. fn concat<A>(&self, accumulator: &mut A) where - A: Accumulator<u8>; + A: Accumulator<u8> + ?Sized; /// Returns a hint to the possible number of bytes that will be accumulated when concatenating /// `self`. @@ -118,7 +118,7 @@ pub trait ConcatBytes { #[inline] fn reserve_concat<A>(&self, accumulator: &mut A) where - A: Accumulator<u8>, + A: Accumulator<u8> + ?Sized, { if let Some(capacity) = self.size_hint() { accumulator.reserve(capacity); @@ -141,12 +141,12 @@ pub trait ConcatBytes { impl<T> ConcatBytes for T where - T: Borrow<[u8]>, + T: Borrow<[u8]> + ?Sized, { #[inline] fn concat<A>(&self, accumulator: &mut A) where - A: Accumulator<u8>, + A: Accumulator<u8> + ?Sized, { accumulator.extend(self.borrow()) } @@ -163,6 +163,7 @@ where macro_rules! concatenate { ($($item:expr),*) => { { + extern crate alloc; let mut accumulator = ::alloc::vec::Vec::new(); $($crate::ConcatBytes::reserve_concat($item, &mut accumulator);)* $crate::Accumulator::finish(accumulator) diff --git a/manta-crypto/src/constraints.rs b/manta-crypto/src/constraints.rs index 65f675852..cd59fca6c 100644 --- a/manta-crypto/src/constraints.rs +++ b/manta-crypto/src/constraints.rs @@ -18,83 +18,6 @@ use alloc::vec::Vec; -/* TODO: -use alloc::borrow::Cow; - -/// Variable Trait -pub trait Variable<T> -where - T: ?Sized + ToOwned, -{ - /// Builds a new variable from a borrowed value. - fn from_borrowed(t: &T) -> Self; - - /// Builds a new variable from an owned value. - fn from_owned(t: T::Owned) -> Self; - - /// Builds a new variable from no value. - fn variable() -> Self; -} - -/// Variable CoW -pub enum VariableCow<'t, T, V> -where - T: 't + ?Sized + ToOwned, - V: Variable<T>, -{ - /// Borrowed data - Borrowed(&'t T), - - /// Owned data - Owned(T::Owned), - - /// Variable - Variable(V), -} - -impl<T, V> VariableCow<'_, T, V> -where - T: ?Sized + ToOwned, - V: Variable<T>, -{ - /// Extracts the owned data. - /// - /// Clones the data if it is not already owned. - #[inline] - pub fn try_into_owned(self) -> Option<T::Owned> { - match self { - Self::Borrowed(borrowed) => Some(borrowed.to_owned()), - Self::Owned(owned) => Some(owned), - _ => None, - } - } - - /// Converts into a variable. - #[inline] - pub fn into_variable(self) -> V { - match self { - Self::Borrowed(borrowed) => V::from_borrowed(borrowed), - Self::Owned(owned) => V::from_owned(owned), - Self::Variable(variable) => variable, - } - } -} - -impl<'t, T, V> From<Cow<'t, T>> for VariableCow<'t, T, V> -where - T: ?Sized + ToOwned, - V: Variable<T>, -{ - #[inline] - fn from(cow: Cow<'t, T>) -> Self { - match cow { - Cow::Borrowed(borrowed) => Self::Borrowed(borrowed), - Cow::Owned(owned) => Self::Owned(owned), - } - } -} -*/ - /// pub enum Variable<T = usize> { /// diff --git a/manta-crypto/src/ies.rs b/manta-crypto/src/ies.rs index 951118b9c..f0a356623 100644 --- a/manta-crypto/src/ies.rs +++ b/manta-crypto/src/ies.rs @@ -16,40 +16,138 @@ //! Integrated Encryption Schemes and Encrypted Messages +// FIXME: add zeroize for secret keys + use core::{fmt::Debug, hash::Hash}; use manta_codec::{ScaleDecode, ScaleEncode}; use rand::{CryptoRng, RngCore}; +pub(crate) mod prelude { + #[doc(inline)] + pub use crate::ies::IntegratedEncryptionScheme; +} + +/// [`IntegratedEncryptionScheme`] Public Key +pub struct PublicKey<I> +where + I: IntegratedEncryptionScheme + ?Sized, +{ + /// Public Key + public_key: I::PublicKey, +} + +impl<I> PublicKey<I> +where + I: IntegratedEncryptionScheme + ?Sized, +{ + /// Generates a new [`PublicKey`] from a + /// [`I::PublicKey`](IntegratedEncryptionScheme::PublicKey). + #[inline] + pub fn new(public_key: I::PublicKey) -> Self { + Self { public_key } + } + + /// Encrypts the `plaintext` with `self`, generating an [`EncryptedMessage`]. + #[inline] + pub fn encrypt<R>( + self, + plaintext: I::Plaintext, + rng: &mut R, + ) -> Result<EncryptedMessage<I>, I::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + I::encrypt(plaintext, self.public_key, rng) + } +} + +/// [`IntegratedEncryptionScheme`] Secret Key +pub struct SecretKey<I> +where + I: IntegratedEncryptionScheme + ?Sized, +{ + /// Secret Key + secret_key: I::SecretKey, +} + +impl<I> SecretKey<I> +where + I: IntegratedEncryptionScheme + ?Sized, +{ + /// Generates a new `SecretKey` from an `I::SecretKey`. + /// + /// # API Note + /// + /// This function is intentionally private so that secret keys are not part of the + /// public interface. + #[inline] + fn new(secret_key: I::SecretKey) -> Self { + Self { secret_key } + } + + /// Decrypts the `message` with `self`. + #[inline] + pub fn decrypt(self, message: EncryptedMessage<I>) -> Result<I::Plaintext, I::Error> { + message.decrypt(self) + } +} + /// [`IntegratedEncryptionScheme`] Key Pair -#[derive(derivative::Derivative, ScaleDecode, ScaleEncode)] -#[derivative( - Clone(bound = "I::PublicKey: Clone, I::SecretKey: Clone"), - Copy(bound = "I::PublicKey: Copy, I::SecretKey: Copy"), - Debug(bound = "I::PublicKey: Debug, I::SecretKey: Debug"), - Default(bound = "I::PublicKey: Default, I::SecretKey: Default"), - Eq(bound = "I::PublicKey: Eq, I::SecretKey: Eq"), - Hash(bound = "I::PublicKey: Hash, I::SecretKey: Hash"), - PartialEq(bound = "I::PublicKey: PartialEq, I::SecretKey: PartialEq") -)] -pub struct KeyPair<I: ?Sized> +pub struct KeyPair<I> where - I: IntegratedEncryptionScheme, + I: IntegratedEncryptionScheme + ?Sized, { /// Public Key - pub public: I::PublicKey, + public_key: I::PublicKey, /// Secret Key - pub secret: I::SecretKey, + secret_key: I::SecretKey, } impl<I> KeyPair<I> where - I: IntegratedEncryptionScheme, + I: IntegratedEncryptionScheme + ?Sized, { - /// Builds a new [`KeyPair`] from a `public` key and a `secret` key. + /// Builds a new [`KeyPair`] from a `public_key` and a `secret_key`. + #[inline] + pub fn new(public_key: I::PublicKey, secret_key: I::SecretKey) -> Self { + Self { + public_key, + secret_key, + } + } + + /// Generates a public/secret keypair. #[inline] - pub fn new(public: I::PublicKey, secret: I::SecretKey) -> Self { - Self { public, secret } + pub fn generate<R>(rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + I::keygen(rng) + } + + /// Splits the key pair into an [`I::PublicKey`](IntegratedEncryptionScheme::PublicKey) + /// and a [`SecretKey`]. + #[inline] + pub fn split(self) -> (PublicKey<I>, SecretKey<I>) { + ( + PublicKey::new(self.public_key), + SecretKey::new(self.secret_key), + ) + } + + /// Encrypts the `plaintext` with `self, generating an [`EncryptedMessage`]. + #[inline] + pub fn encrypt<R>( + self, + plaintext: I::Plaintext, + rng: &mut R, + ) -> Result<(EncryptedMessage<I>, SecretKey<I>), I::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + let (public_key, secret_key) = self.split(); + Ok((public_key.encrypt(plaintext, rng)?, secret_key)) } } @@ -70,109 +168,99 @@ pub trait IntegratedEncryptionScheme { /// Encryption/Decryption Error Type type Error; - /// Generates public/secret keypair. + /// Generates a public/secret keypair. fn keygen<R>(rng: &mut R) -> KeyPair<Self> where - R: CryptoRng + RngCore; + R: CryptoRng + RngCore + ?Sized; + + /// Encrypts the `plaintext` with the `public_key`, generating an [`EncryptedMessage`]. + fn encrypt<R>( + plaintext: Self::Plaintext, + public_key: Self::PublicKey, + rng: &mut R, + ) -> Result<EncryptedMessage<Self>, Self::Error> + where + R: CryptoRng + RngCore + ?Sized; - /// Generates a new keypair and encrypts the `message`, generating an [`EncryptedMessage`], - /// and returning the keypair. + /// Generates a public/secret keypair and then encrypts the `plaintext` with the generated + /// public key, generating an [`EncryptedMessage`] and a [`SecretKey`]. #[inline] fn keygen_encrypt<R>( - message: &Self::Plaintext, + plaintext: Self::Plaintext, rng: &mut R, - ) -> Result<(KeyPair<Self>, EncryptedMessage<Self>), Self::Error> + ) -> Result<(EncryptedMessage<Self>, SecretKey<Self>), Self::Error> where - R: CryptoRng + RngCore, + R: CryptoRng + RngCore + ?Sized, { - let keypair = Self::keygen(rng); - let encrypted_message = Self::encrypt(message, &keypair.public, rng)?; - Ok((keypair, encrypted_message)) + Self::keygen(rng).encrypt(plaintext, rng) } - /// Encrypts the `message` with `pk`, generating an [`EncryptedMessage`]. - fn encrypt<R>( - message: &Self::Plaintext, - pk: &Self::PublicKey, - rng: &mut R, - ) -> Result<EncryptedMessage<Self>, Self::Error> - where - R: CryptoRng + RngCore; - - /// Decrypts the `message` with `sk`. + /// Decrypts the `ciphertext` with the `secret_key`. fn decrypt( - message: &EncryptedMessage<Self>, - sk: &Self::SecretKey, + ciphertext: Self::Ciphertext, + secret_key: Self::SecretKey, ) -> Result<Self::Plaintext, Self::Error>; } /// Encrypted Message #[derive(derivative::Derivative, ScaleDecode, ScaleEncode)] #[derivative( - Clone(bound = "I::Ciphertext: Clone, I::PublicKey: Clone"), - Copy(bound = "I::Ciphertext: Copy, I::PublicKey: Copy"), - Debug(bound = "I::Ciphertext: Debug, I::PublicKey: Debug"), - Default(bound = "I::Ciphertext: Default, I::PublicKey: Default"), - Eq(bound = "I::Ciphertext: Eq, I::PublicKey: Eq"), - Hash(bound = "I::Ciphertext: Hash, I::PublicKey: Hash"), - PartialEq(bound = "I::Ciphertext: PartialEq, I::PublicKey: PartialEq") + Clone(bound = "I::Ciphertext: Clone"), + Copy(bound = "I::Ciphertext: Copy"), + Debug(bound = "I::Ciphertext: Debug"), + Default(bound = "I::Ciphertext: Default"), + Eq(bound = "I::Ciphertext: Eq"), + Hash(bound = "I::Ciphertext: Hash"), + PartialEq(bound = "I::Ciphertext: PartialEq") )] -pub struct EncryptedMessage<I: ?Sized> +pub struct EncryptedMessage<I> where - I: IntegratedEncryptionScheme, + I: IntegratedEncryptionScheme + ?Sized, { - /// Ciphertext of the Message - pub ciphertext: I::Ciphertext, - - /// Ephemeral Public Key - pub ephemeral_public_key: I::PublicKey, + /// Message Ciphertext + ciphertext: I::Ciphertext, } impl<I> EncryptedMessage<I> where - I: IntegratedEncryptionScheme, + I: IntegratedEncryptionScheme + ?Sized, { - /// Builds a new [`EncryptedMessage`] from [`I::Ciphertext`] and an ephemeral [`I::PublicKey`]. - /// - /// [`I::Ciphertext`]: IntegratedEncryptionScheme::Ciphertext - /// [`I::PublicKey`]: IntegratedEncryptionScheme::PublicKey + /// Builds a new [`EncryptedMessage`] from + /// [`I::Ciphertext`](IntegratedEncryptionScheme::Ciphertext). #[inline] - pub fn new(ciphertext: I::Ciphertext, ephemeral_public_key: I::PublicKey) -> Self { - Self { - ciphertext, - ephemeral_public_key, - } + pub fn new(ciphertext: I::Ciphertext) -> Self { + Self { ciphertext } } - /// Generates a new keypair and encrypts the `message`, generating an [`EncryptedMessage`], - /// and returning the keypair. + /// Encrypts the `plaintext` with the `public_key`, generating an [`EncryptedMessage`]. #[inline] - pub fn keygen_encrypt<R>( - message: &I::Plaintext, + pub fn encrypt<R>( + plaintext: I::Plaintext, + public_key: I::PublicKey, rng: &mut R, - ) -> Result<(KeyPair<I>, Self), I::Error> + ) -> Result<Self, I::Error> where - R: CryptoRng + RngCore, + R: CryptoRng + RngCore + ?Sized, { - I::keygen_encrypt(message, rng) + I::encrypt(plaintext, public_key, rng) } - /// Encrypts the `message` with `pk`, generating an [`EncryptedMessage`]. + /// Generates a public/secret keypair and then encrypts the `plaintext` with the generated + /// public key, generating an [`EncryptedMessage`] and a [`SecretKey`]. #[inline] - pub fn encrypt<R>( - message: &I::Plaintext, - pk: &I::PublicKey, + pub fn keygen_encrypt<R>( + plaintext: I::Plaintext, rng: &mut R, - ) -> Result<Self, I::Error> + ) -> Result<(Self, SecretKey<I>), I::Error> where - R: CryptoRng + RngCore, + R: CryptoRng + RngCore + ?Sized, { - I::encrypt(message, pk, rng) + I::keygen_encrypt(plaintext, rng) } - /// Decrypts the `message` with `sk`. + /// Decrypts `self` with the `secret_key`. #[inline] - pub fn decrypt(&self, sk: &I::SecretKey) -> Result<I::Plaintext, I::Error> { - I::decrypt(self, sk) + pub fn decrypt(self, secret_key: SecretKey<I>) -> Result<I::Plaintext, I::Error> { + I::decrypt(self.ciphertext, secret_key.secret_key) } } diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index b0e3b6681..da39f90e4 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -23,15 +23,15 @@ extern crate alloc; mod commitment; mod concat; -mod ies; mod prf; -mod set; pub mod checksum; pub mod constraints; +pub mod ies; +pub mod set; pub use commitment::*; pub use concat::*; -pub use ies::*; +pub use ies::prelude::*; pub use prf::*; -pub use set::*; +pub use set::prelude::*; diff --git a/manta-crypto/src/prf.rs b/manta-crypto/src/prf.rs index 18654bce9..aa82b5e4a 100644 --- a/manta-crypto/src/prf.rs +++ b/manta-crypto/src/prf.rs @@ -16,8 +16,6 @@ //! Pseudorandom Function Families -use core::borrow::Borrow; - /// Pseudorandom Function Families (PRF) Trait pub trait PseudorandomFunctionFamily { /// PRF Seed Type @@ -30,17 +28,11 @@ pub trait PseudorandomFunctionFamily { type Output; /// Evaluates the PRF at the `seed` and `input`. - fn evaluate<S, I>(seed: S, input: I) -> Self::Output - where - S: Borrow<Self::Seed>, - I: Borrow<Self::Input>; + fn evaluate(seed: &Self::Seed, input: &Self::Input) -> Self::Output; /// Evaluates the PRF at the `seed` with the default input. #[inline] - fn evaluate_zero<S>(seed: S) -> Self::Output - where - S: Borrow<Self::Seed>, - { - Self::evaluate(seed, Self::Input::default()) + fn evaluate_zero(seed: &Self::Seed) -> Self::Output { + Self::evaluate(seed, &Self::Input::default()) } } diff --git a/manta-crypto/src/set.rs b/manta-crypto/src/set.rs index bd00493ae..20b64118a 100644 --- a/manta-crypto/src/set.rs +++ b/manta-crypto/src/set.rs @@ -16,8 +16,10 @@ //! Sets and Verified Sets -use core::{fmt::Debug, hash::Hash}; -use manta_codec::{ScaleDecode, ScaleEncode}; +pub(crate) mod prelude { + #[doc(inline)] + pub use crate::set::{Set, VerifiedSet}; +} /// Set Trait pub trait Set { @@ -40,35 +42,30 @@ pub trait Set { } /// Verified Containment Trait -pub trait VerifyContainment<Public, Item> { +pub trait VerifyContainment<Public, Item> +where + Public: ?Sized, + Item: ?Sized, +{ /// Verifies that `self` is a proof that `item` is contained in some [`VerifiedSet`]. fn verify(&self, public: &Public, item: &Item) -> bool; } /// Containment Proof for a [`VerifiedSet`] -#[derive(derivative::Derivative, ScaleDecode, ScaleEncode)] -#[derivative( - Clone(bound = "S::Public: Clone, S::Secret: Clone"), - Debug(bound = "S::Public: Debug, S::Secret: Debug"), - Default(bound = "S::Public: Default, S::Secret: Default"), - Eq(bound = "S::Public: Eq, S::Secret: Eq"), - Hash(bound = "S::Public: Hash, S::Secret: Hash"), - PartialEq(bound = "S::Public: PartialEq, S::Secret: PartialEq") -)] -pub struct ContainmentProof<S: ?Sized> +pub struct ContainmentProof<S> where - S: VerifiedSet, + S: VerifiedSet + ?Sized, { /// Public Input pub input: S::Public, /// Secret Witness - pub witness: S::Secret, + witness: S::Secret, } impl<S> ContainmentProof<S> where - S: VerifiedSet, + S: VerifiedSet + ?Sized, { /// Builds a new [`ContainmentProof`] from public `input` and secret `witness`. #[inline] @@ -126,7 +123,7 @@ pub trait VerifiedSet { impl<S> Set for S where - S: VerifiedSet, + S: VerifiedSet + ?Sized, { type Item = S::Item; diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml new file mode 100644 index 000000000..b1a19e6c6 --- /dev/null +++ b/manta-pay/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "manta-pay" +edition = "2018" +version = "0.3.0" +authors = ["Manta Network <contact@manta.network>"] +readme = "README.md" +license-file = "LICENSE" +repository = "https://github.com/Manta-Network/manta-rs" +homepage = "https://github.com/Manta-Network" +documentation = "https://github.com/Manta-Network/manta-rs" +categories = [""] +keywords = [""] +description = "The Manta-Pay protocol and implementaion." +publish = false + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[badges] +is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } +is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } +maintenance = { status = "actively-developed" } + +[features] +default = [] +std = [] + +[dependencies] +aes-gcm = { version = "0.9.4" } +ark-std = { version = "0.3.0", default-features = false } +blake2 = { version = "0.9.2", default-features = false } +generic-array = { version = "0.14.4", default-features = false } +manta-accounting = { path = "../manta-accounting", default-features = false } +manta-codec = { path = "../manta-codec", default-features = false, features = ["derive"] } +manta-crypto = { path = "../manta-crypto", default-features = false } +manta-util = { path = "../manta-util", default-features = false } +parity-scale-codec = { version = "2.2.0", default-features = false } +x25519-dalek = { git = "https://github.com/Manta-Network/x25519-dalek", default-features = false, features = ["u64_backend"] } diff --git a/manta-pay/LICENSE b/manta-pay/LICENSE new file mode 120000 index 000000000..ea5b60640 --- /dev/null +++ b/manta-pay/LICENSE @@ -0,0 +1 @@ +../LICENSE \ No newline at end of file diff --git a/manta-pay/README.md b/manta-pay/README.md new file mode 100644 index 000000000..6d90a370a --- /dev/null +++ b/manta-pay/README.md @@ -0,0 +1 @@ +# manta-pay diff --git a/manta-pay/build.rs b/manta-pay/build.rs new file mode 100644 index 000000000..9ab651580 --- /dev/null +++ b/manta-pay/build.rs @@ -0,0 +1,19 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Manta Pay Build Script + +fn main() {} diff --git a/manta-pay/src/ies.rs b/manta-pay/src/ies.rs new file mode 100644 index 000000000..652729ada --- /dev/null +++ b/manta-pay/src/ies.rs @@ -0,0 +1,159 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! IES Implementation + +use aes_gcm::{ + aead::{Aead, NewAead}, + Aes256Gcm, Nonce, +}; +use ark_std::rand::{CryptoRng, RngCore}; +use blake2::{Blake2s, Digest}; +use generic_array::GenericArray; +use manta_accounting::Asset; +use manta_codec::{ScaleDecode, ScaleEncode}; +use manta_crypto::{ + ies::{self, KeyPair}, + IntegratedEncryptionScheme, +}; +use manta_util::try_into_array_unchecked; +use x25519_dalek::{EphemeralSecret, PublicKey as PubKey, StaticSecret}; + +/// Public Key Type +pub type PublicKey = [u8; 32]; + +/// Secret Key Type +pub type SecretKey = [u8; 32]; + +/// Ciphertext Type +// FIXME: this should be automatically calculated from [`Asset`] +// FIXME: is this calculation correct? how do we know? +pub type Ciphertext = [u8; Asset::SIZE + 16]; + +/// Ephemeral Public Key Type +pub type EphemeralPublicKey = PublicKey; + +/// Augmented Ciphertext +pub struct AugmentedCiphertext { + /// Base Ciphertext + pub ciphertext: Ciphertext, + + /// Ephemeral Public Key + pub ephemeral_public_key: EphemeralPublicKey, +} + +impl AugmentedCiphertext { + /// Builds a new [`AugmentedCiphertext`] from `ciphertext` and `ephemeral_public_key`. + #[inline] + pub const fn new(ciphertext: Ciphertext, ephemeral_public_key: EphemeralPublicKey) -> Self { + Self { + ciphertext, + ephemeral_public_key, + } + } +} + +/// Encrypted Message for [`IES`] +pub type EncryptedMessage = ies::EncryptedMessage<IES>; + +/// Implementation of [`IntegratedEncryptionScheme`] +#[derive( + Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, ScaleDecode, ScaleEncode, +)] +pub struct IES; + +impl IES { + /// Encryption/Decryption Nonce + const NONCE: &'static [u8] = b"manta rocks!"; + + /// KDF Salt + const KDF_SALT: &'static [u8] = b"manta kdf instantiated with blake2s hash function"; + + /// Runs `blake2s::hkdf_extract(salt, seed)` with a fixed salt. + fn blake2s_kdf(input: &[u8]) -> [u8; 32] { + let mut hasher = Blake2s::new(); + hasher.update([input, Self::KDF_SALT].concat()); + try_into_array_unchecked(hasher.finalize()) + } +} + +impl IntegratedEncryptionScheme for IES { + type PublicKey = PublicKey; + + type SecretKey = SecretKey; + + type Plaintext = Asset; + + type Ciphertext = AugmentedCiphertext; + + type Error = aes_gcm::Error; + + #[inline] + fn keygen<R>(rng: &mut R) -> KeyPair<Self> + where + R: CryptoRng + RngCore + ?Sized, + { + let sk = StaticSecret::new(rng); + let pk = PubKey::from(&sk); + KeyPair::new(pk.to_bytes(), sk.to_bytes()) + } + + fn encrypt<R>( + plaintext: Self::Plaintext, + public_key: Self::PublicKey, + rng: &mut R, + ) -> Result<EncryptedMessage, Self::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + let ephemeral_secret_key = EphemeralSecret::new(rng); + let ephemeral_public_key = PubKey::from(&ephemeral_secret_key); + let shared_secret = ephemeral_secret_key.diffie_hellman(&PubKey::from(public_key)); + let ss = Self::blake2s_kdf(&shared_secret.to_bytes()); + let aes_key = GenericArray::from_slice(&ss); + + // SAFETY: Using a deterministic nonce is ok since we never reuse keys. + let ciphertext = Aes256Gcm::new(aes_key).encrypt( + Nonce::from_slice(Self::NONCE), + plaintext.into_bytes().as_ref(), + )?; + + Ok(EncryptedMessage::new(AugmentedCiphertext::new( + try_into_array_unchecked(ciphertext), + ephemeral_public_key.to_bytes(), + ))) + } + + fn decrypt( + ciphertext: Self::Ciphertext, + secret_key: Self::SecretKey, + ) -> Result<Self::Plaintext, Self::Error> { + let sk = StaticSecret::from(secret_key); + let shared_secret = sk.diffie_hellman(&PubKey::from(ciphertext.ephemeral_public_key)); + let ss = Self::blake2s_kdf(&shared_secret.to_bytes()); + let aes_key = GenericArray::from_slice(&ss); + + // SAFETY: Using a deterministic nonce is ok since we never reuse keys. + let plaintext = Aes256Gcm::new(aes_key).decrypt( + Nonce::from_slice(Self::NONCE), + ciphertext.ciphertext.as_ref(), + )?; + + Ok(Asset::from_bytes(try_into_array_unchecked( + &plaintext[..Asset::SIZE], + ))) + } +} diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs new file mode 100644 index 000000000..b768d7339 --- /dev/null +++ b/manta-pay/src/lib.rs @@ -0,0 +1,22 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Manta Pay Implementation + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(docsrs, feature(doc_cfg), forbid(broken_intra_doc_links))] + +pub mod ies; diff --git a/manta-util/Cargo.toml b/manta-util/Cargo.toml index 78cb7ccfb..acd320013 100644 --- a/manta-util/Cargo.toml +++ b/manta-util/Cargo.toml @@ -10,7 +10,7 @@ homepage = "https://github.com/Manta-Network" documentation = "https://github.com/Manta-Network/manta-rs" categories = [""] keywords = ["utilities"] -description = "Basic utilities for manta-rs crates." +description = "Basic utilities for Manta crates." publish = false [package.metadata.docs.rs] diff --git a/manta/Cargo.toml b/manta/Cargo.toml new file mode 100644 index 000000000..0cc5bbb0b --- /dev/null +++ b/manta/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "manta" +edition = "2018" +version = "0.3.0" +authors = ["Manta Network <contact@manta.network>"] +readme = "README.md" +license-file = "LICENSE" +repository = "https://github.com/Manta-Network/manta-rs" +homepage = "https://github.com/Manta-Network" +documentation = "https://github.com/Manta-Network/manta-rs" +categories = [""] +keywords = [""] +description = "Manta Network main crate." +publish = false + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[badges] +is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } +is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } +maintenance = { status = "actively-developed" } + +[features] +default = [] +std = [] + +[dependencies] +manta-accounting = { path = "../manta-accounting", default-features = false } +manta-codec = { path = "../manta-codec", default-features = false } +manta-crypto = { path = "../manta-crypto", default-features = false } +manta-pay = { path = "../manta-pay", default-features = false } +parity-scale-codec = { version = "2.2.0", default-features = false } diff --git a/manta/LICENSE b/manta/LICENSE new file mode 120000 index 000000000..ea5b60640 --- /dev/null +++ b/manta/LICENSE @@ -0,0 +1 @@ +../LICENSE \ No newline at end of file diff --git a/manta/README.md b/manta/README.md new file mode 100644 index 000000000..339b4bf22 --- /dev/null +++ b/manta/README.md @@ -0,0 +1 @@ +# manta diff --git a/manta/src/lib.rs b/manta/src/lib.rs new file mode 100644 index 000000000..59e33e387 --- /dev/null +++ b/manta/src/lib.rs @@ -0,0 +1,32 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! The Manta Network + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(docsrs, feature(doc_cfg), forbid(broken_intra_doc_links))] + +#[doc(inline)] +pub use manta_accounting as accounting; + +#[doc(inline)] +pub use manta_codec as codec; + +#[doc(inline)] +pub use manta_crypto as crypto; + +#[doc(inline)] +pub use manta_pay as pay; From 4968dfa28e3bd400e4b196559017811c1cb8e9a8 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 3 Sep 2021 03:57:32 -0400 Subject: [PATCH 006/275] fix indentation --- manta-crypto/src/concat.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/manta-crypto/src/concat.rs b/manta-crypto/src/concat.rs index fe6179375..3cc9ad1bf 100644 --- a/manta-crypto/src/concat.rs +++ b/manta-crypto/src/concat.rs @@ -164,9 +164,9 @@ macro_rules! concatenate { ($($item:expr),*) => { { extern crate alloc; - let mut accumulator = ::alloc::vec::Vec::new(); - $($crate::ConcatBytes::reserve_concat($item, &mut accumulator);)* - $crate::Accumulator::finish(accumulator) + let mut accumulator = ::alloc::vec::Vec::new(); + $($crate::ConcatBytes::reserve_concat($item, &mut accumulator);)* + $crate::Accumulator::finish(accumulator) } } } From b6cd7dc226516443ac96ec8750676370a43fe779 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 3 Sep 2021 04:30:19 -0400 Subject: [PATCH 007/275] add methods to Asset, AssetCollection, AssetParameters --- manta-accounting/src/account.rs | 61 +++++++++++++++++++++------------ manta-accounting/src/asset.rs | 60 +++++++++++++++++++++++++++++++- 2 files changed, 99 insertions(+), 22 deletions(-) diff --git a/manta-accounting/src/account.rs b/manta-accounting/src/account.rs index 25b4870da..153400aaa 100644 --- a/manta-accounting/src/account.rs +++ b/manta-accounting/src/account.rs @@ -171,6 +171,43 @@ where utxo_randomness, } } + + /// Generates a new void number commitment using `public_key`. + #[inline] + pub fn void_number_commitment( + &self, + commitment_scheme: &C::CommitmentScheme, + public_key: &PublicKey<C>, + ) -> VoidNumberCommitment<C> + where + PublicKey<C>: ConcatBytes, + VoidNumberGenerator<C>: ConcatBytes, + { + generate_void_number_commitment::<C>( + commitment_scheme, + public_key, + &self.void_number_generator, + &self.void_number_commitment_randomness, + ) + } + + /// Generates a [`Utxo`] from a given `asset`, `void_number_commitment`, and `utxo_randomness`. + pub fn utxo( + &self, + commitment_scheme: &C::CommitmentScheme, + asset: &Asset, + void_number_commitment: &VoidNumberCommitment<C>, + ) -> Utxo<C> + where + VoidNumberCommitment<C>: ConcatBytes, + { + generate_utxo::<C>( + commitment_scheme, + asset, + void_number_commitment, + &self.utxo_randomness, + ) + } } impl<C> Distribution<AssetParameters<C>> for Standard @@ -342,24 +379,6 @@ where ) } - /// Generates a new void number commitment using `parameters`. - #[inline] - pub fn void_number_commitment_from_parameters( - &self, - commitment_scheme: &C::CommitmentScheme, - parameters: &AssetParameters<C>, - ) -> VoidNumberCommitment<C> - where - PublicKey<C>: ConcatBytes, - VoidNumberGenerator<C>: ConcatBytes, - { - self.void_number_commitment( - commitment_scheme, - &parameters.void_number_generator, - &parameters.void_number_commitment_randomness, - ) - } - /// Generates a [`Utxo`] for an `asset` using the `parameters`. #[inline] pub fn utxo( @@ -376,7 +395,7 @@ where generate_utxo::<C>( commitment_scheme, asset, - &self.void_number_commitment_from_parameters(commitment_scheme, parameters), + &parameters.void_number_commitment(commitment_scheme, &self.public_key()), &parameters.utxo_randomness, ) } @@ -425,8 +444,8 @@ where let (asset_public_key, asset_secret_key) = asset_keypair.split(); ( ShieldedIdentity { - void_number_commitment: self - .void_number_commitment_from_parameters(commitment_scheme, &parameters), + void_number_commitment: parameters + .void_number_commitment(commitment_scheme, &self.public_key()), utxo_randomness: parameters.utxo_randomness, asset_public_key, }, diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index a158a9848..cdf20aac1 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -20,6 +20,7 @@ use alloc::vec::Vec; use core::{ + convert::TryFrom, fmt::Debug, hash::Hash, ops::{Add, AddAssign, Mul, Sub, SubAssign}, @@ -29,12 +30,13 @@ use derive_more::{ }; use manta_codec::{ScaleDecode, ScaleEncode}; use manta_crypto::{Accumulator, ConcatBytes}; -use manta_util::try_into_array_unchecked; +use manta_util::{array_map, fallible_array_map, try_into_array_unchecked}; use rand::{ distributions::{Distribution, Standard}, Rng, RngCore, }; +/// [`AssetId`] Base Type type AssetIdType = u32; /// Asset Id Type @@ -112,6 +114,7 @@ impl Mul<AssetId> for AssetIdType { } } +/// [`AssetBalance`] Base Type type AssetBalanceType = u128; /// Asset Balance Type @@ -221,6 +224,12 @@ impl Asset { Self::new(self.id, value) } + /// Checks if the `rhs` asset has the same [`AssetId`]. + #[inline] + pub const fn same_id(&self, rhs: &Asset) -> bool { + self.id.0 == rhs.id.0 + } + /// Converts `self` into a byte array. #[inline] pub fn into_bytes(self) -> [u8; Self::SIZE] { @@ -317,6 +326,10 @@ impl Distribution<Asset> for Standard { } /// Asset Collection +#[derive( + Clone, Copy, Debug, Eq, From, Hash, Ord, PartialEq, PartialOrd, ScaleDecode, ScaleEncode, +)] +#[from(forward)] pub struct AssetCollection<const N: usize> { /// Asset Id pub id: AssetId, @@ -332,3 +345,48 @@ impl<const N: usize> AssetCollection<N> { Self { id, values } } } + +impl<const N: usize> Default for AssetCollection<N> { + #[inline] + fn default() -> Self { + Self::new(Default::default(), [Default::default(); N]) + } +} + +impl<const N: usize> From<AssetCollection<N>> for [Asset; N] { + #[inline] + fn from(collection: AssetCollection<N>) -> Self { + array_map(collection.values, move |v| Asset::new(collection.id, v)) + } +} + +impl<const N: usize> TryFrom<[Asset; N]> for AssetCollection<N> { + type Error = usize; + + #[inline] + fn try_from(array: [Asset; N]) -> Result<Self, Self::Error> { + let mut counter: usize = 0; + let mut base_id = None; + let values = fallible_array_map(array, move |asset| { + let result = match base_id { + Some(id) => { + if id == asset.id { + Ok(asset.value) + } else { + Err(counter) + } + } + _ => { + base_id = Some(asset.id); + Ok(asset.value) + } + }; + counter += 1; + result + })?; + match base_id { + Some(id) => Ok(Self::new(id, values)), + _ => Err(0), + } + } +} From 23903fb5696931dfbd383d1c764131af29401a3c Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 3 Sep 2021 04:47:19 -0400 Subject: [PATCH 008/275] add public/secret transfers --- manta-accounting/src/account.rs | 19 ++++-------- manta-accounting/src/transfer.rs | 50 ++++++++++++++++++-------------- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/manta-accounting/src/account.rs b/manta-accounting/src/account.rs index 153400aaa..b8b83bb83 100644 --- a/manta-accounting/src/account.rs +++ b/manta-accounting/src/account.rs @@ -35,14 +35,11 @@ use rand::{ /// Secret Key Generator Trait pub trait SecretKeyGenerator<SecretKey> { - /// Index Type - type Index; - /// Key Generation Error type Error; - /// Generates a secret key for a given index starting from some root key. - fn generate_key(&self, index: &Self::Index) -> Result<SecretKey, Self::Error>; + /// Generates a new secret key. + fn generate_key(&mut self) -> Result<SecretKey, Self::Error>; } /// [`Identity`] Configuration Trait @@ -63,12 +60,6 @@ pub trait IdentityConfiguration { type Rng: CryptoRng + RngCore + SeedableRng<Seed = Self::SecretKey>; } -/// [`SecretKeyGenerator::Index`] Type Alias -pub type SecretKeyGeneratorIndex<C> = - <<C as IdentityConfiguration>::SecretKeyGenerator as SecretKeyGenerator< - <C as IdentityConfiguration>::SecretKey, - >>::Index; - /// [`SecretKeyGenerator::Error`] Type Alias pub type SecretKeyGeneratorError<C> = <<C as IdentityConfiguration>::SecretKeyGenerator as SecretKeyGenerator< @@ -192,6 +183,7 @@ where } /// Generates a [`Utxo`] from a given `asset`, `void_number_commitment`, and `utxo_randomness`. + #[inline] pub fn utxo( &self, commitment_scheme: &C::CommitmentScheme, @@ -250,10 +242,9 @@ where /// Generates a new [`Identity`] from a secret key generation source. #[inline] pub fn generate( - source: &C::SecretKeyGenerator, - index: &SecretKeyGeneratorIndex<C>, + source: &mut C::SecretKeyGenerator, ) -> Result<Self, SecretKeyGeneratorError<C>> { - source.generate_key(index).map(Self::new) + source.generate_key().map(Self::new) } /// Returns the public key associated with this identity. diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 6cbf0d251..aa3d054f1 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -18,8 +18,9 @@ use crate::{ account::{IdentityConfiguration, Receiver, Sender, Utxo}, - asset::{Asset, AssetCollection}, + asset::{Asset, AssetBalance, AssetId}, }; +use manta_codec::{ScaleDecode, ScaleEncode}; use manta_crypto::{IntegratedEncryptionScheme, VerifiedSet}; /// Transfer Configuration Trait @@ -31,31 +32,21 @@ pub trait TransferConfiguration: IdentityConfiguration { type UtxoSet: VerifiedSet<Item = Utxo<Self>>; } -/// Transfer Protocol -pub struct Transfer< - T, - const SOURCES: usize, - const SENDERS: usize, - const RECEIVERS: usize, - const SINKS: usize, -> where - T: TransferConfiguration, -{ - /// Public Asset Sources - pub sources: AssetCollection<SOURCES>, +/// Public Transfer Protocol +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, ScaleDecode, ScaleEncode)] +pub struct PublicTransfer<const SOURCES: usize, const SINKS: usize> { + /// Asset Id + pub asset_id: AssetId, - /// Secret Senders - pub senders: [Sender<T, T::UtxoSet>; SENDERS], - - /// Secret Receivers - pub receivers: [Receiver<T, T::IntegratedEncryptionScheme>; RECEIVERS], + /// Public Asset Sources + pub sources: [AssetBalance; SOURCES], /// Public Asset Sinks - pub sinks: AssetCollection<SINKS>, + pub sinks: [AssetBalance; SINKS], } -/// Private Transfer Protocol -pub struct PrivateTransfer<T, const SENDERS: usize, const RECEIVERS: usize> +/// Secret Transfer Protocol +pub struct SecretTrasfer<T, const SENDERS: usize, const RECEIVERS: usize> where T: TransferConfiguration, { @@ -65,3 +56,20 @@ where /// Secret Receivers pub receivers: [Receiver<T, T::IntegratedEncryptionScheme>; RECEIVERS], } + +/// Transfer Protocol +pub struct Transfer< + T, + const SOURCES: usize, + const SENDERS: usize, + const RECEIVERS: usize, + const SINKS: usize, +> where + T: TransferConfiguration, +{ + /// Public Transfer + pub public: PublicTransfer<SOURCES, SINKS>, + + /// Secret Trasfer + pub secret: SecretTrasfer<T, SENDERS, RECEIVERS>, +} From dfee11a56b5eb6c889413f07886711b2fedcd391 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 5 Sep 2021 03:24:24 -0400 Subject: [PATCH 009/275] continue polishing account abstractions and begin wallet and ledger abstractions --- manta-accounting/src/account.rs | 490 ++++++++++++++++++++++++++----- manta-accounting/src/asset.rs | 32 ++ manta-accounting/src/ledger.rs | 191 ++++++++++++ manta-accounting/src/lib.rs | 9 +- manta-accounting/src/transfer.rs | 79 +++-- manta-accounting/src/wallet.rs | 167 +++++++++++ manta-crypto/src/ies.rs | 31 +- manta-crypto/src/set.rs | 79 ++--- manta-pay/src/ies.rs | 38 +-- manta-util/src/lib.rs | 1 + 10 files changed, 950 insertions(+), 167 deletions(-) create mode 100644 manta-accounting/src/ledger.rs create mode 100644 manta-accounting/src/wallet.rs diff --git a/manta-accounting/src/account.rs b/manta-accounting/src/account.rs index b8b83bb83..72b347b1d 100644 --- a/manta-accounting/src/account.rs +++ b/manta-accounting/src/account.rs @@ -18,7 +18,10 @@ // FIXME: ensure secret keys cannot be made public by some API call -use crate::asset::Asset; +use crate::{ + asset::Asset, + ledger::{self, IntoPost, Ledger, Post}, +}; use core::{convert::Infallible, fmt::Debug, hash::Hash, marker::PhantomData}; use manta_codec::{ScaleDecode, ScaleEncode}; use manta_crypto::{ @@ -33,6 +36,14 @@ use rand::{ CryptoRng, Rng, RngCore, SeedableRng, }; +pub(crate) mod prelude { + #[doc(inline)] + pub use crate::account::{ + Identity, IdentityConfiguration, Receiver, SecretKeyGeneratorError, Sender, SenderError, + ShieldedIdentity, Spend, SpendError, Utxo, VoidNumber, + }; +} + /// Secret Key Generator Trait pub trait SecretKeyGenerator<SecretKey> { /// Key Generation Error @@ -106,6 +117,41 @@ pub type UtxoRandomness<C> = CommitmentSchemeRandomness<C>; /// UTXO Type Alias pub type Utxo<C> = CommitmentSchemeOutput<C>; +/// Generates a [`VoidNumberCommitment`] from a given `public_key`, `void_number_generator`, and +/// `void_number_commitment_randomness`. +#[inline] +pub fn generate_void_number_commitment<C>( + commitment_scheme: &C::CommitmentScheme, + public_key: &PublicKey<C>, + void_number_generator: &VoidNumberGenerator<C>, + void_number_commitment_randomness: &VoidNumberCommitmentRandomness<C>, +) -> VoidNumberCommitment<C> +where + C: IdentityConfiguration, + PublicKey<C>: ConcatBytes, + VoidNumberGenerator<C>: ConcatBytes, +{ + commitment_scheme.commit( + concatenate!(public_key, void_number_generator), + void_number_commitment_randomness, + ) +} + +/// Generates a [`Utxo`] from a given `asset`, `void_number_commitment`, and `utxo_randomness`. +#[inline] +pub fn generate_utxo<C>( + commitment_scheme: &C::CommitmentScheme, + asset: &Asset, + void_number_commitment: &VoidNumberCommitment<C>, + utxo_randomness: &UtxoRandomness<C>, +) -> Utxo<C> +where + C: IdentityConfiguration, + VoidNumberCommitment<C>: ConcatBytes, +{ + commitment_scheme.commit(concatenate!(asset, void_number_commitment), utxo_randomness) +} + /// Public Parameters for using an [`Asset`] #[derive(derivative::Derivative, ScaleDecode, ScaleEncode)] #[derivative( @@ -182,7 +228,7 @@ where ) } - /// Generates a [`Utxo`] from a given `asset`, `void_number_commitment`, and `utxo_randomness`. + /// Generates a [`Utxo`] from a given `asset` and `void_number_commitment`. #[inline] pub fn utxo( &self, @@ -247,12 +293,6 @@ where source.generate_key().map(Self::new) } - /// Returns the public key associated with this identity. - #[inline] - pub fn public_key(&self) -> PublicKey<C> { - C::PseudorandomFunctionFamily::evaluate_zero(&self.secret_key) - } - /// Generates the associated `C::Rng` and a `AssetParameters<C>` for this identity. /// /// # API Note @@ -287,7 +327,7 @@ where /// Generates [`AssetParameters`] for assets that are used by this identity. #[inline] - pub fn parameters(&self) -> AssetParameters<C> + fn parameters(&self) -> AssetParameters<C> where Standard: Distribution<AssetParameters<C>>, { @@ -323,7 +363,7 @@ where /// Generates [`AssetParameters`] and a [`KeyPair`](ies::KeyPair) for assets that are used by /// this identity. #[inline] - pub fn parameters_and_asset_keypair<I>(&self) -> (AssetParameters<C>, ies::KeyPair<I>) + fn parameters_and_asset_keypair<I>(&self) -> (AssetParameters<C>, ies::KeyPair<I>) where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, @@ -334,45 +374,48 @@ where /// Generates a [`KeyPair`](ies::KeyPair) for assets that are used by this identity. #[inline] - pub fn asset_keypair<I>(&self) -> ies::KeyPair<I> + fn asset_keypair<I>(&self) -> ies::KeyPair<I> where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { - let (_, asset_keypair) = self.parameters_and_asset_keypair(); + let (_, asset_keypair) = self.parameters_and_asset_keypair::<I>(); asset_keypair } + /// Returns the public key associated with this identity. + #[inline] + pub(crate) fn public_key(&self) -> PublicKey<C> { + C::PseudorandomFunctionFamily::evaluate_zero(&self.secret_key) + } + /// Generates a new void number using the `void_number_generator` parameter. #[inline] - pub fn void_number(&self, void_number_generator: &VoidNumberGenerator<C>) -> VoidNumber<C> { + pub(crate) fn void_number( + &self, + void_number_generator: &VoidNumberGenerator<C>, + ) -> VoidNumber<C> { C::PseudorandomFunctionFamily::evaluate(&self.secret_key, void_number_generator) } /// Generates a new void number commitment using the `void_number_generator` and /// `void_number_commitment_randomness`. #[inline] - pub fn void_number_commitment( + pub(crate) fn void_number_commitment( &self, commitment_scheme: &C::CommitmentScheme, - void_number_generator: &VoidNumberGenerator<C>, - void_number_commitment_randomness: &VoidNumberCommitmentRandomness<C>, + parameters: &AssetParameters<C>, ) -> VoidNumberCommitment<C> where PublicKey<C>: ConcatBytes, VoidNumberGenerator<C>: ConcatBytes, { - generate_void_number_commitment::<C>( - commitment_scheme, - &self.public_key(), - void_number_generator, - void_number_commitment_randomness, - ) + parameters.void_number_commitment(commitment_scheme, &self.public_key()) } /// Generates a [`Utxo`] for an `asset` using the `parameters`. #[inline] - pub fn utxo( + pub(crate) fn utxo( &self, commitment_scheme: &C::CommitmentScheme, asset: &Asset, @@ -383,11 +426,10 @@ where VoidNumberGenerator<C>: ConcatBytes, VoidNumberCommitment<C>: ConcatBytes, { - generate_utxo::<C>( + parameters.utxo( commitment_scheme, asset, - &parameters.void_number_commitment(commitment_scheme, &self.public_key()), - &parameters.utxo_randomness, + &self.void_number_commitment(commitment_scheme, parameters), ) } @@ -419,7 +461,106 @@ where }) } - /// Builds a new [`ShieldedIdentity`]-[`Spend`] pair for the given `asset`. + /// Generates a new [`Identity`] from a secret key generation source and builds a new + /// [`Sender`] from it. + #[inline] + pub fn generate_sender<S>( + source: &mut C::SecretKeyGenerator, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + utxo_set: &S, + ) -> Result<Sender<C, S>, SenderError<C, S>> + where + S: VerifiedSet<Item = Utxo<C>>, + Standard: Distribution<AssetParameters<C>>, + PublicKey<C>: ConcatBytes, + VoidNumberGenerator<C>: ConcatBytes, + VoidNumberCommitment<C>: ConcatBytes, + { + Self::generate(source) + .map_err(SenderError::SecretKeyError)? + .into_sender(commitment_scheme, asset, utxo_set) + .map_err(SenderError::MissingUtxo) + } + + /// Builds a new [`ShieldedIdentity`] from `commitment_scheme`, `parameters`, and + /// `asset_keypair`. + #[inline] + fn build_shielded_identity<I>( + &self, + commitment_scheme: &C::CommitmentScheme, + parameters: AssetParameters<C>, + asset_public_key: ies::PublicKey<I>, + ) -> ShieldedIdentity<C, I> + where + I: IntegratedEncryptionScheme<Plaintext = Asset>, + Standard: Distribution<AssetParameters<C>>, + PublicKey<C>: ConcatBytes, + VoidNumberGenerator<C>: ConcatBytes, + { + ShieldedIdentity { + void_number_commitment: self.void_number_commitment(commitment_scheme, &parameters), + utxo_randomness: parameters.utxo_randomness, + asset_public_key, + } + } + + /// Builds a new [`ShieldedIdentity`] from this identity. + #[inline] + pub fn into_shielded<I>(self, commitment_scheme: &C::CommitmentScheme) -> ShieldedIdentity<C, I> + where + I: IntegratedEncryptionScheme<Plaintext = Asset>, + Standard: Distribution<AssetParameters<C>>, + PublicKey<C>: ConcatBytes, + VoidNumberGenerator<C>: ConcatBytes, + { + let (parameters, asset_keypair) = self.parameters_and_asset_keypair::<I>(); + self.build_shielded_identity(commitment_scheme, parameters, asset_keypair.into_public()) + } + + /// Generates a new [`Identity`] from a secret key generation source and builds a new + /// [`ShieldedIdentity`] from it. + #[inline] + pub fn generate_shielded<I>( + source: &mut C::SecretKeyGenerator, + commitment_scheme: &C::CommitmentScheme, + ) -> Result<ShieldedIdentity<C, I>, SecretKeyGeneratorError<C>> + where + I: IntegratedEncryptionScheme<Plaintext = Asset>, + Standard: Distribution<AssetParameters<C>>, + PublicKey<C>: ConcatBytes, + VoidNumberGenerator<C>: ConcatBytes, + { + Ok(Self::generate(source)?.into_shielded(commitment_scheme)) + } + + /// Builds a new [`Spend`]. + #[inline] + pub fn into_spend<I>(self) -> Spend<C, I> + where + I: IntegratedEncryptionScheme<Plaintext = Asset>, + Standard: Distribution<AssetParameters<C>>, + { + Spend { + asset_secret_key: self.asset_keypair::<I>().into_secret(), + identity: self, + } + } + + /// Generates a new [`Identity`] from a secret key generation source and builds a new + /// [`Spend`] from it. + #[inline] + pub fn generate_spend<I>( + source: &mut C::SecretKeyGenerator, + ) -> Result<Spend<C, I>, SecretKeyGeneratorError<C>> + where + I: IntegratedEncryptionScheme<Plaintext = Asset>, + Standard: Distribution<AssetParameters<C>>, + { + Ok(Self::generate(source)?.into_spend()) + } + + /// Builds a new [`ShieldedIdentity`]-[`Spend`] pair. #[inline] pub fn into_receiver<I>( self, @@ -432,20 +573,28 @@ where VoidNumberGenerator<C>: ConcatBytes, { let (parameters, asset_keypair) = self.parameters_and_asset_keypair::<I>(); - let (asset_public_key, asset_secret_key) = asset_keypair.split(); + let (asset_public_key, asset_secret_key) = asset_keypair.into(); ( - ShieldedIdentity { - void_number_commitment: parameters - .void_number_commitment(commitment_scheme, &self.public_key()), - utxo_randomness: parameters.utxo_randomness, - asset_public_key, - }, - Spend { - identity: self, - asset_secret_key, - }, + self.build_shielded_identity(commitment_scheme, parameters, asset_public_key), + Spend::new(self, asset_secret_key), ) } + + /// Generates a new [`Identity`] from a secret key generation source and builds a new + /// [`ShieldedIdentity`]-[`Spend`] pair from it. + #[inline] + pub fn generate_receiver<I>( + source: &mut C::SecretKeyGenerator, + commitment_scheme: &C::CommitmentScheme, + ) -> Result<(ShieldedIdentity<C, I>, Spend<C, I>), SecretKeyGeneratorError<C>> + where + I: IntegratedEncryptionScheme<Plaintext = Asset>, + Standard: Distribution<AssetParameters<C>>, + PublicKey<C>: ConcatBytes, + VoidNumberGenerator<C>: ConcatBytes, + { + Ok(Self::generate(source)?.into_receiver(commitment_scheme)) + } } impl<C> Distribution<Identity<C>> for Standard @@ -480,6 +629,34 @@ where C: IdentityConfiguration, I: IntegratedEncryptionScheme<Plaintext = Asset>, { + /// Builds a new [`ShieldedIdentity`] from `identity` and `commitment_scheme`. + #[inline] + pub fn from_identity(identity: Identity<C>, commitment_scheme: &C::CommitmentScheme) -> Self + where + I: IntegratedEncryptionScheme<Plaintext = Asset>, + Standard: Distribution<AssetParameters<C>>, + PublicKey<C>: ConcatBytes, + VoidNumberGenerator<C>: ConcatBytes, + { + identity.into_shielded(commitment_scheme) + } + + /// Generates a new [`Identity`] from a secret key generation source and builds a + /// [`ShieldedIdentity`] from it. + #[inline] + pub fn generate( + source: &mut C::SecretKeyGenerator, + commitment_scheme: &C::CommitmentScheme, + ) -> Result<Self, SecretKeyGeneratorError<C>> + where + I: IntegratedEncryptionScheme<Plaintext = Asset>, + Standard: Distribution<AssetParameters<C>>, + PublicKey<C>: ConcatBytes, + VoidNumberGenerator<C>: ConcatBytes, + { + Identity::generate_shielded(source, commitment_scheme) + } + /// Generates a [`Receiver`] from a [`ShieldedIdentity`]. #[inline] pub fn into_receiver<R>( @@ -557,10 +734,37 @@ where C: IdentityConfiguration, I: IntegratedEncryptionScheme<Plaintext = Asset>, { - /// Returns the public key associated with this spend. + /// Generates a new `Spend` from an `Identity` and an `ies::SecretKey<I>`. + /// + /// # API Note + /// + /// This function is intentionally private so that secret keys are not part of the + /// public interface. + #[inline] + fn new(identity: Identity<C>, asset_secret_key: ies::SecretKey<I>) -> Self { + Self { + identity, + asset_secret_key, + } + } + + /// Builds a new [`ShieldedIdentity`] from an `identity`. #[inline] - pub fn public_key(&self) -> PublicKey<C> { - self.identity.public_key() + pub fn from_identity(identity: Identity<C>) -> Self + where + Standard: Distribution<AssetParameters<C>>, + { + identity.into_spend() + } + + /// Generates a new [`Identity`] from a secret key generation source and builds a + /// [`Spend`] from it. + #[inline] + pub fn generate(source: &mut C::SecretKeyGenerator) -> Result<Self, SecretKeyGeneratorError<C>> + where + Standard: Distribution<AssetParameters<C>>, + { + Identity::generate_spend(source) } /// Builds a new [`Sender`] for the given `encrypted_asset`. @@ -590,6 +794,40 @@ where } } +impl<C, I> From<Identity<C>> for Spend<C, I> +where + C: IdentityConfiguration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + Standard: Distribution<AssetParameters<C>>, +{ + #[inline] + fn from(identity: Identity<C>) -> Self { + Self::from_identity(identity) + } +} + +/// Sender Error +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "SecretKeyGeneratorError<C>: Clone, S::ContainmentError: Clone"), + Copy(bound = "SecretKeyGeneratorError<C>: Copy, S::ContainmentError: Copy"), + Debug(bound = "SecretKeyGeneratorError<C>: Debug, S::ContainmentError: Debug"), + Eq(bound = "SecretKeyGeneratorError<C>: Eq, S::ContainmentError: Eq"), + Hash(bound = "SecretKeyGeneratorError<C>: Hash, S::ContainmentError: Hash"), + PartialEq(bound = "SecretKeyGeneratorError<C>: PartialEq, S::ContainmentError: PartialEq") +)] +pub enum SenderError<C, S> +where + C: IdentityConfiguration, + S: VerifiedSet<Item = Utxo<C>>, +{ + /// Secret Key Generator Error + SecretKeyError(SecretKeyGeneratorError<C>), + + /// Containment Error + MissingUtxo(S::ContainmentError), +} + /// Sender pub struct Sender<C, S> where @@ -597,7 +835,7 @@ where S: VerifiedSet<Item = Utxo<C>>, { /// Sender Identity - identity: Identity<C>, + pub(crate) identity: Identity<C>, /// Asset pub asset: Asset, @@ -615,6 +853,94 @@ where pub utxo_containment_proof: ContainmentProof<S>, } +impl<C, S> Sender<C, S> +where + C: IdentityConfiguration, + S: VerifiedSet<Item = Utxo<C>>, +{ + /// Builds a new [`Sender`] for this `asset` from an `identity`. + #[inline] + pub fn from_identity( + identity: Identity<C>, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + utxo_set: &S, + ) -> Result<Self, S::ContainmentError> + where + Standard: Distribution<AssetParameters<C>>, + PublicKey<C>: ConcatBytes, + VoidNumberGenerator<C>: ConcatBytes, + VoidNumberCommitment<C>: ConcatBytes, + { + identity.into_sender(commitment_scheme, asset, utxo_set) + } + + /// Generates a new [`Identity`] from a secret key generation source and builds a new + /// [`Sender`] from it. + #[inline] + pub fn generate( + source: &mut C::SecretKeyGenerator, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + utxo_set: &S, + ) -> Result<Self, SenderError<C, S>> + where + Standard: Distribution<AssetParameters<C>>, + PublicKey<C>: ConcatBytes, + VoidNumberGenerator<C>: ConcatBytes, + VoidNumberCommitment<C>: ConcatBytes, + { + Identity::generate_sender(source, commitment_scheme, asset, utxo_set) + } +} + +impl<C, S, L> IntoPost<L> for Sender<C, S> +where + C: IdentityConfiguration, + S: VerifiedSet<Item = Utxo<C>>, + L: Ledger<VoidNumber = VoidNumber<C>, UtxoSet = S> + ?Sized, +{ + type IntoPost = SenderPost<C, S>; + + #[inline] + fn into_post(self) -> Self::IntoPost { + Self::IntoPost { + void_number: self.void_number, + utxo_containment_proof_public_input: self.utxo_containment_proof.public_input, + } + } +} + +/// Sender Post +pub struct SenderPost<C, S> +where + C: IdentityConfiguration, + S: VerifiedSet<Item = Utxo<C>>, +{ + /// Void Number + pub void_number: VoidNumber<C>, + + /// UTXO Containment Proof Public Input + pub utxo_containment_proof_public_input: S::Public, +} + +impl<C, S, L> Post<L> for SenderPost<C, S> +where + C: IdentityConfiguration, + S: VerifiedSet<Item = Utxo<C>>, + L: Ledger<VoidNumber = VoidNumber<C>, UtxoSet = S> + ?Sized, +{ + #[inline] + fn post(self, ledger: &mut L) -> Result<(), ledger::Error<L>> { + ledger::try_post_void_number(ledger, self.void_number)?; + ledger::check_utxo_containment_proof_public_input( + ledger, + self.utxo_containment_proof_public_input, + )?; + Ok(()) + } +} + /// Receiver pub struct Receiver<C, I> where @@ -637,37 +963,67 @@ where pub encrypted_asset: EncryptedMessage<I>, } -/// Generates a [`VoidNumberCommitment`] from a given `public_key`, `void_number_generator`, and -/// `void_number_commitment_randomness`. -#[inline] -pub fn generate_void_number_commitment<C>( - commitment_scheme: &C::CommitmentScheme, - public_key: &PublicKey<C>, - void_number_generator: &VoidNumberGenerator<C>, - void_number_commitment_randomness: &VoidNumberCommitmentRandomness<C>, -) -> VoidNumberCommitment<C> +impl<C, I> Receiver<C, I> where C: IdentityConfiguration, - PublicKey<C>: ConcatBytes, - VoidNumberGenerator<C>: ConcatBytes, + I: IntegratedEncryptionScheme<Plaintext = Asset>, { - commitment_scheme.commit( - concatenate!(public_key, void_number_generator), - void_number_commitment_randomness, - ) + /// Build a [`Receiver`] from a [`ShieldedIdentity`] for the `asset`. + #[inline] + pub fn from_shielded<R>( + identity: ShieldedIdentity<C, I>, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + rng: &mut R, + ) -> Result<Self, I::Error> + where + R: CryptoRng + RngCore + ?Sized, + VoidNumberCommitment<C>: ConcatBytes, + { + identity.into_receiver(commitment_scheme, asset, rng) + } } -/// Generates a [`Utxo`] from a given `asset`, `void_number_commitment`, and `utxo_randomness`. -#[inline] -pub fn generate_utxo<C>( - commitment_scheme: &C::CommitmentScheme, - asset: &Asset, - void_number_commitment: &VoidNumberCommitment<C>, - utxo_randomness: &UtxoRandomness<C>, -) -> Utxo<C> +impl<C, I, L> IntoPost<L> for ReceiverPost<C, I> where C: IdentityConfiguration, - VoidNumberCommitment<C>: ConcatBytes, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + L: Ledger<Utxo = Utxo<C>, EncryptedAsset = EncryptedMessage<I>> + ?Sized, { - commitment_scheme.commit(concatenate!(asset, void_number_commitment), utxo_randomness) + type IntoPost = ReceiverPost<C, I>; + + #[inline] + fn into_post(self) -> Self::IntoPost { + Self::IntoPost { + utxo: self.utxo, + encrypted_asset: self.encrypted_asset, + } + } +} + +/// Receiver Post +pub struct ReceiverPost<C, I> +where + C: IdentityConfiguration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, +{ + /// Unspent Transaction Output + pub utxo: Utxo<C>, + + /// Encrypted [`Asset`] + pub encrypted_asset: EncryptedMessage<I>, +} + +impl<C, I, L> Post<L> for ReceiverPost<C, I> +where + C: IdentityConfiguration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + L: Ledger<Utxo = Utxo<C>, EncryptedAsset = EncryptedMessage<I>> + ?Sized, +{ + #[inline] + fn post(self, ledger: &mut L) -> Result<(), ledger::Error<L>> { + ledger::try_post_utxo(ledger, self.utxo)?; + ledger::try_post_encrypted_asset(ledger, self.encrypted_asset)?; + Ok(()) + } } diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index cdf20aac1..93c92205f 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -17,6 +17,7 @@ //! Assets // TODO: add macro to build `AssetId` and `AssetBalance` +// TODO: implement all `rand` sampling traits use alloc::vec::Vec; use core::{ @@ -167,6 +168,24 @@ impl AssetBalance { pub const fn from_bytes(bytes: [u8; Self::SIZE]) -> Self { Self(AssetBalanceType::from_le_bytes(bytes)) } + + /// Checked integer addition. Computes `self + rhs`, returning `None` if overflow occurred. + #[inline] + pub const fn checked_add(self, rhs: Self) -> Option<Self> { + match self.0.checked_add(rhs.0) { + Some(result) => Some(Self(result)), + _ => None, + } + } + + /// Checked integer subtraction. Computes `self - rhs`, returning `None` if overflow occurred. + #[inline] + pub const fn checked_sub(self, rhs: Self) -> Option<Self> { + match self.0.checked_sub(rhs.0) { + Some(result) => Some(Self(result)), + _ => None, + } + } } impl Distribution<AssetBalance> for Standard { @@ -192,6 +211,19 @@ impl Mul<AssetBalance> for AssetBalanceType { } } +/// [`AssetBalance`] Array Type +pub type AssetBalances<const N: usize> = [AssetBalance; N]; + +#[inline] +pub(crate) fn sample_asset_balances<R, const N: usize>(rng: &mut R) -> AssetBalances<N> +where + R: RngCore + ?Sized, +{ + // FIXME: We have to use this implementation because of a bug in `rand`. + // See `https://github.com/rust-random/rand/pull/1173`. + try_into_array_unchecked(rng.sample_iter(Standard).take(N).collect::<Vec<_>>()) +} + /// Asset #[derive( Clone, Copy, Debug, Default, Display, Eq, From, Hash, PartialEq, ScaleDecode, ScaleEncode, diff --git a/manta-accounting/src/ledger.rs b/manta-accounting/src/ledger.rs new file mode 100644 index 000000000..2850f3d66 --- /dev/null +++ b/manta-accounting/src/ledger.rs @@ -0,0 +1,191 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Ledger Abstraction + +use manta_crypto::{Set, VerifiedSet}; + +pub(crate) mod prelude { + #[doc(inline)] + pub use crate::ledger::Ledger; +} + +/// Ledger Error +pub enum Error<L> +where + L: Ledger + ?Sized, +{ + /// Asset has already been spent + AssetSpent( + /// Void Number + L::VoidNumber, + ), + /// Asset has already been registered + AssetRegistered( + /// Unspent Transaction Output + L::Utxo, + ), + /// Encrypted Asset has already been stored + EncryptedAssetStored( + /// Encrypted [`Asset`](crate::asset::Asset) + L::EncryptedAsset, + ), + /// Utxo [`ContainmentProof`](manta_crypto::set::ContainmentProof) has an invalid public input + InvalidUtxoState( + /// UTXO Containment Proof Public Input + <L::UtxoSet as VerifiedSet>::Public, + ), +} + +/// Post Trait +pub trait Post<L> +where + L: Ledger + ?Sized, +{ + /// Posts an update to the ledger or returns an error if the update could not be + /// completed successfully. + fn post(self, ledger: &mut L) -> Result<(), Error<L>>; +} + +/// Into Post Trait +pub trait IntoPost<L> +where + L: Ledger + ?Sized, +{ + /// Post Data + type IntoPost: Post<L>; + + /// Converts from `self` into its ledger [`Post`] data. + fn into_post(self) -> Self::IntoPost; +} + +/// Ledger Trait +pub trait Ledger { + /// Void Number Type + type VoidNumber; + + /// Void Number Set Type + type VoidNumberSet: Set<Item = Self::VoidNumber>; + + /// Unspent Transaction Output Type + type Utxo; + + /// UTXO Set Type + type UtxoSet: VerifiedSet<Item = Self::Utxo>; + + /// Encrypted Asset Type + type EncryptedAsset; + + /// Encrypted Asset Set Type + type EncryptedAssetSet: Set<Item = Self::EncryptedAsset>; + + /// Returns a shared reference to the [`VoidNumberSet`](Self::VoidNumberSet). + fn void_numbers(&self) -> &Self::VoidNumberSet; + + /// Returns a mutable reference to the [`VoidNumberSet`](Self::VoidNumberSet). + fn void_numbers_mut(&mut self) -> &mut Self::VoidNumberSet; + + /// Returns a shared reference to the [`UtxoSet`](Self::UtxoSet). + fn utxos(&self) -> &Self::UtxoSet; + + /// Returns a mutable reference to the [`UtxoSet`](Self::UtxoSet). + fn utxos_mut(&mut self) -> &mut Self::UtxoSet; + + /// Returns a shared reference to the [`EncryptedAssetSet`](Self::EncryptedAssetSet). + fn encrypted_assets(&self) -> &Self::EncryptedAssetSet; + + /// Returns a mutable reference to the [`EncryptedAssetSet`](Self::EncryptedAssetSet). + fn encrypted_assets_mut(&mut self) -> &mut Self::EncryptedAssetSet; +} + +/// Returns `true` if the `void_number` corresponding to some asset +/// __is not stored__ on the `ledger`. +#[inline] +pub fn is_unspent<L>(ledger: &L, void_number: &L::VoidNumber) -> bool +where + L: Ledger + ?Sized, +{ + !ledger.void_numbers().contains(void_number) +} + +/// Returns `true` if an asset's `utxo` __is stored__ on the `ledger` and that +/// its `void_number` __is not stored__ on the `ledger`. +#[inline] +pub fn is_spendable<L>(ledger: &L, void_number: &L::VoidNumber, utxo: &L::Utxo) -> bool +where + L: Ledger + ?Sized, +{ + is_unspent(ledger, void_number) && ledger.utxos().contains(utxo) +} + +/// Tries to post the `void_number` to the `ledger` returning [`Error::AssetSpent`] if the +/// `void_number` was already stored on the `ledger`. +#[inline] +pub fn try_post_void_number<L>(ledger: &mut L, void_number: L::VoidNumber) -> Result<(), Error<L>> +where + L: Ledger + ?Sized, +{ + ledger + .void_numbers_mut() + .try_insert(void_number) + .map_err(Error::AssetSpent) +} + +/// Tries to post the `utxo` to the `ledger` returning [`Error::AssetRegistered`] if the +/// `utxo` was already stored on the `ledger`. +#[inline] +pub fn try_post_utxo<L>(ledger: &mut L, utxo: L::Utxo) -> Result<(), Error<L>> +where + L: Ledger + ?Sized, +{ + ledger + .utxos_mut() + .try_insert(utxo) + .map_err(Error::AssetRegistered) +} + +/// Tries to post the `encrypted_asset` to the `ledger` returning [`Error::EncryptedAssetStored`] +/// if the `encrypted_asset` was already stored on the `ledger`. +#[inline] +pub fn try_post_encrypted_asset<L>( + ledger: &mut L, + encrypted_asset: L::EncryptedAsset, +) -> Result<(), Error<L>> +where + L: Ledger + ?Sized, +{ + ledger + .encrypted_assets_mut() + .try_insert(encrypted_asset) + .map_err(Error::EncryptedAssetStored) +} + +/// Checks if the `public_input` corresponding to a UTXO containment proof represents the current +/// state of the [`UtxoSet`](Ledger::UtxoSet), returning [`Error::InvalidUtxoState`] if not. +#[inline] +pub fn check_utxo_containment_proof_public_input<L>( + ledger: &mut L, + public_input: <L::UtxoSet as VerifiedSet>::Public, +) -> Result<(), Error<L>> +where + L: Ledger + ?Sized, +{ + if ledger.utxos().check_public_input(&public_input) { + Ok(()) + } else { + Err(Error::InvalidUtxoState(public_input)) + } +} diff --git a/manta-accounting/src/lib.rs b/manta-accounting/src/lib.rs index afc194be9..cfe2e36e5 100644 --- a/manta-accounting/src/lib.rs +++ b/manta-accounting/src/lib.rs @@ -22,10 +22,15 @@ extern crate alloc; extern crate derive_more; -mod account; mod asset; mod transfer; +mod wallet; -pub use account::*; +pub mod account; +pub mod ledger; + +pub use account::prelude::*; pub use asset::*; +pub use ledger::prelude::*; pub use transfer::*; +pub use wallet::*; diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index aa3d054f1..d5a84b4f9 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -18,19 +18,14 @@ use crate::{ account::{IdentityConfiguration, Receiver, Sender, Utxo}, - asset::{Asset, AssetBalance, AssetId}, + asset::{sample_asset_balances, Asset, AssetBalances, AssetId}, }; use manta_codec::{ScaleDecode, ScaleEncode}; use manta_crypto::{IntegratedEncryptionScheme, VerifiedSet}; - -/// Transfer Configuration Trait -pub trait TransferConfiguration: IdentityConfiguration { - /// Integrated Encryption Scheme for [`Asset`] - type IntegratedEncryptionScheme: IntegratedEncryptionScheme<Plaintext = Asset>; - - /// Verified Set for [`Utxo`] - type UtxoSet: VerifiedSet<Item = Utxo<Self>>; -} +use rand::{ + distributions::{Distribution, Standard}, + Rng, RngCore, +}; /// Public Transfer Protocol #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, ScaleDecode, ScaleEncode)] @@ -39,16 +34,53 @@ pub struct PublicTransfer<const SOURCES: usize, const SINKS: usize> { pub asset_id: AssetId, /// Public Asset Sources - pub sources: [AssetBalance; SOURCES], + pub sources: AssetBalances<SOURCES>, /// Public Asset Sinks - pub sinks: [AssetBalance; SINKS], + pub sinks: AssetBalances<SINKS>, +} + +impl<const SOURCES: usize, const SINKS: usize> PublicTransfer<SOURCES, SINKS> { + /// Builds a new [`PublicTransfer`]. + pub const fn new( + asset_id: AssetId, + sources: AssetBalances<SOURCES>, + sinks: AssetBalances<SINKS>, + ) -> Self { + Self { + asset_id, + sources, + sinks, + } + } +} + +impl<const SOURCES: usize, const SINKS: usize> Distribution<PublicTransfer<SOURCES, SINKS>> + for Standard +{ + #[inline] + fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> PublicTransfer<SOURCES, SINKS> { + PublicTransfer::new( + rng.gen(), + sample_asset_balances(rng), + sample_asset_balances(rng), + ) + } +} + +/// Secret Transfer Configuration Trait +pub trait SecretTransferConfiguration: IdentityConfiguration { + /// Integrated Encryption Scheme for [`Asset`] + type IntegratedEncryptionScheme: IntegratedEncryptionScheme<Plaintext = Asset>; + + /// Verified Set for [`Utxo`] + type UtxoSet: VerifiedSet<Item = Utxo<Self>>; } /// Secret Transfer Protocol -pub struct SecretTrasfer<T, const SENDERS: usize, const RECEIVERS: usize> +pub struct SecretTransfer<T, const SENDERS: usize, const RECEIVERS: usize> where - T: TransferConfiguration, + T: SecretTransferConfiguration, { /// Secret Senders pub senders: [Sender<T, T::UtxoSet>; SENDERS], @@ -57,6 +89,19 @@ where pub receivers: [Receiver<T, T::IntegratedEncryptionScheme>; RECEIVERS], } +impl<T, const SENDERS: usize, const RECEIVERS: usize> SecretTransfer<T, SENDERS, RECEIVERS> +where + T: SecretTransferConfiguration, +{ + /// Builds a new [`SecretTransfer`]. + pub fn new( + senders: [Sender<T, T::UtxoSet>; SENDERS], + receivers: [Receiver<T, T::IntegratedEncryptionScheme>; RECEIVERS], + ) -> Self { + Self { senders, receivers } + } +} + /// Transfer Protocol pub struct Transfer< T, @@ -65,11 +110,11 @@ pub struct Transfer< const RECEIVERS: usize, const SINKS: usize, > where - T: TransferConfiguration, + T: SecretTransferConfiguration, { /// Public Transfer pub public: PublicTransfer<SOURCES, SINKS>, - /// Secret Trasfer - pub secret: SecretTrasfer<T, SENDERS, RECEIVERS>, + /// Secret Transfer + pub secret: SecretTransfer<T, SENDERS, RECEIVERS>, } diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs new file mode 100644 index 000000000..19bc59131 --- /dev/null +++ b/manta-accounting/src/wallet.rs @@ -0,0 +1,167 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Wallet Abstractions + +use crate::{ + account::ShieldedIdentity, + asset::{Asset, AssetBalance, AssetId}, + ledger::Ledger, + transfer::{SecretTransfer, SecretTransferConfiguration}, +}; +use core::convert::Infallible; + +/// Asset Map +pub trait AssetMap { + /// Returns the current balance associated with this `id`. + fn balance(&self, id: AssetId) -> AssetBalance; + + /// Sets the asset balance for `id` to `value`. + fn set_balance(&mut self, id: AssetId, value: AssetBalance); + + /// Sets the asset balance for `asset.id` to `asset.value`. + #[inline] + fn set_asset(&mut self, asset: Asset) { + self.set_balance(asset.id, asset.value) + } + + /// Mutates the asset balance for `id` to the result of `f` if it succeeds. + #[inline] + #[must_use = "this only modifies the stored value if the function call succeeded"] + fn mutate<F, E>(&mut self, id: AssetId, f: F) -> Result<AssetBalance, E> + where + F: FnOnce(AssetBalance) -> Result<AssetBalance, E>, + { + // TODO: use `try_trait_v2` when it comes out + f(self.balance(id)).map(move |v| { + self.set_balance(id, v); + v + }) + } + + /// Performs a deposit of value `asset.value` into the balance of `asset.id`, + /// returning the new balance for this `asset.id` if it did not overflow. + /// + /// To skip the overflow check, use [`deposit_unchecked`](Self::deposit_unchecked) instead. + #[inline] + #[must_use = "this only modifies the stored value if the addition did not overflow"] + fn deposit(&mut self, asset: Asset) -> Option<AssetBalance> { + self.mutate(asset.id, move |v| v.checked_add(asset.value).ok_or(())) + .ok() + } + + /// Performs a deposit of value `asset.value` into the balance of `asset.id`, + /// without checking for overflow, returning the new balance for this `asset.id`. + /// + /// # Panics + /// + /// This function panics on overflow. To explicitly check for overflow, use + /// [`deposit`](Self::deposit) instead. + #[inline] + fn deposit_unchecked(&mut self, asset: Asset) -> AssetBalance { + self.mutate::<_, Infallible>(asset.id, move |v| Ok(v + asset.value)) + .unwrap() + } + + /// Performs a withdrawl of value `asset.value` from the balance of `asset.id`, + /// returning the new balance for this `asset.id` if it did not overflow. + /// + /// To skip the overflow check, use [`withdraw_unchecked`](Self::withdraw_unchecked) instead. + #[inline] + #[must_use = "this only modifies the stored value if the subtraction did not overflow"] + fn withdraw(&mut self, asset: Asset) -> Option<AssetBalance> { + self.mutate(asset.id, move |v| v.checked_sub(asset.value).ok_or(())) + .ok() + } + + /// Performs a withdrawl of value `asset.value` from the balance of `asset.id`, + /// without checking for overflow, returning the new balance for this `asset.id`. + /// + /// # Panics + /// + /// This function panics on overflow. To explicitly check for overflow, use + /// [`withdraw`](Self::withdraw) instead. + #[inline] + fn withdraw_unchecked(&mut self, asset: Asset) -> AssetBalance { + self.mutate::<_, Infallible>(asset.id, move |v| Ok(v - asset.value)) + .unwrap() + } +} + +/// Wallet +pub struct Wallet<T, M> +where + T: SecretTransferConfiguration, + M: AssetMap, +{ + /// Secret Key Source + secret_key_source: T::SecretKeyGenerator, + + /// Public Asset Map + public_assets: M, + + /// Secret Asset Map + secret_assets: M, +} + +impl<T, M> Wallet<T, M> +where + T: SecretTransferConfiguration, + M: AssetMap, +{ + /// Builds a new [`Wallet`] from a `secret_key_source`. + #[inline] + pub fn new(secret_key_source: T::SecretKeyGenerator) -> Self + where + M: Default, + { + Self::with_balances(secret_key_source, Default::default(), Default::default()) + } + + /// Builds a new [`Wallet`] from a `secret_key_source` and pre-built + /// `public_assets` map and `secret_assets` map. + #[inline] + pub fn with_balances( + secret_key_source: T::SecretKeyGenerator, + public_assets: M, + secret_assets: M, + ) -> Self { + Self { + secret_key_source, + public_assets, + secret_assets, + } + } + + /// Updates `self` with new information from the ledger. + pub fn update<L>(&mut self, ledger: &L) + where + L: Ledger, + { + let _ = ledger; + todo!() + } + + /// Builds a [`SecretTransfer`] transaction to send `asset` to `receiver`. + pub fn send( + &self, + asset: Asset, + receiver: ShieldedIdentity<T, T::IntegratedEncryptionScheme>, + ) -> SecretTransfer<T, 2, 2> { + let _ = (asset, receiver); + todo!() + } +} diff --git a/manta-crypto/src/ies.rs b/manta-crypto/src/ies.rs index f0a356623..66d632285 100644 --- a/manta-crypto/src/ies.rs +++ b/manta-crypto/src/ies.rs @@ -126,14 +126,16 @@ where I::keygen(rng) } - /// Splits the key pair into an [`I::PublicKey`](IntegratedEncryptionScheme::PublicKey) - /// and a [`SecretKey`]. + /// Returns the public side of the key pair. #[inline] - pub fn split(self) -> (PublicKey<I>, SecretKey<I>) { - ( - PublicKey::new(self.public_key), - SecretKey::new(self.secret_key), - ) + pub fn into_public(self) -> PublicKey<I> { + PublicKey::new(self.public_key) + } + + /// Returns the secret side of the key pair. + #[inline] + pub fn into_secret(self) -> SecretKey<I> { + SecretKey::new(self.secret_key) } /// Encrypts the `plaintext` with `self, generating an [`EncryptedMessage`]. @@ -146,11 +148,24 @@ where where R: CryptoRng + RngCore + ?Sized, { - let (public_key, secret_key) = self.split(); + let (public_key, secret_key) = self.into(); Ok((public_key.encrypt(plaintext, rng)?, secret_key)) } } +impl<I> From<KeyPair<I>> for (PublicKey<I>, SecretKey<I>) +where + I: IntegratedEncryptionScheme + ?Sized, +{ + #[inline] + fn from(keypair: KeyPair<I>) -> Self { + ( + PublicKey::new(keypair.public_key), + SecretKey::new(keypair.secret_key), + ) + } +} + /// Integrated Encryption Scheme Trait pub trait IntegratedEncryptionScheme { /// Public Key Type diff --git a/manta-crypto/src/set.rs b/manta-crypto/src/set.rs index 20b64118a..8c10d17fc 100644 --- a/manta-crypto/src/set.rs +++ b/manta-crypto/src/set.rs @@ -33,8 +33,8 @@ pub trait Set { /// contained in `self`. fn try_insert(&mut self, item: Self::Item) -> Result<(), Self::Item>; - /// Inserts the `item` into `self`, returning `true` if the item was already contained in - /// `self`. + /// Inserts the `item` into `self`, returning `true` if the `item` was not contained and + /// `false` if the item was already contained in `self`. #[inline] fn insert(&mut self, item: Self::Item) -> bool { self.try_insert(item).is_err() @@ -48,7 +48,7 @@ where Item: ?Sized, { /// Verifies that `self` is a proof that `item` is contained in some [`VerifiedSet`]. - fn verify(&self, public: &Public, item: &Item) -> bool; + fn verify(&self, public_input: &Public, item: &Item) -> bool; } /// Containment Proof for a [`VerifiedSet`] @@ -57,88 +57,55 @@ where S: VerifiedSet + ?Sized, { /// Public Input - pub input: S::Public, + pub public_input: S::Public, /// Secret Witness - witness: S::Secret, + secret_witness: S::Secret, } impl<S> ContainmentProof<S> where S: VerifiedSet + ?Sized, { - /// Builds a new [`ContainmentProof`] from public `input` and secret `witness`. + /// Builds a new [`ContainmentProof`] from `public_input` and `secret_witness`. #[inline] - pub fn new(input: S::Public, witness: S::Secret) -> Self { - Self { input, witness } + pub fn new(public_input: S::Public, secret_witness: S::Secret) -> Self { + Self { + public_input, + secret_witness, + } } /// Verifies that the `item` is contained in some [`VerifiedSet`]. #[inline] pub fn verify(&self, item: &S::Item) -> bool { - self.witness.verify(&self.input, item) + self.secret_witness.verify(&self.public_input, item) + } + + /// Returns `true` if `self.public_input` is a valid input for the current state of `set`. + #[inline] + pub fn check_public_input(&self, set: &S) -> bool { + set.check_public_input(&self.public_input) } } /// Verified Set Trait -pub trait VerifiedSet { - /// Item Stored in the [`VerifiedSet`] - type Item; - - /// Public Input for [`Item`](Self::Item) Containment +pub trait VerifiedSet: Set { + /// Public Input for [`Item`](Set::Item) Containment type Public; - /// Secret Witness for [`Item`](Self::Item) Containment + /// Secret Witness for [`Item`](Set::Item) Containment type Secret: VerifyContainment<Self::Public, Self::Item>; /// Error Generating a [`ContainmentProof`] type ContainmentError; - /// Returns `true` if `public` is a valid input for the current state of `self`. - fn check_public_input(&self, public: &Self::Public) -> bool; + /// Returns `true` if `public_input` is a valid input for the current state of `self`. + fn check_public_input(&self, public_input: &Self::Public) -> bool; /// Generates a proof that the given `item` is stored in `self`. fn get_containment_proof( &self, item: &Self::Item, ) -> Result<ContainmentProof<Self>, Self::ContainmentError>; - - /// Returns `true` if there exists a proof that `item` is stored in `self`. - #[inline] - fn contains(&self, item: &Self::Item) -> bool { - self.get_containment_proof(item).is_ok() - } - - /// Tries to insert the `item` into `self`, returning the item back if it was already - /// contained in `self`. - fn try_insert(&mut self, item: Self::Item) -> Result<(), Self::Item>; - - /// Inserts the `item` into `self`, returning `true` if the item was already contained in - /// `self`. - #[inline] - fn insert(&mut self, item: Self::Item) -> bool { - self.try_insert(item).is_err() - } -} - -impl<S> Set for S -where - S: VerifiedSet + ?Sized, -{ - type Item = S::Item; - - #[inline] - fn contains(&self, item: &Self::Item) -> bool { - self.contains(item) - } - - #[inline] - fn try_insert(&mut self, item: Self::Item) -> Result<(), Self::Item> { - self.try_insert(item) - } - - #[inline] - fn insert(&mut self, item: Self::Item) -> bool { - self.insert(item) - } } diff --git a/manta-pay/src/ies.rs b/manta-pay/src/ies.rs index 652729ada..5251ed0ef 100644 --- a/manta-pay/src/ies.rs +++ b/manta-pay/src/ies.rs @@ -38,36 +38,40 @@ pub type PublicKey = [u8; 32]; /// Secret Key Type pub type SecretKey = [u8; 32]; -/// Ciphertext Type +/// Asset Ciphertext Type // FIXME: this should be automatically calculated from [`Asset`] // FIXME: is this calculation correct? how do we know? -pub type Ciphertext = [u8; Asset::SIZE + 16]; +pub type AssetCiphertext = [u8; Asset::SIZE + 16]; /// Ephemeral Public Key Type pub type EphemeralPublicKey = PublicKey; -/// Augmented Ciphertext -pub struct AugmentedCiphertext { - /// Base Ciphertext - pub ciphertext: Ciphertext, +/// Augmented Asset Ciphertext +pub struct AugmentedAssetCiphertext { + /// Asset Ciphertext + pub asset_ciphertext: AssetCiphertext, /// Ephemeral Public Key pub ephemeral_public_key: EphemeralPublicKey, } -impl AugmentedCiphertext { - /// Builds a new [`AugmentedCiphertext`] from `ciphertext` and `ephemeral_public_key`. +impl AugmentedAssetCiphertext { + /// Builds a new [`AugmentedAssetCiphertext`] from `asset_ciphertext` + /// and `ephemeral_public_key`. #[inline] - pub const fn new(ciphertext: Ciphertext, ephemeral_public_key: EphemeralPublicKey) -> Self { + pub const fn new( + asset_ciphertext: AssetCiphertext, + ephemeral_public_key: EphemeralPublicKey, + ) -> Self { Self { - ciphertext, + asset_ciphertext, ephemeral_public_key, } } } /// Encrypted Message for [`IES`] -pub type EncryptedMessage = ies::EncryptedMessage<IES>; +pub type EncryptedAsset = ies::EncryptedMessage<IES>; /// Implementation of [`IntegratedEncryptionScheme`] #[derive( @@ -97,7 +101,7 @@ impl IntegratedEncryptionScheme for IES { type Plaintext = Asset; - type Ciphertext = AugmentedCiphertext; + type Ciphertext = AugmentedAssetCiphertext; type Error = aes_gcm::Error; @@ -115,7 +119,7 @@ impl IntegratedEncryptionScheme for IES { plaintext: Self::Plaintext, public_key: Self::PublicKey, rng: &mut R, - ) -> Result<EncryptedMessage, Self::Error> + ) -> Result<EncryptedAsset, Self::Error> where R: CryptoRng + RngCore + ?Sized, { @@ -126,13 +130,13 @@ impl IntegratedEncryptionScheme for IES { let aes_key = GenericArray::from_slice(&ss); // SAFETY: Using a deterministic nonce is ok since we never reuse keys. - let ciphertext = Aes256Gcm::new(aes_key).encrypt( + let asset_ciphertext = Aes256Gcm::new(aes_key).encrypt( Nonce::from_slice(Self::NONCE), plaintext.into_bytes().as_ref(), )?; - Ok(EncryptedMessage::new(AugmentedCiphertext::new( - try_into_array_unchecked(ciphertext), + Ok(EncryptedAsset::new(AugmentedAssetCiphertext::new( + try_into_array_unchecked(asset_ciphertext), ephemeral_public_key.to_bytes(), ))) } @@ -149,7 +153,7 @@ impl IntegratedEncryptionScheme for IES { // SAFETY: Using a deterministic nonce is ok since we never reuse keys. let plaintext = Aes256Gcm::new(aes_key).decrypt( Nonce::from_slice(Self::NONCE), - ciphertext.ciphertext.as_ref(), + ciphertext.asset_ciphertext.as_ref(), )?; Ok(Asset::from_bytes(try_into_array_unchecked( diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index 5259df74e..ac70ee000 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -25,6 +25,7 @@ use alloc::vec::Vec; use core::convert::TryInto; /// Implements [`From`]`<$from>` for an enum `$to`, choosing the `$kind` variant. +// TODO: add `where` clauses #[macro_export] macro_rules! from_variant_impl { ($to:tt, $kind:ident, $from:tt) => { From f3b4c62de615d8f25b746f0df5517741ac79ad66 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 7 Sep 2021 03:27:51 -0400 Subject: [PATCH 010/275] redo some ledger constructions and start building wallet interface --- .editorconfig | 2 - manta-accounting/Cargo.toml | 2 - manta-accounting/src/account.rs | 151 +++++++++++++++-------- manta-accounting/src/asset.rs | 25 ++-- manta-accounting/src/ledger.rs | 202 +++++++++++-------------------- manta-accounting/src/lib.rs | 10 +- manta-accounting/src/transfer.rs | 170 +++++++++++++++++++++++++- manta-accounting/src/wallet.rs | 36 +++++- manta-crypto/Cargo.toml | 2 - manta-crypto/src/ies.rs | 3 +- manta-pay/Cargo.toml | 8 +- manta-pay/src/ies.rs | 64 +++++++--- manta-util/src/lib.rs | 10 +- 13 files changed, 433 insertions(+), 252 deletions(-) diff --git a/.editorconfig b/.editorconfig index b4aafcadc..135955e95 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,8 +3,6 @@ root = true [*.rs] charset=utf-8 end_of_line=lf -indent_size=tab -indent_style=tab insert_final_newline=true max_line_length=100 tab_width=4 diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 5abb900ac..6daff3c3e 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -25,8 +25,6 @@ maintenance = { status = "actively-developed" } [dependencies] derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } derive_more = { version = "0.99.16", default-features = false, features = ["add", "add_assign", "display", "from", "mul", "mul_assign", "sum"] } -manta-codec = { path = "../manta-codec", default-features = false, features = ["derive"] } manta-crypto = { path = "../manta-crypto", default-features = false } manta-util = { path = "../manta-util", default-features = false } -parity-scale-codec = { version = "2.2.0", default-features = false } rand = { version = "0.8.4", default-features = false } diff --git a/manta-accounting/src/account.rs b/manta-accounting/src/account.rs index 72b347b1d..c1c133243 100644 --- a/manta-accounting/src/account.rs +++ b/manta-accounting/src/account.rs @@ -19,11 +19,10 @@ // FIXME: ensure secret keys cannot be made public by some API call use crate::{ - asset::Asset, - ledger::{self, IntoPost, Ledger, Post}, + asset::{Asset, AssetBalance, AssetId}, + ledger::Ledger, }; use core::{convert::Infallible, fmt::Debug, hash::Hash, marker::PhantomData}; -use manta_codec::{ScaleDecode, ScaleEncode}; use manta_crypto::{ concatenate, ies::{self, EncryptedMessage}, @@ -39,8 +38,8 @@ use rand::{ pub(crate) mod prelude { #[doc(inline)] pub use crate::account::{ - Identity, IdentityConfiguration, Receiver, SecretKeyGeneratorError, Sender, SenderError, - ShieldedIdentity, Spend, SpendError, Utxo, VoidNumber, + Identity, IdentityConfiguration, Receiver, SecretKeyGenerator, SecretKeyGeneratorError, + Sender, SenderError, ShieldedIdentity, Spend, SpendError, Utxo, VoidNumber, }; } @@ -153,7 +152,7 @@ where } /// Public Parameters for using an [`Asset`] -#[derive(derivative::Derivative, ScaleDecode, ScaleEncode)] +#[derive(derivative::Derivative)] #[derivative( Clone( bound = "VoidNumberGenerator<C>: Clone, VoidNumberCommitmentRandomness<C>: Clone, UtxoRandomness<C>: Copy" @@ -368,7 +367,7 @@ where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { - let (_, parameters, asset_keypair) = self.rng_and_parameters_and_asset_keypair::<I>(); + let (_, parameters, asset_keypair) = self.rng_and_parameters_and_asset_keypair(); (parameters, asset_keypair) } @@ -379,7 +378,7 @@ where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { - let (_, asset_keypair) = self.parameters_and_asset_keypair::<I>(); + let (_, asset_keypair) = self.parameters_and_asset_keypair(); asset_keypair } @@ -450,14 +449,13 @@ where { let parameters = self.parameters(); let utxo = self.utxo(commitment_scheme, &asset, &parameters); - let utxo_containment_proof = utxo_set.get_containment_proof(&utxo)?; Ok(Sender { - asset, + utxo_containment_proof: utxo_set.get_containment_proof(&utxo)?, void_number: self.void_number(&parameters.void_number_generator), + identity: self, + asset, parameters, utxo, - utxo_containment_proof, - identity: self, }) } @@ -514,7 +512,7 @@ where PublicKey<C>: ConcatBytes, VoidNumberGenerator<C>: ConcatBytes, { - let (parameters, asset_keypair) = self.parameters_and_asset_keypair::<I>(); + let (parameters, asset_keypair) = self.parameters_and_asset_keypair(); self.build_shielded_identity(commitment_scheme, parameters, asset_keypair.into_public()) } @@ -542,7 +540,7 @@ where Standard: Distribution<AssetParameters<C>>, { Spend { - asset_secret_key: self.asset_keypair::<I>().into_secret(), + asset_secret_key: self.asset_keypair().into_secret(), identity: self, } } @@ -572,7 +570,7 @@ where PublicKey<C>: ConcatBytes, VoidNumberGenerator<C>: ConcatBytes, { - let (parameters, asset_keypair) = self.parameters_and_asset_keypair::<I>(); + let (parameters, asset_keypair) = self.parameters_and_asset_keypair(); let (asset_public_key, asset_secret_key) = asset_keypair.into(); ( self.build_shielded_identity(commitment_scheme, parameters, asset_public_key), @@ -892,23 +890,44 @@ where { Identity::generate_sender(source, commitment_scheme, asset, utxo_set) } -} - -impl<C, S, L> IntoPost<L> for Sender<C, S> -where - C: IdentityConfiguration, - S: VerifiedSet<Item = Utxo<C>>, - L: Ledger<VoidNumber = VoidNumber<C>, UtxoSet = S> + ?Sized, -{ - type IntoPost = SenderPost<C, S>; + /// Extracts ledger posting data for this sender. #[inline] - fn into_post(self) -> Self::IntoPost { - Self::IntoPost { + pub fn into_post(self) -> SenderPost<C, S> { + SenderPost { void_number: self.void_number, utxo_containment_proof_public_input: self.utxo_containment_proof.public_input, } } + + /// Returns the asset id for this sender. + #[inline] + pub fn asset_id(&self) -> AssetId { + self.asset.id + } + + /// Returns the asset value for this sender. + #[inline] + pub fn asset_value(&self) -> AssetBalance { + self.asset.value + } +} + +/// Sender Post Error +pub enum SenderPostError<L> +where + L: Ledger + ?Sized, +{ + /// Asset has already been spent + AssetSpent( + /// Void Number + L::VoidNumber, + ), + /// Utxo [`ContainmentProof`](manta_crypto::set::ContainmentProof) has an invalid public input + InvalidUtxoState( + /// UTXO Containment Proof Public Input + <L::UtxoSet as VerifiedSet>::Public, + ), } /// Sender Post @@ -924,19 +943,23 @@ where pub utxo_containment_proof_public_input: S::Public, } -impl<C, S, L> Post<L> for SenderPost<C, S> +impl<C, S> SenderPost<C, S> where C: IdentityConfiguration, S: VerifiedSet<Item = Utxo<C>>, - L: Ledger<VoidNumber = VoidNumber<C>, UtxoSet = S> + ?Sized, { + /// Posts the [`SenderPost`] data to the `ledger`. #[inline] - fn post(self, ledger: &mut L) -> Result<(), ledger::Error<L>> { - ledger::try_post_void_number(ledger, self.void_number)?; - ledger::check_utxo_containment_proof_public_input( - ledger, - self.utxo_containment_proof_public_input, - )?; + pub fn post<L>(self, ledger: &mut L) -> Result<(), SenderPostError<L>> + where + L: Ledger<VoidNumber = VoidNumber<C>, UtxoSet = S> + ?Sized, + { + ledger + .try_post_void_number(self.void_number) + .map_err(SenderPostError::AssetSpent)?; + ledger + .check_utxo_containment_proof_public_input(self.utxo_containment_proof_public_input) + .map_err(SenderPostError::InvalidUtxoState)?; Ok(()) } } @@ -982,23 +1005,44 @@ where { identity.into_receiver(commitment_scheme, asset, rng) } -} - -impl<C, I, L> IntoPost<L> for ReceiverPost<C, I> -where - C: IdentityConfiguration, - I: IntegratedEncryptionScheme<Plaintext = Asset>, - L: Ledger<Utxo = Utxo<C>, EncryptedAsset = EncryptedMessage<I>> + ?Sized, -{ - type IntoPost = ReceiverPost<C, I>; + /// Extracts ledger posting data for this receiver. #[inline] - fn into_post(self) -> Self::IntoPost { - Self::IntoPost { + pub fn into_post(self) -> ReceiverPost<C, I> { + ReceiverPost { utxo: self.utxo, encrypted_asset: self.encrypted_asset, } } + + /// Returns the asset id for this receiver. + #[inline] + pub fn asset_id(&self) -> AssetId { + self.asset.id + } + + /// Returns the asset value for this receiver. + #[inline] + pub fn asset_value(&self) -> AssetBalance { + self.asset.value + } +} + +/// Receiver Post Error +pub enum ReceiverPostError<L> +where + L: Ledger + ?Sized, +{ + /// Asset has already been registered + AssetRegistered( + /// Unspent Transaction Output + L::Utxo, + ), + /// Encrypted Asset has already been stored + EncryptedAssetStored( + /// Encrypted [`Asset`](crate::asset::Asset) + L::EncryptedAsset, + ), } /// Receiver Post @@ -1014,16 +1058,23 @@ where pub encrypted_asset: EncryptedMessage<I>, } -impl<C, I, L> Post<L> for ReceiverPost<C, I> +impl<C, I> ReceiverPost<C, I> where C: IdentityConfiguration, I: IntegratedEncryptionScheme<Plaintext = Asset>, - L: Ledger<Utxo = Utxo<C>, EncryptedAsset = EncryptedMessage<I>> + ?Sized, { + /// Posts the [`ReceiverPost`] data to the `ledger`. #[inline] - fn post(self, ledger: &mut L) -> Result<(), ledger::Error<L>> { - ledger::try_post_utxo(ledger, self.utxo)?; - ledger::try_post_encrypted_asset(ledger, self.encrypted_asset)?; + pub fn post<L>(self, ledger: &mut L) -> Result<(), ReceiverPostError<L>> + where + L: Ledger<Utxo = Utxo<C>, EncryptedAsset = EncryptedMessage<I>> + ?Sized, + { + ledger + .try_post_utxo(self.utxo) + .map_err(ReceiverPostError::AssetRegistered)?; + ledger + .try_post_encrypted_asset(self.encrypted_asset) + .map_err(ReceiverPostError::EncryptedAssetStored)?; Ok(()) } } diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 93c92205f..131d33a31 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -29,9 +29,8 @@ use core::{ use derive_more::{ Add, AddAssign, Display, Div, DivAssign, From, Mul, MulAssign, Product, Sub, SubAssign, Sum, }; -use manta_codec::{ScaleDecode, ScaleEncode}; use manta_crypto::{Accumulator, ConcatBytes}; -use manta_util::{array_map, fallible_array_map, try_into_array_unchecked}; +use manta_util::{array_map, fallible_array_map, into_array_unchecked}; use rand::{ distributions::{Distribution, Standard}, Rng, RngCore, @@ -60,8 +59,6 @@ type AssetIdType = u32; PartialEq, PartialOrd, Product, - ScaleDecode, - ScaleEncode, Sub, SubAssign, Sum, @@ -138,8 +135,6 @@ type AssetBalanceType = u128; PartialEq, PartialOrd, Product, - ScaleDecode, - ScaleEncode, Sub, SubAssign, Sum, @@ -221,13 +216,11 @@ where { // FIXME: We have to use this implementation because of a bug in `rand`. // See `https://github.com/rust-random/rand/pull/1173`. - try_into_array_unchecked(rng.sample_iter(Standard).take(N).collect::<Vec<_>>()) + into_array_unchecked(rng.sample_iter(Standard).take(N).collect::<Vec<_>>()) } /// Asset -#[derive( - Clone, Copy, Debug, Default, Display, Eq, From, Hash, PartialEq, ScaleDecode, ScaleEncode, -)] +#[derive(Clone, Copy, Debug, Default, Display, Eq, From, Hash, PartialEq)] #[display(fmt = "{{id: {}, value: {}}}", id, value)] pub struct Asset { /// Asset Id @@ -258,14 +251,14 @@ impl Asset { /// Checks if the `rhs` asset has the same [`AssetId`]. #[inline] - pub const fn same_id(&self, rhs: &Asset) -> bool { + pub const fn same_id(&self, rhs: &Self) -> bool { self.id.0 == rhs.id.0 } /// Converts `self` into a byte array. #[inline] pub fn into_bytes(self) -> [u8; Self::SIZE] { - try_into_array_unchecked(self.as_bytes::<Vec<u8>>()) + into_array_unchecked(self.as_bytes::<Vec<u8>>()) } /// Converts a byte array into `self`. @@ -273,8 +266,8 @@ impl Asset { pub fn from_bytes(bytes: [u8; Self::SIZE]) -> Self { let split = (AssetId::BITS / 8) as usize; Self::new( - AssetId::from_bytes(try_into_array_unchecked(&bytes[..split])), - AssetBalance::from_bytes(try_into_array_unchecked(&bytes[split..])), + AssetId::from_bytes(into_array_unchecked(&bytes[..split])), + AssetBalance::from_bytes(into_array_unchecked(&bytes[split..])), ) } } @@ -358,9 +351,7 @@ impl Distribution<Asset> for Standard { } /// Asset Collection -#[derive( - Clone, Copy, Debug, Eq, From, Hash, Ord, PartialEq, PartialOrd, ScaleDecode, ScaleEncode, -)] +#[derive(Clone, Copy, Debug, Eq, From, Hash, Ord, PartialEq, PartialOrd)] #[from(forward)] pub struct AssetCollection<const N: usize> { /// Asset Id diff --git a/manta-accounting/src/ledger.rs b/manta-accounting/src/ledger.rs index 2850f3d66..ddf0ca711 100644 --- a/manta-accounting/src/ledger.rs +++ b/manta-accounting/src/ledger.rs @@ -16,70 +16,14 @@ //! Ledger Abstraction -use manta_crypto::{Set, VerifiedSet}; - -pub(crate) mod prelude { - #[doc(inline)] - pub use crate::ledger::Ledger; -} - -/// Ledger Error -pub enum Error<L> -where - L: Ledger + ?Sized, -{ - /// Asset has already been spent - AssetSpent( - /// Void Number - L::VoidNumber, - ), - /// Asset has already been registered - AssetRegistered( - /// Unspent Transaction Output - L::Utxo, - ), - /// Encrypted Asset has already been stored - EncryptedAssetStored( - /// Encrypted [`Asset`](crate::asset::Asset) - L::EncryptedAsset, - ), - /// Utxo [`ContainmentProof`](manta_crypto::set::ContainmentProof) has an invalid public input - InvalidUtxoState( - /// UTXO Containment Proof Public Input - <L::UtxoSet as VerifiedSet>::Public, - ), -} - -/// Post Trait -pub trait Post<L> -where - L: Ledger + ?Sized, -{ - /// Posts an update to the ledger or returns an error if the update could not be - /// completed successfully. - fn post(self, ledger: &mut L) -> Result<(), Error<L>>; -} - -/// Into Post Trait -pub trait IntoPost<L> -where - L: Ledger + ?Sized, -{ - /// Post Data - type IntoPost: Post<L>; - - /// Converts from `self` into its ledger [`Post`] data. - fn into_post(self) -> Self::IntoPost; -} +use crate::account::{ReceiverPostError, SenderPostError}; +use manta_crypto::VerifiedSet; /// Ledger Trait pub trait Ledger { /// Void Number Type type VoidNumber; - /// Void Number Set Type - type VoidNumberSet: Set<Item = Self::VoidNumber>; - /// Unspent Transaction Output Type type Utxo; @@ -89,103 +33,95 @@ pub trait Ledger { /// Encrypted Asset Type type EncryptedAsset; - /// Encrypted Asset Set Type - type EncryptedAssetSet: Set<Item = Self::EncryptedAsset>; - - /// Returns a shared reference to the [`VoidNumberSet`](Self::VoidNumberSet). - fn void_numbers(&self) -> &Self::VoidNumberSet; - - /// Returns a mutable reference to the [`VoidNumberSet`](Self::VoidNumberSet). - fn void_numbers_mut(&mut self) -> &mut Self::VoidNumberSet; - /// Returns a shared reference to the [`UtxoSet`](Self::UtxoSet). fn utxos(&self) -> &Self::UtxoSet; - /// Returns a mutable reference to the [`UtxoSet`](Self::UtxoSet). - fn utxos_mut(&mut self) -> &mut Self::UtxoSet; + /* TODO: do we want these methods? - /// Returns a shared reference to the [`EncryptedAssetSet`](Self::EncryptedAssetSet). - fn encrypted_assets(&self) -> &Self::EncryptedAssetSet; + /// Returns `true` if the `void_number` corresponding to some asset + /// __is not stored__ on `self`. + fn is_unspent(&self, void_number: &Self::VoidNumber) -> bool; - /// Returns a mutable reference to the [`EncryptedAssetSet`](Self::EncryptedAssetSet). - fn encrypted_assets_mut(&mut self) -> &mut Self::EncryptedAssetSet; -} + /// Returns `true` if the `utxo` corresponding to some asset + /// __is stored__ on `self`. + #[inline] + fn is_registered(&self, utxo: &Self::Utxo) -> bool { + self.utxos().contains(utxo) + } -/// Returns `true` if the `void_number` corresponding to some asset -/// __is not stored__ on the `ledger`. -#[inline] -pub fn is_unspent<L>(ledger: &L, void_number: &L::VoidNumber) -> bool -where - L: Ledger + ?Sized, -{ - !ledger.void_numbers().contains(void_number) -} + /// Returns `true` if an asset's `utxo` __is stored__ on the `ledger` and that + /// its `void_number` __is not stored__ on `self`. + #[inline] + fn is_spendable(&self, utxo: &Self::Utxo, void_number: &Self::VoidNumber) -> bool { + self.is_registered(utxo) && self.is_unspent(void_number) + } -/// Returns `true` if an asset's `utxo` __is stored__ on the `ledger` and that -/// its `void_number` __is not stored__ on the `ledger`. -#[inline] -pub fn is_spendable<L>(ledger: &L, void_number: &L::VoidNumber, utxo: &L::Utxo) -> bool -where - L: Ledger + ?Sized, -{ - is_unspent(ledger, void_number) && ledger.utxos().contains(utxo) -} + */ + + /// Checks if the `public_input` corresponding to a UTXO containment proof represents the current + /// state of the [`UtxoSet`](Self::UtxoSet), returning it back if not. + #[inline] + fn check_utxo_containment_proof_public_input( + &self, + public_input: <Self::UtxoSet as VerifiedSet>::Public, + ) -> Result<(), <Self::UtxoSet as VerifiedSet>::Public> { + if self.utxos().check_public_input(&public_input) { + Ok(()) + } else { + Err(public_input) + } + } -/// Tries to post the `void_number` to the `ledger` returning [`Error::AssetSpent`] if the -/// `void_number` was already stored on the `ledger`. -#[inline] -pub fn try_post_void_number<L>(ledger: &mut L, void_number: L::VoidNumber) -> Result<(), Error<L>> -where - L: Ledger + ?Sized, -{ - ledger - .void_numbers_mut() - .try_insert(void_number) - .map_err(Error::AssetSpent) + /// Tries to post the `void_number` to `self` returning it back if the + /// `void_number` was already stored on `self`. + fn try_post_void_number( + &mut self, + void_number: Self::VoidNumber, + ) -> Result<(), Self::VoidNumber>; + + /// Tries to post the `utxo` to `self` returning it back if the + /// `utxo` was already stored on `self`. + fn try_post_utxo(&mut self, utxo: Self::Utxo) -> Result<(), Self::Utxo>; + + /// Tries to post the `encrypted_asset` to `self` returning it back + /// if the `encrypted_asset` was already stored on `self`. + fn try_post_encrypted_asset( + &mut self, + encrypted_asset: Self::EncryptedAsset, + ) -> Result<(), Self::EncryptedAsset>; } -/// Tries to post the `utxo` to the `ledger` returning [`Error::AssetRegistered`] if the -/// `utxo` was already stored on the `ledger`. -#[inline] -pub fn try_post_utxo<L>(ledger: &mut L, utxo: L::Utxo) -> Result<(), Error<L>> +/// Ledger Post Error +pub enum PostError<L> where L: Ledger + ?Sized, { - ledger - .utxos_mut() - .try_insert(utxo) - .map_err(Error::AssetRegistered) + /// Sender Post Error + Sender(SenderPostError<L>), + + /// Receiver Post Error + Receiver(ReceiverPostError<L>), + + /// Invalid Secret Transfer + InvalidSecretTransfer, } -/// Tries to post the `encrypted_asset` to the `ledger` returning [`Error::EncryptedAssetStored`] -/// if the `encrypted_asset` was already stored on the `ledger`. -#[inline] -pub fn try_post_encrypted_asset<L>( - ledger: &mut L, - encrypted_asset: L::EncryptedAsset, -) -> Result<(), Error<L>> +impl<L> From<SenderPostError<L>> for PostError<L> where L: Ledger + ?Sized, { - ledger - .encrypted_assets_mut() - .try_insert(encrypted_asset) - .map_err(Error::EncryptedAssetStored) + #[inline] + fn from(err: SenderPostError<L>) -> Self { + Self::Sender(err) + } } -/// Checks if the `public_input` corresponding to a UTXO containment proof represents the current -/// state of the [`UtxoSet`](Ledger::UtxoSet), returning [`Error::InvalidUtxoState`] if not. -#[inline] -pub fn check_utxo_containment_proof_public_input<L>( - ledger: &mut L, - public_input: <L::UtxoSet as VerifiedSet>::Public, -) -> Result<(), Error<L>> +impl<L> From<ReceiverPostError<L>> for PostError<L> where L: Ledger + ?Sized, { - if ledger.utxos().check_public_input(&public_input) { - Ok(()) - } else { - Err(Error::InvalidUtxoState(public_input)) + #[inline] + fn from(err: ReceiverPostError<L>) -> Self { + Self::Receiver(err) } } diff --git a/manta-accounting/src/lib.rs b/manta-accounting/src/lib.rs index cfe2e36e5..333c1ac1a 100644 --- a/manta-accounting/src/lib.rs +++ b/manta-accounting/src/lib.rs @@ -23,14 +23,12 @@ extern crate alloc; extern crate derive_more; mod asset; -mod transfer; -mod wallet; +mod ledger; pub mod account; -pub mod ledger; +pub mod transfer; +pub mod wallet; pub use account::prelude::*; pub use asset::*; -pub use ledger::prelude::*; -pub use transfer::*; -pub use wallet::*; +pub use ledger::*; diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index d5a84b4f9..8de7b50f1 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -17,18 +17,21 @@ //! Transfer Protocols use crate::{ - account::{IdentityConfiguration, Receiver, Sender, Utxo}, - asset::{sample_asset_balances, Asset, AssetBalances, AssetId}, + account::{ + IdentityConfiguration, Receiver, ReceiverPost, Sender, SenderPost, Utxo, VoidNumber, + }, + asset::{sample_asset_balances, Asset, AssetBalance, AssetBalances, AssetId}, + ledger::{Ledger, PostError}, }; -use manta_codec::{ScaleDecode, ScaleEncode}; -use manta_crypto::{IntegratedEncryptionScheme, VerifiedSet}; +use manta_crypto::{ies::EncryptedMessage, IntegratedEncryptionScheme, VerifiedSet}; +use manta_util::array_map; use rand::{ distributions::{Distribution, Standard}, Rng, RngCore, }; /// Public Transfer Protocol -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, ScaleDecode, ScaleEncode)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct PublicTransfer<const SOURCES: usize, const SINKS: usize> { /// Asset Id pub asset_id: AssetId, @@ -42,6 +45,7 @@ pub struct PublicTransfer<const SOURCES: usize, const SINKS: usize> { impl<const SOURCES: usize, const SINKS: usize> PublicTransfer<SOURCES, SINKS> { /// Builds a new [`PublicTransfer`]. + #[inline] pub const fn new( asset_id: AssetId, sources: AssetBalances<SOURCES>, @@ -94,21 +98,112 @@ where T: SecretTransferConfiguration, { /// Builds a new [`SecretTransfer`]. + #[inline] pub fn new( senders: [Sender<T, T::UtxoSet>; SENDERS], receivers: [Receiver<T, T::IntegratedEncryptionScheme>; RECEIVERS], ) -> Self { Self { senders, receivers } } + + /// Checks that the asset ids of all the senders and receivers matches. + #[inline] + pub fn has_unique_asset_id(&self) -> bool { + let mut asset_id = None; + self.senders + .iter() + .map(Sender::asset_id) + .chain(self.receivers.iter().map(Receiver::asset_id)) + .all(move |i| asset_id.replace(i).eq(&Some(i))) + && asset_id.is_some() + } + + /// Returns the sum of the asset values of the senders in this transfer. + #[inline] + pub fn sender_sum(&self) -> AssetBalance { + self.senders.iter().map(Sender::asset_value).sum() + } + + /// Returns the sum of the asset values of the receivers in this transfer. + #[inline] + pub fn receiver_sum(&self) -> AssetBalance { + self.receivers.iter().map(Receiver::asset_value).sum() + } + + /// Checks that the [`sender_sum`](Self::sender_sum) equals the + /// [`receiver_sum`](Self::receiver_sum). + #[inline] + pub fn is_balanced(&self) -> bool { + self.sender_sum().eq(&self.receiver_sum()) + } + + #[inline] + fn generate_validity_proof(&self) { + // FIXME: build zkp + todo!() + } + + /// Converts `self` into its ledger post. + #[inline] + pub fn into_post(self) -> SecretTransferPost<T, SENDERS, RECEIVERS> { + let validity_proof = self.generate_validity_proof(); + SecretTransferPost { + sender_posts: array_map(self.senders, Sender::into_post), + receiver_posts: array_map(self.receivers, Receiver::into_post), + validity_proof, + } + } +} + +/// Secret Transfer Post +pub struct SecretTransferPost<T, const SENDERS: usize, const RECEIVERS: usize> +where + T: SecretTransferConfiguration, +{ + /// Sender Posts + pub sender_posts: [SenderPost<T, T::UtxoSet>; SENDERS], + + /// Receiver Posts + pub receiver_posts: [ReceiverPost<T, T::IntegratedEncryptionScheme>; RECEIVERS], + + /// Validity Proof + pub validity_proof: (), +} + +impl<T, const SENDERS: usize, const RECEIVERS: usize> SecretTransferPost<T, SENDERS, RECEIVERS> +where + T: SecretTransferConfiguration, +{ + /// Posts the [`SecretTransferPost`] to the `ledger`. + #[inline] + pub fn post<L>(self, ledger: &mut L) -> Result<(), PostError<L>> + where + L: Ledger< + VoidNumber = VoidNumber<T>, + Utxo = Utxo<T>, + UtxoSet = T::UtxoSet, + EncryptedAsset = EncryptedMessage<T::IntegratedEncryptionScheme>, + > + ?Sized, + { + for sender_post in IntoIterator::into_iter(self.sender_posts) { + sender_post.post(ledger)?; + } + for receiver_post in IntoIterator::into_iter(self.receiver_posts) { + receiver_post.post(ledger)?; + } + // FIXME: proof.post(ledger)?; + // - returns `PostError::InvalidSecretTransfer` on error? + Ok(()) + } } /// Transfer Protocol pub struct Transfer< T, const SOURCES: usize, + const SINKS: usize, const SENDERS: usize, const RECEIVERS: usize, - const SINKS: usize, > where T: SecretTransferConfiguration, { @@ -118,3 +213,66 @@ pub struct Transfer< /// Secret Transfer pub secret: SecretTransfer<T, SENDERS, RECEIVERS>, } + +impl<T, const SOURCES: usize, const SINKS: usize, const SENDERS: usize, const RECEIVERS: usize> + Transfer<T, SOURCES, SINKS, SENDERS, RECEIVERS> +where + T: SecretTransferConfiguration, +{ + /// Builds a new [`Transfer`] from a [`PublicTransfer`] and a [`SecretTransfer`]. + #[inline] + pub fn new( + public: PublicTransfer<SOURCES, SINKS>, + secret: SecretTransfer<T, SENDERS, RECEIVERS>, + ) -> Self { + Self { public, secret } + } + + /// Converts `self` into its ledger post. + #[inline] + pub fn into_post(self) -> TransferPost<T, SOURCES, SINKS, SENDERS, RECEIVERS> { + TransferPost { + public_transfer_post: self.public, + secret_transfer_post: self.secret.into_post(), + } + } +} + +/// Transfer Post +pub struct TransferPost< + T, + const SOURCES: usize, + const SINKS: usize, + const SENDERS: usize, + const RECEIVERS: usize, +> where + T: SecretTransferConfiguration, +{ + /// Public Transfer Post + pub public_transfer_post: PublicTransfer<SOURCES, SINKS>, + + /// Secret Transfer Post + pub secret_transfer_post: SecretTransferPost<T, SENDERS, RECEIVERS>, +} + +impl<T, const SOURCES: usize, const SINKS: usize, const SENDERS: usize, const RECEIVERS: usize> + TransferPost<T, SOURCES, SINKS, SENDERS, RECEIVERS> +where + T: SecretTransferConfiguration, +{ + /// Posts the [`TransferPost`] to the `ledger`. + #[inline] + pub fn post<L>(self, ledger: &mut L) -> Result<(), PostError<L>> + where + L: Ledger< + VoidNumber = VoidNumber<T>, + Utxo = Utxo<T>, + EncryptedAsset = EncryptedMessage<T::IntegratedEncryptionScheme>, + UtxoSet = T::UtxoSet, + > + ?Sized, + { + // FIXME: self.public_transfer_post.post(ledger)?; + self.secret_transfer_post.post(ledger)?; + Ok(()) + } +} diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index 19bc59131..12b2c83ed 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -17,18 +17,25 @@ //! Wallet Abstractions use crate::{ - account::ShieldedIdentity, + account::{Sender, ShieldedIdentity, VoidNumberCommitment}, asset::{Asset, AssetBalance, AssetId}, ledger::Ledger, transfer::{SecretTransfer, SecretTransferConfiguration}, }; use core::convert::Infallible; +use manta_crypto::ConcatBytes; /// Asset Map pub trait AssetMap { /// Returns the current balance associated with this `id`. fn balance(&self, id: AssetId) -> AssetBalance; + /// Returns `true` if `self` contains at least `asset.value` of the asset of kind `asset.id`. + #[inline] + fn contains(&self, asset: Asset) -> bool { + self.balance(asset.id) >= asset.value + } + /// Sets the asset balance for `id` to `value`. fn set_balance(&mut self, id: AssetId, value: AssetBalance); @@ -147,21 +154,40 @@ where } /// Updates `self` with new information from the ledger. - pub fn update<L>(&mut self, ledger: &L) + pub fn pull_updates<L>(&mut self, ledger: &L) where L: Ledger, { + // TODO: pull updates from the ledger + // - new void numbers? + // - new utxos? + // - new encrypted notes? let _ = ledger; todo!() } /// Builds a [`SecretTransfer`] transaction to send `asset` to `receiver`. - pub fn send( + pub fn secret_send( &self, + commitment_scheme: &T::CommitmentScheme, asset: Asset, receiver: ShieldedIdentity<T, T::IntegratedEncryptionScheme>, - ) -> SecretTransfer<T, 2, 2> { - let _ = (asset, receiver); + ) -> Option<SecretTransfer<T, 2, 2>> + where + VoidNumberCommitment<T>: ConcatBytes, + { + // TODO: spec: + // 1. check that we have enough `asset` in the secret_assets map + // 2. find out which keys have control over `asset` + // 3. build two senders and build a receiver and a change receiver for the extra change + + // TODO: which `rng` do we use for the receivers? + + /* + let sender = Sender::generate(self.secret_key_source, commitment_scheme); + let receiver = receiver.into_receiver(commitment_scheme, asset, rng); + */ + let _ = (commitment_scheme, asset, receiver); todo!() } } diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index 73ca89912..9fc803bf3 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -24,6 +24,4 @@ maintenance = { status = "actively-developed" } [dependencies] derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } -manta-codec = { path = "../manta-codec", default-features = false, features = ["derive"] } -parity-scale-codec = { version = "2.2.0", default-features = false } rand = { version = "0.8.4", default-features = false } diff --git a/manta-crypto/src/ies.rs b/manta-crypto/src/ies.rs index 66d632285..0a0d17153 100644 --- a/manta-crypto/src/ies.rs +++ b/manta-crypto/src/ies.rs @@ -19,7 +19,6 @@ // FIXME: add zeroize for secret keys use core::{fmt::Debug, hash::Hash}; -use manta_codec::{ScaleDecode, ScaleEncode}; use rand::{CryptoRng, RngCore}; pub(crate) mod prelude { @@ -218,7 +217,7 @@ pub trait IntegratedEncryptionScheme { } /// Encrypted Message -#[derive(derivative::Derivative, ScaleDecode, ScaleEncode)] +#[derive(derivative::Derivative)] #[derivative( Clone(bound = "I::Ciphertext: Clone"), Copy(bound = "I::Ciphertext: Copy"), diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index b1a19e6c6..8a40e64a2 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -28,12 +28,14 @@ std = [] [dependencies] aes-gcm = { version = "0.9.4" } -ark-std = { version = "0.3.0", default-features = false } blake2 = { version = "0.9.2", default-features = false } generic-array = { version = "0.14.4", default-features = false } manta-accounting = { path = "../manta-accounting", default-features = false } -manta-codec = { path = "../manta-codec", default-features = false, features = ["derive"] } manta-crypto = { path = "../manta-crypto", default-features = false } manta-util = { path = "../manta-util", default-features = false } -parity-scale-codec = { version = "2.2.0", default-features = false } +rand = { version = "0.8.4", default-features = false } x25519-dalek = { git = "https://github.com/Manta-Network/x25519-dalek", default-features = false, features = ["u64_backend"] } + +[dev-dependencies] +rand = "0.8.4" +rand_chacha = "0.3.1" diff --git a/manta-pay/src/ies.rs b/manta-pay/src/ies.rs index 5251ed0ef..1e3e07f63 100644 --- a/manta-pay/src/ies.rs +++ b/manta-pay/src/ies.rs @@ -16,20 +16,21 @@ //! IES Implementation +// FIXME: make sure secret keys are protected + use aes_gcm::{ aead::{Aead, NewAead}, Aes256Gcm, Nonce, }; -use ark_std::rand::{CryptoRng, RngCore}; use blake2::{Blake2s, Digest}; use generic_array::GenericArray; use manta_accounting::Asset; -use manta_codec::{ScaleDecode, ScaleEncode}; use manta_crypto::{ ies::{self, KeyPair}, IntegratedEncryptionScheme, }; -use manta_util::try_into_array_unchecked; +use manta_util::into_array_unchecked; +use rand::{CryptoRng, RngCore}; use x25519_dalek::{EphemeralSecret, PublicKey as PubKey, StaticSecret}; /// Public Key Type @@ -47,6 +48,7 @@ pub type AssetCiphertext = [u8; Asset::SIZE + 16]; pub type EphemeralPublicKey = PublicKey; /// Augmented Asset Ciphertext +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct AugmentedAssetCiphertext { /// Asset Ciphertext pub asset_ciphertext: AssetCiphertext, @@ -74,9 +76,7 @@ impl AugmentedAssetCiphertext { pub type EncryptedAsset = ies::EncryptedMessage<IES>; /// Implementation of [`IntegratedEncryptionScheme`] -#[derive( - Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, ScaleDecode, ScaleEncode, -)] +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct IES; impl IES { @@ -87,10 +87,12 @@ impl IES { const KDF_SALT: &'static [u8] = b"manta kdf instantiated with blake2s hash function"; /// Runs `blake2s::hkdf_extract(salt, seed)` with a fixed salt. + #[inline] fn blake2s_kdf(input: &[u8]) -> [u8; 32] { let mut hasher = Blake2s::new(); - hasher.update([input, Self::KDF_SALT].concat()); - try_into_array_unchecked(hasher.finalize()) + hasher.update(input); + hasher.update(Self::KDF_SALT); + into_array_unchecked(hasher.finalize()) } } @@ -125,18 +127,20 @@ impl IntegratedEncryptionScheme for IES { { let ephemeral_secret_key = EphemeralSecret::new(rng); let ephemeral_public_key = PubKey::from(&ephemeral_secret_key); - let shared_secret = ephemeral_secret_key.diffie_hellman(&PubKey::from(public_key)); - let ss = Self::blake2s_kdf(&shared_secret.to_bytes()); - let aes_key = GenericArray::from_slice(&ss); + let shared_secret = Self::blake2s_kdf( + &ephemeral_secret_key + .diffie_hellman(&public_key.into()) + .to_bytes(), + ); // SAFETY: Using a deterministic nonce is ok since we never reuse keys. - let asset_ciphertext = Aes256Gcm::new(aes_key).encrypt( + let asset_ciphertext = Aes256Gcm::new(GenericArray::from_slice(&shared_secret)).encrypt( Nonce::from_slice(Self::NONCE), plaintext.into_bytes().as_ref(), )?; Ok(EncryptedAsset::new(AugmentedAssetCiphertext::new( - try_into_array_unchecked(asset_ciphertext), + into_array_unchecked(asset_ciphertext), ephemeral_public_key.to_bytes(), ))) } @@ -145,19 +149,41 @@ impl IntegratedEncryptionScheme for IES { ciphertext: Self::Ciphertext, secret_key: Self::SecretKey, ) -> Result<Self::Plaintext, Self::Error> { - let sk = StaticSecret::from(secret_key); - let shared_secret = sk.diffie_hellman(&PubKey::from(ciphertext.ephemeral_public_key)); - let ss = Self::blake2s_kdf(&shared_secret.to_bytes()); - let aes_key = GenericArray::from_slice(&ss); + let shared_secret = Self::blake2s_kdf( + &StaticSecret::from(secret_key) + .diffie_hellman(&ciphertext.ephemeral_public_key.into()) + .to_bytes(), + ); // SAFETY: Using a deterministic nonce is ok since we never reuse keys. - let plaintext = Aes256Gcm::new(aes_key).decrypt( + let plaintext = Aes256Gcm::new(GenericArray::from_slice(&shared_secret)).decrypt( Nonce::from_slice(Self::NONCE), ciphertext.asset_ciphertext.as_ref(), )?; - Ok(Asset::from_bytes(try_into_array_unchecked( + Ok(Asset::from_bytes(into_array_unchecked( &plaintext[..Asset::SIZE], ))) } } + +/// Tests encryption/decryption of an asset. +#[test] +pub fn encryption_decryption() { + use rand::{thread_rng, Rng, SeedableRng}; + use rand_chacha::ChaCha20Rng; + + let mut rng = ChaCha20Rng::from_rng(&mut thread_rng()).expect("failed to seed ChaCha20Rng"); + + let (public_key, secret_key) = IES::keygen(&mut rng).into(); + let asset = rng.gen(); + let reconstructed_asset = secret_key + .decrypt( + public_key + .encrypt(asset, &mut rng) + .expect("unable to encrypt asset"), + ) + .expect("unable to decrypt asset"); + + assert_eq!(asset, reconstructed_asset) +} diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index ac70ee000..55e08b447 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -40,13 +40,13 @@ macro_rules! from_variant_impl { /// Performs the [`TryInto`] conversion into an array without checking if the conversion succeeded. #[inline] -pub fn try_into_array_unchecked<T, V, const N: usize>(v: V) -> [T; N] +pub fn into_array_unchecked<T, V, const N: usize>(v: V) -> [T; N] where V: TryInto<[T; N]>, { match v.try_into() { Ok(array) => array, - _ => unreachable!(), + _ => unreachable!("User was supposed to ensure that this branch is never reached."), } } @@ -57,7 +57,7 @@ where F: FnMut(T) -> U, { // TODO: get rid of this function when `array::map` is stabilized - try_into_array_unchecked(IntoIterator::into_iter(array).map(f).collect::<Vec<_>>()) + into_array_unchecked(IntoIterator::into_iter(array).map(f).collect::<Vec<_>>()) } /// Maps `f` over the `array` by reference. @@ -66,7 +66,7 @@ pub fn array_map_ref<T, U, F, const N: usize>(array: &[T; N], f: F) -> [U; N] where F: FnMut(&T) -> U, { - try_into_array_unchecked(array.iter().map(f).collect::<Vec<_>>()) + into_array_unchecked(array.iter().map(f).collect::<Vec<_>>()) } /// Maps `f` over the `array` returning the target array if all of the mappings succeeded, or @@ -76,7 +76,7 @@ pub fn fallible_array_map<T, U, E, F, const N: usize>(array: [T; N], f: F) -> Re where F: FnMut(T) -> Result<U, E>, { - Ok(try_into_array_unchecked( + Ok(into_array_unchecked( IntoIterator::into_iter(array) .map(f) .collect::<Result<Vec<_>, _>>()?, From 27d90cefd39c07f842a5c65e998d596e969c30a6 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 7 Sep 2021 15:53:21 -0400 Subject: [PATCH 011/275] start implementing ledger and crypto primitives --- manta-accounting/src/account.rs | 3 +- manta-accounting/src/asset.rs | 4 +- manta-accounting/src/wallet.rs | 23 +- manta-crypto/src/commitment.rs | 2 +- manta-crypto/src/concat.rs | 39 ++-- manta-crypto/src/lib.rs | 1 - manta-crypto/src/set.rs | 8 +- manta-pay/Cargo.toml | 2 + manta-pay/src/accounting/ledger.rs | 201 ++++++++++++++++++ .../src/accounting/mod.rs | 19 +- manta-pay/src/crypto/commitment/mod.rs | 19 ++ manta-pay/src/crypto/commitment/pedersen.rs | 73 +++++++ manta-pay/src/{ => crypto}/ies.rs | 0 manta-pay/src/crypto/merkle_tree.rs | 172 +++++++++++++++ manta-pay/src/crypto/mod.rs | 22 ++ manta-pay/src/crypto/prf/blake2s.rs | 37 ++++ manta-pay/src/crypto/prf/mod.rs | 19 ++ manta-pay/src/lib.rs | 5 +- 18 files changed, 607 insertions(+), 42 deletions(-) create mode 100644 manta-pay/src/accounting/ledger.rs rename manta-crypto/src/checksum.rs => manta-pay/src/accounting/mod.rs (55%) create mode 100644 manta-pay/src/crypto/commitment/mod.rs create mode 100644 manta-pay/src/crypto/commitment/pedersen.rs rename manta-pay/src/{ => crypto}/ies.rs (100%) create mode 100644 manta-pay/src/crypto/merkle_tree.rs create mode 100644 manta-pay/src/crypto/mod.rs create mode 100644 manta-pay/src/crypto/prf/blake2s.rs create mode 100644 manta-pay/src/crypto/prf/mod.rs diff --git a/manta-accounting/src/account.rs b/manta-accounting/src/account.rs index c1c133243..01458231a 100644 --- a/manta-accounting/src/account.rs +++ b/manta-accounting/src/account.rs @@ -580,6 +580,7 @@ where /// Generates a new [`Identity`] from a secret key generation source and builds a new /// [`ShieldedIdentity`]-[`Spend`] pair from it. + #[allow(clippy::type_complexity)] // NOTE: It's the generic parameters that make this complex. #[inline] pub fn generate_receiver<I>( source: &mut C::SecretKeyGenerator, @@ -896,7 +897,7 @@ where pub fn into_post(self) -> SenderPost<C, S> { SenderPost { void_number: self.void_number, - utxo_containment_proof_public_input: self.utxo_containment_proof.public_input, + utxo_containment_proof_public_input: self.utxo_containment_proof.into_public_input(), } } diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 131d33a31..c356d762d 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -29,7 +29,7 @@ use core::{ use derive_more::{ Add, AddAssign, Display, Div, DivAssign, From, Mul, MulAssign, Product, Sub, SubAssign, Sum, }; -use manta_crypto::{Accumulator, ConcatBytes}; +use manta_crypto::{ByteAccumulator, ConcatBytes}; use manta_util::{array_map, fallible_array_map, into_array_unchecked}; use rand::{ distributions::{Distribution, Standard}, @@ -310,7 +310,7 @@ impl ConcatBytes for Asset { #[inline] fn concat<A>(&self, accumulator: &mut A) where - A: Accumulator<u8> + ?Sized, + A: ByteAccumulator + ?Sized, { accumulator.extend(&self.id.into_bytes()); accumulator.extend(&self.value.into_bytes()); diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index 12b2c83ed..db51782a7 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -17,13 +17,17 @@ //! Wallet Abstractions use crate::{ - account::{Sender, ShieldedIdentity, VoidNumberCommitment}, + account::{ + AssetParameters, PublicKey, SecretKeyGeneratorError, Sender, ShieldedIdentity, + VoidNumberCommitment, VoidNumberGenerator, + }, asset::{Asset, AssetBalance, AssetId}, ledger::Ledger, transfer::{SecretTransfer, SecretTransferConfiguration}, }; use core::convert::Infallible; use manta_crypto::ConcatBytes; +use rand::distributions::{Distribution, Standard}; /// Asset Map pub trait AssetMap { @@ -166,6 +170,23 @@ where todo!() } + /// Generates a new [`ShieldedIdentity`] to receive assets to this wallet. + #[inline] + pub fn generate_receiver( + &mut self, + commitment_scheme: &T::CommitmentScheme, + ) -> Result<ShieldedIdentity<T, T::IntegratedEncryptionScheme>, SecretKeyGeneratorError<T>> + where + Standard: Distribution<AssetParameters<T>>, + PublicKey<T>: ConcatBytes, + VoidNumberGenerator<T>: ConcatBytes, + { + // FIXME: shouldn't we also produce a `Spend`? since we are modifying the secret key source, + // we can't go back and discover what the actual "derived key path" was except to go into + // recovery mode + ShieldedIdentity::generate(&mut self.secret_key_source, commitment_scheme) + } + /// Builds a [`SecretTransfer`] transaction to send `asset` to `receiver`. pub fn secret_send( &self, diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index 9188fe8ea..fdf075fbd 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -30,7 +30,7 @@ pub trait CommitmentScheme { /// Samples random commitment paramters. fn setup<R>(rng: &mut R) -> Self where - R: RngCore + ?Sized; + R: RngCore; /// Commits the `input` with the given `randomness` parameter. fn commit<I>(&self, input: I, randomness: &Self::Randomness) -> Self::Output diff --git a/manta-crypto/src/concat.rs b/manta-crypto/src/concat.rs index 3cc9ad1bf..dba3ca3d0 100644 --- a/manta-crypto/src/concat.rs +++ b/manta-crypto/src/concat.rs @@ -19,10 +19,10 @@ use alloc::vec::Vec; use core::borrow::Borrow; -/// Accumulation Trait -pub trait Accumulator<T> { +/// Byte Accumulation Trait +pub trait ByteAccumulator { /// Extends the current accumulator by a `buffer` of elements. - fn extend(&mut self, buffer: &[T]); + fn extend(&mut self, buffer: &[u8]); /// Reserves space in the accumulator for `additional` more elements. #[inline] @@ -54,12 +54,12 @@ pub trait Accumulator<T> { } } -impl<T, A> Accumulator<T> for &mut A +impl<A> ByteAccumulator for &mut A where - A: Accumulator<T> + ?Sized, + A: ByteAccumulator + ?Sized, { #[inline] - fn extend(&mut self, buffer: &[T]) { + fn extend(&mut self, buffer: &[u8]) { (**self).extend(buffer) } @@ -74,12 +74,9 @@ where } } -impl<T> Accumulator<T> for Vec<T> -where - T: Clone + ?Sized, -{ +impl ByteAccumulator for Vec<u8> { #[inline] - fn extend(&mut self, buffer: &[T]) { + fn extend(&mut self, buffer: &[u8]) { self.extend_from_slice(buffer) } @@ -105,7 +102,7 @@ pub trait ConcatBytes { /// is not efficient. fn concat<A>(&self, accumulator: &mut A) where - A: Accumulator<u8> + ?Sized; + A: ByteAccumulator + ?Sized; /// Returns a hint to the possible number of bytes that will be accumulated when concatenating /// `self`. @@ -118,7 +115,7 @@ pub trait ConcatBytes { #[inline] fn reserve_concat<A>(&self, accumulator: &mut A) where - A: Accumulator<u8> + ?Sized, + A: ByteAccumulator + ?Sized, { if let Some(capacity) = self.size_hint() { accumulator.reserve(capacity); @@ -131,7 +128,7 @@ pub trait ConcatBytes { #[inline] fn as_bytes<A>(&self) -> A where - A: Default + Accumulator<u8>, + A: Default + ByteAccumulator, { let mut accumulator = A::default(); self.reserve_concat(&mut accumulator); @@ -146,7 +143,7 @@ where #[inline] fn concat<A>(&self, accumulator: &mut A) where - A: Accumulator<u8> + ?Sized, + A: ByteAccumulator + ?Sized, { accumulator.extend(self.borrow()) } @@ -157,7 +154,7 @@ where } } -/// Concatenates `$item`s together by building a [`Accumulator`] and running +/// Concatenates `$item`s together by building a [`ByteAccumulator`] and running /// [`ConcatBytes::concat`] over each `$item`. #[macro_export] macro_rules! concatenate { @@ -166,7 +163,15 @@ macro_rules! concatenate { extern crate alloc; let mut accumulator = ::alloc::vec::Vec::new(); $($crate::ConcatBytes::reserve_concat($item, &mut accumulator);)* - $crate::Accumulator::finish(accumulator) + $crate::ByteAccumulator::finish(accumulator) } } } + +/// Returns byte vector representation of `$item`. +#[macro_export] +macro_rules! as_bytes { + ($item:expr) => { + $crate::concatenate!($item) + }; +} diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index da39f90e4..de6027320 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -25,7 +25,6 @@ mod commitment; mod concat; mod prf; -pub mod checksum; pub mod constraints; pub mod ies; pub mod set; diff --git a/manta-crypto/src/set.rs b/manta-crypto/src/set.rs index 8c10d17fc..d919af234 100644 --- a/manta-crypto/src/set.rs +++ b/manta-crypto/src/set.rs @@ -57,7 +57,7 @@ where S: VerifiedSet + ?Sized, { /// Public Input - pub public_input: S::Public, + public_input: S::Public, /// Secret Witness secret_witness: S::Secret, @@ -76,6 +76,12 @@ where } } + /// Returns [`S::Public`](VerifiedSet::Public) discarding the [`ContainmentProof`]. + #[inline] + pub fn into_public_input(self) -> S::Public { + self.public_input + } + /// Verifies that the `item` is contained in some [`VerifiedSet`]. #[inline] pub fn verify(&self, item: &S::Item) -> bool { diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 8a40e64a2..16f6851ed 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -28,6 +28,8 @@ std = [] [dependencies] aes-gcm = { version = "0.9.4" } +ark-crypto-primitives = { version = "0.3.0", default-features = false } +ark-ed-on-bls12-381 = { version = "0.3.0", default-features = false } blake2 = { version = "0.9.2", default-features = false } generic-array = { version = "0.14.4", default-features = false } manta-accounting = { path = "../manta-accounting", default-features = false } diff --git a/manta-pay/src/accounting/ledger.rs b/manta-pay/src/accounting/ledger.rs new file mode 100644 index 000000000..a29b8048c --- /dev/null +++ b/manta-pay/src/accounting/ledger.rs @@ -0,0 +1,201 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Ledger Implementation + +use crate::crypto::{ + ies::EncryptedAsset, + merkle_tree::{self, MerkleTree, Path, Root}, +}; +use alloc::{vec, vec::Vec}; +use blake2::{ + digest::{Update, VariableOutput}, + VarBlake2s, +}; +use manta_accounting::Ledger as LedgerTrait; +use manta_crypto::set::{ContainmentProof, Set, VerifiedSet}; +use manta_util::into_array_unchecked; + +/// Unspent Transaction Output +type Utxo = [u8; 32]; + +/// UTXO Shard Root +type UtxoShardRoot = Root; + +/// UTXO Shard +#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] +pub struct UtxoShard { + /// Shard Root + root: UtxoShardRoot, + + /// Unspent Transaction Outputs + utxos: Vec<Utxo>, +} + +/// UTXO Set +#[derive(Clone)] +pub struct UtxoSet { + /// UTXO Shards + shards: [UtxoShard; Self::SHARD_COUNT], + + /// Merkle Tree Parameters + parameters: merkle_tree::Parameters, +} + +impl UtxoSet { + const SHARD_COUNT: usize = 256; + + /// Builds a new [`UtxoSet`]. + #[inline] + pub fn new(parameters: merkle_tree::Parameters) -> Self { + Self { + shards: into_array_unchecked(vec![Default::default(); Self::SHARD_COUNT]), + parameters, + } + } + + /// Computes the shard index of this `utxo`. + #[inline] + fn shard_index(utxo: &Utxo) -> usize { + let mut hasher = VarBlake2s::new(1).expect("Failed to generate Variable Blake2s hasher."); + hasher.update(&utxo); + let mut res: usize = 0; + hasher.finalize_variable(|x| res = x[0] as usize); + res + } + + /// Returns a shared reference to the shard which this `utxo` would be stored in. + #[inline] + pub fn shard(&self, utxo: &Utxo) -> &UtxoShard { + &self.shards[Self::shard_index(utxo)] + } + + /// Returns `true` if the `root` belongs to some shard. + #[inline] + pub fn root_exists(&self, root: &UtxoShardRoot) -> bool { + self.shards.iter().any(move |s| s.root == *root) + } + + /// Returns `true` if the `utxo` belongs to the shard it would be stored in. + #[inline] + pub fn utxo_exists(&self, utxo: &Utxo) -> bool { + self.shard(utxo).utxos.iter().any(move |u| u == utxo) + } +} + +impl Set for UtxoSet { + type Item = Utxo; + + #[inline] + fn contains(&self, item: &Self::Item) -> bool { + self.utxo_exists(item) + } + + #[inline] + fn try_insert(&mut self, item: Self::Item) -> Result<(), Self::Item> { + let shard = &mut self.shards[Self::shard_index(&item)]; + if shard.utxos.contains(&item) { + return Err(item); + } + shard.utxos.push(item); + match MerkleTree::build_root(&self.parameters, &shard.utxos) { + Some(root) => { + shard.root = root; + Ok(()) + } + _ => Err(shard.utxos.pop().unwrap()), + } + } +} + +impl VerifiedSet for UtxoSet { + type Public = UtxoShardRoot; + + type Secret = Path<Utxo>; + + // TODO: Give a more informative error. + type ContainmentError = (); + + #[inline] + fn check_public_input(&self, public: &Self::Public) -> bool { + self.root_exists(public) + } + + #[inline] + fn get_containment_proof( + &self, + item: &Self::Item, + ) -> Result<ContainmentProof<Self>, Self::ContainmentError> { + let utxos = &self.shards[Self::shard_index(item)].utxos; + match utxos.iter().position(move |u| u == item) { + Some(index) => MerkleTree::new(&self.parameters, utxos) + .ok_or(())? + .get_containment_proof(index) + .ok_or(()), + _ => Err(()), + } + } +} + +/// Ledger +pub struct Ledger { + /// Void Numbers + void_numbers: (), + + /// Unspent Transaction Outputs + utxos: UtxoSet, + + /// Encrypted Assets + encrypted_assets: (), +} + +impl LedgerTrait for Ledger { + type VoidNumber = (); + + type Utxo = Utxo; + + type UtxoSet = UtxoSet; + + type EncryptedAsset = EncryptedAsset; + + #[inline] + fn utxos(&self) -> &Self::UtxoSet { + &self.utxos + } + + #[inline] + fn try_post_void_number( + &mut self, + void_number: Self::VoidNumber, + ) -> Result<(), Self::VoidNumber> { + let _ = void_number; + todo!() + } + + #[inline] + fn try_post_utxo(&mut self, utxo: Self::Utxo) -> Result<(), Self::Utxo> { + self.utxos.try_insert(utxo) + } + + #[inline] + fn try_post_encrypted_asset( + &mut self, + encrypted_asset: Self::EncryptedAsset, + ) -> Result<(), Self::EncryptedAsset> { + let _ = encrypted_asset; + todo!() + } +} diff --git a/manta-crypto/src/checksum.rs b/manta-pay/src/accounting/mod.rs similarity index 55% rename from manta-crypto/src/checksum.rs rename to manta-pay/src/accounting/mod.rs index 5deb133ef..29873f974 100644 --- a/manta-crypto/src/checksum.rs +++ b/manta-pay/src/accounting/mod.rs @@ -14,21 +14,6 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Checksums +//! Accounting Implementations -// TODO: move this out of this crate, this is for `pallet-manta-pay` when we check local parameters -// against stored ones. maybe this can go in `manta-pay`? - -/// Checksum Equality -pub trait ChecksumEq<SumAlg, Rhs: ?Sized = Self> { - /// Returns `true` if `self` and `other` have the same checksum. - #[must_use] - fn checksum_eq(&self, other: &Rhs) -> bool; - - /// Returns `true` if `self` and `other` have different checksums. - #[inline] - #[must_use] - fn checksum_ne(&self, other: &Rhs) -> bool { - !self.checksum_eq(other) - } -} +pub mod ledger; diff --git a/manta-pay/src/crypto/commitment/mod.rs b/manta-pay/src/crypto/commitment/mod.rs new file mode 100644 index 000000000..3e8cff1b6 --- /dev/null +++ b/manta-pay/src/crypto/commitment/mod.rs @@ -0,0 +1,19 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Commitment Scheme Implementations + +pub mod pedersen; diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs new file mode 100644 index 000000000..856d5761b --- /dev/null +++ b/manta-pay/src/crypto/commitment/pedersen.rs @@ -0,0 +1,73 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Pedersen Commitment Implementation + +use ark_crypto_primitives::commitment::{ + pedersen::{Commitment, Window}, + CommitmentScheme as ArkCommitmentScheme, +}; +use ark_ed_on_bls12_381::EdwardsProjective; +use core::borrow::Borrow; +use manta_crypto::CommitmentScheme; +use rand::RngCore; + +/// Implementation of [`CommitmentScheme`] +#[derive(Clone)] +pub struct PedersenCommitment(<ArkPedersenCommitment as ArkCommitmentScheme>::Parameters); + +impl PedersenCommitment { + /// Pedersen Window Size + pub const WINDOW_SIZE: usize = 4; + + /// Pedersen Window Count + pub const NUM_WINDOWS: usize = 256; +} + +/// Pedersen Window Parameters +#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct PedersenWindow; + +impl Window for PedersenWindow { + const WINDOW_SIZE: usize = PedersenCommitment::WINDOW_SIZE; + const NUM_WINDOWS: usize = PedersenCommitment::NUM_WINDOWS; +} + +/// Arkworks Pedersen Commitment +pub type ArkPedersenCommitment = Commitment<EdwardsProjective, PedersenWindow>; + +impl CommitmentScheme for PedersenCommitment { + type Randomness = <ArkPedersenCommitment as ArkCommitmentScheme>::Randomness; + + type Output = <ArkPedersenCommitment as ArkCommitmentScheme>::Output; + + #[inline] + fn setup<R>(rng: &mut R) -> Self + where + R: RngCore, + { + Self(ArkPedersenCommitment::setup(rng).expect("As of arkworks 0.3.0, this never fails.")) + } + + #[inline] + fn commit<I>(&self, input: I, randomness: &Self::Randomness) -> Self::Output + where + I: Borrow<[u8]>, + { + ArkPedersenCommitment::commit(&self.0, input.borrow(), randomness) + .expect("As of arkworks 0.3.0, this never fails.") + } +} diff --git a/manta-pay/src/ies.rs b/manta-pay/src/crypto/ies.rs similarity index 100% rename from manta-pay/src/ies.rs rename to manta-pay/src/crypto/ies.rs diff --git a/manta-pay/src/crypto/merkle_tree.rs b/manta-pay/src/crypto/merkle_tree.rs new file mode 100644 index 000000000..aab060777 --- /dev/null +++ b/manta-pay/src/crypto/merkle_tree.rs @@ -0,0 +1,172 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Merkle Tree Implementation + +// NOTE: Most if not all of the fallible interfaces in this file never actually fail. We use +// faillible interfaces so that we don't have to depend explicitly on implementation +// details of the `arkworks` project. + +use crate::crypto::commitment::pedersen::PedersenWindow; +use alloc::vec::Vec; +use ark_crypto_primitives::{ + crh::pedersen::CRH, + merkle_tree::{ + Config as MerkleTreeConfig, LeafParam, MerkleTree as ArkMerkleTree, Path as MerkleTreePath, + TwoToOneDigest, TwoToOneParam, + }, +}; +use ark_ed_on_bls12_381::EdwardsProjective; +use core::marker::PhantomData; +use manta_crypto::{ + as_bytes, + set::{ContainmentProof, VerifiedSet, VerifyContainment}, + ConcatBytes, +}; + +/// Merkle Tree Root +pub type Root = TwoToOneDigest<MerkleTreeConfiguration>; + +/// Merkle Tree Parameters +#[derive(Clone)] +pub struct Parameters { + /// Leaf Hash Parameters + leaf: LeafParam<MerkleTreeConfiguration>, + + /// Two-to-One Hash Parameters + two_to_one: TwoToOneParam<MerkleTreeConfiguration>, +} + +/// Merkle Tree Path +#[derive(Clone)] +pub struct Path<T> { + /// Merkle Tree Parameters + parameters: Parameters, + + /// Path + path: MerkleTreePath<MerkleTreeConfiguration>, + + /// Marker + __: PhantomData<T>, +} + +impl<T> Path<T> { + /// Builds a new [`Path`] from `parameters` and `path`. + #[inline] + fn new(parameters: Parameters, path: MerkleTreePath<MerkleTreeConfiguration>) -> Self { + Self { + parameters, + path, + __: PhantomData, + } + } +} + +/// Merkle Tree +#[derive(Clone)] +pub struct MerkleTree<T> { + /// Merkle Tree Parameters + parameters: Parameters, + + /// Merkle Tree + tree: ArkMerkleTree<MerkleTreeConfiguration>, + + /// Marker + __: PhantomData<T>, +} + +impl<T> MerkleTree<T> +where + T: ConcatBytes, +{ + /// Builds a new [`MerkleTree`]. + /// + /// # Panics + /// + /// The length of `leaves` must be a power of 2 or this function will panic. + #[inline] + pub fn new(parameters: &Parameters, leaves: &[T]) -> Option<Self> { + Some(Self { + tree: ArkMerkleTree::new( + &parameters.leaf, + &parameters.two_to_one, + &leaves + .iter() + .map(move |leaf| as_bytes!(leaf)) + .collect::<Vec<_>>(), + ) + .ok()?, + parameters: parameters.clone(), + __: PhantomData, + }) + } + + /// Computes the [`Root`] of the [`MerkleTree`] built from the `leaves`. + #[inline] + pub fn build_root(parameters: &Parameters, leaves: &[T]) -> Option<Root> { + Some(Self::new(parameters, leaves)?.root()) + } + + /// Returns the [`Root`] of this [`MerkleTree`]. + #[inline] + pub fn root(&self) -> Root { + self.tree.root() + } + + /// Builds a containment proof (i.e. merkle root and path) for the leaf at the given `index`. + #[inline] + pub fn get_containment_proof<S>(&self, index: usize) -> Option<ContainmentProof<S>> + where + S: VerifiedSet<Public = Root, Secret = Path<T>>, + { + Some(ContainmentProof::new( + self.root(), + Path::new( + self.parameters.clone(), + self.tree.generate_proof(index).ok()?, + ), + )) + } +} + +/// Merkle Tree Configuration +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct MerkleTreeConfiguration; + +impl MerkleTreeConfig for MerkleTreeConfiguration { + type LeafHash = CRH<EdwardsProjective, PedersenWindow>; + + // TODO: On the arkworks development branch `CRH` was fixed to `TwoToOneCRH`. + // We will need to fix this in the next update. + type TwoToOneHash = CRH<EdwardsProjective, PedersenWindow>; +} + +impl<T> VerifyContainment<Root, T> for Path<T> +where + T: ConcatBytes, +{ + #[inline] + fn verify(&self, root: &Root, item: &T) -> bool { + self.path + .verify( + &self.parameters.leaf, + &self.parameters.two_to_one, + root, + &as_bytes!(item), + ) + .expect("As of arkworks 0.3.0, this never fails.") + } +} diff --git a/manta-pay/src/crypto/mod.rs b/manta-pay/src/crypto/mod.rs new file mode 100644 index 000000000..168ae8da4 --- /dev/null +++ b/manta-pay/src/crypto/mod.rs @@ -0,0 +1,22 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Manta Pay Cryptographic Primitives Implementations + +pub mod commitment; +pub mod ies; +pub mod merkle_tree; +pub mod prf; diff --git a/manta-pay/src/crypto/prf/blake2s.rs b/manta-pay/src/crypto/prf/blake2s.rs new file mode 100644 index 000000000..6a8d324f0 --- /dev/null +++ b/manta-pay/src/crypto/prf/blake2s.rs @@ -0,0 +1,37 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Blake2s PRF Implementation + +use ark_crypto_primitives::prf::{self, PRF}; +use manta_crypto::PseudorandomFunctionFamily; + +/// Blake2s Pseudorandom Function Family +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Blake2s; + +impl PseudorandomFunctionFamily for Blake2s { + type Seed = <prf::Blake2s as PRF>::Seed; + + type Input = <prf::Blake2s as PRF>::Input; + + type Output = <prf::Blake2s as PRF>::Output; + + #[inline] + fn evaluate(seed: &Self::Seed, input: &Self::Input) -> Self::Output { + prf::Blake2s::evaluate(seed, input).expect("As of arkworks 0.3.0, this never fails.") + } +} diff --git a/manta-pay/src/crypto/prf/mod.rs b/manta-pay/src/crypto/prf/mod.rs new file mode 100644 index 000000000..2ff44a2ea --- /dev/null +++ b/manta-pay/src/crypto/prf/mod.rs @@ -0,0 +1,19 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Pseudorandom Function Family Implementations + +pub mod blake2s; diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index b768d7339..4379242e1 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -19,4 +19,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(docsrs, feature(doc_cfg), forbid(broken_intra_doc_links))] -pub mod ies; +extern crate alloc; + +pub mod accounting; +pub mod crypto; From 25f529699022d5ebbae7a9e0e113705aeaf39f54 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 8 Sep 2021 01:21:26 -0400 Subject: [PATCH 012/275] added derived secret key interfaces (hierarchical wallets) --- .../src/{account.rs => identity.rs} | 153 ++++++++----- manta-accounting/src/keys.rs | 216 ++++++++++++++++++ manta-accounting/src/ledger.rs | 8 +- manta-accounting/src/lib.rs | 5 +- manta-accounting/src/transfer.rs | 4 +- manta-accounting/src/wallet.rs | 109 ++++++--- manta-crypto/src/concat.rs | 2 +- manta-crypto/src/ies.rs | 2 +- manta-crypto/src/set.rs | 2 +- manta-pay/Cargo.toml | 1 + manta-pay/src/accounting/keys.rs | 31 +++ manta-pay/src/accounting/ledger.rs | 7 + manta-pay/src/accounting/mod.rs | 1 + manta-pay/src/crypto/ies.rs | 2 +- 14 files changed, 439 insertions(+), 104 deletions(-) rename manta-accounting/src/{account.rs => identity.rs} (90%) create mode 100644 manta-accounting/src/keys.rs create mode 100644 manta-pay/src/accounting/keys.rs diff --git a/manta-accounting/src/account.rs b/manta-accounting/src/identity.rs similarity index 90% rename from manta-accounting/src/account.rs rename to manta-accounting/src/identity.rs index 01458231a..c3dbbd808 100644 --- a/manta-accounting/src/account.rs +++ b/manta-accounting/src/identity.rs @@ -20,6 +20,7 @@ use crate::{ asset::{Asset, AssetBalance, AssetId}, + keys::SecretKeyGenerator, ledger::Ledger, }; use core::{convert::Infallible, fmt::Debug, hash::Hash, marker::PhantomData}; @@ -37,29 +38,17 @@ use rand::{ pub(crate) mod prelude { #[doc(inline)] - pub use crate::account::{ - Identity, IdentityConfiguration, Receiver, SecretKeyGenerator, SecretKeyGeneratorError, - Sender, SenderError, ShieldedIdentity, Spend, SpendError, Utxo, VoidNumber, + pub use super::{ + Identity, IdentityConfiguration, Receiver, Sender, SenderError, ShieldedIdentity, Spend, + SpendError, Utxo, VoidNumber, }; } -/// Secret Key Generator Trait -pub trait SecretKeyGenerator<SecretKey> { - /// Key Generation Error - type Error; - - /// Generates a new secret key. - fn generate_key(&mut self) -> Result<SecretKey, Self::Error>; -} - /// [`Identity`] Configuration Trait pub trait IdentityConfiguration { /// Secret Key Type type SecretKey: Clone; - /// Secret Key Generator Type - type SecretKeyGenerator: SecretKeyGenerator<Self::SecretKey>; - /// Pseudorandom Function Family Type type PseudorandomFunctionFamily: PseudorandomFunctionFamily<Seed = Self::SecretKey>; @@ -70,12 +59,6 @@ pub trait IdentityConfiguration { type Rng: CryptoRng + RngCore + SeedableRng<Seed = Self::SecretKey>; } -/// [`SecretKeyGenerator::Error`] Type Alias -pub type SecretKeyGeneratorError<C> = - <<C as IdentityConfiguration>::SecretKeyGenerator as SecretKeyGenerator< - <C as IdentityConfiguration>::SecretKey, - >>::Error; - /// [`PseudorandomFunctionFamily::Input`] Type Alias pub type PseudorandomFunctionFamilyInput<C> = <<C as IdentityConfiguration>::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Input; @@ -286,9 +269,10 @@ where /// Generates a new [`Identity`] from a secret key generation source. #[inline] - pub fn generate( - source: &mut C::SecretKeyGenerator, - ) -> Result<Self, SecretKeyGeneratorError<C>> { + pub fn generate<G>(source: &mut G) -> Result<Self, G::Error> + where + G: SecretKeyGenerator<SecretKey = C::SecretKey>, + { source.generate_key().map(Self::new) } @@ -462,13 +446,14 @@ where /// Generates a new [`Identity`] from a secret key generation source and builds a new /// [`Sender`] from it. #[inline] - pub fn generate_sender<S>( - source: &mut C::SecretKeyGenerator, + pub fn generate_sender<G, S>( + source: &mut G, commitment_scheme: &C::CommitmentScheme, asset: Asset, utxo_set: &S, - ) -> Result<Sender<C, S>, SenderError<C, S>> + ) -> Result<Sender<C, S>, SenderError<C, G, S>> where + G: SecretKeyGenerator<SecretKey = C::SecretKey>, S: VerifiedSet<Item = Utxo<C>>, Standard: Distribution<AssetParameters<C>>, PublicKey<C>: ConcatBytes, @@ -519,11 +504,12 @@ where /// Generates a new [`Identity`] from a secret key generation source and builds a new /// [`ShieldedIdentity`] from it. #[inline] - pub fn generate_shielded<I>( - source: &mut C::SecretKeyGenerator, + pub fn generate_shielded<G, I>( + source: &mut G, commitment_scheme: &C::CommitmentScheme, - ) -> Result<ShieldedIdentity<C, I>, SecretKeyGeneratorError<C>> + ) -> Result<ShieldedIdentity<C, I>, G::Error> where + G: SecretKeyGenerator<SecretKey = C::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, PublicKey<C>: ConcatBytes, @@ -548,10 +534,9 @@ where /// Generates a new [`Identity`] from a secret key generation source and builds a new /// [`Spend`] from it. #[inline] - pub fn generate_spend<I>( - source: &mut C::SecretKeyGenerator, - ) -> Result<Spend<C, I>, SecretKeyGeneratorError<C>> + pub fn generate_spend<G, I>(source: &mut G) -> Result<Spend<C, I>, G::Error> where + G: SecretKeyGenerator<SecretKey = C::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { @@ -580,13 +565,14 @@ where /// Generates a new [`Identity`] from a secret key generation source and builds a new /// [`ShieldedIdentity`]-[`Spend`] pair from it. - #[allow(clippy::type_complexity)] // NOTE: It's the generic parameters that make this complex. + #[allow(clippy::type_complexity)] // NOTE: This really is not that complex. #[inline] - pub fn generate_receiver<I>( - source: &mut C::SecretKeyGenerator, + pub fn generate_receiver<G, I>( + source: &mut G, commitment_scheme: &C::CommitmentScheme, - ) -> Result<(ShieldedIdentity<C, I>, Spend<C, I>), SecretKeyGeneratorError<C>> + ) -> Result<(ShieldedIdentity<C, I>, Spend<C, I>), G::Error> where + G: SecretKeyGenerator<SecretKey = C::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, PublicKey<C>: ConcatBytes, @@ -643,11 +629,12 @@ where /// Generates a new [`Identity`] from a secret key generation source and builds a /// [`ShieldedIdentity`] from it. #[inline] - pub fn generate( - source: &mut C::SecretKeyGenerator, + pub fn generate<G>( + source: &mut G, commitment_scheme: &C::CommitmentScheme, - ) -> Result<Self, SecretKeyGeneratorError<C>> + ) -> Result<Self, G::Error> where + G: SecretKeyGenerator<SecretKey = C::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, PublicKey<C>: ConcatBytes, @@ -759,13 +746,23 @@ where /// Generates a new [`Identity`] from a secret key generation source and builds a /// [`Spend`] from it. #[inline] - pub fn generate(source: &mut C::SecretKeyGenerator) -> Result<Self, SecretKeyGeneratorError<C>> + pub fn generate<G>(source: &mut G) -> Result<Self, G::Error> where + G: SecretKeyGenerator<SecretKey = C::SecretKey>, Standard: Distribution<AssetParameters<C>>, { Identity::generate_spend(source) } + /// Tries to open an `encrypted_asset` using `self`. + #[inline] + pub fn open(self, encrypted_asset: EncryptedMessage<I>) -> Result<OpenSpend<C>, I::Error> { + Ok(OpenSpend { + asset: self.asset_secret_key.decrypt(encrypted_asset)?, + identity: self.identity, + }) + } + /// Builds a new [`Sender`] for the given `encrypted_asset`. #[inline] pub fn into_sender<S>( @@ -781,14 +778,9 @@ where VoidNumberGenerator<C>: ConcatBytes, VoidNumberCommitment<C>: ConcatBytes, { - self.identity - .into_sender( - commitment_scheme, - self.asset_secret_key - .decrypt(encrypted_asset) - .map_err(SpendError::EncryptionError)?, - utxo_set, - ) + self.open(encrypted_asset) + .map_err(SpendError::EncryptionError)? + .into_sender(commitment_scheme, utxo_set) .map_err(SpendError::MissingUtxo) } } @@ -805,26 +797,66 @@ where } } +/// Open [`Spend`] +pub struct OpenSpend<C> +where + C: IdentityConfiguration, +{ + /// Spender Identity + identity: Identity<C>, + + /// Unencrypted [`Asset`] + asset: Asset, +} + +impl<C> OpenSpend<C> +where + C: IdentityConfiguration, +{ + /// Builds a new [`Sender`] for `self`. + #[inline] + pub fn into_sender<S>( + self, + commitment_scheme: &C::CommitmentScheme, + utxo_set: &S, + ) -> Result<Sender<C, S>, S::ContainmentError> + where + S: VerifiedSet<Item = Utxo<C>>, + Standard: Distribution<AssetParameters<C>>, + PublicKey<C>: ConcatBytes, + VoidNumberGenerator<C>: ConcatBytes, + VoidNumberCommitment<C>: ConcatBytes, + { + self.identity + .into_sender(commitment_scheme, self.asset, utxo_set) + } +} + /// Sender Error #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "SecretKeyGeneratorError<C>: Clone, S::ContainmentError: Clone"), - Copy(bound = "SecretKeyGeneratorError<C>: Copy, S::ContainmentError: Copy"), - Debug(bound = "SecretKeyGeneratorError<C>: Debug, S::ContainmentError: Debug"), - Eq(bound = "SecretKeyGeneratorError<C>: Eq, S::ContainmentError: Eq"), - Hash(bound = "SecretKeyGeneratorError<C>: Hash, S::ContainmentError: Hash"), - PartialEq(bound = "SecretKeyGeneratorError<C>: PartialEq, S::ContainmentError: PartialEq") + Clone(bound = "G::Error: Clone, S::ContainmentError: Clone"), + Copy(bound = "G::Error: Copy, S::ContainmentError: Copy"), + Debug(bound = "G::Error: Debug, S::ContainmentError: Debug"), + Eq(bound = "G::Error: Eq, S::ContainmentError: Eq"), + Hash(bound = "G::Error: Hash, S::ContainmentError: Hash"), + PartialEq(bound = "G::Error: PartialEq, S::ContainmentError: PartialEq") )] -pub enum SenderError<C, S> +pub enum SenderError<C, G, S> where C: IdentityConfiguration, + G: SecretKeyGenerator<SecretKey = C::SecretKey>, S: VerifiedSet<Item = Utxo<C>>, { /// Secret Key Generator Error - SecretKeyError(SecretKeyGeneratorError<C>), + SecretKeyError(G::Error), /// Containment Error MissingUtxo(S::ContainmentError), + + /// Parameter Marker + #[doc(hidden)] + __(Infallible, PhantomData<C>), } /// Sender @@ -877,13 +909,14 @@ where /// Generates a new [`Identity`] from a secret key generation source and builds a new /// [`Sender`] from it. #[inline] - pub fn generate( - source: &mut C::SecretKeyGenerator, + pub fn generate<G>( + source: &mut G, commitment_scheme: &C::CommitmentScheme, asset: Asset, utxo_set: &S, - ) -> Result<Self, SenderError<C, S>> + ) -> Result<Self, SenderError<C, G, S>> where + G: SecretKeyGenerator<SecretKey = C::SecretKey>, Standard: Distribution<AssetParameters<C>>, PublicKey<C>: ConcatBytes, VoidNumberGenerator<C>: ConcatBytes, diff --git a/manta-accounting/src/keys.rs b/manta-accounting/src/keys.rs new file mode 100644 index 000000000..0db140dc2 --- /dev/null +++ b/manta-accounting/src/keys.rs @@ -0,0 +1,216 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Secret Key Generation Primitives + +// NOTE: These interfaces are based on BIP-0044. See the specification here: +// https://raw.githubusercontent.com/bitcoin/bips/master/bip-0044.mediawiki + +// TODO: Check to make sure we conform to the specification and then make a note about it in the +// module documentation, and add a link to the specification. + +/// Secret Key Generator Trait +pub trait SecretKeyGenerator { + /// Secret Key Type + type SecretKey; + + /// Key Generation Error + type Error; + + /// Generates a new secret key. + fn generate_key(&mut self) -> Result<Self::SecretKey, Self::Error>; +} + +/// Derived Secret Key Parameter +pub trait DerivedSecretKeyParameter: Default { + /// Increments the key parameter by one unit. + fn increment(&mut self); +} + +/// Derived Secret Key Generator +pub trait DerivedSecretKeyGenerator { + /// Secret Key Type + type SecretKey; + + /// Account Type + type Account: DerivedSecretKeyParameter; + + /// Index Type + type Index: DerivedSecretKeyParameter; + + /// Key Generation Error + type Error; + + /// Generates a new secret key determined by `is_external` for the `account` with + /// the given `index`. + fn generate_key( + &self, + is_external: bool, + account: &Self::Account, + index: &Self::Index, + ) -> Result<Self::SecretKey, Self::Error>; + + /// Builds a [`SecretKeyGenerator`] for external keys associated to `account`. + #[inline] + fn external_keys<'s>(&'s self, account: &'s Self::Account) -> ExternalKeys<'s, Self> { + ExternalKeys::new(self, account) + } + + /// Builds a [`SecretKeyGenerator`] for internal keys associated to `account`. + #[inline] + fn internal_keys<'s>(&'s self, account: &'s Self::Account) -> InternalKeys<'s, Self> { + InternalKeys::new(self, account) + } +} + +/// Keys +struct Keys<'d, D> +where + D: DerivedSecretKeyGenerator + ?Sized, +{ + /// Derived Key Generator + derived_key_generator: &'d D, + + /// Key Account + account: &'d D::Account, + + /// Current Index + index: D::Index, +} + +impl<'d, D> Keys<'d, D> +where + D: DerivedSecretKeyGenerator + ?Sized, +{ + /// Builds a new [`Keys`] generator from a [`DerivedSecretKeyGenerator`] and an `account`. + #[inline] + fn new(derived_key_generator: &'d D, account: &'d D::Account) -> Self { + Self { + derived_key_generator, + account, + index: Default::default(), + } + } + + /// Generates a secret key according to the [`DerivedSecretKeyGenerator`] protocol and + /// increments the running `self.index`. + #[inline] + fn generate_key(&mut self, is_external: bool) -> Result<D::SecretKey, D::Error> { + let secret_key = + self.derived_key_generator + .generate_key(is_external, self.account, &self.index)?; + self.index.increment(); + Ok(secret_key) + } + + /// Generates an external secret key according to the [`DerivedSecretKeyGenerator`] protocol + /// and increments the running `self.index`. + #[inline] + fn generate_external_key(&mut self) -> Result<D::SecretKey, D::Error> { + self.generate_key(true) + } + + /// Generates an internal secret key according to the [`DerivedSecretKeyGenerator`] protocol + /// and increments the running `self.index`. + #[inline] + fn generate_internal_key(&mut self) -> Result<D::SecretKey, D::Error> { + self.generate_key(false) + } +} + +/// External Keys +pub struct ExternalKeys<'d, D>(Keys<'d, D>) +where + D: DerivedSecretKeyGenerator + ?Sized; + +impl<'d, D> ExternalKeys<'d, D> +where + D: DerivedSecretKeyGenerator + ?Sized, +{ + /// Builds a new [`ExternalKeys`] generator for `account` from a `source`. + #[inline] + pub fn new(source: &'d D, account: &'d D::Account) -> Self { + Self(Keys::new(source, account)) + } +} + +impl<'d, D> SecretKeyGenerator for ExternalKeys<'d, D> +where + D: DerivedSecretKeyGenerator + ?Sized, +{ + type SecretKey = D::SecretKey; + + type Error = D::Error; + + #[inline] + fn generate_key(&mut self) -> Result<Self::SecretKey, Self::Error> { + self.0.generate_external_key() + } +} + +impl<'d, D> Iterator for ExternalKeys<'d, D> +where + D: DerivedSecretKeyGenerator + ?Sized, +{ + type Item = D::SecretKey; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + self.generate_key().ok() + } +} + +/// Internal Keys +pub struct InternalKeys<'d, D>(Keys<'d, D>) +where + D: DerivedSecretKeyGenerator + ?Sized; + +impl<'d, D> InternalKeys<'d, D> +where + D: DerivedSecretKeyGenerator + ?Sized, +{ + /// Builds a new [`InternalKeys`] generator for `account` from a `source`. + #[inline] + pub fn new(source: &'d D, account: &'d D::Account) -> Self { + Self(Keys::new(source, account)) + } +} + +impl<'d, D> SecretKeyGenerator for InternalKeys<'d, D> +where + D: DerivedSecretKeyGenerator + ?Sized, +{ + type SecretKey = D::SecretKey; + + type Error = D::Error; + + #[inline] + fn generate_key(&mut self) -> Result<Self::SecretKey, Self::Error> { + self.0.generate_internal_key() + } +} + +impl<'d, D> Iterator for InternalKeys<'d, D> +where + D: DerivedSecretKeyGenerator + ?Sized, +{ + type Item = D::SecretKey; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + self.generate_key().ok() + } +} diff --git a/manta-accounting/src/ledger.rs b/manta-accounting/src/ledger.rs index ddf0ca711..84b488c02 100644 --- a/manta-accounting/src/ledger.rs +++ b/manta-accounting/src/ledger.rs @@ -16,8 +16,8 @@ //! Ledger Abstraction -use crate::account::{ReceiverPostError, SenderPostError}; -use manta_crypto::VerifiedSet; +use crate::identity::{ReceiverPostError, SenderPostError}; +use manta_crypto::{Set, VerifiedSet}; /// Ledger Trait pub trait Ledger { @@ -36,8 +36,6 @@ pub trait Ledger { /// Returns a shared reference to the [`UtxoSet`](Self::UtxoSet). fn utxos(&self) -> &Self::UtxoSet; - /* TODO: do we want these methods? - /// Returns `true` if the `void_number` corresponding to some asset /// __is not stored__ on `self`. fn is_unspent(&self, void_number: &Self::VoidNumber) -> bool; @@ -56,8 +54,6 @@ pub trait Ledger { self.is_registered(utxo) && self.is_unspent(void_number) } - */ - /// Checks if the `public_input` corresponding to a UTXO containment proof represents the current /// state of the [`UtxoSet`](Self::UtxoSet), returning it back if not. #[inline] diff --git a/manta-accounting/src/lib.rs b/manta-accounting/src/lib.rs index 333c1ac1a..e8a34bb62 100644 --- a/manta-accounting/src/lib.rs +++ b/manta-accounting/src/lib.rs @@ -25,10 +25,11 @@ extern crate derive_more; mod asset; mod ledger; -pub mod account; +pub mod identity; +pub mod keys; pub mod transfer; pub mod wallet; -pub use account::prelude::*; pub use asset::*; +pub use identity::prelude::*; pub use ledger::*; diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 8de7b50f1..1a9c8fe58 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -17,10 +17,10 @@ //! Transfer Protocols use crate::{ - account::{ + asset::{sample_asset_balances, Asset, AssetBalance, AssetBalances, AssetId}, + identity::{ IdentityConfiguration, Receiver, ReceiverPost, Sender, SenderPost, Utxo, VoidNumber, }, - asset::{sample_asset_balances, Asset, AssetBalance, AssetBalances, AssetId}, ledger::{Ledger, PostError}, }; use manta_crypto::{ies::EncryptedMessage, IntegratedEncryptionScheme, VerifiedSet}; diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index db51782a7..ae8355145 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -16,18 +16,24 @@ //! Wallet Abstractions +// TODO: How to manage accounts? Wallet should have a fixed account or not? + use crate::{ - account::{ - AssetParameters, PublicKey, SecretKeyGeneratorError, Sender, ShieldedIdentity, - VoidNumberCommitment, VoidNumberGenerator, - }, asset::{Asset, AssetBalance, AssetId}, + identity::{ + AssetParameters, IdentityConfiguration, OpenSpend, PublicKey, Sender, ShieldedIdentity, + Spend, VoidNumberCommitment, VoidNumberGenerator, + }, + keys::DerivedSecretKeyGenerator, ledger::Ledger, transfer::{SecretTransfer, SecretTransferConfiguration}, }; use core::convert::Infallible; -use manta_crypto::ConcatBytes; -use rand::distributions::{Distribution, Standard}; +use manta_crypto::{ies::EncryptedMessage, ConcatBytes, IntegratedEncryptionScheme}; +use rand::{ + distributions::{Distribution, Standard}, + CryptoRng, RngCore, +}; /// Asset Map pub trait AssetMap { @@ -113,13 +119,16 @@ pub trait AssetMap { } /// Wallet -pub struct Wallet<T, M> +pub struct Wallet<D, M> where - T: SecretTransferConfiguration, + D: DerivedSecretKeyGenerator, M: AssetMap, { /// Secret Key Source - secret_key_source: T::SecretKeyGenerator, + secret_key_source: D, + + /// Wallet Account + account: D::Account, /// Public Asset Map public_assets: M, @@ -128,87 +137,127 @@ where secret_assets: M, } -impl<T, M> Wallet<T, M> +impl<D, M> Wallet<D, M> where - T: SecretTransferConfiguration, + D: DerivedSecretKeyGenerator, M: AssetMap, { - /// Builds a new [`Wallet`] from a `secret_key_source`. + /// Builds a new [`Wallet`] for `account` from a `secret_key_source`. #[inline] - pub fn new(secret_key_source: T::SecretKeyGenerator) -> Self + pub fn new(secret_key_source: D, account: D::Account) -> Self where M: Default, { - Self::with_balances(secret_key_source, Default::default(), Default::default()) + Self::with_balances( + secret_key_source, + account, + Default::default(), + Default::default(), + ) } - /// Builds a new [`Wallet`] from a `secret_key_source` and pre-built + /// Builds a new [`Wallet`] for `account` from a `secret_key_source` and pre-built /// `public_assets` map and `secret_assets` map. #[inline] pub fn with_balances( - secret_key_source: T::SecretKeyGenerator, + secret_key_source: D, + account: D::Account, public_assets: M, secret_assets: M, ) -> Self { Self { secret_key_source, + account, public_assets, secret_assets, } } + /// Looks for an [`OpenSpend`] for this `encrypted_asset`. + pub fn find_open_spend<C, I>( + &self, + encrypted_asset: EncryptedMessage<I>, + gap_limit: usize, + ) -> Option<OpenSpend<C>> + where + C: IdentityConfiguration<SecretKey = D::SecretKey>, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + Standard: Distribution<AssetParameters<C>>, + { + // TODO: Report a more useful error. + + /* FIXME: Is this the right implementation? + let mut external = self.secret_key_source.external_keys(&self.account); + let mut internal = self.secret_key_source.internal_keys(&self.account); + for _ in 0..gap_limit { + if let Ok(sender) = Spend::generate(&mut external).ok()?.into_sender() { + return Some(sender); + } + if let Ok(sender) = Spend::generate(&mut internal).ok()?.into_sender() { + return Some(sender); + } + } + None + */ + + let _ = (encrypted_asset, gap_limit); + todo!() + } + /// Updates `self` with new information from the ledger. pub fn pull_updates<L>(&mut self, ledger: &L) where L: Ledger, { // TODO: pull updates from the ledger - // - new void numbers? - // - new utxos? - // - new encrypted notes? + // 1. Download the new encrypted notes and try to decrypt them using the latest + // keys that haven't been used. + // 2. Download the new vns and utxos and check that we can still spend all the + // tokens we think we can spend. + // 3. compute the new deposits and withdrawls let _ = ledger; todo!() } /// Generates a new [`ShieldedIdentity`] to receive assets to this wallet. #[inline] - pub fn generate_receiver( + pub fn generate_receiver<T>( &mut self, commitment_scheme: &T::CommitmentScheme, - ) -> Result<ShieldedIdentity<T, T::IntegratedEncryptionScheme>, SecretKeyGeneratorError<T>> + ) -> Result<ShieldedIdentity<T, T::IntegratedEncryptionScheme>, D::Error> where + T: SecretTransferConfiguration, Standard: Distribution<AssetParameters<T>>, PublicKey<T>: ConcatBytes, VoidNumberGenerator<T>: ConcatBytes, { - // FIXME: shouldn't we also produce a `Spend`? since we are modifying the secret key source, - // we can't go back and discover what the actual "derived key path" was except to go into - // recovery mode - ShieldedIdentity::generate(&mut self.secret_key_source, commitment_scheme) + // FIXME: ShieldedIdentity::generate(&mut self.secret_key_source, commitment_scheme) + let _ = commitment_scheme; + todo!() } /// Builds a [`SecretTransfer`] transaction to send `asset` to `receiver`. - pub fn secret_send( + pub fn secret_send<T, R>( &self, commitment_scheme: &T::CommitmentScheme, asset: Asset, receiver: ShieldedIdentity<T, T::IntegratedEncryptionScheme>, + rng: &mut R, ) -> Option<SecretTransfer<T, 2, 2>> where + T: SecretTransferConfiguration, VoidNumberCommitment<T>: ConcatBytes, + R: CryptoRng + RngCore + ?Sized, { // TODO: spec: // 1. check that we have enough `asset` in the secret_assets map // 2. find out which keys have control over `asset` // 3. build two senders and build a receiver and a change receiver for the extra change - // TODO: which `rng` do we use for the receivers? - /* let sender = Sender::generate(self.secret_key_source, commitment_scheme); - let receiver = receiver.into_receiver(commitment_scheme, asset, rng); */ - let _ = (commitment_scheme, asset, receiver); + let _ = receiver.into_receiver(commitment_scheme, asset, rng); todo!() } } diff --git a/manta-crypto/src/concat.rs b/manta-crypto/src/concat.rs index dba3ca3d0..3f0709bb5 100644 --- a/manta-crypto/src/concat.rs +++ b/manta-crypto/src/concat.rs @@ -44,7 +44,7 @@ pub trait ByteAccumulator { self } - /// Creates a "by mutable reference" adaptor for this instance of [`Accumulator`]. + /// Creates a "by mutable reference" adaptor for this instance of [`ByteAccumulator`]. #[inline] fn by_ref(&mut self) -> &mut Self where diff --git a/manta-crypto/src/ies.rs b/manta-crypto/src/ies.rs index 0a0d17153..2817bc4bf 100644 --- a/manta-crypto/src/ies.rs +++ b/manta-crypto/src/ies.rs @@ -23,7 +23,7 @@ use rand::{CryptoRng, RngCore}; pub(crate) mod prelude { #[doc(inline)] - pub use crate::ies::IntegratedEncryptionScheme; + pub use super::IntegratedEncryptionScheme; } /// [`IntegratedEncryptionScheme`] Public Key diff --git a/manta-crypto/src/set.rs b/manta-crypto/src/set.rs index d919af234..a66aa5511 100644 --- a/manta-crypto/src/set.rs +++ b/manta-crypto/src/set.rs @@ -18,7 +18,7 @@ pub(crate) mod prelude { #[doc(inline)] - pub use crate::set::{Set, VerifiedSet}; + pub use super::{Set, VerifiedSet}; } /// Set Trait diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 16f6851ed..20e99515f 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -30,6 +30,7 @@ std = [] aes-gcm = { version = "0.9.4" } ark-crypto-primitives = { version = "0.3.0", default-features = false } ark-ed-on-bls12-381 = { version = "0.3.0", default-features = false } +bip32 = { version = "0.2.2", default-features = false } blake2 = { version = "0.9.2", default-features = false } generic-array = { version = "0.14.4", default-features = false } manta-accounting = { path = "../manta-accounting", default-features = false } diff --git a/manta-pay/src/accounting/keys.rs b/manta-pay/src/accounting/keys.rs new file mode 100644 index 000000000..cf44f8718 --- /dev/null +++ b/manta-pay/src/accounting/keys.rs @@ -0,0 +1,31 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Secret Key Generator Implementations + +// TODO: Use the `bip32` crate to implement wallet key generators + +/// BIP-0044 Purpose Id +pub const BIP_44_PURPOSE_ID: u32 = 44; + +/// Manta Coin Type Id +pub const MANTA_COIN_TYPE_ID: u32 = 611; + +/// Calamary Coin Type Id +pub const CALAMARI_COIN_TYPE_ID: u32 = 612; + +/// Testnet Coin Type Id +pub const TESTNET_COIN_TYPE_ID: u32 = 1; diff --git a/manta-pay/src/accounting/ledger.rs b/manta-pay/src/accounting/ledger.rs index a29b8048c..a654109e6 100644 --- a/manta-pay/src/accounting/ledger.rs +++ b/manta-pay/src/accounting/ledger.rs @@ -176,6 +176,13 @@ impl LedgerTrait for Ledger { &self.utxos } + #[inline] + fn is_unspent(&self, void_number: &Self::VoidNumber) -> bool { + // TODO: !self.void_numbers.contains(void_number) + let _ = void_number; + todo!() + } + #[inline] fn try_post_void_number( &mut self, diff --git a/manta-pay/src/accounting/mod.rs b/manta-pay/src/accounting/mod.rs index 29873f974..bf3f63b3e 100644 --- a/manta-pay/src/accounting/mod.rs +++ b/manta-pay/src/accounting/mod.rs @@ -16,4 +16,5 @@ //! Accounting Implementations +pub mod keys; pub mod ledger; diff --git a/manta-pay/src/crypto/ies.rs b/manta-pay/src/crypto/ies.rs index 1e3e07f63..79573f7db 100644 --- a/manta-pay/src/crypto/ies.rs +++ b/manta-pay/src/crypto/ies.rs @@ -169,7 +169,7 @@ impl IntegratedEncryptionScheme for IES { /// Tests encryption/decryption of an asset. #[test] -pub fn encryption_decryption() { +fn encryption_decryption() { use rand::{thread_rng, Rng, SeedableRng}; use rand_chacha::ChaCha20Rng; From ea9ab222b7cd09effd56bd358b86935d1b84a605 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 8 Sep 2021 13:32:23 -0400 Subject: [PATCH 013/275] improved key generation interface and added encrypted asset search --- manta-accounting/src/identity.rs | 41 ++++---- manta-accounting/src/keys.rs | 81 ++++++++++++--- manta-accounting/src/wallet.rs | 165 ++++++++++++++++++++++++------- manta-crypto/src/ies.rs | 34 ++++--- manta-pay/src/crypto/ies.rs | 8 +- 5 files changed, 245 insertions(+), 84 deletions(-) diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index c3dbbd808..b2664991a 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -256,14 +256,9 @@ impl<C> Identity<C> where C: IdentityConfiguration, { - /// Generates a new `Identity` from a `C::SecretKey`. - /// - /// # API Note - /// - /// This function is intentionally private so that secret keys are not part of the - /// public interface. + /// Generates a new [`Identity`] from a [`C::SecretKey`](IdentityConfiguration::SecretKey). #[inline] - fn new(secret_key: C::SecretKey) -> Self { + pub fn new(secret_key: C::SecretKey) -> Self { Self { secret_key } } @@ -280,8 +275,8 @@ where /// /// # API Note /// - /// This function is intentionally private so that random number generators are not part of - /// the public interface. See [`Self::parameters`] for access to the associated + /// This function is intentionally private so that internal random number generator is + /// not part of the public interface. See [`Self::parameters`] for access to the associated /// `parameters`. /// /// # Implementation Note @@ -323,9 +318,9 @@ where /// /// # API Note /// - /// This function is intentionally private so that random number generators are not part of - /// the public interface. See [`Self::parameters_and_asset_keypair`] for access to the - /// associated `parameters` and `asset_keypair`. + /// This function is intentionally private so that the internal random number generator is + /// not part of the public interface. See [`Self::parameters_and_asset_keypair`] for access to + /// the associated `parameters` and `asset_keypair`. /// /// # Implementation Note /// @@ -565,7 +560,7 @@ where /// Generates a new [`Identity`] from a secret key generation source and builds a new /// [`ShieldedIdentity`]-[`Spend`] pair from it. - #[allow(clippy::type_complexity)] // NOTE: This really is not that complex. + #[allow(clippy::type_complexity)] // NOTE: This is not very complex. #[inline] pub fn generate_receiver<G, I>( source: &mut G, @@ -661,7 +656,7 @@ where asset_public_key, } = self; Ok(Receiver { - encrypted_asset: asset_public_key.encrypt(asset, rng)?, + encrypted_asset: asset_public_key.encrypt(&asset, rng)?, utxo: generate_utxo::<C>( commitment_scheme, &asset, @@ -675,7 +670,12 @@ where } } -/// [`Spend`] Error +/// Spend Error +/// +/// This `enum` is the error state for the [`into_sender`] method on [`Spend`]. +/// See its documentation for more. +/// +/// [`into_sender`]: Spend::into_sender #[derive(derivative::Derivative)] #[derivative( Clone(bound = "I::Error: Clone, S::ContainmentError: Clone"), @@ -756,7 +756,7 @@ where /// Tries to open an `encrypted_asset` using `self`. #[inline] - pub fn open(self, encrypted_asset: EncryptedMessage<I>) -> Result<OpenSpend<C>, I::Error> { + pub fn try_open(self, encrypted_asset: &EncryptedMessage<I>) -> Result<OpenSpend<C>, I::Error> { Ok(OpenSpend { asset: self.asset_secret_key.decrypt(encrypted_asset)?, identity: self.identity, @@ -778,7 +778,7 @@ where VoidNumberGenerator<C>: ConcatBytes, VoidNumberCommitment<C>: ConcatBytes, { - self.open(encrypted_asset) + self.try_open(&encrypted_asset) .map_err(SpendError::EncryptionError)? .into_sender(commitment_scheme, utxo_set) .map_err(SpendError::MissingUtxo) @@ -833,6 +833,13 @@ where } /// Sender Error +/// +/// This `enum` is the error state for the [`generate_sender`] method on [`Identity`] +/// and the [`generate`] method on [`Sender`]. +/// See their documentation for more. +/// +/// [`generate_sender`]: Identity::generate_sender +/// [`generate`]: Sender::generate #[derive(derivative::Derivative)] #[derivative( Clone(bound = "G::Error: Clone, S::ContainmentError: Clone"), diff --git a/manta-accounting/src/keys.rs b/manta-accounting/src/keys.rs index 0db140dc2..4a200e34f 100644 --- a/manta-accounting/src/keys.rs +++ b/manta-accounting/src/keys.rs @@ -76,6 +76,51 @@ pub trait DerivedSecretKeyGenerator { } } +/// Generates an internal or external secret key according to the [`DerivedSecretKeyGenerator`] +/// protocol and increments the running `index`. +#[inline] +fn next_key<D>( + source: &D, + is_external: bool, + account: &D::Account, + index: &mut D::Index, +) -> Result<D::SecretKey, D::Error> +where + D: DerivedSecretKeyGenerator + ?Sized, +{ + let secret_key = source.generate_key(is_external, account, index)?; + index.increment(); + Ok(secret_key) +} + +/// Generates an external secret key according to the [`DerivedSecretKeyGenerator`] protocol +/// and increments the running `index`. +#[inline] +pub fn next_external<D>( + source: &D, + account: &D::Account, + index: &mut D::Index, +) -> Result<D::SecretKey, D::Error> +where + D: DerivedSecretKeyGenerator + ?Sized, +{ + next_key(source, true, account, index) +} + +/// Generates an internal secret key according to the [`DerivedSecretKeyGenerator`] protocol +/// and increments the running `index`. +#[inline] +pub fn next_internal<D>( + source: &D, + account: &D::Account, + index: &mut D::Index, +) -> Result<D::SecretKey, D::Error> +where + D: DerivedSecretKeyGenerator + ?Sized, +{ + next_key(source, false, account, index) +} + /// Keys struct Keys<'d, D> where @@ -98,36 +143,32 @@ where /// Builds a new [`Keys`] generator from a [`DerivedSecretKeyGenerator`] and an `account`. #[inline] fn new(derived_key_generator: &'d D, account: &'d D::Account) -> Self { + Self::from_index(derived_key_generator, account, Default::default()) + } + + /// Builds a new [`Keys`] generator from a [`DerivedSecretKeyGenerator`] and an `account`, + /// starting at `index`. + #[inline] + fn from_index(derived_key_generator: &'d D, account: &'d D::Account, index: D::Index) -> Self { Self { derived_key_generator, account, - index: Default::default(), + index, } } - /// Generates a secret key according to the [`DerivedSecretKeyGenerator`] protocol and - /// increments the running `self.index`. - #[inline] - fn generate_key(&mut self, is_external: bool) -> Result<D::SecretKey, D::Error> { - let secret_key = - self.derived_key_generator - .generate_key(is_external, self.account, &self.index)?; - self.index.increment(); - Ok(secret_key) - } - /// Generates an external secret key according to the [`DerivedSecretKeyGenerator`] protocol /// and increments the running `self.index`. #[inline] fn generate_external_key(&mut self) -> Result<D::SecretKey, D::Error> { - self.generate_key(true) + next_external(self.derived_key_generator, self.account, &mut self.index) } /// Generates an internal secret key according to the [`DerivedSecretKeyGenerator`] protocol /// and increments the running `self.index`. #[inline] fn generate_internal_key(&mut self) -> Result<D::SecretKey, D::Error> { - self.generate_key(false) + next_internal(self.derived_key_generator, self.account, &mut self.index) } } @@ -145,6 +186,12 @@ where pub fn new(source: &'d D, account: &'d D::Account) -> Self { Self(Keys::new(source, account)) } + + /// Builds a new [`ExternalKeys`] generator for `account` from a `source`, starting at `index`. + #[inline] + pub fn from_index(source: &'d D, account: &'d D::Account, index: D::Index) -> Self { + Self(Keys::from_index(source, account, index)) + } } impl<'d, D> SecretKeyGenerator for ExternalKeys<'d, D> @@ -187,6 +234,12 @@ where pub fn new(source: &'d D, account: &'d D::Account) -> Self { Self(Keys::new(source, account)) } + + /// Builds a new [`InternalKeys`] generator for `account` from a `source`, starting at `index`. + #[inline] + pub fn from_index(source: &'d D, account: &'d D::Account, index: D::Index) -> Self { + Self(Keys::from_index(source, account, index)) + } } impl<'d, D> SecretKeyGenerator for InternalKeys<'d, D> diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index ae8355145..4c824d386 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -21,14 +21,14 @@ use crate::{ asset::{Asset, AssetBalance, AssetId}, identity::{ - AssetParameters, IdentityConfiguration, OpenSpend, PublicKey, Sender, ShieldedIdentity, - Spend, VoidNumberCommitment, VoidNumberGenerator, + AssetParameters, Identity, IdentityConfiguration, OpenSpend, PublicKey, Receiver, Sender, + ShieldedIdentity, Spend, VoidNumberCommitment, VoidNumberGenerator, }, - keys::DerivedSecretKeyGenerator, + keys::{self, DerivedSecretKeyGenerator}, ledger::Ledger, transfer::{SecretTransfer, SecretTransferConfiguration}, }; -use core::convert::Infallible; +use core::{convert::Infallible, fmt::Debug, hash::Hash}; use manta_crypto::{ies::EncryptedMessage, ConcatBytes, IntegratedEncryptionScheme}; use rand::{ distributions::{Distribution, Standard}, @@ -130,6 +130,12 @@ where /// Wallet Account account: D::Account, + /// External Transaction Running Index + external_index: D::Index, + + /// Internal Transaction Running Index + internal_index: D::Index, + /// Public Asset Map public_assets: M, @@ -168,40 +174,74 @@ where Self { secret_key_source, account, + external_index: Default::default(), + internal_index: Default::default(), public_assets, secret_assets, } } - /// Looks for an [`OpenSpend`] for this `encrypted_asset`. + /// Generates the next external key for this wallet. + #[inline] + fn next_external_key(&mut self) -> Result<D::SecretKey, D::Error> { + keys::next_external( + &self.secret_key_source, + &self.account, + &mut self.external_index, + ) + } + + /// Generates the next internal key for this wallet. + #[inline] + fn next_internal_key(&mut self) -> Result<D::SecretKey, D::Error> { + keys::next_internal( + &self.secret_key_source, + &self.account, + &mut self.internal_index, + ) + } + + /// Generates the next external identity for this wallet. + #[inline] + fn next_external_identity<C>(&mut self) -> Result<Identity<C>, D::Error> + where + C: IdentityConfiguration<SecretKey = D::SecretKey>, + { + self.next_external_key().map(Identity::new) + } + + /// Generates the next internal identity for this wallet. + #[inline] + fn next_internal_identity<C>(&mut self) -> Result<Identity<C>, D::Error> + where + C: IdentityConfiguration<SecretKey = D::SecretKey>, + { + self.next_internal_key().map(Identity::new) + } + + /// Looks for an [`OpenSpend`] for this `encrypted_asset`, only trying `gap_limit`-many + /// external and internal keys. pub fn find_open_spend<C, I>( &self, encrypted_asset: EncryptedMessage<I>, gap_limit: usize, - ) -> Option<OpenSpend<C>> + ) -> Result<Option<OpenSpend<C>>, D::Error> where C: IdentityConfiguration<SecretKey = D::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { - // TODO: Report a more useful error. - - /* FIXME: Is this the right implementation? let mut external = self.secret_key_source.external_keys(&self.account); let mut internal = self.secret_key_source.internal_keys(&self.account); for _ in 0..gap_limit { - if let Ok(sender) = Spend::generate(&mut external).ok()?.into_sender() { - return Some(sender); + if let Ok(opened) = Spend::generate(&mut external)?.try_open(&encrypted_asset) { + return Ok(Some(opened)); } - if let Ok(sender) = Spend::generate(&mut internal).ok()?.into_sender() { - return Some(sender); + if let Ok(opened) = Spend::generate(&mut internal)?.try_open(&encrypted_asset) { + return Ok(Some(opened)); } } - None - */ - - let _ = (encrypted_asset, gap_limit); - todo!() + Ok(None) } /// Updates `self` with new information from the ledger. @@ -219,35 +259,67 @@ where todo!() } - /// Generates a new [`ShieldedIdentity`] to receive assets to this wallet. + /// Generates a new [`ShieldedIdentity`] to receive assets to this wallet via an external + /// transaction. #[inline] - pub fn generate_receiver<T>( + pub fn generate_external_receiver<C, I>( &mut self, - commitment_scheme: &T::CommitmentScheme, - ) -> Result<ShieldedIdentity<T, T::IntegratedEncryptionScheme>, D::Error> + commitment_scheme: &C::CommitmentScheme, + ) -> Result<ShieldedIdentity<C, I>, D::Error> where - T: SecretTransferConfiguration, - Standard: Distribution<AssetParameters<T>>, - PublicKey<T>: ConcatBytes, - VoidNumberGenerator<T>: ConcatBytes, + C: IdentityConfiguration<SecretKey = D::SecretKey>, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + Standard: Distribution<AssetParameters<C>>, + PublicKey<C>: ConcatBytes, + VoidNumberGenerator<C>: ConcatBytes, { - // FIXME: ShieldedIdentity::generate(&mut self.secret_key_source, commitment_scheme) - let _ = commitment_scheme; - todo!() + self.next_external_identity() + .map(move |identity| identity.into_shielded(commitment_scheme)) + } + + /// Generates a new [`Receiver`]-[`Spend`] pair to receive `asset` to this wallet via an + /// internal transaction. + #[allow(clippy::type_complexity)] // NOTE: This is not very complex. + #[inline] + pub fn generate_internal_receiver<C, I, R>( + &mut self, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + rng: &mut R, + ) -> Result<(Receiver<C, I>, Spend<C, I>), InternalReceiverError<D, I>> + where + C: IdentityConfiguration<SecretKey = D::SecretKey>, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + R: CryptoRng + RngCore + ?Sized, + Standard: Distribution<AssetParameters<C>>, + PublicKey<C>: ConcatBytes, + VoidNumberGenerator<C>: ConcatBytes, + VoidNumberCommitment<C>: ConcatBytes, + { + let (shielded_identity, spend) = self + .next_internal_identity() + .map_err(InternalReceiverError::SecretKeyGenerationError)? + .into_receiver(commitment_scheme); + Ok(( + shielded_identity + .into_receiver(commitment_scheme, asset, rng) + .map_err(InternalReceiverError::EncryptionError)?, + spend, + )) } - /// Builds a [`SecretTransfer`] transaction to send `asset` to `receiver`. + /// Builds a [`SecretTransfer`] transaction to send `asset` to an `external_receiver`. pub fn secret_send<T, R>( &self, commitment_scheme: &T::CommitmentScheme, asset: Asset, - receiver: ShieldedIdentity<T, T::IntegratedEncryptionScheme>, + external_receiver: ShieldedIdentity<T, T::IntegratedEncryptionScheme>, rng: &mut R, ) -> Option<SecretTransfer<T, 2, 2>> where T: SecretTransferConfiguration, - VoidNumberCommitment<T>: ConcatBytes, R: CryptoRng + RngCore + ?Sized, + VoidNumberCommitment<T>: ConcatBytes, { // TODO: spec: // 1. check that we have enough `asset` in the secret_assets map @@ -257,7 +329,34 @@ where /* let sender = Sender::generate(self.secret_key_source, commitment_scheme); */ - let _ = receiver.into_receiver(commitment_scheme, asset, rng); + let _ = external_receiver.into_receiver(commitment_scheme, asset, rng); todo!() } } + +/// Internal Receiver Error +/// +/// This `enum` is the error state for the [`generate_internal_receiver`] method on [`Wallet`]. +/// See its documentation for more. +/// +/// [`generate_internal_receiver`]: Wallet::generate_internal_receiver +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "D::Error: Clone, I::Error: Clone"), + Copy(bound = "D::Error: Copy, I::Error: Copy"), + Debug(bound = "D::Error: Debug, I::Error: Debug"), + Eq(bound = "D::Error: Eq, I::Error: Eq"), + Hash(bound = "D::Error: Hash, I::Error: Hash"), + PartialEq(bound = "D::Error: PartialEq, I::Error: PartialEq") +)] +pub enum InternalReceiverError<D, I> +where + D: DerivedSecretKeyGenerator, + I: IntegratedEncryptionScheme<Plaintext = Asset>, +{ + /// Secret Key Generation Error + SecretKeyGenerationError(D::Error), + + /// Encryption Error + EncryptionError(I::Error), +} diff --git a/manta-crypto/src/ies.rs b/manta-crypto/src/ies.rs index 2817bc4bf..029c1b294 100644 --- a/manta-crypto/src/ies.rs +++ b/manta-crypto/src/ies.rs @@ -39,10 +39,13 @@ impl<I> PublicKey<I> where I: IntegratedEncryptionScheme + ?Sized, { - /// Generates a new [`PublicKey`] from a - /// [`I::PublicKey`](IntegratedEncryptionScheme::PublicKey). + /// Generates a new [`PublicKey`] from `I::PublicKey`. + /// + /// # API Note + /// + /// This function is intentionally private so that only [`KeyPair`] can create this type. #[inline] - pub fn new(public_key: I::PublicKey) -> Self { + fn new(public_key: I::PublicKey) -> Self { Self { public_key } } @@ -50,7 +53,7 @@ where #[inline] pub fn encrypt<R>( self, - plaintext: I::Plaintext, + plaintext: &I::Plaintext, rng: &mut R, ) -> Result<EncryptedMessage<I>, I::Error> where @@ -73,12 +76,11 @@ impl<I> SecretKey<I> where I: IntegratedEncryptionScheme + ?Sized, { - /// Generates a new `SecretKey` from an `I::SecretKey`. + /// Generates a new [`SecretKey`] from `I::SecretKey`. /// /// # API Note /// - /// This function is intentionally private so that secret keys are not part of the - /// public interface. + /// This function is intentionally private, so that only `KeyPair` can create this type. #[inline] fn new(secret_key: I::SecretKey) -> Self { Self { secret_key } @@ -86,7 +88,7 @@ where /// Decrypts the `message` with `self`. #[inline] - pub fn decrypt(self, message: EncryptedMessage<I>) -> Result<I::Plaintext, I::Error> { + pub fn decrypt(self, message: &EncryptedMessage<I>) -> Result<I::Plaintext, I::Error> { message.decrypt(self) } } @@ -141,7 +143,7 @@ where #[inline] pub fn encrypt<R>( self, - plaintext: I::Plaintext, + plaintext: &I::Plaintext, rng: &mut R, ) -> Result<(EncryptedMessage<I>, SecretKey<I>), I::Error> where @@ -189,7 +191,7 @@ pub trait IntegratedEncryptionScheme { /// Encrypts the `plaintext` with the `public_key`, generating an [`EncryptedMessage`]. fn encrypt<R>( - plaintext: Self::Plaintext, + plaintext: &Self::Plaintext, public_key: Self::PublicKey, rng: &mut R, ) -> Result<EncryptedMessage<Self>, Self::Error> @@ -200,7 +202,7 @@ pub trait IntegratedEncryptionScheme { /// public key, generating an [`EncryptedMessage`] and a [`SecretKey`]. #[inline] fn keygen_encrypt<R>( - plaintext: Self::Plaintext, + plaintext: &Self::Plaintext, rng: &mut R, ) -> Result<(EncryptedMessage<Self>, SecretKey<Self>), Self::Error> where @@ -211,7 +213,7 @@ pub trait IntegratedEncryptionScheme { /// Decrypts the `ciphertext` with the `secret_key`. fn decrypt( - ciphertext: Self::Ciphertext, + ciphertext: &Self::Ciphertext, secret_key: Self::SecretKey, ) -> Result<Self::Plaintext, Self::Error>; } @@ -249,7 +251,7 @@ where /// Encrypts the `plaintext` with the `public_key`, generating an [`EncryptedMessage`]. #[inline] pub fn encrypt<R>( - plaintext: I::Plaintext, + plaintext: &I::Plaintext, public_key: I::PublicKey, rng: &mut R, ) -> Result<Self, I::Error> @@ -263,7 +265,7 @@ where /// public key, generating an [`EncryptedMessage`] and a [`SecretKey`]. #[inline] pub fn keygen_encrypt<R>( - plaintext: I::Plaintext, + plaintext: &I::Plaintext, rng: &mut R, ) -> Result<(Self, SecretKey<I>), I::Error> where @@ -274,7 +276,7 @@ where /// Decrypts `self` with the `secret_key`. #[inline] - pub fn decrypt(self, secret_key: SecretKey<I>) -> Result<I::Plaintext, I::Error> { - I::decrypt(self.ciphertext, secret_key.secret_key) + pub fn decrypt(&self, secret_key: SecretKey<I>) -> Result<I::Plaintext, I::Error> { + I::decrypt(&self.ciphertext, secret_key.secret_key) } } diff --git a/manta-pay/src/crypto/ies.rs b/manta-pay/src/crypto/ies.rs index 79573f7db..e0185deec 100644 --- a/manta-pay/src/crypto/ies.rs +++ b/manta-pay/src/crypto/ies.rs @@ -118,7 +118,7 @@ impl IntegratedEncryptionScheme for IES { } fn encrypt<R>( - plaintext: Self::Plaintext, + plaintext: &Self::Plaintext, public_key: Self::PublicKey, rng: &mut R, ) -> Result<EncryptedAsset, Self::Error> @@ -146,7 +146,7 @@ impl IntegratedEncryptionScheme for IES { } fn decrypt( - ciphertext: Self::Ciphertext, + ciphertext: &Self::Ciphertext, secret_key: Self::SecretKey, ) -> Result<Self::Plaintext, Self::Error> { let shared_secret = Self::blake2s_kdf( @@ -179,8 +179,8 @@ fn encryption_decryption() { let asset = rng.gen(); let reconstructed_asset = secret_key .decrypt( - public_key - .encrypt(asset, &mut rng) + &public_key + .encrypt(&asset, &mut rng) .expect("unable to encrypt asset"), ) .expect("unable to decrypt asset"); From 2e46d3b39f1aac1ddb563be63875f864b92c947e Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 8 Sep 2021 15:46:56 -0400 Subject: [PATCH 014/275] start setting up test framework and improve documentation build support --- manta-accounting/Cargo.toml | 7 ++++++- manta-accounting/src/identity.rs | 8 ++++---- manta-accounting/src/keys.rs | 24 ++++++++++++++++++++++- manta-accounting/src/lib.rs | 4 +++- manta-accounting/src/wallet.rs | 33 +++++++++++++++++++++++--------- manta-codec/Cargo.toml | 6 ++++-- manta-codec/src/lib.rs | 4 +++- manta-crypto/Cargo.toml | 8 +++++++- manta-crypto/src/ies.rs | 27 ++++++++++++++++++++++++++ manta-crypto/src/lib.rs | 4 +++- manta-pay/Cargo.toml | 5 ++++- manta-pay/src/crypto/ies.rs | 33 ++++++++++++++------------------ manta-pay/src/lib.rs | 4 +++- manta-util/Cargo.toml | 4 +++- manta-util/src/lib.rs | 4 +++- manta/Cargo.toml | 9 ++++++++- manta/src/lib.rs | 4 +++- 17 files changed, 142 insertions(+), 46 deletions(-) diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 6daff3c3e..637a546ef 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -14,14 +14,19 @@ description = "Accounting Primitives for Manta." publish = false [package.metadata.docs.rs] +# To build locally: +# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --no-deps --open all-features = true -rustdoc-args = ["--cfg", "docsrs"] +rustdoc-args = ["--cfg", "doc_cfg"] [badges] is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } +[features] +test = [] + [dependencies] derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } derive_more = { version = "0.99.16", default-features = false, features = ["add", "add_assign", "display", "from", "mul", "mul_assign", "sum"] } diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index b2664991a..1e25599fa 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -724,8 +724,8 @@ where /// /// # API Note /// - /// This function is intentionally private so that secret keys are not part of the - /// public interface. + /// This function is intentionally private so that the `asset_secret_key` is not part + /// of the public interface. #[inline] fn new(identity: Identity<C>, asset_secret_key: ies::SecretKey<I>) -> Self { Self { @@ -734,7 +734,7 @@ where } } - /// Builds a new [`ShieldedIdentity`] from an `identity`. + /// Builds a new [`Spend`] from an `identity`. #[inline] pub fn from_identity(identity: Identity<C>) -> Self where @@ -754,7 +754,7 @@ where Identity::generate_spend(source) } - /// Tries to open an `encrypted_asset` using `self`. + /// Tries to open an `encrypted_asset`, returning an [`OpenSpend`] if successful. #[inline] pub fn try_open(self, encrypted_asset: &EncryptedMessage<I>) -> Result<OpenSpend<C>, I::Error> { Ok(OpenSpend { diff --git a/manta-accounting/src/keys.rs b/manta-accounting/src/keys.rs index 4a200e34f..72b9c5cfa 100644 --- a/manta-accounting/src/keys.rs +++ b/manta-accounting/src/keys.rs @@ -35,7 +35,7 @@ pub trait SecretKeyGenerator { } /// Derived Secret Key Parameter -pub trait DerivedSecretKeyParameter: Default { +pub trait DerivedSecretKeyParameter: Clone + Default { /// Increments the key parameter by one unit. fn increment(&mut self); } @@ -74,6 +74,28 @@ pub trait DerivedSecretKeyGenerator { fn internal_keys<'s>(&'s self, account: &'s Self::Account) -> InternalKeys<'s, Self> { InternalKeys::new(self, account) } + + /// Builds a [`SecretKeyGenerator`] for external keys associated to `account`, starting + /// from `index`. + #[inline] + fn external_keys_from_index<'s>( + &'s self, + account: &'s Self::Account, + index: Self::Index, + ) -> ExternalKeys<'s, Self> { + ExternalKeys::from_index(self, account, index) + } + + /// Builds a [`SecretKeyGenerator`] for internal keys associated to `account`, starting + /// from `index`. + #[inline] + fn internal_keys_from_index<'s>( + &'s self, + account: &'s Self::Account, + index: Self::Index, + ) -> InternalKeys<'s, Self> { + InternalKeys::from_index(self, account, index) + } } /// Generates an internal or external secret key according to the [`DerivedSecretKeyGenerator`] diff --git a/manta-accounting/src/lib.rs b/manta-accounting/src/lib.rs index e8a34bb62..f19d2bb42 100644 --- a/manta-accounting/src/lib.rs +++ b/manta-accounting/src/lib.rs @@ -17,7 +17,9 @@ //! Accounting Primitives #![no_std] -#![cfg_attr(docsrs, feature(doc_cfg), forbid(broken_intra_doc_links))] +#![cfg_attr(doc_cfg, feature(doc_cfg))] +#![forbid(rustdoc::broken_intra_doc_links)] +#![forbid(missing_docs)] extern crate alloc; extern crate derive_more; diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index 4c824d386..294897efa 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -24,7 +24,7 @@ use crate::{ AssetParameters, Identity, IdentityConfiguration, OpenSpend, PublicKey, Receiver, Sender, ShieldedIdentity, Spend, VoidNumberCommitment, VoidNumberGenerator, }, - keys::{self, DerivedSecretKeyGenerator}, + keys::{self, DerivedSecretKeyGenerator, ExternalKeys, InternalKeys}, ledger::Ledger, transfer::{SecretTransfer, SecretTransferConfiguration}, }; @@ -219,11 +219,26 @@ where self.next_internal_key().map(Identity::new) } - /// Looks for an [`OpenSpend`] for this `encrypted_asset`, only trying `gap_limit`-many - /// external and internal keys. + /// + #[inline] + fn external_keys_from_index(&self, index: D::Index) -> ExternalKeys<D> { + self.secret_key_source + .external_keys_from_index(&self.account, index) + } + + /// + #[inline] + fn internal_keys_from_index(&self, index: D::Index) -> InternalKeys<D> { + self.secret_key_source + .internal_keys_from_index(&self.account, index) + } + + /// Looks for an [`OpenSpend`] for this encrypted `asset`, only trying `gap_limit`-many + /// external and internal keys starting from `index`. pub fn find_open_spend<C, I>( &self, - encrypted_asset: EncryptedMessage<I>, + asset: EncryptedMessage<I>, + index: D::Index, gap_limit: usize, ) -> Result<Option<OpenSpend<C>>, D::Error> where @@ -231,13 +246,13 @@ where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { - let mut external = self.secret_key_source.external_keys(&self.account); - let mut internal = self.secret_key_source.internal_keys(&self.account); - for _ in 0..gap_limit { - if let Ok(opened) = Spend::generate(&mut external)?.try_open(&encrypted_asset) { + let external = self.external_keys_from_index(index.clone()); + let internal = self.internal_keys_from_index(index); + for (external_key, internal_key) in external.zip(internal).take(gap_limit) { + if let Ok(opened) = Spend::from(Identity::new(external_key)).try_open(&asset) { return Ok(Some(opened)); } - if let Ok(opened) = Spend::generate(&mut internal)?.try_open(&encrypted_asset) { + if let Ok(opened) = Spend::from(Identity::new(internal_key)).try_open(&asset) { return Ok(Some(opened)); } } diff --git a/manta-codec/Cargo.toml b/manta-codec/Cargo.toml index 23a888b69..b4d895592 100644 --- a/manta-codec/Cargo.toml +++ b/manta-codec/Cargo.toml @@ -14,8 +14,10 @@ description = "Serialization and Deserialization tools for Manta." publish = false [package.metadata.docs.rs] +# To build locally: +# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --no-deps --open all-features = true -rustdoc-args = ["--cfg", "docsrs"] +rustdoc-args = ["--cfg", "doc_cfg"] [badges] is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } @@ -30,6 +32,6 @@ derive = ["scale-codec/derive"] [dependencies] ark-serialize = { version = "0.3.0", default-features = false } ark-std = { version = "0.3.0", default-features = false } -scale-codec = { package = "parity-scale-codec", version = "2.2.0", default-features = false } displaydoc = { version = "0.2.3", default-features = false } manta-util = { path = "../manta-util", default-features = false } +scale-codec = { package = "parity-scale-codec", version = "2.2.0", default-features = false } diff --git a/manta-codec/src/lib.rs b/manta-codec/src/lib.rs index 35962ce42..dd48f3392 100644 --- a/manta-codec/src/lib.rs +++ b/manta-codec/src/lib.rs @@ -20,7 +20,9 @@ // depend on it downstream #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(docsrs, feature(doc_cfg), forbid(broken_intra_doc_links))] +#![cfg_attr(doc_cfg, feature(doc_cfg))] +#![forbid(rustdoc::broken_intra_doc_links)] +#![forbid(missing_docs)] use displaydoc::Display; use manta_util::from_variant_impl; diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index 9fc803bf3..84c5d422a 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -14,14 +14,20 @@ description = "Cryptographic Primitives and Interfaces for Manta." publish = false [package.metadata.docs.rs] +# To build locally: +# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --no-deps --open all-features = true -rustdoc-args = ["--cfg", "docsrs"] +rustdoc-args = ["--cfg", "doc_cfg"] [badges] is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } +[features] +default = [] +test = [] + [dependencies] derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } rand = { version = "0.8.4", default-features = false } diff --git a/manta-crypto/src/ies.rs b/manta-crypto/src/ies.rs index 029c1b294..95eb52196 100644 --- a/manta-crypto/src/ies.rs +++ b/manta-crypto/src/ies.rs @@ -280,3 +280,30 @@ where I::decrypt(&self.ciphertext, secret_key.secret_key) } } + +/// Testing Framework +#[cfg(feature = "test")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] +pub mod test { + use super::*; + use core::fmt::Debug; + + /// Tests encryption/decryption of a sample `plaintext`. + pub fn encryption_decryption<I, R>(plaintext: I::Plaintext, rng: &mut R) + where + I: IntegratedEncryptionScheme, + I::Plaintext: Debug + PartialEq, + I::Error: Debug, + R: CryptoRng + RngCore + ?Sized, + { + let (public_key, secret_key) = I::keygen(rng).into(); + let reconstructed_plaintext = secret_key + .decrypt( + &public_key + .encrypt(&plaintext, rng) + .expect("Unable to encrypt plaintext."), + ) + .expect("Unable to decrypt plaintext."); + assert_eq!(plaintext, reconstructed_plaintext) + } +} diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index de6027320..a24886ab0 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -17,7 +17,9 @@ //! Cryptographic Primitives Library #![no_std] -#![cfg_attr(docsrs, feature(doc_cfg), forbid(broken_intra_doc_links))] +#![cfg_attr(doc_cfg, feature(doc_cfg))] +#![forbid(rustdoc::broken_intra_doc_links)] +#![forbid(missing_docs)] extern crate alloc; diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 20e99515f..7a8981de3 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -14,8 +14,10 @@ description = "The Manta-Pay protocol and implementaion." publish = false [package.metadata.docs.rs] +# To build locally: +# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --no-deps --open all-features = true -rustdoc-args = ["--cfg", "docsrs"] +rustdoc-args = ["--cfg", "doc_cfg"] [badges] is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } @@ -40,5 +42,6 @@ rand = { version = "0.8.4", default-features = false } x25519-dalek = { git = "https://github.com/Manta-Network/x25519-dalek", default-features = false, features = ["u64_backend"] } [dev-dependencies] +manta-crypto = { path = "../manta-crypto", features = ["test"] } rand = "0.8.4" rand_chacha = "0.3.1" diff --git a/manta-pay/src/crypto/ies.rs b/manta-pay/src/crypto/ies.rs index e0185deec..e03b2a6f5 100644 --- a/manta-pay/src/crypto/ies.rs +++ b/manta-pay/src/crypto/ies.rs @@ -167,23 +167,18 @@ impl IntegratedEncryptionScheme for IES { } } -/// Tests encryption/decryption of an asset. -#[test] -fn encryption_decryption() { - use rand::{thread_rng, Rng, SeedableRng}; - use rand_chacha::ChaCha20Rng; - - let mut rng = ChaCha20Rng::from_rng(&mut thread_rng()).expect("failed to seed ChaCha20Rng"); - - let (public_key, secret_key) = IES::keygen(&mut rng).into(); - let asset = rng.gen(); - let reconstructed_asset = secret_key - .decrypt( - &public_key - .encrypt(&asset, &mut rng) - .expect("unable to encrypt asset"), - ) - .expect("unable to decrypt asset"); - - assert_eq!(asset, reconstructed_asset) +/// Testing Suite +#[cfg(test)] +mod test { + use super::*; + use manta_crypto::ies::test as ies_test; + + /// Tests encryption/decryption of a random asset. + #[test] + fn encryption_decryption() { + use rand::{thread_rng, Rng, SeedableRng}; + use rand_chacha::ChaCha20Rng; + let mut rng = ChaCha20Rng::from_rng(&mut thread_rng()).expect("failed to seed ChaCha20Rng"); + ies_test::encryption_decryption::<IES, _>(rng.gen(), &mut rng); + } } diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index 4379242e1..c754ec9ec 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -17,7 +17,9 @@ //! Manta Pay Implementation #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(docsrs, feature(doc_cfg), forbid(broken_intra_doc_links))] +#![cfg_attr(doc_cfg, feature(doc_cfg))] +#![forbid(rustdoc::broken_intra_doc_links)] +#![forbid(missing_docs)] extern crate alloc; diff --git a/manta-util/Cargo.toml b/manta-util/Cargo.toml index acd320013..a14a9a7f7 100644 --- a/manta-util/Cargo.toml +++ b/manta-util/Cargo.toml @@ -14,8 +14,10 @@ description = "Basic utilities for Manta crates." publish = false [package.metadata.docs.rs] +# To build locally: +# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --no-deps --open all-features = true -rustdoc-args = ["--cfg", "docsrs"] +rustdoc-args = ["--cfg", "doc_cfg"] [badges] is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index 55e08b447..4f6bd9e9e 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -17,7 +17,9 @@ //! Utilities #![no_std] -#![cfg_attr(docsrs, feature(doc_cfg), forbid(broken_intra_doc_links))] +#![cfg_attr(doc_cfg, feature(doc_cfg))] +#![forbid(rustdoc::broken_intra_doc_links)] +#![forbid(missing_docs)] extern crate alloc; diff --git a/manta/Cargo.toml b/manta/Cargo.toml index 0cc5bbb0b..b78f0b712 100644 --- a/manta/Cargo.toml +++ b/manta/Cargo.toml @@ -14,8 +14,10 @@ description = "Manta Network main crate." publish = false [package.metadata.docs.rs] +# To build locally: +# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --no-deps --open all-features = true -rustdoc-args = ["--cfg", "docsrs"] +rustdoc-args = ["--cfg", "doc_cfg"] [badges] is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } @@ -25,6 +27,7 @@ maintenance = { status = "actively-developed" } [features] default = [] std = [] +test = ["manta-accounting/test", "manta-crypto/test"] [dependencies] manta-accounting = { path = "../manta-accounting", default-features = false } @@ -32,3 +35,7 @@ manta-codec = { path = "../manta-codec", default-features = false } manta-crypto = { path = "../manta-crypto", default-features = false } manta-pay = { path = "../manta-pay", default-features = false } parity-scale-codec = { version = "2.2.0", default-features = false } + +[dev-dependencies] +manta-accounting = { path = "../manta-accounting", features = ["test"] } +manta-crypto = { path = "../manta-crypto", features = ["test"] } diff --git a/manta/src/lib.rs b/manta/src/lib.rs index 59e33e387..b49ae0d21 100644 --- a/manta/src/lib.rs +++ b/manta/src/lib.rs @@ -17,7 +17,9 @@ //! The Manta Network #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(docsrs, feature(doc_cfg), forbid(broken_intra_doc_links))] +#![cfg_attr(doc_cfg, feature(doc_cfg))] +#![forbid(rustdoc::broken_intra_doc_links)] +#![forbid(missing_docs)] #[doc(inline)] pub use manta_accounting as accounting; From 32a46eae23ed6f39d184c7f83ddc7cec16381521 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 8 Sep 2021 15:49:36 -0400 Subject: [PATCH 015/275] add missing docs --- manta-accounting/src/wallet.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index 294897efa..f91c10dd7 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -219,14 +219,14 @@ where self.next_internal_key().map(Identity::new) } - /// + /// Returns an [`ExternalKeys`] generator starting from the given `index`. #[inline] fn external_keys_from_index(&self, index: D::Index) -> ExternalKeys<D> { self.secret_key_source .external_keys_from_index(&self.account, index) } - /// + /// Returns an [`InternalKeys`] generator starting from the given `index`. #[inline] fn internal_keys_from_index(&self, index: D::Index) -> InternalKeys<D> { self.secret_key_source From 00272d1e8872f7f274f19863b8d6b7563cee066d Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 8 Sep 2021 15:54:50 -0400 Subject: [PATCH 016/275] add some notes --- manta-accounting/src/wallet.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index f91c10dd7..1f684d166 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -17,6 +17,8 @@ //! Wallet Abstractions // TODO: How to manage accounts? Wallet should have a fixed account or not? +// TODO: Is recovery different than just building a fresh `Wallet` instance? +// TODO: Add encrypt-to-disk and decrypt-from-disk methods to save the wallet state. use crate::{ asset::{Asset, AssetBalance, AssetId}, @@ -246,6 +248,11 @@ where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { + // TODO: Maybe add a `try_open` method on `Identity` to make this more ergonomic? + // TODO: Return which kind of spend it was, internal or external. + // TODO: Have separate methods to search for internal and external keys since sometimes + // we know which one we're looking for. + let external = self.external_keys_from_index(index.clone()); let internal = self.internal_keys_from_index(index); for (external_key, internal_key) in external.zip(internal).take(gap_limit) { From e4ab70e7bea8edfeffb517732ceda29a178c1920 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 9 Sep 2021 01:26:30 -0400 Subject: [PATCH 017/275] implement bip44-compliant secret key generator --- manta-pay/Cargo.toml | 3 +- manta-pay/src/accounting/keys.rs | 216 +++++++++++++++++++++++++++-- manta-pay/src/accounting/mod.rs | 1 + manta-pay/src/accounting/wallet.rs | 17 +++ 4 files changed, 226 insertions(+), 11 deletions(-) create mode 100644 manta-pay/src/accounting/wallet.rs diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 7a8981de3..5289567da 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -32,8 +32,9 @@ std = [] aes-gcm = { version = "0.9.4" } ark-crypto-primitives = { version = "0.3.0", default-features = false } ark-ed-on-bls12-381 = { version = "0.3.0", default-features = false } -bip32 = { version = "0.2.2", default-features = false } +bip32 = { version = "0.2.2", default-features = false, features = ["bip39", "secp256k1"] } blake2 = { version = "0.9.2", default-features = false } +cocoon = { version = "0.2.4", default-features = false } generic-array = { version = "0.14.4", default-features = false } manta-accounting = { path = "../manta-accounting", default-features = false } manta-crypto = { path = "../manta-crypto", default-features = false } diff --git a/manta-pay/src/accounting/keys.rs b/manta-pay/src/accounting/keys.rs index cf44f8718..b1c2e640a 100644 --- a/manta-pay/src/accounting/keys.rs +++ b/manta-pay/src/accounting/keys.rs @@ -14,18 +14,214 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Secret Key Generator Implementations +//! Secret Key Generation +//! +//! This module contains [`DerivedKeySecret`] which implements the [`BIP-0044`] specification. We +//! may implement other kinds of key generation schemes in the future. +//! +//! See [`CoinType`] for the coins which this key generation scheme can control. +//! +//! [`BIP-0044`]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki -// TODO: Use the `bip32` crate to implement wallet key generators +use alloc::{format, string::String}; +use bip32::Seed; +use core::{marker::PhantomData, num::ParseIntError, str::FromStr}; +use manta_accounting::keys::{DerivedSecretKeyGenerator, DerivedSecretKeyParameter}; -/// BIP-0044 Purpose Id -pub const BIP_44_PURPOSE_ID: u32 = 44; +pub use bip32::{Error, Mnemonic, XPrv as SecretKey}; -/// Manta Coin Type Id -pub const MANTA_COIN_TYPE_ID: u32 = 611; +/// Sealed Trait Module +mod sealed { + /// Sealed Trait + pub trait Sealed {} +} -/// Calamary Coin Type Id -pub const CALAMARI_COIN_TYPE_ID: u32 = 612; +/// Coin Type Id Type +pub type CoinTypeId = u128; -/// Testnet Coin Type Id -pub const TESTNET_COIN_TYPE_ID: u32 = 1; +/// Coin Type Marker Trait +/// +/// This trait identifies a coin type and its id for the [`BIP-0044`] specification. This trait is +/// sealed and can only be used with the existing implementations. +/// +/// [`BIP-0044`]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki +pub trait CoinType: sealed::Sealed { + /// The coin type id for this coin. + /// + /// See [`SLIP-0044`] for a list of registered coin type ids. + /// + /// [`SLIP-0044`]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md + const COIN_TYPE_ID: CoinTypeId; +} + +/// Implements the [`CoinType`] trait for `$name` with coin type id given by `$id`. +macro_rules! impl_coin_type { + ($name:ty, $id:ident) => { + impl sealed::Sealed for $name {} + impl CoinType for $name { + const COIN_TYPE_ID: CoinTypeId = $id; + } + }; +} + +/// Test Network `testnet` Coin Type +pub struct Testnet; + +/// Test Network `testnet` Coin Type Id +pub const TESTNET_COIN_TYPE_ID: CoinTypeId = 1; + +impl_coin_type!(Testnet, TESTNET_COIN_TYPE_ID); + +/// Main Network `manta` Coin Type +pub struct Manta; + +/// Main Network `manta` Coin Type Id +pub const MANTA_COIN_TYPE_ID: CoinTypeId = 611; + +impl_coin_type!(Manta, MANTA_COIN_TYPE_ID); + +/// Canary Network `calamari` Coin Type +pub struct Calamari; + +/// Canary Network `calamari` Coin Type Id +pub const CALAMARI_COIN_TYPE_ID: CoinTypeId = 612; + +impl_coin_type!(Calamari, CALAMARI_COIN_TYPE_ID); + +/// Parse Parameter Error +pub struct ParseParameterError(ParseIntError); + +/// Implements some [`From`] traits for `$name`. +macro_rules! impl_from_for_parameter { + ($name:ty, $($from:ty),+$(,)?) => { + $( + impl From<$from> for $name { + #[inline] + fn from(t: $from) -> Self { + Self(t.into()) + } + } + )+ + } +} + +/// Implements the [`DerivedSecretKeyParameter`] trait for `$name`. +macro_rules! impl_parameter { + ($name:ty) => { + impl DerivedSecretKeyParameter for $name { + #[inline] + fn increment(&mut self) { + self.0 += 1; + } + } + + impl_from_for_parameter!($name, bool, u8, u16, u32, u64, u128); + + impl FromStr for $name { + type Err = ParseParameterError; + + #[inline] + fn from_str(s: &str) -> Result<Self, Self::Err> { + Ok(Self(s.parse().map_err(ParseParameterError)?)) + } + } + }; +} + +/// Parameter Type +type ParameterType = u128; + +/// Account Parameter +#[derive(Clone, Copy, Default)] +pub struct AccountParameter(ParameterType); + +impl_parameter!(AccountParameter); + +/// Index Parameter +#[derive(Clone, Copy, Default)] +pub struct IndexParameter(ParameterType); + +impl_parameter!(IndexParameter); + +/// [`Testnet`] [`DerivedKeySecret`] Alias Type +pub type TestnetDerivedKeySecret = DerivedKeySecret<Testnet>; + +/// [`Manta`] [`DerivedKeySecret`] Alias Type +pub type MantaDerivedKeySecret = DerivedKeySecret<Manta>; + +/// [`Calamari`] [`DerivedKeySecret`] Alias Type +pub type CalamariDerivedKeySecret = DerivedKeySecret<Calamari>; + +/// Derived Key Secret +pub struct DerivedKeySecret<C> +where + C: CoinType, +{ + /// Derived Key Seed + seed: Seed, + + /// Parameter Marker + __: PhantomData<C>, +} + +impl<C> DerivedKeySecret<C> +where + C: CoinType, +{ + /// Converts a `mnemonic` phrase into a [`DerivedKeySecret`], locking it with `password`. + #[inline] + pub fn from_mnemonic(mnemonic: Mnemonic, password: &str) -> Self { + Self { + seed: mnemonic.to_seed(password), + __: PhantomData, + } + } +} + +/// Computes the [`BIP-0044`] path string for the given coin settings. +/// +/// [`BIP-0044`]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki +pub fn path_string<C>( + is_external: bool, + account: &AccountParameter, + index: &IndexParameter, +) -> String +where + C: CoinType, +{ + const BIP_44_PURPOSE_ID: u8 = 44; + format!( + "m/{}'/{}'/{}'/{}/{}", + BIP_44_PURPOSE_ID, + C::COIN_TYPE_ID, + account.0, + if is_external { 0 } else { 1 }, + index.0 + ) +} + +impl<C> DerivedSecretKeyGenerator for DerivedKeySecret<C> +where + C: CoinType, +{ + type SecretKey = SecretKey; + + type Account = AccountParameter; + + type Index = IndexParameter; + + type Error = Error; + + #[inline] + fn generate_key( + &self, + is_external: bool, + account: &Self::Account, + index: &Self::Index, + ) -> Result<Self::SecretKey, Self::Error> { + SecretKey::derive_from_path( + &self.seed, + &path_string::<C>(is_external, account, index).parse()?, + ) + } +} diff --git a/manta-pay/src/accounting/mod.rs b/manta-pay/src/accounting/mod.rs index bf3f63b3e..e2a454217 100644 --- a/manta-pay/src/accounting/mod.rs +++ b/manta-pay/src/accounting/mod.rs @@ -18,3 +18,4 @@ pub mod keys; pub mod ledger; +pub mod wallet; diff --git a/manta-pay/src/accounting/wallet.rs b/manta-pay/src/accounting/wallet.rs new file mode 100644 index 000000000..363f135e5 --- /dev/null +++ b/manta-pay/src/accounting/wallet.rs @@ -0,0 +1,17 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Wallet Implementation From b30b6fb66470b375474f26d6ecbe4f26d0b6887c Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 9 Sep 2021 01:52:45 -0400 Subject: [PATCH 018/275] add more ergonomic encrypted asset search --- manta-accounting/src/identity.rs | 13 +++++ manta-accounting/src/wallet.rs | 89 +++++++++++++++++++++++++------- 2 files changed, 84 insertions(+), 18 deletions(-) diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 1e25599fa..90957bbbe 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -575,6 +575,19 @@ where { Ok(Self::generate(source)?.into_receiver(commitment_scheme)) } + + /// Tries to open an `encrypted_asset`, returning an [`OpenSpend`] if successful. + #[inline] + pub fn try_open<I>( + self, + encrypted_asset: &EncryptedMessage<I>, + ) -> Result<OpenSpend<C>, I::Error> + where + I: IntegratedEncryptionScheme<Plaintext = Asset>, + Standard: Distribution<AssetParameters<C>>, + { + self.into_spend().try_open(encrypted_asset) + } } impl<C> Distribution<Identity<C>> for Standard diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index 1f684d166..f5dbfcf9b 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -19,6 +19,7 @@ // TODO: How to manage accounts? Wallet should have a fixed account or not? // TODO: Is recovery different than just building a fresh `Wallet` instance? // TODO: Add encrypt-to-disk and decrypt-from-disk methods to save the wallet state. +// TODO: Add query builder for encrypted asset search (internal/external, gap_limit, start_index) use crate::{ asset::{Asset, AssetBalance, AssetId}, @@ -235,35 +236,87 @@ where .internal_keys_from_index(&self.account, index) } - /// Looks for an [`OpenSpend`] for this encrypted `asset`, only trying `gap_limit`-many + /// Looks for an [`OpenSpend`] for this `encrypted_asset` by checking every secret key + /// in the iterator. + #[inline] + fn find_open_spend_from_iter<C, I, Iter>( + &self, + encrypted_asset: EncryptedMessage<I>, + iter: Iter, + ) -> Option<OpenSpend<C>> + where + C: IdentityConfiguration<SecretKey = D::SecretKey>, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + Iter: IntoIterator<Item = D::SecretKey>, + Standard: Distribution<AssetParameters<C>>, + { + iter.into_iter() + .find_map(move |k| Identity::new(k).try_open(&encrypted_asset).ok()) + } + + /// Looks for an [`OpenSpend`] for this `encrypted_asset`, only trying `gap_limit`-many /// external and internal keys starting from `index`. + #[inline] pub fn find_open_spend<C, I>( &self, - asset: EncryptedMessage<I>, + encrypted_asset: EncryptedMessage<I>, index: D::Index, gap_limit: usize, - ) -> Result<Option<OpenSpend<C>>, D::Error> + ) -> Option<OpenSpend<C>> where C: IdentityConfiguration<SecretKey = D::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { - // TODO: Maybe add a `try_open` method on `Identity` to make this more ergonomic? // TODO: Return which kind of spend it was, internal or external. - // TODO: Have separate methods to search for internal and external keys since sometimes - // we know which one we're looking for. - - let external = self.external_keys_from_index(index.clone()); - let internal = self.internal_keys_from_index(index); - for (external_key, internal_key) in external.zip(internal).take(gap_limit) { - if let Ok(opened) = Spend::from(Identity::new(external_key)).try_open(&asset) { - return Ok(Some(opened)); - } - if let Ok(opened) = Spend::from(Identity::new(internal_key)).try_open(&asset) { - return Ok(Some(opened)); - } - } - Ok(None) + + let interleaved_keys = self + .external_keys_from_index(index.clone()) + .zip(self.internal_keys_from_index(index)) + .flat_map(move |(e, i)| [e, i]) + .take(gap_limit); + + self.find_open_spend_from_iter(encrypted_asset, interleaved_keys) + } + + /// Looks for an [`OpenSpend`] for this `encrypted_asset`, only trying `gap_limit`-many + /// external keys starting from `index`. + #[inline] + pub fn find_external_open_spend<C, I>( + &self, + encrypted_asset: EncryptedMessage<I>, + index: D::Index, + gap_limit: usize, + ) -> Option<OpenSpend<C>> + where + C: IdentityConfiguration<SecretKey = D::SecretKey>, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + Standard: Distribution<AssetParameters<C>>, + { + self.find_open_spend_from_iter( + encrypted_asset, + self.external_keys_from_index(index).take(gap_limit), + ) + } + + /// Looks for an [`OpenSpend`] for this `encrypted_asset`, only trying `gap_limit`-many + /// internal keys starting from `index`. + #[inline] + pub fn find_internal_open_spend<C, I>( + &self, + encrypted_asset: EncryptedMessage<I>, + index: D::Index, + gap_limit: usize, + ) -> Option<OpenSpend<C>> + where + C: IdentityConfiguration<SecretKey = D::SecretKey>, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + Standard: Distribution<AssetParameters<C>>, + { + self.find_open_spend_from_iter( + encrypted_asset, + self.internal_keys_from_index(index).take(gap_limit), + ) } /// Updates `self` with new information from the ledger. From f56fcabd70fbe8a227d8d08240b9c78e287e7514 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 9 Sep 2021 17:00:55 -0400 Subject: [PATCH 019/275] make commitment scheme more general --- manta-accounting/src/asset.rs | 7 +- manta-accounting/src/identity.rs | 476 ++++++++++++++------ manta-accounting/src/transfer.rs | 99 +++- manta-accounting/src/wallet.rs | 18 +- manta-crypto/src/commitment.rs | 49 +- manta-crypto/src/concat.rs | 177 -------- manta-crypto/src/constraints.rs | 89 ++++ manta-crypto/src/lib.rs | 2 - manta-crypto/src/prf.rs | 2 +- manta-pay/src/accounting/keys.rs | 2 +- manta-pay/src/crypto/commitment/pedersen.rs | 19 +- manta-pay/src/crypto/merkle_tree.rs | 11 +- manta-util/src/lib.rs | 172 ++++++- 13 files changed, 739 insertions(+), 384 deletions(-) delete mode 100644 manta-crypto/src/concat.rs diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index c356d762d..db6d8877c 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -29,8 +29,9 @@ use core::{ use derive_more::{ Add, AddAssign, Display, Div, DivAssign, From, Mul, MulAssign, Product, Sub, SubAssign, Sum, }; -use manta_crypto::{ByteAccumulator, ConcatBytes}; -use manta_util::{array_map, fallible_array_map, into_array_unchecked}; +use manta_util::{ + array_map, fallible_array_map, into_array_unchecked, ByteAccumulator, ConcatBytes, +}; use rand::{ distributions::{Distribution, Standard}, Rng, RngCore, @@ -258,7 +259,7 @@ impl Asset { /// Converts `self` into a byte array. #[inline] pub fn into_bytes(self) -> [u8; Self::SIZE] { - into_array_unchecked(self.as_bytes::<Vec<u8>>()) + into_array_unchecked(self.as_bytes::<Vec<_>>()) } /// Converts a byte array into `self`. diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 90957bbbe..9c2b01777 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -16,7 +16,10 @@ //! Identities, Senders, and Receivers -// FIXME: ensure secret keys cannot be made public by some API call +// FIXME: Check the secret key APIs. +// FIXME: Should the identity types have methods that expose their members? Or should it be +// completely opaque, and let the internal APIs handle all the logic? +// TODO: Since `IdentityConfiguration::SecretKey: Clone`, should `Identity: Clone`? use crate::{ asset::{Asset, AssetBalance, AssetId}, @@ -25,10 +28,9 @@ use crate::{ }; use core::{convert::Infallible, fmt::Debug, hash::Hash, marker::PhantomData}; use manta_crypto::{ - concatenate, ies::{self, EncryptedMessage}, set::ContainmentProof, - CommitmentScheme, ConcatBytes, IntegratedEncryptionScheme, PseudorandomFunctionFamily, + CommitmentInput, CommitmentScheme, IntegratedEncryptionScheme, PseudorandomFunctionFamily, VerifiedSet, }; use rand::{ @@ -110,13 +112,20 @@ pub fn generate_void_number_commitment<C>( ) -> VoidNumberCommitment<C> where C: IdentityConfiguration, - PublicKey<C>: ConcatBytes, - VoidNumberGenerator<C>: ConcatBytes, + PublicKey<C>: CommitmentInput<C::CommitmentScheme>, + VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, { + let mut buffer = commitment_scheme.start(); + commitment_scheme + .update(&mut buffer, public_key) + .update(&mut buffer, void_number_generator) + .commit(buffer, void_number_commitment_randomness) + /* TODO[remove]: commitment_scheme.commit( concatenate!(public_key, void_number_generator), void_number_commitment_randomness, ) + */ } /// Generates a [`Utxo`] from a given `asset`, `void_number_commitment`, and `utxo_randomness`. @@ -129,9 +138,17 @@ pub fn generate_utxo<C>( ) -> Utxo<C> where C: IdentityConfiguration, - VoidNumberCommitment<C>: ConcatBytes, + Asset: CommitmentInput<C::CommitmentScheme>, + VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { + let mut buffer = commitment_scheme.start(); + commitment_scheme + .update(&mut buffer, asset) + .update(&mut buffer, void_number_commitment) + .commit(buffer, utxo_randomness) + /* TODO[remove]: commitment_scheme.commit(concatenate!(asset, void_number_commitment), utxo_randomness) + */ } /// Public Parameters for using an [`Asset`] @@ -199,8 +216,8 @@ where public_key: &PublicKey<C>, ) -> VoidNumberCommitment<C> where - PublicKey<C>: ConcatBytes, - VoidNumberGenerator<C>: ConcatBytes, + PublicKey<C>: CommitmentInput<C::CommitmentScheme>, + VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, { generate_void_number_commitment::<C>( commitment_scheme, @@ -219,7 +236,8 @@ where void_number_commitment: &VoidNumberCommitment<C>, ) -> Utxo<C> where - VoidNumberCommitment<C>: ConcatBytes, + Asset: CommitmentInput<C::CommitmentScheme>, + VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { generate_utxo::<C>( commitment_scheme, @@ -385,8 +403,8 @@ where parameters: &AssetParameters<C>, ) -> VoidNumberCommitment<C> where - PublicKey<C>: ConcatBytes, - VoidNumberGenerator<C>: ConcatBytes, + PublicKey<C>: CommitmentInput<C::CommitmentScheme>, + VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, { parameters.void_number_commitment(commitment_scheme, &self.public_key()) } @@ -400,9 +418,10 @@ where parameters: &AssetParameters<C>, ) -> Utxo<C> where - PublicKey<C>: ConcatBytes, - VoidNumberGenerator<C>: ConcatBytes, - VoidNumberCommitment<C>: ConcatBytes, + PublicKey<C>: CommitmentInput<C::CommitmentScheme>, + VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, + Asset: CommitmentInput<C::CommitmentScheme>, + VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { parameters.utxo( commitment_scheme, @@ -422,9 +441,10 @@ where where S: VerifiedSet<Item = Utxo<C>>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: ConcatBytes, - VoidNumberGenerator<C>: ConcatBytes, - VoidNumberCommitment<C>: ConcatBytes, + PublicKey<C>: CommitmentInput<C::CommitmentScheme>, + VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, + Asset: CommitmentInput<C::CommitmentScheme>, + VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { let parameters = self.parameters(); let utxo = self.utxo(commitment_scheme, &asset, &parameters); @@ -441,7 +461,7 @@ where /// Generates a new [`Identity`] from a secret key generation source and builds a new /// [`Sender`] from it. #[inline] - pub fn generate_sender<G, S>( + fn generate_sender<G, S>( source: &mut G, commitment_scheme: &C::CommitmentScheme, asset: Asset, @@ -451,9 +471,10 @@ where G: SecretKeyGenerator<SecretKey = C::SecretKey>, S: VerifiedSet<Item = Utxo<C>>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: ConcatBytes, - VoidNumberGenerator<C>: ConcatBytes, - VoidNumberCommitment<C>: ConcatBytes, + PublicKey<C>: CommitmentInput<C::CommitmentScheme>, + VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, + Asset: CommitmentInput<C::CommitmentScheme>, + VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { Self::generate(source) .map_err(SenderError::SecretKeyError)? @@ -473,8 +494,8 @@ where where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: ConcatBytes, - VoidNumberGenerator<C>: ConcatBytes, + PublicKey<C>: CommitmentInput<C::CommitmentScheme>, + VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, { ShieldedIdentity { void_number_commitment: self.void_number_commitment(commitment_scheme, &parameters), @@ -489,8 +510,8 @@ where where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: ConcatBytes, - VoidNumberGenerator<C>: ConcatBytes, + PublicKey<C>: CommitmentInput<C::CommitmentScheme>, + VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, { let (parameters, asset_keypair) = self.parameters_and_asset_keypair(); self.build_shielded_identity(commitment_scheme, parameters, asset_keypair.into_public()) @@ -499,7 +520,7 @@ where /// Generates a new [`Identity`] from a secret key generation source and builds a new /// [`ShieldedIdentity`] from it. #[inline] - pub fn generate_shielded<G, I>( + fn generate_shielded<G, I>( source: &mut G, commitment_scheme: &C::CommitmentScheme, ) -> Result<ShieldedIdentity<C, I>, G::Error> @@ -507,8 +528,8 @@ where G: SecretKeyGenerator<SecretKey = C::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: ConcatBytes, - VoidNumberGenerator<C>: ConcatBytes, + PublicKey<C>: CommitmentInput<C::CommitmentScheme>, + VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, { Ok(Self::generate(source)?.into_shielded(commitment_scheme)) } @@ -529,7 +550,7 @@ where /// Generates a new [`Identity`] from a secret key generation source and builds a new /// [`Spend`] from it. #[inline] - pub fn generate_spend<G, I>(source: &mut G) -> Result<Spend<C, I>, G::Error> + fn generate_spend<G, I>(source: &mut G) -> Result<Spend<C, I>, G::Error> where G: SecretKeyGenerator<SecretKey = C::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, @@ -547,8 +568,8 @@ where where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: ConcatBytes, - VoidNumberGenerator<C>: ConcatBytes, + PublicKey<C>: CommitmentInput<C::CommitmentScheme>, + VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, { let (parameters, asset_keypair) = self.parameters_and_asset_keypair(); let (asset_public_key, asset_secret_key) = asset_keypair.into(); @@ -570,8 +591,8 @@ where G: SecretKeyGenerator<SecretKey = C::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: ConcatBytes, - VoidNumberGenerator<C>: ConcatBytes, + PublicKey<C>: CommitmentInput<C::CommitmentScheme>, + VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, { Ok(Self::generate(source)?.into_receiver(commitment_scheme)) } @@ -608,13 +629,13 @@ where I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// UTXO Randomness - pub utxo_randomness: UtxoRandomness<C>, + utxo_randomness: UtxoRandomness<C>, /// Void Number Commitment - pub void_number_commitment: VoidNumberCommitment<C>, + void_number_commitment: VoidNumberCommitment<C>, /// Encrypted [`Asset`] Public Key - pub asset_public_key: ies::PublicKey<I>, + asset_public_key: ies::PublicKey<I>, } impl<C, I> ShieldedIdentity<C, I> @@ -628,8 +649,8 @@ where where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: ConcatBytes, - VoidNumberGenerator<C>: ConcatBytes, + PublicKey<C>: CommitmentInput<C::CommitmentScheme>, + VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, { identity.into_shielded(commitment_scheme) } @@ -645,12 +666,30 @@ where G: SecretKeyGenerator<SecretKey = C::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: ConcatBytes, - VoidNumberGenerator<C>: ConcatBytes, + PublicKey<C>: CommitmentInput<C::CommitmentScheme>, + VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, { Identity::generate_shielded(source, commitment_scheme) } + /// Returns the UTXO randomness for this shielded identity. + #[inline] + pub fn utxo_randomness(&self) -> &UtxoRandomness<C> { + &self.utxo_randomness + } + + /// Returns the void number commitment for this shielded identity. + #[inline] + pub fn void_number_commitment(&self) -> &VoidNumberCommitment<C> { + &self.void_number_commitment + } + + /// Returns the asset public key for this shielded identity. + #[inline] + pub fn asset_public_key(&self) -> &ies::PublicKey<I> { + &self.asset_public_key + } + /// Generates a [`Receiver`] from a [`ShieldedIdentity`]. #[inline] pub fn into_receiver<R>( @@ -661,7 +700,8 @@ where ) -> Result<Receiver<C, I>, I::Error> where R: CryptoRng + RngCore + ?Sized, - VoidNumberCommitment<C>: ConcatBytes, + Asset: CommitmentInput<C::CommitmentScheme>, + VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { let Self { utxo_randomness, @@ -685,36 +725,48 @@ where /// Spend Error /// -/// This `enum` is the error state for the [`into_sender`] method on [`Spend`]. -/// See its documentation for more. -/// -/// [`into_sender`]: Spend::into_sender -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "I::Error: Clone, S::ContainmentError: Clone"), - Copy(bound = "I::Error: Copy, S::ContainmentError: Copy"), - Debug(bound = "I::Error: Debug, S::ContainmentError: Debug"), - Eq(bound = "I::Error: Eq, S::ContainmentError: Eq"), - Hash(bound = "I::Error: Hash, S::ContainmentError: Hash"), - PartialEq(bound = "I::Error: PartialEq, S::ContainmentError: PartialEq") -)] -pub enum SpendError<C, I, S> -where - C: IdentityConfiguration, - I: IntegratedEncryptionScheme<Plaintext = Asset>, - S: VerifiedSet<Item = Utxo<C>>, -{ - /// Encryption Error - EncryptionError(I::Error), +/// This is a work-around for a `clippy` overreaction bug. +/// See <https://github.com/mcarton/rust-derivative/issues/100>. +#[allow(unreachable_code)] +mod spend_error { + use super::*; - /// Missing UTXO Containment Proof - MissingUtxo(S::ContainmentError), + /// Spend Error + /// + /// This `enum` is the error state for the [`into_sender`] method on [`Spend`]. + /// See its documentation for more. + /// + /// [`into_sender`]: Spend::into_sender + #[derive(derivative::Derivative)] + #[derivative( + Clone(bound = "I::Error: Clone, S::ContainmentError: Clone"), + Copy(bound = "I::Error: Copy, S::ContainmentError: Copy"), + Debug(bound = "I::Error: Debug, S::ContainmentError: Debug"), + Eq(bound = "I::Error: Eq, S::ContainmentError: Eq"), + Hash(bound = "I::Error: Hash, S::ContainmentError: Hash"), + PartialEq(bound = "I::Error: PartialEq, S::ContainmentError: PartialEq") + )] + pub enum SpendError<C, I, S> + where + C: IdentityConfiguration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + S: VerifiedSet<Item = Utxo<C>>, + { + /// Encryption Error + EncryptionError(I::Error), + + /// Missing UTXO Containment Proof + MissingUtxo(S::ContainmentError), - /// Parameter Marker - #[doc(hidden)] - __(Infallible, PhantomData<C>), + /// Type Parameter Marker + #[doc(hidden)] + __(Infallible, PhantomData<C>), + } } +#[doc(inline)] +pub use spend_error::SpendError; + /// Spending Information pub struct Spend<C, I> where @@ -787,9 +839,10 @@ where where S: VerifiedSet<Item = Utxo<C>>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: ConcatBytes, - VoidNumberGenerator<C>: ConcatBytes, - VoidNumberCommitment<C>: ConcatBytes, + PublicKey<C>: CommitmentInput<C::CommitmentScheme>, + VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, + Asset: CommitmentInput<C::CommitmentScheme>, + VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { self.try_open(&encrypted_asset) .map_err(SpendError::EncryptionError)? @@ -836,9 +889,10 @@ where where S: VerifiedSet<Item = Utxo<C>>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: ConcatBytes, - VoidNumberGenerator<C>: ConcatBytes, - VoidNumberCommitment<C>: ConcatBytes, + PublicKey<C>: CommitmentInput<C::CommitmentScheme>, + VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, + Asset: CommitmentInput<C::CommitmentScheme>, + VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { self.identity .into_sender(commitment_scheme, self.asset, utxo_set) @@ -847,38 +901,48 @@ where /// Sender Error /// -/// This `enum` is the error state for the [`generate_sender`] method on [`Identity`] -/// and the [`generate`] method on [`Sender`]. -/// See their documentation for more. -/// -/// [`generate_sender`]: Identity::generate_sender -/// [`generate`]: Sender::generate -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "G::Error: Clone, S::ContainmentError: Clone"), - Copy(bound = "G::Error: Copy, S::ContainmentError: Copy"), - Debug(bound = "G::Error: Debug, S::ContainmentError: Debug"), - Eq(bound = "G::Error: Eq, S::ContainmentError: Eq"), - Hash(bound = "G::Error: Hash, S::ContainmentError: Hash"), - PartialEq(bound = "G::Error: PartialEq, S::ContainmentError: PartialEq") -)] -pub enum SenderError<C, G, S> -where - C: IdentityConfiguration, - G: SecretKeyGenerator<SecretKey = C::SecretKey>, - S: VerifiedSet<Item = Utxo<C>>, -{ - /// Secret Key Generator Error - SecretKeyError(G::Error), +/// This is a work-around for a `clippy` overreaction bug. +/// See <https://github.com/mcarton/rust-derivative/issues/100>. +#[allow(unreachable_code)] +mod sender_error { + use super::*; - /// Containment Error - MissingUtxo(S::ContainmentError), + /// Sender Error + /// + /// This `enum` is the error state for the [`generate`] method on [`Sender`]. + /// See its documentation for more. + /// + /// [`generate`]: Sender::generate + #[derive(derivative::Derivative)] + #[derivative( + Clone(bound = "G::Error: Clone, S::ContainmentError: Clone"), + Copy(bound = "G::Error: Copy, S::ContainmentError: Copy"), + Debug(bound = "G::Error: Debug, S::ContainmentError: Debug"), + Eq(bound = "G::Error: Eq, S::ContainmentError: Eq"), + Hash(bound = "G::Error: Hash, S::ContainmentError: Hash"), + PartialEq(bound = "G::Error: PartialEq, S::ContainmentError: PartialEq") + )] + pub enum SenderError<C, G, S> + where + C: IdentityConfiguration, + G: SecretKeyGenerator<SecretKey = C::SecretKey>, + S: VerifiedSet<Item = Utxo<C>>, + { + /// Secret Key Generator Error + SecretKeyError(G::Error), + + /// Containment Error + MissingUtxo(S::ContainmentError), - /// Parameter Marker - #[doc(hidden)] - __(Infallible, PhantomData<C>), + /// Type Parameter Marker + #[doc(hidden)] + __(Infallible, PhantomData<C>), + } } +#[doc(inline)] +pub use sender_error::SenderError; + /// Sender pub struct Sender<C, S> where @@ -886,22 +950,22 @@ where S: VerifiedSet<Item = Utxo<C>>, { /// Sender Identity - pub(crate) identity: Identity<C>, + identity: Identity<C>, /// Asset - pub asset: Asset, + asset: Asset, /// Asset Parameters - pub parameters: AssetParameters<C>, + parameters: AssetParameters<C>, /// Void Number - pub void_number: VoidNumber<C>, + void_number: VoidNumber<C>, /// Unspent Transaction Output - pub utxo: Utxo<C>, + utxo: Utxo<C>, /// UTXO Containment Proof - pub utxo_containment_proof: ContainmentProof<S>, + utxo_containment_proof: ContainmentProof<S>, } impl<C, S> Sender<C, S> @@ -919,9 +983,10 @@ where ) -> Result<Self, S::ContainmentError> where Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: ConcatBytes, - VoidNumberGenerator<C>: ConcatBytes, - VoidNumberCommitment<C>: ConcatBytes, + PublicKey<C>: CommitmentInput<C::CommitmentScheme>, + VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, + Asset: CommitmentInput<C::CommitmentScheme>, + VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { identity.into_sender(commitment_scheme, asset, utxo_set) } @@ -938,20 +1003,18 @@ where where G: SecretKeyGenerator<SecretKey = C::SecretKey>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: ConcatBytes, - VoidNumberGenerator<C>: ConcatBytes, - VoidNumberCommitment<C>: ConcatBytes, + PublicKey<C>: CommitmentInput<C::CommitmentScheme>, + VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, + Asset: CommitmentInput<C::CommitmentScheme>, + VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { Identity::generate_sender(source, commitment_scheme, asset, utxo_set) } - /// Extracts ledger posting data for this sender. + /// Returns the asset for this sender. #[inline] - pub fn into_post(self) -> SenderPost<C, S> { - SenderPost { - void_number: self.void_number, - utxo_containment_proof_public_input: self.utxo_containment_proof.into_public_input(), - } + pub fn asset(&self) -> Asset { + self.asset } /// Returns the asset id for this sender. @@ -965,7 +1028,77 @@ where pub fn asset_value(&self) -> AssetBalance { self.asset.value } + + /// Returns the asset parameters for this sender. + #[inline] + pub fn parameters(&self) -> &AssetParameters<C> { + &self.parameters + } + + /// Returns the void number for this sender. + #[inline] + pub fn void_number(&self) -> &VoidNumber<C> { + &self.void_number + } + + /// Returns the UTXO for this sender. + #[inline] + pub fn utxo(&self) -> &Utxo<C> { + &self.utxo + } + + /// Returns the UTXO containment proof for this sender. + #[inline] + pub fn utxo_containment_proof(&self) -> &ContainmentProof<S> { + &self.utxo_containment_proof + } + + /// Extracts ledger posting data for this sender. + #[inline] + pub fn into_post(self) -> SenderPost<C, S> { + SenderPost { + void_number: self.void_number, + utxo_containment_proof_public_input: self.utxo_containment_proof.into_public_input(), + } + } +} + +/* TODO: +/// Sender Proof Content +pub struct SenderProofContent<C, S, P> +where + C: IdentityConfiguration, + S: VerifiedSet<Item = Utxo<C>>, + P: ProofSystem + + Register<C::SecretKey> + + Register<Asset> + + Register<AssetParameters<C>> + + Register<VoidNumber<C>> + + Register<Utxo<C>> + + Register<ContainmentProof<S>>, +{ + /// Secret Key + secret_key: <P as Register<C::SecretKey>>::Output, + + /// Asset + asset: <P as Register<Asset>>::Output, + + /// Asset Parameters + parameters: <P as Register<AssetParameters<C>>>::Output, + + /// Void Number + void_number: <P as Register<VoidNumber<C>>>::Output, + + /// Unspent Transaction Output + utxo: <P as Register<Utxo<C>>>::Output, + + /// UTXO Containment Proof + utxo_containment_proof: <P as Register<ContainmentProof<S>>>::Output, + + /// Type Parameter Marker + __: PhantomData<(C, S, P)>, } +*/ /// Sender Post Error pub enum SenderPostError<L> @@ -991,10 +1124,10 @@ where S: VerifiedSet<Item = Utxo<C>>, { /// Void Number - pub void_number: VoidNumber<C>, + void_number: VoidNumber<C>, /// UTXO Containment Proof Public Input - pub utxo_containment_proof_public_input: S::Public, + utxo_containment_proof_public_input: S::Public, } impl<C, S> SenderPost<C, S> @@ -1018,6 +1151,17 @@ where } } +impl<C, S> From<Sender<C, S>> for SenderPost<C, S> +where + C: IdentityConfiguration, + S: VerifiedSet<Item = Utxo<C>>, +{ + #[inline] + fn from(sender: Sender<C, S>) -> Self { + sender.into_post() + } +} + /// Receiver pub struct Receiver<C, I> where @@ -1025,19 +1169,19 @@ where I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// Asset - pub asset: Asset, + asset: Asset, /// UTXO Randomness - pub utxo_randomness: UtxoRandomness<C>, + utxo_randomness: UtxoRandomness<C>, /// Void Number Commitment - pub void_number_commitment: VoidNumberCommitment<C>, + void_number_commitment: VoidNumberCommitment<C>, /// Unspent Transaction Output - pub utxo: Utxo<C>, + utxo: Utxo<C>, /// Encrypted [`Asset`] - pub encrypted_asset: EncryptedMessage<I>, + encrypted_asset: EncryptedMessage<I>, } impl<C, I> Receiver<C, I> @@ -1055,18 +1199,16 @@ where ) -> Result<Self, I::Error> where R: CryptoRng + RngCore + ?Sized, - VoidNumberCommitment<C>: ConcatBytes, + Asset: CommitmentInput<C::CommitmentScheme>, + VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { identity.into_receiver(commitment_scheme, asset, rng) } - /// Extracts ledger posting data for this receiver. + /// Returns the asset for this receiver. #[inline] - pub fn into_post(self) -> ReceiverPost<C, I> { - ReceiverPost { - utxo: self.utxo, - encrypted_asset: self.encrypted_asset, - } + pub fn asset(&self) -> Asset { + self.asset } /// Returns the asset id for this receiver. @@ -1080,8 +1222,61 @@ where pub fn asset_value(&self) -> AssetBalance { self.asset.value } + + /// Returns the UTXO randomness for this receiver. + #[inline] + pub fn utxo_randomness(&self) -> &UtxoRandomness<C> { + &self.utxo_randomness + } + + /// Returns the void number commitment for this receiver. + #[inline] + pub fn void_number_commitment(&self) -> &VoidNumberCommitment<C> { + &self.void_number_commitment + } + + /// Returns the UTXO for this reciever. + #[inline] + pub fn utxo(&self) -> &Utxo<C> { + &self.utxo + } + + /// Returns the encrypted asset for this receiver. + #[inline] + pub fn encrypted_asset(&self) -> &EncryptedMessage<I> { + &self.encrypted_asset + } + + /// Extracts ledger posting data for this receiver. + #[inline] + pub fn into_post(self) -> ReceiverPost<C, I> { + ReceiverPost { + utxo: self.utxo, + encrypted_asset: self.encrypted_asset, + } + } } +/* TODO: +/// Receiver Proof Content +pub struct ReceiverProofContent<C> +where + C: IdentityConfiguration, +{ + /// Asset + asset: Asset, + + /// UTXO Randomness + utxo_randomness: UtxoRandomness<C>, + + /// Void Number Commitment + void_number_commitment: VoidNumberCommitment<C>, + + /// Unspent Transaction Output + utxo: Utxo<C>, +} +*/ + /// Receiver Post Error pub enum ReceiverPostError<L> where @@ -1106,10 +1301,10 @@ where I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// Unspent Transaction Output - pub utxo: Utxo<C>, + utxo: Utxo<C>, /// Encrypted [`Asset`] - pub encrypted_asset: EncryptedMessage<I>, + encrypted_asset: EncryptedMessage<I>, } impl<C, I> ReceiverPost<C, I> @@ -1132,3 +1327,14 @@ where Ok(()) } } + +impl<C, I> From<Receiver<C, I>> for ReceiverPost<C, I> +where + C: IdentityConfiguration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, +{ + #[inline] + fn from(receiver: Receiver<C, I>) -> Self { + receiver.into_post() + } +} diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 1a9c8fe58..e7fb5f251 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -23,7 +23,10 @@ use crate::{ }, ledger::{Ledger, PostError}, }; -use manta_crypto::{ies::EncryptedMessage, IntegratedEncryptionScheme, VerifiedSet}; +use alloc::vec::Vec; +use manta_crypto::{ + constraints::ProofSystem, ies::EncryptedMessage, IntegratedEncryptionScheme, VerifiedSet, +}; use manta_util::array_map; use rand::{ distributions::{Distribution, Standard}, @@ -79,6 +82,9 @@ pub trait SecretTransferConfiguration: IdentityConfiguration { /// Verified Set for [`Utxo`] type UtxoSet: VerifiedSet<Item = Utxo<Self>>; + + /// Proof System for [`SecretTransfer`] + type ProofSystem: ProofSystem; } /// Secret Transfer Protocol @@ -97,12 +103,24 @@ impl<T, const SENDERS: usize, const RECEIVERS: usize> SecretTransfer<T, SENDERS, where T: SecretTransferConfiguration, { + /// Maximum Number of Senders + pub const MAXIMUM_SENDER_COUNT: usize = 10; + + /// Maximum Number of Receivers + pub const MAXIMUM_RECEIVER_COUNT: usize = 10; + /// Builds a new [`SecretTransfer`]. #[inline] pub fn new( senders: [Sender<T, T::UtxoSet>; SENDERS], receivers: [Receiver<T, T::IntegratedEncryptionScheme>; RECEIVERS], ) -> Self { + if SENDERS > Self::MAXIMUM_SENDER_COUNT { + panic!("Allocated too many senders."); + } + if RECEIVERS > Self::MAXIMUM_RECEIVER_COUNT { + panic!("Allocated too many receivers."); + } Self { senders, receivers } } @@ -137,21 +155,67 @@ where self.sender_sum().eq(&self.receiver_sum()) } + fn build_proof_content( + proof_system: &mut T::ProofSystem, + senders: Vec<()>, + receivers: Vec<()>, + ) { + // FIXME: Build secret transfer zero knowledge proof: + + /* TODO: + // 1. Check that all senders are well-formed. + proof_system.assert_all(senders.iter().map(|s| s.is_well_formed())); + + // 2. Check that all receivers are well-formed. + proof_system.assert_all(receivers.iter().map(|r| r.is_well_formed())); + + // 3. Check that there is a unique asset id for all the assets. + let sender_ids = senders.iter().map(|s| s.asset_id()); + let receiver_ids = receivers.iter().map(|r| r.asset_id()); + proof_system.assert_all_eq(sender_ids.chain(receiver_ids)); + + // 4. Check that the transaction is balanced. + proof_system.assert_eq( + senders.iter().map(|s| s.asset_value()).sum(), + receivers.iter().map(|r| r.asset_value()).sum(), + ); + */ + + let _ = (proof_system, senders, receivers); + } + #[inline] - fn generate_validity_proof(&self) { - // FIXME: build zkp + fn generate_validity_proof(&self) -> Option<<T::ProofSystem as ProofSystem>::Proof> { + // FIXME: Build secret transfer zero knowledge proof: + + /* + let mut proof_system = T::ProofSystem::default(); + let senders = self + .senders + .iter() + .map(|s| proof_system.register(s)) + .collect::<Vec<_>>(); + let receivers = self + .receivers + .iter() + .map(|r| proof_system.register(r)) + .collect::<Vec<_>>(); + Self::build_proof_content(&mut proof_system, senders, receivers); + proof_system.finish() + */ + todo!() } /// Converts `self` into its ledger post. #[inline] - pub fn into_post(self) -> SecretTransferPost<T, SENDERS, RECEIVERS> { - let validity_proof = self.generate_validity_proof(); - SecretTransferPost { + pub fn into_post(self) -> Option<SecretTransferPost<T, SENDERS, RECEIVERS>> { + let validity_proof = self.generate_validity_proof()?; + Some(SecretTransferPost { sender_posts: array_map(self.senders, Sender::into_post), receiver_posts: array_map(self.receivers, Receiver::into_post), validity_proof, - } + }) } } @@ -167,7 +231,7 @@ where pub receiver_posts: [ReceiverPost<T, T::IntegratedEncryptionScheme>; RECEIVERS], /// Validity Proof - pub validity_proof: (), + pub validity_proof: <T::ProofSystem as ProofSystem>::Proof, } impl<T, const SENDERS: usize, const RECEIVERS: usize> SecretTransferPost<T, SENDERS, RECEIVERS> @@ -197,6 +261,17 @@ where } } +impl<T, const SENDERS: usize, const RECEIVERS: usize> From<SecretTransfer<T, SENDERS, RECEIVERS>> + for Option<SecretTransferPost<T, SENDERS, RECEIVERS>> +where + T: SecretTransferConfiguration, +{ + #[inline] + fn from(secret_transfer: SecretTransfer<T, SENDERS, RECEIVERS>) -> Self { + secret_transfer.into_post() + } +} + /// Transfer Protocol pub struct Transfer< T, @@ -230,11 +305,11 @@ where /// Converts `self` into its ledger post. #[inline] - pub fn into_post(self) -> TransferPost<T, SOURCES, SINKS, SENDERS, RECEIVERS> { - TransferPost { + pub fn into_post(self) -> Option<TransferPost<T, SOURCES, SINKS, SENDERS, RECEIVERS>> { + Some(TransferPost { public_transfer_post: self.public, - secret_transfer_post: self.secret.into_post(), - } + secret_transfer_post: self.secret.into_post()?, + }) } } diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index f5dbfcf9b..7cff81db6 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -24,7 +24,7 @@ use crate::{ asset::{Asset, AssetBalance, AssetId}, identity::{ - AssetParameters, Identity, IdentityConfiguration, OpenSpend, PublicKey, Receiver, Sender, + AssetParameters, Identity, IdentityConfiguration, OpenSpend, PublicKey, Receiver, ShieldedIdentity, Spend, VoidNumberCommitment, VoidNumberGenerator, }, keys::{self, DerivedSecretKeyGenerator, ExternalKeys, InternalKeys}, @@ -32,7 +32,7 @@ use crate::{ transfer::{SecretTransfer, SecretTransferConfiguration}, }; use core::{convert::Infallible, fmt::Debug, hash::Hash}; -use manta_crypto::{ies::EncryptedMessage, ConcatBytes, IntegratedEncryptionScheme}; +use manta_crypto::{ies::EncryptedMessage, CommitmentInput, IntegratedEncryptionScheme}; use rand::{ distributions::{Distribution, Standard}, CryptoRng, RngCore, @@ -345,8 +345,8 @@ where C: IdentityConfiguration<SecretKey = D::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: ConcatBytes, - VoidNumberGenerator<C>: ConcatBytes, + PublicKey<C>: CommitmentInput<C::CommitmentScheme>, + VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, { self.next_external_identity() .map(move |identity| identity.into_shielded(commitment_scheme)) @@ -367,9 +367,10 @@ where I: IntegratedEncryptionScheme<Plaintext = Asset>, R: CryptoRng + RngCore + ?Sized, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: ConcatBytes, - VoidNumberGenerator<C>: ConcatBytes, - VoidNumberCommitment<C>: ConcatBytes, + PublicKey<C>: CommitmentInput<C::CommitmentScheme>, + VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, + Asset: CommitmentInput<C::CommitmentScheme>, + VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { let (shielded_identity, spend) = self .next_internal_identity() @@ -394,7 +395,8 @@ where where T: SecretTransferConfiguration, R: CryptoRng + RngCore + ?Sized, - VoidNumberCommitment<T>: ConcatBytes, + Asset: CommitmentInput<T::CommitmentScheme>, + VoidNumberCommitment<T>: CommitmentInput<T::CommitmentScheme>, { // TODO: spec: // 1. check that we have enough `asset` in the secret_assets map diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index fdf075fbd..e94bad044 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -14,41 +14,42 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Commitments - -use core::borrow::Borrow; -use rand::RngCore; +//! Commitment Schemes /// Commitment Scheme pub trait CommitmentScheme { + /// Commitment Input Buffer Type + type InputBuffer; + /// Commitment Randomness Parameter Type type Randomness; /// Commitment Output Type - type Output: PartialEq; - - /// Samples random commitment paramters. - fn setup<R>(rng: &mut R) -> Self - where - R: RngCore; + type Output; - /// Commits the `input` with the given `randomness` parameter. - fn commit<I>(&self, input: I, randomness: &Self::Randomness) -> Self::Output - where - I: Borrow<[u8]>; + /// Returns a new [`InputBuffer`](Self::InputBuffer) for building commitments. + #[must_use = "the input buffer is the only way to build a commitment"] + fn start(&self) -> Self::InputBuffer; - /// Checks that the `output` matches the commitment of the `input` with the given `randomness` - /// parameter. + /// Updates the `buffer` with `input`. #[inline] - fn check_commitment<I>( - &self, - input: I, - randomness: &Self::Randomness, - output: &Self::Output, - ) -> bool + fn update<I>(&self, buffer: &mut Self::InputBuffer, input: &I) -> &Self where - I: Borrow<[u8]>, + I: CommitmentInput<Self>, { - &self.commit(input, randomness) == output.borrow() + CommitmentInput::extend(input, buffer); + self } + + /// Commits the `input` buffer with the given `randomness` parameter. + fn commit(&self, input: Self::InputBuffer, randomness: &Self::Randomness) -> Self::Output; +} + +/// Commitment Input +pub trait CommitmentInput<C> +where + C: CommitmentScheme + ?Sized, +{ + /// Extends the input buffer. + fn extend(&self, buffer: &mut C::InputBuffer); } diff --git a/manta-crypto/src/concat.rs b/manta-crypto/src/concat.rs deleted file mode 100644 index 3f0709bb5..000000000 --- a/manta-crypto/src/concat.rs +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Concatenation and Byte Accumulators - -use alloc::vec::Vec; -use core::borrow::Borrow; - -/// Byte Accumulation Trait -pub trait ByteAccumulator { - /// Extends the current accumulator by a `buffer` of elements. - fn extend(&mut self, buffer: &[u8]); - - /// Reserves space in the accumulator for `additional` more elements. - #[inline] - fn reserve(&mut self, additional: usize) { - let _ = additional; - } - - /// Drops extra capacity in the accumulator. - #[inline] - fn shrink_to_fit(&mut self) {} - - /// Captures the accumulator and drops extra capacity before returning an owned copy. - #[inline] - fn finish(mut self) -> Self - where - Self: Sized, - { - self.shrink_to_fit(); - self - } - - /// Creates a "by mutable reference" adaptor for this instance of [`ByteAccumulator`]. - #[inline] - fn by_ref(&mut self) -> &mut Self - where - Self: Sized, - { - self - } -} - -impl<A> ByteAccumulator for &mut A -where - A: ByteAccumulator + ?Sized, -{ - #[inline] - fn extend(&mut self, buffer: &[u8]) { - (**self).extend(buffer) - } - - #[inline] - fn reserve(&mut self, additional: usize) { - (**self).reserve(additional) - } - - #[inline] - fn shrink_to_fit(&mut self) { - (**self).shrink_to_fit() - } -} - -impl ByteAccumulator for Vec<u8> { - #[inline] - fn extend(&mut self, buffer: &[u8]) { - self.extend_from_slice(buffer) - } - - #[inline] - fn reserve(&mut self, additional: usize) { - self.reserve(additional) - } - - #[inline] - fn shrink_to_fit(&mut self) { - self.shrink_to_fit() - } -} - -/// Byte Concatenation Trait -pub trait ConcatBytes { - /// Concatenates `self` on the end of the accumulator. - /// - /// # Note - /// - /// Implementations should not ask to reserve additional space for elements in this method. - /// Instead, reimplement [`reserve_concat`](Self::reserve_concat) if the default implementation - /// is not efficient. - fn concat<A>(&self, accumulator: &mut A) - where - A: ByteAccumulator + ?Sized; - - /// Returns a hint to the possible number of bytes that will be accumulated when concatenating - /// `self`. - #[inline] - fn size_hint(&self) -> Option<usize> { - None - } - - /// Concatenates `self` on the end of the accumulator after trying to reserve space for it. - #[inline] - fn reserve_concat<A>(&self, accumulator: &mut A) - where - A: ByteAccumulator + ?Sized, - { - if let Some(capacity) = self.size_hint() { - accumulator.reserve(capacity); - } - self.concat(accumulator) - } - - /// Constructs a default accumulator and accumulates over `self`, reserving the appropriate - /// capacity. - #[inline] - fn as_bytes<A>(&self) -> A - where - A: Default + ByteAccumulator, - { - let mut accumulator = A::default(); - self.reserve_concat(&mut accumulator); - accumulator.finish() - } -} - -impl<T> ConcatBytes for T -where - T: Borrow<[u8]> + ?Sized, -{ - #[inline] - fn concat<A>(&self, accumulator: &mut A) - where - A: ByteAccumulator + ?Sized, - { - accumulator.extend(self.borrow()) - } - - #[inline] - fn size_hint(&self) -> Option<usize> { - Some(self.borrow().len()) - } -} - -/// Concatenates `$item`s together by building a [`ByteAccumulator`] and running -/// [`ConcatBytes::concat`] over each `$item`. -#[macro_export] -macro_rules! concatenate { - ($($item:expr),*) => { - { - extern crate alloc; - let mut accumulator = ::alloc::vec::Vec::new(); - $($crate::ConcatBytes::reserve_concat($item, &mut accumulator);)* - $crate::ByteAccumulator::finish(accumulator) - } - } -} - -/// Returns byte vector representation of `$item`. -#[macro_export] -macro_rules! as_bytes { - ($item:expr) => { - $crate::concatenate!($item) - }; -} diff --git a/manta-crypto/src/constraints.rs b/manta-crypto/src/constraints.rs index cd59fca6c..8d45474b0 100644 --- a/manta-crypto/src/constraints.rs +++ b/manta-crypto/src/constraints.rs @@ -16,6 +16,94 @@ //! Constraint Systems and Zero Knowledge Proofs +use core::borrow::Borrow; + +/// +pub trait Register<T>: ProofSystem { + /// + type Output; + + /// + fn allocate<B, Opt>(&mut self, t: Opt) -> Self::Output + where + B: Borrow<T>, + Opt: Into<Option<B>>; + + /// + #[inline] + fn variable(&mut self) -> Self::Output { + Register::allocate::<T, _>(self, None) + } +} + +/// +pub trait ProofSystem: Default { + /// + type Variable; + + /// + type Proof; + + /// + type Error; + + /// + #[inline] + fn allocate<T, B>(&mut self, t: impl Into<Option<B>>) -> <Self as Register<T>>::Output + where + B: Borrow<T>, + Self: Register<T>, + { + Register::allocate(self, t) + } + + /// + fn assert(&mut self, b: ()); + + /// + #[inline] + fn assert_all<I>(&mut self, iter: I) + where + I: IntoIterator<Item = ()>, + { + for boolean in iter { + self.assert(boolean) + } + } + + /// + #[inline] + fn assert_eq(&mut self, lhs: &Self::Variable, rhs: &Self::Variable) { + // TODO: self.assert(lhs.eq(rhs)) + let _ = (lhs, rhs); + todo!() + } + + /// + #[inline] + fn assert_all_eq<'i, I>(&mut self, iter: I) + where + Self::Variable: 'i, + I: IntoIterator<Item = &'i Self::Variable>, + { + let mut iter = iter.into_iter(); + if let Some(base) = iter.next() { + for item in iter { + self.assert_eq(base, item); + } + } + } +} + +/// +pub trait ProofBuilder { + /// + fn build<P>(&self, proof_system: &mut P) + where + P: ProofSystem; +} + +/* TODO: use alloc::vec::Vec; /// @@ -93,3 +181,4 @@ pub trait ConstraintSynthesizer<T> { /// fn input(&self) -> Vec<&T>; } +*/ diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index a24886ab0..0a1f769a8 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -24,7 +24,6 @@ extern crate alloc; mod commitment; -mod concat; mod prf; pub mod constraints; @@ -32,7 +31,6 @@ pub mod ies; pub mod set; pub use commitment::*; -pub use concat::*; pub use ies::prelude::*; pub use prf::*; pub use set::prelude::*; diff --git a/manta-crypto/src/prf.rs b/manta-crypto/src/prf.rs index aa82b5e4a..8734eb7c7 100644 --- a/manta-crypto/src/prf.rs +++ b/manta-crypto/src/prf.rs @@ -19,7 +19,7 @@ /// Pseudorandom Function Families (PRF) Trait pub trait PseudorandomFunctionFamily { /// PRF Seed Type - type Seed: ?Sized; + type Seed; /// PRF Input Type type Input: Default; diff --git a/manta-pay/src/accounting/keys.rs b/manta-pay/src/accounting/keys.rs index b1c2e640a..4d3f2a6d4 100644 --- a/manta-pay/src/accounting/keys.rs +++ b/manta-pay/src/accounting/keys.rs @@ -160,7 +160,7 @@ where /// Derived Key Seed seed: Seed, - /// Parameter Marker + /// Type Parameter Marker __: PhantomData<C>, } diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs index 856d5761b..87cfdfc34 100644 --- a/manta-pay/src/crypto/commitment/pedersen.rs +++ b/manta-pay/src/crypto/commitment/pedersen.rs @@ -16,14 +16,13 @@ //! Pedersen Commitment Implementation +use alloc::vec::Vec; use ark_crypto_primitives::commitment::{ pedersen::{Commitment, Window}, CommitmentScheme as ArkCommitmentScheme, }; use ark_ed_on_bls12_381::EdwardsProjective; -use core::borrow::Borrow; use manta_crypto::CommitmentScheme; -use rand::RngCore; /// Implementation of [`CommitmentScheme`] #[derive(Clone)] @@ -50,24 +49,20 @@ impl Window for PedersenWindow { pub type ArkPedersenCommitment = Commitment<EdwardsProjective, PedersenWindow>; impl CommitmentScheme for PedersenCommitment { + type InputBuffer = Vec<u8>; + type Randomness = <ArkPedersenCommitment as ArkCommitmentScheme>::Randomness; type Output = <ArkPedersenCommitment as ArkCommitmentScheme>::Output; #[inline] - fn setup<R>(rng: &mut R) -> Self - where - R: RngCore, - { - Self(ArkPedersenCommitment::setup(rng).expect("As of arkworks 0.3.0, this never fails.")) + fn start(&self) -> Self::InputBuffer { + Default::default() } #[inline] - fn commit<I>(&self, input: I, randomness: &Self::Randomness) -> Self::Output - where - I: Borrow<[u8]>, - { - ArkPedersenCommitment::commit(&self.0, input.borrow(), randomness) + fn commit(&self, input: Self::InputBuffer, randomness: &Self::Randomness) -> Self::Output { + ArkPedersenCommitment::commit(&self.0, &input, randomness) .expect("As of arkworks 0.3.0, this never fails.") } } diff --git a/manta-pay/src/crypto/merkle_tree.rs b/manta-pay/src/crypto/merkle_tree.rs index aab060777..a8acae8ee 100644 --- a/manta-pay/src/crypto/merkle_tree.rs +++ b/manta-pay/src/crypto/merkle_tree.rs @@ -31,11 +31,8 @@ use ark_crypto_primitives::{ }; use ark_ed_on_bls12_381::EdwardsProjective; use core::marker::PhantomData; -use manta_crypto::{ - as_bytes, - set::{ContainmentProof, VerifiedSet, VerifyContainment}, - ConcatBytes, -}; +use manta_crypto::set::{ContainmentProof, VerifiedSet, VerifyContainment}; +use manta_util::{as_bytes, ConcatBytes}; /// Merkle Tree Root pub type Root = TwoToOneDigest<MerkleTreeConfiguration>; @@ -59,7 +56,7 @@ pub struct Path<T> { /// Path path: MerkleTreePath<MerkleTreeConfiguration>, - /// Marker + /// Type Parameter Marker __: PhantomData<T>, } @@ -84,7 +81,7 @@ pub struct MerkleTree<T> { /// Merkle Tree tree: ArkMerkleTree<MerkleTreeConfiguration>, - /// Marker + /// Type Parameter Marker __: PhantomData<T>, } diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index 4f6bd9e9e..7070dfc82 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -24,7 +24,7 @@ extern crate alloc; use alloc::vec::Vec; -use core::convert::TryInto; +use core::{borrow::Borrow, convert::TryInto}; /// Implements [`From`]`<$from>` for an enum `$to`, choosing the `$kind` variant. // TODO: add `where` clauses @@ -58,7 +58,6 @@ pub fn array_map<T, U, F, const N: usize>(array: [T; N], f: F) -> [U; N] where F: FnMut(T) -> U, { - // TODO: get rid of this function when `array::map` is stabilized into_array_unchecked(IntoIterator::into_iter(array).map(f).collect::<Vec<_>>()) } @@ -84,3 +83,172 @@ where .collect::<Result<Vec<_>, _>>()?, )) } + +/// Maps `f` over the `array` by reference returning the target array if all of the mappings +/// succeeded, or returning the first error that occurs. +#[inline] +pub fn fallible_array_map_ref<T, U, E, F, const N: usize>(array: &[T; N], f: F) -> Result<[U; N], E> +where + F: FnMut(&T) -> Result<U, E>, +{ + Ok(into_array_unchecked( + array.iter().map(f).collect::<Result<Vec<_>, _>>()?, + )) +} + +/// Byte Accumulation Trait +pub trait ByteAccumulator { + /// Extends the current accumulator by a `buffer` of elements. + fn extend(&mut self, buffer: &[u8]); + + /// Reserves space in the accumulator for `additional` more elements. + #[inline] + fn reserve(&mut self, additional: usize) { + let _ = additional; + } + + /// Drops extra capacity in the accumulator. + #[inline] + fn shrink_to_fit(&mut self) {} + + /// Captures the accumulator and drops extra capacity before returning an owned copy. + #[inline] + fn finish(mut self) -> Self + where + Self: Sized, + { + self.shrink_to_fit(); + self + } + + /// Creates a "by mutable reference" adaptor for this instance of [`ByteAccumulator`]. + #[inline] + fn by_ref(&mut self) -> &mut Self + where + Self: Sized, + { + self + } +} + +impl<A> ByteAccumulator for &mut A +where + A: ByteAccumulator + ?Sized, +{ + #[inline] + fn extend(&mut self, buffer: &[u8]) { + (**self).extend(buffer) + } + + #[inline] + fn reserve(&mut self, additional: usize) { + (**self).reserve(additional) + } + + #[inline] + fn shrink_to_fit(&mut self) { + (**self).shrink_to_fit() + } +} + +impl ByteAccumulator for Vec<u8> { + #[inline] + fn extend(&mut self, buffer: &[u8]) { + self.extend_from_slice(buffer) + } + + #[inline] + fn reserve(&mut self, additional: usize) { + self.reserve(additional) + } + + #[inline] + fn shrink_to_fit(&mut self) { + self.shrink_to_fit() + } +} + +/// Byte Concatenation Trait +pub trait ConcatBytes { + /// Concatenates `self` on the end of the accumulator. + /// + /// # Note + /// + /// Implementations should not ask to reserve additional space for elements in this method. + /// Instead, reimplement [`reserve_concat`](Self::reserve_concat) if the default implementation + /// is not efficient. + fn concat<A>(&self, accumulator: &mut A) + where + A: ByteAccumulator + ?Sized; + + /// Returns a hint to the possible number of bytes that will be accumulated when concatenating + /// `self`. + #[inline] + fn size_hint(&self) -> Option<usize> { + None + } + + /// Concatenates `self` on the end of the accumulator after trying to reserve space for it. + #[inline] + fn reserve_concat<A>(&self, accumulator: &mut A) + where + A: ByteAccumulator + ?Sized, + { + if let Some(capacity) = self.size_hint() { + accumulator.reserve(capacity); + } + self.concat(accumulator) + } + + /// Constructs a default accumulator and accumulates over `self`, reserving the appropriate + /// capacity. + #[inline] + fn as_bytes<A>(&self) -> A + where + A: Default + ByteAccumulator, + { + let mut accumulator = A::default(); + self.reserve_concat(&mut accumulator); + accumulator.finish() + } +} + +impl<T> ConcatBytes for T +where + T: Borrow<[u8]> + ?Sized, +{ + #[inline] + fn concat<A>(&self, accumulator: &mut A) + where + A: ByteAccumulator + ?Sized, + { + accumulator.extend(self.borrow()) + } + + #[inline] + fn size_hint(&self) -> Option<usize> { + Some(self.borrow().len()) + } +} + +/// Concatenates `$item`s together by building a [`ByteAccumulator`] and running +/// [`ConcatBytes::concat`] over each `$item`. +#[macro_export] +macro_rules! concatenate { + ($($item:expr),*) => { + { + extern crate alloc; + let mut accumulator = ::alloc::vec::Vec::new(); + $($crate::ConcatBytes::reserve_concat($item, &mut accumulator);)* + $crate::ByteAccumulator::finish(accumulator) + } + } +} + +/// Returns byte vector representation of `$item`. +#[macro_export] +macro_rules! as_bytes { + ($item:expr) => { + $crate::concatenate!($item) + }; +} From a23639e0a8b006dc65e6ec61e4e9148f882c141a Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 9 Sep 2021 17:17:41 -0400 Subject: [PATCH 020/275] add commitment builder --- manta-accounting/src/identity.rs | 25 +++------ manta-crypto/src/commitment.rs | 58 ++++++++++++++++----- manta-pay/src/crypto/commitment/pedersen.rs | 5 -- 3 files changed, 54 insertions(+), 34 deletions(-) diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 9c2b01777..45f38e824 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -115,17 +115,11 @@ where PublicKey<C>: CommitmentInput<C::CommitmentScheme>, VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, { - let mut buffer = commitment_scheme.start(); commitment_scheme - .update(&mut buffer, public_key) - .update(&mut buffer, void_number_generator) - .commit(buffer, void_number_commitment_randomness) - /* TODO[remove]: - commitment_scheme.commit( - concatenate!(public_key, void_number_generator), - void_number_commitment_randomness, - ) - */ + .start() + .update(public_key) + .update(void_number_generator) + .commit(void_number_commitment_randomness) } /// Generates a [`Utxo`] from a given `asset`, `void_number_commitment`, and `utxo_randomness`. @@ -141,14 +135,11 @@ where Asset: CommitmentInput<C::CommitmentScheme>, VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { - let mut buffer = commitment_scheme.start(); commitment_scheme - .update(&mut buffer, asset) - .update(&mut buffer, void_number_commitment) - .commit(buffer, utxo_randomness) - /* TODO[remove]: - commitment_scheme.commit(concatenate!(asset, void_number_commitment), utxo_randomness) - */ + .start() + .update(asset) + .update(void_number_commitment) + .commit(utxo_randomness) } /// Public Parameters for using an [`Asset`] diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index e94bad044..85ddbd7fd 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -19,7 +19,7 @@ /// Commitment Scheme pub trait CommitmentScheme { /// Commitment Input Buffer Type - type InputBuffer; + type InputBuffer: Default; /// Commitment Randomness Parameter Type type Randomness; @@ -27,18 +27,10 @@ pub trait CommitmentScheme { /// Commitment Output Type type Output; - /// Returns a new [`InputBuffer`](Self::InputBuffer) for building commitments. - #[must_use = "the input buffer is the only way to build a commitment"] - fn start(&self) -> Self::InputBuffer; - - /// Updates the `buffer` with `input`. + /// Returns a new [`CommitmentBuilder`] to build up a commitment. #[inline] - fn update<I>(&self, buffer: &mut Self::InputBuffer, input: &I) -> &Self - where - I: CommitmentInput<Self>, - { - CommitmentInput::extend(input, buffer); - self + fn start(&self) -> CommitmentBuilder<Self> { + CommitmentBuilder::new(self) } /// Commits the `input` buffer with the given `randomness` parameter. @@ -53,3 +45,45 @@ where /// Extends the input buffer. fn extend(&self, buffer: &mut C::InputBuffer); } + +/// Commitment Builder +pub struct CommitmentBuilder<'c, C> +where + C: CommitmentScheme + ?Sized, +{ + /// Commitment Scheme + commitment_scheme: &'c C, + + /// Input Buffer + input_buffer: C::InputBuffer, +} + +impl<'c, C> CommitmentBuilder<'c, C> +where + C: CommitmentScheme + ?Sized, +{ + /// Returns a new [`CommitmentBuilder`] for this `commitment_scheme`. + #[inline] + pub fn new(commitment_scheme: &'c C) -> Self { + Self { + commitment_scheme, + input_buffer: Default::default(), + } + } + + /// Updates the builder with new `input`. + #[inline] + pub fn update<I>(mut self, input: &I) -> Self + where + I: CommitmentInput<C>, + { + CommitmentInput::extend(input, &mut self.input_buffer); + self + } + + /// Commits to the input stored in the builder with the given `randomness`. + #[inline] + pub fn commit(self, randomness: &C::Randomness) -> C::Output { + self.commitment_scheme.commit(self.input_buffer, randomness) + } +} diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs index 87cfdfc34..4b5e9f544 100644 --- a/manta-pay/src/crypto/commitment/pedersen.rs +++ b/manta-pay/src/crypto/commitment/pedersen.rs @@ -55,11 +55,6 @@ impl CommitmentScheme for PedersenCommitment { type Output = <ArkPedersenCommitment as ArkCommitmentScheme>::Output; - #[inline] - fn start(&self) -> Self::InputBuffer { - Default::default() - } - #[inline] fn commit(&self, input: Self::InputBuffer, randomness: &Self::Randomness) -> Self::Output { ArkPedersenCommitment::commit(&self.0, &input, randomness) From 6aab270f22cd45095a20c800c9458eb50ff64ea1 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 10 Sep 2021 01:12:25 -0400 Subject: [PATCH 021/275] add preliminary proof-system abstraction --- manta-accounting/src/asset.rs | 51 +++- manta-accounting/src/identity.rs | 267 +++++++++++++++++--- manta-accounting/src/transfer.rs | 83 ++++-- manta-accounting/src/wallet.rs | 4 +- manta-crypto/src/commitment.rs | 23 +- manta-crypto/src/constraint.rs | 113 +++++++++ manta-crypto/src/constraints.rs | 184 -------------- manta-crypto/src/lib.rs | 8 +- manta-pay/src/crypto/commitment/pedersen.rs | 2 +- 9 files changed, 476 insertions(+), 259 deletions(-) create mode 100644 manta-crypto/src/constraint.rs delete mode 100644 manta-crypto/src/constraints.rs diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index db6d8877c..f5a9852c4 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -16,8 +16,9 @@ //! Assets -// TODO: add macro to build `AssetId` and `AssetBalance` -// TODO: implement all `rand` sampling traits +// TODO: Add macro to build `AssetId` and `AssetBalance`. +// TODO: Implement all `rand` sampling traits. +// TODO: Should we rename `AssetBalance` to `AssetValue` to be more consistent? use alloc::vec::Vec; use core::{ @@ -29,6 +30,7 @@ use core::{ use derive_more::{ Add, AddAssign, Display, Div, DivAssign, From, Mul, MulAssign, Product, Sub, SubAssign, Sum, }; +use manta_crypto::constraint::{Alloc, ProofSystem, Variable}; use manta_util::{ array_map, fallible_array_map, into_array_unchecked, ByteAccumulator, ConcatBytes, }; @@ -351,6 +353,51 @@ impl Distribution<Asset> for Standard { } } +/// Asset Id Variable +pub type AssetIdVariable<P> = Variable<P, AssetId>; + +/// Asset Balance Variable +pub type AssetBalanceVariable<P> = Variable<P, AssetBalance>; + +/// Asset Variable +pub struct AssetVariable<P> +where + P: ProofSystem, + AssetId: Alloc<P>, + AssetBalance: Alloc<P>, +{ + /// Asset Id + pub id: AssetIdVariable<P>, + + /// Asset Value + pub value: AssetBalanceVariable<P>, +} + +impl<P> Alloc<P> for Asset +where + P: ProofSystem, + AssetId: Alloc<P>, + AssetBalance: Alloc<P>, +{ + type Output = AssetVariable<P>; + + #[inline] + fn as_variable(&self, ps: &mut P) -> Self::Output { + Self::Output { + id: self.id.as_variable(ps), + value: self.value.as_variable(ps), + } + } + + #[inline] + fn unknown(ps: &mut P) -> Self::Output { + Self::Output { + id: AssetId::unknown(ps), + value: AssetBalance::unknown(ps), + } + } +} + /// Asset Collection #[derive(Clone, Copy, Debug, Eq, From, Hash, Ord, PartialEq, PartialOrd)] #[from(forward)] diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 45f38e824..dd1bb5cb0 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -28,10 +28,11 @@ use crate::{ }; use core::{convert::Infallible, fmt::Debug, hash::Hash, marker::PhantomData}; use manta_crypto::{ - ies::{self, EncryptedMessage}, - set::ContainmentProof, - CommitmentInput, CommitmentScheme, IntegratedEncryptionScheme, PseudorandomFunctionFamily, - VerifiedSet, + commitment::{CommitmentScheme, Input as CommitmentInput}, + constraint::{Alloc, Bool, ProofSystem, Variable}, + ies::{self, EncryptedMessage, IntegratedEncryptionScheme}, + set::{ContainmentProof, VerifiedSet}, + PseudorandomFunctionFamily, }; use rand::{ distributions::{Distribution, Standard}, @@ -171,7 +172,7 @@ pub struct AssetParameters<C> where C: IdentityConfiguration, { - /// Void Number Generation Parameter + /// Void Number Generator pub void_number_generator: VoidNumberGenerator<C>, /// Void Number Commitment Randomness @@ -252,6 +253,56 @@ where } } +/// Asset Parameters Variable +pub struct AssetParametersVariable<C, P> +where + C: IdentityConfiguration, + P: ProofSystem, + VoidNumberGenerator<C>: Alloc<P>, + VoidNumberCommitmentRandomness<C>: Alloc<P>, + UtxoRandomness<C>: Alloc<P>, +{ + /// Void Number Generator + pub void_number_generator: Variable<P, VoidNumberGenerator<C>>, + + /// Void Number Commitment Randomness + pub void_number_commitment_randomness: Variable<P, VoidNumberCommitmentRandomness<C>>, + + /// UTXO Randomness + pub utxo_randomness: Variable<P, UtxoRandomness<C>>, +} + +impl<C, P> Alloc<P> for AssetParameters<C> +where + C: IdentityConfiguration, + P: ProofSystem, + VoidNumberGenerator<C>: Alloc<P>, + VoidNumberCommitmentRandomness<C>: Alloc<P>, + UtxoRandomness<C>: Alloc<P>, +{ + type Output = AssetParametersVariable<C, P>; + + #[inline] + fn as_variable(&self, ps: &mut P) -> Self::Output { + Self::Output { + void_number_generator: self.void_number_generator.as_variable(ps), + void_number_commitment_randomness: self + .void_number_commitment_randomness + .as_variable(ps), + utxo_randomness: self.utxo_randomness.as_variable(ps), + } + } + + #[inline] + fn unknown(ps: &mut P) -> Self::Output { + Self::Output { + void_number_generator: VoidNumberGenerator::<C>::unknown(ps), + void_number_commitment_randomness: VoidNumberCommitmentRandomness::<C>::unknown(ps), + utxo_randomness: UtxoRandomness::<C>::unknown(ps), + } + } +} + /// Account Identity pub struct Identity<C> where @@ -1054,42 +1105,121 @@ where } } -/* TODO: -/// Sender Proof Content -pub struct SenderProofContent<C, S, P> +/// Sender Variable +pub struct SenderVariable<C, S, P> where C: IdentityConfiguration, S: VerifiedSet<Item = Utxo<C>>, - P: ProofSystem - + Register<C::SecretKey> - + Register<Asset> - + Register<AssetParameters<C>> - + Register<VoidNumber<C>> - + Register<Utxo<C>> - + Register<ContainmentProof<S>>, + P: ProofSystem, + C::SecretKey: Alloc<P>, + AssetId: Alloc<P>, + AssetBalance: Alloc<P>, + AssetParameters<C>: Alloc<P>, + VoidNumber<C>: Alloc<P>, + Utxo<C>: Alloc<P>, + ContainmentProof<S>: Alloc<P>, { /// Secret Key - secret_key: <P as Register<C::SecretKey>>::Output, + secret_key: Variable<P, C::SecretKey>, /// Asset - asset: <P as Register<Asset>>::Output, + asset: Variable<P, Asset>, /// Asset Parameters - parameters: <P as Register<AssetParameters<C>>>::Output, + parameters: Variable<P, AssetParameters<C>>, /// Void Number - void_number: <P as Register<VoidNumber<C>>>::Output, + void_number: Variable<P, VoidNumber<C>>, /// Unspent Transaction Output - utxo: <P as Register<Utxo<C>>>::Output, + utxo: Variable<P, Utxo<C>>, /// UTXO Containment Proof - utxo_containment_proof: <P as Register<ContainmentProof<S>>>::Output, + utxo_containment_proof: Variable<P, ContainmentProof<S>>, +} + +impl<C, S, P> SenderVariable<C, S, P> +where + C: IdentityConfiguration, + S: VerifiedSet<Item = Utxo<C>>, + P: ProofSystem, + C::SecretKey: Alloc<P>, + AssetId: Alloc<P>, + AssetBalance: Alloc<P>, + AssetParameters<C>: Alloc<P>, + VoidNumber<C>: Alloc<P>, + Utxo<C>: Alloc<P>, + ContainmentProof<S>: Alloc<P>, +{ + /// Returns the asset id of this sender. + #[inline] + pub fn asset_id(&self) -> &Variable<P, AssetId> { + &self.asset.id + } + + /// Returns the asset value of this sender. + #[inline] + pub fn asset_value(&self) -> &Variable<P, AssetBalance> { + &self.asset.value + } + + /// Checks if `self` is a well-formed sender. + #[inline] + pub fn is_well_formed(&self) -> Bool<P> + where + bool: Alloc<P>, + { + // FIXME: Implement well-formedness check: + // + // See `manta-api/src/zkp/circuit.rs`: + // 1. ValidSenderToken + // 2. ValidPrivateKey + // 3. ValidVoidNumber + // 4. StoredCommitment + + todo!() + } +} + +impl<C, S, P> Alloc<P> for Sender<C, S> +where + C: IdentityConfiguration, + S: VerifiedSet<Item = Utxo<C>>, + P: ProofSystem, + C::SecretKey: Alloc<P>, + AssetId: Alloc<P>, + AssetBalance: Alloc<P>, + AssetParameters<C>: Alloc<P>, + VoidNumber<C>: Alloc<P>, + Utxo<C>: Alloc<P>, + ContainmentProof<S>: Alloc<P>, +{ + type Output = SenderVariable<C, S, P>; + + #[inline] + fn as_variable(&self, ps: &mut P) -> Self::Output { + Self::Output { + secret_key: self.identity.secret_key.as_variable(ps), + asset: self.asset.as_variable(ps), + parameters: self.parameters.as_variable(ps), + void_number: self.void_number.as_variable(ps), + utxo: self.utxo.as_variable(ps), + utxo_containment_proof: self.utxo_containment_proof.as_variable(ps), + } + } - /// Type Parameter Marker - __: PhantomData<(C, S, P)>, + #[inline] + fn unknown(ps: &mut P) -> Self::Output { + Self::Output { + secret_key: C::SecretKey::unknown(ps), + asset: Asset::unknown(ps), + parameters: AssetParameters::<_>::unknown(ps), + void_number: VoidNumber::<C>::unknown(ps), + utxo: Utxo::<C>::unknown(ps), + utxo_containment_proof: ContainmentProof::<_>::unknown(ps), + } + } } -*/ /// Sender Post Error pub enum SenderPostError<L> @@ -1248,25 +1378,100 @@ where } } -/* TODO: /// Receiver Proof Content -pub struct ReceiverProofContent<C> +pub struct ReceiverVariable<C, P> where C: IdentityConfiguration, + P: ProofSystem, + AssetId: Alloc<P>, + AssetBalance: Alloc<P>, + UtxoRandomness<C>: Alloc<P>, + VoidNumberCommitment<C>: Alloc<P>, + Utxo<C>: Alloc<P>, { /// Asset - asset: Asset, + asset: Variable<P, Asset>, /// UTXO Randomness - utxo_randomness: UtxoRandomness<C>, + utxo_randomness: Variable<P, UtxoRandomness<C>>, /// Void Number Commitment - void_number_commitment: VoidNumberCommitment<C>, + void_number_commitment: Variable<P, VoidNumberCommitment<C>>, /// Unspent Transaction Output - utxo: Utxo<C>, + utxo: Variable<P, Utxo<C>>, +} + +impl<C, P> ReceiverVariable<C, P> +where + C: IdentityConfiguration, + P: ProofSystem, + AssetId: Alloc<P>, + AssetBalance: Alloc<P>, + UtxoRandomness<C>: Alloc<P>, + VoidNumberCommitment<C>: Alloc<P>, + Utxo<C>: Alloc<P>, +{ + /// Returns the asset id of this receiver. + #[inline] + pub fn asset_id(&self) -> &Variable<P, AssetId> { + &self.asset.id + } + + /// Returns the asset value of this receiver. + #[inline] + pub fn asset_value(&self) -> &Variable<P, AssetBalance> { + &self.asset.value + } + + /// Checks if `self` is a well-formed receiver. + #[inline] + pub fn is_well_formed(&self) -> Bool<P> + where + bool: Alloc<P>, + { + // FIXME: Implement well-formedness check: + // + // See `manta-api/src/zkp/circuit.rs`: + // 1. ValidReceiverToken + + todo!() + } +} + +impl<C, I, P> Alloc<P> for Receiver<C, I> +where + C: IdentityConfiguration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + P: ProofSystem, + AssetId: Alloc<P>, + AssetBalance: Alloc<P>, + UtxoRandomness<C>: Alloc<P>, + VoidNumberCommitment<C>: Alloc<P>, + Utxo<C>: Alloc<P>, +{ + type Output = ReceiverVariable<C, P>; + + #[inline] + fn as_variable(&self, ps: &mut P) -> Self::Output { + Self::Output { + asset: self.asset.as_variable(ps), + utxo_randomness: self.utxo_randomness.as_variable(ps), + void_number_commitment: self.void_number_commitment.as_variable(ps), + utxo: self.utxo.as_variable(ps), + } + } + + #[inline] + fn unknown(ps: &mut P) -> Self::Output { + Self::Output { + asset: Asset::unknown(ps), + utxo_randomness: UtxoRandomness::<C>::unknown(ps), + void_number_commitment: VoidNumberCommitment::<C>::unknown(ps), + utxo: Utxo::<C>::unknown(ps), + } + } } -*/ /// Receiver Post Error pub enum ReceiverPostError<L> diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index e7fb5f251..bed3011ca 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -17,15 +17,22 @@ //! Transfer Protocols use crate::{ - asset::{sample_asset_balances, Asset, AssetBalance, AssetBalances, AssetId}, + asset::{ + sample_asset_balances, Asset, AssetBalance, AssetBalanceVariable, AssetBalances, AssetId, + }, identity::{ - IdentityConfiguration, Receiver, ReceiverPost, Sender, SenderPost, Utxo, VoidNumber, + IdentityConfiguration, Receiver, ReceiverPost, ReceiverVariable, Sender, SenderPost, + SenderVariable, Utxo, UtxoRandomness, VoidNumber, VoidNumberCommitment, + VoidNumberCommitmentRandomness, VoidNumberGenerator, }, ledger::{Ledger, PostError}, }; use alloc::vec::Vec; +use core::iter::Sum; use manta_crypto::{ - constraints::ProofSystem, ies::EncryptedMessage, IntegratedEncryptionScheme, VerifiedSet, + constraint::{Alloc, Assert, AssertEqual, Equal, ProofSystem}, + ies::{EncryptedMessage, IntegratedEncryptionScheme}, + set::{ContainmentProof, VerifiedSet}, }; use manta_util::array_map; use rand::{ @@ -157,54 +164,78 @@ where fn build_proof_content( proof_system: &mut T::ProofSystem, - senders: Vec<()>, - receivers: Vec<()>, - ) { - // FIXME: Build secret transfer zero knowledge proof: - - /* TODO: + senders: Vec<SenderVariable<T, T::UtxoSet, T::ProofSystem>>, + receivers: Vec<ReceiverVariable<T, T::ProofSystem>>, + ) where + T::SecretKey: Alloc<T::ProofSystem>, + T::ProofSystem: AssertEqual<AssetId> + AssertEqual<AssetBalance>, + AssetId: Equal<T::ProofSystem>, + AssetBalance: Equal<T::ProofSystem>, + for<'i> &'i AssetBalanceVariable<T::ProofSystem>: Sum, + VoidNumberGenerator<T>: Alloc<T::ProofSystem>, + VoidNumberCommitmentRandomness<T>: Alloc<T::ProofSystem>, + UtxoRandomness<T>: Alloc<T::ProofSystem>, + VoidNumber<T>: Alloc<T::ProofSystem>, + VoidNumberCommitment<T>: Alloc<T::ProofSystem>, + Utxo<T>: Alloc<T::ProofSystem>, + ContainmentProof<T::UtxoSet>: Alloc<T::ProofSystem>, + bool: Alloc<T::ProofSystem>, + { // 1. Check that all senders are well-formed. - proof_system.assert_all(senders.iter().map(|s| s.is_well_formed())); + proof_system.assert_all(senders.iter().map(SenderVariable::is_well_formed)); // 2. Check that all receivers are well-formed. - proof_system.assert_all(receivers.iter().map(|r| r.is_well_formed())); + proof_system.assert_all(receivers.iter().map(ReceiverVariable::is_well_formed)); // 3. Check that there is a unique asset id for all the assets. - let sender_ids = senders.iter().map(|s| s.asset_id()); - let receiver_ids = receivers.iter().map(|r| r.asset_id()); - proof_system.assert_all_eq(sender_ids.chain(receiver_ids)); + let sender_ids = senders.iter().map(SenderVariable::asset_id); + let receiver_ids = receivers.iter().map(ReceiverVariable::asset_id); + AssertEqual::<AssetId>::assert_all_eq(proof_system, sender_ids.chain(receiver_ids)); // 4. Check that the transaction is balanced. - proof_system.assert_eq( - senders.iter().map(|s| s.asset_value()).sum(), - receivers.iter().map(|r| r.asset_value()).sum(), + AssertEqual::<AssetBalance>::assert_eq( + proof_system, + senders.iter().map(SenderVariable::asset_value).sum(), + receivers.iter().map(ReceiverVariable::asset_value).sum(), ); - */ - - let _ = (proof_system, senders, receivers); } #[inline] fn generate_validity_proof(&self) -> Option<<T::ProofSystem as ProofSystem>::Proof> { // FIXME: Build secret transfer zero knowledge proof: - /* - let mut proof_system = T::ProofSystem::default(); + let proof_system = T::ProofSystem::default(); + + /* TODO: + + // When we know the variables: let senders = self .senders .iter() - .map(|s| proof_system.register(s)) + .map(|s| s.as_variable(&mut proof_system)) .collect::<Vec<_>>(); let receivers = self .receivers .iter() - .map(|r| proof_system.register(r)) + .map(|r| r.as_variable(&mut proof_system)) .collect::<Vec<_>>(); + + // When we don't: + let senders = self + .senders + .iter() + .map(|_| SenderVariable::unknown(&mut proof_system)) + .collect::<Vec<_>>(); + let receivers = self + .receivers + .iter() + .map(|_| ReceiverVariable::unknown(&mut proof_system)) + .collect::<Vec<_>>(); + Self::build_proof_content(&mut proof_system, senders, receivers); - proof_system.finish() */ - todo!() + proof_system.finish().ok() } /// Converts `self` into its ledger post. diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index 7cff81db6..18e9cec70 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -32,7 +32,9 @@ use crate::{ transfer::{SecretTransfer, SecretTransferConfiguration}, }; use core::{convert::Infallible, fmt::Debug, hash::Hash}; -use manta_crypto::{ies::EncryptedMessage, CommitmentInput, IntegratedEncryptionScheme}; +use manta_crypto::{ + commitment::Input as CommitmentInput, ies::EncryptedMessage, IntegratedEncryptionScheme, +}; use rand::{ distributions::{Distribution, Standard}, CryptoRng, RngCore, diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index 85ddbd7fd..ebad134c9 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -16,6 +16,11 @@ //! Commitment Schemes +pub(crate) mod prelude { + #[doc(inline)] + pub use super::CommitmentScheme; +} + /// Commitment Scheme pub trait CommitmentScheme { /// Commitment Input Buffer Type @@ -27,10 +32,10 @@ pub trait CommitmentScheme { /// Commitment Output Type type Output; - /// Returns a new [`CommitmentBuilder`] to build up a commitment. + /// Returns a new [`Builder`] to build up a commitment. #[inline] - fn start(&self) -> CommitmentBuilder<Self> { - CommitmentBuilder::new(self) + fn start(&self) -> Builder<Self> { + Builder::new(self) } /// Commits the `input` buffer with the given `randomness` parameter. @@ -38,7 +43,7 @@ pub trait CommitmentScheme { } /// Commitment Input -pub trait CommitmentInput<C> +pub trait Input<C> where C: CommitmentScheme + ?Sized, { @@ -47,7 +52,7 @@ where } /// Commitment Builder -pub struct CommitmentBuilder<'c, C> +pub struct Builder<'c, C> where C: CommitmentScheme + ?Sized, { @@ -58,11 +63,11 @@ where input_buffer: C::InputBuffer, } -impl<'c, C> CommitmentBuilder<'c, C> +impl<'c, C> Builder<'c, C> where C: CommitmentScheme + ?Sized, { - /// Returns a new [`CommitmentBuilder`] for this `commitment_scheme`. + /// Returns a new [`Builder`] for this `commitment_scheme`. #[inline] pub fn new(commitment_scheme: &'c C) -> Self { Self { @@ -75,9 +80,9 @@ where #[inline] pub fn update<I>(mut self, input: &I) -> Self where - I: CommitmentInput<C>, + I: Input<C>, { - CommitmentInput::extend(input, &mut self.input_buffer); + input.extend(&mut self.input_buffer); self } diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs new file mode 100644 index 000000000..cf3f3c59e --- /dev/null +++ b/manta-crypto/src/constraint.rs @@ -0,0 +1,113 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Constraint Proof Systems + +// TODO: Add derive trait to implement `Alloc` for structs (and enums?). +// FIXME: When running functions from `AssertEqual` the compiler requests the type +// instead of being able to infer it. + +/// Variable Type +pub type Variable<P, T> = <T as Alloc<P>>::Output; + +/// Boolean Variable Type +pub type Bool<P> = Variable<P, bool>; + +/// Byte Variable Type +pub type U8<P> = Variable<P, u8>; + +/// Proof System +pub trait ProofSystem: Default { + /// Proof Type + type Proof; + + /// Error Type + type Error; + + /// Returns a proof that the proof system is consistent. + fn finish(self) -> Result<Self::Proof, Self::Error>; +} + +/// Allocation Trait +pub trait Alloc<P> +where + P: ProofSystem + ?Sized, +{ + /// Resulting Variable Object + type Output; + + /// Returns a new variable with value `self`. + fn as_variable(&self, ps: &mut P) -> Self::Output; + + /// Returns a new variable with an unknown value. + fn unknown(ps: &mut P) -> Self::Output; +} + +/// Assertion Trait +pub trait Assert: ProofSystem +where + bool: Alloc<Self>, +{ + /// Asserts that `b` is `true`. + fn assert(&mut self, b: Bool<Self>); + + /// Asserts that all the booleans in `iter` are `true`. + #[inline] + fn assert_all<I>(&mut self, iter: I) + where + I: IntoIterator<Item = Bool<Self>>, + { + iter.into_iter().for_each(move |b| self.assert(b)) + } +} + +/// Equality Trait +pub trait Equal<P>: Alloc<P> +where + P: ProofSystem + ?Sized, + bool: Alloc<P>, +{ + /// Generates a boolean that represents the fact that `lhs` and `rhs` may be equal. + fn eq(lhs: &Variable<P, Self>, rhs: &Variable<P, Self>) -> Bool<P>; +} + +/// Assert Equal Trait +pub trait AssertEqual<T>: Assert +where + bool: Alloc<Self>, + T: Equal<Self>, +{ + /// Asserts that `lhs` and `rhs` are equal. + #[inline] + fn assert_eq(&mut self, lhs: &Variable<Self, T>, rhs: &Variable<Self, T>) { + self.assert(T::eq(lhs, rhs)) + } + + /// Asserts that all the elements in `iter` are equal. + #[inline] + fn assert_all_eq<'t, I>(&mut self, iter: I) + where + Variable<Self, T>: 't, + I: IntoIterator<Item = &'t Variable<Self, T>>, + { + let mut iter = iter.into_iter(); + if let Some(base) = iter.next() { + for item in iter { + self.assert_eq(base, item); + } + } + } +} diff --git a/manta-crypto/src/constraints.rs b/manta-crypto/src/constraints.rs deleted file mode 100644 index 8d45474b0..000000000 --- a/manta-crypto/src/constraints.rs +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Constraint Systems and Zero Knowledge Proofs - -use core::borrow::Borrow; - -/// -pub trait Register<T>: ProofSystem { - /// - type Output; - - /// - fn allocate<B, Opt>(&mut self, t: Opt) -> Self::Output - where - B: Borrow<T>, - Opt: Into<Option<B>>; - - /// - #[inline] - fn variable(&mut self) -> Self::Output { - Register::allocate::<T, _>(self, None) - } -} - -/// -pub trait ProofSystem: Default { - /// - type Variable; - - /// - type Proof; - - /// - type Error; - - /// - #[inline] - fn allocate<T, B>(&mut self, t: impl Into<Option<B>>) -> <Self as Register<T>>::Output - where - B: Borrow<T>, - Self: Register<T>, - { - Register::allocate(self, t) - } - - /// - fn assert(&mut self, b: ()); - - /// - #[inline] - fn assert_all<I>(&mut self, iter: I) - where - I: IntoIterator<Item = ()>, - { - for boolean in iter { - self.assert(boolean) - } - } - - /// - #[inline] - fn assert_eq(&mut self, lhs: &Self::Variable, rhs: &Self::Variable) { - // TODO: self.assert(lhs.eq(rhs)) - let _ = (lhs, rhs); - todo!() - } - - /// - #[inline] - fn assert_all_eq<'i, I>(&mut self, iter: I) - where - Self::Variable: 'i, - I: IntoIterator<Item = &'i Self::Variable>, - { - let mut iter = iter.into_iter(); - if let Some(base) = iter.next() { - for item in iter { - self.assert_eq(base, item); - } - } - } -} - -/// -pub trait ProofBuilder { - /// - fn build<P>(&self, proof_system: &mut P) - where - P: ProofSystem; -} - -/* TODO: -use alloc::vec::Vec; - -/// -pub enum Variable<T = usize> { - /// - Input(T), - - /// - Witness(T), -} - -/// -pub trait Constraint<T> { - /// - fn is_satisfied<C>(&self, cs: &C) -> Option<bool> - where - C: ConstraintSystem<T>; -} - -/// -pub trait ConstraintSystem<T> { - /// - type Constraint: Constraint<T>; - - /// - fn new(shape: bool) -> Self; - - /// - fn new_input<F>(&mut self, f: F) -> Variable - where - F: FnOnce() -> T; - - /// - fn new_witness<F>(&mut self, f: F) -> Variable - where - F: FnOnce() -> T; - - /// - fn new_variable<F>(&mut self, f: F) -> Variable - where - F: FnOnce() -> Variable<T>; - - /// - fn value(&self, variable: Variable) -> Option<T>; - - /// - fn add(&mut self, constraint: Self::Constraint); - - /// - fn input_count(&self) -> usize; - - /// - fn witness_count(&self) -> usize; - - /// - #[inline] - fn variable_count(&self) -> usize { - self.input_count() + self.witness_count() - } - - /// - fn constraint_count(&self) -> usize; - - /// - fn is_satisfied(&self) -> Option<bool>; -} - -/// -pub trait ConstraintSynthesizer<T> { - /// - fn synthesize<C>(&self, cs: &mut C) - where - C: ConstraintSystem<T>; - - /// - fn input(&self) -> Vec<&T>; -} -*/ diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index 0a1f769a8..81687116b 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -21,16 +21,14 @@ #![forbid(rustdoc::broken_intra_doc_links)] #![forbid(missing_docs)] -extern crate alloc; - -mod commitment; mod prf; -pub mod constraints; +pub mod commitment; +pub mod constraint; pub mod ies; pub mod set; -pub use commitment::*; +pub use commitment::prelude::*; pub use ies::prelude::*; pub use prf::*; pub use set::prelude::*; diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs index 4b5e9f544..24d258260 100644 --- a/manta-pay/src/crypto/commitment/pedersen.rs +++ b/manta-pay/src/crypto/commitment/pedersen.rs @@ -22,7 +22,7 @@ use ark_crypto_primitives::commitment::{ CommitmentScheme as ArkCommitmentScheme, }; use ark_ed_on_bls12_381::EdwardsProjective; -use manta_crypto::CommitmentScheme; +use manta_crypto::commitment::CommitmentScheme; /// Implementation of [`CommitmentScheme`] #[derive(Clone)] From fece82ab44123f051980531b65b440ea391a1d79 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 10 Sep 2021 17:21:37 -0400 Subject: [PATCH 022/275] improve ProofSystem spec, enforce public/secret variables --- manta-accounting/src/asset.rs | 94 ++++---- manta-accounting/src/identity.rs | 337 ++++++++++++++++------------ manta-accounting/src/transfer.rs | 146 ++++++++----- manta-accounting/src/wallet.rs | 4 +- manta-crypto/src/constraint.rs | 363 +++++++++++++++++++++++++++---- 5 files changed, 654 insertions(+), 290 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index f5a9852c4..a3300f60e 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -25,12 +25,13 @@ use core::{ convert::TryFrom, fmt::Debug, hash::Hash, + iter::Sum, ops::{Add, AddAssign, Mul, Sub, SubAssign}, }; use derive_more::{ Add, AddAssign, Display, Div, DivAssign, From, Mul, MulAssign, Product, Sub, SubAssign, Sum, }; -use manta_crypto::constraint::{Alloc, ProofSystem, Variable}; +use manta_crypto::constraint::{IsVariable, Secret, Var, Variable}; use manta_util::{ array_map, fallible_array_map, into_array_unchecked, ByteAccumulator, ConcatBytes, }; @@ -43,29 +44,7 @@ use rand::{ type AssetIdType = u32; /// Asset Id Type -#[derive( - Add, - AddAssign, - Clone, - Copy, - Debug, - Default, - Display, - Div, - DivAssign, - Eq, - From, - Hash, - Mul, - MulAssign, - Ord, - PartialEq, - PartialOrd, - Product, - Sub, - SubAssign, - Sum, -)] +#[derive(Clone, Copy, Debug, Default, Display, Eq, From, Hash, Ord, PartialEq, PartialOrd)] #[from(forward)] pub struct AssetId( /// [`Asset`] Id @@ -106,15 +85,6 @@ impl From<AssetId> for [u8; AssetId::SIZE] { } } -impl Mul<AssetId> for AssetIdType { - type Output = AssetIdType; - - #[inline] - fn mul(self, rhs: AssetId) -> Self::Output { - self * rhs.0 - } -} - /// [`AssetBalance`] Base Type type AssetBalanceType = u128; @@ -209,6 +179,16 @@ impl Mul<AssetBalance> for AssetBalanceType { } } +impl<'a> Sum<&'a AssetBalance> for AssetBalance { + #[inline] + fn sum<I>(iter: I) -> Self + where + I: Iterator<Item = &'a AssetBalance>, + { + iter.copied().sum() + } +} + /// [`AssetBalance`] Array Type pub type AssetBalances<const N: usize> = [AssetBalance; N]; @@ -354,46 +334,52 @@ impl Distribution<Asset> for Standard { } /// Asset Id Variable -pub type AssetIdVariable<P> = Variable<P, AssetId>; +pub type AssetIdVar<P, M> = Variable<AssetId, P, M>; /// Asset Balance Variable -pub type AssetBalanceVariable<P> = Variable<P, AssetBalance>; +pub type AssetBalanceVar<P, M> = Variable<AssetBalance, P, M>; /// Asset Variable -pub struct AssetVariable<P> +pub struct AssetVar<P> where - P: ProofSystem, - AssetId: Alloc<P>, - AssetBalance: Alloc<P>, + AssetId: Var<P, Secret>, + AssetBalance: Var<P, Secret>, { /// Asset Id - pub id: AssetIdVariable<P>, + pub id: AssetIdVar<P, Secret>, /// Asset Value - pub value: AssetBalanceVariable<P>, + pub value: AssetBalanceVar<P, Secret>, +} + +impl<P> IsVariable<P, Secret> for AssetVar<P> +where + AssetId: Var<P, Secret>, + AssetBalance: Var<P, Secret>, +{ + type Type = Asset; } -impl<P> Alloc<P> for Asset +impl<P> Var<P, Secret> for Asset where - P: ProofSystem, - AssetId: Alloc<P>, - AssetBalance: Alloc<P>, + AssetId: Var<P, Secret>, + AssetBalance: Var<P, Secret>, { - type Output = AssetVariable<P>; + type Variable = AssetVar<P>; #[inline] - fn as_variable(&self, ps: &mut P) -> Self::Output { - Self::Output { - id: self.id.as_variable(ps), - value: self.value.as_variable(ps), + fn as_variable(&self, ps: &mut P, mode: Secret) -> Self::Variable { + Self::Variable { + id: self.id.as_variable(ps, mode), + value: self.value.as_variable(ps, mode), } } #[inline] - fn unknown(ps: &mut P) -> Self::Output { - Self::Output { - id: AssetId::unknown(ps), - value: AssetBalance::unknown(ps), + fn unknown(ps: &mut P, mode: Secret) -> Self::Variable { + Self::Variable { + id: AssetId::unknown(ps, mode), + value: AssetBalance::unknown(ps, mode), } } } diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index dd1bb5cb0..a9407c0b3 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -20,16 +20,17 @@ // FIXME: Should the identity types have methods that expose their members? Or should it be // completely opaque, and let the internal APIs handle all the logic? // TODO: Since `IdentityConfiguration::SecretKey: Clone`, should `Identity: Clone`? +// TODO: Should we rename all uses of `C::SecretKey` to `SecretKey<C>` to be consistent? use crate::{ - asset::{Asset, AssetBalance, AssetId}, + asset::{Asset, AssetBalance, AssetBalanceVar, AssetId, AssetIdVar, AssetVar}, keys::SecretKeyGenerator, ledger::Ledger, }; use core::{convert::Infallible, fmt::Debug, hash::Hash, marker::PhantomData}; use manta_crypto::{ commitment::{CommitmentScheme, Input as CommitmentInput}, - constraint::{Alloc, Bool, ProofSystem, Variable}, + constraint::{Bool, BooleanSystem, Derived, IsVariable, Public, Secret, Var, Variable}, ies::{self, EncryptedMessage, IntegratedEncryptionScheme}, set::{ContainmentProof, VerifiedSet}, PseudorandomFunctionFamily, @@ -47,7 +48,7 @@ pub(crate) mod prelude { }; } -/// [`Identity`] Configuration Trait +/// [`Identity`] Configuration pub trait IdentityConfiguration { /// Secret Key Type type SecretKey: Clone; @@ -254,51 +255,61 @@ where } /// Asset Parameters Variable -pub struct AssetParametersVariable<C, P> +pub struct AssetParametersVar<C, P> where C: IdentityConfiguration, - P: ProofSystem, - VoidNumberGenerator<C>: Alloc<P>, - VoidNumberCommitmentRandomness<C>: Alloc<P>, - UtxoRandomness<C>: Alloc<P>, + VoidNumberGenerator<C>: Var<P, Secret>, + VoidNumberCommitmentRandomness<C>: Var<P, Secret>, + UtxoRandomness<C>: Var<P, Secret>, { /// Void Number Generator - pub void_number_generator: Variable<P, VoidNumberGenerator<C>>, + pub void_number_generator: Variable<VoidNumberGenerator<C>, P, Secret>, /// Void Number Commitment Randomness - pub void_number_commitment_randomness: Variable<P, VoidNumberCommitmentRandomness<C>>, + pub void_number_commitment_randomness: Variable<VoidNumberCommitmentRandomness<C>, P, Secret>, /// UTXO Randomness - pub utxo_randomness: Variable<P, UtxoRandomness<C>>, + pub utxo_randomness: Variable<UtxoRandomness<C>, P, Secret>, } -impl<C, P> Alloc<P> for AssetParameters<C> +impl<C, P> IsVariable<P, Secret> for AssetParametersVar<C, P> where C: IdentityConfiguration, - P: ProofSystem, - VoidNumberGenerator<C>: Alloc<P>, - VoidNumberCommitmentRandomness<C>: Alloc<P>, - UtxoRandomness<C>: Alloc<P>, + VoidNumberGenerator<C>: Var<P, Secret>, + VoidNumberCommitmentRandomness<C>: Var<P, Secret>, + UtxoRandomness<C>: Var<P, Secret>, { - type Output = AssetParametersVariable<C, P>; + type Type = AssetParameters<C>; +} + +impl<C, P> Var<P, Secret> for AssetParameters<C> +where + C: IdentityConfiguration, + VoidNumberGenerator<C>: Var<P, Secret>, + VoidNumberCommitmentRandomness<C>: Var<P, Secret>, + UtxoRandomness<C>: Var<P, Secret>, +{ + type Variable = AssetParametersVar<C, P>; #[inline] - fn as_variable(&self, ps: &mut P) -> Self::Output { - Self::Output { - void_number_generator: self.void_number_generator.as_variable(ps), + fn as_variable(&self, ps: &mut P, mode: Secret) -> Self::Variable { + Self::Variable { + void_number_generator: self.void_number_generator.as_variable(ps, mode), void_number_commitment_randomness: self .void_number_commitment_randomness - .as_variable(ps), - utxo_randomness: self.utxo_randomness.as_variable(ps), + .as_variable(ps, mode), + utxo_randomness: self.utxo_randomness.as_variable(ps, mode), } } #[inline] - fn unknown(ps: &mut P) -> Self::Output { - Self::Output { - void_number_generator: VoidNumberGenerator::<C>::unknown(ps), - void_number_commitment_randomness: VoidNumberCommitmentRandomness::<C>::unknown(ps), - utxo_randomness: UtxoRandomness::<C>::unknown(ps), + fn unknown(ps: &mut P, mode: Secret) -> Self::Variable { + Self::Variable { + void_number_generator: VoidNumberGenerator::<C>::unknown(ps, mode), + void_number_commitment_randomness: VoidNumberCommitmentRandomness::<C>::unknown( + ps, mode, + ), + utxo_randomness: UtxoRandomness::<C>::unknown(ps, mode), } } } @@ -1106,117 +1117,140 @@ where } /// Sender Variable -pub struct SenderVariable<C, S, P> +pub struct SenderVar<C, S, P> where C: IdentityConfiguration, S: VerifiedSet<Item = Utxo<C>>, - P: ProofSystem, - C::SecretKey: Alloc<P>, - AssetId: Alloc<P>, - AssetBalance: Alloc<P>, - AssetParameters<C>: Alloc<P>, - VoidNumber<C>: Alloc<P>, - Utxo<C>: Alloc<P>, - ContainmentProof<S>: Alloc<P>, + P: BooleanSystem, + C::SecretKey: Var<P, Secret>, + AssetId: Var<P, Secret>, + AssetBalance: Var<P, Secret>, + VoidNumberGenerator<C>: Var<P, Secret>, + VoidNumberCommitmentRandomness<C>: Var<P, Secret>, + UtxoRandomness<C>: Var<P, Secret>, + VoidNumber<C>: Var<P, Public>, + Utxo<C>: Var<P, Secret>, + ContainmentProof<S>: Var<P, Secret>, { /// Secret Key - secret_key: Variable<P, C::SecretKey>, + secret_key: Variable<C::SecretKey, P, Secret>, /// Asset - asset: Variable<P, Asset>, + asset: AssetVar<P>, /// Asset Parameters - parameters: Variable<P, AssetParameters<C>>, + parameters: AssetParametersVar<C, P>, /// Void Number - void_number: Variable<P, VoidNumber<C>>, + void_number: Variable<VoidNumber<C>, P, Public>, /// Unspent Transaction Output - utxo: Variable<P, Utxo<C>>, + utxo: Variable<Utxo<C>, P, Secret>, /// UTXO Containment Proof - utxo_containment_proof: Variable<P, ContainmentProof<S>>, + utxo_containment_proof: Variable<ContainmentProof<S>, P, Secret>, } -impl<C, S, P> SenderVariable<C, S, P> +impl<C, S, P> SenderVar<C, S, P> where C: IdentityConfiguration, S: VerifiedSet<Item = Utxo<C>>, - P: ProofSystem, - C::SecretKey: Alloc<P>, - AssetId: Alloc<P>, - AssetBalance: Alloc<P>, - AssetParameters<C>: Alloc<P>, - VoidNumber<C>: Alloc<P>, - Utxo<C>: Alloc<P>, - ContainmentProof<S>: Alloc<P>, + P: BooleanSystem, + C::SecretKey: Var<P, Secret>, + AssetId: Var<P, Secret>, + AssetBalance: Var<P, Secret>, + VoidNumberGenerator<C>: Var<P, Secret>, + VoidNumberCommitmentRandomness<C>: Var<P, Secret>, + UtxoRandomness<C>: Var<P, Secret>, + VoidNumber<C>: Var<P, Public>, + Utxo<C>: Var<P, Secret>, + ContainmentProof<S>: Var<P, Secret>, { /// Returns the asset id of this sender. #[inline] - pub fn asset_id(&self) -> &Variable<P, AssetId> { + pub fn asset_id(&self) -> &AssetIdVar<P, Secret> { &self.asset.id } /// Returns the asset value of this sender. #[inline] - pub fn asset_value(&self) -> &Variable<P, AssetBalance> { + pub fn asset_value(&self) -> &AssetBalanceVar<P, Secret> { &self.asset.value } /// Checks if `self` is a well-formed sender. #[inline] - pub fn is_well_formed(&self) -> Bool<P> - where - bool: Alloc<P>, - { + pub fn is_well_formed(&self) -> Bool<P> { // FIXME: Implement well-formedness check: // - // See `manta-api/src/zkp/circuit.rs`: - // 1. ValidSenderToken - // 2. ValidPrivateKey - // 3. ValidVoidNumber - // 4. StoredCommitment + // 1. pk = PRF(sk, 0) [public: (), secret: (pk, sk)] + // 2. vn = PRF(sk, rho) [public: (vn), secret: (sk, rho)] + // 3. k = COM(pk || rho, r) [public: (k), secret: (pk, rho, r)] + // 4. cm = COM(asset_id || asset_value || k, s) [public: (), secret: (cm, asset, k, s)] + // 5. merkle_path(cm, path, root) == true [public: (root), secret: (cm, path)] + // + // FIXME: should `k` be private or not? todo!() } } -impl<C, S, P> Alloc<P> for Sender<C, S> +impl<C, S, P> IsVariable<P, Derived> for SenderVar<C, S, P> +where + C: IdentityConfiguration, + S: VerifiedSet<Item = Utxo<C>>, + P: BooleanSystem, + C::SecretKey: Var<P, Secret>, + AssetId: Var<P, Secret>, + AssetBalance: Var<P, Secret>, + VoidNumberGenerator<C>: Var<P, Secret>, + VoidNumberCommitmentRandomness<C>: Var<P, Secret>, + UtxoRandomness<C>: Var<P, Secret>, + VoidNumber<C>: Var<P, Public>, + Utxo<C>: Var<P, Secret>, + ContainmentProof<S>: Var<P, Secret>, +{ + type Type = Sender<C, S>; +} + +impl<C, S, P> Var<P, Derived> for Sender<C, S> where C: IdentityConfiguration, S: VerifiedSet<Item = Utxo<C>>, - P: ProofSystem, - C::SecretKey: Alloc<P>, - AssetId: Alloc<P>, - AssetBalance: Alloc<P>, - AssetParameters<C>: Alloc<P>, - VoidNumber<C>: Alloc<P>, - Utxo<C>: Alloc<P>, - ContainmentProof<S>: Alloc<P>, + P: BooleanSystem, + C::SecretKey: Var<P, Secret>, + AssetId: Var<P, Secret>, + AssetBalance: Var<P, Secret>, + VoidNumberGenerator<C>: Var<P, Secret>, + VoidNumberCommitmentRandomness<C>: Var<P, Secret>, + UtxoRandomness<C>: Var<P, Secret>, + VoidNumber<C>: Var<P, Public>, + Utxo<C>: Var<P, Secret>, + ContainmentProof<S>: Var<P, Secret>, { - type Output = SenderVariable<C, S, P>; + type Variable = SenderVar<C, S, P>; #[inline] - fn as_variable(&self, ps: &mut P) -> Self::Output { - Self::Output { - secret_key: self.identity.secret_key.as_variable(ps), - asset: self.asset.as_variable(ps), - parameters: self.parameters.as_variable(ps), - void_number: self.void_number.as_variable(ps), - utxo: self.utxo.as_variable(ps), - utxo_containment_proof: self.utxo_containment_proof.as_variable(ps), + fn as_variable(&self, ps: &mut P, mode: Derived) -> Self::Variable { + Self::Variable { + secret_key: self.identity.secret_key.as_variable(ps, mode.into()), + asset: self.asset.as_variable(ps, mode.into()), + parameters: self.parameters.as_variable(ps, mode.into()), + void_number: self.void_number.as_variable(ps, mode.into()), + utxo: self.utxo.as_variable(ps, mode.into()), + utxo_containment_proof: self.utxo_containment_proof.as_variable(ps, mode.into()), } } #[inline] - fn unknown(ps: &mut P) -> Self::Output { - Self::Output { - secret_key: C::SecretKey::unknown(ps), - asset: Asset::unknown(ps), - parameters: AssetParameters::<_>::unknown(ps), - void_number: VoidNumber::<C>::unknown(ps), - utxo: Utxo::<C>::unknown(ps), - utxo_containment_proof: ContainmentProof::<_>::unknown(ps), + fn unknown(ps: &mut P, mode: Derived) -> Self::Variable { + Self::Variable { + secret_key: C::SecretKey::unknown(ps, mode.into()), + asset: Asset::unknown(ps, mode.into()), + parameters: AssetParameters::<C>::unknown(ps, mode.into()), + void_number: VoidNumber::<C>::unknown(ps, mode.into()), + utxo: Utxo::<C>::unknown(ps, mode.into()), + utxo_containment_proof: ContainmentProof::<S>::unknown(ps, mode.into()), } } } @@ -1379,96 +1413,131 @@ where } /// Receiver Proof Content -pub struct ReceiverVariable<C, P> +pub struct ReceiverVar<C, I, P> where C: IdentityConfiguration, - P: ProofSystem, - AssetId: Alloc<P>, - AssetBalance: Alloc<P>, - UtxoRandomness<C>: Alloc<P>, - VoidNumberCommitment<C>: Alloc<P>, - Utxo<C>: Alloc<P>, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + P: BooleanSystem, + AssetId: Var<P, Secret>, + AssetBalance: Var<P, Secret>, + UtxoRandomness<C>: Var<P, Secret>, + VoidNumberCommitment<C>: Var<P, Secret>, + Utxo<C>: Var<P, Public>, { /// Asset - asset: Variable<P, Asset>, + asset: AssetVar<P>, /// UTXO Randomness - utxo_randomness: Variable<P, UtxoRandomness<C>>, + utxo_randomness: Variable<UtxoRandomness<C>, P, Secret>, /// Void Number Commitment - void_number_commitment: Variable<P, VoidNumberCommitment<C>>, + void_number_commitment: Variable<VoidNumberCommitment<C>, P, Secret>, /// Unspent Transaction Output - utxo: Variable<P, Utxo<C>>, + utxo: Variable<Utxo<C>, P, Public>, + + /// Type Parameter Marker + __: PhantomData<I>, } -impl<C, P> ReceiverVariable<C, P> +impl<C, I, P> ReceiverVar<C, I, P> where C: IdentityConfiguration, - P: ProofSystem, - AssetId: Alloc<P>, - AssetBalance: Alloc<P>, - UtxoRandomness<C>: Alloc<P>, - VoidNumberCommitment<C>: Alloc<P>, - Utxo<C>: Alloc<P>, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + P: BooleanSystem, + AssetId: Var<P, Secret>, + AssetBalance: Var<P, Secret>, + UtxoRandomness<C>: Var<P, Secret>, + VoidNumberCommitment<C>: Var<P, Secret>, + Utxo<C>: Var<P, Public>, { /// Returns the asset id of this receiver. #[inline] - pub fn asset_id(&self) -> &Variable<P, AssetId> { + pub fn asset_id(&self) -> &AssetIdVar<P, Secret> { &self.asset.id } /// Returns the asset value of this receiver. #[inline] - pub fn asset_value(&self) -> &Variable<P, AssetBalance> { + pub fn asset_value(&self) -> &AssetBalanceVar<P, Secret> { &self.asset.value } /// Checks if `self` is a well-formed receiver. #[inline] - pub fn is_well_formed(&self) -> Bool<P> - where - bool: Alloc<P>, - { + pub fn is_well_formed(&self) -> Bool<P> { // FIXME: Implement well-formedness check: // - // See `manta-api/src/zkp/circuit.rs`: - // 1. ValidReceiverToken + // 1. cm = COM(asset_id || asset_value || k, s) [public: (cm), secret: (asset, k, s)] todo!() } } -impl<C, I, P> Alloc<P> for Receiver<C, I> +impl<C, I, P> IsVariable<P, Derived> for ReceiverVar<C, I, P> where C: IdentityConfiguration, I: IntegratedEncryptionScheme<Plaintext = Asset>, - P: ProofSystem, - AssetId: Alloc<P>, - AssetBalance: Alloc<P>, - UtxoRandomness<C>: Alloc<P>, - VoidNumberCommitment<C>: Alloc<P>, - Utxo<C>: Alloc<P>, + P: BooleanSystem, + AssetId: Var<P, Secret>, + AssetBalance: Var<P, Secret>, + UtxoRandomness<C>: Var<P, Secret>, + VoidNumberCommitment<C>: Var<P, Secret>, + Utxo<C>: Var<P, Public>, { - type Output = ReceiverVariable<C, P>; + type Type = Receiver<C, I>; +} + +impl<C, I, P> Var<P, Derived> for Receiver<C, I> +where + C: IdentityConfiguration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + P: BooleanSystem, + AssetId: Var<P, Secret>, + AssetBalance: Var<P, Secret>, + UtxoRandomness<C>: Var<P, Secret>, + VoidNumberCommitment<C>: Var<P, Secret>, + Utxo<C>: Var<P, Public>, +{ + type Variable = ReceiverVar<C, I, P>; #[inline] - fn as_variable(&self, ps: &mut P) -> Self::Output { - Self::Output { - asset: self.asset.as_variable(ps), - utxo_randomness: self.utxo_randomness.as_variable(ps), - void_number_commitment: self.void_number_commitment.as_variable(ps), - utxo: self.utxo.as_variable(ps), + fn as_variable(&self, ps: &mut P, mode: Derived) -> Self::Variable { + // FIXME: Why can't we automatically derive the modes for all of them? + // + // > It's because `Utxo` and `VoidNumberCommitment` are both `CommitmentSchemeOutput` + // and so the type system automatically sees that this type should be secret + // because `VoidNumberCommitment` comes first. We'll have to figure out a way to + // fix this. The type system is just getting confused, there is no security issue + // here since only the correct input type will work, it's just that `.into()` is not + // helpful. + // + Self::Variable { + asset: self.asset.as_variable(ps, mode.into()), + utxo_randomness: self.utxo_randomness.as_variable(ps, mode.into()), + void_number_commitment: self.void_number_commitment.as_variable(ps, Secret), + utxo: self.utxo.as_variable(ps, Public), + __: PhantomData, } } #[inline] - fn unknown(ps: &mut P) -> Self::Output { - Self::Output { - asset: Asset::unknown(ps), - utxo_randomness: UtxoRandomness::<C>::unknown(ps), - void_number_commitment: VoidNumberCommitment::<C>::unknown(ps), - utxo: Utxo::<C>::unknown(ps), + fn unknown(ps: &mut P, mode: Derived) -> Self::Variable { + // FIXME: Why can't we automatically derive the modes for all of them? + // + // > It's because `Utxo` and `VoidNumberCommitment` are both `CommitmentSchemeOutput` + // and so the type system automatically sees that this type should be secret + // because `VoidNumberCommitment` comes first. We'll have to figure out a way to + // fix this. The type system is just getting confused, there is no security issue + // here since only the correct input type will work, it's just that `.into()` is not + // helpful. + // + Self::Variable { + asset: Asset::unknown(ps, mode.into()), + utxo_randomness: UtxoRandomness::<C>::unknown(ps, mode.into()), + void_number_commitment: VoidNumberCommitment::<C>::unknown(ps, Secret), + utxo: Utxo::<C>::unknown(ps, Public), + __: PhantomData, } } } diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index bed3011ca..5bfd85f06 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -17,20 +17,18 @@ //! Transfer Protocols use crate::{ - asset::{ - sample_asset_balances, Asset, AssetBalance, AssetBalanceVariable, AssetBalances, AssetId, - }, + asset::{sample_asset_balances, Asset, AssetBalance, AssetBalanceVar, AssetBalances, AssetId}, identity::{ - IdentityConfiguration, Receiver, ReceiverPost, ReceiverVariable, Sender, SenderPost, - SenderVariable, Utxo, UtxoRandomness, VoidNumber, VoidNumberCommitment, - VoidNumberCommitmentRandomness, VoidNumberGenerator, + IdentityConfiguration, Receiver, ReceiverPost, ReceiverVar, Sender, SenderPost, SenderVar, + Utxo, UtxoRandomness, VoidNumber, VoidNumberCommitment, VoidNumberCommitmentRandomness, + VoidNumberGenerator, }, ledger::{Ledger, PostError}, }; use alloc::vec::Vec; use core::iter::Sum; use manta_crypto::{ - constraint::{Alloc, Assert, AssertEqual, Equal, ProofSystem}, + constraint::{Equal, ProofSystem, Public, Secret, Var}, ies::{EncryptedMessage, IntegratedEncryptionScheme}, set::{ContainmentProof, VerifiedSet}, }; @@ -67,6 +65,24 @@ impl<const SOURCES: usize, const SINKS: usize> PublicTransfer<SOURCES, SINKS> { sinks, } } + + /// Returns the sum of the asset values of the sources. + #[inline] + pub fn source_sum(&self) -> AssetBalance { + self.sources.iter().sum() + } + + /// Returns the sum of the asset values of the sinks. + #[inline] + pub fn sink_sum(&self) -> AssetBalance { + self.sinks.iter().sum() + } + + /// Validates the transaction by checking that the source sum equals the sink sum. + #[inline] + pub fn is_valid(&self) -> bool { + self.source_sum() == self.sink_sum() + } } impl<const SOURCES: usize, const SINKS: usize> Distribution<PublicTransfer<SOURCES, SINKS>> @@ -82,33 +98,47 @@ impl<const SOURCES: usize, const SINKS: usize> Distribution<PublicTransfer<SOURC } } -/// Secret Transfer Configuration Trait +/// Secret Transfer Configuration pub trait SecretTransferConfiguration: IdentityConfiguration { /// Integrated Encryption Scheme for [`Asset`] type IntegratedEncryptionScheme: IntegratedEncryptionScheme<Plaintext = Asset>; /// Verified Set for [`Utxo`] type UtxoSet: VerifiedSet<Item = Utxo<Self>>; - - /// Proof System for [`SecretTransfer`] - type ProofSystem: ProofSystem; } +/// Secret Transfer Proof System +pub trait SecretTransferProofSystem: SecretTransferConfiguration + ProofSystem {} + +/// Secret Sender Type +pub type SecretSender<T> = Sender<T, <T as SecretTransferConfiguration>::UtxoSet>; + +/// Secret Receiver Type +pub type SecretReceiver<T> = + Receiver<T, <T as SecretTransferConfiguration>::IntegratedEncryptionScheme>; + +/// Secret Sender Variable Type +pub type SecretSenderVar<T> = SenderVar<T, <T as SecretTransferConfiguration>::UtxoSet, T>; + +/// Secret Receiver Type +pub type SecretReceiverVar<T> = + ReceiverVar<T, <T as SecretTransferConfiguration>::IntegratedEncryptionScheme, T>; + /// Secret Transfer Protocol pub struct SecretTransfer<T, const SENDERS: usize, const RECEIVERS: usize> where - T: SecretTransferConfiguration, + T: SecretTransferProofSystem, { /// Secret Senders - pub senders: [Sender<T, T::UtxoSet>; SENDERS], + pub senders: [SecretSender<T>; SENDERS], /// Secret Receivers - pub receivers: [Receiver<T, T::IntegratedEncryptionScheme>; RECEIVERS], + pub receivers: [SecretReceiver<T>; RECEIVERS], } impl<T, const SENDERS: usize, const RECEIVERS: usize> SecretTransfer<T, SENDERS, RECEIVERS> where - T: SecretTransferConfiguration, + T: SecretTransferProofSystem, { /// Maximum Number of Senders pub const MAXIMUM_SENDER_COUNT: usize = 10; @@ -119,8 +149,8 @@ where /// Builds a new [`SecretTransfer`]. #[inline] pub fn new( - senders: [Sender<T, T::UtxoSet>; SENDERS], - receivers: [Receiver<T, T::IntegratedEncryptionScheme>; RECEIVERS], + senders: [SecretSender<T>; SENDERS], + receivers: [SecretReceiver<T>; RECEIVERS], ) -> Self { if SENDERS > Self::MAXIMUM_SENDER_COUNT { panic!("Allocated too many senders."); @@ -131,6 +161,7 @@ where Self { senders, receivers } } + /* TODO: do we still want these? /// Checks that the asset ids of all the senders and receivers matches. #[inline] pub fn has_unique_asset_id(&self) -> bool { @@ -161,50 +192,50 @@ where pub fn is_balanced(&self) -> bool { self.sender_sum().eq(&self.receiver_sum()) } + */ + /// Builds constraints for secret transfer validity proof. + #[inline] fn build_proof_content( - proof_system: &mut T::ProofSystem, - senders: Vec<SenderVariable<T, T::UtxoSet, T::ProofSystem>>, - receivers: Vec<ReceiverVariable<T, T::ProofSystem>>, + proof_system: &mut T, + senders: Vec<SecretSenderVar<T>>, + receivers: Vec<SecretReceiverVar<T>>, ) where - T::SecretKey: Alloc<T::ProofSystem>, - T::ProofSystem: AssertEqual<AssetId> + AssertEqual<AssetBalance>, - AssetId: Equal<T::ProofSystem>, - AssetBalance: Equal<T::ProofSystem>, - for<'i> &'i AssetBalanceVariable<T::ProofSystem>: Sum, - VoidNumberGenerator<T>: Alloc<T::ProofSystem>, - VoidNumberCommitmentRandomness<T>: Alloc<T::ProofSystem>, - UtxoRandomness<T>: Alloc<T::ProofSystem>, - VoidNumber<T>: Alloc<T::ProofSystem>, - VoidNumberCommitment<T>: Alloc<T::ProofSystem>, - Utxo<T>: Alloc<T::ProofSystem>, - ContainmentProof<T::UtxoSet>: Alloc<T::ProofSystem>, - bool: Alloc<T::ProofSystem>, + T::SecretKey: Var<T, Secret>, + AssetId: Equal<T, Secret>, + AssetBalance: Equal<T, Secret>, + for<'i> &'i AssetBalanceVar<T, Secret>: Sum, + VoidNumberGenerator<T>: Var<T, Secret>, + VoidNumberCommitmentRandomness<T>: Var<T, Secret>, + UtxoRandomness<T>: Var<T, Secret>, + VoidNumber<T>: Var<T, Public>, + VoidNumberCommitment<T>: Var<T, Secret>, + Utxo<T>: Var<T, Secret> + Var<T, Public>, + ContainmentProof<T::UtxoSet>: Var<T, Secret>, { // 1. Check that all senders are well-formed. - proof_system.assert_all(senders.iter().map(SenderVariable::is_well_formed)); + proof_system.assert_all(senders.iter().map(SenderVar::is_well_formed)); // 2. Check that all receivers are well-formed. - proof_system.assert_all(receivers.iter().map(ReceiverVariable::is_well_formed)); + proof_system.assert_all(receivers.iter().map(ReceiverVar::is_well_formed)); // 3. Check that there is a unique asset id for all the assets. - let sender_ids = senders.iter().map(SenderVariable::asset_id); - let receiver_ids = receivers.iter().map(ReceiverVariable::asset_id); - AssertEqual::<AssetId>::assert_all_eq(proof_system, sender_ids.chain(receiver_ids)); + let sender_ids = senders.iter().map(SenderVar::asset_id); + let receiver_ids = receivers.iter().map(ReceiverVar::asset_id); + proof_system.assert_all_eq(sender_ids.chain(receiver_ids)); // 4. Check that the transaction is balanced. - AssertEqual::<AssetBalance>::assert_eq( - proof_system, - senders.iter().map(SenderVariable::asset_value).sum(), - receivers.iter().map(ReceiverVariable::asset_value).sum(), + proof_system.assert_eq( + senders.iter().map(SenderVar::asset_value).sum(), + receivers.iter().map(ReceiverVar::asset_value).sum(), ); } #[inline] - fn generate_validity_proof(&self) -> Option<<T::ProofSystem as ProofSystem>::Proof> { + fn generate_validity_proof(&self) -> Option<T::Proof> { // FIXME: Build secret transfer zero knowledge proof: - let proof_system = T::ProofSystem::default(); + let proof_system = T::default(); /* TODO: @@ -250,24 +281,31 @@ where } } +/// Secret Sender Post Type +pub type SecretSenderPost<T> = SenderPost<T, <T as SecretTransferConfiguration>::UtxoSet>; + +/// Secret Receiver Post Type +pub type SecretReceiverPost<T> = + ReceiverPost<T, <T as SecretTransferConfiguration>::IntegratedEncryptionScheme>; + /// Secret Transfer Post pub struct SecretTransferPost<T, const SENDERS: usize, const RECEIVERS: usize> where - T: SecretTransferConfiguration, + T: SecretTransferProofSystem, { /// Sender Posts - pub sender_posts: [SenderPost<T, T::UtxoSet>; SENDERS], + pub sender_posts: [SecretSenderPost<T>; SENDERS], /// Receiver Posts - pub receiver_posts: [ReceiverPost<T, T::IntegratedEncryptionScheme>; RECEIVERS], + pub receiver_posts: [SecretReceiverPost<T>; RECEIVERS], /// Validity Proof - pub validity_proof: <T::ProofSystem as ProofSystem>::Proof, + pub validity_proof: T::Proof, } impl<T, const SENDERS: usize, const RECEIVERS: usize> SecretTransferPost<T, SENDERS, RECEIVERS> where - T: SecretTransferConfiguration, + T: SecretTransferProofSystem, { /// Posts the [`SecretTransferPost`] to the `ledger`. #[inline] @@ -295,7 +333,7 @@ where impl<T, const SENDERS: usize, const RECEIVERS: usize> From<SecretTransfer<T, SENDERS, RECEIVERS>> for Option<SecretTransferPost<T, SENDERS, RECEIVERS>> where - T: SecretTransferConfiguration, + T: SecretTransferProofSystem, { #[inline] fn from(secret_transfer: SecretTransfer<T, SENDERS, RECEIVERS>) -> Self { @@ -311,7 +349,7 @@ pub struct Transfer< const SENDERS: usize, const RECEIVERS: usize, > where - T: SecretTransferConfiguration, + T: SecretTransferProofSystem, { /// Public Transfer pub public: PublicTransfer<SOURCES, SINKS>, @@ -323,7 +361,7 @@ pub struct Transfer< impl<T, const SOURCES: usize, const SINKS: usize, const SENDERS: usize, const RECEIVERS: usize> Transfer<T, SOURCES, SINKS, SENDERS, RECEIVERS> where - T: SecretTransferConfiguration, + T: SecretTransferProofSystem, { /// Builds a new [`Transfer`] from a [`PublicTransfer`] and a [`SecretTransfer`]. #[inline] @@ -352,7 +390,7 @@ pub struct TransferPost< const SENDERS: usize, const RECEIVERS: usize, > where - T: SecretTransferConfiguration, + T: SecretTransferProofSystem, { /// Public Transfer Post pub public_transfer_post: PublicTransfer<SOURCES, SINKS>, @@ -364,7 +402,7 @@ pub struct TransferPost< impl<T, const SOURCES: usize, const SINKS: usize, const SENDERS: usize, const RECEIVERS: usize> TransferPost<T, SOURCES, SINKS, SENDERS, RECEIVERS> where - T: SecretTransferConfiguration, + T: SecretTransferProofSystem, { /// Posts the [`TransferPost`] to the `ledger`. #[inline] diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index 18e9cec70..585aaf4cd 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -29,7 +29,7 @@ use crate::{ }, keys::{self, DerivedSecretKeyGenerator, ExternalKeys, InternalKeys}, ledger::Ledger, - transfer::{SecretTransfer, SecretTransferConfiguration}, + transfer::{SecretTransfer, SecretTransferProofSystem}, }; use core::{convert::Infallible, fmt::Debug, hash::Hash}; use manta_crypto::{ @@ -395,7 +395,7 @@ where rng: &mut R, ) -> Option<SecretTransfer<T, 2, 2>> where - T: SecretTransferConfiguration, + T: SecretTransferProofSystem, R: CryptoRng + RngCore + ?Sized, Asset: CommitmentInput<T::CommitmentScheme>, VoidNumberCommitment<T>: CommitmentInput<T::CommitmentScheme>, diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index cf3f3c59e..463cd8bca 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -16,98 +16,369 @@ //! Constraint Proof Systems -// TODO: Add derive trait to implement `Alloc` for structs (and enums?). -// FIXME: When running functions from `AssertEqual` the compiler requests the type -// instead of being able to infer it. +// TODO: Add derive trait to implement `Alloc` for structs (and enums?). +// TODO: How to do verification systems? Should it be a separate trait or part of `ProofSystem`? -/// Variable Type -pub type Variable<P, T> = <T as Alloc<P>>::Output; +use core::convert::{Infallible, TryFrom}; /// Boolean Variable Type -pub type Bool<P> = Variable<P, bool>; +pub type Bool<P> = <P as BooleanSystem>::Bool; -/// Byte Variable Type -pub type U8<P> = Variable<P, u8>; +/// Variable Type +pub type Variable<T, P, K = (), U = K> = <T as Var<P, K, U>>::Variable; -/// Proof System -pub trait ProofSystem: Default { - /// Proof Type - type Proof; +/// Character Variable Type +pub type Char<P, K = (), U = K> = Variable<char, P, K, U>; - /// Error Type - type Error; +/// Signed 8-bit Integer Variable Type +pub type I8<P, K = (), U = K> = Variable<i8, P, K, U>; - /// Returns a proof that the proof system is consistent. - fn finish(self) -> Result<Self::Proof, Self::Error>; +/// Signed 16-bit Integer Variable Type +pub type I16<P, K = (), U = K> = Variable<i16, P, K, U>; + +/// Signed 32-bit Integer Variable Type +pub type I32<P, K = (), U = K> = Variable<i32, P, K, U>; + +/// Signed 64-bit Integer Variable Type +pub type I64<P, K = (), U = K> = Variable<i64, P, K, U>; + +/// Signed 128-bit Integer Variable Type +pub type I128<P, K = (), U = K> = Variable<i128, P, K, U>; + +/// Unsigned 8-bit Integer Variable Type +pub type U8<P, K = (), U = K> = Variable<u8, P, K, U>; + +/// Unsigned 16-bit Integer Variable Type +pub type U16<P, K = (), U = K> = Variable<u16, P, K, U>; + +/// Unsigned 32-bit Integer Variable Type +pub type U32<P, K = (), U = K> = Variable<u32, P, K, U>; + +/// Unsigned 64-bit Integer Variable Type +pub type U64<P, K = (), U = K> = Variable<u64, P, K, U>; + +/// Unsigned 128-bit Integer Variable Type +pub type U128<P, K = (), U = K> = Variable<u128, P, K, U>; + +/// Variable Reflection Trait +pub trait IsVariable<P, Known = (), Unknown = Known>: Sized +where + P: ?Sized, +{ + /// Origin Type of the Variable + type Type: Var<P, Known, Unknown, Variable = Self>; + + /// Returns a new variable with `value`. + #[inline] + fn new_variable(value: &Self::Type, ps: &mut P, mode: Known) -> Self { + value.as_variable(ps, mode) + } + + /// Returns a new variable with an unknown value. + #[inline] + fn new_unknown(ps: &mut P, mode: Unknown) -> Self { + Self::Type::unknown(ps, mode) + } } -/// Allocation Trait -pub trait Alloc<P> +/// Variable Trait +pub trait Var<P, Known = (), Unknown = Known> where - P: ProofSystem + ?Sized, + P: ?Sized, { - /// Resulting Variable Object - type Output; + /// Variable Object + type Variable: IsVariable<P, Known, Unknown, Type = Self>; /// Returns a new variable with value `self`. - fn as_variable(&self, ps: &mut P) -> Self::Output; + fn as_variable(&self, ps: &mut P, mode: Known) -> Self::Variable; /// Returns a new variable with an unknown value. - fn unknown(ps: &mut P) -> Self::Output; + fn unknown(ps: &mut P, mode: Unknown) -> Self::Variable; } -/// Assertion Trait -pub trait Assert: ProofSystem +/// Constant +pub trait Const<P>: Var<P, (), Infallible> where - bool: Alloc<Self>, + P: ?Sized, { + /// Returns a new constant with value `self`. + #[inline] + fn as_constant(&self, ps: &mut P) -> Self::Variable { + self.as_variable(ps, ()) + } +} + +/// Boolean Constraint System +pub trait BooleanSystem { + /// Boolean Variable Type + type Bool: IsVariable<Self, Self::KnownBool, Self::UnknownBool, Type = bool>; + + /// Known Boolean Allocation Mode Type + type KnownBool; + + /// Unknown Boolean Allocation Mode Type + type UnknownBool; + + /// Allocates a known boolean with value `b` with the given `mode`. + fn known_bool(&mut self, b: bool, mode: Self::KnownBool) -> Self::Bool; + + /// Allocates an unknown boolean with the given `mode`. + fn unknown_bool(&mut self, mode: Self::UnknownBool) -> Self::Bool; + + /// Allocates a new variable with the given `value`. + #[inline] + fn allocate_variable<T, K, U>(&mut self, value: T, mode: K) -> T::Variable + where + T: Var<Self, K, U>, + { + value.as_variable(self, mode) + } + + /// Allocates a new variable with an unknown value. + #[inline] + fn allocate_unknown<T, K, U>(&mut self, mode: U) -> T::Variable + where + T: Var<Self, K, U>, + { + T::unknown(self, mode) + } + /// Asserts that `b` is `true`. - fn assert(&mut self, b: Bool<Self>); + fn assert(&mut self, b: Self::Bool); /// Asserts that all the booleans in `iter` are `true`. #[inline] fn assert_all<I>(&mut self, iter: I) where - I: IntoIterator<Item = Bool<Self>>, + I: IntoIterator<Item = Self::Bool>, { iter.into_iter().for_each(move |b| self.assert(b)) } + + /// Asserts that `lhs` and `rhs` are equal. + #[inline] + fn assert_eq<V, K, U>(&mut self, lhs: &V, rhs: &V) + where + V: IsVariable<Self, K, U>, + V::Type: Equal<Self, K, U>, + { + V::Type::assert_eq(self, lhs, rhs) + } + + /// Asserts that all the elements in `iter` are equal to some `base` element. + #[inline] + fn assert_all_eq_to_base<'t, V, K, U, I>(&mut self, base: &'t V, iter: I) + where + V: 't + IsVariable<Self, K, U>, + V::Type: Equal<Self, K, U>, + I: IntoIterator<Item = &'t V>, + { + V::Type::assert_all_eq_to_base(self, base, iter) + } + + /// Asserts that all the elements in `iter` are equal. + #[inline] + fn assert_all_eq<'t, V, K, U, I>(&mut self, iter: I) + where + V: 't + IsVariable<Self, K, U>, + V::Type: Equal<Self, K, U>, + I: IntoIterator<Item = &'t V>, + { + V::Type::assert_all_eq(self, iter) + } } -/// Equality Trait -pub trait Equal<P>: Alloc<P> +impl<P> Var<P, P::KnownBool, P::UnknownBool> for bool where - P: ProofSystem + ?Sized, - bool: Alloc<P>, + P: BooleanSystem + ?Sized, { - /// Generates a boolean that represents the fact that `lhs` and `rhs` may be equal. - fn eq(lhs: &Variable<P, Self>, rhs: &Variable<P, Self>) -> Bool<P>; + type Variable = P::Bool; + + #[inline] + fn as_variable(&self, ps: &mut P, mode: P::KnownBool) -> Self::Variable { + ps.known_bool(*self, mode) + } + + #[inline] + fn unknown(ps: &mut P, mode: P::UnknownBool) -> Self::Variable { + ps.unknown_bool(mode) + } } -/// Assert Equal Trait -pub trait AssertEqual<T>: Assert +/// Equality Trait +pub trait Equal<P, Known = (), Unknown = Known>: Var<P, Known, Unknown> where - bool: Alloc<Self>, - T: Equal<Self>, + P: BooleanSystem + ?Sized, { + /// Generates a boolean that represents the fact that `lhs` and `rhs` may be equal. + fn eq(ps: &mut P, lhs: &Self::Variable, rhs: &Self::Variable) -> P::Bool; + /// Asserts that `lhs` and `rhs` are equal. #[inline] - fn assert_eq(&mut self, lhs: &Variable<Self, T>, rhs: &Variable<Self, T>) { - self.assert(T::eq(lhs, rhs)) + fn assert_eq(ps: &mut P, lhs: &Self::Variable, rhs: &Self::Variable) { + let boolean = Self::eq(ps, lhs, rhs); + ps.assert(boolean) + } + + /// Asserts that all the elements in `iter` are equal to some `base` element. + #[inline] + fn assert_all_eq_to_base<'t, I>(ps: &mut P, base: &'t Self::Variable, iter: I) + where + I: IntoIterator<Item = &'t Self::Variable>, + { + for item in iter { + Self::assert_eq(ps, base, item) + } } /// Asserts that all the elements in `iter` are equal. #[inline] - fn assert_all_eq<'t, I>(&mut self, iter: I) + fn assert_all_eq<'t, I>(ps: &mut P, iter: I) where - Variable<Self, T>: 't, - I: IntoIterator<Item = &'t Variable<Self, T>>, + Self::Variable: 't, + I: IntoIterator<Item = &'t Self::Variable>, { let mut iter = iter.into_iter(); if let Some(base) = iter.next() { - for item in iter { - self.assert_eq(base, item); - } + Self::assert_all_eq_to_base(ps, base, iter) + } + } +} + +/// Proof System +pub trait ProofSystem: BooleanSystem + Default { + /// Proof Type + type Proof; + + /// Error Type + type Error; + + /// Returns a proof that the boolean system is consistent. + fn finish(self) -> Result<Self::Proof, Self::Error>; +} + +/// Proof System Verifier +pub trait Verifier<P> +where + P: ProofSystem + ?Sized, +{ + /// Verifies that a proof generated from a proof system is valid. + fn verify(proof: &P::Proof) -> bool; +} + +/// Derived Allocation Mode +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Derived; + +/// Always Public Allocation Mode +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Public; + +impl From<Derived> for Public { + #[inline] + fn from(d: Derived) -> Self { + let _ = d; + Self + } +} + +/// Always Secret Allocation Mode +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Secret; + +impl From<Derived> for Secret { + #[inline] + fn from(d: Derived) -> Self { + let _ = d; + Self + } +} + +/// Public/Secret Allocation Mode +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum PublicOrSecret { + /// Public Variable Mode + Public, + + /// Secret Variable Mode + Secret, +} + +impl PublicOrSecret { + /// Returns `true` if this mode is for public variables. + #[inline] + pub const fn is_public(&self) -> bool { + matches!(self, Self::Public) + } + + /// Converts [`PublicOrSecret`] into `Option<Public>`. + #[inline] + pub const fn public(self) -> Option<Public> { + match self { + Self::Public => Some(Public), + _ => None, + } + } + + /// Returns `true` if this mode is for secret variables. + #[inline] + pub const fn is_secret(&self) -> bool { + matches!(self, Self::Secret) + } + + /// Converts [`PublicOrSecret`] into `Option<Secret>`. + #[inline] + pub const fn secret(self) -> Option<Secret> { + match self { + Self::Secret => Some(Secret), + _ => None, + } + } +} + +impl Default for PublicOrSecret { + #[inline] + fn default() -> Self { + Self::Secret + } +} + +impl From<Public> for PublicOrSecret { + #[inline] + fn from(p: Public) -> Self { + let _ = p; + Self::Public + } +} + +impl TryFrom<PublicOrSecret> for Public { + type Error = Secret; + + #[inline] + fn try_from(pos: PublicOrSecret) -> Result<Self, Self::Error> { + match pos { + PublicOrSecret::Public => Ok(Self), + PublicOrSecret::Secret => Err(Secret), + } + } +} + +impl From<Secret> for PublicOrSecret { + #[inline] + fn from(s: Secret) -> Self { + let _ = s; + Self::Secret + } +} + +impl TryFrom<PublicOrSecret> for Secret { + type Error = Public; + + #[inline] + fn try_from(pos: PublicOrSecret) -> Result<Self, Self::Error> { + match pos { + PublicOrSecret::Secret => Ok(Self), + PublicOrSecret::Public => Err(Public), } } } From e61818e06d8d18fbea3fa14a3485d570cc12790f Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sat, 11 Sep 2021 02:46:54 -0400 Subject: [PATCH 023/275] start setup for constraint system implementations --- manta-pay/Cargo.toml | 7 +++++-- manta-pay/src/crypto/constraint/mod.rs | 21 +++++++++++++++++++ .../src/crypto/constraint/proof_system.rs | 17 +++++++++++++++ manta-pay/src/crypto/mod.rs | 1 + 4 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 manta-pay/src/crypto/constraint/mod.rs create mode 100644 manta-pay/src/crypto/constraint/proof_system.rs diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 5289567da..8fe03f9a1 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -30,8 +30,12 @@ std = [] [dependencies] aes-gcm = { version = "0.9.4" } +ark-bls12-381 = { version = "0.3.0", default-features = false } ark-crypto-primitives = { version = "0.3.0", default-features = false } ark-ed-on-bls12-381 = { version = "0.3.0", default-features = false } +ark-groth16 = { version = "0.3.0", default-features = false } +ark-r1cs-std = { version = "0.3.1", default-features = false } +ark-relations = { version = "0.3.0", default-features = false } bip32 = { version = "0.2.2", default-features = false, features = ["bip39", "secp256k1"] } blake2 = { version = "0.9.2", default-features = false } cocoon = { version = "0.2.4", default-features = false } @@ -40,9 +44,8 @@ manta-accounting = { path = "../manta-accounting", default-features = false } manta-crypto = { path = "../manta-crypto", default-features = false } manta-util = { path = "../manta-util", default-features = false } rand = { version = "0.8.4", default-features = false } +rand_chacha = { version = "0.3.1", default-features = false } x25519-dalek = { git = "https://github.com/Manta-Network/x25519-dalek", default-features = false, features = ["u64_backend"] } [dev-dependencies] manta-crypto = { path = "../manta-crypto", features = ["test"] } -rand = "0.8.4" -rand_chacha = "0.3.1" diff --git a/manta-pay/src/crypto/constraint/mod.rs b/manta-pay/src/crypto/constraint/mod.rs new file mode 100644 index 000000000..c04b76623 --- /dev/null +++ b/manta-pay/src/crypto/constraint/mod.rs @@ -0,0 +1,21 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Constraint System Implementations + +mod proof_system; + +pub use proof_system::*; diff --git a/manta-pay/src/crypto/constraint/proof_system.rs b/manta-pay/src/crypto/constraint/proof_system.rs new file mode 100644 index 000000000..cf98df82c --- /dev/null +++ b/manta-pay/src/crypto/constraint/proof_system.rs @@ -0,0 +1,17 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Proof System Implementation diff --git a/manta-pay/src/crypto/mod.rs b/manta-pay/src/crypto/mod.rs index 168ae8da4..603696d1c 100644 --- a/manta-pay/src/crypto/mod.rs +++ b/manta-pay/src/crypto/mod.rs @@ -17,6 +17,7 @@ //! Manta Pay Cryptographic Primitives Implementations pub mod commitment; +pub mod constraint; pub mod ies; pub mod merkle_tree; pub mod prf; From 31ea7e774a3b9d20459f617c894e34e6825e7e71 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sat, 11 Sep 2021 04:41:03 -0400 Subject: [PATCH 024/275] converge on better representation for type-level constraints --- manta-accounting/src/asset.rs | 30 +-- manta-accounting/src/identity.rs | 440 ++++++++++++++++++++----------- manta-accounting/src/transfer.rs | 66 +++-- manta-codec/Cargo.toml | 2 +- manta-crypto/src/constraint.rs | 25 +- manta-pay/Cargo.toml | 1 + 6 files changed, 373 insertions(+), 191 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index a3300f60e..85405c617 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -31,7 +31,7 @@ use core::{ use derive_more::{ Add, AddAssign, Display, Div, DivAssign, From, Mul, MulAssign, Product, Sub, SubAssign, Sum, }; -use manta_crypto::constraint::{IsVariable, Secret, Var, Variable}; +use manta_crypto::constraint::{IsVariable, PublicOrSecret, Secret, Var, Variable}; use manta_util::{ array_map, fallible_array_map, into_array_unchecked, ByteAccumulator, ConcatBytes, }; @@ -334,52 +334,52 @@ impl Distribution<Asset> for Standard { } /// Asset Id Variable -pub type AssetIdVar<P, M> = Variable<AssetId, P, M>; +pub type AssetIdVar<P> = Variable<AssetId, P, PublicOrSecret>; /// Asset Balance Variable -pub type AssetBalanceVar<P, M> = Variable<AssetBalance, P, M>; +pub type AssetBalanceVar<P> = Variable<AssetBalance, P, PublicOrSecret>; /// Asset Variable pub struct AssetVar<P> where - AssetId: Var<P, Secret>, - AssetBalance: Var<P, Secret>, + AssetId: Var<P, PublicOrSecret>, + AssetBalance: Var<P, PublicOrSecret>, { /// Asset Id - pub id: AssetIdVar<P, Secret>, + pub id: AssetIdVar<P>, /// Asset Value - pub value: AssetBalanceVar<P, Secret>, + pub value: AssetBalanceVar<P>, } impl<P> IsVariable<P, Secret> for AssetVar<P> where - AssetId: Var<P, Secret>, - AssetBalance: Var<P, Secret>, + AssetId: Var<P, PublicOrSecret>, + AssetBalance: Var<P, PublicOrSecret>, { type Type = Asset; } impl<P> Var<P, Secret> for Asset where - AssetId: Var<P, Secret>, - AssetBalance: Var<P, Secret>, + AssetId: Var<P, PublicOrSecret>, + AssetBalance: Var<P, PublicOrSecret>, { type Variable = AssetVar<P>; #[inline] fn as_variable(&self, ps: &mut P, mode: Secret) -> Self::Variable { Self::Variable { - id: self.id.as_variable(ps, mode), - value: self.value.as_variable(ps, mode), + id: self.id.as_variable(ps, mode.into()), + value: self.value.as_variable(ps, mode.into()), } } #[inline] fn unknown(ps: &mut P, mode: Secret) -> Self::Variable { Self::Variable { - id: AssetId::unknown(ps, mode), - value: AssetBalance::unknown(ps, mode), + id: AssetId::unknown(ps, mode.into()), + value: AssetBalance::unknown(ps, mode.into()), } } } diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index a9407c0b3..87753287b 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -30,7 +30,10 @@ use crate::{ use core::{convert::Infallible, fmt::Debug, hash::Hash, marker::PhantomData}; use manta_crypto::{ commitment::{CommitmentScheme, Input as CommitmentInput}, - constraint::{Bool, BooleanSystem, Derived, IsVariable, Public, Secret, Var, Variable}, + constraint::{ + BooleanSystem, Constant, Derived, Equal, IsVariable, Public, PublicOrSecret, Secret, Var, + Variable, + }, ies::{self, EncryptedMessage, IntegratedEncryptionScheme}, set::{ContainmentProof, VerifiedSet}, PseudorandomFunctionFamily, @@ -63,59 +66,129 @@ pub trait IdentityConfiguration { type Rng: CryptoRng + RngCore + SeedableRng<Seed = Self::SecretKey>; } -/// [`PseudorandomFunctionFamily::Input`] Type Alias +/* TODO: +/// [`Identity`] Proof System Configuration +pub trait IdentityProofSystemConfiguration<P>: IdentityConfiguration +where + P: BooleanSystem + ?Sized, + SecretKey<Self>: Var<P, Secret>, + PseudorandomFunctionFamilyInput<Self>: Var<P, Secret>, + PseudorandomFunctionFamilyOutput<Self>: Equal<P, PublicOrSecret>, + CommitmentSchemeRandomness<Self>: Var<P, Secret>, + CommitmentSchemeOutput<Self>: Equal<P, PublicOrSecret>, +{ + /// Pseudorandom Function Family Variable Type + type PseudorandomFunctionFamilyVar: PseudorandomFunctionFamily< + Seed = SecretKeyVar<Self, P>, + Input = PseudorandomFunctionFamilyInputVar<Self, P>, + Output = PseudorandomFunctionFamilyOutputVar<Self, P>, + >; + + /// Commitment Scheme Variable Type + type CommitmentSchemeVar: CommitmentScheme< + Randomness = CommitmentSchemeRandomnessVar<Self, P>, + Output = CommitmentSchemeOutputVar<Self, P>, + >; +} +*/ + +/// [`PseudorandomFunctionFamily::Input`] Type pub type PseudorandomFunctionFamilyInput<C> = <<C as IdentityConfiguration>::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Input; -/// [`PseudorandomFunctionFamily::Output`] Type Alias +/// [`PseudorandomFunctionFamily::Output`] Type pub type PseudorandomFunctionFamilyOutput<C> = <<C as IdentityConfiguration>::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Output; -/// [`CommitmentScheme::Randomness`] Type Alias +/// [`CommitmentScheme::Randomness`] Type pub type CommitmentSchemeRandomness<C> = <<C as IdentityConfiguration>::CommitmentScheme as CommitmentScheme>::Randomness; -/// [`CommitmentScheme::Output`] Type Alias +/// [`CommitmentScheme::Output`] Type pub type CommitmentSchemeOutput<C> = <<C as IdentityConfiguration>::CommitmentScheme as CommitmentScheme>::Output; -/// Secret Key Type Alias +/// Secret Key Type pub type SecretKey<C> = <C as IdentityConfiguration>::SecretKey; -/// Public Key Type Alias +/// Public Key Type pub type PublicKey<C> = PseudorandomFunctionFamilyOutput<C>; -/// Void Number Generator Type Alias +/// Void Number Generator Type pub type VoidNumberGenerator<C> = PseudorandomFunctionFamilyInput<C>; -/// Void Number Type Alias +/// Void Number Type pub type VoidNumber<C> = PseudorandomFunctionFamilyOutput<C>; -/// Void Number Commitment Randomness Type Alias +/// Void Number Commitment Randomness Type pub type VoidNumberCommitmentRandomness<C> = CommitmentSchemeRandomness<C>; -/// Void Number Commitment Type Alias +/// Void Number Commitment Type pub type VoidNumberCommitment<C> = CommitmentSchemeOutput<C>; -/// UTXO Randomness Type Alias +/// UTXO Randomness Type pub type UtxoRandomness<C> = CommitmentSchemeRandomness<C>; -/// UTXO Type Alias +/// UTXO Type pub type Utxo<C> = CommitmentSchemeOutput<C>; -/// Generates a [`VoidNumberCommitment`] from a given `public_key`, `void_number_generator`, and +/// [`PseudorandomFunctionFamily::Input`] Variable Type +pub type PseudorandomFunctionFamilyInputVar<C, P> = + Variable<PseudorandomFunctionFamilyInput<C>, P, Secret>; + +/// [`PseudorandomFunctionFamily::Output`] Variable Type +pub type PseudorandomFunctionFamilyOutputVar<C, P> = + Variable<PseudorandomFunctionFamilyOutput<C>, P, PublicOrSecret>; + +/// [`CommitmentScheme`] Variable Type +pub type CommitmentSchemeVar<C, P> = Constant<<C as IdentityConfiguration>::CommitmentScheme, P>; + +/// [`CommitmentScheme::Randomness`] Variable Type +pub type CommitmentSchemeRandomnessVar<C, P> = Variable<CommitmentSchemeRandomness<C>, P, Secret>; + +/// [`CommitmentScheme::Output`] Variable Type +pub type CommitmentSchemeOutputVar<C, P> = Variable<CommitmentSchemeOutput<C>, P, PublicOrSecret>; + +/// Secret Key Variable Type +pub type SecretKeyVar<C, P> = Variable<SecretKey<C>, P, Secret>; + +/// Public Key Variable Type +pub type PublicKeyVar<C, P> = PseudorandomFunctionFamilyOutputVar<C, P>; + +/// Void Number Generator Variable Type +pub type VoidNumberGeneratorVar<C, P> = PseudorandomFunctionFamilyInputVar<C, P>; + +/// Void Number Variable Type +pub type VoidNumberVar<C, P> = PseudorandomFunctionFamilyOutputVar<C, P>; + +/// Void Number Commitment Randomness Variable Type +pub type VoidNumberCommitmentRandomnessVar<C, P> = CommitmentSchemeRandomnessVar<C, P>; + +/// Void Number Commitment Variable Type +pub type VoidNumberCommitmentVar<C, P> = CommitmentSchemeOutputVar<C, P>; + +/// UTXO Randomness Variable Type +pub type UtxoRandomnessVar<C, P> = CommitmentSchemeRandomnessVar<C, P>; + +/// UTXO Variable Type +pub type UtxoVar<C, P> = CommitmentSchemeOutputVar<C, P>; + +/// UTXO Containment Proof Variable Type +pub type UtxoContainmentProofVar<S, P> = Variable<ContainmentProof<S>, P, Secret>; + +/// Generates a void number commitment from `public_key`, `void_number_generator`, and /// `void_number_commitment_randomness`. #[inline] -pub fn generate_void_number_commitment<C>( - commitment_scheme: &C::CommitmentScheme, - public_key: &PublicKey<C>, - void_number_generator: &VoidNumberGenerator<C>, - void_number_commitment_randomness: &VoidNumberCommitmentRandomness<C>, -) -> VoidNumberCommitment<C> +pub fn generate_void_number_commitment<CS, PK, VNG>( + commitment_scheme: &CS, + public_key: &PK, + void_number_generator: &VNG, + void_number_commitment_randomness: &CS::Randomness, +) -> CS::Output where - C: IdentityConfiguration, - PublicKey<C>: CommitmentInput<C::CommitmentScheme>, - VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, + CS: CommitmentScheme, + PK: CommitmentInput<CS>, + VNG: CommitmentInput<CS>, { commitment_scheme .start() @@ -124,18 +197,18 @@ where .commit(void_number_commitment_randomness) } -/// Generates a [`Utxo`] from a given `asset`, `void_number_commitment`, and `utxo_randomness`. +/// Generates a UTXO from `asset`, `void_number_commitment`, and `utxo_randomness`. #[inline] -pub fn generate_utxo<C>( - commitment_scheme: &C::CommitmentScheme, - asset: &Asset, - void_number_commitment: &VoidNumberCommitment<C>, - utxo_randomness: &UtxoRandomness<C>, -) -> Utxo<C> +pub fn generate_utxo<CS, A, VNC>( + commitment_scheme: &CS, + asset: &A, + void_number_commitment: &VNC, + utxo_randomness: &CS::Randomness, +) -> CS::Output where - C: IdentityConfiguration, - Asset: CommitmentInput<C::CommitmentScheme>, - VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, + CS: CommitmentScheme, + A: CommitmentInput<CS>, + VNC: CommitmentInput<CS>, { commitment_scheme .start() @@ -212,7 +285,7 @@ where PublicKey<C>: CommitmentInput<C::CommitmentScheme>, VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, { - generate_void_number_commitment::<C>( + generate_void_number_commitment( commitment_scheme, public_key, &self.void_number_generator, @@ -232,7 +305,7 @@ where Asset: CommitmentInput<C::CommitmentScheme>, VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { - generate_utxo::<C>( + generate_utxo( commitment_scheme, asset, void_number_commitment, @@ -263,13 +336,13 @@ where UtxoRandomness<C>: Var<P, Secret>, { /// Void Number Generator - pub void_number_generator: Variable<VoidNumberGenerator<C>, P, Secret>, + pub void_number_generator: VoidNumberGeneratorVar<C, P>, /// Void Number Commitment Randomness - pub void_number_commitment_randomness: Variable<VoidNumberCommitmentRandomness<C>, P, Secret>, + pub void_number_commitment_randomness: VoidNumberCommitmentRandomnessVar<C, P>, /// UTXO Randomness - pub utxo_randomness: Variable<UtxoRandomness<C>, P, Secret>, + pub utxo_randomness: UtxoRandomnessVar<C, P>, } impl<C, P> IsVariable<P, Secret> for AssetParametersVar<C, P> @@ -462,25 +535,25 @@ where parameters.void_number_commitment(commitment_scheme, &self.public_key()) } - /// Generates a [`Utxo`] for an `asset` using the `parameters`. + /// Returns the [`PublicKey`], [`VoidNumberCommitment`], and [`Utxo`] for this identity. #[inline] - pub(crate) fn utxo( + pub(crate) fn construct_utxo( &self, commitment_scheme: &C::CommitmentScheme, asset: &Asset, parameters: &AssetParameters<C>, - ) -> Utxo<C> + ) -> (PublicKey<C>, VoidNumberCommitment<C>, Utxo<C>) where PublicKey<C>: CommitmentInput<C::CommitmentScheme>, VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, Asset: CommitmentInput<C::CommitmentScheme>, VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { - parameters.utxo( - commitment_scheme, - asset, - &self.void_number_commitment(commitment_scheme, parameters), - ) + let public_key = self.public_key(); + let void_number_commitment = + parameters.void_number_commitment(commitment_scheme, &public_key); + let utxo = parameters.utxo(commitment_scheme, asset, &void_number_commitment); + (public_key, void_number_commitment, utxo) } /// Builds a new [`Sender`] for the given `asset`. @@ -500,13 +573,16 @@ where VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { let parameters = self.parameters(); - let utxo = self.utxo(commitment_scheme, &asset, &parameters); + let (public_key, void_number_commitment, utxo) = + self.construct_utxo(commitment_scheme, &asset, &parameters); Ok(Sender { utxo_containment_proof: utxo_set.get_containment_proof(&utxo)?, void_number: self.void_number(&parameters.void_number_generator), - identity: self, + secret_key: self.secret_key, + public_key, asset, parameters, + void_number_commitment, utxo, }) } @@ -763,7 +839,7 @@ where } = self; Ok(Receiver { encrypted_asset: asset_public_key.encrypt(&asset, rng)?, - utxo: generate_utxo::<C>( + utxo: generate_utxo( commitment_scheme, &asset, &void_number_commitment, @@ -1002,8 +1078,11 @@ where C: IdentityConfiguration, S: VerifiedSet<Item = Utxo<C>>, { - /// Sender Identity - identity: Identity<C>, + /// Secret Key + secret_key: SecretKey<C>, + + /// Public Key + public_key: PublicKey<C>, /// Asset asset: Asset, @@ -1014,6 +1093,9 @@ where /// Void Number void_number: VoidNumber<C>, + /// Void Number Commitment + void_number_commitment: VoidNumberCommitment<C>, + /// Unspent Transaction Output utxo: Utxo<C>, @@ -1094,6 +1176,12 @@ where &self.void_number } + /// Returns the void number commitment for this sender. + #[inline] + pub fn void_number_commitment(&self) -> &VoidNumberCommitment<C> { + &self.void_number_commitment + } + /// Returns the UTXO for this sender. #[inline] pub fn utxo(&self) -> &Utxo<C> { @@ -1122,18 +1210,20 @@ where C: IdentityConfiguration, S: VerifiedSet<Item = Utxo<C>>, P: BooleanSystem, - C::SecretKey: Var<P, Secret>, - AssetId: Var<P, Secret>, - AssetBalance: Var<P, Secret>, - VoidNumberGenerator<C>: Var<P, Secret>, - VoidNumberCommitmentRandomness<C>: Var<P, Secret>, - UtxoRandomness<C>: Var<P, Secret>, - VoidNumber<C>: Var<P, Public>, - Utxo<C>: Var<P, Secret>, + AssetId: Var<P, PublicOrSecret>, + AssetBalance: Var<P, PublicOrSecret>, + SecretKey<C>: Var<P, Secret>, + PseudorandomFunctionFamilyInput<C>: Var<P, Secret>, + PseudorandomFunctionFamilyOutput<C>: Equal<P, PublicOrSecret>, + CommitmentSchemeRandomness<C>: Var<P, Secret>, + CommitmentSchemeOutput<C>: Equal<P, PublicOrSecret>, ContainmentProof<S>: Var<P, Secret>, { /// Secret Key - secret_key: Variable<C::SecretKey, P, Secret>, + secret_key: SecretKeyVar<C, P>, + + /// Public Key + public_key: PublicKeyVar<C, P>, /// Asset asset: AssetVar<P>, @@ -1142,13 +1232,17 @@ where parameters: AssetParametersVar<C, P>, /// Void Number - void_number: Variable<VoidNumber<C>, P, Public>, + void_number: VoidNumberVar<C, P>, + + /// Void Number Commitment + void_number_commitment: VoidNumberCommitmentVar<C, P>, /// Unspent Transaction Output - utxo: Variable<Utxo<C>, P, Secret>, + utxo: UtxoVar<C, P>, /// UTXO Containment Proof - utxo_containment_proof: Variable<ContainmentProof<S>, P, Secret>, + // TODO: think about what the mode type for this should be + utxo_containment_proof: UtxoContainmentProofVar<S, P>, } impl<C, S, P> SenderVar<C, S, P> @@ -1156,31 +1250,45 @@ where C: IdentityConfiguration, S: VerifiedSet<Item = Utxo<C>>, P: BooleanSystem, - C::SecretKey: Var<P, Secret>, - AssetId: Var<P, Secret>, - AssetBalance: Var<P, Secret>, - VoidNumberGenerator<C>: Var<P, Secret>, - VoidNumberCommitmentRandomness<C>: Var<P, Secret>, - UtxoRandomness<C>: Var<P, Secret>, - VoidNumber<C>: Var<P, Public>, - Utxo<C>: Var<P, Secret>, + AssetId: Var<P, PublicOrSecret>, + AssetBalance: Var<P, PublicOrSecret>, + SecretKey<C>: Var<P, Secret>, + PseudorandomFunctionFamilyInput<C>: Var<P, Secret>, + PseudorandomFunctionFamilyOutput<C>: Equal<P, PublicOrSecret>, + CommitmentSchemeRandomness<C>: Var<P, Secret>, + CommitmentSchemeOutput<C>: Equal<P, PublicOrSecret>, ContainmentProof<S>: Var<P, Secret>, { /// Returns the asset id of this sender. #[inline] - pub fn asset_id(&self) -> &AssetIdVar<P, Secret> { + pub fn asset_id(&self) -> &AssetIdVar<P> { &self.asset.id } /// Returns the asset value of this sender. #[inline] - pub fn asset_value(&self) -> &AssetBalanceVar<P, Secret> { + pub fn asset_value(&self) -> &AssetBalanceVar<P> { &self.asset.value } /// Checks if `self` is a well-formed sender. #[inline] - pub fn is_well_formed(&self) -> Bool<P> { + pub fn verify_well_formed<PRFVar, CSVar>(&self, ps: &mut P, commitment_scheme: &CSVar) + where + PRFVar: PseudorandomFunctionFamily< + Seed = SecretKeyVar<C, P>, + Input = PseudorandomFunctionFamilyInputVar<C, P>, + Output = PseudorandomFunctionFamilyOutputVar<C, P>, + >, + CSVar: CommitmentScheme< + Randomness = CommitmentSchemeRandomnessVar<C, P>, + Output = CommitmentSchemeOutputVar<C, P>, + >, + PublicKeyVar<C, P>: CommitmentInput<CSVar>, + VoidNumberGeneratorVar<C, P>: CommitmentInput<CSVar>, + AssetVar<P>: CommitmentInput<CSVar>, + VoidNumberCommitmentVar<C, P>: CommitmentInput<CSVar>, + { // FIXME: Implement well-formedness check: // // 1. pk = PRF(sk, 0) [public: (), secret: (pk, sk)] @@ -1191,7 +1299,34 @@ where // // FIXME: should `k` be private or not? - todo!() + ps.assert_eq(&self.public_key, &PRFVar::evaluate_zero(&self.secret_key)); + + ps.assert_eq( + &self.void_number, + &PRFVar::evaluate(&self.secret_key, &self.parameters.void_number_generator), + ); + + ps.assert_eq( + &self.void_number_commitment, + &generate_void_number_commitment( + commitment_scheme, + &self.public_key, + &self.parameters.void_number_generator, + &self.parameters.void_number_commitment_randomness, + ), + ); + + ps.assert_eq( + &self.utxo, + &generate_utxo( + commitment_scheme, + &self.asset, + &self.void_number_commitment, + &self.parameters.utxo_randomness, + ), + ); + + // FIXME: ps.assert(&self.utxo_containment_proof.verify(self.utxo)); } } @@ -1200,14 +1335,13 @@ where C: IdentityConfiguration, S: VerifiedSet<Item = Utxo<C>>, P: BooleanSystem, - C::SecretKey: Var<P, Secret>, - AssetId: Var<P, Secret>, - AssetBalance: Var<P, Secret>, - VoidNumberGenerator<C>: Var<P, Secret>, - VoidNumberCommitmentRandomness<C>: Var<P, Secret>, - UtxoRandomness<C>: Var<P, Secret>, - VoidNumber<C>: Var<P, Public>, - Utxo<C>: Var<P, Secret>, + AssetId: Var<P, PublicOrSecret>, + AssetBalance: Var<P, PublicOrSecret>, + SecretKey<C>: Var<P, Secret>, + PseudorandomFunctionFamilyInput<C>: Var<P, Secret>, + PseudorandomFunctionFamilyOutput<C>: Equal<P, PublicOrSecret>, + CommitmentSchemeRandomness<C>: Var<P, Secret>, + CommitmentSchemeOutput<C>: Equal<P, PublicOrSecret>, ContainmentProof<S>: Var<P, Secret>, { type Type = Sender<C, S>; @@ -1218,38 +1352,43 @@ where C: IdentityConfiguration, S: VerifiedSet<Item = Utxo<C>>, P: BooleanSystem, - C::SecretKey: Var<P, Secret>, - AssetId: Var<P, Secret>, - AssetBalance: Var<P, Secret>, - VoidNumberGenerator<C>: Var<P, Secret>, - VoidNumberCommitmentRandomness<C>: Var<P, Secret>, - UtxoRandomness<C>: Var<P, Secret>, - VoidNumber<C>: Var<P, Public>, - Utxo<C>: Var<P, Secret>, + AssetId: Var<P, PublicOrSecret>, + AssetBalance: Var<P, PublicOrSecret>, + SecretKey<C>: Var<P, Secret>, + PseudorandomFunctionFamilyInput<C>: Var<P, Secret>, + PseudorandomFunctionFamilyOutput<C>: Equal<P, PublicOrSecret>, + CommitmentSchemeRandomness<C>: Var<P, Secret>, + CommitmentSchemeOutput<C>: Equal<P, PublicOrSecret>, ContainmentProof<S>: Var<P, Secret>, { type Variable = SenderVar<C, S, P>; #[inline] fn as_variable(&self, ps: &mut P, mode: Derived) -> Self::Variable { + // FIXME: Have one function where we do variable allocation instead of two. Self::Variable { - secret_key: self.identity.secret_key.as_variable(ps, mode.into()), + secret_key: self.secret_key.as_variable(ps, mode.into()), + public_key: self.public_key.as_variable(ps, Secret.into()), asset: self.asset.as_variable(ps, mode.into()), parameters: self.parameters.as_variable(ps, mode.into()), - void_number: self.void_number.as_variable(ps, mode.into()), - utxo: self.utxo.as_variable(ps, mode.into()), + void_number: self.void_number.as_variable(ps, Public.into()), + void_number_commitment: self.void_number_commitment.as_variable(ps, Public.into()), + utxo: self.utxo.as_variable(ps, Secret.into()), utxo_containment_proof: self.utxo_containment_proof.as_variable(ps, mode.into()), } } #[inline] fn unknown(ps: &mut P, mode: Derived) -> Self::Variable { + // FIXME: Have one function where we do variable allocation instead of two. Self::Variable { - secret_key: C::SecretKey::unknown(ps, mode.into()), + secret_key: SecretKey::<C>::unknown(ps, mode.into()), + public_key: PublicKey::<C>::unknown(ps, Secret.into()), asset: Asset::unknown(ps, mode.into()), parameters: AssetParameters::<C>::unknown(ps, mode.into()), - void_number: VoidNumber::<C>::unknown(ps, mode.into()), - utxo: Utxo::<C>::unknown(ps, mode.into()), + void_number: VoidNumber::<C>::unknown(ps, Public.into()), + void_number_commitment: VoidNumberCommitment::<C>::unknown(ps, Public.into()), + utxo: Utxo::<C>::unknown(ps, Secret.into()), utxo_containment_proof: ContainmentProof::<S>::unknown(ps, mode.into()), } } @@ -1412,29 +1551,28 @@ where } } -/// Receiver Proof Content +/// Receiver Variable pub struct ReceiverVar<C, I, P> where C: IdentityConfiguration, I: IntegratedEncryptionScheme<Plaintext = Asset>, P: BooleanSystem, - AssetId: Var<P, Secret>, - AssetBalance: Var<P, Secret>, - UtxoRandomness<C>: Var<P, Secret>, - VoidNumberCommitment<C>: Var<P, Secret>, - Utxo<C>: Var<P, Public>, + AssetId: Var<P, PublicOrSecret>, + AssetBalance: Var<P, PublicOrSecret>, + CommitmentSchemeRandomness<C>: Var<P, Secret>, + CommitmentSchemeOutput<C>: Equal<P, PublicOrSecret>, { /// Asset asset: AssetVar<P>, /// UTXO Randomness - utxo_randomness: Variable<UtxoRandomness<C>, P, Secret>, + utxo_randomness: UtxoRandomnessVar<C, P>, /// Void Number Commitment - void_number_commitment: Variable<VoidNumberCommitment<C>, P, Secret>, + void_number_commitment: VoidNumberCommitmentVar<C, P>, /// Unspent Transaction Output - utxo: Variable<Utxo<C>, P, Public>, + utxo: UtxoVar<C, P>, /// Type Parameter Marker __: PhantomData<I>, @@ -1445,32 +1583,50 @@ where C: IdentityConfiguration, I: IntegratedEncryptionScheme<Plaintext = Asset>, P: BooleanSystem, - AssetId: Var<P, Secret>, - AssetBalance: Var<P, Secret>, - UtxoRandomness<C>: Var<P, Secret>, - VoidNumberCommitment<C>: Var<P, Secret>, - Utxo<C>: Var<P, Public>, + AssetId: Var<P, PublicOrSecret>, + AssetBalance: Var<P, PublicOrSecret>, + CommitmentSchemeRandomness<C>: Var<P, Secret>, + CommitmentSchemeOutput<C>: Equal<P, PublicOrSecret>, { /// Returns the asset id of this receiver. #[inline] - pub fn asset_id(&self) -> &AssetIdVar<P, Secret> { + pub fn asset_id(&self) -> &AssetIdVar<P> { &self.asset.id } /// Returns the asset value of this receiver. #[inline] - pub fn asset_value(&self) -> &AssetBalanceVar<P, Secret> { + pub fn asset_value(&self) -> &AssetBalanceVar<P> { &self.asset.value } /// Checks if `self` is a well-formed receiver. + /// + /// This [`ReceiverVar`] is well-formed whenever: + /// ```text + /// utxo = COM(asset || k, s) + /// ``` + /// where `k` is `self.void_number_commitment` and `s` is `self.utxo_randomness`. In this + /// equation we have `{ utxo } : Public`, `{ asset, k, s } : Secret`. #[inline] - pub fn is_well_formed(&self) -> Bool<P> { - // FIXME: Implement well-formedness check: - // - // 1. cm = COM(asset_id || asset_value || k, s) [public: (cm), secret: (asset, k, s)] - - todo!() + pub fn verify_well_formed<CSVar>(&self, ps: &mut P, commitment_scheme: &CSVar) + where + CSVar: CommitmentScheme< + Randomness = CommitmentSchemeRandomnessVar<C, P>, + Output = CommitmentSchemeOutputVar<C, P>, + >, + AssetVar<P>: CommitmentInput<CSVar>, + VoidNumberCommitmentVar<C, P>: CommitmentInput<CSVar>, + { + ps.assert_eq( + &self.utxo, + &generate_utxo( + commitment_scheme, + &self.asset, + &self.void_number_commitment, + &self.utxo_randomness, + ), + ); } } @@ -1479,11 +1635,10 @@ where C: IdentityConfiguration, I: IntegratedEncryptionScheme<Plaintext = Asset>, P: BooleanSystem, - AssetId: Var<P, Secret>, - AssetBalance: Var<P, Secret>, - UtxoRandomness<C>: Var<P, Secret>, - VoidNumberCommitment<C>: Var<P, Secret>, - Utxo<C>: Var<P, Public>, + AssetId: Var<P, PublicOrSecret>, + AssetBalance: Var<P, PublicOrSecret>, + CommitmentSchemeRandomness<C>: Var<P, Secret>, + CommitmentSchemeOutput<C>: Equal<P, PublicOrSecret>, { type Type = Receiver<C, I>; } @@ -1493,50 +1648,33 @@ where C: IdentityConfiguration, I: IntegratedEncryptionScheme<Plaintext = Asset>, P: BooleanSystem, - AssetId: Var<P, Secret>, - AssetBalance: Var<P, Secret>, - UtxoRandomness<C>: Var<P, Secret>, - VoidNumberCommitment<C>: Var<P, Secret>, - Utxo<C>: Var<P, Public>, + AssetId: Var<P, PublicOrSecret>, + AssetBalance: Var<P, PublicOrSecret>, + CommitmentSchemeRandomness<C>: Var<P, Secret>, + CommitmentSchemeOutput<C>: Equal<P, PublicOrSecret>, { type Variable = ReceiverVar<C, I, P>; #[inline] fn as_variable(&self, ps: &mut P, mode: Derived) -> Self::Variable { - // FIXME: Why can't we automatically derive the modes for all of them? - // - // > It's because `Utxo` and `VoidNumberCommitment` are both `CommitmentSchemeOutput` - // and so the type system automatically sees that this type should be secret - // because `VoidNumberCommitment` comes first. We'll have to figure out a way to - // fix this. The type system is just getting confused, there is no security issue - // here since only the correct input type will work, it's just that `.into()` is not - // helpful. - // + // FIXME: Have one function where we do variable allocation instead of two. Self::Variable { asset: self.asset.as_variable(ps, mode.into()), utxo_randomness: self.utxo_randomness.as_variable(ps, mode.into()), - void_number_commitment: self.void_number_commitment.as_variable(ps, Secret), - utxo: self.utxo.as_variable(ps, Public), + void_number_commitment: self.void_number_commitment.as_variable(ps, Secret.into()), + utxo: self.utxo.as_variable(ps, Public.into()), __: PhantomData, } } #[inline] fn unknown(ps: &mut P, mode: Derived) -> Self::Variable { - // FIXME: Why can't we automatically derive the modes for all of them? - // - // > It's because `Utxo` and `VoidNumberCommitment` are both `CommitmentSchemeOutput` - // and so the type system automatically sees that this type should be secret - // because `VoidNumberCommitment` comes first. We'll have to figure out a way to - // fix this. The type system is just getting confused, there is no security issue - // here since only the correct input type will work, it's just that `.into()` is not - // helpful. - // + // FIXME: Have one function where we do variable allocation instead of two. Self::Variable { asset: Asset::unknown(ps, mode.into()), utxo_randomness: UtxoRandomness::<C>::unknown(ps, mode.into()), - void_number_commitment: VoidNumberCommitment::<C>::unknown(ps, Secret), - utxo: Utxo::<C>::unknown(ps, Public), + void_number_commitment: VoidNumberCommitment::<C>::unknown(ps, Secret.into()), + utxo: Utxo::<C>::unknown(ps, Public.into()), __: PhantomData, } } diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 5bfd85f06..673ed28db 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -17,18 +17,24 @@ //! Transfer Protocols use crate::{ - asset::{sample_asset_balances, Asset, AssetBalance, AssetBalanceVar, AssetBalances, AssetId}, + asset::{ + sample_asset_balances, Asset, AssetBalance, AssetBalanceVar, AssetBalances, AssetId, + AssetVar, + }, identity::{ - IdentityConfiguration, Receiver, ReceiverPost, ReceiverVar, Sender, SenderPost, SenderVar, - Utxo, UtxoRandomness, VoidNumber, VoidNumberCommitment, VoidNumberCommitmentRandomness, - VoidNumberGenerator, + CommitmentSchemeOutput, CommitmentSchemeOutputVar, CommitmentSchemeRandomness, + CommitmentSchemeRandomnessVar, CommitmentSchemeVar, IdentityConfiguration, + PseudorandomFunctionFamilyInput, PseudorandomFunctionFamilyOutput, PublicKeyVar, Receiver, + ReceiverPost, ReceiverVar, SecretKey, Sender, SenderPost, SenderVar, Utxo, VoidNumber, + VoidNumberCommitmentVar, VoidNumberGeneratorVar, }, ledger::{Ledger, PostError}, }; use alloc::vec::Vec; use core::iter::Sum; use manta_crypto::{ - constraint::{Equal, ProofSystem, Public, Secret, Var}, + commitment::{CommitmentScheme, Input as CommitmentInput}, + constraint::{Const, Constant, Equal, ProofSystem, PublicOrSecret, Secret, Var}, ies::{EncryptedMessage, IntegratedEncryptionScheme}, set::{ContainmentProof, VerifiedSet}, }; @@ -196,36 +202,54 @@ where /// Builds constraints for secret transfer validity proof. #[inline] - fn build_proof_content( - proof_system: &mut T, + fn verify( + ps: &mut T, + commitment_scheme: &T::CommitmentScheme, senders: Vec<SecretSenderVar<T>>, receivers: Vec<SecretReceiverVar<T>>, ) where - T::SecretKey: Var<T, Secret>, - AssetId: Equal<T, Secret>, - AssetBalance: Equal<T, Secret>, - for<'i> &'i AssetBalanceVar<T, Secret>: Sum, - VoidNumberGenerator<T>: Var<T, Secret>, - VoidNumberCommitmentRandomness<T>: Var<T, Secret>, - UtxoRandomness<T>: Var<T, Secret>, - VoidNumber<T>: Var<T, Public>, - VoidNumberCommitment<T>: Var<T, Secret>, - Utxo<T>: Var<T, Secret> + Var<T, Public>, + T::CommitmentScheme: Const<T>, + AssetId: Equal<T, PublicOrSecret>, + AssetBalance: Equal<T, PublicOrSecret>, + for<'i> &'i AssetBalanceVar<T>: Sum, + SecretKey<T>: Var<T, Secret>, + PseudorandomFunctionFamilyInput<T>: Var<T, Secret>, + PseudorandomFunctionFamilyOutput<T>: Equal<T, PublicOrSecret>, + CommitmentSchemeRandomness<T>: Var<T, Secret>, + CommitmentSchemeOutput<T>: Equal<T, PublicOrSecret>, ContainmentProof<T::UtxoSet>: Var<T, Secret>, + Constant<T::CommitmentScheme, T>: CommitmentScheme< + Randomness = CommitmentSchemeRandomnessVar<T, T>, + Output = CommitmentSchemeOutputVar<T, T>, + >, + PublicKeyVar<T, T>: CommitmentInput<CommitmentSchemeVar<T, T>>, + VoidNumberGeneratorVar<T, T>: CommitmentInput<CommitmentSchemeVar<T, T>>, + AssetVar<T>: CommitmentInput<CommitmentSchemeVar<T, T>>, + VoidNumberCommitmentVar<T, T>: CommitmentInput<CommitmentSchemeVar<T, T>>, { + // TODO: find a way to do this without having to build intermediate `Vec<_>` + + let commitment_scheme = commitment_scheme.as_constant(ps); + // 1. Check that all senders are well-formed. - proof_system.assert_all(senders.iter().map(SenderVar::is_well_formed)); + /* FIXME: + for sender in &senders { + sender.verify_well_formed(ps, &commitment_scheme); + } + */ // 2. Check that all receivers are well-formed. - proof_system.assert_all(receivers.iter().map(ReceiverVar::is_well_formed)); + for receiver in &receivers { + receiver.verify_well_formed(ps, &commitment_scheme); + } // 3. Check that there is a unique asset id for all the assets. let sender_ids = senders.iter().map(SenderVar::asset_id); let receiver_ids = receivers.iter().map(ReceiverVar::asset_id); - proof_system.assert_all_eq(sender_ids.chain(receiver_ids)); + ps.assert_all_eq(sender_ids.chain(receiver_ids)); // 4. Check that the transaction is balanced. - proof_system.assert_eq( + ps.assert_eq( senders.iter().map(SenderVar::asset_value).sum(), receivers.iter().map(ReceiverVar::asset_value).sum(), ); diff --git a/manta-codec/Cargo.toml b/manta-codec/Cargo.toml index b4d895592..aaa36aba3 100644 --- a/manta-codec/Cargo.toml +++ b/manta-codec/Cargo.toml @@ -34,4 +34,4 @@ ark-serialize = { version = "0.3.0", default-features = false } ark-std = { version = "0.3.0", default-features = false } displaydoc = { version = "0.2.3", default-features = false } manta-util = { path = "../manta-util", default-features = false } -scale-codec = { package = "parity-scale-codec", version = "2.2.0", default-features = false } +scale-codec = { package = "parity-scale-codec", version = "2.3.0", default-features = false } diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index 463cd8bca..7ec66580c 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -27,6 +27,9 @@ pub type Bool<P> = <P as BooleanSystem>::Bool; /// Variable Type pub type Variable<T, P, K = (), U = K> = <T as Var<P, K, U>>::Variable; +/// Constant Type +pub type Constant<T, P> = <T as Var<P, Public, Infallible>>::Variable; + /// Character Variable Type pub type Char<P, K = (), U = K> = Variable<char, P, K, U>; @@ -60,6 +63,15 @@ pub type U64<P, K = (), U = K> = Variable<u64, P, K, U>; /// Unsigned 128-bit Integer Variable Type pub type U128<P, K = (), U = K> = Variable<u128, P, K, U>; +/// Allocation Entry +pub enum Allocation<T, Known = (), Unknown = Known> { + /// Known Value + Value(T, Known), + + /// Unknown Value + Unknown(Unknown), +} + /// Variable Reflection Trait pub trait IsVariable<P, Known = (), Unknown = Known>: Sized where @@ -96,18 +108,25 @@ where fn unknown(ps: &mut P, mode: Unknown) -> Self::Variable; } -/// Constant -pub trait Const<P>: Var<P, (), Infallible> +/// Constant Trait +pub trait Const<P>: Var<P, Public, Infallible> where P: ?Sized, { /// Returns a new constant with value `self`. #[inline] fn as_constant(&self, ps: &mut P) -> Self::Variable { - self.as_variable(ps, ()) + self.as_variable(ps, Public) } } +impl<T, P> Const<P> for T +where + T: Var<P, Public, Infallible>, + P: ?Sized, +{ +} + /// Boolean Constraint System pub trait BooleanSystem { /// Boolean Variable Type diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 8fe03f9a1..0c174004a 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -49,3 +49,4 @@ x25519-dalek = { git = "https://github.com/Manta-Network/x25519-dalek", default- [dev-dependencies] manta-crypto = { path = "../manta-crypto", features = ["test"] } +rand = "0.8.4" From 7716ed696589127f0a7de6ec4c97b7e71c8032a3 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sat, 11 Sep 2021 19:37:50 -0400 Subject: [PATCH 025/275] implement MixedChain iterator --- manta-util/src/array.rs | 76 ++++++++++ manta-util/src/concat.rs | 177 +++++++++++++++++++++++ manta-util/src/lib.rs | 226 ++--------------------------- manta-util/src/mixed_chain.rs | 262 ++++++++++++++++++++++++++++++++++ 4 files changed, 529 insertions(+), 212 deletions(-) create mode 100644 manta-util/src/array.rs create mode 100644 manta-util/src/concat.rs create mode 100644 manta-util/src/mixed_chain.rs diff --git a/manta-util/src/array.rs b/manta-util/src/array.rs new file mode 100644 index 000000000..474b3b92a --- /dev/null +++ b/manta-util/src/array.rs @@ -0,0 +1,76 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Array Utilities + +use alloc::vec::Vec; +use core::convert::TryInto; + +/// Performs the [`TryInto`] conversion into an array without checking if the conversion succeeded. +#[inline] +pub fn into_array_unchecked<T, V, const N: usize>(v: V) -> [T; N] +where + V: TryInto<[T; N]>, +{ + match v.try_into() { + Ok(array) => array, + _ => unreachable!("User was supposed to ensure that this branch is never reached."), + } +} + +/// Maps `f` over the `array`. +#[inline] +pub fn array_map<T, U, F, const N: usize>(array: [T; N], f: F) -> [U; N] +where + F: FnMut(T) -> U, +{ + into_array_unchecked(IntoIterator::into_iter(array).map(f).collect::<Vec<_>>()) +} + +/// Maps `f` over the `array` by reference. +#[inline] +pub fn array_map_ref<T, U, F, const N: usize>(array: &[T; N], f: F) -> [U; N] +where + F: FnMut(&T) -> U, +{ + into_array_unchecked(array.iter().map(f).collect::<Vec<_>>()) +} + +/// Maps `f` over the `array` returning the target array if all of the mappings succeeded, or +/// returning the first error that occurs. +#[inline] +pub fn fallible_array_map<T, U, E, F, const N: usize>(array: [T; N], f: F) -> Result<[U; N], E> +where + F: FnMut(T) -> Result<U, E>, +{ + Ok(into_array_unchecked( + IntoIterator::into_iter(array) + .map(f) + .collect::<Result<Vec<_>, _>>()?, + )) +} + +/// Maps `f` over the `array` by reference returning the target array if all of the mappings +/// succeeded, or returning the first error that occurs. +#[inline] +pub fn fallible_array_map_ref<T, U, E, F, const N: usize>(array: &[T; N], f: F) -> Result<[U; N], E> +where + F: FnMut(&T) -> Result<U, E>, +{ + Ok(into_array_unchecked( + array.iter().map(f).collect::<Result<Vec<_>, _>>()?, + )) +} diff --git a/manta-util/src/concat.rs b/manta-util/src/concat.rs new file mode 100644 index 000000000..afd093431 --- /dev/null +++ b/manta-util/src/concat.rs @@ -0,0 +1,177 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Byte Concatenation Utilities + +use alloc::vec::Vec; +use core::borrow::Borrow; + +/// Byte Accumulation Trait +pub trait ByteAccumulator { + /// Extends the current accumulator by a `buffer` of elements. + fn extend(&mut self, buffer: &[u8]); + + /// Reserves space in the accumulator for `additional` more elements. + #[inline] + fn reserve(&mut self, additional: usize) { + let _ = additional; + } + + /// Drops extra capacity in the accumulator. + #[inline] + fn shrink_to_fit(&mut self) {} + + /// Captures the accumulator and drops extra capacity before returning an owned copy. + #[inline] + fn finish(mut self) -> Self + where + Self: Sized, + { + self.shrink_to_fit(); + self + } + + /// Creates a "by mutable reference" adaptor for this instance of [`ByteAccumulator`]. + #[inline] + fn by_ref(&mut self) -> &mut Self + where + Self: Sized, + { + self + } +} + +impl<A> ByteAccumulator for &mut A +where + A: ByteAccumulator + ?Sized, +{ + #[inline] + fn extend(&mut self, buffer: &[u8]) { + (**self).extend(buffer) + } + + #[inline] + fn reserve(&mut self, additional: usize) { + (**self).reserve(additional) + } + + #[inline] + fn shrink_to_fit(&mut self) { + (**self).shrink_to_fit() + } +} + +impl ByteAccumulator for Vec<u8> { + #[inline] + fn extend(&mut self, buffer: &[u8]) { + self.extend_from_slice(buffer) + } + + #[inline] + fn reserve(&mut self, additional: usize) { + self.reserve(additional) + } + + #[inline] + fn shrink_to_fit(&mut self) { + self.shrink_to_fit() + } +} + +/// Byte Concatenation Trait +pub trait ConcatBytes { + /// Concatenates `self` on the end of the accumulator. + /// + /// # Note + /// + /// Implementations should not ask to reserve additional space for elements in this method. + /// Instead, reimplement [`reserve_concat`](Self::reserve_concat) if the default implementation + /// is not efficient. + fn concat<A>(&self, accumulator: &mut A) + where + A: ByteAccumulator + ?Sized; + + /// Returns a hint to the possible number of bytes that will be accumulated when concatenating + /// `self`. + #[inline] + fn size_hint(&self) -> Option<usize> { + None + } + + /// Concatenates `self` on the end of the accumulator after trying to reserve space for it. + #[inline] + fn reserve_concat<A>(&self, accumulator: &mut A) + where + A: ByteAccumulator + ?Sized, + { + if let Some(capacity) = self.size_hint() { + accumulator.reserve(capacity); + } + self.concat(accumulator) + } + + /// Constructs a default accumulator and accumulates over `self`, reserving the appropriate + /// capacity. + #[inline] + fn as_bytes<A>(&self) -> A + where + A: Default + ByteAccumulator, + { + let mut accumulator = A::default(); + self.reserve_concat(&mut accumulator); + accumulator.finish() + } +} + +impl<T> ConcatBytes for T +where + T: Borrow<[u8]> + ?Sized, +{ + #[inline] + fn concat<A>(&self, accumulator: &mut A) + where + A: ByteAccumulator + ?Sized, + { + accumulator.extend(self.borrow()) + } + + #[inline] + fn size_hint(&self) -> Option<usize> { + Some(self.borrow().len()) + } +} + +/// Concatenates `$item`s together by building a [`ByteAccumulator`] and running +/// [`ConcatBytes::concat`] over each `$item`. +#[macro_export] +macro_rules! concatenate { + ($($item:expr),*) => { + { + extern crate alloc; + let mut accumulator = ::alloc::vec::Vec::new(); + $($crate::ConcatBytes::reserve_concat($item, &mut accumulator);)* + $crate::ByteAccumulator::finish(accumulator) + } + } +} + +/// Returns byte vector representation of `$item`. +#[macro_export] +macro_rules! as_bytes { + ($item:expr) => { + $crate::concatenate!($item) + }; +} diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index 7070dfc82..efbfbd07d 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -23,8 +23,13 @@ extern crate alloc; -use alloc::vec::Vec; -use core::{borrow::Borrow, convert::TryInto}; +mod array; +mod concat; +mod mixed_chain; + +pub use array::*; +pub use concat::*; +pub use mixed_chain::*; /// Implements [`From`]`<$from>` for an enum `$to`, choosing the `$kind` variant. // TODO: add `where` clauses @@ -40,215 +45,12 @@ macro_rules! from_variant_impl { }; } -/// Performs the [`TryInto`] conversion into an array without checking if the conversion succeeded. -#[inline] -pub fn into_array_unchecked<T, V, const N: usize>(v: V) -> [T; N] -where - V: TryInto<[T; N]>, -{ - match v.try_into() { - Ok(array) => array, - _ => unreachable!("User was supposed to ensure that this branch is never reached."), - } -} - -/// Maps `f` over the `array`. -#[inline] -pub fn array_map<T, U, F, const N: usize>(array: [T; N], f: F) -> [U; N] -where - F: FnMut(T) -> U, -{ - into_array_unchecked(IntoIterator::into_iter(array).map(f).collect::<Vec<_>>()) -} - -/// Maps `f` over the `array` by reference. -#[inline] -pub fn array_map_ref<T, U, F, const N: usize>(array: &[T; N], f: F) -> [U; N] -where - F: FnMut(&T) -> U, -{ - into_array_unchecked(array.iter().map(f).collect::<Vec<_>>()) -} - -/// Maps `f` over the `array` returning the target array if all of the mappings succeeded, or -/// returning the first error that occurs. -#[inline] -pub fn fallible_array_map<T, U, E, F, const N: usize>(array: [T; N], f: F) -> Result<[U; N], E> -where - F: FnMut(T) -> Result<U, E>, -{ - Ok(into_array_unchecked( - IntoIterator::into_iter(array) - .map(f) - .collect::<Result<Vec<_>, _>>()?, - )) -} - -/// Maps `f` over the `array` by reference returning the target array if all of the mappings -/// succeeded, or returning the first error that occurs. -#[inline] -pub fn fallible_array_map_ref<T, U, E, F, const N: usize>(array: &[T; N], f: F) -> Result<[U; N], E> -where - F: FnMut(&T) -> Result<U, E>, -{ - Ok(into_array_unchecked( - array.iter().map(f).collect::<Result<Vec<_>, _>>()?, - )) -} - -/// Byte Accumulation Trait -pub trait ByteAccumulator { - /// Extends the current accumulator by a `buffer` of elements. - fn extend(&mut self, buffer: &[u8]); - - /// Reserves space in the accumulator for `additional` more elements. - #[inline] - fn reserve(&mut self, additional: usize) { - let _ = additional; - } - - /// Drops extra capacity in the accumulator. - #[inline] - fn shrink_to_fit(&mut self) {} - - /// Captures the accumulator and drops extra capacity before returning an owned copy. - #[inline] - fn finish(mut self) -> Self - where - Self: Sized, - { - self.shrink_to_fit(); - self - } - - /// Creates a "by mutable reference" adaptor for this instance of [`ByteAccumulator`]. - #[inline] - fn by_ref(&mut self) -> &mut Self - where - Self: Sized, - { - self - } -} - -impl<A> ByteAccumulator for &mut A -where - A: ByteAccumulator + ?Sized, -{ - #[inline] - fn extend(&mut self, buffer: &[u8]) { - (**self).extend(buffer) - } - - #[inline] - fn reserve(&mut self, additional: usize) { - (**self).reserve(additional) - } - - #[inline] - fn shrink_to_fit(&mut self) { - (**self).shrink_to_fit() - } -} - -impl ByteAccumulator for Vec<u8> { - #[inline] - fn extend(&mut self, buffer: &[u8]) { - self.extend_from_slice(buffer) - } - - #[inline] - fn reserve(&mut self, additional: usize) { - self.reserve(additional) - } - - #[inline] - fn shrink_to_fit(&mut self) { - self.shrink_to_fit() - } -} - -/// Byte Concatenation Trait -pub trait ConcatBytes { - /// Concatenates `self` on the end of the accumulator. - /// - /// # Note - /// - /// Implementations should not ask to reserve additional space for elements in this method. - /// Instead, reimplement [`reserve_concat`](Self::reserve_concat) if the default implementation - /// is not efficient. - fn concat<A>(&self, accumulator: &mut A) - where - A: ByteAccumulator + ?Sized; - - /// Returns a hint to the possible number of bytes that will be accumulated when concatenating - /// `self`. - #[inline] - fn size_hint(&self) -> Option<usize> { - None - } +/// Either Type +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum Either<L, R> { + /// Left Variant + Left(L), - /// Concatenates `self` on the end of the accumulator after trying to reserve space for it. - #[inline] - fn reserve_concat<A>(&self, accumulator: &mut A) - where - A: ByteAccumulator + ?Sized, - { - if let Some(capacity) = self.size_hint() { - accumulator.reserve(capacity); - } - self.concat(accumulator) - } - - /// Constructs a default accumulator and accumulates over `self`, reserving the appropriate - /// capacity. - #[inline] - fn as_bytes<A>(&self) -> A - where - A: Default + ByteAccumulator, - { - let mut accumulator = A::default(); - self.reserve_concat(&mut accumulator); - accumulator.finish() - } -} - -impl<T> ConcatBytes for T -where - T: Borrow<[u8]> + ?Sized, -{ - #[inline] - fn concat<A>(&self, accumulator: &mut A) - where - A: ByteAccumulator + ?Sized, - { - accumulator.extend(self.borrow()) - } - - #[inline] - fn size_hint(&self) -> Option<usize> { - Some(self.borrow().len()) - } -} - -/// Concatenates `$item`s together by building a [`ByteAccumulator`] and running -/// [`ConcatBytes::concat`] over each `$item`. -#[macro_export] -macro_rules! concatenate { - ($($item:expr),*) => { - { - extern crate alloc; - let mut accumulator = ::alloc::vec::Vec::new(); - $($crate::ConcatBytes::reserve_concat($item, &mut accumulator);)* - $crate::ByteAccumulator::finish(accumulator) - } - } -} - -/// Returns byte vector representation of `$item`. -#[macro_export] -macro_rules! as_bytes { - ($item:expr) => { - $crate::concatenate!($item) - }; + /// Right Variant + Right(R), } diff --git a/manta-util/src/mixed_chain.rs b/manta-util/src/mixed_chain.rs new file mode 100644 index 000000000..ace57a273 --- /dev/null +++ b/manta-util/src/mixed_chain.rs @@ -0,0 +1,262 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Mixed Chain Iterator + +// TODO: Make extract its own public trait to see if we can get some more combinators out of this. +// FIXME: Implement `Debug` for `MixedChain`. + +use crate::Either; +use core::iter::FusedIterator; + +trait Extract<I> +where + I: Iterator, +{ + fn extract(iter: &mut I) -> Option<I::Item>; +} + +struct Next; + +impl<I> Extract<I> for Next +where + I: Iterator, +{ + #[inline] + fn extract(iter: &mut I) -> Option<I::Item> { + iter.next() + } +} + +struct NextBack; + +impl<I> Extract<I> for NextBack +where + I: DoubleEndedIterator, +{ + #[inline] + fn extract(iter: &mut I) -> Option<I::Item> { + iter.next_back() + } +} + +/// An iterator that links two iterators together, in a chain, mapping each iterator so that they +/// have a common type when chained. +#[derive(Clone)] +#[must_use = "iterators are lazy and do nothing unless consumed"] +pub struct MixedChain<A, B, F> { + // NOTE: See the standard library implementation of `Chain` for an explanation on the + // fusing technique used here. + a: Option<A>, + b: Option<B>, + f: F, +} + +impl<A, B, F> MixedChain<A, B, F> { + /// Builds a new [`MixedChain`] iterator. + #[inline] + fn new(a: A, b: B, f: F) -> Self { + Self { + a: Some(a), + b: Some(b), + f, + } + } + + #[inline] + fn extract<T, E>(&mut self) -> Option<T> + where + A: Iterator, + B: Iterator, + F: FnMut(Either<A::Item, B::Item>) -> T, + E: Extract<A> + Extract<B>, + { + let maybe_item = match self.a { + Some(ref mut iter) => match left_map(E::extract(iter), &mut self.f) { + None => { + self.a = None; + None + } + item => item, + }, + _ => None, + }; + match maybe_item { + None => match self.b { + Some(ref mut iter) => right_map(E::extract(iter), &mut self.f), + _ => None, + }, + item => item, + } + } +} + +#[inline] +fn left<F, L, R, T>(item: L, mut f: F) -> T +where + F: FnMut(Either<L, R>) -> T, +{ + f(Either::Left(item)) +} + +#[inline] +fn left_map<F, L, R, T>(item: Option<L>, f: &mut F) -> Option<T> +where + F: FnMut(Either<L, R>) -> T, +{ + item.map(|i| left(i, f)) +} + +#[inline] +fn right<F, L, R, T>(item: R, mut f: F) -> T +where + F: FnMut(Either<L, R>) -> T, +{ + f(Either::Right(item)) +} + +#[inline] +fn right_map<F, L, R, T>(item: Option<R>, f: &mut F) -> Option<T> +where + F: FnMut(Either<L, R>) -> T, +{ + item.map(|i| right(i, f)) +} + +impl<A, B, F, T> Iterator for MixedChain<A, B, F> +where + A: Iterator, + B: Iterator, + F: FnMut(Either<A::Item, B::Item>) -> T, +{ + type Item = T; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + self.extract::<_, Next>() + } + + #[inline] + // TODO: #[rustc_inherit_overflow_checks] + fn count(self) -> usize { + let Self { a, b, mut f } = self; + let a_count = match a { + Some(a) => a.map(|a| left(a, &mut f)).count(), + None => 0, + }; + let b_count = match b { + Some(b) => b.map(|b| right(b, &mut f)).count(), + None => 0, + }; + a_count + b_count + } + + #[inline] + fn fold<Acc, FoldF>(self, mut acc: Acc, mut fold_f: FoldF) -> Acc + where + FoldF: FnMut(Acc, Self::Item) -> Acc, + { + let Self { a, b, mut f } = self; + if let Some(a) = a { + acc = a.fold(acc, |accum, a| fold_f(accum, left(a, &mut f))); + } + if let Some(b) = b { + acc = b.fold(acc, |accum, b| fold_f(accum, right(b, &mut f))); + } + acc + } + + #[inline] + fn last(self) -> Option<Self::Item> { + // NOTE: Must exhaust a before b. + let Self { a, b, mut f } = self; + let a_last = match a { + Some(a) => a.map(|a| left(a, &mut f)).last(), + None => None, + }; + let b_last = match b { + Some(b) => b.map(|b| right(b, &mut f)).last(), + None => None, + }; + b_last.or(a_last) + } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + match (&self.a, &self.b) { + (Some(a), Some(b)) => { + let (a_lower, a_upper) = a.size_hint(); + let (b_lower, b_upper) = b.size_hint(); + let lower = a_lower.saturating_add(b_lower); + let upper = match (a_upper, b_upper) { + (Some(x), Some(y)) => x.checked_add(y), + _ => None, + }; + (lower, upper) + } + (Some(a), _) => a.size_hint(), + (_, Some(b)) => b.size_hint(), + _ => (0, Some(0)), + } + } +} + +impl<A, B, F, T> DoubleEndedIterator for MixedChain<A, B, F> +where + A: DoubleEndedIterator, + B: DoubleEndedIterator, + F: FnMut(Either<A::Item, B::Item>) -> T, +{ + #[inline] + fn next_back(&mut self) -> Option<Self::Item> { + self.extract::<_, NextBack>() + } + + #[inline] + fn rfold<Acc, FoldF>(self, mut acc: Acc, mut fold_f: FoldF) -> Acc + where + FoldF: FnMut(Acc, Self::Item) -> Acc, + { + let Self { a, b, mut f } = self; + if let Some(b) = b { + acc = b.rfold(acc, |accum, b| fold_f(accum, right(b, &mut f))); + } + if let Some(a) = a { + acc = a.rfold(acc, |accum, a| fold_f(accum, left(a, &mut f))); + } + acc + } +} + +// NOTE: *Both* must be fused to handle double-ended iterators. +impl<A, B, F, T> FusedIterator for MixedChain<A, B, F> +where + A: FusedIterator, + B: FusedIterator, + F: FnMut(Either<A::Item, B::Item>) -> T, +{ +} + +/// Creates a mixed chain iterator. +#[inline] +pub fn mixed_chain<A, B, F, T>(a: A, b: B, f: F) -> MixedChain<A::IntoIter, B::IntoIter, F> +where + A: IntoIterator, + B: IntoIterator, + F: FnMut(Either<A::Item, B::Item>) -> T, +{ + MixedChain::new(a.into_iter(), b.into_iter(), f) +} From 4ff6b470b8cd7953f08567b3980931b6050db8e9 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sat, 11 Sep 2021 19:38:41 -0400 Subject: [PATCH 026/275] WIP: improve type-level constraint management --- manta-accounting/src/identity.rs | 430 +++++++++++++++++-------------- manta-accounting/src/transfer.rs | 139 +++++----- manta-accounting/src/wallet.rs | 4 +- manta-crypto/src/constraint.rs | 24 +- 4 files changed, 317 insertions(+), 280 deletions(-) diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 87753287b..3647bd408 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -20,7 +20,6 @@ // FIXME: Should the identity types have methods that expose their members? Or should it be // completely opaque, and let the internal APIs handle all the logic? // TODO: Since `IdentityConfiguration::SecretKey: Clone`, should `Identity: Clone`? -// TODO: Should we rename all uses of `C::SecretKey` to `SecretKey<C>` to be consistent? use crate::{ asset::{Asset, AssetBalance, AssetBalanceVar, AssetId, AssetIdVar, AssetVar}, @@ -31,8 +30,8 @@ use core::{convert::Infallible, fmt::Debug, hash::Hash, marker::PhantomData}; use manta_crypto::{ commitment::{CommitmentScheme, Input as CommitmentInput}, constraint::{ - BooleanSystem, Constant, Derived, Equal, IsVariable, Public, PublicOrSecret, Secret, Var, - Variable, + BooleanSystem, Derived, Equal, IsVariable, ProofSystem, Public, PublicOrSecret, Secret, + Var, Variable, }, ies::{self, EncryptedMessage, IntegratedEncryptionScheme}, set::{ContainmentProof, VerifiedSet}, @@ -66,31 +65,76 @@ pub trait IdentityConfiguration { type Rng: CryptoRng + RngCore + SeedableRng<Seed = Self::SecretKey>; } -/* TODO: /// [`Identity`] Proof System Configuration -pub trait IdentityProofSystemConfiguration<P>: IdentityConfiguration +pub trait IdentityProofSystemConfiguration { + /// Proof System + type ProofSystem: ProofSystem; + + /// Pseudorandom Function Family Seed + type PseudorandomFunctionFamilySeed: Clone + Var<Self::ProofSystem, Secret>; + + /// Pseudorandom Function Family Input + type PseudorandomFunctionFamilyInput: Var<Self::ProofSystem, Secret>; + + /// Pseudorandom Function Family Output + type PseudorandomFunctionFamilyOutput: Equal<Self::ProofSystem, PublicOrSecret>; + + /// Pseudorandom Function Family + type PseudorandomFunctionFamily: PseudorandomFunctionFamily< + Seed = Self::PseudorandomFunctionFamilySeed, + Input = Self::PseudorandomFunctionFamilyInput, + Output = Self::PseudorandomFunctionFamilyOutput, + > + Var<Self::ProofSystem, Public, Infallible, Variable = Self::PseudorandomFunctionFamilyVar>; + + /// Pseudorandom Function Family Variable + type PseudorandomFunctionFamilyVar: PseudorandomFunctionFamily< + Seed = Variable<Self::PseudorandomFunctionFamilySeed, Self::ProofSystem, Secret>, + Input = Variable<Self::PseudorandomFunctionFamilyInput, Self::ProofSystem, Secret>, + Output = Variable< + Self::PseudorandomFunctionFamilyOutput, + Self::ProofSystem, + PublicOrSecret, + >, + > + IsVariable<Self::ProofSystem, Public, Infallible, Type = Self::PseudorandomFunctionFamily>; + + /// Commitment Scheme Randomness + type CommitmentSchemeRandomness: Var<Self::ProofSystem, Secret>; + + /// Commitment Scheme Output + type CommitmentSchemeOutput: Equal<Self::ProofSystem, PublicOrSecret>; + + /// Commitment Scheme + type CommitmentScheme: CommitmentScheme< + Randomness = Self::CommitmentSchemeRandomness, + Output = Self::CommitmentSchemeOutput, + > + Var<Self::ProofSystem, Public, Infallible, Variable = Self::CommitmentSchemeVar>; + + /// Commitment Scheme Variable + type CommitmentSchemeVar: CommitmentScheme< + Randomness = Variable<Self::CommitmentSchemeRandomness, Self::ProofSystem, Secret>, + Output = Variable<Self::CommitmentSchemeOutput, Self::ProofSystem, PublicOrSecret>, + > + IsVariable<Self::ProofSystem, Public, Infallible, Type = Self::CommitmentScheme>; + + /// Seedable Cryptographic Random Number Generator + type Rng: CryptoRng + RngCore + SeedableRng<Seed = Self::PseudorandomFunctionFamilySeed>; +} + +impl<C> IdentityConfiguration for C where - P: BooleanSystem + ?Sized, - SecretKey<Self>: Var<P, Secret>, - PseudorandomFunctionFamilyInput<Self>: Var<P, Secret>, - PseudorandomFunctionFamilyOutput<Self>: Equal<P, PublicOrSecret>, - CommitmentSchemeRandomness<Self>: Var<P, Secret>, - CommitmentSchemeOutput<Self>: Equal<P, PublicOrSecret>, + C: IdentityProofSystemConfiguration + ?Sized, { - /// Pseudorandom Function Family Variable Type - type PseudorandomFunctionFamilyVar: PseudorandomFunctionFamily< - Seed = SecretKeyVar<Self, P>, - Input = PseudorandomFunctionFamilyInputVar<Self, P>, - Output = PseudorandomFunctionFamilyOutputVar<Self, P>, - >; + type SecretKey = C::PseudorandomFunctionFamilySeed; - /// Commitment Scheme Variable Type - type CommitmentSchemeVar: CommitmentScheme< - Randomness = CommitmentSchemeRandomnessVar<Self, P>, - Output = CommitmentSchemeOutputVar<Self, P>, - >; + type PseudorandomFunctionFamily = C::PseudorandomFunctionFamily; + + type CommitmentScheme = C::CommitmentScheme; + + type Rng = C::Rng; } -*/ + +/// [`PseudorandomFunctionFamily::Seed`] Type +pub type PseudorandomFunctionFamilySeed<C> = + <<C as IdentityConfiguration>::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Seed; /// [`PseudorandomFunctionFamily::Input`] Type pub type PseudorandomFunctionFamilyInput<C> = @@ -132,49 +176,68 @@ pub type UtxoRandomness<C> = CommitmentSchemeRandomness<C>; /// UTXO Type pub type Utxo<C> = CommitmentSchemeOutput<C>; +/// [`PseudorandomFunctionFamily::Seed`] Variable Type +pub type PseudorandomFunctionFamilySeedVar<C> = Variable< + PseudorandomFunctionFamilySeed<C>, + <C as IdentityProofSystemConfiguration>::ProofSystem, + Secret, +>; + /// [`PseudorandomFunctionFamily::Input`] Variable Type -pub type PseudorandomFunctionFamilyInputVar<C, P> = - Variable<PseudorandomFunctionFamilyInput<C>, P, Secret>; +pub type PseudorandomFunctionFamilyInputVar<C> = Variable< + PseudorandomFunctionFamilyInput<C>, + <C as IdentityProofSystemConfiguration>::ProofSystem, + Secret, +>; /// [`PseudorandomFunctionFamily::Output`] Variable Type -pub type PseudorandomFunctionFamilyOutputVar<C, P> = - Variable<PseudorandomFunctionFamilyOutput<C>, P, PublicOrSecret>; - -/// [`CommitmentScheme`] Variable Type -pub type CommitmentSchemeVar<C, P> = Constant<<C as IdentityConfiguration>::CommitmentScheme, P>; +pub type PseudorandomFunctionFamilyOutputVar<C> = Variable< + PseudorandomFunctionFamilyOutput<C>, + <C as IdentityProofSystemConfiguration>::ProofSystem, + PublicOrSecret, +>; /// [`CommitmentScheme::Randomness`] Variable Type -pub type CommitmentSchemeRandomnessVar<C, P> = Variable<CommitmentSchemeRandomness<C>, P, Secret>; +pub type CommitmentSchemeRandomnessVar<C> = Variable< + CommitmentSchemeRandomness<C>, + <C as IdentityProofSystemConfiguration>::ProofSystem, + Secret, +>; /// [`CommitmentScheme::Output`] Variable Type -pub type CommitmentSchemeOutputVar<C, P> = Variable<CommitmentSchemeOutput<C>, P, PublicOrSecret>; +pub type CommitmentSchemeOutputVar<C> = Variable< + CommitmentSchemeOutput<C>, + <C as IdentityProofSystemConfiguration>::ProofSystem, + PublicOrSecret, +>; /// Secret Key Variable Type -pub type SecretKeyVar<C, P> = Variable<SecretKey<C>, P, Secret>; +pub type SecretKeyVar<C> = PseudorandomFunctionFamilySeedVar<C>; /// Public Key Variable Type -pub type PublicKeyVar<C, P> = PseudorandomFunctionFamilyOutputVar<C, P>; +pub type PublicKeyVar<C> = PseudorandomFunctionFamilyOutputVar<C>; /// Void Number Generator Variable Type -pub type VoidNumberGeneratorVar<C, P> = PseudorandomFunctionFamilyInputVar<C, P>; +pub type VoidNumberGeneratorVar<C> = PseudorandomFunctionFamilyInputVar<C>; /// Void Number Variable Type -pub type VoidNumberVar<C, P> = PseudorandomFunctionFamilyOutputVar<C, P>; +pub type VoidNumberVar<C> = PseudorandomFunctionFamilyOutputVar<C>; /// Void Number Commitment Randomness Variable Type -pub type VoidNumberCommitmentRandomnessVar<C, P> = CommitmentSchemeRandomnessVar<C, P>; +pub type VoidNumberCommitmentRandomnessVar<C> = CommitmentSchemeRandomnessVar<C>; /// Void Number Commitment Variable Type -pub type VoidNumberCommitmentVar<C, P> = CommitmentSchemeOutputVar<C, P>; +pub type VoidNumberCommitmentVar<C> = CommitmentSchemeOutputVar<C>; /// UTXO Randomness Variable Type -pub type UtxoRandomnessVar<C, P> = CommitmentSchemeRandomnessVar<C, P>; +pub type UtxoRandomnessVar<C> = CommitmentSchemeRandomnessVar<C>; /// UTXO Variable Type -pub type UtxoVar<C, P> = CommitmentSchemeOutputVar<C, P>; +pub type UtxoVar<C> = CommitmentSchemeOutputVar<C>; /// UTXO Containment Proof Variable Type -pub type UtxoContainmentProofVar<S, P> = Variable<ContainmentProof<S>, P, Secret>; +pub type UtxoContainmentProofVar<C, S> = + Variable<ContainmentProof<S>, <C as IdentityProofSystemConfiguration>::ProofSystem, Secret>; /// Generates a void number commitment from `public_key`, `void_number_generator`, and /// `void_number_commitment_randomness`. @@ -328,44 +391,35 @@ where } /// Asset Parameters Variable -pub struct AssetParametersVar<C, P> +pub struct AssetParametersVar<C> where - C: IdentityConfiguration, - VoidNumberGenerator<C>: Var<P, Secret>, - VoidNumberCommitmentRandomness<C>: Var<P, Secret>, - UtxoRandomness<C>: Var<P, Secret>, + C: IdentityProofSystemConfiguration, { /// Void Number Generator - pub void_number_generator: VoidNumberGeneratorVar<C, P>, + pub void_number_generator: VoidNumberGeneratorVar<C>, /// Void Number Commitment Randomness - pub void_number_commitment_randomness: VoidNumberCommitmentRandomnessVar<C, P>, + pub void_number_commitment_randomness: VoidNumberCommitmentRandomnessVar<C>, /// UTXO Randomness - pub utxo_randomness: UtxoRandomnessVar<C, P>, + pub utxo_randomness: UtxoRandomnessVar<C>, } -impl<C, P> IsVariable<P, Secret> for AssetParametersVar<C, P> +impl<C> IsVariable<C::ProofSystem, Secret> for AssetParametersVar<C> where - C: IdentityConfiguration, - VoidNumberGenerator<C>: Var<P, Secret>, - VoidNumberCommitmentRandomness<C>: Var<P, Secret>, - UtxoRandomness<C>: Var<P, Secret>, + C: IdentityProofSystemConfiguration, { type Type = AssetParameters<C>; } -impl<C, P> Var<P, Secret> for AssetParameters<C> +impl<C> Var<C::ProofSystem, Secret> for AssetParameters<C> where - C: IdentityConfiguration, - VoidNumberGenerator<C>: Var<P, Secret>, - VoidNumberCommitmentRandomness<C>: Var<P, Secret>, - UtxoRandomness<C>: Var<P, Secret>, + C: IdentityProofSystemConfiguration, { - type Variable = AssetParametersVar<C, P>; + type Variable = AssetParametersVar<C>; #[inline] - fn as_variable(&self, ps: &mut P, mode: Secret) -> Self::Variable { + fn as_variable(&self, ps: &mut C::ProofSystem, mode: Secret) -> Self::Variable { Self::Variable { void_number_generator: self.void_number_generator.as_variable(ps, mode), void_number_commitment_randomness: self @@ -376,7 +430,7 @@ where } #[inline] - fn unknown(ps: &mut P, mode: Secret) -> Self::Variable { + fn unknown(ps: &mut C::ProofSystem, mode: Secret) -> Self::Variable { Self::Variable { void_number_generator: VoidNumberGenerator::<C>::unknown(ps, mode), void_number_commitment_randomness: VoidNumberCommitmentRandomness::<C>::unknown( @@ -393,16 +447,16 @@ where C: IdentityConfiguration, { /// Secret Key - secret_key: C::SecretKey, + secret_key: SecretKey<C>, } impl<C> Identity<C> where C: IdentityConfiguration, { - /// Generates a new [`Identity`] from a [`C::SecretKey`](IdentityConfiguration::SecretKey). + /// Generates a new [`Identity`] from a [`SecretKey`]. #[inline] - pub fn new(secret_key: C::SecretKey) -> Self { + pub fn new(secret_key: SecretKey<C>) -> Self { Self { secret_key } } @@ -410,7 +464,7 @@ where #[inline] pub fn generate<G>(source: &mut G) -> Result<Self, G::Error> where - G: SecretKeyGenerator<SecretKey = C::SecretKey>, + G: SecretKeyGenerator<SecretKey = SecretKey<C>>, { source.generate_key().map(Self::new) } @@ -597,7 +651,7 @@ where utxo_set: &S, ) -> Result<Sender<C, S>, SenderError<C, G, S>> where - G: SecretKeyGenerator<SecretKey = C::SecretKey>, + G: SecretKeyGenerator<SecretKey = SecretKey<C>>, S: VerifiedSet<Item = Utxo<C>>, Standard: Distribution<AssetParameters<C>>, PublicKey<C>: CommitmentInput<C::CommitmentScheme>, @@ -654,7 +708,7 @@ where commitment_scheme: &C::CommitmentScheme, ) -> Result<ShieldedIdentity<C, I>, G::Error> where - G: SecretKeyGenerator<SecretKey = C::SecretKey>, + G: SecretKeyGenerator<SecretKey = SecretKey<C>>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, PublicKey<C>: CommitmentInput<C::CommitmentScheme>, @@ -681,7 +735,7 @@ where #[inline] fn generate_spend<G, I>(source: &mut G) -> Result<Spend<C, I>, G::Error> where - G: SecretKeyGenerator<SecretKey = C::SecretKey>, + G: SecretKeyGenerator<SecretKey = SecretKey<C>>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { @@ -717,7 +771,7 @@ where commitment_scheme: &C::CommitmentScheme, ) -> Result<(ShieldedIdentity<C, I>, Spend<C, I>), G::Error> where - G: SecretKeyGenerator<SecretKey = C::SecretKey>, + G: SecretKeyGenerator<SecretKey = SecretKey<C>>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, PublicKey<C>: CommitmentInput<C::CommitmentScheme>, @@ -743,7 +797,7 @@ where impl<C> Distribution<Identity<C>> for Standard where C: IdentityConfiguration, - Standard: Distribution<C::SecretKey>, + Standard: Distribution<SecretKey<C>>, { #[inline] fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> Identity<C> { @@ -792,7 +846,7 @@ where commitment_scheme: &C::CommitmentScheme, ) -> Result<Self, G::Error> where - G: SecretKeyGenerator<SecretKey = C::SecretKey>, + G: SecretKeyGenerator<SecretKey = SecretKey<C>>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, PublicKey<C>: CommitmentInput<C::CommitmentScheme>, @@ -942,7 +996,7 @@ where #[inline] pub fn generate<G>(source: &mut G) -> Result<Self, G::Error> where - G: SecretKeyGenerator<SecretKey = C::SecretKey>, + G: SecretKeyGenerator<SecretKey = SecretKey<C>>, Standard: Distribution<AssetParameters<C>>, { Identity::generate_spend(source) @@ -1054,7 +1108,7 @@ mod sender_error { pub enum SenderError<C, G, S> where C: IdentityConfiguration, - G: SecretKeyGenerator<SecretKey = C::SecretKey>, + G: SecretKeyGenerator<SecretKey = SecretKey<C>>, S: VerifiedSet<Item = Utxo<C>>, { /// Secret Key Generator Error @@ -1136,7 +1190,7 @@ where utxo_set: &S, ) -> Result<Self, SenderError<C, G, S>> where - G: SecretKeyGenerator<SecretKey = C::SecretKey>, + G: SecretKeyGenerator<SecretKey = SecretKey<C>>, Standard: Distribution<AssetParameters<C>>, PublicKey<C>: CommitmentInput<C::CommitmentScheme>, VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, @@ -1205,89 +1259,71 @@ where } /// Sender Variable -pub struct SenderVar<C, S, P> +pub struct SenderVar<C, S> where - C: IdentityConfiguration, + C: IdentityProofSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - P: BooleanSystem, - AssetId: Var<P, PublicOrSecret>, - AssetBalance: Var<P, PublicOrSecret>, - SecretKey<C>: Var<P, Secret>, - PseudorandomFunctionFamilyInput<C>: Var<P, Secret>, - PseudorandomFunctionFamilyOutput<C>: Equal<P, PublicOrSecret>, - CommitmentSchemeRandomness<C>: Var<P, Secret>, - CommitmentSchemeOutput<C>: Equal<P, PublicOrSecret>, - ContainmentProof<S>: Var<P, Secret>, + AssetId: Var<C::ProofSystem, PublicOrSecret>, + AssetBalance: Var<C::ProofSystem, PublicOrSecret>, + ContainmentProof<S>: Var<C::ProofSystem, Secret>, { /// Secret Key - secret_key: SecretKeyVar<C, P>, + secret_key: SecretKeyVar<C>, /// Public Key - public_key: PublicKeyVar<C, P>, + public_key: PublicKeyVar<C>, /// Asset - asset: AssetVar<P>, + asset: AssetVar<C::ProofSystem>, /// Asset Parameters - parameters: AssetParametersVar<C, P>, + parameters: AssetParametersVar<C>, /// Void Number - void_number: VoidNumberVar<C, P>, + void_number: VoidNumberVar<C>, /// Void Number Commitment - void_number_commitment: VoidNumberCommitmentVar<C, P>, + void_number_commitment: VoidNumberCommitmentVar<C>, /// Unspent Transaction Output - utxo: UtxoVar<C, P>, + utxo: UtxoVar<C>, /// UTXO Containment Proof // TODO: think about what the mode type for this should be - utxo_containment_proof: UtxoContainmentProofVar<S, P>, + utxo_containment_proof: UtxoContainmentProofVar<C, S>, } -impl<C, S, P> SenderVar<C, S, P> +impl<C, S> SenderVar<C, S> where - C: IdentityConfiguration, + C: IdentityProofSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - P: BooleanSystem, - AssetId: Var<P, PublicOrSecret>, - AssetBalance: Var<P, PublicOrSecret>, - SecretKey<C>: Var<P, Secret>, - PseudorandomFunctionFamilyInput<C>: Var<P, Secret>, - PseudorandomFunctionFamilyOutput<C>: Equal<P, PublicOrSecret>, - CommitmentSchemeRandomness<C>: Var<P, Secret>, - CommitmentSchemeOutput<C>: Equal<P, PublicOrSecret>, - ContainmentProof<S>: Var<P, Secret>, + AssetId: Var<C::ProofSystem, PublicOrSecret>, + AssetBalance: Var<C::ProofSystem, PublicOrSecret>, + ContainmentProof<S>: Var<C::ProofSystem, Secret>, { /// Returns the asset id of this sender. #[inline] - pub fn asset_id(&self) -> &AssetIdVar<P> { + pub fn asset_id(&self) -> &AssetIdVar<C::ProofSystem> { &self.asset.id } /// Returns the asset value of this sender. #[inline] - pub fn asset_value(&self) -> &AssetBalanceVar<P> { + pub fn asset_value(&self) -> &AssetBalanceVar<C::ProofSystem> { &self.asset.value } /// Checks if `self` is a well-formed sender. #[inline] - pub fn verify_well_formed<PRFVar, CSVar>(&self, ps: &mut P, commitment_scheme: &CSVar) - where - PRFVar: PseudorandomFunctionFamily< - Seed = SecretKeyVar<C, P>, - Input = PseudorandomFunctionFamilyInputVar<C, P>, - Output = PseudorandomFunctionFamilyOutputVar<C, P>, - >, - CSVar: CommitmentScheme< - Randomness = CommitmentSchemeRandomnessVar<C, P>, - Output = CommitmentSchemeOutputVar<C, P>, - >, - PublicKeyVar<C, P>: CommitmentInput<CSVar>, - VoidNumberGeneratorVar<C, P>: CommitmentInput<CSVar>, - AssetVar<P>: CommitmentInput<CSVar>, - VoidNumberCommitmentVar<C, P>: CommitmentInput<CSVar>, + pub fn verify_well_formed( + &self, + ps: &mut C::ProofSystem, + commitment_scheme: &C::CommitmentSchemeVar, + ) where + PublicKeyVar<C>: CommitmentInput<C::CommitmentSchemeVar>, + VoidNumberGeneratorVar<C>: CommitmentInput<C::CommitmentSchemeVar>, + AssetVar<C::ProofSystem>: CommitmentInput<C::CommitmentSchemeVar>, + VoidNumberCommitmentVar<C>: CommitmentInput<C::CommitmentSchemeVar>, { // FIXME: Implement well-formedness check: // @@ -1299,13 +1335,34 @@ where // // FIXME: should `k` be private or not? - ps.assert_eq(&self.public_key, &PRFVar::evaluate_zero(&self.secret_key)); + // 1. Check public key: + // ``` + // pk = PRF(sk, 0) + // ``` + // where public: {}, secret: {pk, sk}. + ps.assert_eq( + &self.public_key, + &C::PseudorandomFunctionFamilyVar::evaluate_zero(&self.secret_key), + ); + // 2. Check void number: + // ``` + // vn = PRF(sk, rho) + // ``` + // where public: {vn}, secret: {sk, rho}. ps.assert_eq( &self.void_number, - &PRFVar::evaluate(&self.secret_key, &self.parameters.void_number_generator), + &C::PseudorandomFunctionFamilyVar::evaluate( + &self.secret_key, + &self.parameters.void_number_generator, + ), ); + // 3. Check void number commitment: + // ``` + // k = COM(pk || rho, r) + // ``` + // where public: {k}, secret: {pk, rho, r}. ps.assert_eq( &self.void_number_commitment, &generate_void_number_commitment( @@ -1316,6 +1373,11 @@ where ), ); + // 4. Check UTXO: + // ``` + // cm = COM(asset || k, s) + // ``` + // where public: {}, secret: {cm, asset, k, s}. ps.assert_eq( &self.utxo, &generate_utxo( @@ -1326,45 +1388,38 @@ where ), ); + // 5. Check UTXO containment proof: + // ``` + // is_path(cm, path, root) == true + // ``` + // where public: {root}, secret: {cm, path}. // FIXME: ps.assert(&self.utxo_containment_proof.verify(self.utxo)); } } -impl<C, S, P> IsVariable<P, Derived> for SenderVar<C, S, P> +impl<C, S> IsVariable<C::ProofSystem, Derived> for SenderVar<C, S> where - C: IdentityConfiguration, + C: IdentityProofSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - P: BooleanSystem, - AssetId: Var<P, PublicOrSecret>, - AssetBalance: Var<P, PublicOrSecret>, - SecretKey<C>: Var<P, Secret>, - PseudorandomFunctionFamilyInput<C>: Var<P, Secret>, - PseudorandomFunctionFamilyOutput<C>: Equal<P, PublicOrSecret>, - CommitmentSchemeRandomness<C>: Var<P, Secret>, - CommitmentSchemeOutput<C>: Equal<P, PublicOrSecret>, - ContainmentProof<S>: Var<P, Secret>, + AssetId: Var<C::ProofSystem, PublicOrSecret>, + AssetBalance: Var<C::ProofSystem, PublicOrSecret>, + ContainmentProof<S>: Var<C::ProofSystem, Secret>, { type Type = Sender<C, S>; } -impl<C, S, P> Var<P, Derived> for Sender<C, S> +impl<C, S> Var<C::ProofSystem, Derived> for Sender<C, S> where - C: IdentityConfiguration, + C: IdentityProofSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - P: BooleanSystem, - AssetId: Var<P, PublicOrSecret>, - AssetBalance: Var<P, PublicOrSecret>, - SecretKey<C>: Var<P, Secret>, - PseudorandomFunctionFamilyInput<C>: Var<P, Secret>, - PseudorandomFunctionFamilyOutput<C>: Equal<P, PublicOrSecret>, - CommitmentSchemeRandomness<C>: Var<P, Secret>, - CommitmentSchemeOutput<C>: Equal<P, PublicOrSecret>, - ContainmentProof<S>: Var<P, Secret>, + AssetId: Var<C::ProofSystem, PublicOrSecret>, + AssetBalance: Var<C::ProofSystem, PublicOrSecret>, + ContainmentProof<S>: Var<C::ProofSystem, Secret>, { - type Variable = SenderVar<C, S, P>; + type Variable = SenderVar<C, S>; #[inline] - fn as_variable(&self, ps: &mut P, mode: Derived) -> Self::Variable { + fn as_variable(&self, ps: &mut C::ProofSystem, mode: Derived) -> Self::Variable { // FIXME: Have one function where we do variable allocation instead of two. Self::Variable { secret_key: self.secret_key.as_variable(ps, mode.into()), @@ -1379,7 +1434,7 @@ where } #[inline] - fn unknown(ps: &mut P, mode: Derived) -> Self::Variable { + fn unknown(ps: &mut C::ProofSystem, mode: Derived) -> Self::Variable { // FIXME: Have one function where we do variable allocation instead of two. Self::Variable { secret_key: SecretKey::<C>::unknown(ps, mode.into()), @@ -1552,51 +1607,45 @@ where } /// Receiver Variable -pub struct ReceiverVar<C, I, P> +pub struct ReceiverVar<C, I> where - C: IdentityConfiguration, + C: IdentityProofSystemConfiguration, I: IntegratedEncryptionScheme<Plaintext = Asset>, - P: BooleanSystem, - AssetId: Var<P, PublicOrSecret>, - AssetBalance: Var<P, PublicOrSecret>, - CommitmentSchemeRandomness<C>: Var<P, Secret>, - CommitmentSchemeOutput<C>: Equal<P, PublicOrSecret>, + AssetId: Var<C::ProofSystem, PublicOrSecret>, + AssetBalance: Var<C::ProofSystem, PublicOrSecret>, { /// Asset - asset: AssetVar<P>, + asset: AssetVar<C::ProofSystem>, /// UTXO Randomness - utxo_randomness: UtxoRandomnessVar<C, P>, + utxo_randomness: UtxoRandomnessVar<C>, /// Void Number Commitment - void_number_commitment: VoidNumberCommitmentVar<C, P>, + void_number_commitment: VoidNumberCommitmentVar<C>, /// Unspent Transaction Output - utxo: UtxoVar<C, P>, + utxo: UtxoVar<C>, /// Type Parameter Marker __: PhantomData<I>, } -impl<C, I, P> ReceiverVar<C, I, P> +impl<C, I> ReceiverVar<C, I> where - C: IdentityConfiguration, + C: IdentityProofSystemConfiguration, I: IntegratedEncryptionScheme<Plaintext = Asset>, - P: BooleanSystem, - AssetId: Var<P, PublicOrSecret>, - AssetBalance: Var<P, PublicOrSecret>, - CommitmentSchemeRandomness<C>: Var<P, Secret>, - CommitmentSchemeOutput<C>: Equal<P, PublicOrSecret>, + AssetId: Var<C::ProofSystem, PublicOrSecret>, + AssetBalance: Var<C::ProofSystem, PublicOrSecret>, { /// Returns the asset id of this receiver. #[inline] - pub fn asset_id(&self) -> &AssetIdVar<P> { + pub fn asset_id(&self) -> &AssetIdVar<C::ProofSystem> { &self.asset.id } /// Returns the asset value of this receiver. #[inline] - pub fn asset_value(&self) -> &AssetBalanceVar<P> { + pub fn asset_value(&self) -> &AssetBalanceVar<C::ProofSystem> { &self.asset.value } @@ -1609,14 +1658,13 @@ where /// where `k` is `self.void_number_commitment` and `s` is `self.utxo_randomness`. In this /// equation we have `{ utxo } : Public`, `{ asset, k, s } : Secret`. #[inline] - pub fn verify_well_formed<CSVar>(&self, ps: &mut P, commitment_scheme: &CSVar) - where - CSVar: CommitmentScheme< - Randomness = CommitmentSchemeRandomnessVar<C, P>, - Output = CommitmentSchemeOutputVar<C, P>, - >, - AssetVar<P>: CommitmentInput<CSVar>, - VoidNumberCommitmentVar<C, P>: CommitmentInput<CSVar>, + pub fn verify_well_formed( + &self, + ps: &mut C::ProofSystem, + commitment_scheme: &C::CommitmentSchemeVar, + ) where + AssetVar<C::ProofSystem>: CommitmentInput<C::CommitmentSchemeVar>, + VoidNumberCommitmentVar<C>: CommitmentInput<C::CommitmentSchemeVar>, { ps.assert_eq( &self.utxo, @@ -1630,33 +1678,27 @@ where } } -impl<C, I, P> IsVariable<P, Derived> for ReceiverVar<C, I, P> +impl<C, I> IsVariable<C::ProofSystem, Derived> for ReceiverVar<C, I> where - C: IdentityConfiguration, + C: IdentityProofSystemConfiguration, I: IntegratedEncryptionScheme<Plaintext = Asset>, - P: BooleanSystem, - AssetId: Var<P, PublicOrSecret>, - AssetBalance: Var<P, PublicOrSecret>, - CommitmentSchemeRandomness<C>: Var<P, Secret>, - CommitmentSchemeOutput<C>: Equal<P, PublicOrSecret>, + AssetId: Var<C::ProofSystem, PublicOrSecret>, + AssetBalance: Var<C::ProofSystem, PublicOrSecret>, { type Type = Receiver<C, I>; } -impl<C, I, P> Var<P, Derived> for Receiver<C, I> +impl<C, I> Var<C::ProofSystem, Derived> for Receiver<C, I> where - C: IdentityConfiguration, + C: IdentityProofSystemConfiguration, I: IntegratedEncryptionScheme<Plaintext = Asset>, - P: BooleanSystem, - AssetId: Var<P, PublicOrSecret>, - AssetBalance: Var<P, PublicOrSecret>, - CommitmentSchemeRandomness<C>: Var<P, Secret>, - CommitmentSchemeOutput<C>: Equal<P, PublicOrSecret>, + AssetId: Var<C::ProofSystem, PublicOrSecret>, + AssetBalance: Var<C::ProofSystem, PublicOrSecret>, { - type Variable = ReceiverVar<C, I, P>; + type Variable = ReceiverVar<C, I>; #[inline] - fn as_variable(&self, ps: &mut P, mode: Derived) -> Self::Variable { + fn as_variable(&self, ps: &mut C::ProofSystem, mode: Derived) -> Self::Variable { // FIXME: Have one function where we do variable allocation instead of two. Self::Variable { asset: self.asset.as_variable(ps, mode.into()), @@ -1668,7 +1710,7 @@ where } #[inline] - fn unknown(ps: &mut P, mode: Derived) -> Self::Variable { + fn unknown(ps: &mut C::ProofSystem, mode: Derived) -> Self::Variable { // FIXME: Have one function where we do variable allocation instead of two. Self::Variable { asset: Asset::unknown(ps, mode.into()), diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 673ed28db..0ba40b641 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -22,19 +22,17 @@ use crate::{ AssetVar, }, identity::{ - CommitmentSchemeOutput, CommitmentSchemeOutputVar, CommitmentSchemeRandomness, - CommitmentSchemeRandomnessVar, CommitmentSchemeVar, IdentityConfiguration, - PseudorandomFunctionFamilyInput, PseudorandomFunctionFamilyOutput, PublicKeyVar, Receiver, - ReceiverPost, ReceiverVar, SecretKey, Sender, SenderPost, SenderVar, Utxo, VoidNumber, - VoidNumberCommitmentVar, VoidNumberGeneratorVar, + IdentityProofSystemConfiguration, PublicKeyVar, Receiver, ReceiverPost, ReceiverVar, + Sender, SenderPost, SenderVar, Utxo, VoidNumber, VoidNumberCommitmentVar, + VoidNumberGeneratorVar, }, ledger::{Ledger, PostError}, }; use alloc::vec::Vec; -use core::iter::Sum; +use core::ops::AddAssign; use manta_crypto::{ - commitment::{CommitmentScheme, Input as CommitmentInput}, - constraint::{Const, Constant, Equal, ProofSystem, PublicOrSecret, Secret, Var}, + commitment::Input as CommitmentInput, + constraint::{BooleanSystem, Derived, Equal, ProofSystem, Public, PublicOrSecret, Secret, Var}, ies::{EncryptedMessage, IntegratedEncryptionScheme}, set::{ContainmentProof, VerifiedSet}, }; @@ -105,7 +103,7 @@ impl<const SOURCES: usize, const SINKS: usize> Distribution<PublicTransfer<SOURC } /// Secret Transfer Configuration -pub trait SecretTransferConfiguration: IdentityConfiguration { +pub trait SecretTransferConfiguration: IdentityProofSystemConfiguration { /// Integrated Encryption Scheme for [`Asset`] type IntegratedEncryptionScheme: IntegratedEncryptionScheme<Plaintext = Asset>; @@ -113,9 +111,6 @@ pub trait SecretTransferConfiguration: IdentityConfiguration { type UtxoSet: VerifiedSet<Item = Utxo<Self>>; } -/// Secret Transfer Proof System -pub trait SecretTransferProofSystem: SecretTransferConfiguration + ProofSystem {} - /// Secret Sender Type pub type SecretSender<T> = Sender<T, <T as SecretTransferConfiguration>::UtxoSet>; @@ -124,16 +119,16 @@ pub type SecretReceiver<T> = Receiver<T, <T as SecretTransferConfiguration>::IntegratedEncryptionScheme>; /// Secret Sender Variable Type -pub type SecretSenderVar<T> = SenderVar<T, <T as SecretTransferConfiguration>::UtxoSet, T>; +pub type SecretSenderVar<T> = SenderVar<T, <T as SecretTransferConfiguration>::UtxoSet>; /// Secret Receiver Type pub type SecretReceiverVar<T> = - ReceiverVar<T, <T as SecretTransferConfiguration>::IntegratedEncryptionScheme, T>; + ReceiverVar<T, <T as SecretTransferConfiguration>::IntegratedEncryptionScheme>; /// Secret Transfer Protocol pub struct SecretTransfer<T, const SENDERS: usize, const RECEIVERS: usize> where - T: SecretTransferProofSystem, + T: SecretTransferConfiguration, { /// Secret Senders pub senders: [SecretSender<T>; SENDERS], @@ -144,7 +139,7 @@ where impl<T, const SENDERS: usize, const RECEIVERS: usize> SecretTransfer<T, SENDERS, RECEIVERS> where - T: SecretTransferProofSystem, + T: SecretTransferConfiguration, { /// Maximum Number of Senders pub const MAXIMUM_SENDER_COUNT: usize = 10; @@ -200,81 +195,92 @@ where } */ + /* TODO: /// Builds constraints for secret transfer validity proof. #[inline] fn verify( - ps: &mut T, + ps: &mut T::ProofSystem, commitment_scheme: &T::CommitmentScheme, senders: Vec<SecretSenderVar<T>>, receivers: Vec<SecretReceiverVar<T>>, ) where - T::CommitmentScheme: Const<T>, - AssetId: Equal<T, PublicOrSecret>, - AssetBalance: Equal<T, PublicOrSecret>, - for<'i> &'i AssetBalanceVar<T>: Sum, - SecretKey<T>: Var<T, Secret>, - PseudorandomFunctionFamilyInput<T>: Var<T, Secret>, - PseudorandomFunctionFamilyOutput<T>: Equal<T, PublicOrSecret>, - CommitmentSchemeRandomness<T>: Var<T, Secret>, - CommitmentSchemeOutput<T>: Equal<T, PublicOrSecret>, - ContainmentProof<T::UtxoSet>: Var<T, Secret>, - Constant<T::CommitmentScheme, T>: CommitmentScheme< - Randomness = CommitmentSchemeRandomnessVar<T, T>, - Output = CommitmentSchemeOutputVar<T, T>, - >, - PublicKeyVar<T, T>: CommitmentInput<CommitmentSchemeVar<T, T>>, - VoidNumberGeneratorVar<T, T>: CommitmentInput<CommitmentSchemeVar<T, T>>, - AssetVar<T>: CommitmentInput<CommitmentSchemeVar<T, T>>, - VoidNumberCommitmentVar<T, T>: CommitmentInput<CommitmentSchemeVar<T, T>>, + AssetId: Equal<T::ProofSystem, PublicOrSecret>, + AssetBalance: Equal<T::ProofSystem, PublicOrSecret>, + for<'i> AssetBalanceVar<T::ProofSystem>: AddAssign<&'i AssetBalanceVar<T::ProofSystem>>, + PublicKeyVar<T>: CommitmentInput<T::CommitmentSchemeVar>, + VoidNumberGeneratorVar<T>: CommitmentInput<T::CommitmentSchemeVar>, + AssetVar<T::ProofSystem>: CommitmentInput<T::CommitmentSchemeVar>, + VoidNumberCommitmentVar<T>: CommitmentInput<T::CommitmentSchemeVar>, + ContainmentProof<T::UtxoSet>: Var<T::ProofSystem, Secret>, { // TODO: find a way to do this without having to build intermediate `Vec<_>` - let commitment_scheme = commitment_scheme.as_constant(ps); + let commitment_scheme = commitment_scheme.as_variable(ps, Public); + + let mut sender_value_total = AssetBalance(0).as_variable(ps, PublicOrSecret::Secret); + let mut receiver_value_total = AssetBalance(0).as_variable(ps, PublicOrSecret::Secret); - // 1. Check that all senders are well-formed. - /* FIXME: + // 1. Check that all senders are well-formed and sum their asset values.. + /* for sender in &senders { sender.verify_well_formed(ps, &commitment_scheme); + sender_value_total += sender.asset_value(); } */ - // 2. Check that all receivers are well-formed. + let asset_ids = senders + .iter() + .map(|sender| { + sender.verify_well_formed(ps, &commitment_scheme); + sender_value_total += sender.asset_value(); + sender.asset_id() + }) + .chain(receivers.iter().map(|receiver| { + receiver.verify_well_formed(ps, &commitment_scheme); + receiver_value_total += receiver.asset_value(); + receiver.asset_id() + })); + + // 2. Check that all receivers are well-formed and sum their asset values. + /* for receiver in &receivers { receiver.verify_well_formed(ps, &commitment_scheme); + receiver_value_total += receiver.asset_value(); } + */ // 3. Check that there is a unique asset id for all the assets. - let sender_ids = senders.iter().map(SenderVar::asset_id); - let receiver_ids = receivers.iter().map(ReceiverVar::asset_id); - ps.assert_all_eq(sender_ids.chain(receiver_ids)); + ps.assert_all_eq(asset_ids); // 4. Check that the transaction is balanced. - ps.assert_eq( - senders.iter().map(SenderVar::asset_value).sum(), - receivers.iter().map(ReceiverVar::asset_value).sum(), - ); + ps.assert_eq(&sender_value_total, &receiver_value_total); } + */ #[inline] - fn generate_validity_proof(&self) -> Option<T::Proof> { + fn generate_validity_proof(&self) -> Option<<T::ProofSystem as ProofSystem>::Proof> + where + AssetId: Equal<T::ProofSystem, PublicOrSecret>, + AssetBalance: Equal<T::ProofSystem, PublicOrSecret>, + ContainmentProof<T::UtxoSet>: Var<T::ProofSystem, Secret>, + { // FIXME: Build secret transfer zero knowledge proof: - let proof_system = T::default(); - - /* TODO: + let mut proof_system = <T::ProofSystem as Default>::default(); // When we know the variables: let senders = self .senders .iter() - .map(|s| s.as_variable(&mut proof_system)) + .map(|s| s.as_variable(&mut proof_system, Derived)) .collect::<Vec<_>>(); let receivers = self .receivers .iter() - .map(|r| r.as_variable(&mut proof_system)) + .map(|r| r.as_variable(&mut proof_system, Derived)) .collect::<Vec<_>>(); + /* TODO: // When we don't: let senders = self .senders @@ -295,7 +301,12 @@ where /// Converts `self` into its ledger post. #[inline] - pub fn into_post(self) -> Option<SecretTransferPost<T, SENDERS, RECEIVERS>> { + pub fn into_post(self) -> Option<SecretTransferPost<T, SENDERS, RECEIVERS>> + where + AssetId: Equal<T::ProofSystem, PublicOrSecret>, + AssetBalance: Equal<T::ProofSystem, PublicOrSecret>, + ContainmentProof<T::UtxoSet>: Var<T::ProofSystem, Secret>, + { let validity_proof = self.generate_validity_proof()?; Some(SecretTransferPost { sender_posts: array_map(self.senders, Sender::into_post), @@ -315,7 +326,7 @@ pub type SecretReceiverPost<T> = /// Secret Transfer Post pub struct SecretTransferPost<T, const SENDERS: usize, const RECEIVERS: usize> where - T: SecretTransferProofSystem, + T: SecretTransferConfiguration, { /// Sender Posts pub sender_posts: [SecretSenderPost<T>; SENDERS], @@ -324,12 +335,12 @@ where pub receiver_posts: [SecretReceiverPost<T>; RECEIVERS], /// Validity Proof - pub validity_proof: T::Proof, + pub validity_proof: <T::ProofSystem as ProofSystem>::Proof, } impl<T, const SENDERS: usize, const RECEIVERS: usize> SecretTransferPost<T, SENDERS, RECEIVERS> where - T: SecretTransferProofSystem, + T: SecretTransferConfiguration, { /// Posts the [`SecretTransferPost`] to the `ledger`. #[inline] @@ -357,7 +368,10 @@ where impl<T, const SENDERS: usize, const RECEIVERS: usize> From<SecretTransfer<T, SENDERS, RECEIVERS>> for Option<SecretTransferPost<T, SENDERS, RECEIVERS>> where - T: SecretTransferProofSystem, + T: SecretTransferConfiguration, + AssetId: Equal<T::ProofSystem, PublicOrSecret>, + AssetBalance: Equal<T::ProofSystem, PublicOrSecret>, + ContainmentProof<T::UtxoSet>: Var<T::ProofSystem, Secret>, { #[inline] fn from(secret_transfer: SecretTransfer<T, SENDERS, RECEIVERS>) -> Self { @@ -373,7 +387,7 @@ pub struct Transfer< const SENDERS: usize, const RECEIVERS: usize, > where - T: SecretTransferProofSystem, + T: SecretTransferConfiguration, { /// Public Transfer pub public: PublicTransfer<SOURCES, SINKS>, @@ -385,7 +399,10 @@ pub struct Transfer< impl<T, const SOURCES: usize, const SINKS: usize, const SENDERS: usize, const RECEIVERS: usize> Transfer<T, SOURCES, SINKS, SENDERS, RECEIVERS> where - T: SecretTransferProofSystem, + T: SecretTransferConfiguration, + AssetId: Equal<T::ProofSystem, PublicOrSecret>, + AssetBalance: Equal<T::ProofSystem, PublicOrSecret>, + ContainmentProof<T::UtxoSet>: Var<T::ProofSystem, Secret>, { /// Builds a new [`Transfer`] from a [`PublicTransfer`] and a [`SecretTransfer`]. #[inline] @@ -414,7 +431,7 @@ pub struct TransferPost< const SENDERS: usize, const RECEIVERS: usize, > where - T: SecretTransferProofSystem, + T: SecretTransferConfiguration, { /// Public Transfer Post pub public_transfer_post: PublicTransfer<SOURCES, SINKS>, @@ -426,7 +443,7 @@ pub struct TransferPost< impl<T, const SOURCES: usize, const SINKS: usize, const SENDERS: usize, const RECEIVERS: usize> TransferPost<T, SOURCES, SINKS, SENDERS, RECEIVERS> where - T: SecretTransferProofSystem, + T: SecretTransferConfiguration, { /// Posts the [`TransferPost`] to the `ledger`. #[inline] diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index 585aaf4cd..18e9cec70 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -29,7 +29,7 @@ use crate::{ }, keys::{self, DerivedSecretKeyGenerator, ExternalKeys, InternalKeys}, ledger::Ledger, - transfer::{SecretTransfer, SecretTransferProofSystem}, + transfer::{SecretTransfer, SecretTransferConfiguration}, }; use core::{convert::Infallible, fmt::Debug, hash::Hash}; use manta_crypto::{ @@ -395,7 +395,7 @@ where rng: &mut R, ) -> Option<SecretTransfer<T, 2, 2>> where - T: SecretTransferProofSystem, + T: SecretTransferConfiguration, R: CryptoRng + RngCore + ?Sized, Asset: CommitmentInput<T::CommitmentScheme>, VoidNumberCommitment<T>: CommitmentInput<T::CommitmentScheme>, diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index 7ec66580c..1a48baedb 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -19,7 +19,7 @@ // TODO: Add derive trait to implement `Alloc` for structs (and enums?). // TODO: How to do verification systems? Should it be a separate trait or part of `ProofSystem`? -use core::convert::{Infallible, TryFrom}; +use core::convert::TryFrom; /// Boolean Variable Type pub type Bool<P> = <P as BooleanSystem>::Bool; @@ -27,9 +27,6 @@ pub type Bool<P> = <P as BooleanSystem>::Bool; /// Variable Type pub type Variable<T, P, K = (), U = K> = <T as Var<P, K, U>>::Variable; -/// Constant Type -pub type Constant<T, P> = <T as Var<P, Public, Infallible>>::Variable; - /// Character Variable Type pub type Char<P, K = (), U = K> = Variable<char, P, K, U>; @@ -108,25 +105,6 @@ where fn unknown(ps: &mut P, mode: Unknown) -> Self::Variable; } -/// Constant Trait -pub trait Const<P>: Var<P, Public, Infallible> -where - P: ?Sized, -{ - /// Returns a new constant with value `self`. - #[inline] - fn as_constant(&self, ps: &mut P) -> Self::Variable { - self.as_variable(ps, Public) - } -} - -impl<T, P> Const<P> for T -where - T: Var<P, Public, Infallible>, - P: ?Sized, -{ -} - /// Boolean Constraint System pub trait BooleanSystem { /// Boolean Variable Type From 4c3e8b1aa16504944815a9e02146d9a58d481140 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 12 Sep 2021 15:16:08 -0400 Subject: [PATCH 027/275] WIP: converge on type-level constraints --- manta-accounting/src/asset.rs | 52 +-- manta-accounting/src/identity.rs | 406 +++++++++--------------- manta-accounting/src/transfer.rs | 188 +++++------ manta-accounting/src/wallet.rs | 18 +- manta-crypto/src/commitment.rs | 15 +- manta-crypto/src/constraint.rs | 387 +++++++++++++++++----- manta-crypto/src/set/constraint.rs | 38 +++ manta-crypto/src/{set.rs => set/mod.rs} | 2 + 8 files changed, 623 insertions(+), 483 deletions(-) create mode 100644 manta-crypto/src/set/constraint.rs rename manta-crypto/src/{set.rs => set/mod.rs} (99%) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 85405c617..9afca46b7 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -31,7 +31,9 @@ use core::{ use derive_more::{ Add, AddAssign, Display, Div, DivAssign, From, Mul, MulAssign, Product, Sub, SubAssign, Sum, }; -use manta_crypto::constraint::{IsVariable, PublicOrSecret, Secret, Var, Variable}; +use manta_crypto::constraint::{ + Alloc, Allocation, HasVariable, PublicOrSecret, Secret, Var, Variable, +}; use manta_util::{ array_map, fallible_array_map, into_array_unchecked, ByteAccumulator, ConcatBytes, }; @@ -334,16 +336,16 @@ impl Distribution<Asset> for Standard { } /// Asset Id Variable -pub type AssetIdVar<P> = Variable<AssetId, P, PublicOrSecret>; +pub type AssetIdVar<P> = Var<AssetId, P>; /// Asset Balance Variable -pub type AssetBalanceVar<P> = Variable<AssetBalance, P, PublicOrSecret>; +pub type AssetBalanceVar<P> = Var<AssetBalance, P>; /// Asset Variable pub struct AssetVar<P> where - AssetId: Var<P, PublicOrSecret>, - AssetBalance: Var<P, PublicOrSecret>, + P: HasVariable<AssetId, Mode = PublicOrSecret> + + HasVariable<AssetBalance, Mode = PublicOrSecret>, { /// Asset Id pub id: AssetIdVar<P>, @@ -352,34 +354,38 @@ where pub value: AssetBalanceVar<P>, } -impl<P> IsVariable<P, Secret> for AssetVar<P> +impl<P> Variable<P> for AssetVar<P> where - AssetId: Var<P, PublicOrSecret>, - AssetBalance: Var<P, PublicOrSecret>, + P: HasVariable<AssetId, Mode = PublicOrSecret> + + HasVariable<AssetBalance, Mode = PublicOrSecret>, { + type Mode = Secret; type Type = Asset; } -impl<P> Var<P, Secret> for Asset +impl<P> Alloc<P> for Asset where - AssetId: Var<P, PublicOrSecret>, - AssetBalance: Var<P, PublicOrSecret>, + P: HasVariable<AssetId, Mode = PublicOrSecret> + + HasVariable<AssetBalance, Mode = PublicOrSecret>, { - type Variable = AssetVar<P>; + type Mode = Secret; - #[inline] - fn as_variable(&self, ps: &mut P, mode: Secret) -> Self::Variable { - Self::Variable { - id: self.id.as_variable(ps, mode.into()), - value: self.value.as_variable(ps, mode.into()), - } - } + type Variable = AssetVar<P>; #[inline] - fn unknown(ps: &mut P, mode: Secret) -> Self::Variable { - Self::Variable { - id: AssetId::unknown(ps, mode.into()), - value: AssetBalance::unknown(ps, mode.into()), + fn variable<'t>(ps: &mut P, allocation: impl Into<Allocation<'t, Self, P>>) -> Self::Variable + where + Self: 't, + { + match allocation.into() { + Allocation::Known(Asset { id, value }, mode) => Self::Variable { + id: ps.allocate((id, mode.into())), + value: ps.allocate((value, mode.into())), + }, + Allocation::Unknown(mode) => Self::Variable { + id: <P as HasVariable<AssetId>>::allocate_unknown(ps, mode.into()), + value: <P as HasVariable<AssetBalance>>::allocate_unknown(ps, mode.into()), + }, } } } diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 3647bd408..e1647119f 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -22,7 +22,7 @@ // TODO: Since `IdentityConfiguration::SecretKey: Clone`, should `Identity: Clone`? use crate::{ - asset::{Asset, AssetBalance, AssetBalanceVar, AssetId, AssetIdVar, AssetVar}, + asset::{Asset, AssetBalance, AssetId, AssetVar}, keys::SecretKeyGenerator, ledger::Ledger, }; @@ -30,8 +30,8 @@ use core::{convert::Infallible, fmt::Debug, hash::Hash, marker::PhantomData}; use manta_crypto::{ commitment::{CommitmentScheme, Input as CommitmentInput}, constraint::{ - BooleanSystem, Derived, Equal, IsVariable, ProofSystem, Public, PublicOrSecret, Secret, - Var, Variable, + Alloc, Allocation, BooleanSystem, Constant, Derived, Equal, HasVariable, ProofSystem, + Public, PublicOrSecret, Secret, Var, Variable, }, ies::{self, EncryptedMessage, IntegratedEncryptionScheme}, set::{ContainmentProof, VerifiedSet}, @@ -59,7 +59,11 @@ pub trait IdentityConfiguration { type PseudorandomFunctionFamily: PseudorandomFunctionFamily<Seed = Self::SecretKey>; /// Commitment Scheme Type - type CommitmentScheme: CommitmentScheme; + type CommitmentScheme: CommitmentScheme + + CommitmentInput<PublicKey<Self>> + + CommitmentInput<VoidNumberGenerator<Self>> + + CommitmentInput<Asset> + + CommitmentInput<VoidNumberCommitment<Self>>; /// Seedable Cryptographic Random Number Generator Type type Rng: CryptoRng + RngCore + SeedableRng<Seed = Self::SecretKey>; @@ -68,52 +72,58 @@ pub trait IdentityConfiguration { /// [`Identity`] Proof System Configuration pub trait IdentityProofSystemConfiguration { /// Proof System - type ProofSystem: ProofSystem; + type ProofSystem: ProofSystem + + HasVariable<AssetId, Mode = PublicOrSecret> + + HasVariable<AssetBalance, Mode = PublicOrSecret>; /// Pseudorandom Function Family Seed - type PseudorandomFunctionFamilySeed: Clone + Var<Self::ProofSystem, Secret>; + type PseudorandomFunctionFamilySeed: Clone + Alloc<Self::ProofSystem, Mode = Secret>; /// Pseudorandom Function Family Input - type PseudorandomFunctionFamilyInput: Var<Self::ProofSystem, Secret>; + type PseudorandomFunctionFamilyInput: Alloc<Self::ProofSystem, Mode = Secret>; /// Pseudorandom Function Family Output - type PseudorandomFunctionFamilyOutput: Equal<Self::ProofSystem, PublicOrSecret>; + type PseudorandomFunctionFamilyOutput: Equal<Self::ProofSystem, Mode = PublicOrSecret>; /// Pseudorandom Function Family type PseudorandomFunctionFamily: PseudorandomFunctionFamily< Seed = Self::PseudorandomFunctionFamilySeed, Input = Self::PseudorandomFunctionFamilyInput, Output = Self::PseudorandomFunctionFamilyOutput, - > + Var<Self::ProofSystem, Public, Infallible, Variable = Self::PseudorandomFunctionFamilyVar>; + > + Alloc<Self::ProofSystem, Mode = Constant, Variable = Self::PseudorandomFunctionFamilyVar>; /// Pseudorandom Function Family Variable type PseudorandomFunctionFamilyVar: PseudorandomFunctionFamily< - Seed = Variable<Self::PseudorandomFunctionFamilySeed, Self::ProofSystem, Secret>, - Input = Variable<Self::PseudorandomFunctionFamilyInput, Self::ProofSystem, Secret>, - Output = Variable< - Self::PseudorandomFunctionFamilyOutput, - Self::ProofSystem, - PublicOrSecret, - >, - > + IsVariable<Self::ProofSystem, Public, Infallible, Type = Self::PseudorandomFunctionFamily>; + Seed = PseudorandomFunctionFamilySeedVar<Self>, + Input = PseudorandomFunctionFamilyInputVar<Self>, + Output = PseudorandomFunctionFamilyOutputVar<Self>, + > + Variable<Self::ProofSystem, Mode = Constant, Type = Self::PseudorandomFunctionFamily>; /// Commitment Scheme Randomness - type CommitmentSchemeRandomness: Var<Self::ProofSystem, Secret>; + type CommitmentSchemeRandomness: Alloc<Self::ProofSystem, Mode = Secret>; /// Commitment Scheme Output - type CommitmentSchemeOutput: Equal<Self::ProofSystem, PublicOrSecret>; + type CommitmentSchemeOutput: Equal<Self::ProofSystem, Mode = PublicOrSecret>; /// Commitment Scheme type CommitmentScheme: CommitmentScheme< Randomness = Self::CommitmentSchemeRandomness, Output = Self::CommitmentSchemeOutput, - > + Var<Self::ProofSystem, Public, Infallible, Variable = Self::CommitmentSchemeVar>; + > + CommitmentInput<PublicKey<Self>> + + CommitmentInput<VoidNumberGenerator<Self>> + + CommitmentInput<Asset> + + CommitmentInput<VoidNumberCommitment<Self>> + + Alloc<Self::ProofSystem, Mode = Constant, Variable = Self::CommitmentSchemeVar>; /// Commitment Scheme Variable type CommitmentSchemeVar: CommitmentScheme< - Randomness = Variable<Self::CommitmentSchemeRandomness, Self::ProofSystem, Secret>, - Output = Variable<Self::CommitmentSchemeOutput, Self::ProofSystem, PublicOrSecret>, - > + IsVariable<Self::ProofSystem, Public, Infallible, Type = Self::CommitmentScheme>; + Randomness = CommitmentSchemeRandomnessVar<Self>, + Output = CommitmentSchemeOutputVar<Self>, + > + CommitmentInput<PublicKeyVar<Self>> + + CommitmentInput<VoidNumberGeneratorVar<Self>> + + CommitmentInput<AssetVar<Self::ProofSystem>> + + CommitmentInput<VoidNumberCommitmentVar<Self>> + + Variable<Self::ProofSystem, Mode = Constant, Type = Self::CommitmentScheme>; /// Seedable Cryptographic Random Number Generator type Rng: CryptoRng + RngCore + SeedableRng<Seed = Self::PseudorandomFunctionFamilySeed>; @@ -177,39 +187,24 @@ pub type UtxoRandomness<C> = CommitmentSchemeRandomness<C>; pub type Utxo<C> = CommitmentSchemeOutput<C>; /// [`PseudorandomFunctionFamily::Seed`] Variable Type -pub type PseudorandomFunctionFamilySeedVar<C> = Variable< - PseudorandomFunctionFamilySeed<C>, - <C as IdentityProofSystemConfiguration>::ProofSystem, - Secret, ->; +pub type PseudorandomFunctionFamilySeedVar<C> = + Var<PseudorandomFunctionFamilySeed<C>, <C as IdentityProofSystemConfiguration>::ProofSystem>; /// [`PseudorandomFunctionFamily::Input`] Variable Type -pub type PseudorandomFunctionFamilyInputVar<C> = Variable< - PseudorandomFunctionFamilyInput<C>, - <C as IdentityProofSystemConfiguration>::ProofSystem, - Secret, ->; +pub type PseudorandomFunctionFamilyInputVar<C> = + Var<PseudorandomFunctionFamilyInput<C>, <C as IdentityProofSystemConfiguration>::ProofSystem>; /// [`PseudorandomFunctionFamily::Output`] Variable Type -pub type PseudorandomFunctionFamilyOutputVar<C> = Variable< - PseudorandomFunctionFamilyOutput<C>, - <C as IdentityProofSystemConfiguration>::ProofSystem, - PublicOrSecret, ->; +pub type PseudorandomFunctionFamilyOutputVar<C> = + Var<PseudorandomFunctionFamilyOutput<C>, <C as IdentityProofSystemConfiguration>::ProofSystem>; /// [`CommitmentScheme::Randomness`] Variable Type -pub type CommitmentSchemeRandomnessVar<C> = Variable< - CommitmentSchemeRandomness<C>, - <C as IdentityProofSystemConfiguration>::ProofSystem, - Secret, ->; +pub type CommitmentSchemeRandomnessVar<C> = + Var<CommitmentSchemeRandomness<C>, <C as IdentityProofSystemConfiguration>::ProofSystem>; /// [`CommitmentScheme::Output`] Variable Type -pub type CommitmentSchemeOutputVar<C> = Variable< - CommitmentSchemeOutput<C>, - <C as IdentityProofSystemConfiguration>::ProofSystem, - PublicOrSecret, ->; +pub type CommitmentSchemeOutputVar<C> = + Var<CommitmentSchemeOutput<C>, <C as IdentityProofSystemConfiguration>::ProofSystem>; /// Secret Key Variable Type pub type SecretKeyVar<C> = PseudorandomFunctionFamilySeedVar<C>; @@ -237,7 +232,7 @@ pub type UtxoVar<C> = CommitmentSchemeOutputVar<C>; /// UTXO Containment Proof Variable Type pub type UtxoContainmentProofVar<C, S> = - Variable<ContainmentProof<S>, <C as IdentityProofSystemConfiguration>::ProofSystem, Secret>; + Var<ContainmentProof<S>, <C as IdentityProofSystemConfiguration>::ProofSystem>; /// Generates a void number commitment from `public_key`, `void_number_generator`, and /// `void_number_commitment_randomness`. @@ -249,9 +244,7 @@ pub fn generate_void_number_commitment<CS, PK, VNG>( void_number_commitment_randomness: &CS::Randomness, ) -> CS::Output where - CS: CommitmentScheme, - PK: CommitmentInput<CS>, - VNG: CommitmentInput<CS>, + CS: CommitmentScheme + CommitmentInput<PK> + CommitmentInput<VNG>, { commitment_scheme .start() @@ -269,9 +262,7 @@ pub fn generate_utxo<CS, A, VNC>( utxo_randomness: &CS::Randomness, ) -> CS::Output where - CS: CommitmentScheme, - A: CommitmentInput<CS>, - VNC: CommitmentInput<CS>, + CS: CommitmentScheme + CommitmentInput<A> + CommitmentInput<VNC>, { commitment_scheme .start() @@ -343,11 +334,7 @@ where &self, commitment_scheme: &C::CommitmentScheme, public_key: &PublicKey<C>, - ) -> VoidNumberCommitment<C> - where - PublicKey<C>: CommitmentInput<C::CommitmentScheme>, - VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, - { + ) -> VoidNumberCommitment<C> { generate_void_number_commitment( commitment_scheme, public_key, @@ -363,11 +350,7 @@ where commitment_scheme: &C::CommitmentScheme, asset: &Asset, void_number_commitment: &VoidNumberCommitment<C>, - ) -> Utxo<C> - where - Asset: CommitmentInput<C::CommitmentScheme>, - VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, - { + ) -> Utxo<C> { generate_utxo( commitment_scheme, asset, @@ -405,38 +388,45 @@ where pub utxo_randomness: UtxoRandomnessVar<C>, } -impl<C> IsVariable<C::ProofSystem, Secret> for AssetParametersVar<C> +impl<C> Variable<C::ProofSystem> for AssetParametersVar<C> where C: IdentityProofSystemConfiguration, { + type Mode = Secret; type Type = AssetParameters<C>; } -impl<C> Var<C::ProofSystem, Secret> for AssetParameters<C> +impl<C> Alloc<C::ProofSystem> for AssetParameters<C> where C: IdentityProofSystemConfiguration, { - type Variable = AssetParametersVar<C>; + type Mode = Secret; - #[inline] - fn as_variable(&self, ps: &mut C::ProofSystem, mode: Secret) -> Self::Variable { - Self::Variable { - void_number_generator: self.void_number_generator.as_variable(ps, mode), - void_number_commitment_randomness: self - .void_number_commitment_randomness - .as_variable(ps, mode), - utxo_randomness: self.utxo_randomness.as_variable(ps, mode), - } - } + type Variable = AssetParametersVar<C>; #[inline] - fn unknown(ps: &mut C::ProofSystem, mode: Secret) -> Self::Variable { - Self::Variable { - void_number_generator: VoidNumberGenerator::<C>::unknown(ps, mode), - void_number_commitment_randomness: VoidNumberCommitmentRandomness::<C>::unknown( - ps, mode, - ), - utxo_randomness: UtxoRandomness::<C>::unknown(ps, mode), + fn variable<'t>( + ps: &mut C::ProofSystem, + allocation: impl Into<Allocation<'t, Self, C::ProofSystem>>, + ) -> Self::Variable + where + Self: 't, + { + match allocation.into() { + Allocation::Known( + AssetParameters { + void_number_generator, + void_number_commitment_randomness, + utxo_randomness, + }, + mode, + ) => Self::Variable { + void_number_generator: ps.allocate((void_number_generator, mode)), + void_number_commitment_randomness: ps + .allocate((void_number_commitment_randomness, mode)), + utxo_randomness: ps.allocate((utxo_randomness, mode)), + }, + _ => todo!(), } } } @@ -581,11 +571,7 @@ where &self, commitment_scheme: &C::CommitmentScheme, parameters: &AssetParameters<C>, - ) -> VoidNumberCommitment<C> - where - PublicKey<C>: CommitmentInput<C::CommitmentScheme>, - VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, - { + ) -> VoidNumberCommitment<C> { parameters.void_number_commitment(commitment_scheme, &self.public_key()) } @@ -596,13 +582,7 @@ where commitment_scheme: &C::CommitmentScheme, asset: &Asset, parameters: &AssetParameters<C>, - ) -> (PublicKey<C>, VoidNumberCommitment<C>, Utxo<C>) - where - PublicKey<C>: CommitmentInput<C::CommitmentScheme>, - VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, - Asset: CommitmentInput<C::CommitmentScheme>, - VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, - { + ) -> (PublicKey<C>, VoidNumberCommitment<C>, Utxo<C>) { let public_key = self.public_key(); let void_number_commitment = parameters.void_number_commitment(commitment_scheme, &public_key); @@ -621,10 +601,6 @@ where where S: VerifiedSet<Item = Utxo<C>>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: CommitmentInput<C::CommitmentScheme>, - VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, - Asset: CommitmentInput<C::CommitmentScheme>, - VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { let parameters = self.parameters(); let (public_key, void_number_commitment, utxo) = @@ -654,10 +630,6 @@ where G: SecretKeyGenerator<SecretKey = SecretKey<C>>, S: VerifiedSet<Item = Utxo<C>>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: CommitmentInput<C::CommitmentScheme>, - VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, - Asset: CommitmentInput<C::CommitmentScheme>, - VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { Self::generate(source) .map_err(SenderError::SecretKeyError)? @@ -677,8 +649,6 @@ where where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: CommitmentInput<C::CommitmentScheme>, - VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, { ShieldedIdentity { void_number_commitment: self.void_number_commitment(commitment_scheme, &parameters), @@ -693,8 +663,6 @@ where where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: CommitmentInput<C::CommitmentScheme>, - VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, { let (parameters, asset_keypair) = self.parameters_and_asset_keypair(); self.build_shielded_identity(commitment_scheme, parameters, asset_keypair.into_public()) @@ -711,8 +679,6 @@ where G: SecretKeyGenerator<SecretKey = SecretKey<C>>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: CommitmentInput<C::CommitmentScheme>, - VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, { Ok(Self::generate(source)?.into_shielded(commitment_scheme)) } @@ -751,8 +717,6 @@ where where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: CommitmentInput<C::CommitmentScheme>, - VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, { let (parameters, asset_keypair) = self.parameters_and_asset_keypair(); let (asset_public_key, asset_secret_key) = asset_keypair.into(); @@ -774,8 +738,6 @@ where G: SecretKeyGenerator<SecretKey = SecretKey<C>>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: CommitmentInput<C::CommitmentScheme>, - VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, { Ok(Self::generate(source)?.into_receiver(commitment_scheme)) } @@ -832,8 +794,6 @@ where where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: CommitmentInput<C::CommitmentScheme>, - VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, { identity.into_shielded(commitment_scheme) } @@ -849,8 +809,6 @@ where G: SecretKeyGenerator<SecretKey = SecretKey<C>>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: CommitmentInput<C::CommitmentScheme>, - VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, { Identity::generate_shielded(source, commitment_scheme) } @@ -883,8 +841,6 @@ where ) -> Result<Receiver<C, I>, I::Error> where R: CryptoRng + RngCore + ?Sized, - Asset: CommitmentInput<C::CommitmentScheme>, - VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { let Self { utxo_randomness, @@ -1022,10 +978,6 @@ where where S: VerifiedSet<Item = Utxo<C>>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: CommitmentInput<C::CommitmentScheme>, - VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, - Asset: CommitmentInput<C::CommitmentScheme>, - VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { self.try_open(&encrypted_asset) .map_err(SpendError::EncryptionError)? @@ -1072,10 +1024,6 @@ where where S: VerifiedSet<Item = Utxo<C>>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: CommitmentInput<C::CommitmentScheme>, - VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, - Asset: CommitmentInput<C::CommitmentScheme>, - VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { self.identity .into_sender(commitment_scheme, self.asset, utxo_set) @@ -1172,10 +1120,6 @@ where ) -> Result<Self, S::ContainmentError> where Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: CommitmentInput<C::CommitmentScheme>, - VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, - Asset: CommitmentInput<C::CommitmentScheme>, - VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { identity.into_sender(commitment_scheme, asset, utxo_set) } @@ -1192,10 +1136,6 @@ where where G: SecretKeyGenerator<SecretKey = SecretKey<C>>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: CommitmentInput<C::CommitmentScheme>, - VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, - Asset: CommitmentInput<C::CommitmentScheme>, - VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { Identity::generate_sender(source, commitment_scheme, asset, utxo_set) } @@ -1263,9 +1203,7 @@ pub struct SenderVar<C, S> where C: IdentityProofSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - AssetId: Var<C::ProofSystem, PublicOrSecret>, - AssetBalance: Var<C::ProofSystem, PublicOrSecret>, - ContainmentProof<S>: Var<C::ProofSystem, Secret>, + ContainmentProof<S>: Alloc<C::ProofSystem, Mode = Secret>, { /// Secret Key secret_key: SecretKeyVar<C>, @@ -1297,34 +1235,15 @@ impl<C, S> SenderVar<C, S> where C: IdentityProofSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - AssetId: Var<C::ProofSystem, PublicOrSecret>, - AssetBalance: Var<C::ProofSystem, PublicOrSecret>, - ContainmentProof<S>: Var<C::ProofSystem, Secret>, + ContainmentProof<S>: Alloc<C::ProofSystem, Mode = Secret>, { - /// Returns the asset id of this sender. + /// Checks if `self` is a well-formed sender and returns its asset. #[inline] - pub fn asset_id(&self) -> &AssetIdVar<C::ProofSystem> { - &self.asset.id - } - - /// Returns the asset value of this sender. - #[inline] - pub fn asset_value(&self) -> &AssetBalanceVar<C::ProofSystem> { - &self.asset.value - } - - /// Checks if `self` is a well-formed sender. - #[inline] - pub fn verify_well_formed( - &self, + pub fn get_well_formed_asset( + self, ps: &mut C::ProofSystem, commitment_scheme: &C::CommitmentSchemeVar, - ) where - PublicKeyVar<C>: CommitmentInput<C::CommitmentSchemeVar>, - VoidNumberGeneratorVar<C>: CommitmentInput<C::CommitmentSchemeVar>, - AssetVar<C::ProofSystem>: CommitmentInput<C::CommitmentSchemeVar>, - VoidNumberCommitmentVar<C>: CommitmentInput<C::CommitmentSchemeVar>, - { + ) -> AssetVar<C::ProofSystem> { // FIXME: Implement well-formedness check: // // 1. pk = PRF(sk, 0) [public: (), secret: (pk, sk)] @@ -1394,57 +1313,62 @@ where // ``` // where public: {root}, secret: {cm, path}. // FIXME: ps.assert(&self.utxo_containment_proof.verify(self.utxo)); + + self.asset } } -impl<C, S> IsVariable<C::ProofSystem, Derived> for SenderVar<C, S> +impl<C, S> Variable<C::ProofSystem> for SenderVar<C, S> where C: IdentityProofSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - AssetId: Var<C::ProofSystem, PublicOrSecret>, - AssetBalance: Var<C::ProofSystem, PublicOrSecret>, - ContainmentProof<S>: Var<C::ProofSystem, Secret>, + ContainmentProof<S>: Alloc<C::ProofSystem, Mode = Secret>, { + type Mode = Derived; type Type = Sender<C, S>; } -impl<C, S> Var<C::ProofSystem, Derived> for Sender<C, S> +impl<C, S> Alloc<C::ProofSystem> for Sender<C, S> where C: IdentityProofSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - AssetId: Var<C::ProofSystem, PublicOrSecret>, - AssetBalance: Var<C::ProofSystem, PublicOrSecret>, - ContainmentProof<S>: Var<C::ProofSystem, Secret>, + ContainmentProof<S>: Alloc<C::ProofSystem, Mode = Secret>, { + type Mode = Derived; type Variable = SenderVar<C, S>; #[inline] - fn as_variable(&self, ps: &mut C::ProofSystem, mode: Derived) -> Self::Variable { - // FIXME: Have one function where we do variable allocation instead of two. - Self::Variable { - secret_key: self.secret_key.as_variable(ps, mode.into()), - public_key: self.public_key.as_variable(ps, Secret.into()), - asset: self.asset.as_variable(ps, mode.into()), - parameters: self.parameters.as_variable(ps, mode.into()), - void_number: self.void_number.as_variable(ps, Public.into()), - void_number_commitment: self.void_number_commitment.as_variable(ps, Public.into()), - utxo: self.utxo.as_variable(ps, Secret.into()), - utxo_containment_proof: self.utxo_containment_proof.as_variable(ps, mode.into()), - } - } - - #[inline] - fn unknown(ps: &mut C::ProofSystem, mode: Derived) -> Self::Variable { - // FIXME: Have one function where we do variable allocation instead of two. - Self::Variable { - secret_key: SecretKey::<C>::unknown(ps, mode.into()), - public_key: PublicKey::<C>::unknown(ps, Secret.into()), - asset: Asset::unknown(ps, mode.into()), - parameters: AssetParameters::<C>::unknown(ps, mode.into()), - void_number: VoidNumber::<C>::unknown(ps, Public.into()), - void_number_commitment: VoidNumberCommitment::<C>::unknown(ps, Public.into()), - utxo: Utxo::<C>::unknown(ps, Secret.into()), - utxo_containment_proof: ContainmentProof::<S>::unknown(ps, mode.into()), + fn variable<'t>( + ps: &mut C::ProofSystem, + allocation: impl Into<Allocation<'t, Self, C::ProofSystem>>, + ) -> Self::Variable + where + Self: 't, + { + match allocation.into() { + Allocation::Known( + Sender { + secret_key, + public_key, + asset, + parameters, + void_number, + void_number_commitment, + utxo, + utxo_containment_proof, + }, + mode, + ) => Self::Variable { + secret_key: ps.allocate((secret_key, mode.into())), + public_key: ps.allocate((public_key, Secret.into())), + asset: ps.allocate((asset, mode.into())), + parameters: ps.allocate((parameters, mode.into())), + void_number: ps.allocate((void_number, Public.into())), + void_number_commitment: ps.allocate((void_number_commitment, Public.into())), + utxo: ps.allocate((utxo, Secret.into())), + utxo_containment_proof: ps.allocate((utxo_containment_proof, mode.into())), + }, + _ => todo!(), } } } @@ -1548,8 +1472,6 @@ where ) -> Result<Self, I::Error> where R: CryptoRng + RngCore + ?Sized, - Asset: CommitmentInput<C::CommitmentScheme>, - VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { identity.into_receiver(commitment_scheme, asset, rng) } @@ -1611,8 +1533,6 @@ pub struct ReceiverVar<C, I> where C: IdentityProofSystemConfiguration, I: IntegratedEncryptionScheme<Plaintext = Asset>, - AssetId: Var<C::ProofSystem, PublicOrSecret>, - AssetBalance: Var<C::ProofSystem, PublicOrSecret>, { /// Asset asset: AssetVar<C::ProofSystem>, @@ -1634,22 +1554,8 @@ impl<C, I> ReceiverVar<C, I> where C: IdentityProofSystemConfiguration, I: IntegratedEncryptionScheme<Plaintext = Asset>, - AssetId: Var<C::ProofSystem, PublicOrSecret>, - AssetBalance: Var<C::ProofSystem, PublicOrSecret>, { - /// Returns the asset id of this receiver. - #[inline] - pub fn asset_id(&self) -> &AssetIdVar<C::ProofSystem> { - &self.asset.id - } - - /// Returns the asset value of this receiver. - #[inline] - pub fn asset_value(&self) -> &AssetBalanceVar<C::ProofSystem> { - &self.asset.value - } - - /// Checks if `self` is a well-formed receiver. + /// Checks if `self` is a well-formed receiver and returns its asset. /// /// This [`ReceiverVar`] is well-formed whenever: /// ```text @@ -1658,14 +1564,11 @@ where /// where `k` is `self.void_number_commitment` and `s` is `self.utxo_randomness`. In this /// equation we have `{ utxo } : Public`, `{ asset, k, s } : Secret`. #[inline] - pub fn verify_well_formed( - &self, + pub fn get_well_formed_asset( + self, ps: &mut C::ProofSystem, commitment_scheme: &C::CommitmentSchemeVar, - ) where - AssetVar<C::ProofSystem>: CommitmentInput<C::CommitmentSchemeVar>, - VoidNumberCommitmentVar<C>: CommitmentInput<C::CommitmentSchemeVar>, - { + ) -> AssetVar<C::ProofSystem> { ps.assert_eq( &self.utxo, &generate_utxo( @@ -1675,49 +1578,54 @@ where &self.utxo_randomness, ), ); + self.asset } } -impl<C, I> IsVariable<C::ProofSystem, Derived> for ReceiverVar<C, I> +impl<C, I> Variable<C::ProofSystem> for ReceiverVar<C, I> where C: IdentityProofSystemConfiguration, I: IntegratedEncryptionScheme<Plaintext = Asset>, - AssetId: Var<C::ProofSystem, PublicOrSecret>, - AssetBalance: Var<C::ProofSystem, PublicOrSecret>, { + type Mode = Derived; type Type = Receiver<C, I>; } -impl<C, I> Var<C::ProofSystem, Derived> for Receiver<C, I> +impl<C, I> Alloc<C::ProofSystem> for Receiver<C, I> where C: IdentityProofSystemConfiguration, I: IntegratedEncryptionScheme<Plaintext = Asset>, - AssetId: Var<C::ProofSystem, PublicOrSecret>, - AssetBalance: Var<C::ProofSystem, PublicOrSecret>, { - type Variable = ReceiverVar<C, I>; + type Mode = Derived; - #[inline] - fn as_variable(&self, ps: &mut C::ProofSystem, mode: Derived) -> Self::Variable { - // FIXME: Have one function where we do variable allocation instead of two. - Self::Variable { - asset: self.asset.as_variable(ps, mode.into()), - utxo_randomness: self.utxo_randomness.as_variable(ps, mode.into()), - void_number_commitment: self.void_number_commitment.as_variable(ps, Secret.into()), - utxo: self.utxo.as_variable(ps, Public.into()), - __: PhantomData, - } - } + type Variable = ReceiverVar<C, I>; #[inline] - fn unknown(ps: &mut C::ProofSystem, mode: Derived) -> Self::Variable { - // FIXME: Have one function where we do variable allocation instead of two. - Self::Variable { - asset: Asset::unknown(ps, mode.into()), - utxo_randomness: UtxoRandomness::<C>::unknown(ps, mode.into()), - void_number_commitment: VoidNumberCommitment::<C>::unknown(ps, Secret.into()), - utxo: Utxo::<C>::unknown(ps, Public.into()), - __: PhantomData, + fn variable<'t>( + ps: &mut C::ProofSystem, + allocation: impl Into<Allocation<'t, Self, C::ProofSystem>>, + ) -> Self::Variable + where + Self: 't, + { + match allocation.into() { + Allocation::Known( + Receiver { + asset, + utxo_randomness, + void_number_commitment, + utxo, + .. + }, + mode, + ) => Self::Variable { + asset: ps.allocate((asset, mode.into())), + utxo_randomness: ps.allocate((utxo_randomness, mode.into())), + void_number_commitment: ps.allocate((void_number_commitment, Secret.into())), + utxo: ps.allocate((utxo, Public.into())), + __: PhantomData, + }, + _ => todo!(), } } } diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 0ba40b641..25553dd4e 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -17,26 +17,24 @@ //! Transfer Protocols use crate::{ - asset::{ - sample_asset_balances, Asset, AssetBalance, AssetBalanceVar, AssetBalances, AssetId, - AssetVar, - }, + asset::{sample_asset_balances, Asset, AssetBalance, AssetBalanceVar, AssetBalances, AssetId}, identity::{ - IdentityProofSystemConfiguration, PublicKeyVar, Receiver, ReceiverPost, ReceiverVar, - Sender, SenderPost, SenderVar, Utxo, VoidNumber, VoidNumberCommitmentVar, - VoidNumberGeneratorVar, + IdentityProofSystemConfiguration, Receiver, ReceiverPost, ReceiverVar, Sender, SenderPost, + SenderVar, Utxo, VoidNumber, }, ledger::{Ledger, PostError}, }; use alloc::vec::Vec; use core::ops::AddAssign; use manta_crypto::{ - commitment::Input as CommitmentInput, - constraint::{BooleanSystem, Derived, Equal, ProofSystem, Public, PublicOrSecret, Secret, Var}, + constraint::{ + Alloc, BooleanSystem, Derived, Equal, HasVariable, ProofSystem, Public, PublicOrSecret, + Secret, + }, ies::{EncryptedMessage, IntegratedEncryptionScheme}, set::{ContainmentProof, VerifiedSet}, }; -use manta_util::array_map; +use manta_util::{array_map, mixed_chain, Either}; use rand::{ distributions::{Distribution, Standard}, Rng, RngCore, @@ -195,119 +193,95 @@ where } */ - /* TODO: /// Builds constraints for secret transfer validity proof. #[inline] - fn verify( + fn verify<S, R>( ps: &mut T::ProofSystem, commitment_scheme: &T::CommitmentScheme, - senders: Vec<SecretSenderVar<T>>, - receivers: Vec<SecretReceiverVar<T>>, + senders: S, + receivers: R, ) where - AssetId: Equal<T::ProofSystem, PublicOrSecret>, - AssetBalance: Equal<T::ProofSystem, PublicOrSecret>, + S: IntoIterator<Item = SecretSenderVar<T>>, + R: IntoIterator<Item = SecretReceiverVar<T>>, + AssetId: Equal<T::ProofSystem, Mode = PublicOrSecret>, + AssetBalance: Equal<T::ProofSystem, Mode = PublicOrSecret>, for<'i> AssetBalanceVar<T::ProofSystem>: AddAssign<&'i AssetBalanceVar<T::ProofSystem>>, - PublicKeyVar<T>: CommitmentInput<T::CommitmentSchemeVar>, - VoidNumberGeneratorVar<T>: CommitmentInput<T::CommitmentSchemeVar>, - AssetVar<T::ProofSystem>: CommitmentInput<T::CommitmentSchemeVar>, - VoidNumberCommitmentVar<T>: CommitmentInput<T::CommitmentSchemeVar>, - ContainmentProof<T::UtxoSet>: Var<T::ProofSystem, Secret>, + ContainmentProof<T::UtxoSet>: Alloc<T::ProofSystem, Mode = Secret>, { - // TODO: find a way to do this without having to build intermediate `Vec<_>` - - let commitment_scheme = commitment_scheme.as_variable(ps, Public); - - let mut sender_value_total = AssetBalance(0).as_variable(ps, PublicOrSecret::Secret); - let mut receiver_value_total = AssetBalance(0).as_variable(ps, PublicOrSecret::Secret); - - // 1. Check that all senders are well-formed and sum their asset values.. - /* - for sender in &senders { - sender.verify_well_formed(ps, &commitment_scheme); - sender_value_total += sender.asset_value(); - } - */ - - let asset_ids = senders - .iter() - .map(|sender| { - sender.verify_well_formed(ps, &commitment_scheme); - sender_value_total += sender.asset_value(); - sender.asset_id() - }) - .chain(receivers.iter().map(|receiver| { - receiver.verify_well_formed(ps, &commitment_scheme); - receiver_value_total += receiver.asset_value(); - receiver.asset_id() - })); - - // 2. Check that all receivers are well-formed and sum their asset values. - /* - for receiver in &receivers { - receiver.verify_well_formed(ps, &commitment_scheme); - receiver_value_total += receiver.asset_value(); - } - */ - - // 3. Check that there is a unique asset id for all the assets. - ps.assert_all_eq(asset_ids); + let commitment_scheme = ps.allocate((commitment_scheme, Public)); + + let mut sender_sum = ps.allocate((&AssetBalance(0), Secret.into())); + let mut receiver_sum = ps.allocate((&AssetBalance(0), Secret.into())); + + #[allow(clippy::needless_collect)] // NOTE: `ps` is being mutated, we need to collect. + let asset_ids = mixed_chain(senders, receivers, |c| match c { + Either::Left(sender) => { + let asset = sender.get_well_formed_asset(ps, &commitment_scheme); + sender_sum += &asset.value; + asset.id + } + Either::Right(receiver) => { + let asset = receiver.get_well_formed_asset(ps, &commitment_scheme); + receiver_sum += &asset.value; + asset.id + } + }) + .collect::<Vec<_>>(); - // 4. Check that the transaction is balanced. - ps.assert_eq(&sender_value_total, &receiver_value_total); + ps.assert_all_eq(asset_ids.iter()); + ps.assert_eq(&sender_sum, &receiver_sum); } - */ #[inline] - fn generate_validity_proof(&self) -> Option<<T::ProofSystem as ProofSystem>::Proof> + fn generate_validity_proof( + &self, + commitment_scheme: &T::CommitmentScheme, + ) -> Option<<T::ProofSystem as ProofSystem>::Proof> where - AssetId: Equal<T::ProofSystem, PublicOrSecret>, - AssetBalance: Equal<T::ProofSystem, PublicOrSecret>, - ContainmentProof<T::UtxoSet>: Var<T::ProofSystem, Secret>, + AssetId: Equal<T::ProofSystem, Mode = PublicOrSecret>, + AssetBalance: Equal<T::ProofSystem, Mode = PublicOrSecret>, + for<'i> AssetBalanceVar<T::ProofSystem>: AddAssign<&'i AssetBalanceVar<T::ProofSystem>>, + ContainmentProof<T::UtxoSet>: Alloc<T::ProofSystem, Mode = Secret>, { - // FIXME: Build secret transfer zero knowledge proof: + let mut ps = <T::ProofSystem as Default>::default(); - let mut proof_system = <T::ProofSystem as Default>::default(); - - // When we know the variables: + #[allow(clippy::needless_collect)] // NOTE: `ps` is being mutated, we need to collect. let senders = self .senders .iter() - .map(|s| s.as_variable(&mut proof_system, Derived)) - .collect::<Vec<_>>(); - let receivers = self - .receivers - .iter() - .map(|r| r.as_variable(&mut proof_system, Derived)) + .map(|sender| ps.allocate((sender, Derived))) .collect::<Vec<_>>(); - /* TODO: - // When we don't: - let senders = self - .senders - .iter() - .map(|_| SenderVariable::unknown(&mut proof_system)) - .collect::<Vec<_>>(); + #[allow(clippy::needless_collect)] // NOTE: `ps` is being mutated, we need to collect. let receivers = self .receivers .iter() - .map(|_| ReceiverVariable::unknown(&mut proof_system)) + .map(|receiver| ps.allocate((receiver, Derived))) .collect::<Vec<_>>(); - Self::build_proof_content(&mut proof_system, senders, receivers); - */ + Self::verify( + &mut ps, + commitment_scheme, + senders.into_iter(), + receivers.into_iter(), + ); - proof_system.finish().ok() + ps.finish().ok() } /// Converts `self` into its ledger post. #[inline] - pub fn into_post(self) -> Option<SecretTransferPost<T, SENDERS, RECEIVERS>> + pub fn into_post( + self, + commitment_scheme: &T::CommitmentScheme, + ) -> Option<SecretTransferPost<T, SENDERS, RECEIVERS>> where - AssetId: Equal<T::ProofSystem, PublicOrSecret>, - AssetBalance: Equal<T::ProofSystem, PublicOrSecret>, - ContainmentProof<T::UtxoSet>: Var<T::ProofSystem, Secret>, + AssetId: Equal<T::ProofSystem, Mode = PublicOrSecret>, + AssetBalance: Equal<T::ProofSystem, Mode = PublicOrSecret>, + for<'i> AssetBalanceVar<T::ProofSystem>: AddAssign<&'i AssetBalanceVar<T::ProofSystem>>, + ContainmentProof<T::UtxoSet>: Alloc<T::ProofSystem, Mode = Secret>, { - let validity_proof = self.generate_validity_proof()?; + let validity_proof = self.generate_validity_proof(commitment_scheme)?; Some(SecretTransferPost { sender_posts: array_map(self.senders, Sender::into_post), receiver_posts: array_map(self.receivers, Receiver::into_post), @@ -365,20 +339,6 @@ where } } -impl<T, const SENDERS: usize, const RECEIVERS: usize> From<SecretTransfer<T, SENDERS, RECEIVERS>> - for Option<SecretTransferPost<T, SENDERS, RECEIVERS>> -where - T: SecretTransferConfiguration, - AssetId: Equal<T::ProofSystem, PublicOrSecret>, - AssetBalance: Equal<T::ProofSystem, PublicOrSecret>, - ContainmentProof<T::UtxoSet>: Var<T::ProofSystem, Secret>, -{ - #[inline] - fn from(secret_transfer: SecretTransfer<T, SENDERS, RECEIVERS>) -> Self { - secret_transfer.into_post() - } -} - /// Transfer Protocol pub struct Transfer< T, @@ -400,9 +360,6 @@ impl<T, const SOURCES: usize, const SINKS: usize, const SENDERS: usize, const RE Transfer<T, SOURCES, SINKS, SENDERS, RECEIVERS> where T: SecretTransferConfiguration, - AssetId: Equal<T::ProofSystem, PublicOrSecret>, - AssetBalance: Equal<T::ProofSystem, PublicOrSecret>, - ContainmentProof<T::UtxoSet>: Var<T::ProofSystem, Secret>, { /// Builds a new [`Transfer`] from a [`PublicTransfer`] and a [`SecretTransfer`]. #[inline] @@ -415,10 +372,19 @@ where /// Converts `self` into its ledger post. #[inline] - pub fn into_post(self) -> Option<TransferPost<T, SOURCES, SINKS, SENDERS, RECEIVERS>> { + pub fn into_post( + self, + commitment_scheme: &T::CommitmentScheme, + ) -> Option<TransferPost<T, SOURCES, SINKS, SENDERS, RECEIVERS>> + where + AssetId: Equal<T::ProofSystem, Mode = PublicOrSecret>, + AssetBalance: Equal<T::ProofSystem, Mode = PublicOrSecret>, + for<'i> AssetBalanceVar<T::ProofSystem>: AddAssign<&'i AssetBalanceVar<T::ProofSystem>>, + ContainmentProof<T::UtxoSet>: Alloc<T::ProofSystem, Mode = Secret>, + { Some(TransferPost { public_transfer_post: self.public, - secret_transfer_post: self.secret.into_post()?, + secret_transfer_post: self.secret.into_post(commitment_scheme)?, }) } } diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index 18e9cec70..7f9307b47 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -24,17 +24,15 @@ use crate::{ asset::{Asset, AssetBalance, AssetId}, identity::{ - AssetParameters, Identity, IdentityConfiguration, OpenSpend, PublicKey, Receiver, - ShieldedIdentity, Spend, VoidNumberCommitment, VoidNumberGenerator, + AssetParameters, Identity, IdentityConfiguration, OpenSpend, Receiver, ShieldedIdentity, + Spend, }, keys::{self, DerivedSecretKeyGenerator, ExternalKeys, InternalKeys}, ledger::Ledger, transfer::{SecretTransfer, SecretTransferConfiguration}, }; use core::{convert::Infallible, fmt::Debug, hash::Hash}; -use manta_crypto::{ - commitment::Input as CommitmentInput, ies::EncryptedMessage, IntegratedEncryptionScheme, -}; +use manta_crypto::ies::{EncryptedMessage, IntegratedEncryptionScheme}; use rand::{ distributions::{Distribution, Standard}, CryptoRng, RngCore, @@ -276,7 +274,7 @@ where .external_keys_from_index(index.clone()) .zip(self.internal_keys_from_index(index)) .flat_map(move |(e, i)| [e, i]) - .take(gap_limit); + .take(2 * gap_limit); self.find_open_spend_from_iter(encrypted_asset, interleaved_keys) } @@ -347,8 +345,6 @@ where C: IdentityConfiguration<SecretKey = D::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: CommitmentInput<C::CommitmentScheme>, - VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, { self.next_external_identity() .map(move |identity| identity.into_shielded(commitment_scheme)) @@ -369,10 +365,6 @@ where I: IntegratedEncryptionScheme<Plaintext = Asset>, R: CryptoRng + RngCore + ?Sized, Standard: Distribution<AssetParameters<C>>, - PublicKey<C>: CommitmentInput<C::CommitmentScheme>, - VoidNumberGenerator<C>: CommitmentInput<C::CommitmentScheme>, - Asset: CommitmentInput<C::CommitmentScheme>, - VoidNumberCommitment<C>: CommitmentInput<C::CommitmentScheme>, { let (shielded_identity, spend) = self .next_internal_identity() @@ -397,8 +389,6 @@ where where T: SecretTransferConfiguration, R: CryptoRng + RngCore + ?Sized, - Asset: CommitmentInput<T::CommitmentScheme>, - VoidNumberCommitment<T>: CommitmentInput<T::CommitmentScheme>, { // TODO: spec: // 1. check that we have enough `asset` in the secret_assets map diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index ebad134c9..5662277e1 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -43,12 +43,9 @@ pub trait CommitmentScheme { } /// Commitment Input -pub trait Input<C> -where - C: CommitmentScheme + ?Sized, -{ - /// Extends the input buffer. - fn extend(&self, buffer: &mut C::InputBuffer); +pub trait Input<T>: CommitmentScheme { + /// Extends the input buffer with `input`. + fn extend(buffer: &mut Self::InputBuffer, input: &T); } /// Commitment Builder @@ -78,11 +75,11 @@ where /// Updates the builder with new `input`. #[inline] - pub fn update<I>(mut self, input: &I) -> Self + pub fn update<T>(mut self, input: &T) -> Self where - I: Input<C>, + C: Input<T>, { - input.extend(&mut self.input_buffer); + C::extend(&mut self.input_buffer, input); self } diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index 1a48baedb..389cb616e 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -17,157 +17,335 @@ //! Constraint Proof Systems // TODO: Add derive trait to implement `Alloc` for structs (and enums?). +// TODO: Add more convenience functions for allocating unknown variables. // TODO: How to do verification systems? Should it be a separate trait or part of `ProofSystem`? -use core::convert::TryFrom; +use core::convert::{Infallible, TryFrom}; +/* /// Boolean Variable Type pub type Bool<P> = <P as BooleanSystem>::Bool; +*/ /// Variable Type -pub type Variable<T, P, K = (), U = K> = <T as Var<P, K, U>>::Variable; +pub type Var<T, P> = <P as HasVariable<T>>::Variable; +/// Allocation Mode Type +pub type Mode<T, P> = <Var<T, P> as Variable<P>>::Mode; + +/// Known Allocation Mode Type +pub type KnownMode<T, P> = <Mode<T, P> as AllocationMode>::Known; + +/// Known Allocation Mode Type +pub type UnknownMode<T, P> = <Mode<T, P> as AllocationMode>::Unknown; + +/// Boolean Variable Type +pub type Bool<P> = Var<bool, P>; + +/* TODO: /// Character Variable Type -pub type Char<P, K = (), U = K> = Variable<char, P, K, U>; +pub type Char<P, K = (), U = K> = Var<char, P, K, U>; /// Signed 8-bit Integer Variable Type -pub type I8<P, K = (), U = K> = Variable<i8, P, K, U>; +pub type I8<P, K = (), U = K> = Var<i8, P, K, U>; /// Signed 16-bit Integer Variable Type -pub type I16<P, K = (), U = K> = Variable<i16, P, K, U>; +pub type I16<P, K = (), U = K> = Var<i16, P, K, U>; /// Signed 32-bit Integer Variable Type -pub type I32<P, K = (), U = K> = Variable<i32, P, K, U>; +pub type I32<P, K = (), U = K> = Var<i32, P, K, U>; /// Signed 64-bit Integer Variable Type -pub type I64<P, K = (), U = K> = Variable<i64, P, K, U>; +pub type I64<P, K = (), U = K> = Var<i64, P, K, U>; /// Signed 128-bit Integer Variable Type -pub type I128<P, K = (), U = K> = Variable<i128, P, K, U>; +pub type I128<P, K = (), U = K> = Var<i128, P, K, U>; /// Unsigned 8-bit Integer Variable Type -pub type U8<P, K = (), U = K> = Variable<u8, P, K, U>; +pub type U8<P, K = (), U = K> = Var<u8, P, K, U>; /// Unsigned 16-bit Integer Variable Type -pub type U16<P, K = (), U = K> = Variable<u16, P, K, U>; +pub type U16<P, K = (), U = K> = Var<u16, P, K, U>; /// Unsigned 32-bit Integer Variable Type -pub type U32<P, K = (), U = K> = Variable<u32, P, K, U>; +pub type U32<P, K = (), U = K> = Var<u32, P, K, U>; /// Unsigned 64-bit Integer Variable Type -pub type U64<P, K = (), U = K> = Variable<u64, P, K, U>; +pub type U64<P, K = (), U = K> = Var<u64, P, K, U>; /// Unsigned 128-bit Integer Variable Type -pub type U128<P, K = (), U = K> = Variable<u128, P, K, U>; +pub type U128<P, K = (), U = K> = Var<u128, P, K, U>; +*/ + +/// Allocation Mode +pub trait AllocationMode { + /// Known Allocation Mode + type Known; + + /// Unknown Allocation Mode + type Unknown; + + /* TODO: Do we want this? + /// Upgrades the unknown allocation mode to the known allocation mode. + #[inline] + fn upgrade_mode(mode: Self::Unknown) -> Self::Known; + + /// Upgrades the value from an unknown to known allocation. + #[inline] + fn upgrade<P, T>(value: &T, mode: Self::Unknown) -> Allocation<T, P> + where + T: Alloc<P, Mode = Self>, + { + Allocation::Known(value, Self::upgrade_mode(mode)) + } + */ +} /// Allocation Entry -pub enum Allocation<T, Known = (), Unknown = Known> { +pub enum Allocation<'t, T, P> +where + T: ?Sized, + P: HasVariable<T> + ?Sized, +{ /// Known Value - Value(T, Known), - + Known( + /// Allocation Value + &'t T, + /// Allocation Mode + KnownMode<T, P>, + ), /// Unknown Value - Unknown(Unknown), + Unknown( + /// Allocation Mode + UnknownMode<T, P>, + ), } -/// Variable Reflection Trait -pub trait IsVariable<P, Known = (), Unknown = Known>: Sized +impl<'t, T, P> Allocation<'t, T, P> where - P: ?Sized, + T: ?Sized, + P: HasVariable<T> + ?Sized, { - /// Origin Type of the Variable - type Type: Var<P, Known, Unknown, Variable = Self>; + /// Returns `true` if `self` represents a known variable. + #[inline] + pub fn is_known(&self) -> bool { + matches!(self, Self::Known(..)) + } + + /// Returns `true` if `self` represents an unknown value. + #[inline] + pub fn is_unknown(&self) -> bool { + matches!(self, Self::Unknown(..)) + } - /// Returns a new variable with `value`. + /// Converts `self` into a possible known value. #[inline] - fn new_variable(value: &Self::Type, ps: &mut P, mode: Known) -> Self { - value.as_variable(ps, mode) + pub fn known(self) -> Option<(&'t T, KnownMode<T, P>)> { + match self { + Self::Known(value, mode) => Some((value, mode)), + _ => None, + } } - /// Returns a new variable with an unknown value. + /// Converts `self` into a possibly unknown value. #[inline] - fn new_unknown(ps: &mut P, mode: Unknown) -> Self { - Self::Type::unknown(ps, mode) + pub fn unknown(self) -> Option<UnknownMode<T, P>> { + match self { + Self::Unknown(mode) => Some(mode), + _ => None, + } + } + + /// Maps the underlying allocation value if it is known. + #[inline] + pub fn map<'u, U, Q, F>(self, f: F) -> Allocation<'u, U, Q> + where + U: Alloc<Q, Mode = Mode<T, P>>, + Q: ?Sized, + F: FnOnce(&'t T) -> &'u U, + { + match self { + Self::Known(value, mode) => Allocation::Known(f(value), mode), + Self::Unknown(mode) => Allocation::Unknown(mode), + } + } + + /// Allocates a variable into `ps` using `self` as the allocation. + #[inline] + pub fn into_variable(self, ps: &mut P) -> Var<T, P> { + ps.allocate(self) + } +} + +impl<'t, T, P> From<(&'t T, KnownMode<T, P>)> for Allocation<'t, T, P> +where + T: ?Sized, + P: HasVariable<T> + ?Sized, +{ + #[inline] + fn from((value, mode): (&'t T, KnownMode<T, P>)) -> Self { + Self::Known(value, mode) } } -/// Variable Trait -pub trait Var<P, Known = (), Unknown = Known> +/// Variable Allocation Trait +pub trait Alloc<P> where P: ?Sized, { - /// Variable Object - type Variable: IsVariable<P, Known, Unknown, Type = Self>; + /// Allocation Mode + type Mode: AllocationMode; - /// Returns a new variable with value `self`. - fn as_variable(&self, ps: &mut P, mode: Known) -> Self::Variable; + /// Variable Object Type + type Variable: Variable<P, Mode = Self::Mode, Type = Self>; - /// Returns a new variable with an unknown value. - fn unknown(ps: &mut P, mode: Unknown) -> Self::Variable; + /// Allocates a new variable into `ps` with the given `allocation`. + fn variable<'t>(ps: &mut P, allocation: impl Into<Allocation<'t, Self, P>>) -> Self::Variable + where + Self: 't; } -/// Boolean Constraint System -pub trait BooleanSystem { - /// Boolean Variable Type - type Bool: IsVariable<Self, Self::KnownBool, Self::UnknownBool, Type = bool>; +/// Variable Reflection Trait +pub trait Variable<P>: Sized +where + P: ?Sized, +{ + /// Allocation Mode + type Mode: AllocationMode; + + /// Origin Type of the Variable + type Type: Alloc<P, Mode = Self::Mode, Variable = Self>; + + /// Allocates a new variable into `ps` with the given `allocation`. + #[inline] + fn new<'t>(ps: &mut P, allocation: impl Into<Allocation<'t, Self::Type, P>>) -> Self + where + Self::Type: 't, + { + Self::Type::variable(ps, allocation) + } +} - /// Known Boolean Allocation Mode Type - type KnownBool; +/// Variable Reflection Trait +pub trait HasVariable<T> +where + T: ?Sized, +{ + /// Allocation Mode + type Mode: AllocationMode; + + /// Variable Object Type + type Variable: Variable<Self, Mode = Self::Mode, Type = T>; + + /// Allocates a new variable into `self` with the given `allocation`. + #[inline] + fn allocate<'t>(&mut self, allocation: impl Into<Allocation<'t, T, Self>>) -> Self::Variable + where + T: 't, + { + Self::Variable::new(self, allocation) + } + + /// Allocates a new unknown variable into `self` with the given `mode`. + #[inline] + fn allocate_unknown( + &mut self, + mode: <Self::Mode as AllocationMode>::Unknown, + ) -> Self::Variable { + self.allocate(Allocation::Unknown(mode)) + } +} - /// Unknown Boolean Allocation Mode Type - type UnknownBool; +impl<P, T> HasVariable<T> for P +where + P: ?Sized, + T: Alloc<P> + ?Sized, +{ + type Mode = T::Mode; + type Variable = T::Variable; +} - /// Allocates a known boolean with value `b` with the given `mode`. - fn known_bool(&mut self, b: bool, mode: Self::KnownBool) -> Self::Bool; +/* TODO[remove]: +impl<P, T> Alloc<P> for T +where + P: ?Sized + HasVariable<T>, +{ + type Mode = <P as HasVariable<T>>::Mode; - /// Allocates an unknown boolean with the given `mode`. - fn unknown_bool(&mut self, mode: Self::UnknownBool) -> Self::Bool; + type Variable = <P as HasVariable<T>>::Variable; - /// Allocates a new variable with the given `value`. #[inline] - fn allocate_variable<T, K, U>(&mut self, value: T, mode: K) -> T::Variable + fn allocate<'t>(ps: &mut P, allocation: impl Into<Allocation<'t, Self, P>>) -> Self::Variable where - T: Var<Self, K, U>, + Self: 't, { - value.as_variable(self, mode) + // TODO: Self::Variable::allocate(ps, allocation) + todo!() } +} +*/ + +/// Boolean Constraint System +pub trait BooleanSystem: HasVariable<bool> { + /* TODO[remove]: + /// Boolean Variable Type + type Bool: Variable<Self, Type = bool>; + + /// Allocates a new boolean into `self` with the given `allocation`. + fn allocate_bool<'t>( + &mut self, + allocation: impl Into<Allocation<'t, bool, Self>>, + ) -> Self::Bool; - /// Allocates a new variable with an unknown value. + /// Allocates a new variable into `self` with the given `allocation`. #[inline] - fn allocate_unknown<T, K, U>(&mut self, mode: U) -> T::Variable + fn allocate<'t, T>(&mut self, allocation: impl Into<Allocation<'t, T, Self>>) -> T::Variable where - T: Var<Self, K, U>, + Self: HasVariable<T>, + T: 't, { - T::unknown(self, mode) + T::allocate(self, allocation) } + */ /// Asserts that `b` is `true`. - fn assert(&mut self, b: Self::Bool); + fn assert(&mut self, b: Bool<Self>); /// Asserts that all the booleans in `iter` are `true`. #[inline] fn assert_all<I>(&mut self, iter: I) where - I: IntoIterator<Item = Self::Bool>, + I: IntoIterator<Item = Bool<Self>>, { iter.into_iter().for_each(move |b| self.assert(b)) } + /// Generates a boolean that represents the fact that `lhs` and `rhs` may be equal. + #[inline] + fn eq<V>(&mut self, lhs: &V, rhs: &V) -> Bool<Self> + where + V: Variable<Self>, + V::Type: Equal<Self>, + { + V::Type::eq(self, lhs, rhs) + } + /// Asserts that `lhs` and `rhs` are equal. #[inline] - fn assert_eq<V, K, U>(&mut self, lhs: &V, rhs: &V) + fn assert_eq<V>(&mut self, lhs: &V, rhs: &V) where - V: IsVariable<Self, K, U>, - V::Type: Equal<Self, K, U>, + V: Variable<Self>, + V::Type: Equal<Self>, { V::Type::assert_eq(self, lhs, rhs) } /// Asserts that all the elements in `iter` are equal to some `base` element. #[inline] - fn assert_all_eq_to_base<'t, V, K, U, I>(&mut self, base: &'t V, iter: I) + fn assert_all_eq_to_base<'t, V, I>(&mut self, base: &'t V, iter: I) where - V: 't + IsVariable<Self, K, U>, - V::Type: Equal<Self, K, U>, + V: 't + Variable<Self>, + V::Type: Equal<Self>, I: IntoIterator<Item = &'t V>, { V::Type::assert_all_eq_to_base(self, base, iter) @@ -175,40 +353,39 @@ pub trait BooleanSystem { /// Asserts that all the elements in `iter` are equal. #[inline] - fn assert_all_eq<'t, V, K, U, I>(&mut self, iter: I) + fn assert_all_eq<'t, V, I>(&mut self, iter: I) where - V: 't + IsVariable<Self, K, U>, - V::Type: Equal<Self, K, U>, + V: 't + Variable<Self>, + V::Type: Equal<Self>, I: IntoIterator<Item = &'t V>, { V::Type::assert_all_eq(self, iter) } } -impl<P> Var<P, P::KnownBool, P::UnknownBool> for bool +/* TODO[remove]: +impl<P> Alloc<P> for bool where P: BooleanSystem + ?Sized, { - type Variable = P::Bool; + type Mode = <P::Bool as Variable<P>>::Mode; - #[inline] - fn as_variable(&self, ps: &mut P, mode: P::KnownBool) -> Self::Variable { - ps.known_bool(*self, mode) - } + type Variable = P::Bool; #[inline] - fn unknown(ps: &mut P, mode: P::UnknownBool) -> Self::Variable { - ps.unknown_bool(mode) + fn allocate<'t>(ps: &mut P, allocation: impl Into<Allocation<'t, bool, P>>) -> Self::Variable { + ps.allocate_bool(allocation) } } +*/ /// Equality Trait -pub trait Equal<P, Known = (), Unknown = Known>: Var<P, Known, Unknown> +pub trait Equal<P>: Alloc<P> where P: BooleanSystem + ?Sized, { /// Generates a boolean that represents the fact that `lhs` and `rhs` may be equal. - fn eq(ps: &mut P, lhs: &Self::Variable, rhs: &Self::Variable) -> P::Bool; + fn eq(ps: &mut P, lhs: &Self::Variable, rhs: &Self::Variable) -> Bool<P>; /// Asserts that `lhs` and `rhs` are equal. #[inline] @@ -267,10 +444,34 @@ where #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Derived; +impl AllocationMode for Derived { + type Known = Derived; + type Unknown = Derived; +} + +/// Constant Allocation Mode +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Constant<T = Public>(T) +where + T: AllocationMode; + +impl<T> AllocationMode for Constant<T> +where + T: AllocationMode, +{ + type Known = T::Known; + type Unknown = Infallible; +} + /// Always Public Allocation Mode #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Public; +impl AllocationMode for Public { + type Known = Public; + type Unknown = Public; +} + impl From<Derived> for Public { #[inline] fn from(d: Derived) -> Self { @@ -283,6 +484,11 @@ impl From<Derived> for Public { #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Secret; +impl AllocationMode for Secret { + type Known = Secret; + type Unknown = Secret; +} + impl From<Derived> for Secret { #[inline] fn from(d: Derived) -> Self { @@ -333,6 +539,11 @@ impl PublicOrSecret { } } +impl AllocationMode for PublicOrSecret { + type Known = PublicOrSecret; + type Unknown = PublicOrSecret; +} + impl Default for PublicOrSecret { #[inline] fn default() -> Self { @@ -379,3 +590,25 @@ impl TryFrom<PublicOrSecret> for Secret { } } } + +impl<T> From<Constant<T>> for PublicOrSecret +where + T: AllocationMode + Into<PublicOrSecret>, +{ + #[inline] + fn from(c: Constant<T>) -> Self { + c.0.into() + } +} + +impl<T> TryFrom<PublicOrSecret> for Constant<T> +where + T: AllocationMode + TryFrom<PublicOrSecret>, +{ + type Error = T::Error; + + #[inline] + fn try_from(pos: PublicOrSecret) -> Result<Self, Self::Error> { + T::try_from(pos).map(Self) + } +} diff --git a/manta-crypto/src/set/constraint.rs b/manta-crypto/src/set/constraint.rs new file mode 100644 index 000000000..a4d214499 --- /dev/null +++ b/manta-crypto/src/set/constraint.rs @@ -0,0 +1,38 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Sets and Verified Set Constraint Systems + +use crate::{ + constraint::{Alloc, BooleanSystem, Var, Variable}, + set::{ContainmentProof, VerifiedSet}, +}; + +/// Containment Proof Variable +pub trait ContainmentProofVariable<P>: + Variable<P, Type = ContainmentProof<Self::VerifiedSet>> +where + P: BooleanSystem + ?Sized, +{ + /// Item Type + type Item: Alloc<P>; + + /// Verified Set + type VerifiedSet: VerifiedSet<Item = Self::Item>; + + /// Asserts that `self` is a witness to the fact that `item` is stored in the verified set. + fn assert_verified(&self, item: &Var<Self::Item, P>, ps: &mut P); +} diff --git a/manta-crypto/src/set.rs b/manta-crypto/src/set/mod.rs similarity index 99% rename from manta-crypto/src/set.rs rename to manta-crypto/src/set/mod.rs index a66aa5511..af28a2bb9 100644 --- a/manta-crypto/src/set.rs +++ b/manta-crypto/src/set/mod.rs @@ -16,6 +16,8 @@ //! Sets and Verified Sets +pub mod constraint; + pub(crate) mod prelude { #[doc(inline)] pub use super::{Set, VerifiedSet}; From ca9d52de3aca261c818d8bf9d013de4dcd053209 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 12 Sep 2021 18:32:16 -0400 Subject: [PATCH 028/275] finish type-level constraints --- manta-accounting/src/asset.rs | 40 ++-- manta-accounting/src/identity.rs | 131 ++++++------ manta-accounting/src/transfer.rs | 29 ++- manta-crypto/src/constraint.rs | 321 +++++++++++++++++++---------- manta-crypto/src/set/constraint.rs | 30 +-- 5 files changed, 348 insertions(+), 203 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 9afca46b7..84c536e25 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -32,7 +32,7 @@ use derive_more::{ Add, AddAssign, Display, Div, DivAssign, From, Mul, MulAssign, Product, Sub, SubAssign, Sum, }; use manta_crypto::constraint::{ - Alloc, Allocation, HasVariable, PublicOrSecret, Secret, Var, Variable, + unknown, Alloc, Allocation, HasVariable, PublicOrSecret, Secret, Var, Variable, }; use manta_util::{ array_map, fallible_array_map, into_array_unchecked, ByteAccumulator, ConcatBytes, @@ -345,7 +345,8 @@ pub type AssetBalanceVar<P> = Var<AssetBalance, P>; pub struct AssetVar<P> where P: HasVariable<AssetId, Mode = PublicOrSecret> - + HasVariable<AssetBalance, Mode = PublicOrSecret>, + + HasVariable<AssetBalance, Mode = PublicOrSecret> + + ?Sized, { /// Asset Id pub id: AssetIdVar<P>, @@ -354,10 +355,24 @@ where pub value: AssetBalanceVar<P>, } +impl<P> AssetVar<P> +where + P: HasVariable<AssetId, Mode = PublicOrSecret> + + HasVariable<AssetBalance, Mode = PublicOrSecret> + + ?Sized, +{ + /// Builds a new [`AssetVar`] from an `id` and a `value`. + #[inline] + pub fn new(id: AssetIdVar<P>, value: AssetBalanceVar<P>) -> Self { + Self { id, value } + } +} + impl<P> Variable<P> for AssetVar<P> where P: HasVariable<AssetId, Mode = PublicOrSecret> - + HasVariable<AssetBalance, Mode = PublicOrSecret>, + + HasVariable<AssetBalance, Mode = PublicOrSecret> + + ?Sized, { type Mode = Secret; type Type = Asset; @@ -366,7 +381,8 @@ where impl<P> Alloc<P> for Asset where P: HasVariable<AssetId, Mode = PublicOrSecret> - + HasVariable<AssetBalance, Mode = PublicOrSecret>, + + HasVariable<AssetBalance, Mode = PublicOrSecret> + + ?Sized, { type Mode = Secret; @@ -378,14 +394,14 @@ where Self: 't, { match allocation.into() { - Allocation::Known(Asset { id, value }, mode) => Self::Variable { - id: ps.allocate((id, mode.into())), - value: ps.allocate((value, mode.into())), - }, - Allocation::Unknown(mode) => Self::Variable { - id: <P as HasVariable<AssetId>>::allocate_unknown(ps, mode.into()), - value: <P as HasVariable<AssetBalance>>::allocate_unknown(ps, mode.into()), - }, + Allocation::Known(this, mode) => Self::Variable::new( + ps.allocate_known(&this.id, mode), + ps.allocate_known(&this.value, mode), + ), + Allocation::Unknown(mode) => Self::Variable::new( + unknown::<AssetId, _>(ps, mode.into()), + unknown::<AssetBalance, _>(ps, mode.into()), + ), } } } diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index e1647119f..c1c4c6878 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -30,11 +30,11 @@ use core::{convert::Infallible, fmt::Debug, hash::Hash, marker::PhantomData}; use manta_crypto::{ commitment::{CommitmentScheme, Input as CommitmentInput}, constraint::{ - Alloc, Allocation, BooleanSystem, Constant, Derived, Equal, HasVariable, ProofSystem, - Public, PublicOrSecret, Secret, Var, Variable, + unknown, Alloc, Allocation, BooleanSystem, Constant, Derived, Equal, HasVariable, + ProofSystem, Public, PublicOrSecret, Secret, Var, Variable, }, ies::{self, EncryptedMessage, IntegratedEncryptionScheme}, - set::{ContainmentProof, VerifiedSet}, + set::{constraint::VerifiedSetProofSystem, ContainmentProof, VerifiedSet}, PseudorandomFunctionFamily, }; use rand::{ @@ -388,6 +388,25 @@ where pub utxo_randomness: UtxoRandomnessVar<C>, } +impl<C> AssetParametersVar<C> +where + C: IdentityProofSystemConfiguration, +{ + /// Builds a new [`AssetParametersVar`] from parameter variables. + #[inline] + pub fn new( + void_number_generator: VoidNumberGeneratorVar<C>, + void_number_commitment_randomness: VoidNumberCommitmentRandomnessVar<C>, + utxo_randomness: UtxoRandomnessVar<C>, + ) -> Self { + Self { + void_number_generator, + void_number_commitment_randomness, + utxo_randomness, + } + } +} + impl<C> Variable<C::ProofSystem> for AssetParametersVar<C> where C: IdentityProofSystemConfiguration, @@ -413,20 +432,16 @@ where Self: 't, { match allocation.into() { - Allocation::Known( - AssetParameters { - void_number_generator, - void_number_commitment_randomness, - utxo_randomness, - }, - mode, - ) => Self::Variable { - void_number_generator: ps.allocate((void_number_generator, mode)), - void_number_commitment_randomness: ps - .allocate((void_number_commitment_randomness, mode)), - utxo_randomness: ps.allocate((utxo_randomness, mode)), - }, - _ => todo!(), + Allocation::Known(this, mode) => Self::Variable::new( + this.void_number_generator.as_known(ps, mode), + this.void_number_commitment_randomness.as_known(ps, mode), + this.utxo_randomness.as_known(ps, mode), + ), + Allocation::Unknown(mode) => Self::Variable::new( + VoidNumberGenerator::<C>::unknown(ps, mode), + VoidNumberCommitmentRandomness::<C>::unknown(ps, mode), + UtxoRandomness::<C>::unknown(ps, mode), + ), } } } @@ -1203,7 +1218,8 @@ pub struct SenderVar<C, S> where C: IdentityProofSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - ContainmentProof<S>: Alloc<C::ProofSystem, Mode = Secret>, + C::ProofSystem: + VerifiedSetProofSystem<S, ItemMode = PublicOrSecret, ContainmentProofMode = Derived>, { /// Secret Key secret_key: SecretKeyVar<C>, @@ -1235,7 +1251,8 @@ impl<C, S> SenderVar<C, S> where C: IdentityProofSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - ContainmentProof<S>: Alloc<C::ProofSystem, Mode = Secret>, + C::ProofSystem: + VerifiedSetProofSystem<S, ItemMode = PublicOrSecret, ContainmentProofMode = Derived>, { /// Checks if `self` is a well-formed sender and returns its asset. #[inline] @@ -1312,7 +1329,7 @@ where // is_path(cm, path, root) == true // ``` // where public: {root}, secret: {cm, path}. - // FIXME: ps.assert(&self.utxo_containment_proof.verify(self.utxo)); + // FIXME: self.utxo_containment_proof.assert_verified(&self.utxo, ps); self.asset } @@ -1322,7 +1339,8 @@ impl<C, S> Variable<C::ProofSystem> for SenderVar<C, S> where C: IdentityProofSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - ContainmentProof<S>: Alloc<C::ProofSystem, Mode = Secret>, + C::ProofSystem: + VerifiedSetProofSystem<S, ItemMode = PublicOrSecret, ContainmentProofMode = Derived>, { type Mode = Derived; type Type = Sender<C, S>; @@ -1332,7 +1350,8 @@ impl<C, S> Alloc<C::ProofSystem> for Sender<C, S> where C: IdentityProofSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - ContainmentProof<S>: Alloc<C::ProofSystem, Mode = Secret>, + C::ProofSystem: + VerifiedSetProofSystem<S, ItemMode = PublicOrSecret, ContainmentProofMode = Derived>, { type Mode = Derived; type Variable = SenderVar<C, S>; @@ -1346,29 +1365,26 @@ where Self: 't, { match allocation.into() { - Allocation::Known( - Sender { - secret_key, - public_key, - asset, - parameters, - void_number, - void_number_commitment, - utxo, - utxo_containment_proof, - }, - mode, - ) => Self::Variable { - secret_key: ps.allocate((secret_key, mode.into())), - public_key: ps.allocate((public_key, Secret.into())), - asset: ps.allocate((asset, mode.into())), - parameters: ps.allocate((parameters, mode.into())), - void_number: ps.allocate((void_number, Public.into())), - void_number_commitment: ps.allocate((void_number_commitment, Public.into())), - utxo: ps.allocate((utxo, Secret.into())), - utxo_containment_proof: ps.allocate((utxo_containment_proof, mode.into())), + Allocation::Known(this, mode) => Self::Variable { + secret_key: this.secret_key.as_known(ps, mode), + public_key: this.public_key.as_known(ps, Secret), + asset: this.asset.as_known(ps, mode), + parameters: this.parameters.as_known(ps, mode), + void_number: this.void_number.as_known(ps, Public), + void_number_commitment: ps.allocate_known(&this.void_number_commitment, Public), + utxo: ps.allocate_known(&this.utxo, Secret), + utxo_containment_proof: ps.allocate_known(&this.utxo_containment_proof, mode), + }, + Allocation::Unknown(mode) => Self::Variable { + secret_key: SecretKey::<C>::unknown(ps, mode), + public_key: PublicKey::<C>::unknown(ps, Secret), + asset: Asset::unknown(ps, mode), + parameters: AssetParameters::unknown(ps, mode), + void_number: VoidNumber::<C>::unknown(ps, Public), + void_number_commitment: unknown::<VoidNumberCommitment<C>, _>(ps, Public.into()), + utxo: unknown::<Utxo<C>, _>(ps, Secret.into()), + utxo_containment_proof: unknown::<ContainmentProof<S>, _>(ps, mode), }, - _ => todo!(), } } } @@ -1609,23 +1625,20 @@ where Self: 't, { match allocation.into() { - Allocation::Known( - Receiver { - asset, - utxo_randomness, - void_number_commitment, - utxo, - .. - }, - mode, - ) => Self::Variable { - asset: ps.allocate((asset, mode.into())), - utxo_randomness: ps.allocate((utxo_randomness, mode.into())), - void_number_commitment: ps.allocate((void_number_commitment, Secret.into())), - utxo: ps.allocate((utxo, Public.into())), + Allocation::Known(this, mode) => Self::Variable { + asset: this.asset.as_known(ps, mode), + utxo_randomness: this.utxo_randomness.as_known(ps, mode), + void_number_commitment: this.void_number_commitment.as_known(ps, Secret), + utxo: this.utxo.as_known(ps, Public), + __: PhantomData, + }, + Allocation::Unknown(mode) => Self::Variable { + asset: Asset::unknown(ps, mode), + utxo_randomness: UtxoRandomness::<C>::unknown(ps, mode), + void_number_commitment: VoidNumberCommitment::<C>::unknown(ps, Secret), + utxo: Utxo::<C>::unknown(ps, Public), __: PhantomData, }, - _ => todo!(), } } } diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 25553dd4e..10fd23c3c 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -28,11 +28,10 @@ use alloc::vec::Vec; use core::ops::AddAssign; use manta_crypto::{ constraint::{ - Alloc, BooleanSystem, Derived, Equal, HasVariable, ProofSystem, Public, PublicOrSecret, - Secret, + BooleanSystem, Derived, Equal, HasVariable, ProofSystem, Public, PublicOrSecret, Secret, }, ies::{EncryptedMessage, IntegratedEncryptionScheme}, - set::{ContainmentProof, VerifiedSet}, + set::{constraint::VerifiedSetProofSystem, VerifiedSet}, }; use manta_util::{array_map, mixed_chain, Either}; use rand::{ @@ -206,7 +205,11 @@ where AssetId: Equal<T::ProofSystem, Mode = PublicOrSecret>, AssetBalance: Equal<T::ProofSystem, Mode = PublicOrSecret>, for<'i> AssetBalanceVar<T::ProofSystem>: AddAssign<&'i AssetBalanceVar<T::ProofSystem>>, - ContainmentProof<T::UtxoSet>: Alloc<T::ProofSystem, Mode = Secret>, + T::ProofSystem: VerifiedSetProofSystem< + T::UtxoSet, + ItemMode = PublicOrSecret, + ContainmentProofMode = Derived, + >, { let commitment_scheme = ps.allocate((commitment_scheme, Public)); @@ -241,7 +244,11 @@ where AssetId: Equal<T::ProofSystem, Mode = PublicOrSecret>, AssetBalance: Equal<T::ProofSystem, Mode = PublicOrSecret>, for<'i> AssetBalanceVar<T::ProofSystem>: AddAssign<&'i AssetBalanceVar<T::ProofSystem>>, - ContainmentProof<T::UtxoSet>: Alloc<T::ProofSystem, Mode = Secret>, + T::ProofSystem: VerifiedSetProofSystem< + T::UtxoSet, + ItemMode = PublicOrSecret, + ContainmentProofMode = Derived, + >, { let mut ps = <T::ProofSystem as Default>::default(); @@ -279,7 +286,11 @@ where AssetId: Equal<T::ProofSystem, Mode = PublicOrSecret>, AssetBalance: Equal<T::ProofSystem, Mode = PublicOrSecret>, for<'i> AssetBalanceVar<T::ProofSystem>: AddAssign<&'i AssetBalanceVar<T::ProofSystem>>, - ContainmentProof<T::UtxoSet>: Alloc<T::ProofSystem, Mode = Secret>, + T::ProofSystem: VerifiedSetProofSystem< + T::UtxoSet, + ItemMode = PublicOrSecret, + ContainmentProofMode = Derived, + >, { let validity_proof = self.generate_validity_proof(commitment_scheme)?; Some(SecretTransferPost { @@ -380,7 +391,11 @@ where AssetId: Equal<T::ProofSystem, Mode = PublicOrSecret>, AssetBalance: Equal<T::ProofSystem, Mode = PublicOrSecret>, for<'i> AssetBalanceVar<T::ProofSystem>: AddAssign<&'i AssetBalanceVar<T::ProofSystem>>, - ContainmentProof<T::UtxoSet>: Alloc<T::ProofSystem, Mode = Secret>, + T::ProofSystem: VerifiedSetProofSystem< + T::UtxoSet, + ItemMode = PublicOrSecret, + ContainmentProofMode = Derived, + >, { Some(TransferPost { public_transfer_post: self.public, diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index 389cb616e..89cf02ed7 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -16,16 +16,19 @@ //! Constraint Proof Systems +// TODO: Add derive macros to all the enums/structs here. // TODO: Add derive trait to implement `Alloc` for structs (and enums?). // TODO: Add more convenience functions for allocating unknown variables. // TODO: How to do verification systems? Should it be a separate trait or part of `ProofSystem`? +// TODO: Should we (can we?) seal the `HasVariable` trait so no one implements it by accident? -use core::convert::{Infallible, TryFrom}; +use core::{ + convert::{Infallible, TryFrom}, + marker::PhantomData, +}; -/* -/// Boolean Variable Type -pub type Bool<P> = <P as BooleanSystem>::Bool; -*/ +#[doc(inline)] +pub use types::Bool; /// Variable Type pub type Var<T, P> = <P as HasVariable<T>>::Variable; @@ -39,44 +42,6 @@ pub type KnownMode<T, P> = <Mode<T, P> as AllocationMode>::Known; /// Known Allocation Mode Type pub type UnknownMode<T, P> = <Mode<T, P> as AllocationMode>::Unknown; -/// Boolean Variable Type -pub type Bool<P> = Var<bool, P>; - -/* TODO: -/// Character Variable Type -pub type Char<P, K = (), U = K> = Var<char, P, K, U>; - -/// Signed 8-bit Integer Variable Type -pub type I8<P, K = (), U = K> = Var<i8, P, K, U>; - -/// Signed 16-bit Integer Variable Type -pub type I16<P, K = (), U = K> = Var<i16, P, K, U>; - -/// Signed 32-bit Integer Variable Type -pub type I32<P, K = (), U = K> = Var<i32, P, K, U>; - -/// Signed 64-bit Integer Variable Type -pub type I64<P, K = (), U = K> = Var<i64, P, K, U>; - -/// Signed 128-bit Integer Variable Type -pub type I128<P, K = (), U = K> = Var<i128, P, K, U>; - -/// Unsigned 8-bit Integer Variable Type -pub type U8<P, K = (), U = K> = Var<u8, P, K, U>; - -/// Unsigned 16-bit Integer Variable Type -pub type U16<P, K = (), U = K> = Var<u16, P, K, U>; - -/// Unsigned 32-bit Integer Variable Type -pub type U32<P, K = (), U = K> = Var<u32, P, K, U>; - -/// Unsigned 64-bit Integer Variable Type -pub type U64<P, K = (), U = K> = Var<u64, P, K, U>; - -/// Unsigned 128-bit Integer Variable Type -pub type U128<P, K = (), U = K> = Var<u128, P, K, U>; -*/ - /// Allocation Mode pub trait AllocationMode { /// Known Allocation Mode @@ -101,6 +66,16 @@ pub trait AllocationMode { */ } +impl AllocationMode for Infallible { + type Known = Infallible; + type Unknown = Infallible; +} + +impl AllocationMode for () { + type Known = (); + type Unknown = (); +} + /// Allocation Entry pub enum Allocation<'t, T, P> where @@ -160,8 +135,8 @@ where #[inline] pub fn map<'u, U, Q, F>(self, f: F) -> Allocation<'u, U, Q> where - U: Alloc<Q, Mode = Mode<T, P>>, - Q: ?Sized, + U: ?Sized, + Q: HasVariable<U, Mode = Mode<T, P>> + ?Sized, F: FnOnce(&'t T) -> &'u U, { match self { @@ -203,6 +178,25 @@ where fn variable<'t>(ps: &mut P, allocation: impl Into<Allocation<'t, Self, P>>) -> Self::Variable where Self: 't; + + /// Allocates a new known variable into `ps` with the given `mode`. + #[inline] + fn as_known( + &self, + ps: &mut P, + mode: impl Into<<Self::Mode as AllocationMode>::Known>, + ) -> Self::Variable { + Self::variable(ps, (self, mode.into())) + } + + /// Allocates a new unknown variable into `ps` with the given `mode`. + #[inline] + fn unknown( + ps: &mut P, + mode: impl Into<<Self::Mode as AllocationMode>::Unknown>, + ) -> Self::Variable { + Self::variable(ps, Allocation::Unknown(mode.into())) + } } /// Variable Reflection Trait @@ -224,6 +218,22 @@ where { Self::Type::variable(ps, allocation) } + + /// Allocates a new known variable into `ps` with the given `mode`. + #[inline] + fn new_known( + ps: &mut P, + value: &Self::Type, + mode: impl Into<<Self::Mode as AllocationMode>::Known>, + ) -> Self { + Self::new(ps, (value, mode.into())) + } + + /// Allocates a new unknown variable into `ps` with the given `mode`. + #[inline] + fn new_unknown(ps: &mut P, mode: impl Into<<Self::Mode as AllocationMode>::Unknown>) -> Self { + Self::new(ps, Allocation::Unknown(mode.into())) + } } /// Variable Reflection Trait @@ -246,13 +256,23 @@ where Self::Variable::new(self, allocation) } + /// Allocates a new known variable into `self` with the given `mode`. + #[inline] + fn allocate_known( + &mut self, + value: &T, + mode: impl Into<<Self::Mode as AllocationMode>::Known>, + ) -> Self::Variable { + self.allocate((value, mode.into())) + } + /// Allocates a new unknown variable into `self` with the given `mode`. #[inline] fn allocate_unknown( &mut self, - mode: <Self::Mode as AllocationMode>::Unknown, + mode: impl Into<<Self::Mode as AllocationMode>::Unknown>, ) -> Self::Variable { - self.allocate(Allocation::Unknown(mode)) + self.allocate(Allocation::Unknown(mode.into())) } } @@ -265,49 +285,56 @@ where type Variable = T::Variable; } -/* TODO[remove]: -impl<P, T> Alloc<P> for T +/// Allocates a new unknown variable into `ps` with the given `mode`. +#[inline] +pub fn known<T, P>(ps: &mut P, value: &T, mode: KnownMode<T, P>) -> Var<T, P> where - P: ?Sized + HasVariable<T>, + T: ?Sized, + P: HasVariable<T> + ?Sized, { - type Mode = <P as HasVariable<T>>::Mode; + ps.allocate_known(value, mode) +} - type Variable = <P as HasVariable<T>>::Variable; +/// Allocates a new unknown variable into `ps` with the given `mode`. +#[inline] +pub fn unknown<T, P>(ps: &mut P, mode: UnknownMode<T, P>) -> Var<T, P> +where + T: ?Sized, + P: HasVariable<T> + ?Sized, +{ + ps.allocate_unknown(mode) +} + +impl<T, P> Alloc<P> for PhantomData<T> +where + T: ?Sized, + P: ?Sized, +{ + type Mode = (); + + type Variable = PhantomData<T>; #[inline] - fn allocate<'t>(ps: &mut P, allocation: impl Into<Allocation<'t, Self, P>>) -> Self::Variable + fn variable<'t>(ps: &mut P, allocation: impl Into<Allocation<'t, Self, P>>) -> Self::Variable where Self: 't, { - // TODO: Self::Variable::allocate(ps, allocation) - todo!() + let _ = (ps, allocation); + PhantomData } } -*/ + +impl<T, P> Variable<P> for PhantomData<T> +where + T: ?Sized, + P: ?Sized, +{ + type Mode = (); + type Type = PhantomData<T>; +} /// Boolean Constraint System pub trait BooleanSystem: HasVariable<bool> { - /* TODO[remove]: - /// Boolean Variable Type - type Bool: Variable<Self, Type = bool>; - - /// Allocates a new boolean into `self` with the given `allocation`. - fn allocate_bool<'t>( - &mut self, - allocation: impl Into<Allocation<'t, bool, Self>>, - ) -> Self::Bool; - - /// Allocates a new variable into `self` with the given `allocation`. - #[inline] - fn allocate<'t, T>(&mut self, allocation: impl Into<Allocation<'t, T, Self>>) -> T::Variable - where - Self: HasVariable<T>, - T: 't, - { - T::allocate(self, allocation) - } - */ - /// Asserts that `b` is `true`. fn assert(&mut self, b: Bool<Self>); @@ -363,22 +390,6 @@ pub trait BooleanSystem: HasVariable<bool> { } } -/* TODO[remove]: -impl<P> Alloc<P> for bool -where - P: BooleanSystem + ?Sized, -{ - type Mode = <P::Bool as Variable<P>>::Mode; - - type Variable = P::Bool; - - #[inline] - fn allocate<'t>(ps: &mut P, allocation: impl Into<Allocation<'t, bool, P>>) -> Self::Variable { - ps.allocate_bool(allocation) - } -} -*/ - /// Equality Trait pub trait Equal<P>: Alloc<P> where @@ -440,6 +451,23 @@ where fn verify(proof: &P::Proof) -> bool; } +/* TODO: Should we have this mode? +/// Compound Allocation Mode +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum CompoundMode<Known, Unknown> { + /// Known Allocation Mode + Known(Known), + + /// Unknown Allocation Mode + Unknown(Unknown), +} + +impl<Known, Unknown> AllocationMode for CompoundMode<Known, Unknown> { + type Known = Known; + type Unknown = Unknown; +} +*/ + /// Derived Allocation Mode #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Derived; @@ -449,18 +477,11 @@ impl AllocationMode for Derived { type Unknown = Derived; } -/// Constant Allocation Mode -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Constant<T = Public>(T) -where - T: AllocationMode; - -impl<T> AllocationMode for Constant<T> -where - T: AllocationMode, -{ - type Known = T::Known; - type Unknown = Infallible; +impl From<Derived> for () { + #[inline] + fn from(d: Derived) -> Self { + let _ = d; + } } /// Always Public Allocation Mode @@ -497,6 +518,33 @@ impl From<Derived> for Secret { } } +/// Constant Allocation Mode +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Constant<T = Public>( + /// Underyling Allocation Mode + pub T, +) +where + T: AllocationMode; + +impl<T> AllocationMode for Constant<T> +where + T: AllocationMode, +{ + type Known = T::Known; + type Unknown = Infallible; +} + +impl<T> From<Derived> for Constant<T> +where + T: AllocationMode + From<Derived>, +{ + #[inline] + fn from(d: Derived) -> Self { + Self(d.into()) + } +} + /// Public/Secret Allocation Mode #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum PublicOrSecret { @@ -564,10 +612,7 @@ impl TryFrom<PublicOrSecret> for Public { #[inline] fn try_from(pos: PublicOrSecret) -> Result<Self, Self::Error> { - match pos { - PublicOrSecret::Public => Ok(Self), - PublicOrSecret::Secret => Err(Secret), - } + pos.public().ok_or(Secret) } } @@ -584,10 +629,7 @@ impl TryFrom<PublicOrSecret> for Secret { #[inline] fn try_from(pos: PublicOrSecret) -> Result<Self, Self::Error> { - match pos { - PublicOrSecret::Secret => Ok(Self), - PublicOrSecret::Public => Err(Public), - } + pos.secret().ok_or(Public) } } @@ -612,3 +654,56 @@ where T::try_from(pos).map(Self) } } + +/// Type Aliases +pub mod types { + use super::*; + + /// Boolean Variable Type + pub type Bool<P> = Var<bool, P>; + + /// Character Variable Type + pub type Char<P> = Var<char, P>; + + /// 32-bit Floating Point Variable Type + pub type F32<P> = Var<f32, P>; + + /// 64-bit Floating Point Variable Type + pub type F64<P> = Var<f64, P>; + + /// Signed 8-bit Integer Variable Type + pub type I8<P> = Var<i8, P>; + + /// Signed 16-bit Integer Variable Type + pub type I16<P> = Var<i16, P>; + + /// Signed 32-bit Integer Variable Type + pub type I32<P> = Var<i32, P>; + + /// Signed 64-bit Integer Variable Type + pub type I64<P> = Var<i64, P>; + + /// Signed 128-bit Integer Variable Type + pub type I128<P> = Var<i128, P>; + + /// Pointer-Sized Integer Variable Type + pub type Isize<P> = Var<isize, P>; + + /// Unsigned 8-bit Integer Variable Type + pub type U8<P> = Var<u8, P>; + + /// Unsigned 16-bit Integer Variable Type + pub type U16<P> = Var<u16, P>; + + /// Unsigned 32-bit Integer Variable Type + pub type U32<P> = Var<u32, P>; + + /// Unsigned 64-bit Integer Variable Type + pub type U64<P> = Var<u64, P>; + + /// Unsigned 128-bit Integer Variable Type + pub type U128<P> = Var<u128, P>; + + /// Pointer-Sized Unsigned Integer Variable Type + pub type Usize<P> = Var<usize, P>; +} diff --git a/manta-crypto/src/set/constraint.rs b/manta-crypto/src/set/constraint.rs index a4d214499..5f7b134ca 100644 --- a/manta-crypto/src/set/constraint.rs +++ b/manta-crypto/src/set/constraint.rs @@ -14,25 +14,31 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Sets and Verified Set Constraint Systems +//! Sets and Verified Set Proof Systems use crate::{ - constraint::{Alloc, BooleanSystem, Var, Variable}, + constraint::{AllocationMode, BooleanSystem, HasVariable, Var}, set::{ContainmentProof, VerifiedSet}, }; -/// Containment Proof Variable -pub trait ContainmentProofVariable<P>: - Variable<P, Type = ContainmentProof<Self::VerifiedSet>> +/// Verified Set Proof System +pub trait VerifiedSetProofSystem<S>: + BooleanSystem + + HasVariable<S::Item, Mode = Self::ItemMode> + + HasVariable<ContainmentProof<S>, Mode = Self::ContainmentProofMode> where - P: BooleanSystem + ?Sized, + S: VerifiedSet, { - /// Item Type - type Item: Alloc<P>; + /// Item Allocation Mode + type ItemMode: AllocationMode; - /// Verified Set - type VerifiedSet: VerifiedSet<Item = Self::Item>; + /// Containment Proof Allocation Mode + type ContainmentProofMode: AllocationMode; - /// Asserts that `self` is a witness to the fact that `item` is stored in the verified set. - fn assert_verified(&self, item: &Var<Self::Item, P>, ps: &mut P); + /// Asserts that `proof` is a witness to the fact that `item` is stored in the verified set. + fn assert_verified( + &mut self, + proof: &Var<ContainmentProof<S>, Self>, + item: &Var<S::Item, Self>, + ); } From ec2aad77f48bcbc2fb60378c5031573d7ff2302e Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 13 Sep 2021 02:15:12 -0400 Subject: [PATCH 029/275] update verified set proofs, add proofs to ledger --- manta-accounting/src/identity.rs | 64 +++++--- manta-accounting/src/ledger.rs | 40 ++++- manta-accounting/src/transfer.rs | 54 ++++--- manta-crypto/src/constraint.rs | 42 +---- manta-crypto/src/set/constraint.rs | 146 +++++++++++++++++- manta-pay/src/accounting/ledger.rs | 43 ++++-- .../src/crypto/constraint/proof_system.rs | 50 ++++++ 7 files changed, 332 insertions(+), 107 deletions(-) diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index c1c4c6878..3787c316d 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -30,11 +30,14 @@ use core::{convert::Infallible, fmt::Debug, hash::Hash, marker::PhantomData}; use manta_crypto::{ commitment::{CommitmentScheme, Input as CommitmentInput}, constraint::{ - unknown, Alloc, Allocation, BooleanSystem, Constant, Derived, Equal, HasVariable, + unknown, Alloc, AllocEq, Allocation, BooleanSystem, Constant, Derived, HasVariable, ProofSystem, Public, PublicOrSecret, Secret, Var, Variable, }, ies::{self, EncryptedMessage, IntegratedEncryptionScheme}, - set::{constraint::VerifiedSetProofSystem, ContainmentProof, VerifiedSet}, + set::{ + constraint::{ContainmentProofVar, VerifiedSetProofSystem}, + ContainmentProof, VerifiedSet, + }, PseudorandomFunctionFamily, }; use rand::{ @@ -83,7 +86,7 @@ pub trait IdentityProofSystemConfiguration { type PseudorandomFunctionFamilyInput: Alloc<Self::ProofSystem, Mode = Secret>; /// Pseudorandom Function Family Output - type PseudorandomFunctionFamilyOutput: Equal<Self::ProofSystem, Mode = PublicOrSecret>; + type PseudorandomFunctionFamilyOutput: AllocEq<Self::ProofSystem, Mode = PublicOrSecret>; /// Pseudorandom Function Family type PseudorandomFunctionFamily: PseudorandomFunctionFamily< @@ -103,7 +106,7 @@ pub trait IdentityProofSystemConfiguration { type CommitmentSchemeRandomness: Alloc<Self::ProofSystem, Mode = Secret>; /// Commitment Scheme Output - type CommitmentSchemeOutput: Equal<Self::ProofSystem, Mode = PublicOrSecret>; + type CommitmentSchemeOutput: AllocEq<Self::ProofSystem, Mode = PublicOrSecret>; /// Commitment Scheme type CommitmentScheme: CommitmentScheme< @@ -232,7 +235,7 @@ pub type UtxoVar<C> = CommitmentSchemeOutputVar<C>; /// UTXO Containment Proof Variable Type pub type UtxoContainmentProofVar<C, S> = - Var<ContainmentProof<S>, <C as IdentityProofSystemConfiguration>::ProofSystem>; + ContainmentProofVar<S, <C as IdentityProofSystemConfiguration>::ProofSystem>; /// Generates a void number commitment from `public_key`, `void_number_generator`, and /// `void_number_commitment_randomness`. @@ -1218,8 +1221,12 @@ pub struct SenderVar<C, S> where C: IdentityProofSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - C::ProofSystem: - VerifiedSetProofSystem<S, ItemMode = PublicOrSecret, ContainmentProofMode = Derived>, + C::ProofSystem: VerifiedSetProofSystem< + S, + ItemMode = PublicOrSecret, + PublicMode = Public, + SecretMode = Secret, + >, { /// Secret Key secret_key: SecretKeyVar<C>, @@ -1243,7 +1250,6 @@ where utxo: UtxoVar<C>, /// UTXO Containment Proof - // TODO: think about what the mode type for this should be utxo_containment_proof: UtxoContainmentProofVar<C, S>, } @@ -1251,8 +1257,12 @@ impl<C, S> SenderVar<C, S> where C: IdentityProofSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - C::ProofSystem: - VerifiedSetProofSystem<S, ItemMode = PublicOrSecret, ContainmentProofMode = Derived>, + C::ProofSystem: VerifiedSetProofSystem< + S, + ItemMode = PublicOrSecret, + PublicMode = Public, + SecretMode = Secret, + >, { /// Checks if `self` is a well-formed sender and returns its asset. #[inline] @@ -1261,13 +1271,13 @@ where ps: &mut C::ProofSystem, commitment_scheme: &C::CommitmentSchemeVar, ) -> AssetVar<C::ProofSystem> { - // FIXME: Implement well-formedness check: + // Well-formed check: // - // 1. pk = PRF(sk, 0) [public: (), secret: (pk, sk)] - // 2. vn = PRF(sk, rho) [public: (vn), secret: (sk, rho)] - // 3. k = COM(pk || rho, r) [public: (k), secret: (pk, rho, r)] - // 4. cm = COM(asset_id || asset_value || k, s) [public: (), secret: (cm, asset, k, s)] - // 5. merkle_path(cm, path, root) == true [public: (root), secret: (cm, path)] + // 1. pk = PRF(sk, 0) [public: (), secret: (pk, sk)] + // 2. vn = PRF(sk, rho) [public: (vn), secret: (sk, rho)] + // 3. k = COM(pk || rho, r) [public: (k), secret: (pk, rho, r)] + // 4. cm = COM(asset || k, s) [public: (), secret: (cm, asset, k, s)] + // 5. is_path(cm, path, root) == true [public: (root), secret: (cm, path)] // // FIXME: should `k` be private or not? @@ -1329,7 +1339,7 @@ where // is_path(cm, path, root) == true // ``` // where public: {root}, secret: {cm, path}. - // FIXME: self.utxo_containment_proof.assert_verified(&self.utxo, ps); + self.utxo_containment_proof.assert_validity(&self.utxo, ps); self.asset } @@ -1339,8 +1349,12 @@ impl<C, S> Variable<C::ProofSystem> for SenderVar<C, S> where C: IdentityProofSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - C::ProofSystem: - VerifiedSetProofSystem<S, ItemMode = PublicOrSecret, ContainmentProofMode = Derived>, + C::ProofSystem: VerifiedSetProofSystem< + S, + ItemMode = PublicOrSecret, + PublicMode = Public, + SecretMode = Secret, + >, { type Mode = Derived; type Type = Sender<C, S>; @@ -1350,8 +1364,12 @@ impl<C, S> Alloc<C::ProofSystem> for Sender<C, S> where C: IdentityProofSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - C::ProofSystem: - VerifiedSetProofSystem<S, ItemMode = PublicOrSecret, ContainmentProofMode = Derived>, + C::ProofSystem: VerifiedSetProofSystem< + S, + ItemMode = PublicOrSecret, + PublicMode = Public, + SecretMode = Secret, + >, { type Mode = Derived; type Variable = SenderVar<C, S>; @@ -1373,7 +1391,7 @@ where void_number: this.void_number.as_known(ps, Public), void_number_commitment: ps.allocate_known(&this.void_number_commitment, Public), utxo: ps.allocate_known(&this.utxo, Secret), - utxo_containment_proof: ps.allocate_known(&this.utxo_containment_proof, mode), + utxo_containment_proof: this.utxo_containment_proof.as_known(ps, mode), }, Allocation::Unknown(mode) => Self::Variable { secret_key: SecretKey::<C>::unknown(ps, mode), @@ -1383,7 +1401,7 @@ where void_number: VoidNumber::<C>::unknown(ps, Public), void_number_commitment: unknown::<VoidNumberCommitment<C>, _>(ps, Public.into()), utxo: unknown::<Utxo<C>, _>(ps, Secret.into()), - utxo_containment_proof: unknown::<ContainmentProof<S>, _>(ps, mode), + utxo_containment_proof: ContainmentProof::<S>::unknown(ps, mode), }, } } diff --git a/manta-accounting/src/ledger.rs b/manta-accounting/src/ledger.rs index 84b488c02..516d12bac 100644 --- a/manta-accounting/src/ledger.rs +++ b/manta-accounting/src/ledger.rs @@ -17,7 +17,10 @@ //! Ledger Abstraction use crate::identity::{ReceiverPostError, SenderPostError}; -use manta_crypto::{Set, VerifiedSet}; +use manta_crypto::{ + constraint::ProofSystem, + set::{Set, VerifiedSet}, +}; /// Ledger Trait pub trait Ledger { @@ -33,6 +36,9 @@ pub trait Ledger { /// Encrypted Asset Type type EncryptedAsset; + /// Proof System + type ProofSystem: ProofSystem; + /// Returns a shared reference to the [`UtxoSet`](Self::UtxoSet). fn utxos(&self) -> &Self::UtxoSet; @@ -85,6 +91,24 @@ pub trait Ledger { &mut self, encrypted_asset: Self::EncryptedAsset, ) -> Result<(), Self::EncryptedAsset>; + + /// Checks that the given `proof` is valid. + fn check_proof( + &self, + proof: <Self::ProofSystem as ProofSystem>::Proof, + ) -> Result<(), ProofPostError<Self>>; +} + +/// Proof Post Error +pub enum ProofPostError<L> +where + L: Ledger + ?Sized, +{ + /// Proof was invalid + InvalidProof( + /// Proof + <L::ProofSystem as ProofSystem>::Proof, + ), } /// Ledger Post Error @@ -98,8 +122,8 @@ where /// Receiver Post Error Receiver(ReceiverPostError<L>), - /// Invalid Secret Transfer - InvalidSecretTransfer, + /// Proof Post Error + Proof(ProofPostError<L>), } impl<L> From<SenderPostError<L>> for PostError<L> @@ -121,3 +145,13 @@ where Self::Receiver(err) } } + +impl<L> From<ProofPostError<L>> for PostError<L> +where + L: Ledger + ?Sized, +{ + #[inline] + fn from(err: ProofPostError<L>) -> Self { + Self::Proof(err) + } +} diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 10fd23c3c..47137e9dd 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -28,7 +28,7 @@ use alloc::vec::Vec; use core::ops::AddAssign; use manta_crypto::{ constraint::{ - BooleanSystem, Derived, Equal, HasVariable, ProofSystem, Public, PublicOrSecret, Secret, + AllocEq, BooleanSystem, Derived, HasVariable, ProofSystem, Public, PublicOrSecret, Secret, }, ies::{EncryptedMessage, IntegratedEncryptionScheme}, set::{constraint::VerifiedSetProofSystem, VerifiedSet}, @@ -139,10 +139,10 @@ where T: SecretTransferConfiguration, { /// Maximum Number of Senders - pub const MAXIMUM_SENDER_COUNT: usize = 10; + pub const MAXIMUM_SENDER_COUNT: usize = 32; /// Maximum Number of Receivers - pub const MAXIMUM_RECEIVER_COUNT: usize = 10; + pub const MAXIMUM_RECEIVER_COUNT: usize = 32; /// Builds a new [`SecretTransfer`]. #[inline] @@ -150,6 +150,7 @@ where senders: [SecretSender<T>; SENDERS], receivers: [SecretReceiver<T>; RECEIVERS], ) -> Self { + // FIXME: Should we have arrays of senders and receivers or use vectors? if SENDERS > Self::MAXIMUM_SENDER_COUNT { panic!("Allocated too many senders."); } @@ -159,7 +160,6 @@ where Self { senders, receivers } } - /* TODO: do we still want these? /// Checks that the asset ids of all the senders and receivers matches. #[inline] pub fn has_unique_asset_id(&self) -> bool { @@ -190,7 +190,6 @@ where pub fn is_balanced(&self) -> bool { self.sender_sum().eq(&self.receiver_sum()) } - */ /// Builds constraints for secret transfer validity proof. #[inline] @@ -202,13 +201,14 @@ where ) where S: IntoIterator<Item = SecretSenderVar<T>>, R: IntoIterator<Item = SecretReceiverVar<T>>, - AssetId: Equal<T::ProofSystem, Mode = PublicOrSecret>, - AssetBalance: Equal<T::ProofSystem, Mode = PublicOrSecret>, - for<'i> AssetBalanceVar<T::ProofSystem>: AddAssign<&'i AssetBalanceVar<T::ProofSystem>>, + AssetId: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, + AssetBalance: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, + AssetBalanceVar<T::ProofSystem>: AddAssign<AssetBalanceVar<T::ProofSystem>>, T::ProofSystem: VerifiedSetProofSystem< T::UtxoSet, ItemMode = PublicOrSecret, - ContainmentProofMode = Derived, + PublicMode = Public, + SecretMode = Secret, >, { let commitment_scheme = ps.allocate((commitment_scheme, Public)); @@ -220,12 +220,12 @@ where let asset_ids = mixed_chain(senders, receivers, |c| match c { Either::Left(sender) => { let asset = sender.get_well_formed_asset(ps, &commitment_scheme); - sender_sum += &asset.value; + sender_sum += asset.value; asset.id } Either::Right(receiver) => { let asset = receiver.get_well_formed_asset(ps, &commitment_scheme); - receiver_sum += &asset.value; + receiver_sum += asset.value; asset.id } }) @@ -241,13 +241,14 @@ where commitment_scheme: &T::CommitmentScheme, ) -> Option<<T::ProofSystem as ProofSystem>::Proof> where - AssetId: Equal<T::ProofSystem, Mode = PublicOrSecret>, - AssetBalance: Equal<T::ProofSystem, Mode = PublicOrSecret>, - for<'i> AssetBalanceVar<T::ProofSystem>: AddAssign<&'i AssetBalanceVar<T::ProofSystem>>, + AssetId: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, + AssetBalance: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, + AssetBalanceVar<T::ProofSystem>: AddAssign<AssetBalanceVar<T::ProofSystem>>, T::ProofSystem: VerifiedSetProofSystem< T::UtxoSet, ItemMode = PublicOrSecret, - ContainmentProofMode = Derived, + PublicMode = Public, + SecretMode = Secret, >, { let mut ps = <T::ProofSystem as Default>::default(); @@ -283,13 +284,14 @@ where commitment_scheme: &T::CommitmentScheme, ) -> Option<SecretTransferPost<T, SENDERS, RECEIVERS>> where - AssetId: Equal<T::ProofSystem, Mode = PublicOrSecret>, - AssetBalance: Equal<T::ProofSystem, Mode = PublicOrSecret>, - for<'i> AssetBalanceVar<T::ProofSystem>: AddAssign<&'i AssetBalanceVar<T::ProofSystem>>, + AssetId: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, + AssetBalance: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, + AssetBalanceVar<T::ProofSystem>: AddAssign<AssetBalanceVar<T::ProofSystem>>, T::ProofSystem: VerifiedSetProofSystem< T::UtxoSet, ItemMode = PublicOrSecret, - ContainmentProofMode = Derived, + PublicMode = Public, + SecretMode = Secret, >, { let validity_proof = self.generate_validity_proof(commitment_scheme)?; @@ -336,6 +338,7 @@ where Utxo = Utxo<T>, UtxoSet = T::UtxoSet, EncryptedAsset = EncryptedMessage<T::IntegratedEncryptionScheme>, + ProofSystem = T::ProofSystem, > + ?Sized, { for sender_post in IntoIterator::into_iter(self.sender_posts) { @@ -344,8 +347,7 @@ where for receiver_post in IntoIterator::into_iter(self.receiver_posts) { receiver_post.post(ledger)?; } - // FIXME: proof.post(ledger)?; - // - returns `PostError::InvalidSecretTransfer` on error? + ledger.check_proof(self.validity_proof)?; Ok(()) } } @@ -388,13 +390,14 @@ where commitment_scheme: &T::CommitmentScheme, ) -> Option<TransferPost<T, SOURCES, SINKS, SENDERS, RECEIVERS>> where - AssetId: Equal<T::ProofSystem, Mode = PublicOrSecret>, - AssetBalance: Equal<T::ProofSystem, Mode = PublicOrSecret>, - for<'i> AssetBalanceVar<T::ProofSystem>: AddAssign<&'i AssetBalanceVar<T::ProofSystem>>, + AssetId: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, + AssetBalance: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, + AssetBalanceVar<T::ProofSystem>: AddAssign<AssetBalanceVar<T::ProofSystem>>, T::ProofSystem: VerifiedSetProofSystem< T::UtxoSet, ItemMode = PublicOrSecret, - ContainmentProofMode = Derived, + PublicMode = Public, + SecretMode = Secret, >, { Some(TransferPost { @@ -435,6 +438,7 @@ where Utxo = Utxo<T>, EncryptedAsset = EncryptedMessage<T::IntegratedEncryptionScheme>, UtxoSet = T::UtxoSet, + ProofSystem = T::ProofSystem, > + ?Sized, { // FIXME: self.public_transfer_post.post(ledger)?; diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index 89cf02ed7..0ce8ef903 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -49,21 +49,6 @@ pub trait AllocationMode { /// Unknown Allocation Mode type Unknown; - - /* TODO: Do we want this? - /// Upgrades the unknown allocation mode to the known allocation mode. - #[inline] - fn upgrade_mode(mode: Self::Unknown) -> Self::Known; - - /// Upgrades the value from an unknown to known allocation. - #[inline] - fn upgrade<P, T>(value: &T, mode: Self::Unknown) -> Allocation<T, P> - where - T: Alloc<P, Mode = Self>, - { - Allocation::Known(value, Self::upgrade_mode(mode)) - } - */ } impl AllocationMode for Infallible { @@ -352,7 +337,7 @@ pub trait BooleanSystem: HasVariable<bool> { fn eq<V>(&mut self, lhs: &V, rhs: &V) -> Bool<Self> where V: Variable<Self>, - V::Type: Equal<Self>, + V::Type: AllocEq<Self>, { V::Type::eq(self, lhs, rhs) } @@ -362,7 +347,7 @@ pub trait BooleanSystem: HasVariable<bool> { fn assert_eq<V>(&mut self, lhs: &V, rhs: &V) where V: Variable<Self>, - V::Type: Equal<Self>, + V::Type: AllocEq<Self>, { V::Type::assert_eq(self, lhs, rhs) } @@ -372,7 +357,7 @@ pub trait BooleanSystem: HasVariable<bool> { fn assert_all_eq_to_base<'t, V, I>(&mut self, base: &'t V, iter: I) where V: 't + Variable<Self>, - V::Type: Equal<Self>, + V::Type: AllocEq<Self>, I: IntoIterator<Item = &'t V>, { V::Type::assert_all_eq_to_base(self, base, iter) @@ -383,7 +368,7 @@ pub trait BooleanSystem: HasVariable<bool> { fn assert_all_eq<'t, V, I>(&mut self, iter: I) where V: 't + Variable<Self>, - V::Type: Equal<Self>, + V::Type: AllocEq<Self>, I: IntoIterator<Item = &'t V>, { V::Type::assert_all_eq(self, iter) @@ -391,7 +376,7 @@ pub trait BooleanSystem: HasVariable<bool> { } /// Equality Trait -pub trait Equal<P>: Alloc<P> +pub trait AllocEq<P>: Alloc<P> where P: BooleanSystem + ?Sized, { @@ -451,23 +436,6 @@ where fn verify(proof: &P::Proof) -> bool; } -/* TODO: Should we have this mode? -/// Compound Allocation Mode -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum CompoundMode<Known, Unknown> { - /// Known Allocation Mode - Known(Known), - - /// Unknown Allocation Mode - Unknown(Unknown), -} - -impl<Known, Unknown> AllocationMode for CompoundMode<Known, Unknown> { - type Known = Known; - type Unknown = Unknown; -} -*/ - /// Derived Allocation Mode #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Derived; diff --git a/manta-crypto/src/set/constraint.rs b/manta-crypto/src/set/constraint.rs index 5f7b134ca..ed70eb814 100644 --- a/manta-crypto/src/set/constraint.rs +++ b/manta-crypto/src/set/constraint.rs @@ -17,28 +17,158 @@ //! Sets and Verified Set Proof Systems use crate::{ - constraint::{AllocationMode, BooleanSystem, HasVariable, Var}, + constraint::{ + unknown, Alloc, Allocation, AllocationMode, BooleanSystem, Derived, HasVariable, Mode, Var, + Variable, + }, set::{ContainmentProof, VerifiedSet}, }; +use core::marker::PhantomData; + +/// Containment Proof Allocation Mode Entry +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +pub struct ContainmentProofModeEntry<PublicMode, SecretMode> { + /// Public Input Allocation Mode + pub public: PublicMode, + + /// Secret Witness Allocation Mode + pub secret: SecretMode, +} + +impl<PublicMode, SecretMode> ContainmentProofModeEntry<PublicMode, SecretMode> { + /// Builds a new [`ContainmentProofModeEntry`] from a `public` mode and a `secret` mode. + #[inline] + pub fn new(public: PublicMode, secret: SecretMode) -> Self { + Self { public, secret } + } +} + +impl<PublicMode, SecretMode> From<Derived> for ContainmentProofModeEntry<PublicMode, SecretMode> +where + PublicMode: From<Derived>, + SecretMode: From<Derived>, +{ + #[inline] + fn from(d: Derived) -> Self { + Self::new(d.into(), d.into()) + } +} + +/// Containment Proof Allocation Mode +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct ContainmentProofMode<PublicMode, SecretMode>(PhantomData<(PublicMode, SecretMode)>) +where + PublicMode: AllocationMode, + SecretMode: AllocationMode; + +impl<PublicMode, SecretMode> AllocationMode for ContainmentProofMode<PublicMode, SecretMode> +where + PublicMode: AllocationMode, + SecretMode: AllocationMode, +{ + type Known = ContainmentProofModeEntry<PublicMode::Known, SecretMode::Known>; + type Unknown = ContainmentProofModeEntry<PublicMode::Unknown, SecretMode::Unknown>; +} + +/// Containment Proof Variable +pub struct ContainmentProofVar<S, P> +where + S: VerifiedSet + ?Sized, + P: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, +{ + /// Public Input + public_input: Var<S::Public, P>, + + /// Secret Witness + secret_witness: Var<S::Secret, P>, +} + +impl<S, P> ContainmentProofVar<S, P> +where + S: VerifiedSet + ?Sized, + P: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, +{ + /// Builds a new [`ContainmentProofVar`] from `public_input` and `secret_witness`. + #[inline] + pub fn new(public_input: Var<S::Public, P>, secret_witness: Var<S::Secret, P>) -> Self { + Self { + public_input, + secret_witness, + } + } + + /// Asserts that `self` is a valid proof to the fact that `item` is stored in the verified set. + #[inline] + pub fn assert_validity(&self, item: &Var<S::Item, P>, ps: &mut P) + where + P: VerifiedSetProofSystem<S>, + { + ps.assert_validity(&self.public_input, &self.secret_witness, item) + } +} + +impl<S, P> Variable<P> for ContainmentProofVar<S, P> +where + S: VerifiedSet + ?Sized, + P: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, +{ + type Mode = ContainmentProofMode<Mode<S::Public, P>, Mode<S::Secret, P>>; + type Type = ContainmentProof<S>; +} + +impl<S, P> Alloc<P> for ContainmentProof<S> +where + S: VerifiedSet + ?Sized, + P: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, +{ + type Mode = ContainmentProofMode<Mode<S::Public, P>, Mode<S::Secret, P>>; + + type Variable = ContainmentProofVar<S, P>; + + #[inline] + fn variable<'t>(ps: &mut P, allocation: impl Into<Allocation<'t, Self, P>>) -> Self::Variable + where + Self: 't, + { + match allocation.into() { + Allocation::Known(this, mode) => Self::Variable::new( + ps.allocate_known(&this.public_input, mode.public), + ps.allocate_known(&this.secret_witness, mode.secret), + ), + Allocation::Unknown(mode) => Self::Variable::new( + unknown::<S::Public, _>(ps, mode.public), + unknown::<S::Secret, _>(ps, mode.secret), + ), + } + } +} /// Verified Set Proof System pub trait VerifiedSetProofSystem<S>: BooleanSystem + HasVariable<S::Item, Mode = Self::ItemMode> - + HasVariable<ContainmentProof<S>, Mode = Self::ContainmentProofMode> + + HasVariable<S::Public, Mode = Self::PublicMode> + + HasVariable<S::Secret, Mode = Self::SecretMode> where - S: VerifiedSet, + S: VerifiedSet + ?Sized, { /// Item Allocation Mode type ItemMode: AllocationMode; - /// Containment Proof Allocation Mode - type ContainmentProofMode: AllocationMode; + /// Public Input Allocation Mode + type PublicMode: AllocationMode; + + /// Secret Witness Allocation Mode + type SecretMode: AllocationMode; - /// Asserts that `proof` is a witness to the fact that `item` is stored in the verified set. - fn assert_verified( + /// Asserts that `public_input` and `secret_witness` form a proof to the fact that `item` is + /// stored in the verified set. + fn assert_validity( &mut self, - proof: &Var<ContainmentProof<S>, Self>, + public_input: &Var<S::Public, Self>, + secret_witness: &Var<S::Secret, Self>, item: &Var<S::Item, Self>, ); } diff --git a/manta-pay/src/accounting/ledger.rs b/manta-pay/src/accounting/ledger.rs index a654109e6..a6ed09e34 100644 --- a/manta-pay/src/accounting/ledger.rs +++ b/manta-pay/src/accounting/ledger.rs @@ -16,7 +16,10 @@ //! Ledger Implementation +// FIXME: Use more type-safe definitions for `VoidNumber` and `Utxo`. + use crate::crypto::{ + constraint::ArkProofSystem, ies::EncryptedAsset, merkle_tree::{self, MerkleTree, Path, Root}, }; @@ -25,10 +28,16 @@ use blake2::{ digest::{Update, VariableOutput}, VarBlake2s, }; -use manta_accounting::Ledger as LedgerTrait; -use manta_crypto::set::{ContainmentProof, Set, VerifiedSet}; +use manta_accounting::{Ledger as LedgerTrait, ProofPostError}; +use manta_crypto::{ + constraint::ProofSystem, + set::{ContainmentProof, Set, VerifiedSet}, +}; use manta_util::into_array_unchecked; +/// Void Number +type VoidNumber = [u8; 32]; + /// Unspent Transaction Output type Utxo = [u8; 32]; @@ -153,17 +162,17 @@ impl VerifiedSet for UtxoSet { /// Ledger pub struct Ledger { /// Void Numbers - void_numbers: (), + void_numbers: Vec<VoidNumber>, /// Unspent Transaction Outputs utxos: UtxoSet, /// Encrypted Assets - encrypted_assets: (), + encrypted_assets: Vec<EncryptedAsset>, } impl LedgerTrait for Ledger { - type VoidNumber = (); + type VoidNumber = VoidNumber; type Utxo = Utxo; @@ -171,6 +180,8 @@ impl LedgerTrait for Ledger { type EncryptedAsset = EncryptedAsset; + type ProofSystem = ArkProofSystem; + #[inline] fn utxos(&self) -> &Self::UtxoSet { &self.utxos @@ -178,9 +189,7 @@ impl LedgerTrait for Ledger { #[inline] fn is_unspent(&self, void_number: &Self::VoidNumber) -> bool { - // TODO: !self.void_numbers.contains(void_number) - let _ = void_number; - todo!() + !self.void_numbers.contains(void_number) } #[inline] @@ -188,8 +197,11 @@ impl LedgerTrait for Ledger { &mut self, void_number: Self::VoidNumber, ) -> Result<(), Self::VoidNumber> { - let _ = void_number; - todo!() + if self.void_numbers.contains(&void_number) { + return Err(void_number); + } + self.void_numbers.push(void_number); + Ok(()) } #[inline] @@ -202,7 +214,16 @@ impl LedgerTrait for Ledger { &mut self, encrypted_asset: Self::EncryptedAsset, ) -> Result<(), Self::EncryptedAsset> { - let _ = encrypted_asset; + self.encrypted_assets.push(encrypted_asset); + Ok(()) + } + + #[inline] + fn check_proof( + &self, + proof: <Self::ProofSystem as ProofSystem>::Proof, + ) -> Result<(), ProofPostError<Self>> { + let _ = proof; todo!() } } diff --git a/manta-pay/src/crypto/constraint/proof_system.rs b/manta-pay/src/crypto/constraint/proof_system.rs index cf98df82c..0f7cfe4f5 100644 --- a/manta-pay/src/crypto/constraint/proof_system.rs +++ b/manta-pay/src/crypto/constraint/proof_system.rs @@ -15,3 +15,53 @@ // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. //! Proof System Implementation + +use manta_crypto::constraint::{Alloc, Allocation, Bool, BooleanSystem, ProofSystem, Variable}; + +/// Arkworks Proof System +#[derive(Default)] +pub struct ArkProofSystem; + +/// TODO +pub struct BoolVar; + +impl Variable<ArkProofSystem> for BoolVar { + type Mode = (); + type Type = bool; +} + +impl Alloc<ArkProofSystem> for bool { + type Mode = (); + type Variable = BoolVar; + + #[inline] + fn variable<'t>( + ps: &mut ArkProofSystem, + allocation: impl Into<Allocation<'t, Self, ArkProofSystem>>, + ) -> Self::Variable + where + Self: 't, + { + let _ = (ps, allocation); + todo!() + } +} + +impl BooleanSystem for ArkProofSystem { + #[inline] + fn assert(&mut self, b: Bool<Self>) { + let _ = b; + todo!() + } +} + +impl ProofSystem for ArkProofSystem { + type Proof = (); + + type Error = (); + + #[inline] + fn finish(self) -> Result<Self::Proof, Self::Error> { + todo!() + } +} From 5a8467f02378ebe5add2f44e21e6414210ab0abd Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 13 Sep 2021 18:53:10 -0400 Subject: [PATCH 030/275] WIP: implement pedersen/blake2s backend --- manta-accounting/src/asset.rs | 33 +- manta-accounting/src/identity.rs | 91 ++-- manta-accounting/src/transfer.rs | 37 +- manta-crypto/Cargo.toml | 1 + manta-crypto/src/commitment.rs | 14 + manta-crypto/src/constraint.rs | 24 +- manta-crypto/src/prf.rs | 2 +- manta-pay/Cargo.toml | 9 +- manta-pay/src/accounting/config.rs | 100 ++++ manta-pay/src/accounting/ledger.rs | 25 +- manta-pay/src/accounting/mod.rs | 1 + manta-pay/src/crypto/commitment/pedersen.rs | 497 +++++++++++++++++- .../src/crypto/constraint/proof_system.rs | 113 +++- manta-pay/src/crypto/merkle_tree.rs | 104 ++-- manta-pay/src/crypto/prf/blake2s.rs | 173 +++++- manta-util/src/concat.rs | 92 ++-- 16 files changed, 1095 insertions(+), 221 deletions(-) create mode 100644 manta-pay/src/accounting/config.rs diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 84c536e25..33278138a 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -19,6 +19,7 @@ // TODO: Add macro to build `AssetId` and `AssetBalance`. // TODO: Implement all `rand` sampling traits. // TODO: Should we rename `AssetBalance` to `AssetValue` to be more consistent? +// TODO: Implement `Concat` for `AssetId` and `AssetBalance`. use alloc::vec::Vec; use core::{ @@ -34,9 +35,7 @@ use derive_more::{ use manta_crypto::constraint::{ unknown, Alloc, Allocation, HasVariable, PublicOrSecret, Secret, Var, Variable, }; -use manta_util::{ - array_map, fallible_array_map, into_array_unchecked, ByteAccumulator, ConcatBytes, -}; +use manta_util::{array_map, fallible_array_map, into_array_unchecked, Concat, ConcatAccumulator}; use rand::{ distributions::{Distribution, Standard}, Rng, RngCore, @@ -243,7 +242,7 @@ impl Asset { /// Converts `self` into a byte array. #[inline] pub fn into_bytes(self) -> [u8; Self::SIZE] { - into_array_unchecked(self.as_bytes::<Vec<_>>()) + into_array_unchecked(self.accumulated::<Vec<_>>()) } /// Converts a byte array into `self`. @@ -291,11 +290,13 @@ impl SubAssign<AssetBalance> for Asset { } } -impl ConcatBytes for Asset { +impl Concat for Asset { + type Item = u8; + #[inline] fn concat<A>(&self, accumulator: &mut A) where - A: ByteAccumulator + ?Sized, + A: ConcatAccumulator<Self::Item> + ?Sized, { accumulator.extend(&self.id.into_bytes()); accumulator.extend(&self.value.into_bytes()); @@ -368,6 +369,26 @@ where } } +impl<P> Concat for AssetVar<P> +where + P: HasVariable<AssetId, Mode = PublicOrSecret> + + HasVariable<AssetBalance, Mode = PublicOrSecret> + + ?Sized, + AssetIdVar<P>: Concat, + AssetBalanceVar<P>: Concat<Item = <AssetIdVar<P> as Concat>::Item>, +{ + type Item = <AssetIdVar<P> as Concat>::Item; + + #[inline] + fn concat<A>(&self, accumulator: &mut A) + where + A: ConcatAccumulator<Self::Item> + ?Sized, + { + self.id.concat(accumulator); + self.value.concat(accumulator); + } +} + impl<P> Variable<P> for AssetVar<P> where P: HasVariable<AssetId, Mode = PublicOrSecret> diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 3787c316d..5e666adeb 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -30,8 +30,8 @@ use core::{convert::Infallible, fmt::Debug, hash::Hash, marker::PhantomData}; use manta_crypto::{ commitment::{CommitmentScheme, Input as CommitmentInput}, constraint::{ - unknown, Alloc, AllocEq, Allocation, BooleanSystem, Constant, Derived, HasVariable, - ProofSystem, Public, PublicOrSecret, Secret, Var, Variable, + unknown, Alloc, AllocEq, Allocation, BooleanSystem, Constant, Derived, HasVariable, Public, + PublicOrSecret, Secret, Var, Variable, }, ies::{self, EncryptedMessage, IntegratedEncryptionScheme}, set::{ @@ -74,39 +74,39 @@ pub trait IdentityConfiguration { /// [`Identity`] Proof System Configuration pub trait IdentityProofSystemConfiguration { - /// Proof System - type ProofSystem: ProofSystem + /// Boolean System + type BooleanSystem: BooleanSystem + HasVariable<AssetId, Mode = PublicOrSecret> + HasVariable<AssetBalance, Mode = PublicOrSecret>; /// Pseudorandom Function Family Seed - type PseudorandomFunctionFamilySeed: Clone + Alloc<Self::ProofSystem, Mode = Secret>; + type PseudorandomFunctionFamilySeed: Clone + Alloc<Self::BooleanSystem, Mode = Secret>; /// Pseudorandom Function Family Input - type PseudorandomFunctionFamilyInput: Alloc<Self::ProofSystem, Mode = Secret>; + type PseudorandomFunctionFamilyInput: Alloc<Self::BooleanSystem, Mode = Secret>; /// Pseudorandom Function Family Output - type PseudorandomFunctionFamilyOutput: AllocEq<Self::ProofSystem, Mode = PublicOrSecret>; + type PseudorandomFunctionFamilyOutput: AllocEq<Self::BooleanSystem, Mode = PublicOrSecret>; /// Pseudorandom Function Family type PseudorandomFunctionFamily: PseudorandomFunctionFamily< Seed = Self::PseudorandomFunctionFamilySeed, Input = Self::PseudorandomFunctionFamilyInput, Output = Self::PseudorandomFunctionFamilyOutput, - > + Alloc<Self::ProofSystem, Mode = Constant, Variable = Self::PseudorandomFunctionFamilyVar>; + > + Alloc<Self::BooleanSystem, Mode = Constant, Variable = Self::PseudorandomFunctionFamilyVar>; /// Pseudorandom Function Family Variable type PseudorandomFunctionFamilyVar: PseudorandomFunctionFamily< Seed = PseudorandomFunctionFamilySeedVar<Self>, Input = PseudorandomFunctionFamilyInputVar<Self>, Output = PseudorandomFunctionFamilyOutputVar<Self>, - > + Variable<Self::ProofSystem, Mode = Constant, Type = Self::PseudorandomFunctionFamily>; + > + Variable<Self::BooleanSystem, Mode = Constant, Type = Self::PseudorandomFunctionFamily>; /// Commitment Scheme Randomness - type CommitmentSchemeRandomness: Alloc<Self::ProofSystem, Mode = Secret>; + type CommitmentSchemeRandomness: Alloc<Self::BooleanSystem, Mode = Secret>; /// Commitment Scheme Output - type CommitmentSchemeOutput: AllocEq<Self::ProofSystem, Mode = PublicOrSecret>; + type CommitmentSchemeOutput: AllocEq<Self::BooleanSystem, Mode = PublicOrSecret>; /// Commitment Scheme type CommitmentScheme: CommitmentScheme< @@ -116,7 +116,7 @@ pub trait IdentityProofSystemConfiguration { + CommitmentInput<VoidNumberGenerator<Self>> + CommitmentInput<Asset> + CommitmentInput<VoidNumberCommitment<Self>> - + Alloc<Self::ProofSystem, Mode = Constant, Variable = Self::CommitmentSchemeVar>; + + Alloc<Self::BooleanSystem, Mode = Constant, Variable = Self::CommitmentSchemeVar>; /// Commitment Scheme Variable type CommitmentSchemeVar: CommitmentScheme< @@ -124,9 +124,9 @@ pub trait IdentityProofSystemConfiguration { Output = CommitmentSchemeOutputVar<Self>, > + CommitmentInput<PublicKeyVar<Self>> + CommitmentInput<VoidNumberGeneratorVar<Self>> - + CommitmentInput<AssetVar<Self::ProofSystem>> + + CommitmentInput<AssetVar<Self::BooleanSystem>> + CommitmentInput<VoidNumberCommitmentVar<Self>> - + Variable<Self::ProofSystem, Mode = Constant, Type = Self::CommitmentScheme>; + + Variable<Self::BooleanSystem, Mode = Constant, Type = Self::CommitmentScheme>; /// Seedable Cryptographic Random Number Generator type Rng: CryptoRng + RngCore + SeedableRng<Seed = Self::PseudorandomFunctionFamilySeed>; @@ -137,11 +137,8 @@ where C: IdentityProofSystemConfiguration + ?Sized, { type SecretKey = C::PseudorandomFunctionFamilySeed; - type PseudorandomFunctionFamily = C::PseudorandomFunctionFamily; - type CommitmentScheme = C::CommitmentScheme; - type Rng = C::Rng; } @@ -191,23 +188,25 @@ pub type Utxo<C> = CommitmentSchemeOutput<C>; /// [`PseudorandomFunctionFamily::Seed`] Variable Type pub type PseudorandomFunctionFamilySeedVar<C> = - Var<PseudorandomFunctionFamilySeed<C>, <C as IdentityProofSystemConfiguration>::ProofSystem>; + Var<PseudorandomFunctionFamilySeed<C>, <C as IdentityProofSystemConfiguration>::BooleanSystem>; /// [`PseudorandomFunctionFamily::Input`] Variable Type pub type PseudorandomFunctionFamilyInputVar<C> = - Var<PseudorandomFunctionFamilyInput<C>, <C as IdentityProofSystemConfiguration>::ProofSystem>; + Var<PseudorandomFunctionFamilyInput<C>, <C as IdentityProofSystemConfiguration>::BooleanSystem>; /// [`PseudorandomFunctionFamily::Output`] Variable Type -pub type PseudorandomFunctionFamilyOutputVar<C> = - Var<PseudorandomFunctionFamilyOutput<C>, <C as IdentityProofSystemConfiguration>::ProofSystem>; +pub type PseudorandomFunctionFamilyOutputVar<C> = Var< + PseudorandomFunctionFamilyOutput<C>, + <C as IdentityProofSystemConfiguration>::BooleanSystem, +>; /// [`CommitmentScheme::Randomness`] Variable Type pub type CommitmentSchemeRandomnessVar<C> = - Var<CommitmentSchemeRandomness<C>, <C as IdentityProofSystemConfiguration>::ProofSystem>; + Var<CommitmentSchemeRandomness<C>, <C as IdentityProofSystemConfiguration>::BooleanSystem>; /// [`CommitmentScheme::Output`] Variable Type pub type CommitmentSchemeOutputVar<C> = - Var<CommitmentSchemeOutput<C>, <C as IdentityProofSystemConfiguration>::ProofSystem>; + Var<CommitmentSchemeOutput<C>, <C as IdentityProofSystemConfiguration>::BooleanSystem>; /// Secret Key Variable Type pub type SecretKeyVar<C> = PseudorandomFunctionFamilySeedVar<C>; @@ -235,7 +234,7 @@ pub type UtxoVar<C> = CommitmentSchemeOutputVar<C>; /// UTXO Containment Proof Variable Type pub type UtxoContainmentProofVar<C, S> = - ContainmentProofVar<S, <C as IdentityProofSystemConfiguration>::ProofSystem>; + ContainmentProofVar<S, <C as IdentityProofSystemConfiguration>::BooleanSystem>; /// Generates a void number commitment from `public_key`, `void_number_generator`, and /// `void_number_commitment_randomness`. @@ -410,7 +409,7 @@ where } } -impl<C> Variable<C::ProofSystem> for AssetParametersVar<C> +impl<C> Variable<C::BooleanSystem> for AssetParametersVar<C> where C: IdentityProofSystemConfiguration, { @@ -418,7 +417,7 @@ where type Type = AssetParameters<C>; } -impl<C> Alloc<C::ProofSystem> for AssetParameters<C> +impl<C> Alloc<C::BooleanSystem> for AssetParameters<C> where C: IdentityProofSystemConfiguration, { @@ -428,8 +427,8 @@ where #[inline] fn variable<'t>( - ps: &mut C::ProofSystem, - allocation: impl Into<Allocation<'t, Self, C::ProofSystem>>, + ps: &mut C::BooleanSystem, + allocation: impl Into<Allocation<'t, Self, C::BooleanSystem>>, ) -> Self::Variable where Self: 't, @@ -1221,7 +1220,7 @@ pub struct SenderVar<C, S> where C: IdentityProofSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - C::ProofSystem: VerifiedSetProofSystem< + C::BooleanSystem: VerifiedSetProofSystem< S, ItemMode = PublicOrSecret, PublicMode = Public, @@ -1235,7 +1234,7 @@ where public_key: PublicKeyVar<C>, /// Asset - asset: AssetVar<C::ProofSystem>, + asset: AssetVar<C::BooleanSystem>, /// Asset Parameters parameters: AssetParametersVar<C>, @@ -1257,7 +1256,7 @@ impl<C, S> SenderVar<C, S> where C: IdentityProofSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - C::ProofSystem: VerifiedSetProofSystem< + C::BooleanSystem: VerifiedSetProofSystem< S, ItemMode = PublicOrSecret, PublicMode = Public, @@ -1268,9 +1267,9 @@ where #[inline] pub fn get_well_formed_asset( self, - ps: &mut C::ProofSystem, + ps: &mut C::BooleanSystem, commitment_scheme: &C::CommitmentSchemeVar, - ) -> AssetVar<C::ProofSystem> { + ) -> AssetVar<C::BooleanSystem> { // Well-formed check: // // 1. pk = PRF(sk, 0) [public: (), secret: (pk, sk)] @@ -1345,11 +1344,11 @@ where } } -impl<C, S> Variable<C::ProofSystem> for SenderVar<C, S> +impl<C, S> Variable<C::BooleanSystem> for SenderVar<C, S> where C: IdentityProofSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - C::ProofSystem: VerifiedSetProofSystem< + C::BooleanSystem: VerifiedSetProofSystem< S, ItemMode = PublicOrSecret, PublicMode = Public, @@ -1360,11 +1359,11 @@ where type Type = Sender<C, S>; } -impl<C, S> Alloc<C::ProofSystem> for Sender<C, S> +impl<C, S> Alloc<C::BooleanSystem> for Sender<C, S> where C: IdentityProofSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - C::ProofSystem: VerifiedSetProofSystem< + C::BooleanSystem: VerifiedSetProofSystem< S, ItemMode = PublicOrSecret, PublicMode = Public, @@ -1376,8 +1375,8 @@ where #[inline] fn variable<'t>( - ps: &mut C::ProofSystem, - allocation: impl Into<Allocation<'t, Self, C::ProofSystem>>, + ps: &mut C::BooleanSystem, + allocation: impl Into<Allocation<'t, Self, C::BooleanSystem>>, ) -> Self::Variable where Self: 't, @@ -1569,7 +1568,7 @@ where I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// Asset - asset: AssetVar<C::ProofSystem>, + asset: AssetVar<C::BooleanSystem>, /// UTXO Randomness utxo_randomness: UtxoRandomnessVar<C>, @@ -1600,9 +1599,9 @@ where #[inline] pub fn get_well_formed_asset( self, - ps: &mut C::ProofSystem, + ps: &mut C::BooleanSystem, commitment_scheme: &C::CommitmentSchemeVar, - ) -> AssetVar<C::ProofSystem> { + ) -> AssetVar<C::BooleanSystem> { ps.assert_eq( &self.utxo, &generate_utxo( @@ -1616,7 +1615,7 @@ where } } -impl<C, I> Variable<C::ProofSystem> for ReceiverVar<C, I> +impl<C, I> Variable<C::BooleanSystem> for ReceiverVar<C, I> where C: IdentityProofSystemConfiguration, I: IntegratedEncryptionScheme<Plaintext = Asset>, @@ -1625,7 +1624,7 @@ where type Type = Receiver<C, I>; } -impl<C, I> Alloc<C::ProofSystem> for Receiver<C, I> +impl<C, I> Alloc<C::BooleanSystem> for Receiver<C, I> where C: IdentityProofSystemConfiguration, I: IntegratedEncryptionScheme<Plaintext = Asset>, @@ -1636,8 +1635,8 @@ where #[inline] fn variable<'t>( - ps: &mut C::ProofSystem, - allocation: impl Into<Allocation<'t, Self, C::ProofSystem>>, + ps: &mut C::BooleanSystem, + allocation: impl Into<Allocation<'t, Self, C::BooleanSystem>>, ) -> Self::Variable where Self: 't, diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 47137e9dd..6d0ea3490 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -100,7 +100,18 @@ impl<const SOURCES: usize, const SINKS: usize> Distribution<PublicTransfer<SOURC } /// Secret Transfer Configuration -pub trait SecretTransferConfiguration: IdentityProofSystemConfiguration { +pub trait SecretTransferConfiguration: + IdentityProofSystemConfiguration<BooleanSystem = Self::ProofSystem> +{ + /// Proof System + type ProofSystem: ProofSystem + + VerifiedSetProofSystem< + Self::UtxoSet, + ItemMode = PublicOrSecret, + PublicMode = Public, + SecretMode = Secret, + >; + /// Integrated Encryption Scheme for [`Asset`] type IntegratedEncryptionScheme: IntegratedEncryptionScheme<Plaintext = Asset>; @@ -204,12 +215,6 @@ where AssetId: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, AssetBalance: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, AssetBalanceVar<T::ProofSystem>: AddAssign<AssetBalanceVar<T::ProofSystem>>, - T::ProofSystem: VerifiedSetProofSystem< - T::UtxoSet, - ItemMode = PublicOrSecret, - PublicMode = Public, - SecretMode = Secret, - >, { let commitment_scheme = ps.allocate((commitment_scheme, Public)); @@ -244,12 +249,6 @@ where AssetId: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, AssetBalance: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, AssetBalanceVar<T::ProofSystem>: AddAssign<AssetBalanceVar<T::ProofSystem>>, - T::ProofSystem: VerifiedSetProofSystem< - T::UtxoSet, - ItemMode = PublicOrSecret, - PublicMode = Public, - SecretMode = Secret, - >, { let mut ps = <T::ProofSystem as Default>::default(); @@ -287,12 +286,6 @@ where AssetId: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, AssetBalance: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, AssetBalanceVar<T::ProofSystem>: AddAssign<AssetBalanceVar<T::ProofSystem>>, - T::ProofSystem: VerifiedSetProofSystem< - T::UtxoSet, - ItemMode = PublicOrSecret, - PublicMode = Public, - SecretMode = Secret, - >, { let validity_proof = self.generate_validity_proof(commitment_scheme)?; Some(SecretTransferPost { @@ -393,12 +386,6 @@ where AssetId: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, AssetBalance: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, AssetBalanceVar<T::ProofSystem>: AddAssign<AssetBalanceVar<T::ProofSystem>>, - T::ProofSystem: VerifiedSetProofSystem< - T::UtxoSet, - ItemMode = PublicOrSecret, - PublicMode = Public, - SecretMode = Secret, - >, { Some(TransferPost { public_transfer_post: self.public, diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index 84c5d422a..cac935052 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -30,4 +30,5 @@ test = [] [dependencies] derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } +manta-util = { path = "../manta-util", default-features = false } rand = { version = "0.8.4", default-features = false } diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index 5662277e1..603c2fdfb 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -16,6 +16,8 @@ //! Commitment Schemes +use manta_util::{Concat, ConcatAccumulator}; + pub(crate) mod prelude { #[doc(inline)] pub use super::CommitmentScheme; @@ -48,6 +50,18 @@ pub trait Input<T>: CommitmentScheme { fn extend(buffer: &mut Self::InputBuffer, input: &T); } +impl<C, I> Input<I> for C +where + C: CommitmentScheme + ?Sized, + C::InputBuffer: ConcatAccumulator<I::Item>, + I: Concat, +{ + #[inline] + fn extend(buffer: &mut C::InputBuffer, input: &I) { + input.concat(buffer) + } +} + /// Commitment Builder pub struct Builder<'c, C> where diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index 0ce8ef903..d5f432a25 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -52,13 +52,13 @@ pub trait AllocationMode { } impl AllocationMode for Infallible { - type Known = Infallible; - type Unknown = Infallible; + type Known = Self; + type Unknown = Self; } impl AllocationMode for () { - type Known = (); - type Unknown = (); + type Known = Self; + type Unknown = Self; } /// Allocation Entry @@ -441,8 +441,8 @@ where pub struct Derived; impl AllocationMode for Derived { - type Known = Derived; - type Unknown = Derived; + type Known = Self; + type Unknown = Self; } impl From<Derived> for () { @@ -457,8 +457,8 @@ impl From<Derived> for () { pub struct Public; impl AllocationMode for Public { - type Known = Public; - type Unknown = Public; + type Known = Self; + type Unknown = Self; } impl From<Derived> for Public { @@ -474,8 +474,8 @@ impl From<Derived> for Public { pub struct Secret; impl AllocationMode for Secret { - type Known = Secret; - type Unknown = Secret; + type Known = Self; + type Unknown = Self; } impl From<Derived> for Secret { @@ -556,8 +556,8 @@ impl PublicOrSecret { } impl AllocationMode for PublicOrSecret { - type Known = PublicOrSecret; - type Unknown = PublicOrSecret; + type Known = Self; + type Unknown = Self; } impl Default for PublicOrSecret { diff --git a/manta-crypto/src/prf.rs b/manta-crypto/src/prf.rs index 8734eb7c7..aa82b5e4a 100644 --- a/manta-crypto/src/prf.rs +++ b/manta-crypto/src/prf.rs @@ -19,7 +19,7 @@ /// Pseudorandom Function Families (PRF) Trait pub trait PseudorandomFunctionFamily { /// PRF Seed Type - type Seed; + type Seed: ?Sized; /// PRF Input Type type Input: Default; diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 0c174004a..279c7c551 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -29,16 +29,19 @@ default = [] std = [] [dependencies] -aes-gcm = { version = "0.9.4" } +aes-gcm = { version = "0.9.4", default-features = false } ark-bls12-381 = { version = "0.3.0", default-features = false } -ark-crypto-primitives = { version = "0.3.0", default-features = false } -ark-ed-on-bls12-381 = { version = "0.3.0", default-features = false } +ark-crypto-primitives = { version = "0.3.0", default-features = false, features = ["r1cs"] } +ark-ec = { version = "0.3.0", default-features = false } +ark-ed-on-bls12-381 = { version = "0.3.0", default-features = false, features = ["r1cs"] } +ark-ff = { version = "0.3.0", default-features = false } ark-groth16 = { version = "0.3.0", default-features = false } ark-r1cs-std = { version = "0.3.1", default-features = false } ark-relations = { version = "0.3.0", default-features = false } bip32 = { version = "0.2.2", default-features = false, features = ["bip39", "secp256k1"] } blake2 = { version = "0.9.2", default-features = false } cocoon = { version = "0.2.4", default-features = false } +derivative = { version = "2.2.0", default-features = false } generic-array = { version = "0.14.4", default-features = false } manta-accounting = { path = "../manta-accounting", default-features = false } manta-crypto = { path = "../manta-crypto", default-features = false } diff --git a/manta-pay/src/accounting/config.rs b/manta-pay/src/accounting/config.rs new file mode 100644 index 000000000..87f0701f5 --- /dev/null +++ b/manta-pay/src/accounting/config.rs @@ -0,0 +1,100 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Identity and Transfer Configurations + +#![allow(unused_imports)] // FIXME: Remove this once we are done implementing config. + +use crate::{ + accounting::ledger::UtxoSet, + crypto::{ + commitment::pedersen::{self, PedersenWindow}, + constraint::ArkProofSystem, + ies::IES, + prf::blake2s::{constraint::Blake2sVar, Blake2s}, + }, +}; +use ark_ed_on_bls12_381::{constraints::EdwardsVar, EdwardsProjective, Fq}; +use manta_accounting::{ + identity::{IdentityConfiguration, IdentityProofSystemConfiguration}, + transfer::SecretTransferConfiguration, +}; +use manta_crypto::{commitment::CommitmentScheme, PseudorandomFunctionFamily}; +use rand_chacha::ChaCha20Rng; + +/// Pedersen Window Parameters +#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct PedersenCommitmentWindowParameters; + +impl PedersenWindow for PedersenCommitmentWindowParameters { + const WINDOW_SIZE: usize = 4; + const NUM_WINDOWS: usize = 256; +} + +/// Pedersen Commitment Projective Curve +pub type PedersenCommitmentProjectiveCurve = EdwardsProjective; + +/// Pedersen Commitment Projective Curve +pub type PedersenCommitmentProjectiveCurveVar = EdwardsVar; + +/// Pedersen Commitment Scheme +pub type PedersenCommitment = pedersen::constraint::PedersenCommitmentWrapper< + PedersenCommitmentWindowParameters, + PedersenCommitmentProjectiveCurve, + PedersenCommitmentProjectiveCurveVar, +>; + +/// Pedersen Commitment Scheme Variable +pub type PedersenCommitmentVar = pedersen::constraint::PedersenCommitmentVar< + PedersenCommitmentWindowParameters, + PedersenCommitmentProjectiveCurve, + PedersenCommitmentProjectiveCurveVar, +>; + +/// Manta Pay Configuration +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Configuration; + +impl IdentityConfiguration for Configuration { + type SecretKey = [u8; 32]; + type PseudorandomFunctionFamily = Blake2s; + type CommitmentScheme = PedersenCommitment; + type Rng = ChaCha20Rng; +} + +/* TODO: +impl IdentityProofSystemConfiguration for Configuration { + type BooleanSystem = ArkProofSystem<Fq>; + type PseudorandomFunctionFamilySeed = <Blake2s as PseudorandomFunctionFamily>::Seed; + type PseudorandomFunctionFamilyInput = <Blake2s as PseudorandomFunctionFamily>::Input; + type PseudorandomFunctionFamilyOutput = <Blake2s as PseudorandomFunctionFamily>::Output; + type PseudorandomFunctionFamily = Blake2s; + type PseudorandomFunctionFamilyVar = Blake2sVar<Fq>; + type CommitmentSchemeRandomness = <PedersenCommitment as CommitmentScheme>::Randomness; + type CommitmentSchemeOutput = <PedersenCommitment as CommitmentScheme>::Output; + type CommitmentScheme = PedersenCommitment; + type CommitmentSchemeVar = PedersenCommitmentVar; + type Rng = ChaCha20Rng; +} +*/ + +/* TODO: +impl SecretTransferConfiguration for Configuration { + type ProofSystem = ArkProofSystem; + type IntegratedEncryptionScheme = IES; + type UtxoSet = UtxoSet; +} +*/ diff --git a/manta-pay/src/accounting/ledger.rs b/manta-pay/src/accounting/ledger.rs index a6ed09e34..0ad21ae52 100644 --- a/manta-pay/src/accounting/ledger.rs +++ b/manta-pay/src/accounting/ledger.rs @@ -18,10 +18,13 @@ // FIXME: Use more type-safe definitions for `VoidNumber` and `Utxo`. -use crate::crypto::{ - constraint::ArkProofSystem, - ies::EncryptedAsset, - merkle_tree::{self, MerkleTree, Path, Root}, +use crate::{ + accounting::config::{PedersenCommitmentProjectiveCurve, PedersenCommitmentWindowParameters}, + crypto::{ + constraint::ArkProofSystem, + ies::EncryptedAsset, + merkle_tree::{self, MerkleTree, Path, Root}, + }, }; use alloc::{vec, vec::Vec}; use blake2::{ @@ -42,7 +45,11 @@ type VoidNumber = [u8; 32]; type Utxo = [u8; 32]; /// UTXO Shard Root -type UtxoShardRoot = Root; +type UtxoShardRoot = Root<PedersenCommitmentWindowParameters, PedersenCommitmentProjectiveCurve>; + +/// Merkle Tree Parameters +type Parameters = + merkle_tree::Parameters<PedersenCommitmentWindowParameters, PedersenCommitmentProjectiveCurve>; /// UTXO Shard #[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] @@ -61,7 +68,7 @@ pub struct UtxoSet { shards: [UtxoShard; Self::SHARD_COUNT], /// Merkle Tree Parameters - parameters: merkle_tree::Parameters, + parameters: Parameters, } impl UtxoSet { @@ -69,7 +76,7 @@ impl UtxoSet { /// Builds a new [`UtxoSet`]. #[inline] - pub fn new(parameters: merkle_tree::Parameters) -> Self { + pub fn new(parameters: Parameters) -> Self { Self { shards: into_array_unchecked(vec![Default::default(); Self::SHARD_COUNT]), parameters, @@ -133,7 +140,7 @@ impl Set for UtxoSet { impl VerifiedSet for UtxoSet { type Public = UtxoShardRoot; - type Secret = Path<Utxo>; + type Secret = Path<PedersenCommitmentWindowParameters, PedersenCommitmentProjectiveCurve, Utxo>; // TODO: Give a more informative error. type ContainmentError = (); @@ -171,6 +178,7 @@ pub struct Ledger { encrypted_assets: Vec<EncryptedAsset>, } +/* TODO: impl LedgerTrait for Ledger { type VoidNumber = VoidNumber; @@ -227,3 +235,4 @@ impl LedgerTrait for Ledger { todo!() } } +*/ diff --git a/manta-pay/src/accounting/mod.rs b/manta-pay/src/accounting/mod.rs index e2a454217..d926fe54d 100644 --- a/manta-pay/src/accounting/mod.rs +++ b/manta-pay/src/accounting/mod.rs @@ -16,6 +16,7 @@ //! Accounting Implementations +pub mod config; pub mod keys; pub mod ledger; pub mod wallet; diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs index 24d258260..ca4f5c2f5 100644 --- a/manta-pay/src/crypto/commitment/pedersen.rs +++ b/manta-pay/src/crypto/commitment/pedersen.rs @@ -14,50 +14,495 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Pedersen Commitment Implementation +//! Arkworks Pedersen Commitment Implementation use alloc::vec::Vec; use ark_crypto_primitives::commitment::{ - pedersen::{Commitment, Window}, - CommitmentScheme as ArkCommitmentScheme, + pedersen::Commitment as ArkPedersenCommitment, CommitmentScheme as ArkCommitmentScheme, }; -use ark_ed_on_bls12_381::EdwardsProjective; +use ark_ff::bytes::ToBytes; use manta_crypto::commitment::CommitmentScheme; +use manta_util::{Concat, ConcatAccumulator}; -/// Implementation of [`CommitmentScheme`] -#[derive(Clone)] -pub struct PedersenCommitment(<ArkPedersenCommitment as ArkCommitmentScheme>::Parameters); +/// Pedersen Window Parameters Trait +// TODO: Remove this comment once `arkworks` writes their own. +pub use ark_crypto_primitives::commitment::pedersen::Window as PedersenWindow; -impl PedersenCommitment { - /// Pedersen Window Size - pub const WINDOW_SIZE: usize = 4; +pub use ark_ec::ProjectiveCurve; - /// Pedersen Window Count - pub const NUM_WINDOWS: usize = 256; -} +/// Arkworks Pedersen Commitment Parameters +type ArkPedersenCommitmentParameters<W, C> = + <ArkPedersenCommitment<C, W> as ArkCommitmentScheme>::Parameters; + +/// Arkworks Pedersen Commitment Randomness +type ArkPedersenCommitmentRandomness<W, C> = + <ArkPedersenCommitment<C, W> as ArkCommitmentScheme>::Randomness; + +/// Arkworks Pedersen Commitment Output +type ArkPedersenCommitmentOutput<W, C> = + <ArkPedersenCommitment<C, W> as ArkCommitmentScheme>::Output; -/// Pedersen Window Parameters -#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct PedersenWindow; +/// Pedersen Commitment Randomness +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = ""), + Debug(bound = ""), + Default(bound = ""), + Eq(bound = ""), + PartialEq(bound = "") +)] +pub struct PedersenCommitmentRandomness<W, C>(ArkPedersenCommitmentRandomness<W, C>) +where + W: PedersenWindow, + C: ProjectiveCurve; -impl Window for PedersenWindow { - const WINDOW_SIZE: usize = PedersenCommitment::WINDOW_SIZE; - const NUM_WINDOWS: usize = PedersenCommitment::NUM_WINDOWS; +/// Pedersen Commitment Output +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = ""), + Debug(bound = ""), + Default(bound = ""), + Eq(bound = ""), + Hash(bound = ""), + PartialEq(bound = "") +)] +pub struct PedersenCommitmentOutput<W, C>(ArkPedersenCommitmentOutput<W, C>) +where + W: PedersenWindow, + C: ProjectiveCurve; + +impl<W, C> Concat for PedersenCommitmentOutput<W, C> +where + W: PedersenWindow, + C: ProjectiveCurve, +{ + type Item = u8; + + #[inline] + fn concat<A>(&self, accumulator: &mut A) + where + A: ConcatAccumulator<Self::Item> + ?Sized, + { + // TODO: See if we can extend `ConcatAccumulator` to allow something like `Vec::append`, + // to improve the efficiency here. + let mut buffer = Vec::new(); + self.0 + .write(&mut buffer) + .expect("This does not fail. See the implementation of `Write` for `Vec`."); + accumulator.extend(&buffer); + } } -/// Arkworks Pedersen Commitment -pub type ArkPedersenCommitment = Commitment<EdwardsProjective, PedersenWindow>; +/// Implementation of [`CommitmentScheme`] for Pedersen Commitments +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""))] +pub struct PedersenCommitment<W, C>(ArkPedersenCommitmentParameters<W, C>) +where + W: PedersenWindow, + C: ProjectiveCurve; + +impl<W, C> PedersenCommitment<W, C> +where + W: PedersenWindow, + C: ProjectiveCurve, +{ + /// Pedersen Window Size + pub const WINDOW_SIZE: usize = W::WINDOW_SIZE; -impl CommitmentScheme for PedersenCommitment { + /// Pedersen Window Count + pub const NUM_WINDOWS: usize = W::NUM_WINDOWS; +} + +impl<W, C> CommitmentScheme for PedersenCommitment<W, C> +where + W: PedersenWindow, + C: ProjectiveCurve, +{ type InputBuffer = Vec<u8>; - type Randomness = <ArkPedersenCommitment as ArkCommitmentScheme>::Randomness; + type Randomness = PedersenCommitmentRandomness<W, C>; - type Output = <ArkPedersenCommitment as ArkCommitmentScheme>::Output; + type Output = PedersenCommitmentOutput<W, C>; #[inline] fn commit(&self, input: Self::InputBuffer, randomness: &Self::Randomness) -> Self::Output { - ArkPedersenCommitment::commit(&self.0, &input, randomness) - .expect("As of arkworks 0.3.0, this never fails.") + // FIXME: Make a note about the failure properties of commitment schemes. + PedersenCommitmentOutput( + ArkPedersenCommitment::<_, W>::commit(&self.0, &input, &randomness.0) + .expect("Failure outcomes are not accepted."), + ) + } +} + +/// Pedersen Commitment Scheme Constraint System Implementation +pub mod constraint { + use super::*; + use crate::crypto::constraint::ArkProofSystem; + use ark_crypto_primitives::{ + commitment::pedersen::constraints::{CommGadget, ParametersVar, RandomnessVar}, + CommitmentGadget, + }; + use ark_ff::Field; + use ark_r1cs_std::{ + groups::{CurveVar, GroupOpsBounds}, + uint8::UInt8, + }; + use core::marker::PhantomData; + use manta_crypto::constraint::{Alloc, Allocation, Constant, PublicOrSecret, Secret, Variable}; + + /// Constraint Field Type + pub type ConstraintField<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField; + + /// Proof System Type + type ProofSystem<C> = ArkProofSystem<ConstraintField<C>>; + + /// Input Buffer Type + type InputBuffer<F> = Vec<UInt8<F>>; + + /// Pedersen Commitment Output Wrapper + #[derive(derivative::Derivative)] + #[derivative(Clone(bound = ""))] + pub struct PedersenCommitmentOutputWrapper<W, C, GG> + where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + { + /// Commitment Output + output: PedersenCommitmentOutput<W, C>, + + /// Type Parameter Marker + __: PhantomData<GG>, + } + + impl<W, C, GG> From<PedersenCommitmentOutput<W, C>> for PedersenCommitmentOutputWrapper<W, C, GG> + where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + { + #[inline] + fn from(output: PedersenCommitmentOutput<W, C>) -> Self { + Self { + output, + __: PhantomData, + } + } + } + + impl<W, C, GG> From<PedersenCommitmentOutputWrapper<W, C, GG>> for PedersenCommitmentOutput<W, C> + where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + { + #[inline] + fn from(wrapper: PedersenCommitmentOutputWrapper<W, C, GG>) -> Self { + wrapper.output + } + } + + /// Pedersen Commitment Scheme Wrapper + #[derive(derivative::Derivative)] + #[derivative(Clone(bound = ""))] + pub struct PedersenCommitmentWrapper<W, C, GG> + where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + { + /// Commitment Scheme + commitment_scheme: PedersenCommitment<W, C>, + + /// Type Parameter Marker + __: PhantomData<GG>, + } + + impl<W, C, GG> PedersenCommitmentWrapper<W, C, GG> + where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + { + /// Pedersen Window Size + pub const WINDOW_SIZE: usize = W::WINDOW_SIZE; + + /// Pedersen Window Count + pub const NUM_WINDOWS: usize = W::NUM_WINDOWS; + } + + impl<W, C, GG> From<PedersenCommitment<W, C>> for PedersenCommitmentWrapper<W, C, GG> + where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + { + #[inline] + fn from(commitment_scheme: PedersenCommitment<W, C>) -> Self { + Self { + commitment_scheme, + __: PhantomData, + } + } + } + + impl<W, C, GG> From<PedersenCommitmentWrapper<W, C, GG>> for PedersenCommitment<W, C> + where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + { + #[inline] + fn from(wrapper: PedersenCommitmentWrapper<W, C, GG>) -> Self { + wrapper.commitment_scheme + } + } + + impl<W, C, GG> CommitmentScheme for PedersenCommitmentWrapper<W, C, GG> + where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + { + type InputBuffer = <PedersenCommitment<W, C> as CommitmentScheme>::InputBuffer; + + type Randomness = <PedersenCommitment<W, C> as CommitmentScheme>::Randomness; + + type Output = <PedersenCommitment<W, C> as CommitmentScheme>::Output; + + #[inline] + fn commit(&self, input: Self::InputBuffer, randomness: &Self::Randomness) -> Self::Output { + self.commitment_scheme.commit(input, randomness) + } + } + + /// Pedersen Commitment Randomness Variable + #[derive(derivative::Derivative)] + #[derivative(Clone(bound = ""))] + pub struct PedersenCommitmentRandomnessVar<W, C> + where + W: PedersenWindow, + C: ProjectiveCurve, + { + /// Randomness Variable + randomness_var: RandomnessVar<ConstraintField<C>>, + + /// Type Parameter Marker + __: PhantomData<W>, + } + + impl<W, C> Variable<ProofSystem<C>> for PedersenCommitmentRandomnessVar<W, C> + where + W: PedersenWindow, + C: ProjectiveCurve, + { + type Mode = Secret; + type Type = PedersenCommitmentRandomness<W, C>; + } + + impl<W, C> Alloc<ProofSystem<C>> for PedersenCommitmentRandomness<W, C> + where + W: PedersenWindow, + C: ProjectiveCurve, + { + type Mode = Secret; + + type Variable = PedersenCommitmentRandomnessVar<W, C>; + + #[inline] + fn variable<'t>( + ps: &mut ProofSystem<C>, + allocation: impl Into<Allocation<'t, Self, ProofSystem<C>>>, + ) -> Self::Variable + where + Self: 't, + { + // FIXME: implement + let _ = (ps, allocation); + todo!() + } + } + + /// Pedersen Commitment Output Variable + #[derive(derivative::Derivative)] + #[derivative(Clone(bound = ""))] + pub struct PedersenCommitmentOutputVar<W, C, GG> + where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + { + /// Output Variable + output_var: GG, + + /// Type Parameter Marker + __: PhantomData<(W, C)>, + } + + impl<W, C, GG> PedersenCommitmentOutputVar<W, C, GG> + where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + { + /// Builds a new [`PedersenCommitmentOutputVar`] from `output_var`. + #[inline] + fn new(output_var: GG) -> Self { + Self { + output_var, + __: PhantomData, + } + } + } + + impl<W, C, GG> Concat for PedersenCommitmentOutputVar<W, C, GG> + where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + { + type Item = UInt8<ConstraintField<C>>; + + #[inline] + fn concat<A>(&self, accumulator: &mut A) + where + A: ConcatAccumulator<Self::Item> + ?Sized, + { + accumulator.extend( + &self + .output_var + .to_bytes() + .expect("This is not allowed to fail."), + ); + } + } + + impl<W, C, GG> Variable<ProofSystem<C>> for PedersenCommitmentOutputVar<W, C, GG> + where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + { + type Mode = PublicOrSecret; + type Type = PedersenCommitmentOutputWrapper<W, C, GG>; + } + + impl<W, C, GG> Alloc<ProofSystem<C>> for PedersenCommitmentOutputWrapper<W, C, GG> + where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + { + type Mode = PublicOrSecret; + + type Variable = PedersenCommitmentOutputVar<W, C, GG>; + + #[inline] + fn variable<'t>( + ps: &mut ProofSystem<C>, + allocation: impl Into<Allocation<'t, Self, ProofSystem<C>>>, + ) -> Self::Variable + where + Self: 't, + { + // FIXME: implement + let _ = (ps, allocation); + todo!() + } + } + + /// Pedersen Commitment Scheme Variable + #[derive(derivative::Derivative)] + #[derivative(Clone(bound = ""))] + pub struct PedersenCommitmentVar<W, C, GG> + where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + { + /// Commitment Parameters Variable + parameters: ParametersVar<C, GG>, + + /// Type Parameter Marker + __: PhantomData<W>, + } + + impl<W, C, GG> Variable<ProofSystem<C>> for PedersenCommitmentVar<W, C, GG> + where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + { + type Mode = Constant; + type Type = PedersenCommitmentWrapper<W, C, GG>; + } + + impl<W, C, GG> Alloc<ArkProofSystem<ConstraintField<C>>> for PedersenCommitmentWrapper<W, C, GG> + where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + { + type Mode = Constant; + + type Variable = PedersenCommitmentVar<W, C, GG>; + + #[inline] + fn variable<'t>( + ps: &mut ProofSystem<C>, + allocation: impl Into<Allocation<'t, Self, ProofSystem<C>>>, + ) -> Self::Variable + where + Self: 't, + { + match allocation.into() { + Allocation::Known(this, _) => { + // FIXME: `this.parameters.new_constant(ps)` + let _ = (ps, this); + todo!() + } + _ => unreachable!( + "Since we use a constant allocation mode, we always know the variable value." + ), + } + } + } + + impl<W, C, GG> CommitmentScheme for PedersenCommitmentVar<W, C, GG> + where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + { + type InputBuffer = InputBuffer<ConstraintField<C>>; + + type Randomness = PedersenCommitmentRandomnessVar<W, C>; + + type Output = PedersenCommitmentOutputVar<W, C, GG>; + + #[inline] + fn commit(&self, input: Self::InputBuffer, randomness: &Self::Randomness) -> Self::Output { + PedersenCommitmentOutputVar::new( + CommGadget::<_, _, W>::commit(&self.parameters, &input, &randomness.randomness_var) + .expect("Failure outcomes are not accepted."), + ) + } } } diff --git a/manta-pay/src/crypto/constraint/proof_system.rs b/manta-pay/src/crypto/constraint/proof_system.rs index 0f7cfe4f5..c27f5d9bf 100644 --- a/manta-pay/src/crypto/constraint/proof_system.rs +++ b/manta-pay/src/crypto/constraint/proof_system.rs @@ -14,48 +14,123 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Proof System Implementation +//! Arkworks Proof System Implementation -use manta_crypto::constraint::{Alloc, Allocation, Bool, BooleanSystem, ProofSystem, Variable}; +use ark_ff::fields::Field; +use ark_r1cs_std::{alloc::AllocVar, bits::boolean::Boolean, eq::EqGadget}; +use ark_relations::{ + ns, + r1cs::{ConstraintSystem, ConstraintSystemRef, SynthesisError}, +}; +use manta_crypto::constraint::{ + Alloc, Allocation, AllocationMode, Bool, BooleanSystem, ProofSystem, PublicOrSecret, Variable, +}; + +/// Returns a blank variable assignment. +const fn blank<T>() -> Result<T, SynthesisError> { + Err(SynthesisError::AssignmentMissing) +} + +/// Arkworks Allocation Mode +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum ArkAllocationMode { + /// Allocates a Constant Variable + Constant, + + /// Allocates a Public Input Variable + Public, + + /// Allocates a Secret Witness Variable + Secret, +} + +impl AllocationMode for ArkAllocationMode { + type Known = Self; + type Unknown = PublicOrSecret; +} /// Arkworks Proof System -#[derive(Default)] -pub struct ArkProofSystem; +pub struct ArkProofSystem<F> +where + F: Field, +{ + /// Constraint System + cs: ConstraintSystemRef<F>, +} -/// TODO -pub struct BoolVar; +impl<F> Default for ArkProofSystem<F> +where + F: Field, +{ + #[inline] + fn default() -> Self { + Self { + cs: ConstraintSystem::new_ref(), + } + } +} -impl Variable<ArkProofSystem> for BoolVar { - type Mode = (); +impl<F> Variable<ArkProofSystem<F>> for Boolean<F> +where + F: Field, +{ + type Mode = ArkAllocationMode; type Type = bool; } -impl Alloc<ArkProofSystem> for bool { - type Mode = (); - type Variable = BoolVar; +impl<F> Alloc<ArkProofSystem<F>> for bool +where + F: Field, +{ + type Mode = ArkAllocationMode; + type Variable = Boolean<F>; #[inline] fn variable<'t>( - ps: &mut ArkProofSystem, - allocation: impl Into<Allocation<'t, Self, ArkProofSystem>>, + ps: &mut ArkProofSystem<F>, + allocation: impl Into<Allocation<'t, Self, ArkProofSystem<F>>>, ) -> Self::Variable where Self: 't, { - let _ = (ps, allocation); - todo!() + use ArkAllocationMode::*; + match allocation.into() { + Allocation::Known(this, mode) => match mode { + Constant => Self::Variable::new_constant(ns!(ps.cs, "boolean constant"), this), + Public => Self::Variable::new_input(ns!(ps.cs, "boolean input"), move || Ok(this)), + Secret => { + Self::Variable::new_witness(ns!(ps.cs, "boolean witness"), move || Ok(this)) + } + }, + Allocation::Unknown(mode) => match mode { + PublicOrSecret::Public => { + Self::Variable::new_input(ns!(ps.cs, "boolean input"), blank::<bool>) + } + PublicOrSecret::Secret => { + Self::Variable::new_witness(ns!(ps.cs, "boolean witness"), blank::<bool>) + } + }, + } + .expect("Variable allocation is not allowed to fail.") } } -impl BooleanSystem for ArkProofSystem { +impl<F> BooleanSystem for ArkProofSystem<F> +where + F: Field, +{ #[inline] fn assert(&mut self, b: Bool<Self>) { - let _ = b; - todo!() + // FIXME: Is there a more direct way to do assertions? + b.enforce_equal(&Boolean::TRUE) + .expect("This should never fail.") } } -impl ProofSystem for ArkProofSystem { +impl<F> ProofSystem for ArkProofSystem<F> +where + F: Field, +{ type Proof = (); type Error = (); diff --git a/manta-pay/src/crypto/merkle_tree.rs b/manta-pay/src/crypto/merkle_tree.rs index a8acae8ee..378e70315 100644 --- a/manta-pay/src/crypto/merkle_tree.rs +++ b/manta-pay/src/crypto/merkle_tree.rs @@ -20,7 +20,11 @@ // faillible interfaces so that we don't have to depend explicitly on implementation // details of the `arkworks` project. -use crate::crypto::commitment::pedersen::PedersenWindow; +// TODO: We use the Pedersen commitment settings for `CRH` and `TwoToOneCRH`. We should write our +// own `CRH` and `TwoToOneCRH` traits and then in the configuration we align them with the +// Pedersen settings. + +use crate::crypto::commitment::pedersen::{PedersenWindow, ProjectiveCurve}; use alloc::vec::Vec; use ark_crypto_primitives::{ crh::pedersen::CRH, @@ -29,41 +33,57 @@ use ark_crypto_primitives::{ TwoToOneDigest, TwoToOneParam, }, }; -use ark_ed_on_bls12_381::EdwardsProjective; use core::marker::PhantomData; use manta_crypto::set::{ContainmentProof, VerifiedSet, VerifyContainment}; -use manta_util::{as_bytes, ConcatBytes}; +use manta_util::{as_bytes, Concat}; /// Merkle Tree Root -pub type Root = TwoToOneDigest<MerkleTreeConfiguration>; +pub type Root<W, C> = TwoToOneDigest<MerkleTreeConfiguration<W, C>>; /// Merkle Tree Parameters -#[derive(Clone)] -pub struct Parameters { +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""))] +pub struct Parameters<W, C> +where + W: PedersenWindow, + C: ProjectiveCurve, +{ /// Leaf Hash Parameters - leaf: LeafParam<MerkleTreeConfiguration>, + leaf: LeafParam<MerkleTreeConfiguration<W, C>>, /// Two-to-One Hash Parameters - two_to_one: TwoToOneParam<MerkleTreeConfiguration>, + two_to_one: TwoToOneParam<MerkleTreeConfiguration<W, C>>, } /// Merkle Tree Path -#[derive(Clone)] -pub struct Path<T> { +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""))] +pub struct Path<W, C, T> +where + W: PedersenWindow, + C: ProjectiveCurve, +{ /// Merkle Tree Parameters - parameters: Parameters, + parameters: Parameters<W, C>, /// Path - path: MerkleTreePath<MerkleTreeConfiguration>, + path: MerkleTreePath<MerkleTreeConfiguration<W, C>>, /// Type Parameter Marker __: PhantomData<T>, } -impl<T> Path<T> { +impl<W, C, T> Path<W, C, T> +where + W: PedersenWindow, + C: ProjectiveCurve, +{ /// Builds a new [`Path`] from `parameters` and `path`. #[inline] - fn new(parameters: Parameters, path: MerkleTreePath<MerkleTreeConfiguration>) -> Self { + fn new( + parameters: Parameters<W, C>, + path: MerkleTreePath<MerkleTreeConfiguration<W, C>>, + ) -> Self { Self { parameters, path, @@ -73,21 +93,28 @@ impl<T> Path<T> { } /// Merkle Tree -#[derive(Clone)] -pub struct MerkleTree<T> { +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""))] +pub struct MerkleTree<W, C, T> +where + W: PedersenWindow, + C: ProjectiveCurve, +{ /// Merkle Tree Parameters - parameters: Parameters, + parameters: Parameters<W, C>, /// Merkle Tree - tree: ArkMerkleTree<MerkleTreeConfiguration>, + tree: ArkMerkleTree<MerkleTreeConfiguration<W, C>>, /// Type Parameter Marker __: PhantomData<T>, } -impl<T> MerkleTree<T> +impl<W, C, T> MerkleTree<W, C, T> where - T: ConcatBytes, + W: PedersenWindow, + C: ProjectiveCurve, + T: Concat<Item = u8>, { /// Builds a new [`MerkleTree`]. /// @@ -95,7 +122,7 @@ where /// /// The length of `leaves` must be a power of 2 or this function will panic. #[inline] - pub fn new(parameters: &Parameters, leaves: &[T]) -> Option<Self> { + pub fn new(parameters: &Parameters<W, C>, leaves: &[T]) -> Option<Self> { Some(Self { tree: ArkMerkleTree::new( &parameters.leaf, @@ -113,13 +140,13 @@ where /// Computes the [`Root`] of the [`MerkleTree`] built from the `leaves`. #[inline] - pub fn build_root(parameters: &Parameters, leaves: &[T]) -> Option<Root> { + pub fn build_root(parameters: &Parameters<W, C>, leaves: &[T]) -> Option<Root<W, C>> { Some(Self::new(parameters, leaves)?.root()) } /// Returns the [`Root`] of this [`MerkleTree`]. #[inline] - pub fn root(&self) -> Root { + pub fn root(&self) -> Root<W, C> { self.tree.root() } @@ -127,7 +154,7 @@ where #[inline] pub fn get_containment_proof<S>(&self, index: usize) -> Option<ContainmentProof<S>> where - S: VerifiedSet<Public = Root, Secret = Path<T>>, + S: VerifiedSet<Public = Root<W, C>, Secret = Path<W, C, T>>, { Some(ContainmentProof::new( self.root(), @@ -140,23 +167,30 @@ where } /// Merkle Tree Configuration -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct MerkleTreeConfiguration; - -impl MerkleTreeConfig for MerkleTreeConfiguration { - type LeafHash = CRH<EdwardsProjective, PedersenWindow>; +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct MerkleTreeConfiguration<W, C>(PhantomData<(W, C)>) +where + W: PedersenWindow, + C: ProjectiveCurve; - // TODO: On the arkworks development branch `CRH` was fixed to `TwoToOneCRH`. - // We will need to fix this in the next update. - type TwoToOneHash = CRH<EdwardsProjective, PedersenWindow>; +impl<W, C> MerkleTreeConfig for MerkleTreeConfiguration<W, C> +where + W: PedersenWindow, + C: ProjectiveCurve, +{ + type LeafHash = CRH<C, W>; + type TwoToOneHash = CRH<C, W>; } -impl<T> VerifyContainment<Root, T> for Path<T> +impl<W, C, T> VerifyContainment<Root<W, C>, T> for Path<W, C, T> where - T: ConcatBytes, + W: PedersenWindow, + C: ProjectiveCurve, + T: Concat<Item = u8>, { #[inline] - fn verify(&self, root: &Root, item: &T) -> bool { + fn verify(&self, root: &Root<W, C>, item: &T) -> bool { self.path .verify( &self.parameters.leaf, diff --git a/manta-pay/src/crypto/prf/blake2s.rs b/manta-pay/src/crypto/prf/blake2s.rs index 6a8d324f0..a9e451cf9 100644 --- a/manta-pay/src/crypto/prf/blake2s.rs +++ b/manta-pay/src/crypto/prf/blake2s.rs @@ -16,22 +16,185 @@ //! Blake2s PRF Implementation -use ark_crypto_primitives::prf::{self, PRF}; +use ark_crypto_primitives::prf::{Blake2s as ArkBlake2s, PRF}; use manta_crypto::PseudorandomFunctionFamily; +use manta_util::{Concat, ConcatAccumulator}; /// Blake2s Pseudorandom Function Family #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Blake2s; +/// Blake2s Pseudorandom Function Family Seed +pub type Blake2sSeed = <ArkBlake2s as PRF>::Seed; + +/// Blake2s Pseudorandom Function Family Input +pub type Blake2sInput = <ArkBlake2s as PRF>::Input; + +/// Blake2s Pseudorandom Function Family Output +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Blake2sOutput(<ArkBlake2s as PRF>::Output); + +impl Concat for Blake2sOutput { + type Item = u8; + + #[inline] + fn concat<A>(&self, accumulator: &mut A) + where + A: ConcatAccumulator<Self::Item> + ?Sized, + { + self.0.concat(accumulator) + } +} + impl PseudorandomFunctionFamily for Blake2s { - type Seed = <prf::Blake2s as PRF>::Seed; + type Seed = Blake2sSeed; - type Input = <prf::Blake2s as PRF>::Input; + type Input = Blake2sInput; - type Output = <prf::Blake2s as PRF>::Output; + type Output = Blake2sOutput; #[inline] fn evaluate(seed: &Self::Seed, input: &Self::Input) -> Self::Output { - prf::Blake2s::evaluate(seed, input).expect("As of arkworks 0.3.0, this never fails.") + Blake2sOutput( + ArkBlake2s::evaluate(seed, input).expect("As of arkworks 0.3.0, this never fails."), + ) + } +} + +/// Blake2s PRF Constraint System Implementations +pub mod constraint { + use super::*; + use crate::crypto::constraint::ArkProofSystem as ProofSystem; + use alloc::vec::Vec; + use ark_crypto_primitives::{ + prf::blake2s::constraints::Blake2sGadget as ArkBlake2sVar, PRFGadget, + }; + use ark_ff::PrimeField; + use ark_r1cs_std::uint8::UInt8; + use core::marker::PhantomData; + use manta_crypto::constraint::{Alloc, Allocation, Constant, PublicOrSecret, Variable}; + + /// Blake2s Pseudorandom Function Family Output Wrapper + #[derive(derivative::Derivative)] + #[derivative(Clone)] + pub struct Blake2sOutputWrapper<F> + where + F: PrimeField, + { + /// PRF Output + output: Blake2sOutput, + + /// Type Parameter Marker + __: PhantomData<F>, + } + + impl<F> From<Blake2sOutput> for Blake2sOutputWrapper<F> + where + F: PrimeField, + { + #[inline] + fn from(output: Blake2sOutput) -> Blake2sOutputWrapper<F> { + Self { + output, + __: PhantomData, + } + } + } + + impl<F> From<Blake2sOutputWrapper<F>> for Blake2sOutput + where + F: PrimeField, + { + #[inline] + fn from(wrapper: Blake2sOutputWrapper<F>) -> Self { + wrapper.output + } + } + + /// Blake2s Pseudorandom Function Family Output Variable + #[derive(derivative::Derivative)] + #[derivative(Clone)] + pub struct Blake2sOutputVar<F>(<ArkBlake2sVar as PRFGadget<ArkBlake2s, F>>::OutputVar) + where + F: PrimeField; + + impl<F> Variable<ProofSystem<F>> for Blake2sOutputVar<F> + where + F: PrimeField, + { + type Mode = PublicOrSecret; + type Type = Blake2sOutputWrapper<F>; + } + + impl<F> Alloc<ProofSystem<F>> for Blake2sOutputWrapper<F> + where + F: PrimeField, + { + type Mode = PublicOrSecret; + + type Variable = Blake2sOutputVar<F>; + + #[inline] + fn variable<'t>( + ps: &mut ProofSystem<F>, + allocation: impl Into<Allocation<'t, Self, ProofSystem<F>>>, + ) -> Self::Variable + where + Self: 't, + { + // FIXME: implement + let _ = (ps, allocation); + todo!() + } + } + + /// Blake2s Pseudorandom Function Family Variable + #[derive(derivative::Derivative)] + #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct Blake2sVar<F>(PhantomData<F>) + where + F: PrimeField; + + impl<F> Variable<ProofSystem<F>> for Blake2sVar<F> + where + F: PrimeField, + { + type Mode = Constant; + type Type = Blake2s; + } + + impl<F> Alloc<ProofSystem<F>> for Blake2s + where + F: PrimeField, + { + type Mode = Constant; + + type Variable = Blake2sVar<F>; + + #[inline] + fn variable<'t>( + ps: &mut ProofSystem<F>, + allocation: impl Into<Allocation<'t, Self, ProofSystem<F>>>, + ) -> Self::Variable { + let _ = (ps, allocation); + Default::default() + } + } + + impl<F> PseudorandomFunctionFamily for Blake2sVar<F> + where + F: PrimeField, + { + type Seed = [UInt8<F>]; + type Input = Vec<UInt8<F>>; + type Output = Blake2sOutputVar<F>; + + #[inline] + fn evaluate(seed: &Self::Seed, input: &Self::Input) -> Self::Output { + // FIXME: Make a note about the failure properties of PRFs. + Blake2sOutputVar( + ArkBlake2sVar::evaluate(seed, input).expect("Failure outcomes are not accepted."), + ) + } } } diff --git a/manta-util/src/concat.rs b/manta-util/src/concat.rs index afd093431..bb6cc93e4 100644 --- a/manta-util/src/concat.rs +++ b/manta-util/src/concat.rs @@ -17,12 +17,11 @@ //! Byte Concatenation Utilities use alloc::vec::Vec; -use core::borrow::Borrow; -/// Byte Accumulation Trait -pub trait ByteAccumulator { +/// Concatenation Accumulator Trait +pub trait ConcatAccumulator<T> { /// Extends the current accumulator by a `buffer` of elements. - fn extend(&mut self, buffer: &[u8]); + fn extend(&mut self, buffer: &[T]); /// Reserves space in the accumulator for `additional` more elements. #[inline] @@ -44,7 +43,7 @@ pub trait ByteAccumulator { self } - /// Creates a "by mutable reference" adaptor for this instance of [`ByteAccumulator`]. + /// Creates a "by mutable reference" adaptor for this instance of [`ConcatAccumulator`]. #[inline] fn by_ref(&mut self) -> &mut Self where @@ -54,12 +53,12 @@ pub trait ByteAccumulator { } } -impl<A> ByteAccumulator for &mut A +impl<T, A> ConcatAccumulator<T> for &mut A where - A: ByteAccumulator + ?Sized, + A: ConcatAccumulator<T> + ?Sized, { #[inline] - fn extend(&mut self, buffer: &[u8]) { + fn extend(&mut self, buffer: &[T]) { (**self).extend(buffer) } @@ -74,9 +73,12 @@ where } } -impl ByteAccumulator for Vec<u8> { +impl<T> ConcatAccumulator<T> for Vec<T> +where + T: Clone, +{ #[inline] - fn extend(&mut self, buffer: &[u8]) { + fn extend(&mut self, buffer: &[T]) { self.extend_from_slice(buffer) } @@ -91,8 +93,11 @@ impl ByteAccumulator for Vec<u8> { } } -/// Byte Concatenation Trait -pub trait ConcatBytes { +/// Concatenation Trait +pub trait Concat { + /// Item Type + type Item; + /// Concatenates `self` on the end of the accumulator. /// /// # Note @@ -102,7 +107,7 @@ pub trait ConcatBytes { /// is not efficient. fn concat<A>(&self, accumulator: &mut A) where - A: ByteAccumulator + ?Sized; + A: ConcatAccumulator<Self::Item> + ?Sized; /// Returns a hint to the possible number of bytes that will be accumulated when concatenating /// `self`. @@ -115,7 +120,7 @@ pub trait ConcatBytes { #[inline] fn reserve_concat<A>(&self, accumulator: &mut A) where - A: ByteAccumulator + ?Sized, + A: ConcatAccumulator<Self::Item> + ?Sized, { if let Some(capacity) = self.size_hint() { accumulator.reserve(capacity); @@ -126,9 +131,9 @@ pub trait ConcatBytes { /// Constructs a default accumulator and accumulates over `self`, reserving the appropriate /// capacity. #[inline] - fn as_bytes<A>(&self) -> A + fn accumulated<A>(&self) -> A where - A: Default + ByteAccumulator, + A: Default + ConcatAccumulator<Self::Item>, { let mut accumulator = A::default(); self.reserve_concat(&mut accumulator); @@ -136,42 +141,59 @@ pub trait ConcatBytes { } } -impl<T> ConcatBytes for T -where - T: Borrow<[u8]> + ?Sized, -{ +impl<T> Concat for [T] { + type Item = T; + + #[inline] + fn concat<A>(&self, accumulator: &mut A) + where + A: ConcatAccumulator<T> + ?Sized, + { + accumulator.extend(self) + } + + #[inline] + fn size_hint(&self) -> Option<usize> { + Some(self.len()) + } +} + +impl<T, const N: usize> Concat for [T; N] { + type Item = T; + #[inline] fn concat<A>(&self, accumulator: &mut A) where - A: ByteAccumulator + ?Sized, + A: ConcatAccumulator<T> + ?Sized, { - accumulator.extend(self.borrow()) + accumulator.extend(self) } #[inline] fn size_hint(&self) -> Option<usize> { - Some(self.borrow().len()) + Some(self.len()) } } -/// Concatenates `$item`s together by building a [`ByteAccumulator`] and running -/// [`ConcatBytes::concat`] over each `$item`. +/// Concatenates `$item`s together by building a [`ConcatAccumulator`] and running +/// [`Concat::concat`] over each `$item`. #[macro_export] macro_rules! concatenate { - ($($item:expr),*) => { - { + ($($item:expr),*) => { + { extern crate alloc; let mut accumulator = ::alloc::vec::Vec::new(); - $($crate::ConcatBytes::reserve_concat($item, &mut accumulator);)* - $crate::ByteAccumulator::finish(accumulator) - } - } + $($crate::Concat::reserve_concat($item, &mut accumulator);)* + $crate::ConcatAccumulator::finish(accumulator) + } + } } -/// Returns byte vector representation of `$item`. +/// Returns byte vector representation of `$item` if it implements [`Concat<Item = u8>`](Concat). #[macro_export] macro_rules! as_bytes { - ($item:expr) => { - $crate::concatenate!($item) - }; + ($item:expr) => {{ + let bytes: Vec<u8> = $crate::concatenate!($item); + bytes + }}; } From 62576cac7785a6fb52f57c81252fed34f4288dc7 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 14 Sep 2021 01:04:15 -0400 Subject: [PATCH 031/275] WIP: finish identity-ps-config backend impl --- manta-crypto/src/constraint.rs | 1 + manta-pay/src/accounting/config.rs | 14 +- manta-pay/src/crypto/commitment/pedersen.rs | 43 ++- .../src/crypto/constraint/proof_system.rs | 291 +++++++++++++++++- manta-pay/src/crypto/mod.rs | 1 + manta-pay/src/crypto/prf/blake2s.rs | 196 ++++++++++-- manta-pay/src/crypto/rand.rs | 73 +++++ 7 files changed, 570 insertions(+), 49 deletions(-) create mode 100644 manta-pay/src/crypto/rand.rs diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index d5f432a25..413e42c06 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -21,6 +21,7 @@ // TODO: Add more convenience functions for allocating unknown variables. // TODO: How to do verification systems? Should it be a separate trait or part of `ProofSystem`? // TODO: Should we (can we?) seal the `HasVariable` trait so no one implements it by accident? +// TODO: Can we replace `AllocEq` with an equality trait that extends `Variable` not `Alloc`? use core::{ convert::{Infallible, TryFrom}, diff --git a/manta-pay/src/accounting/config.rs b/manta-pay/src/accounting/config.rs index 87f0701f5..54fc53d5f 100644 --- a/manta-pay/src/accounting/config.rs +++ b/manta-pay/src/accounting/config.rs @@ -25,6 +25,7 @@ use crate::{ constraint::ArkProofSystem, ies::IES, prf::blake2s::{constraint::Blake2sVar, Blake2s}, + rand::ChaCha20RngBlake2sSeedable, }, }; use ark_ed_on_bls12_381::{constraints::EdwardsVar, EdwardsProjective, Fq}; @@ -68,14 +69,6 @@ pub type PedersenCommitmentVar = pedersen::constraint::PedersenCommitmentVar< #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Configuration; -impl IdentityConfiguration for Configuration { - type SecretKey = [u8; 32]; - type PseudorandomFunctionFamily = Blake2s; - type CommitmentScheme = PedersenCommitment; - type Rng = ChaCha20Rng; -} - -/* TODO: impl IdentityProofSystemConfiguration for Configuration { type BooleanSystem = ArkProofSystem<Fq>; type PseudorandomFunctionFamilySeed = <Blake2s as PseudorandomFunctionFamily>::Seed; @@ -87,13 +80,12 @@ impl IdentityProofSystemConfiguration for Configuration { type CommitmentSchemeOutput = <PedersenCommitment as CommitmentScheme>::Output; type CommitmentScheme = PedersenCommitment; type CommitmentSchemeVar = PedersenCommitmentVar; - type Rng = ChaCha20Rng; + type Rng = ChaCha20RngBlake2sSeedable; } -*/ /* TODO: impl SecretTransferConfiguration for Configuration { - type ProofSystem = ArkProofSystem; + type ProofSystem = ArkProofSystem<Fq>; type IntegratedEncryptionScheme = IES; type UtxoSet = UtxoSet; } diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs index ca4f5c2f5..8a5c89144 100644 --- a/manta-pay/src/crypto/commitment/pedersen.rs +++ b/manta-pay/src/crypto/commitment/pedersen.rs @@ -148,7 +148,9 @@ pub mod constraint { uint8::UInt8, }; use core::marker::PhantomData; - use manta_crypto::constraint::{Alloc, Allocation, Constant, PublicOrSecret, Secret, Variable}; + use manta_crypto::constraint::{ + Alloc, AllocEq, Allocation, Bool, Constant, PublicOrSecret, Secret, Var, Variable, + }; /// Constraint Field Type pub type ConstraintField<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField; @@ -205,6 +207,24 @@ pub mod constraint { } } + impl<W, C, GG> Concat for PedersenCommitmentOutputWrapper<W, C, GG> + where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + { + type Item = u8; + + #[inline] + fn concat<A>(&self, accumulator: &mut A) + where + A: ConcatAccumulator<Self::Item> + ?Sized, + { + self.output.concat(accumulator) + } + } + /// Pedersen Commitment Scheme Wrapper #[derive(derivative::Derivative)] #[derivative(Clone(bound = ""))] @@ -276,11 +296,11 @@ pub mod constraint { type Randomness = <PedersenCommitment<W, C> as CommitmentScheme>::Randomness; - type Output = <PedersenCommitment<W, C> as CommitmentScheme>::Output; + type Output = PedersenCommitmentOutputWrapper<W, C, GG>; #[inline] fn commit(&self, input: Self::InputBuffer, randomness: &Self::Randomness) -> Self::Output { - self.commitment_scheme.commit(input, randomness) + self.commitment_scheme.commit(input, randomness).into() } } @@ -424,6 +444,23 @@ pub mod constraint { } } + impl<W, C, GG> AllocEq<ProofSystem<C>> for PedersenCommitmentOutputWrapper<W, C, GG> + where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + { + #[inline] + fn eq( + ps: &mut ProofSystem<C>, + lhs: &Var<Self, ProofSystem<C>>, + rhs: &Var<Self, ProofSystem<C>>, + ) -> Bool<ProofSystem<C>> { + todo!() + } + } + /// Pedersen Commitment Scheme Variable #[derive(derivative::Derivative)] #[derivative(Clone(bound = ""))] diff --git a/manta-pay/src/crypto/constraint/proof_system.rs b/manta-pay/src/crypto/constraint/proof_system.rs index c27f5d9bf..5c8064559 100644 --- a/manta-pay/src/crypto/constraint/proof_system.rs +++ b/manta-pay/src/crypto/constraint/proof_system.rs @@ -16,21 +16,39 @@ //! Arkworks Proof System Implementation -use ark_ff::fields::Field; -use ark_r1cs_std::{alloc::AllocVar, bits::boolean::Boolean, eq::EqGadget}; +use alloc::vec::Vec; +use ark_ff::{fields::Field, PrimeField}; +use ark_r1cs_std::{ + alloc::AllocVar, bits::boolean::Boolean, eq::EqGadget, fields::fp::FpVar, uint8::UInt8, + ToBytesGadget, +}; use ark_relations::{ ns, r1cs::{ConstraintSystem, ConstraintSystemRef, SynthesisError}, }; +use core::borrow::Borrow; +use manta_accounting::{AssetBalance, AssetId}; use manta_crypto::constraint::{ - Alloc, Allocation, AllocationMode, Bool, BooleanSystem, ProofSystem, PublicOrSecret, Variable, + Alloc, Allocation, AllocationMode, Bool, BooleanSystem, ProofSystem, Public, PublicOrSecret, + Secret, Variable, }; +use manta_util::{Concat, ConcatAccumulator}; + +/// Synthesis Result +type SynthesisResult<T> = Result<T, SynthesisError>; -/// Returns a blank variable assignment. -const fn blank<T>() -> Result<T, SynthesisError> { +/// Returns an empty variable assignment. +#[inline] +const fn empty<T>() -> SynthesisResult<T> { Err(SynthesisError::AssignmentMissing) } +/// Returns a filled variable assignment. +#[inline] +fn full<T>(t: T) -> impl FnOnce() -> SynthesisResult<T> { + move || Ok(t) +} + /// Arkworks Allocation Mode #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub enum ArkAllocationMode { @@ -49,6 +67,32 @@ impl AllocationMode for ArkAllocationMode { type Unknown = PublicOrSecret; } +impl From<Public> for ArkAllocationMode { + #[inline] + fn from(p: Public) -> Self { + let _ = p; + Self::Public + } +} + +impl From<Secret> for ArkAllocationMode { + #[inline] + fn from(s: Secret) -> Self { + let _ = s; + Self::Secret + } +} + +impl From<PublicOrSecret> for ArkAllocationMode { + #[inline] + fn from(pos: PublicOrSecret) -> Self { + match pos { + PublicOrSecret::Public => Self::Public, + PublicOrSecret::Secret => Self::Secret, + } + } +} + /// Arkworks Proof System pub struct ArkProofSystem<F> where @@ -97,17 +141,70 @@ where match allocation.into() { Allocation::Known(this, mode) => match mode { Constant => Self::Variable::new_constant(ns!(ps.cs, "boolean constant"), this), - Public => Self::Variable::new_input(ns!(ps.cs, "boolean input"), move || Ok(this)), + Public => Self::Variable::new_input(ns!(ps.cs, "boolean input"), full(this)), + Secret => Self::Variable::new_witness(ns!(ps.cs, "boolean witness"), full(this)), + }, + Allocation::Unknown(mode) => match mode { + PublicOrSecret::Public => { + Self::Variable::new_input(ns!(ps.cs, "boolean input"), empty::<bool>) + } + PublicOrSecret::Secret => { + Self::Variable::new_witness(ns!(ps.cs, "boolean witness"), empty::<bool>) + } + }, + } + .expect("Variable allocation is not allowed to fail.") + } +} + +/// Prime Field Element +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +pub struct Fp<F>(F) +where + F: PrimeField; + +impl<F> Variable<ArkProofSystem<F>> for FpVar<F> +where + F: PrimeField, +{ + type Mode = ArkAllocationMode; + type Type = Fp<F>; +} + +impl<F> Alloc<ArkProofSystem<F>> for Fp<F> +where + F: PrimeField, +{ + type Mode = ArkAllocationMode; + + type Variable = FpVar<F>; + + #[inline] + fn variable<'t>( + ps: &mut ArkProofSystem<F>, + allocation: impl Into<Allocation<'t, Self, ArkProofSystem<F>>>, + ) -> Self::Variable + where + Self: 't, + { + use ArkAllocationMode::*; + match allocation.into() { + Allocation::Known(this, mode) => match mode { + Constant => { + Self::Variable::new_constant(ns!(ps.cs, "prime field constant"), this.0) + } + Public => Self::Variable::new_input(ns!(ps.cs, "prime field input"), full(this.0)), Secret => { - Self::Variable::new_witness(ns!(ps.cs, "boolean witness"), move || Ok(this)) + Self::Variable::new_witness(ns!(ps.cs, "prime field witness"), full(this.0)) } }, Allocation::Unknown(mode) => match mode { PublicOrSecret::Public => { - Self::Variable::new_input(ns!(ps.cs, "boolean input"), blank::<bool>) + Self::Variable::new_input(ns!(ps.cs, "prime field input"), empty::<F>) } PublicOrSecret::Secret => { - Self::Variable::new_witness(ns!(ps.cs, "boolean witness"), blank::<bool>) + Self::Variable::new_witness(ns!(ps.cs, "prime field witness"), empty::<F>) } }, } @@ -115,6 +212,182 @@ where } } +/// Byte Array Variable +#[derive(derivative::Derivative)] +#[derivative(Clone, Debug)] +pub struct ByteArrayVar<F, const N: usize>(Vec<UInt8<F>>) +where + F: Field; + +impl<F, const N: usize> AsRef<[UInt8<F>]> for ByteArrayVar<F, N> +where + F: Field, +{ + #[inline] + fn as_ref(&self) -> &[UInt8<F>] { + &self.0 + } +} + +impl<F, const N: usize> Borrow<[UInt8<F>]> for ByteArrayVar<F, N> +where + F: Field, +{ + #[inline] + fn borrow(&self) -> &[UInt8<F>] { + &self.0 + } +} + +impl<F, const N: usize> Concat for ByteArrayVar<F, N> +where + F: Field, +{ + type Item = UInt8<F>; + + #[inline] + fn concat<A>(&self, accumulator: &mut A) + where + A: ConcatAccumulator<Self::Item> + ?Sized, + { + accumulator.extend(&self.0) + } +} + +impl<F, const N: usize> Variable<ArkProofSystem<F>> for ByteArrayVar<F, N> +where + F: Field, +{ + type Mode = ArkAllocationMode; + type Type = [u8; N]; +} + +impl<F, const N: usize> Alloc<ArkProofSystem<F>> for [u8; N] +where + F: Field, +{ + type Mode = ArkAllocationMode; + + type Variable = ByteArrayVar<F, N>; + + #[inline] + fn variable<'t>( + ps: &mut ArkProofSystem<F>, + allocation: impl Into<Allocation<'t, Self, ArkProofSystem<F>>>, + ) -> Self::Variable + where + Self: 't, + { + todo!() + } +} + +/// Asset Id Variable +#[derive(derivative::Derivative)] +#[derivative(Clone, Debug)] +pub struct AssetIdVar<F>(FpVar<F>) +where + F: PrimeField; + +impl<F> Concat for AssetIdVar<F> +where + F: PrimeField, +{ + type Item = UInt8<F>; + + #[inline] + fn concat<A>(&self, accumulator: &mut A) + where + A: ConcatAccumulator<Self::Item> + ?Sized, + { + accumulator.extend(&self.0.to_bytes().expect("This is not allowed to fail.")) + } +} + +impl<F> Variable<ArkProofSystem<F>> for AssetIdVar<F> +where + F: PrimeField, +{ + type Mode = PublicOrSecret; + type Type = AssetId; +} + +impl<F> Alloc<ArkProofSystem<F>> for AssetId +where + F: PrimeField, +{ + type Mode = PublicOrSecret; + + type Variable = AssetIdVar<F>; + + #[inline] + fn variable<'t>( + ps: &mut ArkProofSystem<F>, + allocation: impl Into<Allocation<'t, Self, ArkProofSystem<F>>>, + ) -> Self::Variable + where + Self: 't, + { + AssetIdVar(match allocation.into() { + Allocation::Known(this, mode) => Fp(F::from(this.0)).as_known(ps, mode), + Allocation::Unknown(mode) => Fp::unknown(ps, mode), + }) + } +} + +/// Asset Balance Variable +#[derive(derivative::Derivative)] +#[derivative(Clone, Debug)] +pub struct AssetBalanceVar<F>(FpVar<F>) +where + F: PrimeField; + +impl<F> Concat for AssetBalanceVar<F> +where + F: PrimeField, +{ + type Item = UInt8<F>; + + #[inline] + fn concat<A>(&self, accumulator: &mut A) + where + A: ConcatAccumulator<Self::Item> + ?Sized, + { + accumulator.extend(&self.0.to_bytes().expect("This is not allowed to fail.")) + } +} + +impl<F> Variable<ArkProofSystem<F>> for AssetBalanceVar<F> +where + F: PrimeField, +{ + type Mode = PublicOrSecret; + type Type = AssetBalance; +} + +impl<F> Alloc<ArkProofSystem<F>> for AssetBalance +where + F: PrimeField, +{ + type Mode = PublicOrSecret; + + type Variable = AssetBalanceVar<F>; + + #[inline] + fn variable<'t>( + ps: &mut ArkProofSystem<F>, + allocation: impl Into<Allocation<'t, Self, ArkProofSystem<F>>>, + ) -> Self::Variable + where + Self: 't, + { + AssetBalanceVar(match allocation.into() { + Allocation::Known(this, mode) => Fp(F::from(this.0)).as_known(ps, mode), + Allocation::Unknown(mode) => Fp::unknown(ps, mode), + }) + } +} + impl<F> BooleanSystem for ArkProofSystem<F> where F: Field, diff --git a/manta-pay/src/crypto/mod.rs b/manta-pay/src/crypto/mod.rs index 603696d1c..55e4f7807 100644 --- a/manta-pay/src/crypto/mod.rs +++ b/manta-pay/src/crypto/mod.rs @@ -21,3 +21,4 @@ pub mod constraint; pub mod ies; pub mod merkle_tree; pub mod prf; +pub mod rand; diff --git a/manta-pay/src/crypto/prf/blake2s.rs b/manta-pay/src/crypto/prf/blake2s.rs index a9e451cf9..104600535 100644 --- a/manta-pay/src/crypto/prf/blake2s.rs +++ b/manta-pay/src/crypto/prf/blake2s.rs @@ -25,10 +25,43 @@ use manta_util::{Concat, ConcatAccumulator}; pub struct Blake2s; /// Blake2s Pseudorandom Function Family Seed -pub type Blake2sSeed = <ArkBlake2s as PRF>::Seed; +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Blake2sSeed(pub(crate) <ArkBlake2s as PRF>::Seed); + +impl AsMut<[u8]> for Blake2sSeed { + #[inline] + fn as_mut(&mut self) -> &mut [u8] { + self.0.as_mut() + } +} + +impl Concat for Blake2sSeed { + type Item = u8; + + #[inline] + fn concat<A>(&self, accumulator: &mut A) + where + A: ConcatAccumulator<Self::Item> + ?Sized, + { + self.0.concat(accumulator) + } +} /// Blake2s Pseudorandom Function Family Input -pub type Blake2sInput = <ArkBlake2s as PRF>::Input; +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Blake2sInput(<ArkBlake2s as PRF>::Input); + +impl Concat for Blake2sInput { + type Item = u8; + + #[inline] + fn concat<A>(&self, accumulator: &mut A) + where + A: ConcatAccumulator<Self::Item> + ?Sized, + { + self.0.concat(accumulator) + } +} /// Blake2s Pseudorandom Function Family Output #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -56,7 +89,8 @@ impl PseudorandomFunctionFamily for Blake2s { #[inline] fn evaluate(seed: &Self::Seed, input: &Self::Input) -> Self::Output { Blake2sOutput( - ArkBlake2s::evaluate(seed, input).expect("As of arkworks 0.3.0, this never fails."), + ArkBlake2s::evaluate(&seed.0, &input.0) + .expect("As of arkworks 0.3.0, this never fails."), ) } } @@ -64,50 +98,130 @@ impl PseudorandomFunctionFamily for Blake2s { /// Blake2s PRF Constraint System Implementations pub mod constraint { use super::*; - use crate::crypto::constraint::ArkProofSystem as ProofSystem; + use crate::crypto::constraint::{ArkProofSystem as ProofSystem, ByteArrayVar}; use alloc::vec::Vec; use ark_crypto_primitives::{ prf::blake2s::constraints::Blake2sGadget as ArkBlake2sVar, PRFGadget, }; use ark_ff::PrimeField; - use ark_r1cs_std::uint8::UInt8; + use ark_r1cs_std::{uint8::UInt8, ToBytesGadget}; use core::marker::PhantomData; - use manta_crypto::constraint::{Alloc, Allocation, Constant, PublicOrSecret, Variable}; + use manta_crypto::constraint::{ + Alloc, AllocEq, Allocation, Bool, Constant, PublicOrSecret, Secret, Var, Variable, + }; + + /// Blake2s Pseudorandom Function Family Seed Variable + #[derive(derivative::Derivative)] + #[derivative(Clone)] + pub struct Blake2sSeedVar<F>(ByteArrayVar<F, 32>) + where + F: PrimeField; + + impl<F> Concat for Blake2sSeedVar<F> + where + F: PrimeField, + { + type Item = UInt8<F>; + + #[inline] + fn concat<A>(&self, accumulator: &mut A) + where + A: ConcatAccumulator<Self::Item> + ?Sized, + { + self.0.concat(accumulator) + } + } + + impl<F> Variable<ProofSystem<F>> for Blake2sSeedVar<F> + where + F: PrimeField, + { + type Mode = Secret; + type Type = Blake2sSeed; + } + + impl<F> Alloc<ProofSystem<F>> for Blake2sSeed + where + F: PrimeField, + { + type Mode = Secret; + + type Variable = Blake2sSeedVar<F>; + + #[inline] + fn variable<'t>( + ps: &mut ProofSystem<F>, + allocation: impl Into<Allocation<'t, Self, ProofSystem<F>>>, + ) -> Self::Variable + where + Self: 't, + { + // FIXME: implement + let _ = (ps, allocation); + todo!() + } + } - /// Blake2s Pseudorandom Function Family Output Wrapper + /// Blake2s Pseudorandom Function Family Input Variable #[derive(derivative::Derivative)] #[derivative(Clone)] - pub struct Blake2sOutputWrapper<F> + pub struct Blake2sInputVar<F>(ByteArrayVar<F, 32>) + where + F: PrimeField; + + impl<F> Concat for Blake2sInputVar<F> where F: PrimeField, { - /// PRF Output - output: Blake2sOutput, + type Item = UInt8<F>; - /// Type Parameter Marker - __: PhantomData<F>, + #[inline] + fn concat<A>(&self, accumulator: &mut A) + where + A: ConcatAccumulator<Self::Item> + ?Sized, + { + self.0.concat(accumulator) + } } - impl<F> From<Blake2sOutput> for Blake2sOutputWrapper<F> + impl<F> Default for Blake2sInputVar<F> where F: PrimeField, { #[inline] - fn from(output: Blake2sOutput) -> Blake2sOutputWrapper<F> { - Self { - output, - __: PhantomData, - } + fn default() -> Self { + // TODO: Should be secret values! + todo!() } } - impl<F> From<Blake2sOutputWrapper<F>> for Blake2sOutput + impl<F> Variable<ProofSystem<F>> for Blake2sInputVar<F> + where + F: PrimeField, + { + type Mode = Secret; + type Type = Blake2sInput; + } + + impl<F> Alloc<ProofSystem<F>> for Blake2sInput where F: PrimeField, { + type Mode = Secret; + + type Variable = Blake2sInputVar<F>; + #[inline] - fn from(wrapper: Blake2sOutputWrapper<F>) -> Self { - wrapper.output + fn variable<'t>( + ps: &mut ProofSystem<F>, + allocation: impl Into<Allocation<'t, Self, ProofSystem<F>>>, + ) -> Self::Variable + where + Self: 't, + { + // FIXME: implement + let _ = (ps, allocation); + todo!() } } @@ -118,15 +232,30 @@ pub mod constraint { where F: PrimeField; + impl<F> Concat for Blake2sOutputVar<F> + where + F: PrimeField, + { + type Item = UInt8<F>; + + #[inline] + fn concat<A>(&self, accumulator: &mut A) + where + A: ConcatAccumulator<Self::Item> + ?Sized, + { + accumulator.extend(&self.0.to_bytes().expect("This is not allowed to fail.")); + } + } + impl<F> Variable<ProofSystem<F>> for Blake2sOutputVar<F> where F: PrimeField, { type Mode = PublicOrSecret; - type Type = Blake2sOutputWrapper<F>; + type Type = Blake2sOutput; } - impl<F> Alloc<ProofSystem<F>> for Blake2sOutputWrapper<F> + impl<F> Alloc<ProofSystem<F>> for Blake2sOutput where F: PrimeField, { @@ -148,6 +277,20 @@ pub mod constraint { } } + impl<F> AllocEq<ProofSystem<F>> for Blake2sOutput + where + F: PrimeField, + { + #[inline] + fn eq( + ps: &mut ProofSystem<F>, + lhs: &Var<Self, ProofSystem<F>>, + rhs: &Var<Self, ProofSystem<F>>, + ) -> Bool<ProofSystem<F>> { + todo!() + } + } + /// Blake2s Pseudorandom Function Family Variable #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -185,15 +328,16 @@ pub mod constraint { where F: PrimeField, { - type Seed = [UInt8<F>]; - type Input = Vec<UInt8<F>>; + type Seed = Blake2sSeedVar<F>; + type Input = Blake2sInputVar<F>; type Output = Blake2sOutputVar<F>; #[inline] fn evaluate(seed: &Self::Seed, input: &Self::Input) -> Self::Output { // FIXME: Make a note about the failure properties of PRFs. Blake2sOutputVar( - ArkBlake2sVar::evaluate(seed, input).expect("Failure outcomes are not accepted."), + ArkBlake2sVar::evaluate(seed.0.as_ref(), input.0.as_ref()) + .expect("Failure outcomes are not accepted."), ) } } diff --git a/manta-pay/src/crypto/rand.rs b/manta-pay/src/crypto/rand.rs new file mode 100644 index 000000000..e75d99491 --- /dev/null +++ b/manta-pay/src/crypto/rand.rs @@ -0,0 +1,73 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Random Number Generator Wrappers + +use crate::crypto::prf::blake2s::Blake2sSeed; +use rand::{CryptoRng, Error, RngCore, SeedableRng}; +use rand_chacha::ChaCha20Rng; + +/// Cha-Cha 20 RNG Seedable from a Blake2s Seed +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ChaCha20RngBlake2sSeedable(ChaCha20Rng); + +impl CryptoRng for ChaCha20RngBlake2sSeedable {} + +impl RngCore for ChaCha20RngBlake2sSeedable { + #[inline] + fn next_u32(&mut self) -> u32 { + self.0.next_u32() + } + + #[inline] + fn next_u64(&mut self) -> u64 { + self.0.next_u64() + } + + #[inline] + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.0.fill_bytes(dest) + } + + #[inline] + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + self.0.try_fill_bytes(dest) + } +} + +impl SeedableRng for ChaCha20RngBlake2sSeedable { + type Seed = Blake2sSeed; + + #[inline] + fn from_seed(seed: Self::Seed) -> Self { + Self(ChaCha20Rng::from_seed(seed.0)) + } + + #[inline] + fn seed_from_u64(state: u64) -> Self { + Self(ChaCha20Rng::seed_from_u64(state)) + } + + #[inline] + fn from_rng<R: RngCore>(rng: R) -> Result<Self, Error> { + ChaCha20Rng::from_rng(rng).map(Self) + } + + #[inline] + fn from_entropy() -> Self { + Self(ChaCha20Rng::from_entropy()) + } +} From cd7902831713a44bb414eba37788fbe05e11454c Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 14 Sep 2021 16:33:58 -0400 Subject: [PATCH 032/275] WIP: universal transfers done, fix set variables --- manta-accounting/src/identity.rs | 40 +- manta-accounting/src/transfer.rs | 660 +++++++++++++++++++++------- manta-accounting/src/wallet.rs | 4 +- manta-crypto/src/set/constraint.rs | 50 ++- manta-crypto/src/set/mod.rs | 25 +- manta-pay/src/accounting/config.rs | 15 +- manta-pay/src/accounting/ledger.rs | 50 ++- manta-pay/src/crypto/merkle_tree.rs | 88 ++-- 8 files changed, 655 insertions(+), 277 deletions(-) diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 5e666adeb..c9187ed08 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -35,7 +35,7 @@ use manta_crypto::{ }, ies::{self, EncryptedMessage, IntegratedEncryptionScheme}, set::{ - constraint::{ContainmentProofVar, VerifiedSetProofSystem}, + constraint::{ContainmentProofVar, VerifiedSetVariable}, ContainmentProof, VerifiedSet, }, PseudorandomFunctionFamily, @@ -1220,12 +1220,7 @@ pub struct SenderVar<C, S> where C: IdentityProofSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - C::BooleanSystem: VerifiedSetProofSystem< - S, - ItemMode = PublicOrSecret, - PublicMode = Public, - SecretMode = Secret, - >, + C::BooleanSystem: HasVariable<S::Public> + HasVariable<S::Secret>, { /// Secret Key secret_key: SecretKeyVar<C>, @@ -1256,12 +1251,7 @@ impl<C, S> SenderVar<C, S> where C: IdentityProofSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - C::BooleanSystem: VerifiedSetProofSystem< - S, - ItemMode = PublicOrSecret, - PublicMode = Public, - SecretMode = Secret, - >, + C::BooleanSystem: HasVariable<S::Public> + HasVariable<S::Secret>, { /// Checks if `self` is a well-formed sender and returns its asset. #[inline] @@ -1269,7 +1259,12 @@ where self, ps: &mut C::BooleanSystem, commitment_scheme: &C::CommitmentSchemeVar, - ) -> AssetVar<C::BooleanSystem> { + utxo_set: &Var<S, C::BooleanSystem>, + ) -> AssetVar<C::BooleanSystem> + where + S: Alloc<C::BooleanSystem>, + S::Variable: VerifiedSetVariable<C::BooleanSystem>, + { // Well-formed check: // // 1. pk = PRF(sk, 0) [public: (), secret: (pk, sk)] @@ -1338,7 +1333,8 @@ where // is_path(cm, path, root) == true // ``` // where public: {root}, secret: {cm, path}. - self.utxo_containment_proof.assert_validity(&self.utxo, ps); + self.utxo_containment_proof + .assert_validity(utxo_set, &self.utxo, ps); self.asset } @@ -1348,12 +1344,7 @@ impl<C, S> Variable<C::BooleanSystem> for SenderVar<C, S> where C: IdentityProofSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - C::BooleanSystem: VerifiedSetProofSystem< - S, - ItemMode = PublicOrSecret, - PublicMode = Public, - SecretMode = Secret, - >, + C::BooleanSystem: HasVariable<S::Public, Mode = Public> + HasVariable<S::Secret, Mode = Secret>, { type Mode = Derived; type Type = Sender<C, S>; @@ -1363,12 +1354,7 @@ impl<C, S> Alloc<C::BooleanSystem> for Sender<C, S> where C: IdentityProofSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - C::BooleanSystem: VerifiedSetProofSystem< - S, - ItemMode = PublicOrSecret, - PublicMode = Public, - SecretMode = Secret, - >, + C::BooleanSystem: HasVariable<S::Public, Mode = Public> + HasVariable<S::Secret, Mode = Secret>, { type Mode = Derived; type Variable = SenderVar<C, S>; diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 6d0ea3490..b41400d02 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -17,7 +17,10 @@ //! Transfer Protocols use crate::{ - asset::{sample_asset_balances, Asset, AssetBalance, AssetBalanceVar, AssetBalances, AssetId}, + asset::{ + sample_asset_balances, Asset, AssetBalance, AssetBalanceVar, AssetBalances, AssetId, + AssetIdVar, + }, identity::{ IdentityProofSystemConfiguration, Receiver, ReceiverPost, ReceiverVar, Sender, SenderPost, SenderVar, Utxo, VoidNumber, @@ -28,10 +31,11 @@ use alloc::vec::Vec; use core::ops::AddAssign; use manta_crypto::{ constraint::{ - AllocEq, BooleanSystem, Derived, HasVariable, ProofSystem, Public, PublicOrSecret, Secret, + Alloc, AllocEq, BooleanSystem, Constant, Derived, ProofSystem, Public, PublicOrSecret, + Secret, }, ies::{EncryptedMessage, IntegratedEncryptionScheme}, - set::{constraint::VerifiedSetProofSystem, VerifiedSet}, + set::{constraint::VerifiedSetVariable, VerifiedSet}, }; use manta_util::{array_map, mixed_chain, Either}; use rand::{ @@ -43,7 +47,7 @@ use rand::{ #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct PublicTransfer<const SOURCES: usize, const SINKS: usize> { /// Asset Id - pub asset_id: AssetId, + pub asset_id: Option<AssetId>, /// Public Asset Sources pub sources: AssetBalances<SOURCES>, @@ -52,6 +56,18 @@ pub struct PublicTransfer<const SOURCES: usize, const SINKS: usize> { pub sinks: AssetBalances<SINKS>, } +#[allow(clippy::derivable_impls)] // NOTE: We only want default on the `<0, 0>` setting. +impl Default for PublicTransfer<0, 0> { + #[inline] + fn default() -> Self { + Self { + asset_id: None, + sources: [], + sinks: [], + } + } +} + impl<const SOURCES: usize, const SINKS: usize> PublicTransfer<SOURCES, SINKS> { /// Builds a new [`PublicTransfer`]. #[inline] @@ -61,25 +77,30 @@ impl<const SOURCES: usize, const SINKS: usize> PublicTransfer<SOURCES, SINKS> { sinks: AssetBalances<SINKS>, ) -> Self { Self { - asset_id, + asset_id: if SOURCES == 0 && SINKS == 0 { + None + } else { + Some(asset_id) + }, sources, sinks, } } - /// Returns the sum of the asset values of the sources. + /// Returns the sum of the asset values of the sources in this transfer. #[inline] pub fn source_sum(&self) -> AssetBalance { self.sources.iter().sum() } - /// Returns the sum of the asset values of the sinks. + /// Returns the sum of the asset values of the sinks in this transfer. #[inline] pub fn sink_sum(&self) -> AssetBalance { self.sinks.iter().sum() } - /// Validates the transaction by checking that the source sum equals the sink sum. + /// Validates the transaction by checking that the [`source_sum`](Self::source_sum) + /// equals the [`sink_sum`](Self::sink_sum). #[inline] pub fn is_valid(&self) -> bool { self.source_sum() == self.sink_sum() @@ -99,44 +120,53 @@ impl<const SOURCES: usize, const SINKS: usize> Distribution<PublicTransfer<SOURC } } -/// Secret Transfer Configuration -pub trait SecretTransferConfiguration: +/// Transfer Configuration +pub trait TransferConfiguration: IdentityProofSystemConfiguration<BooleanSystem = Self::ProofSystem> { /// Proof System - type ProofSystem: ProofSystem - + VerifiedSetProofSystem< - Self::UtxoSet, - ItemMode = PublicOrSecret, - PublicMode = Public, - SecretMode = Secret, - >; + type ProofSystem: ProofSystem; /// Integrated Encryption Scheme for [`Asset`] type IntegratedEncryptionScheme: IntegratedEncryptionScheme<Plaintext = Asset>; + /// Verified Set Public Input + type UtxoSetPublicInput: Alloc<Self::ProofSystem, Mode = Public>; + + /// Verified Set Secret Witness + type UtxoSetSecretWitness: Alloc<Self::ProofSystem, Mode = Secret>; + /// Verified Set for [`Utxo`] - type UtxoSet: VerifiedSet<Item = Utxo<Self>>; + type UtxoSet: VerifiedSet< + Item = Utxo<Self>, + Public = Self::UtxoSetPublicInput, + Secret = Self::UtxoSetSecretWitness, + > + Alloc<Self::ProofSystem, Mode = Constant, Variable = Self::UtxoSetVar>; + + /// Verified Set Variable for [`Utxo`] + type UtxoSetVar: VerifiedSetVariable<Self::ProofSystem, Mode = Constant, Type = Self::UtxoSet>; } /// Secret Sender Type -pub type SecretSender<T> = Sender<T, <T as SecretTransferConfiguration>::UtxoSet>; +pub type SecretSender<T> = Sender<T, <T as TransferConfiguration>::UtxoSet>; /// Secret Receiver Type -pub type SecretReceiver<T> = - Receiver<T, <T as SecretTransferConfiguration>::IntegratedEncryptionScheme>; +pub type SecretReceiver<T> = Receiver<T, <T as TransferConfiguration>::IntegratedEncryptionScheme>; /// Secret Sender Variable Type -pub type SecretSenderVar<T> = SenderVar<T, <T as SecretTransferConfiguration>::UtxoSet>; +pub type SecretSenderVar<T> = SenderVar<T, <T as TransferConfiguration>::UtxoSet>; /// Secret Receiver Type pub type SecretReceiverVar<T> = - ReceiverVar<T, <T as SecretTransferConfiguration>::IntegratedEncryptionScheme>; + ReceiverVar<T, <T as TransferConfiguration>::IntegratedEncryptionScheme>; + +/// Secret Transfer Proof Type +pub type Proof<T> = <<T as TransferConfiguration>::ProofSystem as ProofSystem>::Proof; /// Secret Transfer Protocol pub struct SecretTransfer<T, const SENDERS: usize, const RECEIVERS: usize> where - T: SecretTransferConfiguration, + T: TransferConfiguration, { /// Secret Senders pub senders: [SecretSender<T>; SENDERS], @@ -147,7 +177,7 @@ where impl<T, const SENDERS: usize, const RECEIVERS: usize> SecretTransfer<T, SENDERS, RECEIVERS> where - T: SecretTransferConfiguration, + T: TransferConfiguration, { /// Maximum Number of Senders pub const MAXIMUM_SENDER_COUNT: usize = 32; @@ -161,26 +191,67 @@ where senders: [SecretSender<T>; SENDERS], receivers: [SecretReceiver<T>; RECEIVERS], ) -> Self { - // FIXME: Should we have arrays of senders and receivers or use vectors? - if SENDERS > Self::MAXIMUM_SENDER_COUNT { - panic!("Allocated too many senders."); + Self::check_sender_side(); + Self::check_receiver_side(); + Self::check_size_overflow(); + Self::new_unchecked(senders, receivers) + } + + /// Checks that the sender side is not empty. + #[inline] + fn check_sender_side() { + if SENDERS == 0 { + panic!("Not enough senders.") } - if RECEIVERS > Self::MAXIMUM_RECEIVER_COUNT { - panic!("Allocated too many receivers."); + } + + /// Checks that the receiver side is not empty. + #[inline] + fn check_receiver_side() { + if RECEIVERS == 0 { + panic!("Not enough receivers.") } + } + + /// Checks that the number of senders and/or receivers does not exceed the allocation limit. + #[inline] + fn check_size_overflow() { + // FIXME: Should we have arrays of senders and receivers or use vectors? + match ( + SENDERS > Self::MAXIMUM_SENDER_COUNT, + RECEIVERS > Self::MAXIMUM_RECEIVER_COUNT, + ) { + (true, true) => panic!("Allocated too many senders and receivers."), + (true, _) => panic!("Allocated too many senders."), + (_, true) => panic!("Allocated too many receivers."), + _ => {} + } + } + + /// Builds a new [`SecretTransfer`] without checking the number of senders and receivers. + #[inline] + fn new_unchecked( + senders: [SecretSender<T>; SENDERS], + receivers: [SecretReceiver<T>; RECEIVERS], + ) -> Self { Self { senders, receivers } } - /// Checks that the asset ids of all the senders and receivers matches. + /// Returns an iterator over all the asset ids in this transfer. #[inline] - pub fn has_unique_asset_id(&self) -> bool { - let mut asset_id = None; + fn asset_id_iter(&self) -> impl '_ + Iterator<Item = AssetId> { self.senders .iter() .map(Sender::asset_id) .chain(self.receivers.iter().map(Receiver::asset_id)) - .all(move |i| asset_id.replace(i).eq(&Some(i))) - && asset_id.is_some() + } + + /// Checks that the asset ids of all the senders and receivers matches. + #[inline] + pub fn has_unique_asset_id(&self) -> bool { + let mut asset_id = None; + self.asset_id_iter() + .all(move |i| asset_id.replace(i) == Some(i)) } /// Returns the sum of the asset values of the senders in this transfer. @@ -199,81 +270,7 @@ where /// [`receiver_sum`](Self::receiver_sum). #[inline] pub fn is_balanced(&self) -> bool { - self.sender_sum().eq(&self.receiver_sum()) - } - - /// Builds constraints for secret transfer validity proof. - #[inline] - fn verify<S, R>( - ps: &mut T::ProofSystem, - commitment_scheme: &T::CommitmentScheme, - senders: S, - receivers: R, - ) where - S: IntoIterator<Item = SecretSenderVar<T>>, - R: IntoIterator<Item = SecretReceiverVar<T>>, - AssetId: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, - AssetBalance: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, - AssetBalanceVar<T::ProofSystem>: AddAssign<AssetBalanceVar<T::ProofSystem>>, - { - let commitment_scheme = ps.allocate((commitment_scheme, Public)); - - let mut sender_sum = ps.allocate((&AssetBalance(0), Secret.into())); - let mut receiver_sum = ps.allocate((&AssetBalance(0), Secret.into())); - - #[allow(clippy::needless_collect)] // NOTE: `ps` is being mutated, we need to collect. - let asset_ids = mixed_chain(senders, receivers, |c| match c { - Either::Left(sender) => { - let asset = sender.get_well_formed_asset(ps, &commitment_scheme); - sender_sum += asset.value; - asset.id - } - Either::Right(receiver) => { - let asset = receiver.get_well_formed_asset(ps, &commitment_scheme); - receiver_sum += asset.value; - asset.id - } - }) - .collect::<Vec<_>>(); - - ps.assert_all_eq(asset_ids.iter()); - ps.assert_eq(&sender_sum, &receiver_sum); - } - - #[inline] - fn generate_validity_proof( - &self, - commitment_scheme: &T::CommitmentScheme, - ) -> Option<<T::ProofSystem as ProofSystem>::Proof> - where - AssetId: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, - AssetBalance: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, - AssetBalanceVar<T::ProofSystem>: AddAssign<AssetBalanceVar<T::ProofSystem>>, - { - let mut ps = <T::ProofSystem as Default>::default(); - - #[allow(clippy::needless_collect)] // NOTE: `ps` is being mutated, we need to collect. - let senders = self - .senders - .iter() - .map(|sender| ps.allocate((sender, Derived))) - .collect::<Vec<_>>(); - - #[allow(clippy::needless_collect)] // NOTE: `ps` is being mutated, we need to collect. - let receivers = self - .receivers - .iter() - .map(|receiver| ps.allocate((receiver, Derived))) - .collect::<Vec<_>>(); - - Self::verify( - &mut ps, - commitment_scheme, - senders.into_iter(), - receivers.into_iter(), - ); - - ps.finish().ok() + self.sender_sum() == self.receiver_sum() } /// Converts `self` into its ledger post. @@ -281,32 +278,40 @@ where pub fn into_post( self, commitment_scheme: &T::CommitmentScheme, + utxo_set: &T::UtxoSet, ) -> Option<SecretTransferPost<T, SENDERS, RECEIVERS>> where AssetId: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, AssetBalance: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, AssetBalanceVar<T::ProofSystem>: AddAssign<AssetBalanceVar<T::ProofSystem>>, { - let validity_proof = self.generate_validity_proof(commitment_scheme)?; Some(SecretTransferPost { + validity_proof: Transfer::<T, 0, SENDERS, RECEIVERS, 0>::generate_validity_proof( + None, + &[], + &self.senders, + &self.receivers, + &[], + commitment_scheme, + utxo_set, + )?, sender_posts: array_map(self.senders, Sender::into_post), receiver_posts: array_map(self.receivers, Receiver::into_post), - validity_proof, }) } } /// Secret Sender Post Type -pub type SecretSenderPost<T> = SenderPost<T, <T as SecretTransferConfiguration>::UtxoSet>; +pub type SecretSenderPost<T> = SenderPost<T, <T as TransferConfiguration>::UtxoSet>; /// Secret Receiver Post Type pub type SecretReceiverPost<T> = - ReceiverPost<T, <T as SecretTransferConfiguration>::IntegratedEncryptionScheme>; + ReceiverPost<T, <T as TransferConfiguration>::IntegratedEncryptionScheme>; /// Secret Transfer Post pub struct SecretTransferPost<T, const SENDERS: usize, const RECEIVERS: usize> where - T: SecretTransferConfiguration, + T: TransferConfiguration, { /// Sender Posts pub sender_posts: [SecretSenderPost<T>; SENDERS], @@ -315,12 +320,12 @@ where pub receiver_posts: [SecretReceiverPost<T>; RECEIVERS], /// Validity Proof - pub validity_proof: <T::ProofSystem as ProofSystem>::Proof, + pub validity_proof: Proof<T>, } impl<T, const SENDERS: usize, const RECEIVERS: usize> SecretTransferPost<T, SENDERS, RECEIVERS> where - T: SecretTransferConfiguration, + T: TransferConfiguration, { /// Posts the [`SecretTransferPost`] to the `ledger`. #[inline] @@ -334,14 +339,23 @@ where ProofSystem = T::ProofSystem, > + ?Sized, { - for sender_post in IntoIterator::into_iter(self.sender_posts) { - sender_post.post(ledger)?; - } - for receiver_post in IntoIterator::into_iter(self.receiver_posts) { - receiver_post.post(ledger)?; + TransferPost::<T, 0, SENDERS, RECEIVERS, 0>::from(self).post(ledger) + } +} + +impl<T, const SENDERS: usize, const RECEIVERS: usize> + From<SecretTransferPost<T, SENDERS, RECEIVERS>> for TransferPost<T, 0, SENDERS, RECEIVERS, 0> +where + T: TransferConfiguration, +{ + #[inline] + fn from(post: SecretTransferPost<T, SENDERS, RECEIVERS>) -> Self { + TransferPost { + public_transfer: Default::default(), + secret_sender_posts: post.sender_posts, + secret_receiver_posts: post.receiver_posts, + validity_proof: Some(post.validity_proof), } - ledger.check_proof(self.validity_proof)?; - Ok(()) } } @@ -349,31 +363,222 @@ where pub struct Transfer< T, const SOURCES: usize, - const SINKS: usize, const SENDERS: usize, const RECEIVERS: usize, + const SINKS: usize, > where - T: SecretTransferConfiguration, + T: TransferConfiguration, { - /// Public Transfer - pub public: PublicTransfer<SOURCES, SINKS>, + /// Public Part of the Transfer + public: PublicTransfer<SOURCES, SINKS>, - /// Secret Transfer - pub secret: SecretTransfer<T, SENDERS, RECEIVERS>, + /// Secret Part of the Transfer + secret: SecretTransfer<T, SENDERS, RECEIVERS>, } -impl<T, const SOURCES: usize, const SINKS: usize, const SENDERS: usize, const RECEIVERS: usize> - Transfer<T, SOURCES, SINKS, SENDERS, RECEIVERS> +impl<T, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> + Transfer<T, SOURCES, SENDERS, RECEIVERS, SINKS> where - T: SecretTransferConfiguration, + T: TransferConfiguration, { /// Builds a new [`Transfer`] from a [`PublicTransfer`] and a [`SecretTransfer`]. #[inline] pub fn new( - public: PublicTransfer<SOURCES, SINKS>, - secret: SecretTransfer<T, SENDERS, RECEIVERS>, + asset_id: AssetId, + sources: AssetBalances<SOURCES>, + senders: [SecretSender<T>; SENDERS], + receivers: [SecretReceiver<T>; RECEIVERS], + sinks: AssetBalances<SINKS>, + ) -> Self { + Self::check_sender_side(); + Self::check_receiver_side(); + SecretTransfer::<T, SENDERS, RECEIVERS>::check_size_overflow(); + Self::new_unchecked(asset_id, sources, senders, receivers, sinks) + } + + /// Checks that the sender side is not empty. + #[inline] + fn check_sender_side() { + if SOURCES + SENDERS == 0 { + panic!("Not enough participants on the sender side."); + } + } + + /// Checks that the receiver side is not empty. + #[inline] + fn check_receiver_side() { + if RECEIVERS + SINKS == 0 { + panic!("Not enough participants on the receiver side."); + } + } + + /// Builds a new [`Transfer`] without checking the number of participants on the sender and + /// receiver side. + #[inline] + fn new_unchecked( + asset_id: AssetId, + sources: AssetBalances<SOURCES>, + senders: [SecretSender<T>; SENDERS], + receivers: [SecretReceiver<T>; RECEIVERS], + sinks: AssetBalances<SINKS>, ) -> Self { - Self { public, secret } + Self { + public: PublicTransfer::new(asset_id, sources, sinks), + secret: SecretTransfer::new_unchecked(senders, receivers), + } + } + + /// Checks that there is one unique asset id for all participants in this transfer. + #[inline] + pub fn has_unique_asset_id(&self) -> bool { + if let Some(asset_id) = self.public.asset_id { + self.secret.asset_id_iter().all(move |i| asset_id == i) + } else { + self.secret.has_unique_asset_id() + } + } + + /// Returns the sum of the asset values of the sources in this transfer. + #[inline] + pub fn source_sum(&self) -> AssetBalance { + self.public.source_sum() + } + + /// Returns the sum of the asset values of the senders in this transfer. + #[inline] + pub fn sender_sum(&self) -> AssetBalance { + self.secret.sender_sum() + } + + /// Returns the sum of the asset values of the receivers in this transfer. + #[inline] + pub fn receiver_sum(&self) -> AssetBalance { + self.secret.receiver_sum() + } + + /// Returns the sum of the asset values of the sinks in this transfer. + #[inline] + pub fn sink_sum(&self) -> AssetBalance { + self.public.sink_sum() + } + + /// Checks that the transaction is balanced. + #[inline] + pub fn is_balanced(&self) -> bool { + self.source_sum() + self.sender_sum() == self.receiver_sum() + self.sink_sum() + } + + /// Builds constraints for transfer validity proof. + #[allow(clippy::too_many_arguments)] // NOTE: We don't want to make a new `struct` for this. + #[inline] + fn verify<Sources, Senders, Receivers, Sinks>( + ps: &mut T::ProofSystem, + commitment_scheme: &T::CommitmentSchemeVar, + utxo_set: &T::UtxoSetVar, + base_asset_id: Option<AssetIdVar<T::ProofSystem>>, + sources: Sources, + senders: Senders, + receivers: Receivers, + sinks: Sinks, + ) where + Sources: Iterator<Item = AssetBalanceVar<T::ProofSystem>>, + Senders: Iterator<Item = SecretSenderVar<T>>, + Receivers: Iterator<Item = SecretReceiverVar<T>>, + Sinks: Iterator<Item = AssetBalanceVar<T::ProofSystem>>, + AssetId: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, + AssetBalance: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, + AssetBalanceVar<T::ProofSystem>: AddAssign<AssetBalanceVar<T::ProofSystem>>, + { + let mut sender_sum = AssetBalance(0).as_known(ps, Secret); + let mut receiver_sum = AssetBalance(0).as_known(ps, Secret); + + sources.for_each(|source| sender_sum += source); + sinks.for_each(|sink| receiver_sum += sink); + + #[allow(clippy::needless_collect)] // NOTE: `ps` is being mutated, we need to collect. + let secret_asset_ids = mixed_chain(senders, receivers, |c| match c { + Either::Left(sender) => { + let asset = sender.get_well_formed_asset(ps, commitment_scheme, utxo_set); + sender_sum += asset.value; + asset.id + } + Either::Right(receiver) => { + let asset = receiver.get_well_formed_asset(ps, commitment_scheme); + receiver_sum += asset.value; + asset.id + } + }) + .collect::<Vec<_>>(); + + match base_asset_id { + Some(asset_id) => ps.assert_all_eq_to_base(&asset_id, secret_asset_ids.iter()), + _ => ps.assert_all_eq(secret_asset_ids.iter()), + } + + ps.assert_eq(&sender_sum, &receiver_sum); + } + + /// Generates a validity proof for this transfer. + #[inline] + fn generate_validity_proof( + base_asset_id: Option<AssetId>, + sources: &AssetBalances<SOURCES>, + senders: &[SecretSender<T>; SENDERS], + receivers: &[SecretReceiver<T>; RECEIVERS], + sinks: &AssetBalances<SINKS>, + commitment_scheme: &T::CommitmentScheme, + utxo_set: &T::UtxoSet, + ) -> Option<Proof<T>> + where + AssetId: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, + AssetBalance: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, + AssetBalanceVar<T::ProofSystem>: AddAssign<AssetBalanceVar<T::ProofSystem>>, + { + // FIXME: Find a better way to allocate variables without so much hassle. + + let mut ps = <T::ProofSystem as Default>::default(); + + let base_asset_id = base_asset_id.map(|id| id.as_known(&mut ps, Public)); + + #[allow(clippy::needless_collect)] // NOTE: `ps` is being mutated, we need to collect. + let sources = sources + .iter() + .map(|source| source.as_known(&mut ps, Public)) + .collect::<Vec<_>>(); + + #[allow(clippy::needless_collect)] // NOTE: `ps` is being mutated, we need to collect. + let senders = senders + .iter() + .map(|sender| sender.as_known(&mut ps, Derived)) + .collect::<Vec<_>>(); + + #[allow(clippy::needless_collect)] // NOTE: `ps` is being mutated, we need to collect. + let receivers = receivers + .iter() + .map(|receiver| receiver.as_known(&mut ps, Derived)) + .collect::<Vec<_>>(); + + #[allow(clippy::needless_collect)] // NOTE: `ps` is being mutated, we need to collect. + let sinks = sinks + .iter() + .map(|sink| sink.as_known(&mut ps, Public)) + .collect::<Vec<_>>(); + + let commitment_scheme = commitment_scheme.as_known(&mut ps, Public); + let utxo_set = utxo_set.as_known(&mut ps, Public); + + Self::verify( + &mut ps, + &commitment_scheme, + &utxo_set, + base_asset_id, + sources.into_iter(), + senders.into_iter(), + receivers.into_iter(), + sinks.into_iter(), + ); + + ps.finish().ok() } /// Converts `self` into its ledger post. @@ -381,15 +586,30 @@ where pub fn into_post( self, commitment_scheme: &T::CommitmentScheme, - ) -> Option<TransferPost<T, SOURCES, SINKS, SENDERS, RECEIVERS>> + utxo_set: &T::UtxoSet, + ) -> Option<TransferPost<T, SOURCES, SENDERS, RECEIVERS, SINKS>> where AssetId: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, AssetBalance: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, AssetBalanceVar<T::ProofSystem>: AddAssign<AssetBalanceVar<T::ProofSystem>>, { Some(TransferPost { - public_transfer_post: self.public, - secret_transfer_post: self.secret.into_post(commitment_scheme)?, + validity_proof: if SENDERS == 0 { + None + } else { + Some(Self::generate_validity_proof( + self.public.asset_id, + &self.public.sources, + &self.secret.senders, + &self.secret.receivers, + &self.public.sinks, + commitment_scheme, + utxo_set, + )?) + }, + public_transfer: self.public, + secret_sender_posts: array_map(self.secret.senders, Sender::into_post), + secret_receiver_posts: array_map(self.secret.receivers, Receiver::into_post), }) } } @@ -398,23 +618,29 @@ where pub struct TransferPost< T, const SOURCES: usize, - const SINKS: usize, const SENDERS: usize, const RECEIVERS: usize, + const SINKS: usize, > where - T: SecretTransferConfiguration, + T: TransferConfiguration, { - /// Public Transfer Post - pub public_transfer_post: PublicTransfer<SOURCES, SINKS>, + /// Public Transfer + public_transfer: PublicTransfer<SOURCES, SINKS>, + + /// Secret Sender Posts + secret_sender_posts: [SecretSenderPost<T>; SENDERS], + + /// Secret Receiver Posts + secret_receiver_posts: [SecretReceiverPost<T>; RECEIVERS], - /// Secret Transfer Post - pub secret_transfer_post: SecretTransferPost<T, SENDERS, RECEIVERS>, + /// Validity Proof + validity_proof: Option<Proof<T>>, } -impl<T, const SOURCES: usize, const SINKS: usize, const SENDERS: usize, const RECEIVERS: usize> - TransferPost<T, SOURCES, SINKS, SENDERS, RECEIVERS> +impl<T, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> + TransferPost<T, SOURCES, SENDERS, RECEIVERS, SINKS> where - T: SecretTransferConfiguration, + T: TransferConfiguration, { /// Posts the [`TransferPost`] to the `ledger`. #[inline] @@ -428,8 +654,142 @@ where ProofSystem = T::ProofSystem, > + ?Sized, { - // FIXME: self.public_transfer_post.post(ledger)?; - self.secret_transfer_post.post(ledger)?; + // FIXME: Does the public transfer component need to be validated? + // + // > Probably not. The public part of a transfer comes from the same place that the + // ledger is stored so the ledger can check whether the balances come from accounts + // which have the right amount of assets to spend. Eventually, we either inherit that + // logic from another library or we implement it here in `manta-rs`. + // + let _ = self.public_transfer; + + for sender_post in IntoIterator::into_iter(self.secret_sender_posts) { + sender_post.post(ledger)?; + } + for receiver_post in IntoIterator::into_iter(self.secret_receiver_posts) { + receiver_post.post(ledger)?; + } + if let Some(proof) = self.validity_proof { + ledger.check_proof(proof)?; + } Ok(()) } } + +/// Sealed Trait Module +mod sealed { + /// Sealed Trait + pub trait Sealed {} +} + +/// Transfer Shapes +/// +/// This trait identifies a transfer shape, i.e. the number and type of participants on the sender +/// and receiver side of the transaction. This trait is sealed and can only be used with the +/// existing implementations. +pub trait Shape: sealed::Sealed { + /// Number of Sources + const SOURCES: usize; + + /// Number of Senders + const SENDERS: usize; + + /// Number of Receivers + const RECEIVERS: usize; + + /// Number of Sinks + const SINKS: usize; +} + +/// Canonical Transaction Types +pub mod canonical { + use super::*; + + /// Implements [`Shape`] for a given shape type. + macro_rules! impl_shape { + ($shape:tt, $sources:expr, $senders:expr, $receivers:expr, $sinks:expr) => { + impl sealed::Sealed for $shape {} + impl Shape for $shape { + const SOURCES: usize = $sources; + const SENDERS: usize = $senders; + const RECEIVERS: usize = $receivers; + const SINKS: usize = $sinks; + } + }; + } + + /// Builds a new alias using the given shape type. + macro_rules! alias_type { + ($type:tt, $t:ident, $shape:tt) => { + $type< + $t, + { $shape::SOURCES }, + { $shape::SENDERS }, + { $shape::RECEIVERS }, + { $shape::SINKS }, + > + } + } + + /// Builds a new [`Transfer`] alias using the given shape type. + macro_rules! transfer_alias { + ($t:ident, $shape:tt) => { + alias_type!(Transfer, $t, $shape) + }; + } + + /// Builds a new [`TransferPost`] alias using the given shape type. + macro_rules! transfer_post_alias { + ($t:ident, $shape:tt) => { + alias_type!(TransferPost, $t, $shape) + }; + } + + /// Mint Transaction Shape + /// + /// ``` + /// <1, 0, 1, 0> + /// ``` + #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd)] + pub struct MintShape; + + impl_shape!(MintShape, 1, 0, 1, 0); + + /// Mint Transaction + pub type Mint<T> = transfer_alias!(T, MintShape); + + /// Mint Transaction Ledger Post + pub type MintPost<T> = transfer_post_alias!(T, MintShape); + + /// Private Transfer Transaction Shape + /// + /// ``` + /// <0, 2, 2, 0> + /// ``` + #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd)] + pub struct PrivateTransferShape; + + impl_shape!(PrivateTransferShape, 0, 2, 2, 0); + + /// Private Transfer Transaction + pub type PrivateTransfer<T> = transfer_alias!(T, PrivateTransferShape); + + /// Private Transfer Transaction Post + pub type PrivateTransferPost<T> = transfer_post_alias!(T, PrivateTransferShape); + + /// Reclaim Transaction Shape + /// + /// ``` + /// <0, 2, 1, 1> + /// ``` + #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd)] + pub struct ReclaimShape; + + impl_shape!(ReclaimShape, 0, 2, 1, 1); + + /// Reclaim Transaction + pub type Reclaim<T> = transfer_alias!(T, ReclaimShape); + + /// Reclaim Transaction Post + pub type ReclaimPost<T> = transfer_post_alias!(T, ReclaimShape); +} diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index 7f9307b47..518080549 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -29,7 +29,7 @@ use crate::{ }, keys::{self, DerivedSecretKeyGenerator, ExternalKeys, InternalKeys}, ledger::Ledger, - transfer::{SecretTransfer, SecretTransferConfiguration}, + transfer::{SecretTransfer, TransferConfiguration}, }; use core::{convert::Infallible, fmt::Debug, hash::Hash}; use manta_crypto::ies::{EncryptedMessage, IntegratedEncryptionScheme}; @@ -387,7 +387,7 @@ where rng: &mut R, ) -> Option<SecretTransfer<T, 2, 2>> where - T: SecretTransferConfiguration, + T: TransferConfiguration, R: CryptoRng + RngCore + ?Sized, { // TODO: spec: diff --git a/manta-crypto/src/set/constraint.rs b/manta-crypto/src/set/constraint.rs index ed70eb814..1b8eb8c7f 100644 --- a/manta-crypto/src/set/constraint.rs +++ b/manta-crypto/src/set/constraint.rs @@ -21,7 +21,7 @@ use crate::{ unknown, Alloc, Allocation, AllocationMode, BooleanSystem, Derived, HasVariable, Mode, Var, Variable, }, - set::{ContainmentProof, VerifiedSet}, + set::{ContainmentProof, Set, VerifiedSet}, }; use core::marker::PhantomData; @@ -101,11 +101,12 @@ where /// Asserts that `self` is a valid proof to the fact that `item` is stored in the verified set. #[inline] - pub fn assert_validity(&self, item: &Var<S::Item, P>, ps: &mut P) + pub fn assert_validity<V>(&self, set: &V, item: &ItemVar<V, P>, ps: &mut P) where - P: VerifiedSetProofSystem<S>, + P: BooleanSystem + HasVariable<S::Item>, + V: VerifiedSetVariable<P, Type = S>, { - ps.assert_validity(&self.public_input, &self.secret_witness, item) + set.assert_valid_containment_proof(&self.public_input, &self.secret_witness, item, ps) } } @@ -145,6 +146,46 @@ where } } +/// Item Type for [`VerifiedSetVariable`] +pub type ItemType<V, P> = <<V as Variable<P>>::Type as Set>::Item; + +/// Public Input Type for [`VerifiedSetVariable`] +pub type PublicInputType<V, P> = <<V as Variable<P>>::Type as VerifiedSet>::Public; + +/// Secret Witness Type for [`VerifiedSetVariable`] +pub type SecretWitnessType<V, P> = <<V as Variable<P>>::Type as VerifiedSet>::Secret; + +/// Item Variable for [`VerifiedSetVariable`] +pub type ItemVar<V, P> = Var<ItemType<V, P>, P>; + +/// Public Input Variable for [`VerifiedSetVariable`] +pub type PublicInputVar<V, P> = Var<PublicInputType<V, P>, P>; + +/// Secret Witness Variable for [`VerifiedSetVariable`] +pub type SecretWitnessVar<V, P> = Var<SecretWitnessType<V, P>, P>; + +/// Verified Set Variable +pub trait VerifiedSetVariable<P>: Variable<P> +where + P: BooleanSystem + + HasVariable<ItemType<Self, P>> + + HasVariable<PublicInputType<Self, P>> + + HasVariable<SecretWitnessType<Self, P>> + + ?Sized, + Self::Type: VerifiedSet, +{ + /// Asserts that `public_input` and `secret_witness` form a proof to the fact that `item` is + /// stored in `self`. + fn assert_valid_containment_proof( + &self, + public_input: &PublicInputVar<Self, P>, + secret_witness: &SecretWitnessVar<Self, P>, + item: &ItemVar<Self, P>, + ps: &mut P, + ); +} + +/* TODO[remove]: /// Verified Set Proof System pub trait VerifiedSetProofSystem<S>: BooleanSystem @@ -172,3 +213,4 @@ where item: &Var<S::Item, Self>, ); } +*/ diff --git a/manta-crypto/src/set/mod.rs b/manta-crypto/src/set/mod.rs index af28a2bb9..48027bd46 100644 --- a/manta-crypto/src/set/mod.rs +++ b/manta-crypto/src/set/mod.rs @@ -43,16 +43,6 @@ pub trait Set { } } -/// Verified Containment Trait -pub trait VerifyContainment<Public, Item> -where - Public: ?Sized, - Item: ?Sized, -{ - /// Verifies that `self` is a proof that `item` is contained in some [`VerifiedSet`]. - fn verify(&self, public_input: &Public, item: &Item) -> bool; -} - /// Containment Proof for a [`VerifiedSet`] pub struct ContainmentProof<S> where @@ -86,8 +76,8 @@ where /// Verifies that the `item` is contained in some [`VerifiedSet`]. #[inline] - pub fn verify(&self, item: &S::Item) -> bool { - self.secret_witness.verify(&self.public_input, item) + pub fn verify(&self, set: &S, item: &S::Item) -> bool { + set.check_containment_proof(&self.public_input, &self.secret_witness, item) } /// Returns `true` if `self.public_input` is a valid input for the current state of `set`. @@ -103,7 +93,7 @@ pub trait VerifiedSet: Set { type Public; /// Secret Witness for [`Item`](Set::Item) Containment - type Secret: VerifyContainment<Self::Public, Self::Item>; + type Secret; /// Error Generating a [`ContainmentProof`] type ContainmentError; @@ -111,6 +101,15 @@ pub trait VerifiedSet: Set { /// Returns `true` if `public_input` is a valid input for the current state of `self`. fn check_public_input(&self, public_input: &Self::Public) -> bool; + /// Returns `true` if `public_input` and `secret_witness` make up a valid proof that `item` + /// is stored in `self`. + fn check_containment_proof( + &self, + public_input: &Self::Public, + secret_witness: &Self::Secret, + item: &Self::Item, + ) -> bool; + /// Generates a proof that the given `item` is stored in `self`. fn get_containment_proof( &self, diff --git a/manta-pay/src/accounting/config.rs b/manta-pay/src/accounting/config.rs index 54fc53d5f..b63d22a89 100644 --- a/manta-pay/src/accounting/config.rs +++ b/manta-pay/src/accounting/config.rs @@ -16,8 +16,6 @@ //! Identity and Transfer Configurations -#![allow(unused_imports)] // FIXME: Remove this once we are done implementing config. - use crate::{ accounting::ledger::UtxoSet, crypto::{ @@ -30,11 +28,9 @@ use crate::{ }; use ark_ed_on_bls12_381::{constraints::EdwardsVar, EdwardsProjective, Fq}; use manta_accounting::{ - identity::{IdentityConfiguration, IdentityProofSystemConfiguration}, - transfer::SecretTransferConfiguration, + identity::IdentityProofSystemConfiguration, transfer::TransferConfiguration, }; use manta_crypto::{commitment::CommitmentScheme, PseudorandomFunctionFamily}; -use rand_chacha::ChaCha20Rng; /// Pedersen Window Parameters #[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -65,12 +61,15 @@ pub type PedersenCommitmentVar = pedersen::constraint::PedersenCommitmentVar< PedersenCommitmentProjectiveCurveVar, >; +/// Proof System +pub type ProofSystem = ArkProofSystem<Fq>; + /// Manta Pay Configuration #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Configuration; impl IdentityProofSystemConfiguration for Configuration { - type BooleanSystem = ArkProofSystem<Fq>; + type BooleanSystem = ProofSystem; type PseudorandomFunctionFamilySeed = <Blake2s as PseudorandomFunctionFamily>::Seed; type PseudorandomFunctionFamilyInput = <Blake2s as PseudorandomFunctionFamily>::Input; type PseudorandomFunctionFamilyOutput = <Blake2s as PseudorandomFunctionFamily>::Output; @@ -84,9 +83,11 @@ impl IdentityProofSystemConfiguration for Configuration { } /* TODO: -impl SecretTransferConfiguration for Configuration { +impl TransferConfiguration for Configuration { type ProofSystem = ArkProofSystem<Fq>; type IntegratedEncryptionScheme = IES; + type UtxoPublicInput = (); + type UtxoSecretWitness = (); type UtxoSet = UtxoSet; } */ diff --git a/manta-pay/src/accounting/ledger.rs b/manta-pay/src/accounting/ledger.rs index 0ad21ae52..54229114b 100644 --- a/manta-pay/src/accounting/ledger.rs +++ b/manta-pay/src/accounting/ledger.rs @@ -19,11 +19,13 @@ // FIXME: Use more type-safe definitions for `VoidNumber` and `Utxo`. use crate::{ - accounting::config::{PedersenCommitmentProjectiveCurve, PedersenCommitmentWindowParameters}, + accounting::config::{ + PedersenCommitment, PedersenCommitmentProjectiveCurve, PedersenCommitmentWindowParameters, + ProofSystem, + }, crypto::{ - constraint::ArkProofSystem, ies::EncryptedAsset, - merkle_tree::{self, MerkleTree, Path, Root}, + merkle_tree::{self, MerkleTree, Path}, }, }; use alloc::{vec, vec::Vec}; @@ -33,21 +35,23 @@ use blake2::{ }; use manta_accounting::{Ledger as LedgerTrait, ProofPostError}; use manta_crypto::{ - constraint::ProofSystem, - set::{ContainmentProof, Set, VerifiedSet}, + commitment::CommitmentScheme, + constraint::{Public, PublicOrSecret, Secret, Var}, + set::{constraint::VerifiedSetVariable, ContainmentProof, Set, VerifiedSet}, }; -use manta_util::into_array_unchecked; +use manta_util::{as_bytes, into_array_unchecked}; /// Void Number type VoidNumber = [u8; 32]; /// Unspent Transaction Output -type Utxo = [u8; 32]; +type Utxo = [u8; 32]; // TODO: <PedersenCommitment as CommitmentScheme>::Output; /// UTXO Shard Root -type UtxoShardRoot = Root<PedersenCommitmentWindowParameters, PedersenCommitmentProjectiveCurve>; +type UtxoShardRoot = + merkle_tree::Root<PedersenCommitmentWindowParameters, PedersenCommitmentProjectiveCurve>; -/// Merkle Tree Parameters +/// UTXO Set Parameters type Parameters = merkle_tree::Parameters<PedersenCommitmentWindowParameters, PedersenCommitmentProjectiveCurve>; @@ -140,14 +144,32 @@ impl Set for UtxoSet { impl VerifiedSet for UtxoSet { type Public = UtxoShardRoot; - type Secret = Path<PedersenCommitmentWindowParameters, PedersenCommitmentProjectiveCurve, Utxo>; + type Secret = Path<PedersenCommitmentWindowParameters, PedersenCommitmentProjectiveCurve>; // TODO: Give a more informative error. type ContainmentError = (); #[inline] - fn check_public_input(&self, public: &Self::Public) -> bool { - self.root_exists(public) + fn check_public_input(&self, public_input: &Self::Public) -> bool { + self.root_exists(public_input) + } + + #[inline] + fn check_containment_proof( + &self, + public_input: &Self::Public, + secret_witness: &Self::Secret, + item: &Self::Item, + ) -> bool { + secret_witness + .path + .verify( + &self.parameters.leaf, + &self.parameters.two_to_one, + public_input, + &as_bytes!(item), + ) + .expect("As of arkworks 0.3.0, this never fails.") } #[inline] @@ -188,7 +210,7 @@ impl LedgerTrait for Ledger { type EncryptedAsset = EncryptedAsset; - type ProofSystem = ArkProofSystem; + type ProofSystem = ProofSystem; #[inline] fn utxos(&self) -> &Self::UtxoSet { @@ -229,7 +251,7 @@ impl LedgerTrait for Ledger { #[inline] fn check_proof( &self, - proof: <Self::ProofSystem as ProofSystem>::Proof, + proof: <Self::ProofSystem as constraint::ProofSystem>::Proof, ) -> Result<(), ProofPostError<Self>> { let _ = proof; todo!() diff --git a/manta-pay/src/crypto/merkle_tree.rs b/manta-pay/src/crypto/merkle_tree.rs index 378e70315..e4d81127a 100644 --- a/manta-pay/src/crypto/merkle_tree.rs +++ b/manta-pay/src/crypto/merkle_tree.rs @@ -34,7 +34,7 @@ use ark_crypto_primitives::{ }, }; use core::marker::PhantomData; -use manta_crypto::set::{ContainmentProof, VerifiedSet, VerifyContainment}; +use manta_crypto::set::{ContainmentProof, VerifiedSet}; use manta_util::{as_bytes, Concat}; /// Merkle Tree Root @@ -49,72 +49,58 @@ where C: ProjectiveCurve, { /// Leaf Hash Parameters - leaf: LeafParam<MerkleTreeConfiguration<W, C>>, + pub(crate) leaf: LeafParam<MerkleTreeConfiguration<W, C>>, /// Two-to-One Hash Parameters - two_to_one: TwoToOneParam<MerkleTreeConfiguration<W, C>>, + pub(crate) two_to_one: TwoToOneParam<MerkleTreeConfiguration<W, C>>, } /// Merkle Tree Path #[derive(derivative::Derivative)] #[derivative(Clone(bound = ""))] -pub struct Path<W, C, T> +pub struct Path<W, C> where W: PedersenWindow, C: ProjectiveCurve, { - /// Merkle Tree Parameters - parameters: Parameters<W, C>, - /// Path - path: MerkleTreePath<MerkleTreeConfiguration<W, C>>, - - /// Type Parameter Marker - __: PhantomData<T>, + pub(crate) path: MerkleTreePath<MerkleTreeConfiguration<W, C>>, } -impl<W, C, T> Path<W, C, T> +impl<W, C> Path<W, C> where W: PedersenWindow, C: ProjectiveCurve, { - /// Builds a new [`Path`] from `parameters` and `path`. + /// Builds a new [`Path`] from `path`. #[inline] - fn new( - parameters: Parameters<W, C>, - path: MerkleTreePath<MerkleTreeConfiguration<W, C>>, - ) -> Self { - Self { - parameters, - path, - __: PhantomData, - } + fn new(path: MerkleTreePath<MerkleTreeConfiguration<W, C>>) -> Self { + Self { path } } } +/// Path Variable +pub struct PathVar<W, C>(PhantomData<(W, C)>) +where + W: PedersenWindow, + C: ProjectiveCurve; + /// Merkle Tree #[derive(derivative::Derivative)] #[derivative(Clone(bound = ""))] -pub struct MerkleTree<W, C, T> +pub struct MerkleTree<W, C> where W: PedersenWindow, C: ProjectiveCurve, { - /// Merkle Tree Parameters - parameters: Parameters<W, C>, - /// Merkle Tree - tree: ArkMerkleTree<MerkleTreeConfiguration<W, C>>, - - /// Type Parameter Marker - __: PhantomData<T>, + pub(crate) tree: ArkMerkleTree<MerkleTreeConfiguration<W, C>>, } -impl<W, C, T> MerkleTree<W, C, T> +impl<W, C> MerkleTree<W, C> where W: PedersenWindow, C: ProjectiveCurve, - T: Concat<Item = u8>, { /// Builds a new [`MerkleTree`]. /// @@ -122,7 +108,10 @@ where /// /// The length of `leaves` must be a power of 2 or this function will panic. #[inline] - pub fn new(parameters: &Parameters<W, C>, leaves: &[T]) -> Option<Self> { + pub fn new<T>(parameters: &Parameters<W, C>, leaves: &[T]) -> Option<Self> + where + T: Concat<Item = u8>, + { Some(Self { tree: ArkMerkleTree::new( &parameters.leaf, @@ -133,14 +122,15 @@ where .collect::<Vec<_>>(), ) .ok()?, - parameters: parameters.clone(), - __: PhantomData, }) } /// Computes the [`Root`] of the [`MerkleTree`] built from the `leaves`. #[inline] - pub fn build_root(parameters: &Parameters<W, C>, leaves: &[T]) -> Option<Root<W, C>> { + pub fn build_root<T>(parameters: &Parameters<W, C>, leaves: &[T]) -> Option<Root<W, C>> + where + T: Concat<Item = u8>, + { Some(Self::new(parameters, leaves)?.root()) } @@ -154,14 +144,11 @@ where #[inline] pub fn get_containment_proof<S>(&self, index: usize) -> Option<ContainmentProof<S>> where - S: VerifiedSet<Public = Root<W, C>, Secret = Path<W, C, T>>, + S: VerifiedSet<Public = Root<W, C>, Secret = Path<W, C>>, { Some(ContainmentProof::new( self.root(), - Path::new( - self.parameters.clone(), - self.tree.generate_proof(index).ok()?, - ), + Path::new(self.tree.generate_proof(index).ok()?), )) } } @@ -182,22 +169,3 @@ where type LeafHash = CRH<C, W>; type TwoToOneHash = CRH<C, W>; } - -impl<W, C, T> VerifyContainment<Root<W, C>, T> for Path<W, C, T> -where - W: PedersenWindow, - C: ProjectiveCurve, - T: Concat<Item = u8>, -{ - #[inline] - fn verify(&self, root: &Root<W, C>, item: &T) -> bool { - self.path - .verify( - &self.parameters.leaf, - &self.parameters.two_to_one, - root, - &as_bytes!(item), - ) - .expect("As of arkworks 0.3.0, this never fails.") - } -} From cb655c81a96879e118a00dad8c4287898815f999 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 15 Sep 2021 02:45:05 -0400 Subject: [PATCH 033/275] WIP: finish type-check for ark, some impls remain --- manta-accounting/src/transfer.rs | 6 +- manta-pay/src/accounting/config.rs | 20 +- manta-pay/src/accounting/ledger.rs | 140 ++++-- manta-pay/src/crypto/commitment/pedersen.rs | 106 ++-- .../src/crypto/constraint/proof_system.rs | 6 +- manta-pay/src/crypto/ies.rs | 6 +- manta-pay/src/crypto/merkle_tree.rs | 464 ++++++++++++++++-- manta-pay/src/crypto/prf/blake2s.rs | 37 +- 8 files changed, 605 insertions(+), 180 deletions(-) diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index b41400d02..07b8a4fb1 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -747,7 +747,7 @@ pub mod canonical { /// Mint Transaction Shape /// - /// ``` + /// ```text /// <1, 0, 1, 0> /// ``` #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd)] @@ -763,7 +763,7 @@ pub mod canonical { /// Private Transfer Transaction Shape /// - /// ``` + /// ```text /// <0, 2, 2, 0> /// ``` #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd)] @@ -779,7 +779,7 @@ pub mod canonical { /// Reclaim Transaction Shape /// - /// ``` + /// ```text /// <0, 2, 1, 1> /// ``` #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd)] diff --git a/manta-pay/src/accounting/config.rs b/manta-pay/src/accounting/config.rs index b63d22a89..193539202 100644 --- a/manta-pay/src/accounting/config.rs +++ b/manta-pay/src/accounting/config.rs @@ -17,7 +17,7 @@ //! Identity and Transfer Configurations use crate::{ - accounting::ledger::UtxoSet, + accounting::ledger::{UtxoSet, UtxoSetVar}, crypto::{ commitment::pedersen::{self, PedersenWindow}, constraint::ArkProofSystem, @@ -30,7 +30,7 @@ use ark_ed_on_bls12_381::{constraints::EdwardsVar, EdwardsProjective, Fq}; use manta_accounting::{ identity::IdentityProofSystemConfiguration, transfer::TransferConfiguration, }; -use manta_crypto::{commitment::CommitmentScheme, PseudorandomFunctionFamily}; +use manta_crypto::{commitment::CommitmentScheme, set::VerifiedSet, PseudorandomFunctionFamily}; /// Pedersen Window Parameters #[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -61,8 +61,11 @@ pub type PedersenCommitmentVar = pedersen::constraint::PedersenCommitmentVar< PedersenCommitmentProjectiveCurveVar, >; +/// Constraint Field +pub type ConstraintField = Fq; + /// Proof System -pub type ProofSystem = ArkProofSystem<Fq>; +pub type ProofSystem = ArkProofSystem<ConstraintField>; /// Manta Pay Configuration #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -74,7 +77,7 @@ impl IdentityProofSystemConfiguration for Configuration { type PseudorandomFunctionFamilyInput = <Blake2s as PseudorandomFunctionFamily>::Input; type PseudorandomFunctionFamilyOutput = <Blake2s as PseudorandomFunctionFamily>::Output; type PseudorandomFunctionFamily = Blake2s; - type PseudorandomFunctionFamilyVar = Blake2sVar<Fq>; + type PseudorandomFunctionFamilyVar = Blake2sVar<ConstraintField>; type CommitmentSchemeRandomness = <PedersenCommitment as CommitmentScheme>::Randomness; type CommitmentSchemeOutput = <PedersenCommitment as CommitmentScheme>::Output; type CommitmentScheme = PedersenCommitment; @@ -82,12 +85,11 @@ impl IdentityProofSystemConfiguration for Configuration { type Rng = ChaCha20RngBlake2sSeedable; } -/* TODO: impl TransferConfiguration for Configuration { - type ProofSystem = ArkProofSystem<Fq>; + type ProofSystem = ProofSystem; type IntegratedEncryptionScheme = IES; - type UtxoPublicInput = (); - type UtxoSecretWitness = (); + type UtxoSetPublicInput = <UtxoSet as VerifiedSet>::Public; + type UtxoSetSecretWitness = <UtxoSet as VerifiedSet>::Secret; type UtxoSet = UtxoSet; + type UtxoSetVar = UtxoSetVar; } -*/ diff --git a/manta-pay/src/accounting/ledger.rs b/manta-pay/src/accounting/ledger.rs index 54229114b..5f3da2e5e 100644 --- a/manta-pay/src/accounting/ledger.rs +++ b/manta-pay/src/accounting/ledger.rs @@ -16,16 +16,16 @@ //! Ledger Implementation -// FIXME: Use more type-safe definitions for `VoidNumber` and `Utxo`. +// FIXME: How should we handle serdes? use crate::{ accounting::config::{ - PedersenCommitment, PedersenCommitmentProjectiveCurve, PedersenCommitmentWindowParameters, - ProofSystem, + Configuration, PedersenCommitmentProjectiveCurve, PedersenCommitmentProjectiveCurveVar, + PedersenCommitmentWindowParameters, ProofSystem, }, crypto::{ ies::EncryptedAsset, - merkle_tree::{self, MerkleTree, Path}, + merkle_tree::{self, MerkleTree}, }, }; use alloc::{vec, vec::Vec}; @@ -33,33 +33,69 @@ use blake2::{ digest::{Update, VariableOutput}, VarBlake2s, }; -use manta_accounting::{Ledger as LedgerTrait, ProofPostError}; +use manta_accounting::{identity, Ledger as LedgerTrait, ProofPostError}; use manta_crypto::{ - commitment::CommitmentScheme, - constraint::{Public, PublicOrSecret, Secret, Var}, + constraint::{self, Alloc, Allocation, Constant, Variable}, set::{constraint::VerifiedSetVariable, ContainmentProof, Set, VerifiedSet}, }; -use manta_util::{as_bytes, into_array_unchecked}; +use manta_util::{as_bytes, concatenate, into_array_unchecked}; /// Void Number -type VoidNumber = [u8; 32]; +type VoidNumber = identity::VoidNumber<Configuration>; /// Unspent Transaction Output -type Utxo = [u8; 32]; // TODO: <PedersenCommitment as CommitmentScheme>::Output; +type Utxo = identity::Utxo<Configuration>; + +/// UTXO Variable +type UtxoVar = identity::UtxoVar<Configuration>; /// UTXO Shard Root -type UtxoShardRoot = - merkle_tree::Root<PedersenCommitmentWindowParameters, PedersenCommitmentProjectiveCurve>; +type Root = merkle_tree::RootWrapper< + PedersenCommitmentWindowParameters, + PedersenCommitmentProjectiveCurve, + PedersenCommitmentProjectiveCurveVar, +>; + +/// UTXO Shard Root Variable +type RootVar = merkle_tree::RootVar< + PedersenCommitmentWindowParameters, + PedersenCommitmentProjectiveCurve, + PedersenCommitmentProjectiveCurveVar, +>; /// UTXO Set Parameters -type Parameters = - merkle_tree::Parameters<PedersenCommitmentWindowParameters, PedersenCommitmentProjectiveCurve>; +type Parameters = merkle_tree::ParametersWrapper< + PedersenCommitmentWindowParameters, + PedersenCommitmentProjectiveCurve, + PedersenCommitmentProjectiveCurveVar, +>; + +/// UTXO Set Parameters Variable +type ParametersVar = merkle_tree::ParametersVar< + PedersenCommitmentWindowParameters, + PedersenCommitmentProjectiveCurve, + PedersenCommitmentProjectiveCurveVar, +>; + +/// UTXO Set Path +type Path = merkle_tree::PathWrapper< + PedersenCommitmentWindowParameters, + PedersenCommitmentProjectiveCurve, + PedersenCommitmentProjectiveCurveVar, +>; + +/// UTXO Set Path Variable +type PathVar = merkle_tree::PathVar< + PedersenCommitmentWindowParameters, + PedersenCommitmentProjectiveCurve, + PedersenCommitmentProjectiveCurveVar, +>; /// UTXO Shard #[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] pub struct UtxoShard { /// Shard Root - root: UtxoShardRoot, + root: Root, /// Unspent Transaction Outputs utxos: Vec<Utxo>, @@ -91,7 +127,7 @@ impl UtxoSet { #[inline] fn shard_index(utxo: &Utxo) -> usize { let mut hasher = VarBlake2s::new(1).expect("Failed to generate Variable Blake2s hasher."); - hasher.update(&utxo); + hasher.update(&as_bytes!(utxo)); let mut res: usize = 0; hasher.finalize_variable(|x| res = x[0] as usize); res @@ -105,7 +141,7 @@ impl UtxoSet { /// Returns `true` if the `root` belongs to some shard. #[inline] - pub fn root_exists(&self, root: &UtxoShardRoot) -> bool { + pub fn root_exists(&self, root: &Root) -> bool { self.shards.iter().any(move |s| s.root == *root) } @@ -126,12 +162,13 @@ impl Set for UtxoSet { #[inline] fn try_insert(&mut self, item: Self::Item) -> Result<(), Self::Item> { + // FIXME: This will panic because `shard.utxos.len()` is not always a power of two. let shard = &mut self.shards[Self::shard_index(&item)]; if shard.utxos.contains(&item) { return Err(item); } shard.utxos.push(item); - match MerkleTree::build_root(&self.parameters, &shard.utxos) { + match MerkleTree::build_root_wrapped(&self.parameters, &shard.utxos) { Some(root) => { shard.root = root; Ok(()) @@ -142,9 +179,9 @@ impl Set for UtxoSet { } impl VerifiedSet for UtxoSet { - type Public = UtxoShardRoot; + type Public = Root; - type Secret = Path<PedersenCommitmentWindowParameters, PedersenCommitmentProjectiveCurve>; + type Secret = Path; // TODO: Give a more informative error. type ContainmentError = (); @@ -161,15 +198,7 @@ impl VerifiedSet for UtxoSet { secret_witness: &Self::Secret, item: &Self::Item, ) -> bool { - secret_witness - .path - .verify( - &self.parameters.leaf, - &self.parameters.two_to_one, - public_input, - &as_bytes!(item), - ) - .expect("As of arkworks 0.3.0, this never fails.") + self.parameters.verify(public_input, secret_witness, item) } #[inline] @@ -177,17 +206,64 @@ impl VerifiedSet for UtxoSet { &self, item: &Self::Item, ) -> Result<ContainmentProof<Self>, Self::ContainmentError> { + // FIXME: This will panic because `utxos.len()` is not always a power of two. let utxos = &self.shards[Self::shard_index(item)].utxos; match utxos.iter().position(move |u| u == item) { - Some(index) => MerkleTree::new(&self.parameters, utxos) + Some(index) => MerkleTree::from_wrapped(&self.parameters, utxos) .ok_or(())? - .get_containment_proof(index) + .get_wrapped_containment_proof(index) .ok_or(()), _ => Err(()), } } } +/// UTXO Set Variable +#[derive(Clone)] +pub struct UtxoSetVar(ParametersVar); + +impl Variable<ProofSystem> for UtxoSetVar { + type Mode = Constant; + type Type = UtxoSet; +} + +impl Alloc<ProofSystem> for UtxoSet { + type Mode = Constant; + + type Variable = UtxoSetVar; + + #[inline] + fn variable<'t>( + ps: &mut ProofSystem, + allocation: impl Into<Allocation<'t, Self, ProofSystem>>, + ) -> Self::Variable + where + Self: 't, + { + UtxoSetVar(match allocation.into() { + Allocation::Known(this, mode) => this.parameters.as_known(ps, mode), + _ => unreachable!( + "Since we use a constant allocation mode, we always know the variable value." + ), + }) + } +} + +impl VerifiedSetVariable<ProofSystem> for UtxoSetVar { + #[inline] + fn assert_valid_containment_proof( + &self, + public_input: &RootVar, + secret_witness: &PathVar, + item: &UtxoVar, + ps: &mut ProofSystem, + ) { + let _ = ps; + self.0 + .assert_verified(public_input, secret_witness, &concatenate!(item)) + } +} + /// Ledger pub struct Ledger { /// Void Numbers @@ -200,7 +276,6 @@ pub struct Ledger { encrypted_assets: Vec<EncryptedAsset>, } -/* TODO: impl LedgerTrait for Ledger { type VoidNumber = VoidNumber; @@ -257,4 +332,3 @@ impl LedgerTrait for Ledger { todo!() } } -*/ diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs index 8a5c89144..b8d6210f6 100644 --- a/manta-pay/src/crypto/commitment/pedersen.rs +++ b/manta-pay/src/crypto/commitment/pedersen.rs @@ -163,20 +163,23 @@ pub mod constraint { /// Pedersen Commitment Output Wrapper #[derive(derivative::Derivative)] - #[derivative(Clone(bound = ""))] - pub struct PedersenCommitmentOutputWrapper<W, C, GG> + #[derivative( + Clone(bound = ""), + Debug(bound = ""), + Default(bound = ""), + Eq(bound = ""), + Hash(bound = ""), + PartialEq(bound = "") + )] + pub struct PedersenCommitmentOutputWrapper<W, C, GG>( + PedersenCommitmentOutput<W, C>, + PhantomData<GG>, + ) where W: PedersenWindow, C: ProjectiveCurve, GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - { - /// Commitment Output - output: PedersenCommitmentOutput<W, C>, - - /// Type Parameter Marker - __: PhantomData<GG>, - } + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; impl<W, C, GG> From<PedersenCommitmentOutput<W, C>> for PedersenCommitmentOutputWrapper<W, C, GG> where @@ -187,10 +190,7 @@ pub mod constraint { { #[inline] fn from(output: PedersenCommitmentOutput<W, C>) -> Self { - Self { - output, - __: PhantomData, - } + Self(output, PhantomData) } } @@ -203,7 +203,7 @@ pub mod constraint { { #[inline] fn from(wrapper: PedersenCommitmentOutputWrapper<W, C, GG>) -> Self { - wrapper.output + wrapper.0 } } @@ -221,26 +221,19 @@ pub mod constraint { where A: ConcatAccumulator<Self::Item> + ?Sized, { - self.output.concat(accumulator) + self.0.concat(accumulator) } } /// Pedersen Commitment Scheme Wrapper #[derive(derivative::Derivative)] #[derivative(Clone(bound = ""))] - pub struct PedersenCommitmentWrapper<W, C, GG> + pub struct PedersenCommitmentWrapper<W, C, GG>(PedersenCommitment<W, C>, PhantomData<GG>) where W: PedersenWindow, C: ProjectiveCurve, GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - { - /// Commitment Scheme - commitment_scheme: PedersenCommitment<W, C>, - - /// Type Parameter Marker - __: PhantomData<GG>, - } + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; impl<W, C, GG> PedersenCommitmentWrapper<W, C, GG> where @@ -265,10 +258,7 @@ pub mod constraint { { #[inline] fn from(commitment_scheme: PedersenCommitment<W, C>) -> Self { - Self { - commitment_scheme, - __: PhantomData, - } + Self(commitment_scheme, PhantomData) } } @@ -281,7 +271,7 @@ pub mod constraint { { #[inline] fn from(wrapper: PedersenCommitmentWrapper<W, C, GG>) -> Self { - wrapper.commitment_scheme + wrapper.0 } } @@ -300,24 +290,20 @@ pub mod constraint { #[inline] fn commit(&self, input: Self::InputBuffer, randomness: &Self::Randomness) -> Self::Output { - self.commitment_scheme.commit(input, randomness).into() + self.0.commit(input, randomness).into() } } /// Pedersen Commitment Randomness Variable #[derive(derivative::Derivative)] #[derivative(Clone(bound = ""))] - pub struct PedersenCommitmentRandomnessVar<W, C> + pub struct PedersenCommitmentRandomnessVar<W, C>( + RandomnessVar<ConstraintField<C>>, + PhantomData<W>, + ) where W: PedersenWindow, - C: ProjectiveCurve, - { - /// Randomness Variable - randomness_var: RandomnessVar<ConstraintField<C>>, - - /// Type Parameter Marker - __: PhantomData<W>, - } + C: ProjectiveCurve; impl<W, C> Variable<ProofSystem<C>> for PedersenCommitmentRandomnessVar<W, C> where @@ -345,8 +331,6 @@ pub mod constraint { where Self: 't, { - // FIXME: implement - let _ = (ps, allocation); todo!() } } @@ -354,19 +338,12 @@ pub mod constraint { /// Pedersen Commitment Output Variable #[derive(derivative::Derivative)] #[derivative(Clone(bound = ""))] - pub struct PedersenCommitmentOutputVar<W, C, GG> + pub struct PedersenCommitmentOutputVar<W, C, GG>(GG, PhantomData<(W, C)>) where W: PedersenWindow, C: ProjectiveCurve, GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - { - /// Output Variable - output_var: GG, - - /// Type Parameter Marker - __: PhantomData<(W, C)>, - } + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; impl<W, C, GG> PedersenCommitmentOutputVar<W, C, GG> where @@ -378,10 +355,7 @@ pub mod constraint { /// Builds a new [`PedersenCommitmentOutputVar`] from `output_var`. #[inline] fn new(output_var: GG) -> Self { - Self { - output_var, - __: PhantomData, - } + Self(output_var, PhantomData) } } @@ -399,12 +373,7 @@ pub mod constraint { where A: ConcatAccumulator<Self::Item> + ?Sized, { - accumulator.extend( - &self - .output_var - .to_bytes() - .expect("This is not allowed to fail."), - ); + accumulator.extend(&self.0.to_bytes().expect("This is not allowed to fail.")); } } @@ -438,8 +407,6 @@ pub mod constraint { where Self: 't, { - // FIXME: implement - let _ = (ps, allocation); todo!() } } @@ -464,19 +431,12 @@ pub mod constraint { /// Pedersen Commitment Scheme Variable #[derive(derivative::Derivative)] #[derivative(Clone(bound = ""))] - pub struct PedersenCommitmentVar<W, C, GG> + pub struct PedersenCommitmentVar<W, C, GG>(ParametersVar<C, GG>, PhantomData<W>) where W: PedersenWindow, C: ProjectiveCurve, GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - { - /// Commitment Parameters Variable - parameters: ParametersVar<C, GG>, - - /// Type Parameter Marker - __: PhantomData<W>, - } + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; impl<W, C, GG> Variable<ProofSystem<C>> for PedersenCommitmentVar<W, C, GG> where @@ -537,7 +497,7 @@ pub mod constraint { #[inline] fn commit(&self, input: Self::InputBuffer, randomness: &Self::Randomness) -> Self::Output { PedersenCommitmentOutputVar::new( - CommGadget::<_, _, W>::commit(&self.parameters, &input, &randomness.randomness_var) + CommGadget::<_, _, W>::commit(&self.0, &input, &randomness.0) .expect("Failure outcomes are not accepted."), ) } diff --git a/manta-pay/src/crypto/constraint/proof_system.rs b/manta-pay/src/crypto/constraint/proof_system.rs index 5c8064559..bc0de9f4e 100644 --- a/manta-pay/src/crypto/constraint/proof_system.rs +++ b/manta-pay/src/crypto/constraint/proof_system.rs @@ -39,13 +39,13 @@ type SynthesisResult<T> = Result<T, SynthesisError>; /// Returns an empty variable assignment. #[inline] -const fn empty<T>() -> SynthesisResult<T> { +pub(crate) const fn empty<T>() -> SynthesisResult<T> { Err(SynthesisError::AssignmentMissing) } /// Returns a filled variable assignment. #[inline] -fn full<T>(t: T) -> impl FnOnce() -> SynthesisResult<T> { +pub(crate) fn full<T>(t: T) -> impl FnOnce() -> SynthesisResult<T> { move || Ok(t) } @@ -99,7 +99,7 @@ where F: Field, { /// Constraint System - cs: ConstraintSystemRef<F>, + pub(crate) cs: ConstraintSystemRef<F>, } impl<F> Default for ArkProofSystem<F> diff --git a/manta-pay/src/crypto/ies.rs b/manta-pay/src/crypto/ies.rs index e03b2a6f5..802351316 100644 --- a/manta-pay/src/crypto/ies.rs +++ b/manta-pay/src/crypto/ies.rs @@ -16,7 +16,7 @@ //! IES Implementation -// FIXME: make sure secret keys are protected +// FIXME: Make sure secret keys are protected. use aes_gcm::{ aead::{Aead, NewAead}, @@ -40,8 +40,8 @@ pub type PublicKey = [u8; 32]; pub type SecretKey = [u8; 32]; /// Asset Ciphertext Type -// FIXME: this should be automatically calculated from [`Asset`] -// FIXME: is this calculation correct? how do we know? +// FIXME: This should be automatically calculated from [`Asset`]. +// FIXME: Is this calculation correct and how do we know? pub type AssetCiphertext = [u8; Asset::SIZE + 16]; /// Ephemeral Public Key Type diff --git a/manta-pay/src/crypto/merkle_tree.rs b/manta-pay/src/crypto/merkle_tree.rs index e4d81127a..f0282f8ab 100644 --- a/manta-pay/src/crypto/merkle_tree.rs +++ b/manta-pay/src/crypto/merkle_tree.rs @@ -24,21 +24,78 @@ // own `CRH` and `TwoToOneCRH` traits and then in the configuration we align them with the // Pedersen settings. -use crate::crypto::commitment::pedersen::{PedersenWindow, ProjectiveCurve}; +use crate::crypto::{ + commitment::pedersen::{PedersenWindow, ProjectiveCurve}, + constraint::{empty, full, ArkProofSystem}, +}; use alloc::vec::Vec; use ark_crypto_primitives::{ - crh::pedersen::CRH, + crh::{ + constraints::{CRHGadget as CRHGadgetTrait, TwoToOneCRHGadget as TwoToOneCRHGadgetTrait}, + pedersen::{constraints::CRHGadget, CRH}, + }, merkle_tree::{ - Config as MerkleTreeConfig, LeafParam, MerkleTree as ArkMerkleTree, Path as MerkleTreePath, - TwoToOneDigest, TwoToOneParam, + constraints::PathVar as ArkPathVar, Config, LeafParam as ArkLeafParam, + MerkleTree as ArkMerkleTree, Path as ArkPath, TwoToOneDigest, + TwoToOneParam as ArkTwoToOneParam, }, }; +use ark_ff::Field; +use ark_r1cs_std::{ + alloc::AllocVar, + boolean::Boolean, + eq::EqGadget, + groups::{CurveVar, GroupOpsBounds}, + uint8::UInt8, +}; +use ark_relations::ns; use core::marker::PhantomData; -use manta_crypto::set::{ContainmentProof, VerifiedSet}; +use manta_crypto::{ + constraint::{Alloc, Allocation, Constant, Public, Secret, Variable}, + set::{ContainmentProof, VerifiedSet}, +}; use manta_util::{as_bytes, Concat}; -/// Merkle Tree Root -pub type Root<W, C> = TwoToOneDigest<MerkleTreeConfiguration<W, C>>; +/// Constraint Field Type +pub type ConstraintField<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField; + +/// Proof System +type ProofSystem<C> = ArkProofSystem<ConstraintField<C>>; + +/// Merkle Tree Configuration +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Configuration<W, C>(PhantomData<(W, C)>) +where + W: PedersenWindow, + C: ProjectiveCurve; + +impl<W, C> Config for Configuration<W, C> +where + W: PedersenWindow, + C: ProjectiveCurve, +{ + type LeafHash = CRH<C, W>; + type TwoToOneHash = CRH<C, W>; +} + +/// Leaf Hash Parameters Type +type LeafParam<W, C> = ArkLeafParam<Configuration<W, C>>; + +/// Two-to-One Hash Parameters Type +type TwoToOneParam<W, C> = ArkTwoToOneParam<Configuration<W, C>>; + +/// Leaf Hash Parameters Variable +type LeafParamVar<W, C, GG> = <CRHGadget<C, GG, W> as CRHGadgetTrait< + <Configuration<W, C> as Config>::LeafHash, + ConstraintField<C>, +>>::ParametersVar; + +/// Two-to-One Hash Parameters Variable +type TwoToOneParamVar<W, C, GG> = <CRHGadget<C, GG, W> as TwoToOneCRHGadgetTrait< + <Configuration<W, C> as Config>::TwoToOneHash, + ConstraintField<C>, +>>::ParametersVar; /// Merkle Tree Parameters #[derive(derivative::Derivative)] @@ -49,54 +106,325 @@ where C: ProjectiveCurve, { /// Leaf Hash Parameters - pub(crate) leaf: LeafParam<MerkleTreeConfiguration<W, C>>, + leaf: LeafParam<W, C>, /// Two-to-One Hash Parameters - pub(crate) two_to_one: TwoToOneParam<MerkleTreeConfiguration<W, C>>, + two_to_one: TwoToOneParam<W, C>, } -/// Merkle Tree Path +impl<W, C> Parameters<W, C> +where + W: PedersenWindow, + C: ProjectiveCurve, +{ + /// Verifies that `path` constitutes a proof that `item` is contained in the Merkle Tree + /// with the given `root`. + #[inline] + pub fn verify<T>(&self, root: &Root<W, C>, path: &Path<W, C>, item: &T) -> bool + where + T: Concat<Item = u8>, + { + path.0 + .verify(&self.leaf, &self.two_to_one, &root.0, &as_bytes!(item)) + .expect("As of arkworks 0.3.0, this never fails.") + } +} + +/// Merkle Tree Parameters Wrapper +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""))] +pub struct ParametersWrapper<W, C, GG>(Parameters<W, C>, PhantomData<GG>) +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; + +impl<W, C, GG> ParametersWrapper<W, C, GG> +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, +{ + /// Verifies that `path` constitutes a proof that `item` is contained in the Merkle Tree + /// with the given `root`. + #[inline] + pub fn verify<T>( + &self, + root: &RootWrapper<W, C, GG>, + path: &PathWrapper<W, C, GG>, + item: &T, + ) -> bool + where + T: Concat<Item = u8>, + { + self.0.verify(&root.0, &path.0, item) + } +} + +/// Merkle Tree Parameters Variable #[derive(derivative::Derivative)] #[derivative(Clone(bound = ""))] -pub struct Path<W, C> +pub struct ParametersVar<W, C, GG> +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, +{ + /// Leaf Hash Parameters Variable + leaf: LeafParamVar<W, C, GG>, + + /// Two-to-One Hash Parameters Variable + two_to_one: TwoToOneParamVar<W, C, GG>, +} + +impl<W, C, GG> ParametersVar<W, C, GG> +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, +{ + /// Verifies that `path` constitutes a proof that `item` is contained in the Merkle Tree + /// with the given `root`. + #[inline] + pub fn verify( + &self, + root: &RootVar<W, C, GG>, + path: &PathVar<W, C, GG>, + item: &[UInt8<ConstraintField<C>>], + ) -> Boolean<ConstraintField<C>> { + path.0 + .verify_membership(&self.leaf, &self.two_to_one, &root.0, &item) + .expect("This is not allowed to fail.") + } + + /// Asserts that `path` constitutes a proof that `item` is contained in the Merkle Tree + /// with the given `root`. + #[inline] + pub fn assert_verified( + &self, + root: &RootVar<W, C, GG>, + path: &PathVar<W, C, GG>, + item: &[UInt8<ConstraintField<C>>], + ) { + self.verify(root, path, item) + .enforce_equal(&Boolean::TRUE) + .expect("This is not allowed to fail.") + } +} + +impl<W, C, GG> Variable<ProofSystem<C>> for ParametersVar<W, C, GG> where W: PedersenWindow, C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, { - /// Path - pub(crate) path: MerkleTreePath<MerkleTreeConfiguration<W, C>>, + type Mode = Constant; + type Type = ParametersWrapper<W, C, GG>; } -impl<W, C> Path<W, C> +impl<W, C, GG> Alloc<ProofSystem<C>> for ParametersWrapper<W, C, GG> where W: PedersenWindow, C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, { - /// Builds a new [`Path`] from `path`. + type Mode = Constant; + + type Variable = ParametersVar<W, C, GG>; + #[inline] - fn new(path: MerkleTreePath<MerkleTreeConfiguration<W, C>>) -> Self { - Self { path } + fn variable<'t>( + ps: &mut ProofSystem<C>, + allocation: impl Into<Allocation<'t, Self, ProofSystem<C>>>, + ) -> Self::Variable + where + Self: 't, + { + match allocation.into() { + Allocation::Known(this, mode) => ParametersVar { + leaf: LeafParamVar::<W, _, _>::new_constant( + ns!(ps.cs, "leaf hash parameter constant"), + &this.0.leaf, + ) + .expect("Variable allocation is not allowed to fail."), + two_to_one: TwoToOneParamVar::<W, _, _>::new_constant( + ns!(ps.cs, "two-to-one hash parameter constant"), + &this.0.two_to_one, + ) + .expect("Variable allocation is not allowed to fail."), + }, + _ => unreachable!( + "Since we use a constant allocation mode, we always know the variable value." + ), + } } } -/// Path Variable -pub struct PathVar<W, C>(PhantomData<(W, C)>) +/// Merkle Tree Root Inner Type +type RootInnerType<W, C> = TwoToOneDigest<Configuration<W, C>>; + +/// Merkle Tree Root +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +pub struct Root<W, C>(RootInnerType<W, C>) where W: PedersenWindow, C: ProjectiveCurve; -/// Merkle Tree +/// Merkle Tree Root Wrapper +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +pub struct RootWrapper<W, C, GG>(Root<W, C>, PhantomData<GG>) +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; + +/// Merkle Tree Root Variable +#[derive(derivative::Derivative)] +#[derivative(Clone)] +pub struct RootVar<W, C, GG>(GG, PhantomData<(W, C)>) +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; + +impl<W, C, GG> Variable<ProofSystem<C>> for RootVar<W, C, GG> +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, +{ + type Mode = Public; + type Type = RootWrapper<W, C, GG>; +} + +impl<W, C, GG> Alloc<ProofSystem<C>> for RootWrapper<W, C, GG> +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, +{ + type Mode = Public; + + type Variable = RootVar<W, C, GG>; + + #[inline] + fn variable<'t>( + ps: &mut ProofSystem<C>, + allocation: impl Into<Allocation<'t, Self, ProofSystem<C>>>, + ) -> Self::Variable + where + Self: 't, + { + RootVar( + match allocation.into().known() { + Some(this) => AllocVar::<RootInnerType<W, C>, _>::new_input( + ns!(ps.cs, "merkle tree root public input"), + full(((this.0).0).0), + ), + _ => AllocVar::<RootInnerType<W, C>, _>::new_input( + ns!(ps.cs, "merkle tree root public input"), + empty::<RootInnerType<W, C>>, + ), + } + .expect("Variable allocation is not allowed to fail."), + PhantomData, + ) + } +} + +/// Merkle Tree Path Inner Type +type PathInnerType<W, C> = ArkPath<Configuration<W, C>>; + +/// Merkle Tree Path +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""))] +pub struct Path<W, C>(PathInnerType<W, C>) +where + W: PedersenWindow, + C: ProjectiveCurve; + +/// Merkle Tree Path Wrapper #[derive(derivative::Derivative)] #[derivative(Clone(bound = ""))] -pub struct MerkleTree<W, C> +pub struct PathWrapper<W, C, GG>(Path<W, C>, PhantomData<GG>) +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; + +/// Merkle Tree Path Variable Inner Type +type PathVarInnerType<W, C, GG> = + ArkPathVar<Configuration<W, C>, CRHGadget<C, GG, W>, CRHGadget<C, GG, W>, ConstraintField<C>>; + +/// Merkle Tree Path Variable +pub struct PathVar<W, C, GG>(PathVarInnerType<W, C, GG>) +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; + +impl<W, C, GG> Variable<ProofSystem<C>> for PathVar<W, C, GG> +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, +{ + type Mode = Secret; + type Type = PathWrapper<W, C, GG>; +} + +impl<W, C, GG> Alloc<ProofSystem<C>> for PathWrapper<W, C, GG> where W: PedersenWindow, C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, { - /// Merkle Tree - pub(crate) tree: ArkMerkleTree<MerkleTreeConfiguration<W, C>>, + type Mode = Secret; + + type Variable = PathVar<W, C, GG>; + + #[inline] + fn variable<'t>( + ps: &mut ProofSystem<C>, + allocation: impl Into<Allocation<'t, Self, ProofSystem<C>>>, + ) -> Self::Variable + where + Self: 't, + { + PathVar( + match allocation.into().known() { + Some(this) => PathVarInnerType::new_witness(ns!(ps.cs, ""), full(&((this.0).0).0)), + _ => PathVarInnerType::new_witness(ns!(ps.cs, ""), empty::<PathInnerType<W, C>>), + } + .expect("Variable allocatino is not allowed to fail."), + ) + } } +/// Merkle Tree +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""))] +pub struct MerkleTree<W, C>(ArkMerkleTree<Configuration<W, C>>) +where + W: PedersenWindow, + C: ProjectiveCurve; + impl<W, C> MerkleTree<W, C> where W: PedersenWindow, @@ -112,8 +440,8 @@ where where T: Concat<Item = u8>, { - Some(Self { - tree: ArkMerkleTree::new( + Some(Self( + ArkMerkleTree::new( &parameters.leaf, &parameters.two_to_one, &leaves @@ -122,7 +450,25 @@ where .collect::<Vec<_>>(), ) .ok()?, - }) + )) + } + + /// Builds a new [`MerkleTree`]. + /// + /// # Panics + /// + /// The length of `leaves` must be a power of 2 or this function will panic. + #[inline] + pub fn from_wrapped<GG, T>( + parameters: &ParametersWrapper<W, C, GG>, + leaves: &[T], + ) -> Option<Self> + where + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + T: Concat<Item = u8>, + { + Self::new(&parameters.0, leaves) } /// Computes the [`Root`] of the [`MerkleTree`] built from the `leaves`. @@ -134,10 +480,40 @@ where Some(Self::new(parameters, leaves)?.root()) } + /// Computes the [`RootWrapper`] of the [`MerkleTree`] built from the `leaves`. + #[inline] + pub fn build_root_wrapped<GG, T>( + parameters: &ParametersWrapper<W, C, GG>, + leaves: &[T], + ) -> Option<RootWrapper<W, C, GG>> + where + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + T: Concat<Item = u8>, + { + Self::build_root(&parameters.0, leaves).map(move |r| RootWrapper(r, PhantomData)) + } + /// Returns the [`Root`] of this [`MerkleTree`]. #[inline] pub fn root(&self) -> Root<W, C> { - self.tree.root() + Root(self.0.root()) + } + + /// Returns the [`RootWrapper`] of this [`MerkleTree`]. + #[inline] + pub fn root_wrapped<GG>(&self) -> RootWrapper<W, C, GG> + where + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + { + RootWrapper(self.root(), PhantomData) + } + + /// Computes the root and the path for the leaf at the given `index`. + #[inline] + fn compute_containment_proof(&self, index: usize) -> Option<(Root<W, C>, Path<W, C>)> { + Some((self.root(), Path(self.0.generate_proof(index).ok()?))) } /// Builds a containment proof (i.e. merkle root and path) for the leaf at the given `index`. @@ -146,26 +522,22 @@ where where S: VerifiedSet<Public = Root<W, C>, Secret = Path<W, C>>, { + let (root, path) = self.compute_containment_proof(index)?; + Some(ContainmentProof::new(root, path)) + } + + /// Builds a containment proof (i.e. merkle root and path) for the leaf at the given `index`. + #[inline] + pub fn get_wrapped_containment_proof<GG, S>(&self, index: usize) -> Option<ContainmentProof<S>> + where + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + S: VerifiedSet<Public = RootWrapper<W, C, GG>, Secret = PathWrapper<W, C, GG>>, + { + let (root, path) = self.compute_containment_proof(index)?; Some(ContainmentProof::new( - self.root(), - Path::new(self.tree.generate_proof(index).ok()?), + RootWrapper(root, PhantomData), + PathWrapper(path, PhantomData), )) } } - -/// Merkle Tree Configuration -#[derive(derivative::Derivative)] -#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct MerkleTreeConfiguration<W, C>(PhantomData<(W, C)>) -where - W: PedersenWindow, - C: ProjectiveCurve; - -impl<W, C> MerkleTreeConfig for MerkleTreeConfiguration<W, C> -where - W: PedersenWindow, - C: ProjectiveCurve, -{ - type LeafHash = CRH<C, W>; - type TwoToOneHash = CRH<C, W>; -} diff --git a/manta-pay/src/crypto/prf/blake2s.rs b/manta-pay/src/crypto/prf/blake2s.rs index 104600535..7a020e423 100644 --- a/manta-pay/src/crypto/prf/blake2s.rs +++ b/manta-pay/src/crypto/prf/blake2s.rs @@ -99,7 +99,6 @@ impl PseudorandomFunctionFamily for Blake2s { pub mod constraint { use super::*; use crate::crypto::constraint::{ArkProofSystem as ProofSystem, ByteArrayVar}; - use alloc::vec::Vec; use ark_crypto_primitives::{ prf::blake2s::constraints::Blake2sGadget as ArkBlake2sVar, PRFGadget, }; @@ -156,9 +155,10 @@ pub mod constraint { where Self: 't, { - // FIXME: implement - let _ = (ps, allocation); - todo!() + match allocation.into().known() { + Some(this) => todo!(), + _ => todo!(), + } } } @@ -219,16 +219,20 @@ pub mod constraint { where Self: 't, { - // FIXME: implement - let _ = (ps, allocation); - todo!() + match allocation.into().known() { + Some(this) => todo!(), + _ => todo!(), + } } } + /// Blake2s Pseudorandom Function Family Output Variable Inner Type + type Blake2sOutputVarInnerType<F> = <ArkBlake2sVar as PRFGadget<ArkBlake2s, F>>::OutputVar; + /// Blake2s Pseudorandom Function Family Output Variable #[derive(derivative::Derivative)] #[derivative(Clone)] - pub struct Blake2sOutputVar<F>(<ArkBlake2sVar as PRFGadget<ArkBlake2s, F>>::OutputVar) + pub struct Blake2sOutputVar<F>(Blake2sOutputVarInnerType<F>) where F: PrimeField; @@ -271,8 +275,21 @@ pub mod constraint { where Self: 't, { - // FIXME: implement - let _ = (ps, allocation); + /* TODO: + Blake2sOutputVar( + match allocation.into() { + Allocation::Known(this, mode) => match mode { + PublicOrSecret::Public => todo!(), + PublicOrSecret::Secret => todo!(), + }, + Allocation::Unknown(mode) => match mode { + PublicOrSecret::Public => todo!(), + PublicOrSecret::Secret => todo!(), + }, + } + .expect("Variable allocation is not allowed to fail."), + ) + */ todo!() } } From adcd3b81b680d8d29eb107f8cfecd58b7c9c5008 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 15 Sep 2021 19:41:02 -0400 Subject: [PATCH 034/275] WIP[broken]: fixing constraint system --- manta-accounting/src/asset.rs | 38 +- manta-accounting/src/identity.rs | 292 +++++------ manta-accounting/src/transfer.rs | 81 ++- manta-crypto/src/constraint.rs | 477 +++++++++++------- manta-crypto/src/set/constraint.rs | 77 +-- manta-pay/src/accounting/config.rs | 2 + manta-pay/src/accounting/ledger.rs | 2 +- manta-pay/src/crypto/commitment/pedersen.rs | 4 +- .../src/crypto/constraint/proof_system.rs | 59 ++- .../{merkle_tree.rs => merkle_tree/mod.rs} | 0 manta-pay/src/crypto/prf/blake2s.rs | 7 +- 11 files changed, 568 insertions(+), 471 deletions(-) rename manta-pay/src/crypto/{merkle_tree.rs => merkle_tree/mod.rs} (100%) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 33278138a..35f176569 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -33,7 +33,8 @@ use derive_more::{ Add, AddAssign, Display, Div, DivAssign, From, Mul, MulAssign, Product, Sub, SubAssign, Sum, }; use manta_crypto::constraint::{ - unknown, Alloc, Allocation, HasVariable, PublicOrSecret, Secret, Var, Variable, + reflection::{unknown, HasAllocation, HasVariable, Var}, + Allocation, PublicOrSecret, Secret, Variable, }; use manta_util::{array_map, fallible_array_map, into_array_unchecked, Concat, ConcatAccumulator}; use rand::{ @@ -395,31 +396,18 @@ where + HasVariable<AssetBalance, Mode = PublicOrSecret> + ?Sized, { - type Mode = Secret; type Type = Asset; -} -impl<P> Alloc<P> for Asset -where - P: HasVariable<AssetId, Mode = PublicOrSecret> - + HasVariable<AssetBalance, Mode = PublicOrSecret> - + ?Sized, -{ type Mode = Secret; - type Variable = AssetVar<P>; - #[inline] - fn variable<'t>(ps: &mut P, allocation: impl Into<Allocation<'t, Self, P>>) -> Self::Variable - where - Self: 't, - { - match allocation.into() { - Allocation::Known(this, mode) => Self::Variable::new( - ps.allocate_known(&this.id, mode), - ps.allocate_known(&this.value, mode), + fn new(ps: &mut P, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + match allocation { + Allocation::Known(this, mode) => Self::new( + ps.new_known_allocation(&this.id, mode), + ps.new_known_allocation(&this.value, mode), ), - Allocation::Unknown(mode) => Self::Variable::new( + Allocation::Unknown(mode) => Self::new( unknown::<AssetId, _>(ps, mode.into()), unknown::<AssetBalance, _>(ps, mode.into()), ), @@ -427,6 +415,16 @@ where } } +impl<P> HasAllocation<P> for Asset +where + P: HasVariable<AssetId, Mode = PublicOrSecret> + + HasVariable<AssetBalance, Mode = PublicOrSecret> + + ?Sized, +{ + type Variable = AssetVar<P>; + type Mode = Secret; +} + /// Asset Collection #[derive(Clone, Copy, Debug, Eq, From, Hash, Ord, PartialEq, PartialOrd)] #[from(forward)] diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index c9187ed08..8af462fca 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -30,8 +30,9 @@ use core::{convert::Infallible, fmt::Debug, hash::Hash, marker::PhantomData}; use manta_crypto::{ commitment::{CommitmentScheme, Input as CommitmentInput}, constraint::{ - unknown, Alloc, AllocEq, Allocation, BooleanSystem, Constant, Derived, HasVariable, Public, - PublicOrSecret, Secret, Var, Variable, + reflection::{HasAllocation, HasVariable, Var}, + Allocation, BooleanSystem, Constant, Derived, Equal, Public, PublicOrSecret, Secret, + Variable, }, ies::{self, EncryptedMessage, IntegratedEncryptionScheme}, set::{ @@ -73,93 +74,75 @@ pub trait IdentityConfiguration { } /// [`Identity`] Proof System Configuration -pub trait IdentityProofSystemConfiguration { +pub trait IdentityProofSystemConfiguration: IdentityConfiguration { /// Boolean System type BooleanSystem: BooleanSystem + HasVariable<AssetId, Mode = PublicOrSecret> + HasVariable<AssetBalance, Mode = PublicOrSecret>; - /// Pseudorandom Function Family Seed - type PseudorandomFunctionFamilySeed: Clone + Alloc<Self::BooleanSystem, Mode = Secret>; + /// Secret Key Variable + type SecretKeyVar: Variable<Self::BooleanSystem, Type = Self::SecretKey, Mode = Secret>; - /// Pseudorandom Function Family Input - type PseudorandomFunctionFamilyInput: Alloc<Self::BooleanSystem, Mode = Secret>; + /// Pseudorandom Function Family Input Variable + type PseudorandomFunctionFamilyInputVar: Variable< + Self::BooleanSystem, + Type = <Self::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Input, + Mode = Secret, + >; - /// Pseudorandom Function Family Output - type PseudorandomFunctionFamilyOutput: AllocEq<Self::BooleanSystem, Mode = PublicOrSecret>; - - /// Pseudorandom Function Family - type PseudorandomFunctionFamily: PseudorandomFunctionFamily< - Seed = Self::PseudorandomFunctionFamilySeed, - Input = Self::PseudorandomFunctionFamilyInput, - Output = Self::PseudorandomFunctionFamilyOutput, - > + Alloc<Self::BooleanSystem, Mode = Constant, Variable = Self::PseudorandomFunctionFamilyVar>; + /// Pseudorandom Function Family Output Variable + type PseudorandomFunctionFamilyOutputVar: Variable< + Self::BooleanSystem, + Type = <Self::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Output, + Mode = PublicOrSecret, + > + Equal<Self::BooleanSystem>; /// Pseudorandom Function Family Variable type PseudorandomFunctionFamilyVar: PseudorandomFunctionFamily< - Seed = PseudorandomFunctionFamilySeedVar<Self>, - Input = PseudorandomFunctionFamilyInputVar<Self>, - Output = PseudorandomFunctionFamilyOutputVar<Self>, - > + Variable<Self::BooleanSystem, Mode = Constant, Type = Self::PseudorandomFunctionFamily>; - - /// Commitment Scheme Randomness - type CommitmentSchemeRandomness: Alloc<Self::BooleanSystem, Mode = Secret>; - - /// Commitment Scheme Output - type CommitmentSchemeOutput: AllocEq<Self::BooleanSystem, Mode = PublicOrSecret>; - - /// Commitment Scheme - type CommitmentScheme: CommitmentScheme< - Randomness = Self::CommitmentSchemeRandomness, - Output = Self::CommitmentSchemeOutput, - > + CommitmentInput<PublicKey<Self>> - + CommitmentInput<VoidNumberGenerator<Self>> - + CommitmentInput<Asset> - + CommitmentInput<VoidNumberCommitment<Self>> - + Alloc<Self::BooleanSystem, Mode = Constant, Variable = Self::CommitmentSchemeVar>; + Seed = Self::SecretKeyVar, + Input = Self::PseudorandomFunctionFamilyInputVar, + Output = Self::PseudorandomFunctionFamilyOutputVar, + > + Variable<Self::BooleanSystem, Type = Self::PseudorandomFunctionFamily, Mode = Constant>; + + /// Commitment Scheme Randomness Variable + type CommitmentSchemeRandomnessVar: Variable< + Self::BooleanSystem, + Type = <Self::CommitmentScheme as CommitmentScheme>::Randomness, + Mode = Secret, + >; + + /// Commitment Scheme Output Variable + type CommitmentSchemeOutputVar: Variable< + Self::BooleanSystem, + Type = <Self::CommitmentScheme as CommitmentScheme>::Output, + Mode = PublicOrSecret, + > + Equal<Self::BooleanSystem>; /// Commitment Scheme Variable type CommitmentSchemeVar: CommitmentScheme< - Randomness = CommitmentSchemeRandomnessVar<Self>, - Output = CommitmentSchemeOutputVar<Self>, + Randomness = Self::CommitmentSchemeRandomnessVar, + Output = Self::CommitmentSchemeOutputVar, > + CommitmentInput<PublicKeyVar<Self>> + CommitmentInput<VoidNumberGeneratorVar<Self>> + CommitmentInput<AssetVar<Self::BooleanSystem>> + CommitmentInput<VoidNumberCommitmentVar<Self>> - + Variable<Self::BooleanSystem, Mode = Constant, Type = Self::CommitmentScheme>; - - /// Seedable Cryptographic Random Number Generator - type Rng: CryptoRng + RngCore + SeedableRng<Seed = Self::PseudorandomFunctionFamilySeed>; + + Variable<Self::BooleanSystem, Type = Self::CommitmentScheme, Mode = Constant>; } -impl<C> IdentityConfiguration for C -where - C: IdentityProofSystemConfiguration + ?Sized, -{ - type SecretKey = C::PseudorandomFunctionFamilySeed; - type PseudorandomFunctionFamily = C::PseudorandomFunctionFamily; - type CommitmentScheme = C::CommitmentScheme; - type Rng = C::Rng; -} - -/// [`PseudorandomFunctionFamily::Seed`] Type -pub type PseudorandomFunctionFamilySeed<C> = - <<C as IdentityConfiguration>::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Seed; - /// [`PseudorandomFunctionFamily::Input`] Type -pub type PseudorandomFunctionFamilyInput<C> = +type PseudorandomFunctionFamilyInput<C> = <<C as IdentityConfiguration>::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Input; /// [`PseudorandomFunctionFamily::Output`] Type -pub type PseudorandomFunctionFamilyOutput<C> = +type PseudorandomFunctionFamilyOutput<C> = <<C as IdentityConfiguration>::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Output; /// [`CommitmentScheme::Randomness`] Type -pub type CommitmentSchemeRandomness<C> = +type CommitmentSchemeRandomness<C> = <<C as IdentityConfiguration>::CommitmentScheme as CommitmentScheme>::Randomness; /// [`CommitmentScheme::Output`] Type -pub type CommitmentSchemeOutput<C> = +type CommitmentSchemeOutput<C> = <<C as IdentityConfiguration>::CommitmentScheme as CommitmentScheme>::Output; /// Secret Key Type @@ -186,30 +169,24 @@ pub type UtxoRandomness<C> = CommitmentSchemeRandomness<C>; /// UTXO Type pub type Utxo<C> = CommitmentSchemeOutput<C>; -/// [`PseudorandomFunctionFamily::Seed`] Variable Type -pub type PseudorandomFunctionFamilySeedVar<C> = - Var<PseudorandomFunctionFamilySeed<C>, <C as IdentityProofSystemConfiguration>::BooleanSystem>; - /// [`PseudorandomFunctionFamily::Input`] Variable Type -pub type PseudorandomFunctionFamilyInputVar<C> = - Var<PseudorandomFunctionFamilyInput<C>, <C as IdentityProofSystemConfiguration>::BooleanSystem>; +type PseudorandomFunctionFamilyInputVar<C> = + <C as IdentityProofSystemConfiguration>::PseudorandomFunctionFamilyInputVar; /// [`PseudorandomFunctionFamily::Output`] Variable Type -pub type PseudorandomFunctionFamilyOutputVar<C> = Var< - PseudorandomFunctionFamilyOutput<C>, - <C as IdentityProofSystemConfiguration>::BooleanSystem, ->; +type PseudorandomFunctionFamilyOutputVar<C> = + <C as IdentityProofSystemConfiguration>::PseudorandomFunctionFamilyOutputVar; /// [`CommitmentScheme::Randomness`] Variable Type -pub type CommitmentSchemeRandomnessVar<C> = - Var<CommitmentSchemeRandomness<C>, <C as IdentityProofSystemConfiguration>::BooleanSystem>; +type CommitmentSchemeRandomnessVar<C> = + <C as IdentityProofSystemConfiguration>::CommitmentSchemeRandomnessVar; /// [`CommitmentScheme::Output`] Variable Type -pub type CommitmentSchemeOutputVar<C> = - Var<CommitmentSchemeOutput<C>, <C as IdentityProofSystemConfiguration>::BooleanSystem>; +type CommitmentSchemeOutputVar<C> = + <C as IdentityProofSystemConfiguration>::CommitmentSchemeOutputVar; /// Secret Key Variable Type -pub type SecretKeyVar<C> = PseudorandomFunctionFamilySeedVar<C>; +pub type SecretKeyVar<C> = <C as IdentityProofSystemConfiguration>::SecretKeyVar; /// Public Key Variable Type pub type PublicKeyVar<C> = PseudorandomFunctionFamilyOutputVar<C>; @@ -413,41 +390,39 @@ impl<C> Variable<C::BooleanSystem> for AssetParametersVar<C> where C: IdentityProofSystemConfiguration, { - type Mode = Secret; type Type = AssetParameters<C>; -} -impl<C> Alloc<C::BooleanSystem> for AssetParameters<C> -where - C: IdentityProofSystemConfiguration, -{ type Mode = Secret; - type Variable = AssetParametersVar<C>; - #[inline] - fn variable<'t>( - ps: &mut C::BooleanSystem, - allocation: impl Into<Allocation<'t, Self, C::BooleanSystem>>, - ) -> Self::Variable - where - Self: 't, - { - match allocation.into() { - Allocation::Known(this, mode) => Self::Variable::new( - this.void_number_generator.as_known(ps, mode), - this.void_number_commitment_randomness.as_known(ps, mode), - this.utxo_randomness.as_known(ps, mode), + fn new(ps: &mut C::BooleanSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + match allocation { + Allocation::Known(this, mode) => Self::new( + VoidNumberGeneratorVar::<C>::new_known(ps, &this.void_number_generator, mode), + VoidNumberCommitmentRandomnessVar::<C>::new_known( + ps, + &this.void_number_commitment_randomness, + mode, + ), + UtxoRandomnessVar::<C>::new_known(ps, &this.utxo_randomness, mode), ), - Allocation::Unknown(mode) => Self::Variable::new( - VoidNumberGenerator::<C>::unknown(ps, mode), - VoidNumberCommitmentRandomness::<C>::unknown(ps, mode), - UtxoRandomness::<C>::unknown(ps, mode), + Allocation::Unknown(mode) => Self::new( + VoidNumberGeneratorVar::<C>::new_unknown(ps, mode), + VoidNumberCommitmentRandomnessVar::<C>::new_unknown(ps, mode), + UtxoRandomnessVar::<C>::new_unknown(ps, mode), ), } } } +impl<C> HasAllocation<C::BooleanSystem> for AssetParameters<C> +where + C: IdentityProofSystemConfiguration, +{ + type Variable = AssetParametersVar<C>; + type Mode = Secret; +} + /// Account Identity pub struct Identity<C> where @@ -1262,8 +1237,8 @@ where utxo_set: &Var<S, C::BooleanSystem>, ) -> AssetVar<C::BooleanSystem> where - S: Alloc<C::BooleanSystem>, - S::Variable: VerifiedSetVariable<C::BooleanSystem>, + S: HasAllocation<C::BooleanSystem>, + S::Variable: VerifiedSetVariable<C::BooleanSystem, ItemVar = UtxoVar<C>>, { // Well-formed check: // @@ -1346,52 +1321,51 @@ where S: VerifiedSet<Item = Utxo<C>>, C::BooleanSystem: HasVariable<S::Public, Mode = Public> + HasVariable<S::Secret, Mode = Secret>, { - type Mode = Derived; type Type = Sender<C, S>; -} -impl<C, S> Alloc<C::BooleanSystem> for Sender<C, S> -where - C: IdentityProofSystemConfiguration, - S: VerifiedSet<Item = Utxo<C>>, - C::BooleanSystem: HasVariable<S::Public, Mode = Public> + HasVariable<S::Secret, Mode = Secret>, -{ type Mode = Derived; - type Variable = SenderVar<C, S>; #[inline] - fn variable<'t>( - ps: &mut C::BooleanSystem, - allocation: impl Into<Allocation<'t, Self, C::BooleanSystem>>, - ) -> Self::Variable - where - Self: 't, - { - match allocation.into() { - Allocation::Known(this, mode) => Self::Variable { - secret_key: this.secret_key.as_known(ps, mode), - public_key: this.public_key.as_known(ps, Secret), - asset: this.asset.as_known(ps, mode), - parameters: this.parameters.as_known(ps, mode), - void_number: this.void_number.as_known(ps, Public), - void_number_commitment: ps.allocate_known(&this.void_number_commitment, Public), - utxo: ps.allocate_known(&this.utxo, Secret), - utxo_containment_proof: this.utxo_containment_proof.as_known(ps, mode), + fn new(ps: &mut C::BooleanSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + match allocation { + Allocation::Known(this, mode) => Self { + secret_key: SecretKeyVar::<C>::new_known(ps, &this.secret_key, mode), + public_key: PublicKeyVar::<C>::new_known(ps, &this.public_key, Secret), + asset: this.asset.known(ps, mode), + parameters: this.parameters.known(ps, mode), + void_number: VoidNumberVar::<C>::new_known(ps, &this.void_number, Public), + void_number_commitment: VoidNumberCommitmentVar::<C>::new_known( + ps, + &this.void_number_commitment, + Public, + ), + utxo: UtxoVar::<C>::new_known(ps, &this.utxo, Secret), + utxo_containment_proof: this.utxo_containment_proof.known(ps, mode), }, - Allocation::Unknown(mode) => Self::Variable { - secret_key: SecretKey::<C>::unknown(ps, mode), - public_key: PublicKey::<C>::unknown(ps, Secret), + Allocation::Unknown(mode) => Self { + secret_key: SecretKeyVar::<C>::new_unknown(ps, mode), + public_key: PublicKeyVar::<C>::new_unknown(ps, Secret), asset: Asset::unknown(ps, mode), parameters: AssetParameters::unknown(ps, mode), - void_number: VoidNumber::<C>::unknown(ps, Public), - void_number_commitment: unknown::<VoidNumberCommitment<C>, _>(ps, Public.into()), - utxo: unknown::<Utxo<C>, _>(ps, Secret.into()), + void_number: VoidNumberVar::<C>::new_unknown(ps, Public), + void_number_commitment: VoidNumberCommitmentVar::<C>::new_unknown(ps, Public), + utxo: UtxoVar::<C>::new_unknown(ps, Secret), utxo_containment_proof: ContainmentProof::<S>::unknown(ps, mode), }, } } } +impl<C, S> HasAllocation<C::BooleanSystem> for Sender<C, S> +where + C: IdentityProofSystemConfiguration, + S: VerifiedSet<Item = Utxo<C>>, + C::BooleanSystem: HasVariable<S::Public, Mode = Public> + HasVariable<S::Secret, Mode = Secret>, +{ + type Variable = SenderVar<C, S>; + type Mode = Derived; +} + /// Sender Post Error pub enum SenderPostError<L> where @@ -1606,46 +1580,44 @@ where C: IdentityProofSystemConfiguration, I: IntegratedEncryptionScheme<Plaintext = Asset>, { - type Mode = Derived; type Type = Receiver<C, I>; -} -impl<C, I> Alloc<C::BooleanSystem> for Receiver<C, I> -where - C: IdentityProofSystemConfiguration, - I: IntegratedEncryptionScheme<Plaintext = Asset>, -{ type Mode = Derived; - type Variable = ReceiverVar<C, I>; - #[inline] - fn variable<'t>( - ps: &mut C::BooleanSystem, - allocation: impl Into<Allocation<'t, Self, C::BooleanSystem>>, - ) -> Self::Variable - where - Self: 't, - { - match allocation.into() { - Allocation::Known(this, mode) => Self::Variable { - asset: this.asset.as_known(ps, mode), - utxo_randomness: this.utxo_randomness.as_known(ps, mode), - void_number_commitment: this.void_number_commitment.as_known(ps, Secret), - utxo: this.utxo.as_known(ps, Public), + fn new(ps: &mut C::BooleanSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + match allocation { + Allocation::Known(this, mode) => Self { + asset: AssetVar::new_known(ps, &this.asset, mode), + utxo_randomness: UtxoRandomnessVar::<C>::new_known(ps, &this.utxo_randomness, mode), + void_number_commitment: VoidNumberCommitmentVar::<C>::new_known( + ps, + &this.void_number_commitment, + Secret, + ), + utxo: UtxoVar::<C>::new_known(ps, &this.utxo, Public), __: PhantomData, }, - Allocation::Unknown(mode) => Self::Variable { - asset: Asset::unknown(ps, mode), - utxo_randomness: UtxoRandomness::<C>::unknown(ps, mode), - void_number_commitment: VoidNumberCommitment::<C>::unknown(ps, Secret), - utxo: Utxo::<C>::unknown(ps, Public), + Allocation::Unknown(mode) => Self { + asset: AssetVar::new_unknown(ps, mode), + utxo_randomness: UtxoRandomnessVar::<C>::new_unknown(ps, mode), + void_number_commitment: VoidNumberCommitmentVar::<C>::new_unknown(ps, Secret), + utxo: UtxoVar::<C>::new_unknown(ps, Public), __: PhantomData, }, } } } +impl<C, I> HasAllocation<C::BooleanSystem> for Receiver<C, I> +where + C: IdentityProofSystemConfiguration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, +{ + type Variable = ReceiverVar<C, I>; + type Mode = Derived; +} + /// Receiver Post Error pub enum ReceiverPostError<L> where diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 07b8a4fb1..833009fc4 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -17,13 +17,10 @@ //! Transfer Protocols use crate::{ - asset::{ - sample_asset_balances, Asset, AssetBalance, AssetBalanceVar, AssetBalances, AssetId, - AssetIdVar, - }, + asset::{sample_asset_balances, Asset, AssetBalance, AssetBalanceVar, AssetBalances, AssetId}, identity::{ IdentityProofSystemConfiguration, Receiver, ReceiverPost, ReceiverVar, Sender, SenderPost, - SenderVar, Utxo, VoidNumber, + SenderVar, Utxo, UtxoVar, VoidNumber, }, ledger::{Ledger, PostError}, }; @@ -31,8 +28,9 @@ use alloc::vec::Vec; use core::ops::AddAssign; use manta_crypto::{ constraint::{ - Alloc, AllocEq, BooleanSystem, Constant, Derived, ProofSystem, Public, PublicOrSecret, - Secret, + reflection::{HasAllocation, HasVariable}, + BooleanSystem, Constant, Derived, Equal, ProofSystem, Public, PublicOrSecret, Secret, + Variable, VariableSource, }, ies::{EncryptedMessage, IntegratedEncryptionScheme}, set::{constraint::VerifiedSetVariable, VerifiedSet}, @@ -125,26 +123,35 @@ pub trait TransferConfiguration: IdentityProofSystemConfiguration<BooleanSystem = Self::ProofSystem> { /// Proof System - type ProofSystem: ProofSystem; + type ProofSystem: ProofSystem + + HasVariable<AssetId, Variable = Self::AssetIdVar, Mode = PublicOrSecret> + + HasVariable<AssetBalance, Variable = Self::AssetBalanceVar, Mode = PublicOrSecret> + + HasVariable<<Self::UtxoSet as VerifiedSet>::Public, Mode = Public> + + HasVariable<<Self::UtxoSet as VerifiedSet>::Secret, Mode = Secret>; - /// Integrated Encryption Scheme for [`Asset`] - type IntegratedEncryptionScheme: IntegratedEncryptionScheme<Plaintext = Asset>; + /// Asset Id Variable + type AssetIdVar: Variable<Self::ProofSystem, Mode = PublicOrSecret, Type = AssetId> + + Equal<Self::ProofSystem>; - /// Verified Set Public Input - type UtxoSetPublicInput: Alloc<Self::ProofSystem, Mode = Public>; + /// Asset Balance Variable + type AssetBalanceVar: Variable<Self::ProofSystem, Mode = PublicOrSecret, Type = AssetBalance> + + Equal<Self::ProofSystem> + + AddAssign; - /// Verified Set Secret Witness - type UtxoSetSecretWitness: Alloc<Self::ProofSystem, Mode = Secret>; + /// Integrated Encryption Scheme for [`Asset`] + type IntegratedEncryptionScheme: IntegratedEncryptionScheme<Plaintext = Asset>; /// Verified Set for [`Utxo`] - type UtxoSet: VerifiedSet< - Item = Utxo<Self>, - Public = Self::UtxoSetPublicInput, - Secret = Self::UtxoSetSecretWitness, - > + Alloc<Self::ProofSystem, Mode = Constant, Variable = Self::UtxoSetVar>; + type UtxoSet: VerifiedSet<Item = Utxo<Self>> + + HasAllocation<Self::ProofSystem, Variable = Self::UtxoSetVar, Mode = Constant>; /// Verified Set Variable for [`Utxo`] - type UtxoSetVar: VerifiedSetVariable<Self::ProofSystem, Mode = Constant, Type = Self::UtxoSet>; + type UtxoSetVar: VerifiedSetVariable< + Self::ProofSystem, + ItemVar = UtxoVar<Self>, + Type = Self::UtxoSet, + Mode = Constant, + >; } /// Secret Sender Type @@ -279,12 +286,7 @@ where self, commitment_scheme: &T::CommitmentScheme, utxo_set: &T::UtxoSet, - ) -> Option<SecretTransferPost<T, SENDERS, RECEIVERS>> - where - AssetId: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, - AssetBalance: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, - AssetBalanceVar<T::ProofSystem>: AddAssign<AssetBalanceVar<T::ProofSystem>>, - { + ) -> Option<SecretTransferPost<T, SENDERS, RECEIVERS>> { Some(SecretTransferPost { validity_proof: Transfer::<T, 0, SENDERS, RECEIVERS, 0>::generate_validity_proof( None, @@ -475,7 +477,7 @@ where ps: &mut T::ProofSystem, commitment_scheme: &T::CommitmentSchemeVar, utxo_set: &T::UtxoSetVar, - base_asset_id: Option<AssetIdVar<T::ProofSystem>>, + base_asset_id: Option<T::AssetIdVar>, sources: Sources, senders: Senders, receivers: Receivers, @@ -485,12 +487,9 @@ where Senders: Iterator<Item = SecretSenderVar<T>>, Receivers: Iterator<Item = SecretReceiverVar<T>>, Sinks: Iterator<Item = AssetBalanceVar<T::ProofSystem>>, - AssetId: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, - AssetBalance: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, - AssetBalanceVar<T::ProofSystem>: AddAssign<AssetBalanceVar<T::ProofSystem>>, { - let mut sender_sum = AssetBalance(0).as_known(ps, Secret); - let mut receiver_sum = AssetBalance(0).as_known(ps, Secret); + let mut sender_sum = T::AssetBalanceVar::from_default(ps, Secret); + let mut receiver_sum = T::AssetBalanceVar::from_default(ps, Secret); sources.for_each(|source| sender_sum += source); sinks.for_each(|sink| receiver_sum += sink); @@ -528,12 +527,7 @@ where sinks: &AssetBalances<SINKS>, commitment_scheme: &T::CommitmentScheme, utxo_set: &T::UtxoSet, - ) -> Option<Proof<T>> - where - AssetId: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, - AssetBalance: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, - AssetBalanceVar<T::ProofSystem>: AddAssign<AssetBalanceVar<T::ProofSystem>>, - { + ) -> Option<Proof<T>> { // FIXME: Find a better way to allocate variables without so much hassle. let mut ps = <T::ProofSystem as Default>::default(); @@ -549,13 +543,13 @@ where #[allow(clippy::needless_collect)] // NOTE: `ps` is being mutated, we need to collect. let senders = senders .iter() - .map(|sender| sender.as_known(&mut ps, Derived)) + .map(|sender| sender.known(&mut ps, Derived)) .collect::<Vec<_>>(); #[allow(clippy::needless_collect)] // NOTE: `ps` is being mutated, we need to collect. let receivers = receivers .iter() - .map(|receiver| receiver.as_known(&mut ps, Derived)) + .map(|receiver| receiver.known(&mut ps, Derived)) .collect::<Vec<_>>(); #[allow(clippy::needless_collect)] // NOTE: `ps` is being mutated, we need to collect. @@ -587,12 +581,7 @@ where self, commitment_scheme: &T::CommitmentScheme, utxo_set: &T::UtxoSet, - ) -> Option<TransferPost<T, SOURCES, SENDERS, RECEIVERS, SINKS>> - where - AssetId: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, - AssetBalance: AllocEq<T::ProofSystem, Mode = PublicOrSecret>, - AssetBalanceVar<T::ProofSystem>: AddAssign<AssetBalanceVar<T::ProofSystem>>, - { + ) -> Option<TransferPost<T, SOURCES, SENDERS, RECEIVERS, SINKS>> { Some(TransferPost { validity_proof: if SENDERS == 0 { None diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index 413e42c06..b774339af 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -17,32 +17,17 @@ //! Constraint Proof Systems // TODO: Add derive macros to all the enums/structs here. -// TODO: Add derive trait to implement `Alloc` for structs (and enums?). +// TODO: Add derive trait to implement `HasAllocation` for structs (and enums?). // TODO: Add more convenience functions for allocating unknown variables. // TODO: How to do verification systems? Should it be a separate trait or part of `ProofSystem`? -// TODO: Should we (can we?) seal the `HasVariable` trait so no one implements it by accident? -// TODO: Can we replace `AllocEq` with an equality trait that extends `Variable` not `Alloc`? use core::{ convert::{Infallible, TryFrom}, + fmt::Debug, + hash::Hash, marker::PhantomData, }; -#[doc(inline)] -pub use types::Bool; - -/// Variable Type -pub type Var<T, P> = <P as HasVariable<T>>::Variable; - -/// Allocation Mode Type -pub type Mode<T, P> = <Var<T, P> as Variable<P>>::Mode; - -/// Known Allocation Mode Type -pub type KnownMode<T, P> = <Mode<T, P> as AllocationMode>::Known; - -/// Known Allocation Mode Type -pub type UnknownMode<T, P> = <Mode<T, P> as AllocationMode>::Unknown; - /// Allocation Mode pub trait AllocationMode { /// Known Allocation Mode @@ -63,29 +48,38 @@ impl AllocationMode for () { } /// Allocation Entry -pub enum Allocation<'t, T, P> +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "Mode::Known: Clone, Mode::Unknown: Clone"), + Copy(bound = "Mode::Known: Copy, Mode::Unknown: Copy"), + Debug(bound = "T: Debug, Mode::Known: Debug, Mode::Unknown: Debug"), + Eq(bound = "T: Eq, Mode::Known: Eq, Mode::Unknown: Eq"), + Hash(bound = "T: Hash, Mode::Known: Hash, Mode::Unknown: Hash"), + PartialEq(bound = "T: PartialEq, Mode::Known: PartialEq, Mode::Unknown: PartialEq") +)] +pub enum Allocation<'t, T, Mode> where T: ?Sized, - P: HasVariable<T> + ?Sized, + Mode: AllocationMode, { /// Known Value Known( /// Allocation Value &'t T, /// Allocation Mode - KnownMode<T, P>, + Mode::Known, ), /// Unknown Value Unknown( /// Allocation Mode - UnknownMode<T, P>, + Mode::Unknown, ), } -impl<'t, T, P> Allocation<'t, T, P> +impl<'t, T, Mode> Allocation<'t, T, Mode> where T: ?Sized, - P: HasVariable<T> + ?Sized, + Mode: AllocationMode, { /// Returns `true` if `self` represents a known variable. #[inline] @@ -101,7 +95,7 @@ where /// Converts `self` into a possible known value. #[inline] - pub fn known(self) -> Option<(&'t T, KnownMode<T, P>)> { + pub fn known(self) -> Option<(&'t T, Mode::Known)> { match self { Self::Known(value, mode) => Some((value, mode)), _ => None, @@ -110,292 +104,276 @@ where /// Converts `self` into a possibly unknown value. #[inline] - pub fn unknown(self) -> Option<UnknownMode<T, P>> { + pub fn unknown(self) -> Option<Mode::Unknown> { match self { Self::Unknown(mode) => Some(mode), _ => None, } } - - /// Maps the underlying allocation value if it is known. - #[inline] - pub fn map<'u, U, Q, F>(self, f: F) -> Allocation<'u, U, Q> - where - U: ?Sized, - Q: HasVariable<U, Mode = Mode<T, P>> + ?Sized, - F: FnOnce(&'t T) -> &'u U, - { - match self { - Self::Known(value, mode) => Allocation::Known(f(value), mode), - Self::Unknown(mode) => Allocation::Unknown(mode), - } - } - - /// Allocates a variable into `ps` using `self` as the allocation. - #[inline] - pub fn into_variable(self, ps: &mut P) -> Var<T, P> { - ps.allocate(self) - } } -impl<'t, T, P> From<(&'t T, KnownMode<T, P>)> for Allocation<'t, T, P> +impl<'t, T, Mode> From<(&'t T, Mode::Known)> for Allocation<'t, T, Mode> where T: ?Sized, - P: HasVariable<T> + ?Sized, + Mode: AllocationMode, { #[inline] - fn from((value, mode): (&'t T, KnownMode<T, P>)) -> Self { + fn from((value, mode): (&'t T, Mode::Known)) -> Self { Self::Known(value, mode) } } /// Variable Allocation Trait -pub trait Alloc<P> +pub trait Variable<P>: Sized where P: ?Sized, { + /// Origin Type of the Variable + type Type; + /// Allocation Mode type Mode: AllocationMode; - /// Variable Object Type - type Variable: Variable<P, Mode = Self::Mode, Type = Self>; - /// Allocates a new variable into `ps` with the given `allocation`. - fn variable<'t>(ps: &mut P, allocation: impl Into<Allocation<'t, Self, P>>) -> Self::Variable - where - Self: 't; + fn new(ps: &mut P, allocation: Allocation<Self::Type, Self::Mode>) -> Self; /// Allocates a new known variable into `ps` with the given `mode`. #[inline] - fn as_known( - &self, + fn new_known( ps: &mut P, + value: &Self::Type, mode: impl Into<<Self::Mode as AllocationMode>::Known>, - ) -> Self::Variable { - Self::variable(ps, (self, mode.into())) + ) -> Self { + Self::new(ps, Allocation::Known(value, mode.into())) } /// Allocates a new unknown variable into `ps` with the given `mode`. #[inline] - fn unknown( - ps: &mut P, - mode: impl Into<<Self::Mode as AllocationMode>::Unknown>, - ) -> Self::Variable { - Self::variable(ps, Allocation::Unknown(mode.into())) + fn new_unknown(ps: &mut P, mode: impl Into<<Self::Mode as AllocationMode>::Unknown>) -> Self { + Self::new(ps, Allocation::Unknown(mode.into())) } -} -/// Variable Reflection Trait -pub trait Variable<P>: Sized -where - P: ?Sized, -{ - /// Allocation Mode - type Mode: AllocationMode; + /// Allocates a new known variable into `ps` with the given `mode` which holds the default + /// value of [`Self::Type`]. + #[inline] + fn from_default(ps: &mut P, mode: impl Into<<Self::Mode as AllocationMode>::Known>) -> Self + where + Self::Type: Default, + { + Self::new_known(ps, &Default::default(), mode) + } - /// Origin Type of the Variable - type Type: Alloc<P, Mode = Self::Mode, Variable = Self>; + /// Allocates a new known variable into `ps` with the given `mode` which holds the default + /// value of [`&Self::Type`](Self::Type). + #[inline] + fn from_default_ref<'t>( + ps: &mut P, + mode: impl Into<<Self::Mode as AllocationMode>::Known>, + ) -> Self + where + Self::Type: 't, + &'t Self::Type: Default, + { + Self::new_known(ps, Default::default(), mode) + } +} +/// Variable Source +pub trait VariableSource { /// Allocates a new variable into `ps` with the given `allocation`. #[inline] - fn new<'t>(ps: &mut P, allocation: impl Into<Allocation<'t, Self::Type, P>>) -> Self + fn as_variable<P, V>(ps: &mut P, allocation: Allocation<Self, V::Mode>) -> V where - Self::Type: 't, + P: ?Sized, + V: Variable<P, Type = Self>, { - Self::Type::variable(ps, allocation) + V::new(ps, allocation) } /// Allocates a new known variable into `ps` with the given `mode`. #[inline] - fn new_known( - ps: &mut P, - value: &Self::Type, - mode: impl Into<<Self::Mode as AllocationMode>::Known>, - ) -> Self { - Self::new(ps, (value, mode.into())) + fn as_known<P, V>(&self, ps: &mut P, mode: impl Into<<V::Mode as AllocationMode>::Known>) -> V + where + P: ?Sized, + V: Variable<P, Type = Self>, + { + V::new_known(ps, self, mode) } /// Allocates a new unknown variable into `ps` with the given `mode`. #[inline] - fn new_unknown(ps: &mut P, mode: impl Into<<Self::Mode as AllocationMode>::Unknown>) -> Self { - Self::new(ps, Allocation::Unknown(mode.into())) + fn as_unknown<P, V>(ps: &mut P, mode: impl Into<<V::Mode as AllocationMode>::Unknown>) -> V + where + P: ?Sized, + V: Variable<P, Type = Self>, + { + V::new_unknown(ps, mode) } } -/// Variable Reflection Trait -pub trait HasVariable<T> +impl<T> VariableSource for T where T: ?Sized {} + +impl<T, P> Variable<P> for PhantomData<T> where T: ?Sized, + P: ?Sized, { - /// Allocation Mode - type Mode: AllocationMode; - - /// Variable Object Type - type Variable: Variable<Self, Mode = Self::Mode, Type = T>; - - /// Allocates a new variable into `self` with the given `allocation`. - #[inline] - fn allocate<'t>(&mut self, allocation: impl Into<Allocation<'t, T, Self>>) -> Self::Variable - where - T: 't, - { - Self::Variable::new(self, allocation) - } + type Type = PhantomData<T>; - /// Allocates a new known variable into `self` with the given `mode`. - #[inline] - fn allocate_known( - &mut self, - value: &T, - mode: impl Into<<Self::Mode as AllocationMode>::Known>, - ) -> Self::Variable { - self.allocate((value, mode.into())) - } + type Mode = (); - /// Allocates a new unknown variable into `self` with the given `mode`. #[inline] - fn allocate_unknown( - &mut self, - mode: impl Into<<Self::Mode as AllocationMode>::Unknown>, - ) -> Self::Variable { - self.allocate(Allocation::Unknown(mode.into())) + fn new(ps: &mut P, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + let _ = (ps, allocation); + PhantomData } } -impl<P, T> HasVariable<T> for P +impl<T, P> reflection::HasAllocation<P> for PhantomData<T> where + T: ?Sized, P: ?Sized, - T: Alloc<P> + ?Sized, { - type Mode = T::Mode; - type Variable = T::Variable; + type Variable = PhantomData<T>; + type Mode = (); } -/// Allocates a new unknown variable into `ps` with the given `mode`. +/// Allocates a new known variable into `ps` with the given `mode`. #[inline] -pub fn known<T, P>(ps: &mut P, value: &T, mode: KnownMode<T, P>) -> Var<T, P> +pub fn known<P, V>( + ps: &mut P, + value: &V::Type, + mode: impl Into<<V::Mode as AllocationMode>::Known>, +) -> V where - T: ?Sized, - P: HasVariable<T> + ?Sized, + P: ?Sized, + V: Variable<P>, { - ps.allocate_known(value, mode) + V::new_known(ps, value, mode) } /// Allocates a new unknown variable into `ps` with the given `mode`. #[inline] -pub fn unknown<T, P>(ps: &mut P, mode: UnknownMode<T, P>) -> Var<T, P> +pub fn unknown<P, V>(ps: &mut P, mode: impl Into<<V::Mode as AllocationMode>::Unknown>) -> V where - T: ?Sized, - P: HasVariable<T> + ?Sized, + P: ?Sized, + V: Variable<P>, { - ps.allocate_unknown(mode) + V::new_unknown(ps, mode) } -impl<T, P> Alloc<P> for PhantomData<T> -where - T: ?Sized, - P: ?Sized, -{ - type Mode = (); +/// Allocation System +pub trait AllocationSystem { + /// Allocates a new variable into `self` with the given `allocation`. + #[inline] + fn allocate<V>(&mut self, allocation: Allocation<V::Type, V::Mode>) -> V + where + V: Variable<Self>, + { + V::new(self, allocation) + } - type Variable = PhantomData<T>; + /// Allocates a new known variable into `self` with the given `mode`. + #[inline] + fn allocate_known<V>( + &mut self, + value: &V::Type, + mode: impl Into<<V::Mode as AllocationMode>::Known>, + ) -> V + where + V: Variable<Self>, + { + known(self, value, mode) + } + /// Allocates a new unknown variable into `self` with the given `mode`. #[inline] - fn variable<'t>(ps: &mut P, allocation: impl Into<Allocation<'t, Self, P>>) -> Self::Variable + fn allocate_unknown<V>(&mut self, mode: impl Into<<V::Mode as AllocationMode>::Unknown>) -> V where - Self: 't, + V: Variable<Self>, { - let _ = (ps, allocation); - PhantomData + unknown(self, mode) } } -impl<T, P> Variable<P> for PhantomData<T> -where - T: ?Sized, - P: ?Sized, -{ - type Mode = (); - type Type = PhantomData<T>; -} +impl<P> AllocationSystem for P where P: ?Sized {} /// Boolean Constraint System -pub trait BooleanSystem: HasVariable<bool> { +pub trait BooleanSystem { + /// Boolean Variable Type + type Bool: Variable<Self, Type = bool>; + /// Asserts that `b` is `true`. - fn assert(&mut self, b: Bool<Self>); + fn assert(&mut self, b: Self::Bool); /// Asserts that all the booleans in `iter` are `true`. #[inline] fn assert_all<I>(&mut self, iter: I) where - I: IntoIterator<Item = Bool<Self>>, + I: IntoIterator<Item = Self::Bool>, { iter.into_iter().for_each(move |b| self.assert(b)) } /// Generates a boolean that represents the fact that `lhs` and `rhs` may be equal. #[inline] - fn eq<V>(&mut self, lhs: &V, rhs: &V) -> Bool<Self> + fn eq<V>(&mut self, lhs: &V, rhs: &V) -> Self::Bool where - V: Variable<Self>, - V::Type: AllocEq<Self>, + V: Variable<Self> + Equal<Self>, { - V::Type::eq(self, lhs, rhs) + V::eq(self, lhs, rhs) } /// Asserts that `lhs` and `rhs` are equal. #[inline] fn assert_eq<V>(&mut self, lhs: &V, rhs: &V) where - V: Variable<Self>, - V::Type: AllocEq<Self>, + V: Variable<Self> + Equal<Self>, { - V::Type::assert_eq(self, lhs, rhs) + V::assert_eq(self, lhs, rhs) } /// Asserts that all the elements in `iter` are equal to some `base` element. #[inline] fn assert_all_eq_to_base<'t, V, I>(&mut self, base: &'t V, iter: I) where - V: 't + Variable<Self>, - V::Type: AllocEq<Self>, + V: 't + Variable<Self> + Equal<Self>, I: IntoIterator<Item = &'t V>, { - V::Type::assert_all_eq_to_base(self, base, iter) + V::assert_all_eq_to_base(self, base, iter) } /// Asserts that all the elements in `iter` are equal. #[inline] fn assert_all_eq<'t, V, I>(&mut self, iter: I) where - V: 't + Variable<Self>, - V::Type: AllocEq<Self>, + V: 't + Variable<Self> + Equal<Self>, I: IntoIterator<Item = &'t V>, { - V::Type::assert_all_eq(self, iter) + V::assert_all_eq(self, iter) } } /// Equality Trait -pub trait AllocEq<P>: Alloc<P> +pub trait Equal<P>: Variable<P> where P: BooleanSystem + ?Sized, { /// Generates a boolean that represents the fact that `lhs` and `rhs` may be equal. - fn eq(ps: &mut P, lhs: &Self::Variable, rhs: &Self::Variable) -> Bool<P>; + fn eq(ps: &mut P, lhs: &Self, rhs: &Self) -> P::Bool; /// Asserts that `lhs` and `rhs` are equal. #[inline] - fn assert_eq(ps: &mut P, lhs: &Self::Variable, rhs: &Self::Variable) { + fn assert_eq(ps: &mut P, lhs: &Self, rhs: &Self) { let boolean = Self::eq(ps, lhs, rhs); ps.assert(boolean) } /// Asserts that all the elements in `iter` are equal to some `base` element. #[inline] - fn assert_all_eq_to_base<'t, I>(ps: &mut P, base: &'t Self::Variable, iter: I) + fn assert_all_eq_to_base<'t, I>(ps: &mut P, base: &'t Self, iter: I) where - I: IntoIterator<Item = &'t Self::Variable>, + I: IntoIterator<Item = &'t Self>, { for item in iter { Self::assert_eq(ps, base, item) @@ -406,8 +384,8 @@ where #[inline] fn assert_all_eq<'t, I>(ps: &mut P, iter: I) where - Self::Variable: 't, - I: IntoIterator<Item = &'t Self::Variable>, + Self: 't, + I: IntoIterator<Item = &'t Self>, { let mut iter = iter.into_iter(); if let Some(base) = iter.next() { @@ -624,9 +602,162 @@ where } } +/// Opt-In Compile-Time Reflection Capabilities +/// +/// See [`HasAllocation`] and [`HasVariable`] for more information. +/// +/// [`HasAllocation`]: reflection::HasAllocation +/// [`HasVariable`]: reflection::HasVariable +pub mod reflection { + use super::*; + + /// Variable Type + /// + /// Requires a [`HasAllocation`] implementation for `T`. + pub type Var<T, P> = <P as HasVariable<T>>::Variable; + + /// Allocation Mode Type + /// + /// Requires a [`HasAllocation`] implementation for `T`. + pub type Mode<T, P> = <Var<T, P> as Variable<P>>::Mode; + + /// Known Allocation Mode Type + /// + /// Requires a [`HasAllocation`] implementation for `T`. + pub type KnownMode<T, P> = <Mode<T, P> as AllocationMode>::Known; + + /// Known Allocation Mode Type + /// + /// Requires a [`HasAllocation`] implementation for `T`. + pub type UnknownMode<T, P> = <Mode<T, P> as AllocationMode>::Unknown; + + /// Allocation Entry Type + /// + /// Requires a [`HasAllocation`] implementation for `T`. + pub type Alloc<'t, T, P> = Allocation<'t, T, Mode<T, P>>; + + /// Variable Existence Reflection Trait + /// + /// This trait can be optionally implemented by any type `T` which has an existing variable + /// type that implements [`Variable<P, Type = T>`](Variable). Implementing this trait unlocks + /// all of the reflection capabilities in this module. + /// + /// Whenever possible, library authors should implement [`HasAllocation`] on their types which + /// have associated variables but should minimize their use of [`HasVariable`] so that users + /// can take advantage of as much of a library as possible while implementing as little as + /// possible. + pub trait HasAllocation<P> + where + P: ?Sized, + { + /// Variable Object Type + type Variable: Variable<P, Mode = Self::Mode, Type = Self>; + + /// Allocation Mode + type Mode: AllocationMode; + + /// Allocates a new variable into `ps` with the given `allocation`. + #[inline] + fn variable(ps: &mut P, allocation: Allocation<Self, Self::Mode>) -> Self::Variable { + Self::Variable::new(ps, allocation) + } + + /// Allocates a new known variable into `ps` with the given `mode`. + #[inline] + fn known( + &self, + ps: &mut P, + mode: impl Into<<Self::Mode as AllocationMode>::Known>, + ) -> Self::Variable { + Self::Variable::new_known(ps, self, mode) + } + + /// Allocates a new unknown variable into `ps` with the given `mode`. + #[inline] + fn unknown( + ps: &mut P, + mode: impl Into<<Self::Mode as AllocationMode>::Unknown>, + ) -> Self::Variable { + Self::Variable::new_unknown(ps, mode) + } + } + + /// Variable Existence Reflection Trait + /// + /// This trait is automatically implemented for all types [`T: HasAllocation`](HasAllocation) + /// and it activates all the reflection features in this module. See that trait for more + /// information on activating reflection. + pub trait HasVariable<T> + where + T: ?Sized, + { + /// Variable Object Type + type Variable: Variable<Self, Mode = Self::Mode, Type = T>; + + /// Allocation Mode + type Mode: AllocationMode; + + /// Allocates a new variable into `self` with the given `allocation`. + #[inline] + fn new_allocation(&mut self, allocation: Allocation<T, Self::Mode>) -> Self::Variable { + Self::Variable::new(self, allocation) + } + + /// Allocates a new known variable into `self` with the given `mode`. + #[inline] + fn new_known_allocation( + &mut self, + value: &T, + mode: impl Into<<Self::Mode as AllocationMode>::Known>, + ) -> Self::Variable { + Self::Variable::new_known(self, value, mode) + } + + /// Allocates a new unknown variable into `self` with the given `mode`. + #[inline] + fn new_unknown_allocation( + &mut self, + mode: impl Into<<Self::Mode as AllocationMode>::Unknown>, + ) -> Self::Variable { + Self::Variable::new_unknown(self, mode) + } + } + + impl<P, T> HasVariable<T> for P + where + P: ?Sized, + T: HasAllocation<P> + ?Sized, + { + type Variable = T::Variable; + type Mode = T::Mode; + } + + /// Allocates a new unknown variable into `ps` with the given `mode`. + #[inline] + pub fn known<T, P>(ps: &mut P, value: &T, mode: KnownMode<T, P>) -> Var<T, P> + where + T: ?Sized, + P: HasVariable<T> + ?Sized, + { + ps.new_known_allocation(value, mode) + } + + /// Allocates a new unknown variable into `ps` with the given `mode`. + #[inline] + pub fn unknown<T, P>(ps: &mut P, mode: UnknownMode<T, P>) -> Var<T, P> + where + T: ?Sized, + P: HasVariable<T> + ?Sized, + { + ps.new_unknown_allocation(mode) + } +} + /// Type Aliases +/// +/// All of these types depend on reflection capabilities. See [`reflection`] for more information. pub mod types { - use super::*; + use super::reflection::Var; /// Boolean Variable Type pub type Bool<P> = Var<bool, P>; diff --git a/manta-crypto/src/set/constraint.rs b/manta-crypto/src/set/constraint.rs index 1b8eb8c7f..dee44aca3 100644 --- a/manta-crypto/src/set/constraint.rs +++ b/manta-crypto/src/set/constraint.rs @@ -18,8 +18,8 @@ use crate::{ constraint::{ - unknown, Alloc, Allocation, AllocationMode, BooleanSystem, Derived, HasVariable, Mode, Var, - Variable, + reflection::{unknown, HasAllocation, HasVariable, Mode, Var}, + Allocation, AllocationMode, AllocationSystem, BooleanSystem, Derived, Variable, }, set::{ContainmentProof, Set, VerifiedSet}, }; @@ -101,9 +101,9 @@ where /// Asserts that `self` is a valid proof to the fact that `item` is stored in the verified set. #[inline] - pub fn assert_validity<V>(&self, set: &V, item: &ItemVar<V, P>, ps: &mut P) + pub fn assert_validity<V>(&self, set: &V, item: &V::ItemVar, ps: &mut P) where - P: BooleanSystem + HasVariable<S::Item>, + P: BooleanSystem, V: VerifiedSetVariable<P, Type = S>, { set.assert_valid_containment_proof(&self.public_input, &self.secret_witness, item, ps) @@ -115,30 +115,18 @@ where S: VerifiedSet + ?Sized, P: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, { - type Mode = ContainmentProofMode<Mode<S::Public, P>, Mode<S::Secret, P>>; type Type = ContainmentProof<S>; -} -impl<S, P> Alloc<P> for ContainmentProof<S> -where - S: VerifiedSet + ?Sized, - P: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, -{ type Mode = ContainmentProofMode<Mode<S::Public, P>, Mode<S::Secret, P>>; - type Variable = ContainmentProofVar<S, P>; - #[inline] - fn variable<'t>(ps: &mut P, allocation: impl Into<Allocation<'t, Self, P>>) -> Self::Variable - where - Self: 't, - { - match allocation.into() { - Allocation::Known(this, mode) => Self::Variable::new( + fn new(ps: &mut P, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + match allocation { + Allocation::Known(this, mode) => Self::new( ps.allocate_known(&this.public_input, mode.public), ps.allocate_known(&this.secret_witness, mode.secret), ), - Allocation::Unknown(mode) => Self::Variable::new( + Allocation::Unknown(mode) => Self::new( unknown::<S::Public, _>(ps, mode.public), unknown::<S::Secret, _>(ps, mode.secret), ), @@ -146,8 +134,14 @@ where } } -/// Item Type for [`VerifiedSetVariable`] -pub type ItemType<V, P> = <<V as Variable<P>>::Type as Set>::Item; +impl<S, P> HasAllocation<P> for ContainmentProof<S> +where + S: VerifiedSet + ?Sized, + P: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, +{ + type Variable = ContainmentProofVar<S, P>; + type Mode = ContainmentProofMode<Mode<S::Public, P>, Mode<S::Secret, P>>; +} /// Public Input Type for [`VerifiedSetVariable`] pub type PublicInputType<V, P> = <<V as Variable<P>>::Type as VerifiedSet>::Public; @@ -155,9 +149,6 @@ pub type PublicInputType<V, P> = <<V as Variable<P>>::Type as VerifiedSet>::Publ /// Secret Witness Type for [`VerifiedSetVariable`] pub type SecretWitnessType<V, P> = <<V as Variable<P>>::Type as VerifiedSet>::Secret; -/// Item Variable for [`VerifiedSetVariable`] -pub type ItemVar<V, P> = Var<ItemType<V, P>, P>; - /// Public Input Variable for [`VerifiedSetVariable`] pub type PublicInputVar<V, P> = Var<PublicInputType<V, P>, P>; @@ -168,49 +159,21 @@ pub type SecretWitnessVar<V, P> = Var<SecretWitnessType<V, P>, P>; pub trait VerifiedSetVariable<P>: Variable<P> where P: BooleanSystem - + HasVariable<ItemType<Self, P>> + HasVariable<PublicInputType<Self, P>> + HasVariable<SecretWitnessType<Self, P>> + ?Sized, Self::Type: VerifiedSet, { + /// Item Variable + type ItemVar: Variable<P, Type = <Self::Type as Set>::Item>; + /// Asserts that `public_input` and `secret_witness` form a proof to the fact that `item` is /// stored in `self`. fn assert_valid_containment_proof( &self, public_input: &PublicInputVar<Self, P>, secret_witness: &SecretWitnessVar<Self, P>, - item: &ItemVar<Self, P>, + item: &Self::ItemVar, ps: &mut P, ); } - -/* TODO[remove]: -/// Verified Set Proof System -pub trait VerifiedSetProofSystem<S>: - BooleanSystem - + HasVariable<S::Item, Mode = Self::ItemMode> - + HasVariable<S::Public, Mode = Self::PublicMode> - + HasVariable<S::Secret, Mode = Self::SecretMode> -where - S: VerifiedSet + ?Sized, -{ - /// Item Allocation Mode - type ItemMode: AllocationMode; - - /// Public Input Allocation Mode - type PublicMode: AllocationMode; - - /// Secret Witness Allocation Mode - type SecretMode: AllocationMode; - - /// Asserts that `public_input` and `secret_witness` form a proof to the fact that `item` is - /// stored in the verified set. - fn assert_validity( - &mut self, - public_input: &Var<S::Public, Self>, - secret_witness: &Var<S::Secret, Self>, - item: &Var<S::Item, Self>, - ); -} -*/ diff --git a/manta-pay/src/accounting/config.rs b/manta-pay/src/accounting/config.rs index 193539202..6b5d55410 100644 --- a/manta-pay/src/accounting/config.rs +++ b/manta-pay/src/accounting/config.rs @@ -71,6 +71,7 @@ pub type ProofSystem = ArkProofSystem<ConstraintField>; #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Configuration; +/* FIXME: impl IdentityProofSystemConfiguration for Configuration { type BooleanSystem = ProofSystem; type PseudorandomFunctionFamilySeed = <Blake2s as PseudorandomFunctionFamily>::Seed; @@ -93,3 +94,4 @@ impl TransferConfiguration for Configuration { type UtxoSet = UtxoSet; type UtxoSetVar = UtxoSetVar; } +*/ diff --git a/manta-pay/src/accounting/ledger.rs b/manta-pay/src/accounting/ledger.rs index 5f3da2e5e..8b714201a 100644 --- a/manta-pay/src/accounting/ledger.rs +++ b/manta-pay/src/accounting/ledger.rs @@ -35,7 +35,7 @@ use blake2::{ }; use manta_accounting::{identity, Ledger as LedgerTrait, ProofPostError}; use manta_crypto::{ - constraint::{self, Alloc, Allocation, Constant, Variable}, + constraint::{self, reflection::HasAllocation, Allocation, Constant, Variable}, set::{constraint::VerifiedSetVariable, ContainmentProof, Set, VerifiedSet}, }; use manta_util::{as_bytes, concatenate, into_array_unchecked}; diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs index b8d6210f6..2f164f9d7 100644 --- a/manta-pay/src/crypto/commitment/pedersen.rs +++ b/manta-pay/src/crypto/commitment/pedersen.rs @@ -149,7 +149,7 @@ pub mod constraint { }; use core::marker::PhantomData; use manta_crypto::constraint::{ - Alloc, AllocEq, Allocation, Bool, Constant, PublicOrSecret, Secret, Var, Variable, + Allocation, Constant, HasAllocation, PublicOrSecret, Secret, Var, Variable, }; /// Constraint Field Type @@ -423,7 +423,7 @@ pub mod constraint { ps: &mut ProofSystem<C>, lhs: &Var<Self, ProofSystem<C>>, rhs: &Var<Self, ProofSystem<C>>, - ) -> Bool<ProofSystem<C>> { + ) -> ProofSystem<C>::Bool { todo!() } } diff --git a/manta-pay/src/crypto/constraint/proof_system.rs b/manta-pay/src/crypto/constraint/proof_system.rs index bc0de9f4e..0a73be443 100644 --- a/manta-pay/src/crypto/constraint/proof_system.rs +++ b/manta-pay/src/crypto/constraint/proof_system.rs @@ -29,8 +29,8 @@ use ark_relations::{ use core::borrow::Borrow; use manta_accounting::{AssetBalance, AssetId}; use manta_crypto::constraint::{ - Alloc, Allocation, AllocationMode, Bool, BooleanSystem, ProofSystem, Public, PublicOrSecret, - Secret, Variable, + Alloc, AllocEq, Allocation, AllocationMode, Bool, BooleanSystem, ProofSystem, Public, + PublicOrSecret, Secret, Var, Variable, }; use manta_util::{Concat, ConcatAccumulator}; @@ -137,12 +137,17 @@ where where Self: 't, { - use ArkAllocationMode::*; match allocation.into() { Allocation::Known(this, mode) => match mode { - Constant => Self::Variable::new_constant(ns!(ps.cs, "boolean constant"), this), - Public => Self::Variable::new_input(ns!(ps.cs, "boolean input"), full(this)), - Secret => Self::Variable::new_witness(ns!(ps.cs, "boolean witness"), full(this)), + ArkAllocationMode::Constant => { + Self::Variable::new_constant(ns!(ps.cs, "boolean constant"), this) + } + ArkAllocationMode::Public => { + Self::Variable::new_input(ns!(ps.cs, "boolean input"), full(this)) + } + ArkAllocationMode::Secret => { + Self::Variable::new_witness(ns!(ps.cs, "boolean witness"), full(this)) + } }, Allocation::Unknown(mode) => match mode { PublicOrSecret::Public => { @@ -157,6 +162,22 @@ where } } +impl<F> AllocEq<ArkProofSystem<F>> for bool +where + F: Field, +{ + #[inline] + fn eq( + ps: &mut ArkProofSystem<F>, + lhs: &Var<Self, ArkProofSystem<F>>, + rhs: &Var<Self, ArkProofSystem<F>>, + ) -> Bool<ArkProofSystem<F>> { + let _ = ps; + lhs.is_eq(rhs) + .expect("Equality checking is not allowed to fail.") + } +} + /// Prime Field Element #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] @@ -188,14 +209,15 @@ where where Self: 't, { - use ArkAllocationMode::*; match allocation.into() { Allocation::Known(this, mode) => match mode { - Constant => { + ArkAllocationMode::Constant => { Self::Variable::new_constant(ns!(ps.cs, "prime field constant"), this.0) } - Public => Self::Variable::new_input(ns!(ps.cs, "prime field input"), full(this.0)), - Secret => { + ArkAllocationMode::Public => { + Self::Variable::new_input(ns!(ps.cs, "prime field input"), full(this.0)) + } + ArkAllocationMode::Secret => { Self::Variable::new_witness(ns!(ps.cs, "prime field witness"), full(this.0)) } }, @@ -335,6 +357,23 @@ where } } +impl<F> AllocEq<ArkProofSystem<F>> for AssetId +where + F: PrimeField, +{ + #[inline] + fn eq( + ps: &mut ArkProofSystem<F>, + lhs: &Var<Self, ArkProofSystem<F>>, + rhs: &Var<Self, ArkProofSystem<F>>, + ) -> Bool<ArkProofSystem<F>> { + let _ = ps; + lhs.0 + .is_eq(&rhs.0) + .expect("Equality checking is not allowed to fail.") + } +} + /// Asset Balance Variable #[derive(derivative::Derivative)] #[derivative(Clone, Debug)] diff --git a/manta-pay/src/crypto/merkle_tree.rs b/manta-pay/src/crypto/merkle_tree/mod.rs similarity index 100% rename from manta-pay/src/crypto/merkle_tree.rs rename to manta-pay/src/crypto/merkle_tree/mod.rs diff --git a/manta-pay/src/crypto/prf/blake2s.rs b/manta-pay/src/crypto/prf/blake2s.rs index 7a020e423..4be12e8fd 100644 --- a/manta-pay/src/crypto/prf/blake2s.rs +++ b/manta-pay/src/crypto/prf/blake2s.rs @@ -103,7 +103,7 @@ pub mod constraint { prf::blake2s::constraints::Blake2sGadget as ArkBlake2sVar, PRFGadget, }; use ark_ff::PrimeField; - use ark_r1cs_std::{uint8::UInt8, ToBytesGadget}; + use ark_r1cs_std::{eq::EqGadget, uint8::UInt8, ToBytesGadget}; use core::marker::PhantomData; use manta_crypto::constraint::{ Alloc, AllocEq, Allocation, Bool, Constant, PublicOrSecret, Secret, Var, Variable, @@ -304,7 +304,10 @@ pub mod constraint { lhs: &Var<Self, ProofSystem<F>>, rhs: &Var<Self, ProofSystem<F>>, ) -> Bool<ProofSystem<F>> { - todo!() + let _ = ps; + lhs.0 + .is_eq(&rhs.0) + .expect("Equality checking is not allowed to fail.") } } From 1dcbfd1ac1a895382d86de3d6a21f159d90bab2c Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 16 Sep 2021 01:34:22 -0400 Subject: [PATCH 035/275] clean up constraint system APIs --- manta-accounting/src/identity.rs | 112 +++---- manta-accounting/src/transfer.rs | 8 +- manta-crypto/src/constraint.rs | 97 +++++- manta-crypto/src/set/constraint.rs | 6 +- manta-pay/src/accounting/config.rs | 37 ++- manta-pay/src/accounting/ledger.rs | 28 +- manta-pay/src/crypto/commitment/pedersen.rs | 148 ++++++---- .../src/crypto/constraint/proof_system.rs | 276 +++++++++--------- manta-pay/src/crypto/merkle_tree/mod.rs | 125 ++++---- manta-pay/src/crypto/prf/blake2s.rs | 142 ++++----- 10 files changed, 511 insertions(+), 468 deletions(-) diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 8af462fca..12c13c4d4 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -31,7 +31,7 @@ use manta_crypto::{ commitment::{CommitmentScheme, Input as CommitmentInput}, constraint::{ reflection::{HasAllocation, HasVariable, Var}, - Allocation, BooleanSystem, Constant, Derived, Equal, Public, PublicOrSecret, Secret, + Allocation, Constant, ConstraintSystem, Derived, Equal, Public, PublicOrSecret, Secret, Variable, }, ies::{self, EncryptedMessage, IntegratedEncryptionScheme}, @@ -73,50 +73,50 @@ pub trait IdentityConfiguration { type Rng: CryptoRng + RngCore + SeedableRng<Seed = Self::SecretKey>; } -/// [`Identity`] Proof System Configuration -pub trait IdentityProofSystemConfiguration: IdentityConfiguration { - /// Boolean System - type BooleanSystem: BooleanSystem +/// [`Identity`] Constraint System Configuration +pub trait IdentityConstraintSystemConfiguration: IdentityConfiguration { + /// Constraint System + type ConstraintSystem: ConstraintSystem + HasVariable<AssetId, Mode = PublicOrSecret> + HasVariable<AssetBalance, Mode = PublicOrSecret>; /// Secret Key Variable - type SecretKeyVar: Variable<Self::BooleanSystem, Type = Self::SecretKey, Mode = Secret>; + type SecretKeyVar: Variable<Self::ConstraintSystem, Type = Self::SecretKey, Mode = Secret>; /// Pseudorandom Function Family Input Variable type PseudorandomFunctionFamilyInputVar: Variable< - Self::BooleanSystem, + Self::ConstraintSystem, Type = <Self::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Input, Mode = Secret, >; /// Pseudorandom Function Family Output Variable type PseudorandomFunctionFamilyOutputVar: Variable< - Self::BooleanSystem, + Self::ConstraintSystem, Type = <Self::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Output, Mode = PublicOrSecret, - > + Equal<Self::BooleanSystem>; + > + Equal<Self::ConstraintSystem>; /// Pseudorandom Function Family Variable type PseudorandomFunctionFamilyVar: PseudorandomFunctionFamily< Seed = Self::SecretKeyVar, Input = Self::PseudorandomFunctionFamilyInputVar, Output = Self::PseudorandomFunctionFamilyOutputVar, - > + Variable<Self::BooleanSystem, Type = Self::PseudorandomFunctionFamily, Mode = Constant>; + > + Variable<Self::ConstraintSystem, Type = Self::PseudorandomFunctionFamily, Mode = Constant>; /// Commitment Scheme Randomness Variable type CommitmentSchemeRandomnessVar: Variable< - Self::BooleanSystem, + Self::ConstraintSystem, Type = <Self::CommitmentScheme as CommitmentScheme>::Randomness, Mode = Secret, >; /// Commitment Scheme Output Variable type CommitmentSchemeOutputVar: Variable< - Self::BooleanSystem, + Self::ConstraintSystem, Type = <Self::CommitmentScheme as CommitmentScheme>::Output, Mode = PublicOrSecret, - > + Equal<Self::BooleanSystem>; + > + Equal<Self::ConstraintSystem>; /// Commitment Scheme Variable type CommitmentSchemeVar: CommitmentScheme< @@ -124,9 +124,9 @@ pub trait IdentityProofSystemConfiguration: IdentityConfiguration { Output = Self::CommitmentSchemeOutputVar, > + CommitmentInput<PublicKeyVar<Self>> + CommitmentInput<VoidNumberGeneratorVar<Self>> - + CommitmentInput<AssetVar<Self::BooleanSystem>> + + CommitmentInput<AssetVar<Self::ConstraintSystem>> + CommitmentInput<VoidNumberCommitmentVar<Self>> - + Variable<Self::BooleanSystem, Type = Self::CommitmentScheme, Mode = Constant>; + + Variable<Self::ConstraintSystem, Type = Self::CommitmentScheme, Mode = Constant>; } /// [`PseudorandomFunctionFamily::Input`] Type @@ -171,22 +171,22 @@ pub type Utxo<C> = CommitmentSchemeOutput<C>; /// [`PseudorandomFunctionFamily::Input`] Variable Type type PseudorandomFunctionFamilyInputVar<C> = - <C as IdentityProofSystemConfiguration>::PseudorandomFunctionFamilyInputVar; + <C as IdentityConstraintSystemConfiguration>::PseudorandomFunctionFamilyInputVar; /// [`PseudorandomFunctionFamily::Output`] Variable Type type PseudorandomFunctionFamilyOutputVar<C> = - <C as IdentityProofSystemConfiguration>::PseudorandomFunctionFamilyOutputVar; + <C as IdentityConstraintSystemConfiguration>::PseudorandomFunctionFamilyOutputVar; /// [`CommitmentScheme::Randomness`] Variable Type type CommitmentSchemeRandomnessVar<C> = - <C as IdentityProofSystemConfiguration>::CommitmentSchemeRandomnessVar; + <C as IdentityConstraintSystemConfiguration>::CommitmentSchemeRandomnessVar; /// [`CommitmentScheme::Output`] Variable Type type CommitmentSchemeOutputVar<C> = - <C as IdentityProofSystemConfiguration>::CommitmentSchemeOutputVar; + <C as IdentityConstraintSystemConfiguration>::CommitmentSchemeOutputVar; /// Secret Key Variable Type -pub type SecretKeyVar<C> = <C as IdentityProofSystemConfiguration>::SecretKeyVar; +pub type SecretKeyVar<C> = <C as IdentityConstraintSystemConfiguration>::SecretKeyVar; /// Public Key Variable Type pub type PublicKeyVar<C> = PseudorandomFunctionFamilyOutputVar<C>; @@ -211,7 +211,7 @@ pub type UtxoVar<C> = CommitmentSchemeOutputVar<C>; /// UTXO Containment Proof Variable Type pub type UtxoContainmentProofVar<C, S> = - ContainmentProofVar<S, <C as IdentityProofSystemConfiguration>::BooleanSystem>; + ContainmentProofVar<S, <C as IdentityConstraintSystemConfiguration>::ConstraintSystem>; /// Generates a void number commitment from `public_key`, `void_number_generator`, and /// `void_number_commitment_randomness`. @@ -355,7 +355,7 @@ where /// Asset Parameters Variable pub struct AssetParametersVar<C> where - C: IdentityProofSystemConfiguration, + C: IdentityConstraintSystemConfiguration, { /// Void Number Generator pub void_number_generator: VoidNumberGeneratorVar<C>, @@ -369,7 +369,7 @@ where impl<C> AssetParametersVar<C> where - C: IdentityProofSystemConfiguration, + C: IdentityConstraintSystemConfiguration, { /// Builds a new [`AssetParametersVar`] from parameter variables. #[inline] @@ -386,16 +386,16 @@ where } } -impl<C> Variable<C::BooleanSystem> for AssetParametersVar<C> +impl<C> Variable<C::ConstraintSystem> for AssetParametersVar<C> where - C: IdentityProofSystemConfiguration, + C: IdentityConstraintSystemConfiguration, { type Type = AssetParameters<C>; type Mode = Secret; #[inline] - fn new(ps: &mut C::BooleanSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new(ps: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self::new( VoidNumberGeneratorVar::<C>::new_known(ps, &this.void_number_generator, mode), @@ -415,9 +415,9 @@ where } } -impl<C> HasAllocation<C::BooleanSystem> for AssetParameters<C> +impl<C> HasAllocation<C::ConstraintSystem> for AssetParameters<C> where - C: IdentityProofSystemConfiguration, + C: IdentityConstraintSystemConfiguration, { type Variable = AssetParametersVar<C>; type Mode = Secret; @@ -1193,9 +1193,9 @@ where /// Sender Variable pub struct SenderVar<C, S> where - C: IdentityProofSystemConfiguration, + C: IdentityConstraintSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - C::BooleanSystem: HasVariable<S::Public> + HasVariable<S::Secret>, + C::ConstraintSystem: HasVariable<S::Public> + HasVariable<S::Secret>, { /// Secret Key secret_key: SecretKeyVar<C>, @@ -1204,7 +1204,7 @@ where public_key: PublicKeyVar<C>, /// Asset - asset: AssetVar<C::BooleanSystem>, + asset: AssetVar<C::ConstraintSystem>, /// Asset Parameters parameters: AssetParametersVar<C>, @@ -1224,21 +1224,21 @@ where impl<C, S> SenderVar<C, S> where - C: IdentityProofSystemConfiguration, + C: IdentityConstraintSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - C::BooleanSystem: HasVariable<S::Public> + HasVariable<S::Secret>, + C::ConstraintSystem: HasVariable<S::Public> + HasVariable<S::Secret>, { /// Checks if `self` is a well-formed sender and returns its asset. #[inline] pub fn get_well_formed_asset( self, - ps: &mut C::BooleanSystem, + ps: &mut C::ConstraintSystem, commitment_scheme: &C::CommitmentSchemeVar, - utxo_set: &Var<S, C::BooleanSystem>, - ) -> AssetVar<C::BooleanSystem> + utxo_set: &Var<S, C::ConstraintSystem>, + ) -> AssetVar<C::ConstraintSystem> where - S: HasAllocation<C::BooleanSystem>, - S::Variable: VerifiedSetVariable<C::BooleanSystem, ItemVar = UtxoVar<C>>, + S: HasAllocation<C::ConstraintSystem>, + S::Variable: VerifiedSetVariable<C::ConstraintSystem, ItemVar = UtxoVar<C>>, { // Well-formed check: // @@ -1315,18 +1315,19 @@ where } } -impl<C, S> Variable<C::BooleanSystem> for SenderVar<C, S> +impl<C, S> Variable<C::ConstraintSystem> for SenderVar<C, S> where - C: IdentityProofSystemConfiguration, + C: IdentityConstraintSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - C::BooleanSystem: HasVariable<S::Public, Mode = Public> + HasVariable<S::Secret, Mode = Secret>, + C::ConstraintSystem: + HasVariable<S::Public, Mode = Public> + HasVariable<S::Secret, Mode = Secret>, { type Type = Sender<C, S>; type Mode = Derived; #[inline] - fn new(ps: &mut C::BooleanSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new(ps: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self { secret_key: SecretKeyVar::<C>::new_known(ps, &this.secret_key, mode), @@ -1356,11 +1357,12 @@ where } } -impl<C, S> HasAllocation<C::BooleanSystem> for Sender<C, S> +impl<C, S> HasAllocation<C::ConstraintSystem> for Sender<C, S> where - C: IdentityProofSystemConfiguration, + C: IdentityConstraintSystemConfiguration, S: VerifiedSet<Item = Utxo<C>>, - C::BooleanSystem: HasVariable<S::Public, Mode = Public> + HasVariable<S::Secret, Mode = Secret>, + C::ConstraintSystem: + HasVariable<S::Public, Mode = Public> + HasVariable<S::Secret, Mode = Secret>, { type Variable = SenderVar<C, S>; type Mode = Derived; @@ -1524,11 +1526,11 @@ where /// Receiver Variable pub struct ReceiverVar<C, I> where - C: IdentityProofSystemConfiguration, + C: IdentityConstraintSystemConfiguration, I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// Asset - asset: AssetVar<C::BooleanSystem>, + asset: AssetVar<C::ConstraintSystem>, /// UTXO Randomness utxo_randomness: UtxoRandomnessVar<C>, @@ -1545,7 +1547,7 @@ where impl<C, I> ReceiverVar<C, I> where - C: IdentityProofSystemConfiguration, + C: IdentityConstraintSystemConfiguration, I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// Checks if `self` is a well-formed receiver and returns its asset. @@ -1559,9 +1561,9 @@ where #[inline] pub fn get_well_formed_asset( self, - ps: &mut C::BooleanSystem, + ps: &mut C::ConstraintSystem, commitment_scheme: &C::CommitmentSchemeVar, - ) -> AssetVar<C::BooleanSystem> { + ) -> AssetVar<C::ConstraintSystem> { ps.assert_eq( &self.utxo, &generate_utxo( @@ -1575,9 +1577,9 @@ where } } -impl<C, I> Variable<C::BooleanSystem> for ReceiverVar<C, I> +impl<C, I> Variable<C::ConstraintSystem> for ReceiverVar<C, I> where - C: IdentityProofSystemConfiguration, + C: IdentityConstraintSystemConfiguration, I: IntegratedEncryptionScheme<Plaintext = Asset>, { type Type = Receiver<C, I>; @@ -1585,7 +1587,7 @@ where type Mode = Derived; #[inline] - fn new(ps: &mut C::BooleanSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new(ps: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self { asset: AssetVar::new_known(ps, &this.asset, mode), @@ -1609,9 +1611,9 @@ where } } -impl<C, I> HasAllocation<C::BooleanSystem> for Receiver<C, I> +impl<C, I> HasAllocation<C::ConstraintSystem> for Receiver<C, I> where - C: IdentityProofSystemConfiguration, + C: IdentityConstraintSystemConfiguration, I: IntegratedEncryptionScheme<Plaintext = Asset>, { type Variable = ReceiverVar<C, I>; diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 833009fc4..abc06937c 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -19,8 +19,8 @@ use crate::{ asset::{sample_asset_balances, Asset, AssetBalance, AssetBalanceVar, AssetBalances, AssetId}, identity::{ - IdentityProofSystemConfiguration, Receiver, ReceiverPost, ReceiverVar, Sender, SenderPost, - SenderVar, Utxo, UtxoVar, VoidNumber, + IdentityConstraintSystemConfiguration, Receiver, ReceiverPost, ReceiverVar, Sender, + SenderPost, SenderVar, Utxo, UtxoVar, VoidNumber, }, ledger::{Ledger, PostError}, }; @@ -29,7 +29,7 @@ use core::ops::AddAssign; use manta_crypto::{ constraint::{ reflection::{HasAllocation, HasVariable}, - BooleanSystem, Constant, Derived, Equal, ProofSystem, Public, PublicOrSecret, Secret, + Constant, ConstraintSystem, Derived, Equal, ProofSystem, Public, PublicOrSecret, Secret, Variable, VariableSource, }, ies::{EncryptedMessage, IntegratedEncryptionScheme}, @@ -120,7 +120,7 @@ impl<const SOURCES: usize, const SINKS: usize> Distribution<PublicTransfer<SOURC /// Transfer Configuration pub trait TransferConfiguration: - IdentityProofSystemConfiguration<BooleanSystem = Self::ProofSystem> + IdentityConstraintSystemConfiguration<ConstraintSystem = Self::ProofSystem> { /// Proof System type ProofSystem: ProofSystem diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index b774339af..b4f91f624 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -76,24 +76,35 @@ where ), } +impl<'t, T, Mode> From<(&'t T, Mode::Known)> for Allocation<'t, T, Mode> +where + T: ?Sized, + Mode: AllocationMode, +{ + #[inline] + fn from((value, mode): (&'t T, Mode::Known)) -> Self { + Self::Known(value, mode) + } +} + impl<'t, T, Mode> Allocation<'t, T, Mode> where T: ?Sized, Mode: AllocationMode, { - /// Returns `true` if `self` represents a known variable. + /// Returns `true` if `self` represents a known value and mode. #[inline] pub fn is_known(&self) -> bool { matches!(self, Self::Known(..)) } - /// Returns `true` if `self` represents an unknown value. + /// Returns `true` if `self` represents an unknown value mode. #[inline] pub fn is_unknown(&self) -> bool { matches!(self, Self::Unknown(..)) } - /// Converts `self` into a possible known value. + /// Converts `self` into a possibly known value and mode. #[inline] pub fn known(self) -> Option<(&'t T, Mode::Known)> { match self { @@ -102,7 +113,7 @@ where } } - /// Converts `self` into a possibly unknown value. + /// Converts `self` into a possibly unknown mode. #[inline] pub fn unknown(self) -> Option<Mode::Unknown> { match self { @@ -110,16 +121,70 @@ where _ => None, } } -} -impl<'t, T, Mode> From<(&'t T, Mode::Known)> for Allocation<'t, T, Mode> -where - T: ?Sized, - Mode: AllocationMode, -{ + /// Converts `self` into a known value and mode whenever its unknown mode is [`Infallible`]. #[inline] - fn from((value, mode): (&'t T, Mode::Known)) -> Self { - Self::Known(value, mode) + pub fn into_known(self) -> (&'t T, Mode::Known) + where + Mode: AllocationMode<Unknown = Infallible>, + { + match self { + Self::Known(value, mode) => (value, mode), + _ => unreachable!("Values of infallible types cannot be constructed."), + } + } + + /// Converts `self` into an unknown mode whenever its known mode is [`Infallible`]. + #[inline] + pub fn into_unknown(self) -> Mode::Unknown + where + Mode: AllocationMode<Known = Infallible>, + { + match self { + Self::Unknown(mode) => mode, + _ => unreachable!("Values of infallible types cannot be constructed."), + } + } + + /// Maps over the possible value stored in `self`. + #[inline] + pub fn map<'u, U, N, F>(self, f: F) -> Allocation<'u, U, N> + where + Mode::Known: Into<N::Known>, + Mode::Unknown: Into<N::Unknown>, + N: AllocationMode, + F: FnOnce(&'t T) -> &'u U, + { + match self { + Self::Known(value, mode) => Allocation::Known(f(value), mode.into()), + Self::Unknown(mode) => Allocation::Unknown(mode.into()), + } + } + + /// Allocates a variable with `self` as the allocation entry into `ps`. + #[inline] + pub fn allocate<P, V>(self, ps: &mut P) -> V + where + P: ?Sized, + V: Variable<P, Type = T, Mode = Mode>, + { + V::new(ps, self) + } + + /// Allocates a variable into `ps` after mapping over `self`. + #[inline] + pub fn map_allocate<P, V, F>(self, ps: &mut P, f: F) -> V + where + Mode::Known: Into<<V::Mode as AllocationMode>::Known>, + Mode::Unknown: Into<<V::Mode as AllocationMode>::Unknown>, + P: ?Sized, + V: Variable<P>, + F: FnOnce(&'t T) -> V::Type, + { + match self { + Self::Known(value, mode) => V::new_known(ps, &f(value), mode), + Self::Unknown(mode) => V::new_unknown(ps, mode), + } } } @@ -298,8 +363,8 @@ pub trait AllocationSystem { impl<P> AllocationSystem for P where P: ?Sized {} -/// Boolean Constraint System -pub trait BooleanSystem { +/// Constraint System +pub trait ConstraintSystem { /// Boolean Variable Type type Bool: Variable<Self, Type = bool>; @@ -357,7 +422,7 @@ pub trait BooleanSystem { /// Equality Trait pub trait Equal<P>: Variable<P> where - P: BooleanSystem + ?Sized, + P: ConstraintSystem + ?Sized, { /// Generates a boolean that represents the fact that `lhs` and `rhs` may be equal. fn eq(ps: &mut P, lhs: &Self, rhs: &Self) -> P::Bool; @@ -395,7 +460,7 @@ where } /// Proof System -pub trait ProofSystem: BooleanSystem + Default { +pub trait ProofSystem: ConstraintSystem + Default { /// Proof Type type Proof; diff --git a/manta-crypto/src/set/constraint.rs b/manta-crypto/src/set/constraint.rs index dee44aca3..2d435879f 100644 --- a/manta-crypto/src/set/constraint.rs +++ b/manta-crypto/src/set/constraint.rs @@ -19,7 +19,7 @@ use crate::{ constraint::{ reflection::{unknown, HasAllocation, HasVariable, Mode, Var}, - Allocation, AllocationMode, AllocationSystem, BooleanSystem, Derived, Variable, + Allocation, AllocationMode, AllocationSystem, ConstraintSystem, Derived, Variable, }, set::{ContainmentProof, Set, VerifiedSet}, }; @@ -103,7 +103,7 @@ where #[inline] pub fn assert_validity<V>(&self, set: &V, item: &V::ItemVar, ps: &mut P) where - P: BooleanSystem, + P: ConstraintSystem, V: VerifiedSetVariable<P, Type = S>, { set.assert_valid_containment_proof(&self.public_input, &self.secret_witness, item, ps) @@ -158,7 +158,7 @@ pub type SecretWitnessVar<V, P> = Var<SecretWitnessType<V, P>, P>; /// Verified Set Variable pub trait VerifiedSetVariable<P>: Variable<P> where - P: BooleanSystem + P: ConstraintSystem + HasVariable<PublicInputType<Self, P>> + HasVariable<SecretWitnessType<Self, P>> + ?Sized, diff --git a/manta-pay/src/accounting/config.rs b/manta-pay/src/accounting/config.rs index 6b5d55410..1e3a29763 100644 --- a/manta-pay/src/accounting/config.rs +++ b/manta-pay/src/accounting/config.rs @@ -20,7 +20,7 @@ use crate::{ accounting::ledger::{UtxoSet, UtxoSetVar}, crypto::{ commitment::pedersen::{self, PedersenWindow}, - constraint::ArkProofSystem, + constraint::{ArkProofSystem, AssetBalanceVar, AssetIdVar}, ies::IES, prf::blake2s::{constraint::Blake2sVar, Blake2s}, rand::ChaCha20RngBlake2sSeedable, @@ -28,9 +28,10 @@ use crate::{ }; use ark_ed_on_bls12_381::{constraints::EdwardsVar, EdwardsProjective, Fq}; use manta_accounting::{ - identity::IdentityProofSystemConfiguration, transfer::TransferConfiguration, + identity::{IdentityConfiguration, IdentityConstraintSystemConfiguration}, + transfer::TransferConfiguration, }; -use manta_crypto::{commitment::CommitmentScheme, set::VerifiedSet, PseudorandomFunctionFamily}; +use manta_crypto::{commitment::CommitmentScheme, PseudorandomFunctionFamily}; /// Pedersen Window Parameters #[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -71,27 +72,31 @@ pub type ProofSystem = ArkProofSystem<ConstraintField>; #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Configuration; -/* FIXME: -impl IdentityProofSystemConfiguration for Configuration { - type BooleanSystem = ProofSystem; - type PseudorandomFunctionFamilySeed = <Blake2s as PseudorandomFunctionFamily>::Seed; - type PseudorandomFunctionFamilyInput = <Blake2s as PseudorandomFunctionFamily>::Input; - type PseudorandomFunctionFamilyOutput = <Blake2s as PseudorandomFunctionFamily>::Output; +impl IdentityConfiguration for Configuration { + type SecretKey = <Blake2s as PseudorandomFunctionFamily>::Seed; type PseudorandomFunctionFamily = Blake2s; - type PseudorandomFunctionFamilyVar = Blake2sVar<ConstraintField>; - type CommitmentSchemeRandomness = <PedersenCommitment as CommitmentScheme>::Randomness; - type CommitmentSchemeOutput = <PedersenCommitment as CommitmentScheme>::Output; type CommitmentScheme = PedersenCommitment; - type CommitmentSchemeVar = PedersenCommitmentVar; type Rng = ChaCha20RngBlake2sSeedable; } +impl IdentityConstraintSystemConfiguration for Configuration { + type ConstraintSystem = ProofSystem; + type SecretKeyVar = <Blake2sVar<ConstraintField> as PseudorandomFunctionFamily>::Seed; + type PseudorandomFunctionFamilyInputVar = + <Blake2sVar<ConstraintField> as PseudorandomFunctionFamily>::Input; + type PseudorandomFunctionFamilyOutputVar = + <Blake2sVar<ConstraintField> as PseudorandomFunctionFamily>::Output; + type PseudorandomFunctionFamilyVar = Blake2sVar<ConstraintField>; + type CommitmentSchemeRandomnessVar = <PedersenCommitmentVar as CommitmentScheme>::Randomness; + type CommitmentSchemeOutputVar = <PedersenCommitmentVar as CommitmentScheme>::Output; + type CommitmentSchemeVar = PedersenCommitmentVar; +} + impl TransferConfiguration for Configuration { type ProofSystem = ProofSystem; + type AssetIdVar = AssetIdVar<ConstraintField>; + type AssetBalanceVar = AssetBalanceVar<ConstraintField>; type IntegratedEncryptionScheme = IES; - type UtxoSetPublicInput = <UtxoSet as VerifiedSet>::Public; - type UtxoSetSecretWitness = <UtxoSet as VerifiedSet>::Secret; type UtxoSet = UtxoSet; type UtxoSetVar = UtxoSetVar; } -*/ diff --git a/manta-pay/src/accounting/ledger.rs b/manta-pay/src/accounting/ledger.rs index 8b714201a..e09e4dd2a 100644 --- a/manta-pay/src/accounting/ledger.rs +++ b/manta-pay/src/accounting/ledger.rs @@ -223,33 +223,25 @@ impl VerifiedSet for UtxoSet { pub struct UtxoSetVar(ParametersVar); impl Variable<ProofSystem> for UtxoSetVar { - type Mode = Constant; type Type = UtxoSet; -} -impl Alloc<ProofSystem> for UtxoSet { type Mode = Constant; - type Variable = UtxoSetVar; - #[inline] - fn variable<'t>( - ps: &mut ProofSystem, - allocation: impl Into<Allocation<'t, Self, ProofSystem>>, - ) -> Self::Variable - where - Self: 't, - { - UtxoSetVar(match allocation.into() { - Allocation::Known(this, mode) => this.parameters.as_known(ps, mode), - _ => unreachable!( - "Since we use a constant allocation mode, we always know the variable value." - ), - }) + fn new(ps: &mut ProofSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + let (this, mode) = allocation.into_known(); + Self(this.parameters.known(ps, mode)) } } +impl HasAllocation<ProofSystem> for UtxoSet { + type Variable = UtxoSetVar; + type Mode = Constant; +} + impl VerifiedSetVariable<ProofSystem> for UtxoSetVar { + type ItemVar = UtxoVar; + #[inline] fn assert_valid_containment_proof( &self, diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs index 2f164f9d7..d3eec83d4 100644 --- a/manta-pay/src/crypto/commitment/pedersen.rs +++ b/manta-pay/src/crypto/commitment/pedersen.rs @@ -137,19 +137,22 @@ where /// Pedersen Commitment Scheme Constraint System Implementation pub mod constraint { use super::*; - use crate::crypto::constraint::ArkProofSystem; + use crate::crypto::constraint::{empty, full, ArkProofSystem}; use ark_crypto_primitives::{ commitment::pedersen::constraints::{CommGadget, ParametersVar, RandomnessVar}, CommitmentGadget, }; use ark_ff::Field; use ark_r1cs_std::{ + alloc::AllocVar, groups::{CurveVar, GroupOpsBounds}, uint8::UInt8, }; + use ark_relations::ns; use core::marker::PhantomData; use manta_crypto::constraint::{ - Allocation, Constant, HasAllocation, PublicOrSecret, Secret, Var, Variable, + reflection::HasAllocation, types::Bool, Allocation, Constant, Equal, PublicOrSecret, + Secret, Variable, }; /// Constraint Field Type @@ -310,29 +313,36 @@ pub mod constraint { W: PedersenWindow, C: ProjectiveCurve, { - type Mode = Secret; type Type = PedersenCommitmentRandomness<W, C>; + + type Mode = Secret; + + #[inline] + fn new(ps: &mut ProofSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + Self( + match allocation.known() { + Some((this, _)) => RandomnessVar::new_witness( + ns!(ps.cs, "pedersen commitment randomness secret witness"), + full(&this.0), + ), + _ => RandomnessVar::new_witness( + ns!(ps.cs, "pedersen commitment randomness secret witness"), + empty::<ArkPedersenCommitmentRandomness<W, C>>, + ), + } + .expect("Variable allocation is not allowed to fail."), + PhantomData, + ) + } } - impl<W, C> Alloc<ProofSystem<C>> for PedersenCommitmentRandomness<W, C> + impl<W, C> HasAllocation<ProofSystem<C>> for PedersenCommitmentRandomness<W, C> where W: PedersenWindow, C: ProjectiveCurve, { - type Mode = Secret; - type Variable = PedersenCommitmentRandomnessVar<W, C>; - - #[inline] - fn variable<'t>( - ps: &mut ProofSystem<C>, - allocation: impl Into<Allocation<'t, Self, ProofSystem<C>>>, - ) -> Self::Variable - where - Self: 't, - { - todo!() - } + type Mode = Secret; } /// Pedersen Commitment Output Variable @@ -384,34 +394,53 @@ pub mod constraint { GG: CurveVar<C, ConstraintField<C>>, for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, { - type Mode = PublicOrSecret; type Type = PedersenCommitmentOutputWrapper<W, C, GG>; + + type Mode = PublicOrSecret; + + #[inline] + fn new(ps: &mut ProofSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + Self( + match allocation { + Allocation::Known(this, PublicOrSecret::Public) => { + AllocVar::<ArkPedersenCommitmentOutput<W, C>, _>::new_input( + ns!(ps.cs, "pedersen commitment output public input"), + full(&(this.0).0), + ) + } + Allocation::Known(this, PublicOrSecret::Secret) => { + AllocVar::<ArkPedersenCommitmentOutput<W, C>, _>::new_witness( + ns!(ps.cs, "pedersen commitment output secret witness"), + full(&(this.0).0), + ) + } + Allocation::Unknown(PublicOrSecret::Public) => GG::new_input( + ns!(ps.cs, "pedersen commitment output public input"), + empty::<ArkPedersenCommitmentOutput<W, C>>, + ), + Allocation::Unknown(PublicOrSecret::Secret) => GG::new_witness( + ns!(ps.cs, "pedersen commitment output secret witness"), + empty::<ArkPedersenCommitmentOutput<W, C>>, + ), + } + .expect("Variable allocation is not allowed to fail."), + PhantomData, + ) + } } - impl<W, C, GG> Alloc<ProofSystem<C>> for PedersenCommitmentOutputWrapper<W, C, GG> + impl<W, C, GG> HasAllocation<ProofSystem<C>> for PedersenCommitmentOutputWrapper<W, C, GG> where W: PedersenWindow, C: ProjectiveCurve, GG: CurveVar<C, ConstraintField<C>>, for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, { - type Mode = PublicOrSecret; - type Variable = PedersenCommitmentOutputVar<W, C, GG>; - - #[inline] - fn variable<'t>( - ps: &mut ProofSystem<C>, - allocation: impl Into<Allocation<'t, Self, ProofSystem<C>>>, - ) -> Self::Variable - where - Self: 't, - { - todo!() - } + type Mode = PublicOrSecret; } - impl<W, C, GG> AllocEq<ProofSystem<C>> for PedersenCommitmentOutputWrapper<W, C, GG> + impl<W, C, GG> Equal<ProofSystem<C>> for PedersenCommitmentOutputVar<W, C, GG> where W: PedersenWindow, C: ProjectiveCurve, @@ -419,12 +448,11 @@ pub mod constraint { for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, { #[inline] - fn eq( - ps: &mut ProofSystem<C>, - lhs: &Var<Self, ProofSystem<C>>, - rhs: &Var<Self, ProofSystem<C>>, - ) -> ProofSystem<C>::Bool { - todo!() + fn eq(ps: &mut ProofSystem<C>, lhs: &Self, rhs: &Self) -> Bool<ProofSystem<C>> { + let _ = ps; + lhs.0 + .is_eq(&rhs.0) + .expect("Equality checking is not allowed to fail.") } } @@ -445,40 +473,34 @@ pub mod constraint { GG: CurveVar<C, ConstraintField<C>>, for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, { - type Mode = Constant; type Type = PedersenCommitmentWrapper<W, C, GG>; + + type Mode = Constant; + + #[inline] + fn new(ps: &mut ProofSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + let (this, _) = allocation.into_known(); + Self( + ParametersVar::new_constant( + ns!(ps.cs, "pedersen commitment paramters constant"), + &(this.0).0, + ) + .expect("Variable allocation is not allowed to fail."), + PhantomData, + ) + } } - impl<W, C, GG> Alloc<ArkProofSystem<ConstraintField<C>>> for PedersenCommitmentWrapper<W, C, GG> + impl<W, C, GG> HasAllocation<ArkProofSystem<ConstraintField<C>>> + for PedersenCommitmentWrapper<W, C, GG> where W: PedersenWindow, C: ProjectiveCurve, GG: CurveVar<C, ConstraintField<C>>, for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, { - type Mode = Constant; - type Variable = PedersenCommitmentVar<W, C, GG>; - - #[inline] - fn variable<'t>( - ps: &mut ProofSystem<C>, - allocation: impl Into<Allocation<'t, Self, ProofSystem<C>>>, - ) -> Self::Variable - where - Self: 't, - { - match allocation.into() { - Allocation::Known(this, _) => { - // FIXME: `this.parameters.new_constant(ps)` - let _ = (ps, this); - todo!() - } - _ => unreachable!( - "Since we use a constant allocation mode, we always know the variable value." - ), - } - } + type Mode = Constant; } impl<W, C, GG> CommitmentScheme for PedersenCommitmentVar<W, C, GG> diff --git a/manta-pay/src/crypto/constraint/proof_system.rs b/manta-pay/src/crypto/constraint/proof_system.rs index 0a73be443..3dbcd20c9 100644 --- a/manta-pay/src/crypto/constraint/proof_system.rs +++ b/manta-pay/src/crypto/constraint/proof_system.rs @@ -16,7 +16,7 @@ //! Arkworks Proof System Implementation -use alloc::vec::Vec; +use alloc::{vec, vec::Vec}; use ark_ff::{fields::Field, PrimeField}; use ark_r1cs_std::{ alloc::AllocVar, bits::boolean::Boolean, eq::EqGadget, fields::fp::FpVar, uint8::UInt8, @@ -24,13 +24,16 @@ use ark_r1cs_std::{ }; use ark_relations::{ ns, - r1cs::{ConstraintSystem, ConstraintSystemRef, SynthesisError}, + r1cs::{ + ConstraintSystem as ArkConstraintSystem, ConstraintSystemRef as ArkConstraintSystemRef, + SynthesisError, + }, }; -use core::borrow::Borrow; +use core::{borrow::Borrow, ops::AddAssign}; use manta_accounting::{AssetBalance, AssetId}; use manta_crypto::constraint::{ - Alloc, AllocEq, Allocation, AllocationMode, Bool, BooleanSystem, ProofSystem, Public, - PublicOrSecret, Secret, Var, Variable, + reflection::HasAllocation, types::Bool, Allocation, AllocationMode, ConstraintSystem, Equal, + ProofSystem, Public, PublicOrSecret, Secret, Variable, }; use manta_util::{Concat, ConcatAccumulator}; @@ -99,7 +102,7 @@ where F: Field, { /// Constraint System - pub(crate) cs: ConstraintSystemRef<F>, + pub(crate) cs: ArkConstraintSystemRef<F>, } impl<F> Default for ArkProofSystem<F> @@ -109,52 +112,66 @@ where #[inline] fn default() -> Self { Self { - cs: ConstraintSystem::new_ref(), + cs: ArkConstraintSystem::new_ref(), } } } -impl<F> Variable<ArkProofSystem<F>> for Boolean<F> +impl<F> ConstraintSystem for ArkProofSystem<F> where F: Field, { - type Mode = ArkAllocationMode; - type Type = bool; + type Bool = Boolean<F>; + + #[inline] + fn assert(&mut self, b: Bool<Self>) { + // FIXME: Is there a more direct way to do assertions? + b.enforce_equal(&Boolean::TRUE) + .expect("This should never fail.") + } } -impl<F> Alloc<ArkProofSystem<F>> for bool +impl<F> ProofSystem for ArkProofSystem<F> +where + F: Field, +{ + type Proof = (); + + type Error = (); + + #[inline] + fn finish(self) -> Result<Self::Proof, Self::Error> { + todo!() + } +} +impl<F> Variable<ArkProofSystem<F>> for Boolean<F> where F: Field, { + type Type = bool; + type Mode = ArkAllocationMode; - type Variable = Boolean<F>; #[inline] - fn variable<'t>( - ps: &mut ArkProofSystem<F>, - allocation: impl Into<Allocation<'t, Self, ArkProofSystem<F>>>, - ) -> Self::Variable - where - Self: 't, - { - match allocation.into() { + fn new(ps: &mut ArkProofSystem<F>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + match allocation { Allocation::Known(this, mode) => match mode { ArkAllocationMode::Constant => { - Self::Variable::new_constant(ns!(ps.cs, "boolean constant"), this) + Self::new_constant(ns!(ps.cs, "boolean constant"), this) } ArkAllocationMode::Public => { - Self::Variable::new_input(ns!(ps.cs, "boolean input"), full(this)) + Self::new_input(ns!(ps.cs, "boolean input"), full(this)) } ArkAllocationMode::Secret => { - Self::Variable::new_witness(ns!(ps.cs, "boolean witness"), full(this)) + Self::new_witness(ns!(ps.cs, "boolean witness"), full(this)) } }, Allocation::Unknown(mode) => match mode { PublicOrSecret::Public => { - Self::Variable::new_input(ns!(ps.cs, "boolean input"), empty::<bool>) + Self::new_input(ns!(ps.cs, "boolean input"), empty::<bool>) } PublicOrSecret::Secret => { - Self::Variable::new_witness(ns!(ps.cs, "boolean witness"), empty::<bool>) + Self::new_witness(ns!(ps.cs, "boolean witness"), empty::<bool>) } }, } @@ -162,16 +179,20 @@ where } } -impl<F> AllocEq<ArkProofSystem<F>> for bool +impl<F> HasAllocation<ArkProofSystem<F>> for bool +where + F: Field, +{ + type Variable = Boolean<F>; + type Mode = ArkAllocationMode; +} + +impl<F> Equal<ArkProofSystem<F>> for Boolean<F> where F: Field, { #[inline] - fn eq( - ps: &mut ArkProofSystem<F>, - lhs: &Var<Self, ArkProofSystem<F>>, - rhs: &Var<Self, ArkProofSystem<F>>, - ) -> Bool<ArkProofSystem<F>> { + fn eq(ps: &mut ArkProofSystem<F>, lhs: &Self, rhs: &Self) -> Boolean<F> { let _ = ps; lhs.is_eq(rhs) .expect("Equality checking is not allowed to fail.") @@ -189,51 +210,41 @@ impl<F> Variable<ArkProofSystem<F>> for FpVar<F> where F: PrimeField, { - type Mode = ArkAllocationMode; type Type = Fp<F>; -} -impl<F> Alloc<ArkProofSystem<F>> for Fp<F> -where - F: PrimeField, -{ type Mode = ArkAllocationMode; - type Variable = FpVar<F>; - #[inline] - fn variable<'t>( - ps: &mut ArkProofSystem<F>, - allocation: impl Into<Allocation<'t, Self, ArkProofSystem<F>>>, - ) -> Self::Variable - where - Self: 't, - { - match allocation.into() { - Allocation::Known(this, mode) => match mode { - ArkAllocationMode::Constant => { - Self::Variable::new_constant(ns!(ps.cs, "prime field constant"), this.0) - } - ArkAllocationMode::Public => { - Self::Variable::new_input(ns!(ps.cs, "prime field input"), full(this.0)) - } - ArkAllocationMode::Secret => { - Self::Variable::new_witness(ns!(ps.cs, "prime field witness"), full(this.0)) - } - }, - Allocation::Unknown(mode) => match mode { - PublicOrSecret::Public => { - Self::Variable::new_input(ns!(ps.cs, "prime field input"), empty::<F>) - } - PublicOrSecret::Secret => { - Self::Variable::new_witness(ns!(ps.cs, "prime field witness"), empty::<F>) - } - }, + fn new(ps: &mut ArkProofSystem<F>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + match allocation { + Allocation::Known(this, ArkAllocationMode::Constant) => { + Self::new_constant(ns!(ps.cs, "prime field constant"), this.0) + } + Allocation::Known(this, ArkAllocationMode::Public) => { + Self::new_input(ns!(ps.cs, "prime field input"), full(this.0)) + } + Allocation::Known(this, ArkAllocationMode::Secret) => { + Self::new_witness(ns!(ps.cs, "prime field witness"), full(this.0)) + } + Allocation::Unknown(PublicOrSecret::Public) => { + Self::new_input(ns!(ps.cs, "prime field input"), empty::<F>) + } + Allocation::Unknown(PublicOrSecret::Secret) => { + Self::new_witness(ns!(ps.cs, "prime field witness"), empty::<F>) + } } .expect("Variable allocation is not allowed to fail.") } } +impl<F> HasAllocation<ArkProofSystem<F>> for Fp<F> +where + F: PrimeField, +{ + type Variable = FpVar<F>; + type Mode = ArkAllocationMode; +} + /// Byte Array Variable #[derive(derivative::Derivative)] #[derivative(Clone, Debug)] @@ -278,32 +289,44 @@ where impl<F, const N: usize> Variable<ArkProofSystem<F>> for ByteArrayVar<F, N> where - F: Field, + F: PrimeField, { - type Mode = ArkAllocationMode; type Type = [u8; N]; -} -impl<F, const N: usize> Alloc<ArkProofSystem<F>> for [u8; N] -where - F: Field, -{ type Mode = ArkAllocationMode; - type Variable = ByteArrayVar<F, N>; - #[inline] - fn variable<'t>( - ps: &mut ArkProofSystem<F>, - allocation: impl Into<Allocation<'t, Self, ArkProofSystem<F>>>, - ) -> Self::Variable - where - Self: 't, - { - todo!() + fn new(ps: &mut ArkProofSystem<F>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + Self( + match allocation { + Allocation::Known(this, ArkAllocationMode::Constant) => { + let _ = this; + todo!() + } + Allocation::Known(this, ArkAllocationMode::Public) => { + UInt8::new_input_vec(ns!(ps.cs, ""), this) + } + Allocation::Known(this, ArkAllocationMode::Secret) => { + UInt8::new_witness_vec(ns!(ps.cs, ""), this) + } + Allocation::Unknown(PublicOrSecret::Public) => todo!(), + Allocation::Unknown(PublicOrSecret::Secret) => { + UInt8::new_witness_vec(ns!(ps.cs, ""), &vec![None; N]) + } + } + .expect("Variable allocation is not allowed to fail."), + ) } } +impl<F, const N: usize> HasAllocation<ArkProofSystem<F>> for [u8; N] +where + F: PrimeField, +{ + type Variable = ByteArrayVar<F, N>; + type Mode = ArkAllocationMode; +} + /// Asset Id Variable #[derive(derivative::Derivative)] #[derivative(Clone, Debug)] @@ -330,43 +353,30 @@ impl<F> Variable<ArkProofSystem<F>> for AssetIdVar<F> where F: PrimeField, { - type Mode = PublicOrSecret; type Type = AssetId; + + type Mode = PublicOrSecret; + + #[inline] + fn new(ps: &mut ArkProofSystem<F>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + Self(allocation.map_allocate(ps, move |this| Fp(F::from(this.0)))) + } } -impl<F> Alloc<ArkProofSystem<F>> for AssetId +impl<F> HasAllocation<ArkProofSystem<F>> for AssetId where F: PrimeField, { - type Mode = PublicOrSecret; - type Variable = AssetIdVar<F>; - - #[inline] - fn variable<'t>( - ps: &mut ArkProofSystem<F>, - allocation: impl Into<Allocation<'t, Self, ArkProofSystem<F>>>, - ) -> Self::Variable - where - Self: 't, - { - AssetIdVar(match allocation.into() { - Allocation::Known(this, mode) => Fp(F::from(this.0)).as_known(ps, mode), - Allocation::Unknown(mode) => Fp::unknown(ps, mode), - }) - } + type Mode = PublicOrSecret; } -impl<F> AllocEq<ArkProofSystem<F>> for AssetId +impl<F> Equal<ArkProofSystem<F>> for AssetIdVar<F> where F: PrimeField, { #[inline] - fn eq( - ps: &mut ArkProofSystem<F>, - lhs: &Var<Self, ArkProofSystem<F>>, - rhs: &Var<Self, ArkProofSystem<F>>, - ) -> Bool<ArkProofSystem<F>> { + fn eq(ps: &mut ArkProofSystem<F>, lhs: &Self, rhs: &Self) -> Boolean<F> { let _ = ps; lhs.0 .is_eq(&rhs.0) @@ -400,55 +410,43 @@ impl<F> Variable<ArkProofSystem<F>> for AssetBalanceVar<F> where F: PrimeField, { - type Mode = PublicOrSecret; type Type = AssetBalance; + + type Mode = PublicOrSecret; + + #[inline] + fn new(ps: &mut ArkProofSystem<F>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + Self(allocation.map_allocate(ps, move |this| Fp(F::from(this.0)))) + } } -impl<F> Alloc<ArkProofSystem<F>> for AssetBalance +impl<F> HasAllocation<ArkProofSystem<F>> for AssetBalance where F: PrimeField, { - type Mode = PublicOrSecret; - type Variable = AssetBalanceVar<F>; - - #[inline] - fn variable<'t>( - ps: &mut ArkProofSystem<F>, - allocation: impl Into<Allocation<'t, Self, ArkProofSystem<F>>>, - ) -> Self::Variable - where - Self: 't, - { - AssetBalanceVar(match allocation.into() { - Allocation::Known(this, mode) => Fp(F::from(this.0)).as_known(ps, mode), - Allocation::Unknown(mode) => Fp::unknown(ps, mode), - }) - } + type Mode = PublicOrSecret; } -impl<F> BooleanSystem for ArkProofSystem<F> +impl<F> Equal<ArkProofSystem<F>> for AssetBalanceVar<F> where - F: Field, + F: PrimeField, { #[inline] - fn assert(&mut self, b: Bool<Self>) { - // FIXME: Is there a more direct way to do assertions? - b.enforce_equal(&Boolean::TRUE) - .expect("This should never fail.") + fn eq(ps: &mut ArkProofSystem<F>, lhs: &Self, rhs: &Self) -> Boolean<F> { + let _ = ps; + lhs.0 + .is_eq(&rhs.0) + .expect("Equality checking is not allowed to fail.") } } -impl<F> ProofSystem for ArkProofSystem<F> +impl<F> AddAssign for AssetBalanceVar<F> where - F: Field, + F: PrimeField, { - type Proof = (); - - type Error = (); - #[inline] - fn finish(self) -> Result<Self::Proof, Self::Error> { - todo!() + fn add_assign(&mut self, rhs: Self) { + self.0 += rhs.0 } } diff --git a/manta-pay/src/crypto/merkle_tree/mod.rs b/manta-pay/src/crypto/merkle_tree/mod.rs index f0282f8ab..428b5a5b8 100644 --- a/manta-pay/src/crypto/merkle_tree/mod.rs +++ b/manta-pay/src/crypto/merkle_tree/mod.rs @@ -51,7 +51,7 @@ use ark_r1cs_std::{ use ark_relations::ns; use core::marker::PhantomData; use manta_crypto::{ - constraint::{Alloc, Allocation, Constant, Public, Secret, Variable}, + constraint::{reflection::HasAllocation, Allocation, Constant, Public, Secret, Variable}, set::{ContainmentProof, VerifiedSet}, }; use manta_util::{as_bytes, Concat}; @@ -223,47 +223,38 @@ where GG: CurveVar<C, ConstraintField<C>>, for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, { - type Mode = Constant; type Type = ParametersWrapper<W, C, GG>; + + type Mode = Constant; + + #[inline] + fn new(ps: &mut ProofSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + let (this, _) = allocation.into_known(); + ParametersVar { + leaf: LeafParamVar::<W, _, _>::new_constant( + ns!(ps.cs, "leaf hash parameter constant"), + &this.0.leaf, + ) + .expect("Variable allocation is not allowed to fail."), + two_to_one: TwoToOneParamVar::<W, _, _>::new_constant( + ns!(ps.cs, "two-to-one hash parameter constant"), + &this.0.two_to_one, + ) + .expect("Variable allocation is not allowed to fail."), + } + } } -impl<W, C, GG> Alloc<ProofSystem<C>> for ParametersWrapper<W, C, GG> +impl<W, C, GG> HasAllocation<ProofSystem<C>> for ParametersWrapper<W, C, GG> where W: PedersenWindow, C: ProjectiveCurve, GG: CurveVar<C, ConstraintField<C>>, for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, { - type Mode = Constant; - type Variable = ParametersVar<W, C, GG>; - #[inline] - fn variable<'t>( - ps: &mut ProofSystem<C>, - allocation: impl Into<Allocation<'t, Self, ProofSystem<C>>>, - ) -> Self::Variable - where - Self: 't, - { - match allocation.into() { - Allocation::Known(this, mode) => ParametersVar { - leaf: LeafParamVar::<W, _, _>::new_constant( - ns!(ps.cs, "leaf hash parameter constant"), - &this.0.leaf, - ) - .expect("Variable allocation is not allowed to fail."), - two_to_one: TwoToOneParamVar::<W, _, _>::new_constant( - ns!(ps.cs, "two-to-one hash parameter constant"), - &this.0.two_to_one, - ) - .expect("Variable allocation is not allowed to fail."), - }, - _ => unreachable!( - "Since we use a constant allocation mode, we always know the variable value." - ), - } - } + type Mode = Constant; } /// Merkle Tree Root Inner Type @@ -304,34 +295,17 @@ where GG: CurveVar<C, ConstraintField<C>>, for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, { - type Mode = Public; type Type = RootWrapper<W, C, GG>; -} -impl<W, C, GG> Alloc<ProofSystem<C>> for RootWrapper<W, C, GG> -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, -{ type Mode = Public; - type Variable = RootVar<W, C, GG>; - #[inline] - fn variable<'t>( - ps: &mut ProofSystem<C>, - allocation: impl Into<Allocation<'t, Self, ProofSystem<C>>>, - ) -> Self::Variable - where - Self: 't, - { + fn new(ps: &mut ProofSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { RootVar( - match allocation.into().known() { - Some(this) => AllocVar::<RootInnerType<W, C>, _>::new_input( + match allocation.known() { + Some((this, _)) => AllocVar::<RootInnerType<W, C>, _>::new_input( ns!(ps.cs, "merkle tree root public input"), - full(((this.0).0).0), + full((this.0).0), ), _ => AllocVar::<RootInnerType<W, C>, _>::new_input( ns!(ps.cs, "merkle tree root public input"), @@ -344,6 +318,17 @@ where } } +impl<W, C, GG> HasAllocation<ProofSystem<C>> for RootWrapper<W, C, GG> +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, +{ + type Variable = RootVar<W, C, GG>; + type Mode = Public; +} + /// Merkle Tree Path Inner Type type PathInnerType<W, C> = ArkPath<Configuration<W, C>>; @@ -384,39 +369,33 @@ where GG: CurveVar<C, ConstraintField<C>>, for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, { - type Mode = Secret; type Type = PathWrapper<W, C, GG>; -} -impl<W, C, GG> Alloc<ProofSystem<C>> for PathWrapper<W, C, GG> -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, -{ type Mode = Secret; - type Variable = PathVar<W, C, GG>; - #[inline] - fn variable<'t>( - ps: &mut ProofSystem<C>, - allocation: impl Into<Allocation<'t, Self, ProofSystem<C>>>, - ) -> Self::Variable - where - Self: 't, - { + fn new(ps: &mut ProofSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { PathVar( - match allocation.into().known() { - Some(this) => PathVarInnerType::new_witness(ns!(ps.cs, ""), full(&((this.0).0).0)), + match allocation.known() { + Some((this, _)) => PathVarInnerType::new_witness(ns!(ps.cs, ""), full(&(this.0).0)), _ => PathVarInnerType::new_witness(ns!(ps.cs, ""), empty::<PathInnerType<W, C>>), } - .expect("Variable allocatino is not allowed to fail."), + .expect("Variable allocation is not allowed to fail."), ) } } +impl<W, C, GG> HasAllocation<ProofSystem<C>> for PathWrapper<W, C, GG> +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, +{ + type Variable = PathVar<W, C, GG>; + type Mode = Secret; +} + /// Merkle Tree #[derive(derivative::Derivative)] #[derivative(Clone(bound = ""))] diff --git a/manta-pay/src/crypto/prf/blake2s.rs b/manta-pay/src/crypto/prf/blake2s.rs index 4be12e8fd..13d8cd0ec 100644 --- a/manta-pay/src/crypto/prf/blake2s.rs +++ b/manta-pay/src/crypto/prf/blake2s.rs @@ -98,15 +98,17 @@ impl PseudorandomFunctionFamily for Blake2s { /// Blake2s PRF Constraint System Implementations pub mod constraint { use super::*; - use crate::crypto::constraint::{ArkProofSystem as ProofSystem, ByteArrayVar}; + use crate::crypto::constraint::{empty, full, ArkProofSystem as ProofSystem, ByteArrayVar}; use ark_crypto_primitives::{ prf::blake2s::constraints::Blake2sGadget as ArkBlake2sVar, PRFGadget, }; use ark_ff::PrimeField; - use ark_r1cs_std::{eq::EqGadget, uint8::UInt8, ToBytesGadget}; + use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget, uint8::UInt8, ToBytesGadget}; + use ark_relations::ns; use core::marker::PhantomData; use manta_crypto::constraint::{ - Alloc, AllocEq, Allocation, Bool, Constant, PublicOrSecret, Secret, Var, Variable, + reflection::HasAllocation, types::Bool, Allocation, Constant, Equal, PublicOrSecret, + Secret, Variable, }; /// Blake2s Pseudorandom Function Family Seed Variable @@ -135,31 +137,22 @@ pub mod constraint { where F: PrimeField, { - type Mode = Secret; type Type = Blake2sSeed; + + type Mode = Secret; + + #[inline] + fn new(ps: &mut ProofSystem<F>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + Self(allocation.map_allocate(ps, move |this| this.0)) + } } - impl<F> Alloc<ProofSystem<F>> for Blake2sSeed + impl<F> HasAllocation<ProofSystem<F>> for Blake2sSeed where F: PrimeField, { - type Mode = Secret; - type Variable = Blake2sSeedVar<F>; - - #[inline] - fn variable<'t>( - ps: &mut ProofSystem<F>, - allocation: impl Into<Allocation<'t, Self, ProofSystem<F>>>, - ) -> Self::Variable - where - Self: 't, - { - match allocation.into().known() { - Some(this) => todo!(), - _ => todo!(), - } - } + type Mode = Secret; } /// Blake2s Pseudorandom Function Family Input Variable @@ -199,31 +192,22 @@ pub mod constraint { where F: PrimeField, { - type Mode = Secret; type Type = Blake2sInput; + + type Mode = Secret; + + #[inline] + fn new(ps: &mut ProofSystem<F>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + Self(allocation.map_allocate(ps, move |this| this.0)) + } } - impl<F> Alloc<ProofSystem<F>> for Blake2sInput + impl<F> HasAllocation<ProofSystem<F>> for Blake2sInput where F: PrimeField, { - type Mode = Secret; - type Variable = Blake2sInputVar<F>; - - #[inline] - fn variable<'t>( - ps: &mut ProofSystem<F>, - allocation: impl Into<Allocation<'t, Self, ProofSystem<F>>>, - ) -> Self::Variable - where - Self: 't, - { - match allocation.into().known() { - Some(this) => todo!(), - _ => todo!(), - } - } + type Mode = Secret; } /// Blake2s Pseudorandom Function Family Output Variable Inner Type @@ -255,55 +239,54 @@ pub mod constraint { where F: PrimeField, { - type Mode = PublicOrSecret; type Type = Blake2sOutput; - } - impl<F> Alloc<ProofSystem<F>> for Blake2sOutput - where - F: PrimeField, - { type Mode = PublicOrSecret; - type Variable = Blake2sOutputVar<F>; - #[inline] - fn variable<'t>( - ps: &mut ProofSystem<F>, - allocation: impl Into<Allocation<'t, Self, ProofSystem<F>>>, - ) -> Self::Variable - where - Self: 't, - { - /* TODO: - Blake2sOutputVar( - match allocation.into() { + fn new(ps: &mut ProofSystem<F>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + Self( + match allocation { Allocation::Known(this, mode) => match mode { - PublicOrSecret::Public => todo!(), - PublicOrSecret::Secret => todo!(), + PublicOrSecret::Public => Blake2sOutputVarInnerType::new_input( + ns!(ps.cs, "blake2s output public input"), + full(this.0), + ), + PublicOrSecret::Secret => Blake2sOutputVarInnerType::new_witness( + ns!(ps.cs, "blake2s output secret witness"), + full(this.0), + ), }, Allocation::Unknown(mode) => match mode { - PublicOrSecret::Public => todo!(), - PublicOrSecret::Secret => todo!(), + PublicOrSecret::Public => Blake2sOutputVarInnerType::new_input( + ns!(ps.cs, "blake2s output public input"), + empty::<<ArkBlake2s as PRF>::Output>, + ), + PublicOrSecret::Secret => Blake2sOutputVarInnerType::new_witness( + ns!(ps.cs, "blake2s output secret witness"), + empty::<<ArkBlake2s as PRF>::Output>, + ), }, } .expect("Variable allocation is not allowed to fail."), ) - */ - todo!() } } - impl<F> AllocEq<ProofSystem<F>> for Blake2sOutput + impl<F> HasAllocation<ProofSystem<F>> for Blake2sOutput + where + F: PrimeField, + { + type Variable = Blake2sOutputVar<F>; + type Mode = PublicOrSecret; + } + + impl<F> Equal<ProofSystem<F>> for Blake2sOutputVar<F> where F: PrimeField, { #[inline] - fn eq( - ps: &mut ProofSystem<F>, - lhs: &Var<Self, ProofSystem<F>>, - rhs: &Var<Self, ProofSystem<F>>, - ) -> Bool<ProofSystem<F>> { + fn eq(ps: &mut ProofSystem<F>, lhs: &Self, rhs: &Self) -> Bool<ProofSystem<F>> { let _ = ps; lhs.0 .is_eq(&rhs.0) @@ -322,28 +305,25 @@ pub mod constraint { where F: PrimeField, { - type Mode = Constant; type Type = Blake2s; - } - impl<F> Alloc<ProofSystem<F>> for Blake2s - where - F: PrimeField, - { type Mode = Constant; - type Variable = Blake2sVar<F>; - #[inline] - fn variable<'t>( - ps: &mut ProofSystem<F>, - allocation: impl Into<Allocation<'t, Self, ProofSystem<F>>>, - ) -> Self::Variable { + fn new(ps: &mut ProofSystem<F>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { let _ = (ps, allocation); Default::default() } } + impl<F> HasAllocation<ProofSystem<F>> for Blake2s + where + F: PrimeField, + { + type Variable = Blake2sVar<F>; + type Mode = Constant; + } + impl<F> PseudorandomFunctionFamily for Blake2sVar<F> where F: PrimeField, From 9ba9e14d7e2234c157e5a232d2d8daee03df2d6b Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 16 Sep 2021 17:37:16 -0400 Subject: [PATCH 036/275] prepare for groth 16 proving system tests --- manta-accounting/src/transfer.rs | 500 +++++++++++------ manta-crypto/src/commitment.rs | 8 +- manta-crypto/src/constraint.rs | 33 +- manta-crypto/src/prf.rs | 9 +- .../src/crypto/constraint/proof_system.rs | 118 ++-- manta-pay/src/crypto/merkle_tree/basic.rs | 522 ++++++++++++++++++ .../src/crypto/merkle_tree/incremental.rs | 422 ++++++++++++++ manta-pay/src/crypto/merkle_tree/mod.rs | 509 +---------------- manta-pay/src/crypto/prf/blake2s.rs | 28 +- manta-pay/src/lib.rs | 2 +- 10 files changed, 1405 insertions(+), 746 deletions(-) create mode 100644 manta-pay/src/crypto/merkle_tree/basic.rs create mode 100644 manta-pay/src/crypto/merkle_tree/incremental.rs diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index abc06937c..16f4e8432 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -17,20 +17,20 @@ //! Transfer Protocols use crate::{ - asset::{sample_asset_balances, Asset, AssetBalance, AssetBalanceVar, AssetBalances, AssetId}, - identity::{ - IdentityConstraintSystemConfiguration, Receiver, ReceiverPost, ReceiverVar, Sender, - SenderPost, SenderVar, Utxo, UtxoVar, VoidNumber, - }, + asset::{sample_asset_balances, Asset, AssetBalance, AssetBalances, AssetId}, + identity::{self, IdentityConstraintSystemConfiguration, Utxo, UtxoVar, VoidNumber}, ledger::{Ledger, PostError}, }; use alloc::vec::Vec; -use core::ops::AddAssign; +use core::{ + convert::{TryFrom, TryInto}, + ops::AddAssign, +}; use manta_crypto::{ constraint::{ reflection::{HasAllocation, HasVariable}, - Constant, ConstraintSystem, Derived, Equal, ProofSystem, Public, PublicOrSecret, Secret, - Variable, VariableSource, + Allocation, Constant, ConstraintSystem, Derived, Equal, ProofSystem, Public, + PublicOrSecret, Secret, Variable, VariableSource, }, ies::{EncryptedMessage, IntegratedEncryptionScheme}, set::{constraint::VerifiedSetVariable, VerifiedSet}, @@ -41,6 +41,17 @@ use rand::{ Rng, RngCore, }; +/// Returns `true` if the transfer with this shape would have no public side. +#[inline] +const fn has_no_public_side< + const SOURCES: usize, + const SENDERS: usize, + const RECEIVERS: usize, + const SINKS: usize, +>() -> bool { + SOURCES == 0 && SINKS == 0 +} + /// Public Transfer Protocol #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct PublicTransfer<const SOURCES: usize, const SINKS: usize> { @@ -58,11 +69,7 @@ pub struct PublicTransfer<const SOURCES: usize, const SINKS: usize> { impl Default for PublicTransfer<0, 0> { #[inline] fn default() -> Self { - Self { - asset_id: None, - sources: [], - sinks: [], - } + Self::new_unchecked(None, [], []) } } @@ -74,14 +81,28 @@ impl<const SOURCES: usize, const SINKS: usize> PublicTransfer<SOURCES, SINKS> { sources: AssetBalances<SOURCES>, sinks: AssetBalances<SINKS>, ) -> Self { - Self { - asset_id: if SOURCES == 0 && SINKS == 0 { + Self::new_unchecked( + if has_no_public_side::<SOURCES, 0, 0, SINKS>() { None } else { Some(asset_id) }, sources, sinks, + ) + } + + /// Builds a new [`PublicTransfer`] without checking if the asset id should be `None`. + #[inline] + const fn new_unchecked( + asset_id: Option<AssetId>, + sources: AssetBalances<SOURCES>, + sinks: AssetBalances<SINKS>, + ) -> Self { + Self { + asset_id, + sources, + sinks, } } @@ -154,32 +175,39 @@ pub trait TransferConfiguration: >; } -/// Secret Sender Type -pub type SecretSender<T> = Sender<T, <T as TransferConfiguration>::UtxoSet>; +/// Sender Type +pub type Sender<T> = identity::Sender<T, <T as TransferConfiguration>::UtxoSet>; + +/// Receiver Type +pub type Receiver<T> = + identity::Receiver<T, <T as TransferConfiguration>::IntegratedEncryptionScheme>; -/// Secret Receiver Type -pub type SecretReceiver<T> = Receiver<T, <T as TransferConfiguration>::IntegratedEncryptionScheme>; +/// Sender Variable Type +pub type SenderVar<T> = identity::SenderVar<T, <T as TransferConfiguration>::UtxoSet>; -/// Secret Sender Variable Type -pub type SecretSenderVar<T> = SenderVar<T, <T as TransferConfiguration>::UtxoSet>; +/// Receiver Type +pub type ReceiverVar<T> = + identity::ReceiverVar<T, <T as TransferConfiguration>::IntegratedEncryptionScheme>; -/// Secret Receiver Type -pub type SecretReceiverVar<T> = - ReceiverVar<T, <T as TransferConfiguration>::IntegratedEncryptionScheme>; +/// Secret Transfer Proof Error Type +pub type ProofError<T> = <<T as TransferConfiguration>::ProofSystem as ProofSystem>::Error; /// Secret Transfer Proof Type pub type Proof<T> = <<T as TransferConfiguration>::ProofSystem as ProofSystem>::Proof; +/// Secret Transfer Verifier Type +pub type Verifier<T> = <<T as TransferConfiguration>::ProofSystem as ProofSystem>::Verifier; + /// Secret Transfer Protocol pub struct SecretTransfer<T, const SENDERS: usize, const RECEIVERS: usize> where T: TransferConfiguration, { - /// Secret Senders - pub senders: [SecretSender<T>; SENDERS], + /// Senders + pub senders: [Sender<T>; SENDERS], - /// Secret Receivers - pub receivers: [SecretReceiver<T>; RECEIVERS], + /// Receivers + pub receivers: [Receiver<T>; RECEIVERS], } impl<T, const SENDERS: usize, const RECEIVERS: usize> SecretTransfer<T, SENDERS, RECEIVERS> @@ -194,10 +222,7 @@ where /// Builds a new [`SecretTransfer`]. #[inline] - pub fn new( - senders: [SecretSender<T>; SENDERS], - receivers: [SecretReceiver<T>; RECEIVERS], - ) -> Self { + pub fn new(senders: [Sender<T>; SENDERS], receivers: [Receiver<T>; RECEIVERS]) -> Self { Self::check_sender_side(); Self::check_receiver_side(); Self::check_size_overflow(); @@ -237,10 +262,7 @@ where /// Builds a new [`SecretTransfer`] without checking the number of senders and receivers. #[inline] - fn new_unchecked( - senders: [SecretSender<T>; SENDERS], - receivers: [SecretReceiver<T>; RECEIVERS], - ) -> Self { + fn new_unchecked(senders: [Sender<T>; SENDERS], receivers: [Receiver<T>; RECEIVERS]) -> Self { Self { senders, receivers } } @@ -286,29 +308,37 @@ where self, commitment_scheme: &T::CommitmentScheme, utxo_set: &T::UtxoSet, - ) -> Option<SecretTransferPost<T, SENDERS, RECEIVERS>> { - Some(SecretTransferPost { - validity_proof: Transfer::<T, 0, SENDERS, RECEIVERS, 0>::generate_validity_proof( - None, - &[], - &self.senders, - &self.receivers, - &[], - commitment_scheme, - utxo_set, - )?, - sender_posts: array_map(self.senders, Sender::into_post), - receiver_posts: array_map(self.receivers, Receiver::into_post), - }) + ) -> Result<SecretTransferPost<T, SENDERS, RECEIVERS>, ProofError<T>> { + match Transfer::from(self) + .into_post(commitment_scheme, utxo_set)? + .try_into() + { + Ok(post) => Ok(post), + _ => unreachable!("We convert there and back so we know that the proof exists."), + } + } +} + +impl<T, const SENDERS: usize, const RECEIVERS: usize> From<SecretTransfer<T, SENDERS, RECEIVERS>> + for Transfer<T, 0, SENDERS, RECEIVERS, 0> +where + T: TransferConfiguration, +{ + #[inline] + fn from(transfer: SecretTransfer<T, SENDERS, RECEIVERS>) -> Self { + Self { + public: Default::default(), + secret: transfer, + } } } -/// Secret Sender Post Type -pub type SecretSenderPost<T> = SenderPost<T, <T as TransferConfiguration>::UtxoSet>; +/// Sender Post Type +pub type SenderPost<T> = identity::SenderPost<T, <T as TransferConfiguration>::UtxoSet>; -/// Secret Receiver Post Type -pub type SecretReceiverPost<T> = - ReceiverPost<T, <T as TransferConfiguration>::IntegratedEncryptionScheme>; +/// Receiver Post Type +pub type ReceiverPost<T> = + identity::ReceiverPost<T, <T as TransferConfiguration>::IntegratedEncryptionScheme>; /// Secret Transfer Post pub struct SecretTransferPost<T, const SENDERS: usize, const RECEIVERS: usize> @@ -316,10 +346,10 @@ where T: TransferConfiguration, { /// Sender Posts - pub sender_posts: [SecretSenderPost<T>; SENDERS], + pub sender_posts: [SenderPost<T>; SENDERS], /// Receiver Posts - pub receiver_posts: [SecretReceiverPost<T>; RECEIVERS], + pub receiver_posts: [ReceiverPost<T>; RECEIVERS], /// Validity Proof pub validity_proof: Proof<T>, @@ -341,7 +371,7 @@ where ProofSystem = T::ProofSystem, > + ?Sized, { - TransferPost::<T, 0, SENDERS, RECEIVERS, 0>::from(self).post(ledger) + TransferPost::from(self).post(ledger) } } @@ -352,15 +382,31 @@ where { #[inline] fn from(post: SecretTransferPost<T, SENDERS, RECEIVERS>) -> Self { - TransferPost { - public_transfer: Default::default(), - secret_sender_posts: post.sender_posts, - secret_receiver_posts: post.receiver_posts, + Self { + sender_posts: post.sender_posts, + receiver_posts: post.receiver_posts, validity_proof: Some(post.validity_proof), } } } +impl<T, const SENDERS: usize, const RECEIVERS: usize> + TryFrom<TransferPost<T, 0, SENDERS, RECEIVERS, 0>> for SecretTransferPost<T, SENDERS, RECEIVERS> +where + T: TransferConfiguration, +{ + type Error = (); + + #[inline] + fn try_from(post: TransferPost<T, 0, SENDERS, RECEIVERS, 0>) -> Result<Self, Self::Error> { + Ok(Self { + sender_posts: post.sender_posts, + receiver_posts: post.receiver_posts, + validity_proof: post.validity_proof.ok_or(())?, + }) + } +} + /// Transfer Protocol pub struct Transfer< T, @@ -388,8 +434,8 @@ where pub fn new( asset_id: AssetId, sources: AssetBalances<SOURCES>, - senders: [SecretSender<T>; SENDERS], - receivers: [SecretReceiver<T>; RECEIVERS], + senders: [Sender<T>; SENDERS], + receivers: [Receiver<T>; RECEIVERS], sinks: AssetBalances<SINKS>, ) -> Self { Self::check_sender_side(); @@ -420,8 +466,8 @@ where fn new_unchecked( asset_id: AssetId, sources: AssetBalances<SOURCES>, - senders: [SecretSender<T>; SENDERS], - receivers: [SecretReceiver<T>; RECEIVERS], + senders: [Sender<T>; SENDERS], + receivers: [Receiver<T>; RECEIVERS], sinks: AssetBalances<SINKS>, ) -> Self { Self { @@ -470,43 +516,91 @@ where self.source_sum() + self.sender_sum() == self.receiver_sum() + self.sink_sum() } - /// Builds constraints for transfer validity proof. - #[allow(clippy::too_many_arguments)] // NOTE: We don't want to make a new `struct` for this. + /// Generates the unknown variables for the validity proof. + #[inline] + fn unknown_variables( + commitment_scheme: &T::CommitmentScheme, + utxo_set: &T::UtxoSet, + ps: &mut T::ProofSystem, + ) -> ( + Option<T::AssetIdVar>, + TransferParticipantsVar<T, SOURCES, SENDERS, RECEIVERS, SINKS>, + T::CommitmentSchemeVar, + T::UtxoSetVar, + ) { + let base_asset_id = if has_no_public_side::<SOURCES, SENDERS, RECEIVERS, SINKS>() { + None + } else { + Some(()) + }; + ( + base_asset_id.map(|_| T::AssetIdVar::new_unknown(ps, Public)), + TransferParticipantsVar::new_unknown(ps, Derived), + commitment_scheme.as_known(ps, Public), + utxo_set.as_known(ps, Public), + ) + } + + /// Generates the known variables for the validity proof. #[inline] - fn verify<Sources, Senders, Receivers, Sinks>( + fn known_variables( + &self, + commitment_scheme: &T::CommitmentScheme, + utxo_set: &T::UtxoSet, ps: &mut T::ProofSystem, - commitment_scheme: &T::CommitmentSchemeVar, - utxo_set: &T::UtxoSetVar, + ) -> ( + Option<T::AssetIdVar>, + TransferParticipantsVar<T, SOURCES, SENDERS, RECEIVERS, SINKS>, + T::CommitmentSchemeVar, + T::UtxoSetVar, + ) { + ( + self.public.asset_id.map(|id| id.as_known(ps, Public)), + TransferParticipantsVar::new_known(ps, self, Derived), + commitment_scheme.as_known(ps, Public), + utxo_set.as_known(ps, Public), + ) + } + + /// Builds constraints for transfer validity proof/verifier. + #[inline] + fn build_constraints( base_asset_id: Option<T::AssetIdVar>, - sources: Sources, - senders: Senders, - receivers: Receivers, - sinks: Sinks, - ) where - Sources: Iterator<Item = AssetBalanceVar<T::ProofSystem>>, - Senders: Iterator<Item = SecretSenderVar<T>>, - Receivers: Iterator<Item = SecretReceiverVar<T>>, - Sinks: Iterator<Item = AssetBalanceVar<T::ProofSystem>>, - { + participants: TransferParticipantsVar<T, SOURCES, SENDERS, RECEIVERS, SINKS>, + commitment_scheme: T::CommitmentSchemeVar, + utxo_set: T::UtxoSetVar, + ps: &mut T::ProofSystem, + ) { let mut sender_sum = T::AssetBalanceVar::from_default(ps, Secret); let mut receiver_sum = T::AssetBalanceVar::from_default(ps, Secret); - sources.for_each(|source| sender_sum += source); - sinks.for_each(|sink| receiver_sum += sink); + participants + .sources + .into_iter() + .for_each(|source| sender_sum += source); + + participants + .sinks + .into_iter() + .for_each(|sink| receiver_sum += sink); #[allow(clippy::needless_collect)] // NOTE: `ps` is being mutated, we need to collect. - let secret_asset_ids = mixed_chain(senders, receivers, |c| match c { - Either::Left(sender) => { - let asset = sender.get_well_formed_asset(ps, commitment_scheme, utxo_set); - sender_sum += asset.value; - asset.id - } - Either::Right(receiver) => { - let asset = receiver.get_well_formed_asset(ps, commitment_scheme); - receiver_sum += asset.value; - asset.id - } - }) + let secret_asset_ids = mixed_chain( + participants.senders.into_iter(), + participants.receivers.into_iter(), + |c| match c { + Either::Left(sender) => { + let asset = sender.get_well_formed_asset(ps, &commitment_scheme, &utxo_set); + sender_sum += asset.value; + asset.id + } + Either::Right(receiver) => { + let asset = receiver.get_well_formed_asset(ps, &commitment_scheme); + receiver_sum += asset.value; + asset.id + } + }, + ) .collect::<Vec<_>>(); match base_asset_id { @@ -517,62 +611,50 @@ where ps.assert_eq(&sender_sum, &receiver_sum); } - /// Generates a validity proof for this transfer. + /// Generates a verifier for this transfer shape. + /// + /// Given a transfer object of the same shape, if the same `commitment_scheme` and `utxo_set` + /// are passed in to [`generate_proof`](Self::generate_proof) and proof is successfully + /// generated, then the verifier returned by this function will be able to verify that proof. #[inline] - fn generate_validity_proof( - base_asset_id: Option<AssetId>, - sources: &AssetBalances<SOURCES>, - senders: &[SecretSender<T>; SENDERS], - receivers: &[SecretReceiver<T>; RECEIVERS], - sinks: &AssetBalances<SINKS>, + pub fn generate_verifier( commitment_scheme: &T::CommitmentScheme, utxo_set: &T::UtxoSet, - ) -> Option<Proof<T>> { - // FIXME: Find a better way to allocate variables without so much hassle. - - let mut ps = <T::ProofSystem as Default>::default(); - - let base_asset_id = base_asset_id.map(|id| id.as_known(&mut ps, Public)); - - #[allow(clippy::needless_collect)] // NOTE: `ps` is being mutated, we need to collect. - let sources = sources - .iter() - .map(|source| source.as_known(&mut ps, Public)) - .collect::<Vec<_>>(); - - #[allow(clippy::needless_collect)] // NOTE: `ps` is being mutated, we need to collect. - let senders = senders - .iter() - .map(|sender| sender.known(&mut ps, Derived)) - .collect::<Vec<_>>(); - - #[allow(clippy::needless_collect)] // NOTE: `ps` is being mutated, we need to collect. - let receivers = receivers - .iter() - .map(|receiver| receiver.known(&mut ps, Derived)) - .collect::<Vec<_>>(); - - #[allow(clippy::needless_collect)] // NOTE: `ps` is being mutated, we need to collect. - let sinks = sinks - .iter() - .map(|sink| sink.as_known(&mut ps, Public)) - .collect::<Vec<_>>(); - - let commitment_scheme = commitment_scheme.as_known(&mut ps, Public); - let utxo_set = utxo_set.as_known(&mut ps, Public); - - Self::verify( - &mut ps, - &commitment_scheme, - &utxo_set, + ) -> Result<Verifier<T>, ProofError<T>> { + let mut ps = T::ProofSystem::setup_to_verify(); + let (base_asset_id, participants, commitment_scheme, utxo_set) = + Self::unknown_variables(commitment_scheme, utxo_set, &mut ps); + Self::build_constraints( base_asset_id, - sources.into_iter(), - senders.into_iter(), - receivers.into_iter(), - sinks.into_iter(), + participants, + commitment_scheme, + utxo_set, + &mut ps, ); + ps.into_verifier() + } - ps.finish().ok() + /// Generates a validity proof for this transfer. + /// + /// To verify this proof, run [`generate_verifier`](Self::generate_verifier) with the same + /// `commitment_scheme` and `utxo_set` and use the returned verifier. + #[inline] + pub fn generate_proof( + &self, + commitment_scheme: &T::CommitmentScheme, + utxo_set: &T::UtxoSet, + ) -> Result<Proof<T>, ProofError<T>> { + let mut ps = T::ProofSystem::setup_to_prove(); + let (base_asset_id, participants, commitment_scheme, utxo_set) = + self.known_variables(commitment_scheme, utxo_set, &mut ps); + Self::build_constraints( + base_asset_id, + participants, + commitment_scheme, + utxo_set, + &mut ps, + ); + ps.into_proof() } /// Converts `self` into its ledger post. @@ -581,28 +663,102 @@ where self, commitment_scheme: &T::CommitmentScheme, utxo_set: &T::UtxoSet, - ) -> Option<TransferPost<T, SOURCES, SENDERS, RECEIVERS, SINKS>> { - Some(TransferPost { + ) -> Result<TransferPost<T, SOURCES, SENDERS, RECEIVERS, SINKS>, ProofError<T>> { + Ok(TransferPost { validity_proof: if SENDERS == 0 { None } else { - Some(Self::generate_validity_proof( - self.public.asset_id, - &self.public.sources, - &self.secret.senders, - &self.secret.receivers, - &self.public.sinks, - commitment_scheme, - utxo_set, - )?) + Some(self.generate_proof(commitment_scheme, utxo_set)?) }, - public_transfer: self.public, - secret_sender_posts: array_map(self.secret.senders, Sender::into_post), - secret_receiver_posts: array_map(self.secret.receivers, Receiver::into_post), + sender_posts: array_map(self.secret.senders, Sender::into_post), + receiver_posts: array_map(self.secret.receivers, Receiver::into_post), }) } } +/// Transfer Participants Variable +struct TransferParticipantsVar< + T, + const SOURCES: usize, + const SENDERS: usize, + const RECEIVERS: usize, + const SINKS: usize, +> where + T: TransferConfiguration, +{ + /// Source Variables + sources: Vec<T::AssetBalanceVar>, + + /// Sender Variables + senders: Vec<SenderVar<T>>, + + /// Receiver Variables + receivers: Vec<ReceiverVar<T>>, + + /// Sink Variables + sinks: Vec<T::AssetBalanceVar>, +} + +impl<T, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> + Variable<T::ProofSystem> for TransferParticipantsVar<T, SOURCES, SENDERS, RECEIVERS, SINKS> +where + T: TransferConfiguration, +{ + type Type = Transfer<T, SOURCES, SENDERS, RECEIVERS, SINKS>; + + type Mode = Derived; + + #[inline] + fn new(ps: &mut T::ProofSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + match allocation { + Allocation::Known(this, mode) => Self { + sources: this + .public + .sources + .iter() + .map(|source| source.as_known(ps, Public)) + .collect(), + senders: this + .secret + .senders + .iter() + .map(|sender| sender.known(ps, mode)) + .collect(), + receivers: this + .secret + .receivers + .iter() + .map(|receiver| receiver.known(ps, mode)) + .collect(), + sinks: this + .public + .sinks + .iter() + .map(|sink| sink.as_known(ps, Public)) + .collect(), + }, + Allocation::Unknown(mode) => Self { + sources: (0..SOURCES) + .into_iter() + .map(|_| T::AssetBalanceVar::new_unknown(ps, Public)) + .collect(), + senders: (0..SENDERS) + .into_iter() + .map(|_| SenderVar::new_unknown(ps, mode)) + .collect(), + receivers: (0..RECEIVERS) + .into_iter() + .map(|_| ReceiverVar::new_unknown(ps, mode)) + .collect(), + sinks: (0..SINKS) + .into_iter() + .map(|_| T::AssetBalanceVar::new_unknown(ps, Public)) + .collect(), + }, + } + } +} + /// Transfer Post pub struct TransferPost< T, @@ -613,17 +769,14 @@ pub struct TransferPost< > where T: TransferConfiguration, { - /// Public Transfer - public_transfer: PublicTransfer<SOURCES, SINKS>, - - /// Secret Sender Posts - secret_sender_posts: [SecretSenderPost<T>; SENDERS], + /// Sender Posts + pub sender_posts: [SenderPost<T>; SENDERS], - /// Secret Receiver Posts - secret_receiver_posts: [SecretReceiverPost<T>; RECEIVERS], + /// Receiver Posts + pub receiver_posts: [ReceiverPost<T>; RECEIVERS], /// Validity Proof - validity_proof: Option<Proof<T>>, + pub validity_proof: Option<Proof<T>>, } impl<T, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> @@ -643,19 +796,10 @@ where ProofSystem = T::ProofSystem, > + ?Sized, { - // FIXME: Does the public transfer component need to be validated? - // - // > Probably not. The public part of a transfer comes from the same place that the - // ledger is stored so the ledger can check whether the balances come from accounts - // which have the right amount of assets to spend. Eventually, we either inherit that - // logic from another library or we implement it here in `manta-rs`. - // - let _ = self.public_transfer; - - for sender_post in IntoIterator::into_iter(self.secret_sender_posts) { + for sender_post in IntoIterator::into_iter(self.sender_posts) { sender_post.post(ledger)?; } - for receiver_post in IntoIterator::into_iter(self.secret_receiver_posts) { + for receiver_post in IntoIterator::into_iter(self.receiver_posts) { receiver_post.post(ledger)?; } if let Some(proof) = self.validity_proof { diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index 603c2fdfb..b74474225 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -45,7 +45,10 @@ pub trait CommitmentScheme { } /// Commitment Input -pub trait Input<T>: CommitmentScheme { +pub trait Input<T>: CommitmentScheme +where + T: ?Sized, +{ /// Extends the input buffer with `input`. fn extend(buffer: &mut Self::InputBuffer, input: &T); } @@ -54,7 +57,7 @@ impl<C, I> Input<I> for C where C: CommitmentScheme + ?Sized, C::InputBuffer: ConcatAccumulator<I::Item>, - I: Concat, + I: Concat + ?Sized, { #[inline] fn extend(buffer: &mut C::InputBuffer, input: &I) { @@ -91,6 +94,7 @@ where #[inline] pub fn update<T>(mut self, input: &T) -> Self where + T: ?Sized, C: Input<T>, { C::extend(&mut self.input_buffer, input); diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index b4f91f624..f18892b6e 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -16,10 +16,12 @@ //! Constraint Proof Systems -// TODO: Add derive macros to all the enums/structs here. -// TODO: Add derive trait to implement `HasAllocation` for structs (and enums?). -// TODO: Add more convenience functions for allocating unknown variables. -// TODO: How to do verification systems? Should it be a separate trait or part of `ProofSystem`? +// TODO: Add derive macros to all the enums/structs here. +// TODO: Add derive trait to implement `HasAllocation` for structs (and enums?). +// TODO: Add more convenience functions for allocating unknown variables. +// FIXME: Leverage the type system to constraint allocation to only unknown modes for verifier +// generation and only known modes for proof generation, instead of relying on the `setup_*` +// methods to "do the right thing". use core::{ convert::{Infallible, TryFrom}, @@ -460,15 +462,27 @@ where } /// Proof System -pub trait ProofSystem: ConstraintSystem + Default { +pub trait ProofSystem: ConstraintSystem { + /// Verifier Type + type Verifier: Verifier<Self>; + /// Proof Type type Proof; /// Error Type type Error; - /// Returns a proof that the boolean system is consistent. - fn finish(self) -> Result<Self::Proof, Self::Error>; + /// Returns a proof system which is setup to build a verifier. + fn setup_to_verify() -> Self; + + /// Returns a proof system which is setup to build a proof. + fn setup_to_prove() -> Self; + + /// Returns a verifier object for the constraints contained in `self`. + fn into_verifier(self) -> Result<Self::Verifier, Self::Error>; + + /// Returns a proof that the constraint system `self` is consistent. + fn into_proof(self) -> Result<Self::Proof, Self::Error>; } /// Proof System Verifier @@ -476,8 +490,11 @@ pub trait Verifier<P> where P: ProofSystem + ?Sized, { + /// Error Type + type Error; + /// Verifies that a proof generated from a proof system is valid. - fn verify(proof: &P::Proof) -> bool; + fn verify(&self, proof: &P::Proof) -> Result<bool, Self::Error>; } /// Derived Allocation Mode diff --git a/manta-crypto/src/prf.rs b/manta-crypto/src/prf.rs index aa82b5e4a..92d4a72fd 100644 --- a/manta-crypto/src/prf.rs +++ b/manta-crypto/src/prf.rs @@ -22,7 +22,7 @@ pub trait PseudorandomFunctionFamily { type Seed: ?Sized; /// PRF Input Type - type Input: Default; + type Input; /// PRF Output Type type Output; @@ -30,9 +30,6 @@ pub trait PseudorandomFunctionFamily { /// Evaluates the PRF at the `seed` and `input`. fn evaluate(seed: &Self::Seed, input: &Self::Input) -> Self::Output; - /// Evaluates the PRF at the `seed` with the default input. - #[inline] - fn evaluate_zero(seed: &Self::Seed) -> Self::Output { - Self::evaluate(seed, &Self::Input::default()) - } + /// Evaluates the PRF at the `seed` with input set to a zero value. + fn evaluate_zero(seed: &Self::Seed) -> Self::Output; } diff --git a/manta-pay/src/crypto/constraint/proof_system.rs b/manta-pay/src/crypto/constraint/proof_system.rs index 3dbcd20c9..6430821ca 100644 --- a/manta-pay/src/crypto/constraint/proof_system.rs +++ b/manta-pay/src/crypto/constraint/proof_system.rs @@ -20,7 +20,7 @@ use alloc::{vec, vec::Vec}; use ark_ff::{fields::Field, PrimeField}; use ark_r1cs_std::{ alloc::AllocVar, bits::boolean::Boolean, eq::EqGadget, fields::fp::FpVar, uint8::UInt8, - ToBytesGadget, + R1CSVar, ToBytesGadget, }; use ark_relations::{ ns, @@ -33,7 +33,7 @@ use core::{borrow::Borrow, ops::AddAssign}; use manta_accounting::{AssetBalance, AssetId}; use manta_crypto::constraint::{ reflection::HasAllocation, types::Bool, Allocation, AllocationMode, ConstraintSystem, Equal, - ProofSystem, Public, PublicOrSecret, Secret, Variable, + ProofSystem, Public, PublicOrSecret, Secret, Variable, Verifier, }; use manta_util::{Concat, ConcatAccumulator}; @@ -105,18 +105,6 @@ where pub(crate) cs: ArkConstraintSystemRef<F>, } -impl<F> Default for ArkProofSystem<F> -where - F: Field, -{ - #[inline] - fn default() -> Self { - Self { - cs: ArkConstraintSystem::new_ref(), - } - } -} - impl<F> ConstraintSystem for ArkProofSystem<F> where F: Field, @@ -135,15 +123,51 @@ impl<F> ProofSystem for ArkProofSystem<F> where F: Field, { + type Verifier = Groth16Verifier; + type Proof = (); type Error = (); #[inline] - fn finish(self) -> Result<Self::Proof, Self::Error> { + fn setup_to_verify() -> Self { + todo!() + } + + #[inline] + fn setup_to_prove() -> Self { + todo!() + } + + #[inline] + fn into_verifier(self) -> Result<Self::Verifier, Self::Error> { + todo!() + } + + #[inline] + fn into_proof(self) -> Result<Self::Proof, Self::Error> { + todo!() + } +} + +/// Arkworks Groth 16 Verifier +pub struct Groth16Verifier; + +impl<F> Verifier<ArkProofSystem<F>> for Groth16Verifier +where + F: Field, +{ + type Error = (); + + #[inline] + fn verify( + &self, + proof: &<ArkProofSystem<F> as ProofSystem>::Proof, + ) -> Result<bool, Self::Error> { todo!() } } + impl<F> Variable<ArkProofSystem<F>> for Boolean<F> where F: Field, @@ -252,6 +276,46 @@ pub struct ByteArrayVar<F, const N: usize>(Vec<UInt8<F>>) where F: Field; +impl<F, const N: usize> ByteArrayVar<F, N> +where + F: Field, +{ + /// Returns an reference to the internal arkworks constriant system. + #[inline] + pub(crate) fn constraint_system_ref(&self) -> ArkConstraintSystemRef<F> { + self.0.cs() + } + + /// Allocates a new byte vector according to the `allocation` entry. + #[inline] + pub(crate) fn allocate( + cs: &ArkConstraintSystemRef<F>, + allocation: Allocation<[u8; N], PublicOrSecret>, + ) -> Self + where + F: PrimeField, + { + Self( + match allocation { + Allocation::Known(this, PublicOrSecret::Public) => { + UInt8::new_input_vec(ns!(cs, "byte array public input"), this) + } + Allocation::Known(this, PublicOrSecret::Secret) => { + UInt8::new_witness_vec(ns!(cs, "byte array secret witness"), this) + } + Allocation::Unknown(PublicOrSecret::Public) => { + // FIXME: What goes here? + todo!() + } + Allocation::Unknown(PublicOrSecret::Secret) => { + UInt8::new_witness_vec(ns!(cs, "byte array secret witness"), &vec![None; N]) + } + } + .expect("Variable allocation is not allowed to fail."), + ) + } +} + impl<F, const N: usize> AsRef<[UInt8<F>]> for ByteArrayVar<F, N> where F: Field, @@ -293,29 +357,11 @@ where { type Type = [u8; N]; - type Mode = ArkAllocationMode; + type Mode = PublicOrSecret; #[inline] fn new(ps: &mut ArkProofSystem<F>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - Self( - match allocation { - Allocation::Known(this, ArkAllocationMode::Constant) => { - let _ = this; - todo!() - } - Allocation::Known(this, ArkAllocationMode::Public) => { - UInt8::new_input_vec(ns!(ps.cs, ""), this) - } - Allocation::Known(this, ArkAllocationMode::Secret) => { - UInt8::new_witness_vec(ns!(ps.cs, ""), this) - } - Allocation::Unknown(PublicOrSecret::Public) => todo!(), - Allocation::Unknown(PublicOrSecret::Secret) => { - UInt8::new_witness_vec(ns!(ps.cs, ""), &vec![None; N]) - } - } - .expect("Variable allocation is not allowed to fail."), - ) + Self::allocate(&ps.cs, allocation) } } @@ -324,7 +370,7 @@ where F: PrimeField, { type Variable = ByteArrayVar<F, N>; - type Mode = ArkAllocationMode; + type Mode = PublicOrSecret; } /// Asset Id Variable diff --git a/manta-pay/src/crypto/merkle_tree/basic.rs b/manta-pay/src/crypto/merkle_tree/basic.rs new file mode 100644 index 000000000..389b07ecf --- /dev/null +++ b/manta-pay/src/crypto/merkle_tree/basic.rs @@ -0,0 +1,522 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Basic Merkle Tree Implementation + +// NOTE: Most if not all of the fallible interfaces in this file never actually fail. We use +// faillible interfaces so that we don't have to depend explicitly on implementation +// details of the `arkworks` project. + +// TODO: We use the Pedersen commitment settings for `CRH` and `TwoToOneCRH`. We should write our +// own `CRH` and `TwoToOneCRH` traits and then in the configuration we align them with the +// Pedersen settings. + +use crate::crypto::{ + commitment::pedersen::{PedersenWindow, ProjectiveCurve}, + constraint::{empty, full, ArkProofSystem}, +}; +use alloc::vec::Vec; +use ark_crypto_primitives::{ + crh::{ + constraints::{CRHGadget as CRHGadgetTrait, TwoToOneCRHGadget as TwoToOneCRHGadgetTrait}, + pedersen::{constraints::CRHGadget, CRH}, + }, + merkle_tree::{ + constraints::PathVar as ArkPathVar, Config, LeafParam as ArkLeafParam, + MerkleTree as ArkMerkleTree, Path as ArkPath, TwoToOneDigest, + TwoToOneParam as ArkTwoToOneParam, + }, +}; +use ark_ff::Field; +use ark_r1cs_std::{ + alloc::AllocVar, + boolean::Boolean, + eq::EqGadget, + groups::{CurveVar, GroupOpsBounds}, + uint8::UInt8, +}; +use ark_relations::ns; +use core::marker::PhantomData; +use manta_crypto::{ + constraint::{reflection::HasAllocation, Allocation, Constant, Public, Secret, Variable}, + set::{ContainmentProof, VerifiedSet}, +}; +use manta_util::{as_bytes, Concat}; + +/// Constraint Field Type +pub type ConstraintField<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField; + +/// Proof System +type ProofSystem<C> = ArkProofSystem<ConstraintField<C>>; + +/// Merkle Tree Configuration +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Configuration<W, C>(PhantomData<(W, C)>) +where + W: PedersenWindow, + C: ProjectiveCurve; + +impl<W, C> Config for Configuration<W, C> +where + W: PedersenWindow, + C: ProjectiveCurve, +{ + type LeafHash = CRH<C, W>; + type TwoToOneHash = CRH<C, W>; +} + +/// Leaf Hash Parameters Type +type LeafParam<W, C> = ArkLeafParam<Configuration<W, C>>; + +/// Two-to-One Hash Parameters Type +type TwoToOneParam<W, C> = ArkTwoToOneParam<Configuration<W, C>>; + +/// Leaf Hash Parameters Variable +type LeafParamVar<W, C, GG> = <CRHGadget<C, GG, W> as CRHGadgetTrait< + <Configuration<W, C> as Config>::LeafHash, + ConstraintField<C>, +>>::ParametersVar; + +/// Two-to-One Hash Parameters Variable +type TwoToOneParamVar<W, C, GG> = <CRHGadget<C, GG, W> as TwoToOneCRHGadgetTrait< + <Configuration<W, C> as Config>::TwoToOneHash, + ConstraintField<C>, +>>::ParametersVar; + +/// Merkle Tree Parameters +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""))] +pub struct Parameters<W, C> +where + W: PedersenWindow, + C: ProjectiveCurve, +{ + /// Leaf Hash Parameters + leaf: LeafParam<W, C>, + + /// Two-to-One Hash Parameters + two_to_one: TwoToOneParam<W, C>, +} + +impl<W, C> Parameters<W, C> +where + W: PedersenWindow, + C: ProjectiveCurve, +{ + /// Verifies that `path` constitutes a proof that `item` is contained in the Merkle Tree + /// with the given `root`. + #[inline] + pub fn verify<T>(&self, root: &Root<W, C>, path: &Path<W, C>, item: &T) -> bool + where + T: Concat<Item = u8>, + { + path.0 + .verify(&self.leaf, &self.two_to_one, &root.0, &as_bytes!(item)) + .expect("As of arkworks 0.3.0, this never fails.") + } +} + +/// Merkle Tree Parameters Wrapper +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""))] +pub struct ParametersWrapper<W, C, GG>(Parameters<W, C>, PhantomData<GG>) +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; + +impl<W, C, GG> ParametersWrapper<W, C, GG> +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, +{ + /// Verifies that `path` constitutes a proof that `item` is contained in the Merkle Tree + /// with the given `root`. + #[inline] + pub fn verify<T>( + &self, + root: &RootWrapper<W, C, GG>, + path: &PathWrapper<W, C, GG>, + item: &T, + ) -> bool + where + T: Concat<Item = u8>, + { + self.0.verify(&root.0, &path.0, item) + } +} + +/// Merkle Tree Parameters Variable +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""))] +pub struct ParametersVar<W, C, GG> +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, +{ + /// Leaf Hash Parameters Variable + leaf: LeafParamVar<W, C, GG>, + + /// Two-to-One Hash Parameters Variable + two_to_one: TwoToOneParamVar<W, C, GG>, +} + +impl<W, C, GG> ParametersVar<W, C, GG> +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, +{ + /// Verifies that `path` constitutes a proof that `item` is contained in the Merkle Tree + /// with the given `root`. + #[inline] + pub fn verify( + &self, + root: &RootVar<W, C, GG>, + path: &PathVar<W, C, GG>, + item: &[UInt8<ConstraintField<C>>], + ) -> Boolean<ConstraintField<C>> { + path.0 + .verify_membership(&self.leaf, &self.two_to_one, &root.0, &item) + .expect("This is not allowed to fail.") + } + + /// Asserts that `path` constitutes a proof that `item` is contained in the Merkle Tree + /// with the given `root`. + #[inline] + pub fn assert_verified( + &self, + root: &RootVar<W, C, GG>, + path: &PathVar<W, C, GG>, + item: &[UInt8<ConstraintField<C>>], + ) { + self.verify(root, path, item) + .enforce_equal(&Boolean::TRUE) + .expect("This is not allowed to fail.") + } +} + +impl<W, C, GG> Variable<ProofSystem<C>> for ParametersVar<W, C, GG> +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, +{ + type Type = ParametersWrapper<W, C, GG>; + + type Mode = Constant; + + #[inline] + fn new(ps: &mut ProofSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + let (this, _) = allocation.into_known(); + ParametersVar { + leaf: LeafParamVar::<W, _, _>::new_constant( + ns!(ps.cs, "leaf hash parameter constant"), + &this.0.leaf, + ) + .expect("Variable allocation is not allowed to fail."), + two_to_one: TwoToOneParamVar::<W, _, _>::new_constant( + ns!(ps.cs, "two-to-one hash parameter constant"), + &this.0.two_to_one, + ) + .expect("Variable allocation is not allowed to fail."), + } + } +} + +impl<W, C, GG> HasAllocation<ProofSystem<C>> for ParametersWrapper<W, C, GG> +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, +{ + type Variable = ParametersVar<W, C, GG>; + + type Mode = Constant; +} + +/// Merkle Tree Root Inner Type +type RootInnerType<W, C> = TwoToOneDigest<Configuration<W, C>>; + +/// Merkle Tree Root +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +pub struct Root<W, C>(RootInnerType<W, C>) +where + W: PedersenWindow, + C: ProjectiveCurve; + +/// Merkle Tree Root Wrapper +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +pub struct RootWrapper<W, C, GG>(Root<W, C>, PhantomData<GG>) +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; + +/// Merkle Tree Root Variable +#[derive(derivative::Derivative)] +#[derivative(Clone)] +pub struct RootVar<W, C, GG>(GG, PhantomData<(W, C)>) +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; + +impl<W, C, GG> Variable<ProofSystem<C>> for RootVar<W, C, GG> +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, +{ + type Type = RootWrapper<W, C, GG>; + + type Mode = Public; + + #[inline] + fn new(ps: &mut ProofSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + RootVar( + match allocation.known() { + Some((this, _)) => AllocVar::<RootInnerType<W, C>, _>::new_input( + ns!(ps.cs, "merkle tree root public input"), + full((this.0).0), + ), + _ => AllocVar::<RootInnerType<W, C>, _>::new_input( + ns!(ps.cs, "merkle tree root public input"), + empty::<RootInnerType<W, C>>, + ), + } + .expect("Variable allocation is not allowed to fail."), + PhantomData, + ) + } +} + +impl<W, C, GG> HasAllocation<ProofSystem<C>> for RootWrapper<W, C, GG> +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, +{ + type Variable = RootVar<W, C, GG>; + type Mode = Public; +} + +/// Merkle Tree Path Inner Type +type PathInnerType<W, C> = ArkPath<Configuration<W, C>>; + +/// Merkle Tree Path +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""))] +pub struct Path<W, C>(PathInnerType<W, C>) +where + W: PedersenWindow, + C: ProjectiveCurve; + +/// Merkle Tree Path Wrapper +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""))] +pub struct PathWrapper<W, C, GG>(Path<W, C>, PhantomData<GG>) +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; + +/// Merkle Tree Path Variable Inner Type +type PathVarInnerType<W, C, GG> = + ArkPathVar<Configuration<W, C>, CRHGadget<C, GG, W>, CRHGadget<C, GG, W>, ConstraintField<C>>; + +/// Merkle Tree Path Variable +pub struct PathVar<W, C, GG>(PathVarInnerType<W, C, GG>) +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; + +impl<W, C, GG> Variable<ProofSystem<C>> for PathVar<W, C, GG> +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, +{ + type Type = PathWrapper<W, C, GG>; + + type Mode = Secret; + + #[inline] + fn new(ps: &mut ProofSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + PathVar( + match allocation.known() { + Some((this, _)) => PathVarInnerType::new_witness(ns!(ps.cs, ""), full(&(this.0).0)), + _ => PathVarInnerType::new_witness(ns!(ps.cs, ""), empty::<PathInnerType<W, C>>), + } + .expect("Variable allocation is not allowed to fail."), + ) + } +} + +impl<W, C, GG> HasAllocation<ProofSystem<C>> for PathWrapper<W, C, GG> +where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, +{ + type Variable = PathVar<W, C, GG>; + type Mode = Secret; +} + +/// Merkle Tree +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""))] +pub struct MerkleTree<W, C>(ArkMerkleTree<Configuration<W, C>>) +where + W: PedersenWindow, + C: ProjectiveCurve; + +impl<W, C> MerkleTree<W, C> +where + W: PedersenWindow, + C: ProjectiveCurve, +{ + /// Builds a new [`MerkleTree`]. + /// + /// # Panics + /// + /// The length of `leaves` must be a power of 2 or this function will panic. + #[inline] + pub fn new<T>(parameters: &Parameters<W, C>, leaves: &[T]) -> Option<Self> + where + T: Concat<Item = u8>, + { + Some(Self( + ArkMerkleTree::new( + &parameters.leaf, + &parameters.two_to_one, + &leaves + .iter() + .map(move |leaf| as_bytes!(leaf)) + .collect::<Vec<_>>(), + ) + .ok()?, + )) + } + + /// Builds a new [`MerkleTree`]. + /// + /// # Panics + /// + /// The length of `leaves` must be a power of 2 or this function will panic. + #[inline] + pub fn from_wrapped<GG, T>( + parameters: &ParametersWrapper<W, C, GG>, + leaves: &[T], + ) -> Option<Self> + where + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + T: Concat<Item = u8>, + { + Self::new(&parameters.0, leaves) + } + + /// Computes the [`Root`] of the [`MerkleTree`] built from the `leaves`. + #[inline] + pub fn build_root<T>(parameters: &Parameters<W, C>, leaves: &[T]) -> Option<Root<W, C>> + where + T: Concat<Item = u8>, + { + Some(Self::new(parameters, leaves)?.root()) + } + + /// Computes the [`RootWrapper`] of the [`MerkleTree`] built from the `leaves`. + #[inline] + pub fn build_root_wrapped<GG, T>( + parameters: &ParametersWrapper<W, C, GG>, + leaves: &[T], + ) -> Option<RootWrapper<W, C, GG>> + where + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + T: Concat<Item = u8>, + { + Self::build_root(&parameters.0, leaves).map(move |r| RootWrapper(r, PhantomData)) + } + + /// Returns the [`Root`] of this [`MerkleTree`]. + #[inline] + pub fn root(&self) -> Root<W, C> { + Root(self.0.root()) + } + + /// Returns the [`RootWrapper`] of this [`MerkleTree`]. + #[inline] + pub fn root_wrapped<GG>(&self) -> RootWrapper<W, C, GG> + where + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + { + RootWrapper(self.root(), PhantomData) + } + + /// Computes the root and the path for the leaf at the given `index`. + #[inline] + fn compute_containment_proof(&self, index: usize) -> Option<(Root<W, C>, Path<W, C>)> { + Some((self.root(), Path(self.0.generate_proof(index).ok()?))) + } + + /// Builds a containment proof (i.e. merkle root and path) for the leaf at the given `index`. + #[inline] + pub fn get_containment_proof<S>(&self, index: usize) -> Option<ContainmentProof<S>> + where + S: VerifiedSet<Public = Root<W, C>, Secret = Path<W, C>>, + { + let (root, path) = self.compute_containment_proof(index)?; + Some(ContainmentProof::new(root, path)) + } + + /// Builds a containment proof (i.e. merkle root and path) for the leaf at the given `index`. + #[inline] + pub fn get_wrapped_containment_proof<GG, S>(&self, index: usize) -> Option<ContainmentProof<S>> + where + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + S: VerifiedSet<Public = RootWrapper<W, C, GG>, Secret = PathWrapper<W, C, GG>>, + { + let (root, path) = self.compute_containment_proof(index)?; + Some(ContainmentProof::new( + RootWrapper(root, PhantomData), + PathWrapper(path, PhantomData), + )) + } +} diff --git a/manta-pay/src/crypto/merkle_tree/incremental.rs b/manta-pay/src/crypto/merkle_tree/incremental.rs new file mode 100644 index 000000000..c1963f2cc --- /dev/null +++ b/manta-pay/src/crypto/merkle_tree/incremental.rs @@ -0,0 +1,422 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Incremental Merkle Tree Implementation + +use alloc::vec::Vec; +use ark_crypto_primitives::{ + crh::TwoToOneCRH, + merkle_tree::{Config, LeafDigest, LeafParam, TwoToOneDigest, TwoToOneParam}, + Error, Path, CRH, +}; +use ark_ff::{to_bytes, ToBytes}; + +/// Incremental Merkle Tree +/// +/// This merkle tree implementation has a fixed runtime height `h`, and stores `2^h` leaves. +#[derive(Clone)] +pub struct IncrementalMerkleTree<P> +where + P: Config, +{ + /// Hashes of leaf nodes from left to right + leaf_nodes: Vec<LeafDigest<P>>, + + /// Inner Hash Parameters + two_to_one_hash_param: TwoToOneParam<P>, + + /// Leaf Hash Parameters + leaf_hash_param: LeafParam<P>, + + /// Fixed Merkle Tree Height + height: usize, + + /// Path of the Current Leaf + current_path: Path<P>, + + /// Root of the Merkle Tree + root: TwoToOneDigest<P>, + + /// Emptiness Flag + empty: bool, +} + +impl<P> IncrementalMerkleTree<P> +where + P: Config, +{ + /// Checks if `self` is an empty tree. + #[inline] + pub fn is_empty(&self) -> bool { + self.empty + } + + /// Returns the index of the current (right-most) leaf. + #[inline] + pub fn current_index(&self) -> Option<usize> { + if self.is_empty() { + None + } else { + Some(self.current_path.leaf_index) + } + } + + /// Returns the next available index for a leaf node. + #[inline] + pub fn next_available(&self) -> Option<usize> { + let current_index = self.current_path.leaf_index; + if self.is_empty() { + Some(0) + } else if current_index < (1 << (self.height - 1)) - 1 { + Some(current_index + 1) + } else { + None + } + } + + /// Returns the proof for the current (right-most) leaf. + #[inline] + pub fn current_proof(&self) -> &Path<P> { + &self.current_path + } + + /// Returns the root of the tree. + #[inline] + pub fn root(&self) -> &TwoToOneDigest<P> { + &self.root + } + + /// Creates an empty merkle tree with leaves filled with the sentinel value. + /// + /// # Panics + /// + /// This function panics if the given `height` is less than `2`. + pub fn blank( + leaf_hash_param: &LeafParam<P>, + two_to_one_hash_param: &TwoToOneParam<P>, + height: usize, + ) -> Self { + assert!( + height > 1, + "the height of incremental merkle tree should be at least 2" + ); + IncrementalMerkleTree { + current_path: Path { + leaf_sibling_hash: Default::default(), + auth_path: Default::default(), + leaf_index: Default::default(), + }, + leaf_nodes: Default::default(), + two_to_one_hash_param: two_to_one_hash_param.clone(), + leaf_hash_param: leaf_hash_param.clone(), + root: Default::default(), + height, + empty: true, + } + } + + /// Asserts that we are inserting into a valid index. + #[inline] + fn assert_valid_index(&self) { + assert!(self.next_available() != None, "index out of range"); + } + + /// Appends `leaf` to the tree at the next available index. + /// + /// # Example + /// + /// Given the following tree: + /// ```tree_diagram + /// [A] + /// / \ + /// [B] () + /// / \ / \ + /// D [E] () () + /// .. / \ .... + /// [I]{leaf} + /// ``` + /// running `append({leaf})` would insert `leaf` after `[I]` and would trigger a recompute of + /// `[E]`, `[B]`, and `[A]`. + pub fn append<L>(&mut self, leaf: L) -> Result<(), Error> + where + L: ToBytes, + { + self.assert_valid_index(); + let leaf_digest = P::LeafHash::evaluate(&self.leaf_hash_param, &to_bytes!(leaf)?)?; + let (path, root) = self.next_path(&leaf_digest)?; + self.leaf_nodes.push(leaf_digest); + self.current_path = path; + self.root = root; + self.empty = false; + Ok(()) + } + + /// Generates the new path and root of the tree given `new_leaf_digest` as the next inserted + /// leaf in the tree. + fn next_path( + &self, + new_leaf_digest: &LeafDigest<P>, + ) -> Result<(Path<P>, TwoToOneDigest<P>), Error> { + self.assert_valid_index(); + + // Calculate tree height and empty hash. + let tree_height = self.height; + let hash_of_empty_node = TwoToOneDigest::<P>::default(); + let hash_of_empty_leaf = LeafDigest::<P>::default(); + + // Create a new auth path with length two less than the tree height. + let mut new_auth_path = Vec::with_capacity(tree_height - 2); + + if self.is_empty() { + // generate auth path and calculate the root + let mut current_node = P::TwoToOneHash::evaluate( + &self.two_to_one_hash_param, + &to_bytes!(new_leaf_digest)?, + &to_bytes!(LeafDigest::<P>::default())?, + )?; + // all the auth path node are empty nodes + for _ in 0..tree_height - 2 { + new_auth_path.push(hash_of_empty_node.clone()); + current_node = P::TwoToOneHash::evaluate( + &self.two_to_one_hash_param, + &to_bytes!(current_node)?, + &to_bytes!(hash_of_empty_node.clone())?, + )?; + } + + let path = Path { + leaf_index: 0, + auth_path: new_auth_path, + leaf_sibling_hash: hash_of_empty_leaf, + }; + Ok((path, current_node)) + } else { + // compute next path of a non-empty tree + // Get the indices of the previous and propsed (new) leaf node + let mut new_index = self.next_available().unwrap(); + let mut old_index = self.current_index().unwrap(); + let old_leaf = &self.leaf_nodes[old_index]; + + // generate two mutable node: old_current_node, new_current_node to iterate on + let (old_left_leaf, old_right_leaf) = if is_left_child(old_index) { + ( + self.leaf_nodes[old_index].clone(), + self.current_path.leaf_sibling_hash.clone(), + ) + } else { + ( + self.current_path.leaf_sibling_hash.clone(), + self.leaf_nodes[old_index].clone(), + ) + }; + + let (new_left_leaf, new_right_leaf, leaf_sibling) = if is_left_child(new_index) { + (new_leaf_digest, &hash_of_empty_leaf, &hash_of_empty_leaf) + } else { + (old_leaf, new_leaf_digest, old_leaf) + }; + + let mut old_current_node = P::TwoToOneHash::evaluate( + &self.two_to_one_hash_param, + &to_bytes!(old_left_leaf)?, + &to_bytes!(old_right_leaf)?, + )?; + let mut new_current_node = P::TwoToOneHash::evaluate( + &self.two_to_one_hash_param, + &to_bytes!(new_left_leaf)?, + &to_bytes!(new_right_leaf)?, + )?; + + // reverse the old_auth_path to make it bottom up + let mut old_auth_path = self.current_path.auth_path.clone(); + old_auth_path.reverse(); + + // build new_auth_path and root recursively + for old_auth_path_point in old_auth_path.iter().take(tree_height - 2) { + new_index = parent_index_on_level(new_index); + old_index = parent_index_on_level(old_index); + if new_index == old_index { + // this means the old path and new path are merged, + // as a result, no need to update the old_current_node any more + + // add the auth path node + new_auth_path.push(old_auth_path_point.clone()); + + // update the new current node (this is needed to compute the root) + let (new_left, new_right) = if is_left_child(new_index) { + (new_current_node, hash_of_empty_node.clone()) + } else { + (old_auth_path_point.clone(), new_current_node) + }; + new_current_node = P::TwoToOneHash::evaluate( + &self.two_to_one_hash_param, + &to_bytes!(new_left)?, + &to_bytes!(new_right)?, + )?; + } else { + // this means old path and new path haven't been merged, + // as a reulst, need to update both the new_current_node and new_current_node + let auth_node = if is_left_child(new_index) { + hash_of_empty_node.clone() + } else { + old_current_node.clone() + }; + new_auth_path.push(auth_node); + + // update both old_current_node and new_current_node + // update new_current_node + let (new_left, new_right) = if is_left_child(new_index) { + (new_current_node.clone(), hash_of_empty_node.clone()) + } else { + (old_current_node.clone(), new_current_node) + }; + new_current_node = P::TwoToOneHash::evaluate( + &self.two_to_one_hash_param, + &to_bytes!(new_left)?, + &to_bytes!(new_right)?, + )?; + + // We only need to update the old_current_node bottom up when it is right child + if !is_left_child(old_index) { + old_current_node = P::TwoToOneHash::evaluate( + &self.two_to_one_hash_param, + &to_bytes!(old_auth_path_point.clone())?, + &to_bytes!(old_current_node)?, + )?; + } + } + } + + // reverse new_auth_path to top down + new_auth_path.reverse(); + let path = Path { + leaf_index: self.next_available().unwrap(), + auth_path: new_auth_path, + leaf_sibling_hash: leaf_sibling.clone(), + }; + Ok((path, new_current_node)) + } + } +} + +/// Returns `true` if and only if the given index on the current level represents a left child. +#[inline] +fn is_left_child(index_on_level: usize) -> bool { + index_on_level % 2 == 0 +} + +/// Returns the parent index for the index on the current level. +#[inline] +fn parent_index_on_level(index_on_level: usize) -> usize { + index_on_level >> 1 +} + +#[cfg(test)] +mod test { + use super::*; + use alloc::vec::Vec; + use ark_crypto_primitives::crh::{pedersen, TwoToOneCRH as TwoToOneCRHTrait, CRH as CRHTrait}; + use ark_ed_on_bls12_381::EdwardsProjective as JubJub; + use ark_ff::{BigInteger256, UniformRand}; + use rand::{rngs::ThreadRng, thread_rng}; + + /// Pedersen Window Parameters + #[derive(Clone)] + struct Window4x256; + + impl pedersen::Window for Window4x256 { + const WINDOW_SIZE: usize = 4; + const NUM_WINDOWS: usize = 256; + } + + /// Leaf Hash + type LeafH = pedersen::CRH<JubJub, Window4x256>; + + /// Two-to-One Pedersen Hash + type CompressH = pedersen::CRH<JubJub, Window4x256>; + + /// JubJub Merkle Tree Parameters + struct JubJubMerkleTreeParams; + + impl Config for JubJubMerkleTreeParams { + type LeafHash = LeafH; + type TwoToOneHash = CompressH; + } + + /// Jub Jub Incremental Merkle Tree + type JubJubIncrementalMerkleTree = IncrementalMerkleTree<JubJubMerkleTreeParams>; + + /// Builds an incremental merkle tree element by element and tests that each generated proof is + /// valid. + fn incremental_merkle_tree_test<L: ToBytes>(tree_height: usize, update_query: &[L]) { + let mut rng = thread_rng(); + let leaf_crh_params = <LeafH as CRHTrait>::setup(&mut rng).unwrap(); + let two_to_one_params = <CompressH as TwoToOneCRHTrait>::setup(&mut rng).unwrap(); + let mut tree = + JubJubIncrementalMerkleTree::blank(&leaf_crh_params, &two_to_one_params, tree_height); + for v in update_query { + let v = to_bytes!(v).unwrap(); + tree.append(v.clone()).unwrap(); + println!("{:?}", tree.next_available()); + println!("{:?}", tree.is_empty()); + let proof = tree.current_proof(); + assert!(proof + .verify(&leaf_crh_params, &two_to_one_params, tree.root(), &v) + .unwrap()); + } + } + + /// Tests the emptiness criterion for an incremental merkle tree. + #[test] + fn test_emptyness_for_imt() { + let mut rng = thread_rng(); + let leaf_crh_params = <LeafH as CRHTrait>::setup(&mut rng).unwrap(); + let two_to_one_params = <CompressH as TwoToOneCRHTrait>::setup(&mut rng).unwrap(); + let mut tree = JubJubIncrementalMerkleTree::blank(&leaf_crh_params, &two_to_one_params, 5); + assert!(tree.is_empty()); + let v = BigInteger256::rand(&mut rng); + tree.append(to_bytes!(v).unwrap()).unwrap(); + assert!(!tree.is_empty()); + } + + /// Samples merkle tree updates from `rng` and tests them with the + /// [`incremental_merkle_tree_test`] function. + #[inline] + fn sample_updates_and_test_imt(rng: &mut ThreadRng, update_count: usize, tree_height: usize) { + let mut updates = Vec::new(); + for _ in 0..update_count { + updates.push(BigInteger256::rand(rng)); + } + incremental_merkle_tree_test(tree_height, &updates); + } + + /// Runs tests for well-formed trees. + #[test] + fn good_root_test_for_imt() { + let mut rng = thread_rng(); + sample_updates_and_test_imt(&mut rng, 2, 2); + sample_updates_and_test_imt(&mut rng, 7, 4); + sample_updates_and_test_imt(&mut rng, 128, 8); + } + + /// Runs test for a tree which has exceeded capacity. + #[test] + #[should_panic] + fn out_of_capacity_test_for_imt() { + let mut rng = thread_rng(); + sample_updates_and_test_imt(&mut rng, 3, 2); + } +} diff --git a/manta-pay/src/crypto/merkle_tree/mod.rs b/manta-pay/src/crypto/merkle_tree/mod.rs index 428b5a5b8..4d0a4ce96 100644 --- a/manta-pay/src/crypto/merkle_tree/mod.rs +++ b/manta-pay/src/crypto/merkle_tree/mod.rs @@ -14,509 +14,10 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Merkle Tree Implementation +//! Merkle Tree Implementations -// NOTE: Most if not all of the fallible interfaces in this file never actually fail. We use -// faillible interfaces so that we don't have to depend explicitly on implementation -// details of the `arkworks` project. +mod basic; +mod incremental; -// TODO: We use the Pedersen commitment settings for `CRH` and `TwoToOneCRH`. We should write our -// own `CRH` and `TwoToOneCRH` traits and then in the configuration we align them with the -// Pedersen settings. - -use crate::crypto::{ - commitment::pedersen::{PedersenWindow, ProjectiveCurve}, - constraint::{empty, full, ArkProofSystem}, -}; -use alloc::vec::Vec; -use ark_crypto_primitives::{ - crh::{ - constraints::{CRHGadget as CRHGadgetTrait, TwoToOneCRHGadget as TwoToOneCRHGadgetTrait}, - pedersen::{constraints::CRHGadget, CRH}, - }, - merkle_tree::{ - constraints::PathVar as ArkPathVar, Config, LeafParam as ArkLeafParam, - MerkleTree as ArkMerkleTree, Path as ArkPath, TwoToOneDigest, - TwoToOneParam as ArkTwoToOneParam, - }, -}; -use ark_ff::Field; -use ark_r1cs_std::{ - alloc::AllocVar, - boolean::Boolean, - eq::EqGadget, - groups::{CurveVar, GroupOpsBounds}, - uint8::UInt8, -}; -use ark_relations::ns; -use core::marker::PhantomData; -use manta_crypto::{ - constraint::{reflection::HasAllocation, Allocation, Constant, Public, Secret, Variable}, - set::{ContainmentProof, VerifiedSet}, -}; -use manta_util::{as_bytes, Concat}; - -/// Constraint Field Type -pub type ConstraintField<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField; - -/// Proof System -type ProofSystem<C> = ArkProofSystem<ConstraintField<C>>; - -/// Merkle Tree Configuration -#[derive(derivative::Derivative)] -#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Configuration<W, C>(PhantomData<(W, C)>) -where - W: PedersenWindow, - C: ProjectiveCurve; - -impl<W, C> Config for Configuration<W, C> -where - W: PedersenWindow, - C: ProjectiveCurve, -{ - type LeafHash = CRH<C, W>; - type TwoToOneHash = CRH<C, W>; -} - -/// Leaf Hash Parameters Type -type LeafParam<W, C> = ArkLeafParam<Configuration<W, C>>; - -/// Two-to-One Hash Parameters Type -type TwoToOneParam<W, C> = ArkTwoToOneParam<Configuration<W, C>>; - -/// Leaf Hash Parameters Variable -type LeafParamVar<W, C, GG> = <CRHGadget<C, GG, W> as CRHGadgetTrait< - <Configuration<W, C> as Config>::LeafHash, - ConstraintField<C>, ->>::ParametersVar; - -/// Two-to-One Hash Parameters Variable -type TwoToOneParamVar<W, C, GG> = <CRHGadget<C, GG, W> as TwoToOneCRHGadgetTrait< - <Configuration<W, C> as Config>::TwoToOneHash, - ConstraintField<C>, ->>::ParametersVar; - -/// Merkle Tree Parameters -#[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""))] -pub struct Parameters<W, C> -where - W: PedersenWindow, - C: ProjectiveCurve, -{ - /// Leaf Hash Parameters - leaf: LeafParam<W, C>, - - /// Two-to-One Hash Parameters - two_to_one: TwoToOneParam<W, C>, -} - -impl<W, C> Parameters<W, C> -where - W: PedersenWindow, - C: ProjectiveCurve, -{ - /// Verifies that `path` constitutes a proof that `item` is contained in the Merkle Tree - /// with the given `root`. - #[inline] - pub fn verify<T>(&self, root: &Root<W, C>, path: &Path<W, C>, item: &T) -> bool - where - T: Concat<Item = u8>, - { - path.0 - .verify(&self.leaf, &self.two_to_one, &root.0, &as_bytes!(item)) - .expect("As of arkworks 0.3.0, this never fails.") - } -} - -/// Merkle Tree Parameters Wrapper -#[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""))] -pub struct ParametersWrapper<W, C, GG>(Parameters<W, C>, PhantomData<GG>) -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; - -impl<W, C, GG> ParametersWrapper<W, C, GG> -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, -{ - /// Verifies that `path` constitutes a proof that `item` is contained in the Merkle Tree - /// with the given `root`. - #[inline] - pub fn verify<T>( - &self, - root: &RootWrapper<W, C, GG>, - path: &PathWrapper<W, C, GG>, - item: &T, - ) -> bool - where - T: Concat<Item = u8>, - { - self.0.verify(&root.0, &path.0, item) - } -} - -/// Merkle Tree Parameters Variable -#[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""))] -pub struct ParametersVar<W, C, GG> -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, -{ - /// Leaf Hash Parameters Variable - leaf: LeafParamVar<W, C, GG>, - - /// Two-to-One Hash Parameters Variable - two_to_one: TwoToOneParamVar<W, C, GG>, -} - -impl<W, C, GG> ParametersVar<W, C, GG> -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, -{ - /// Verifies that `path` constitutes a proof that `item` is contained in the Merkle Tree - /// with the given `root`. - #[inline] - pub fn verify( - &self, - root: &RootVar<W, C, GG>, - path: &PathVar<W, C, GG>, - item: &[UInt8<ConstraintField<C>>], - ) -> Boolean<ConstraintField<C>> { - path.0 - .verify_membership(&self.leaf, &self.two_to_one, &root.0, &item) - .expect("This is not allowed to fail.") - } - - /// Asserts that `path` constitutes a proof that `item` is contained in the Merkle Tree - /// with the given `root`. - #[inline] - pub fn assert_verified( - &self, - root: &RootVar<W, C, GG>, - path: &PathVar<W, C, GG>, - item: &[UInt8<ConstraintField<C>>], - ) { - self.verify(root, path, item) - .enforce_equal(&Boolean::TRUE) - .expect("This is not allowed to fail.") - } -} - -impl<W, C, GG> Variable<ProofSystem<C>> for ParametersVar<W, C, GG> -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, -{ - type Type = ParametersWrapper<W, C, GG>; - - type Mode = Constant; - - #[inline] - fn new(ps: &mut ProofSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - let (this, _) = allocation.into_known(); - ParametersVar { - leaf: LeafParamVar::<W, _, _>::new_constant( - ns!(ps.cs, "leaf hash parameter constant"), - &this.0.leaf, - ) - .expect("Variable allocation is not allowed to fail."), - two_to_one: TwoToOneParamVar::<W, _, _>::new_constant( - ns!(ps.cs, "two-to-one hash parameter constant"), - &this.0.two_to_one, - ) - .expect("Variable allocation is not allowed to fail."), - } - } -} - -impl<W, C, GG> HasAllocation<ProofSystem<C>> for ParametersWrapper<W, C, GG> -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, -{ - type Variable = ParametersVar<W, C, GG>; - - type Mode = Constant; -} - -/// Merkle Tree Root Inner Type -type RootInnerType<W, C> = TwoToOneDigest<Configuration<W, C>>; - -/// Merkle Tree Root -#[derive(derivative::Derivative)] -#[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] -pub struct Root<W, C>(RootInnerType<W, C>) -where - W: PedersenWindow, - C: ProjectiveCurve; - -/// Merkle Tree Root Wrapper -#[derive(derivative::Derivative)] -#[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] -pub struct RootWrapper<W, C, GG>(Root<W, C>, PhantomData<GG>) -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; - -/// Merkle Tree Root Variable -#[derive(derivative::Derivative)] -#[derivative(Clone)] -pub struct RootVar<W, C, GG>(GG, PhantomData<(W, C)>) -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; - -impl<W, C, GG> Variable<ProofSystem<C>> for RootVar<W, C, GG> -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, -{ - type Type = RootWrapper<W, C, GG>; - - type Mode = Public; - - #[inline] - fn new(ps: &mut ProofSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - RootVar( - match allocation.known() { - Some((this, _)) => AllocVar::<RootInnerType<W, C>, _>::new_input( - ns!(ps.cs, "merkle tree root public input"), - full((this.0).0), - ), - _ => AllocVar::<RootInnerType<W, C>, _>::new_input( - ns!(ps.cs, "merkle tree root public input"), - empty::<RootInnerType<W, C>>, - ), - } - .expect("Variable allocation is not allowed to fail."), - PhantomData, - ) - } -} - -impl<W, C, GG> HasAllocation<ProofSystem<C>> for RootWrapper<W, C, GG> -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, -{ - type Variable = RootVar<W, C, GG>; - type Mode = Public; -} - -/// Merkle Tree Path Inner Type -type PathInnerType<W, C> = ArkPath<Configuration<W, C>>; - -/// Merkle Tree Path -#[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""))] -pub struct Path<W, C>(PathInnerType<W, C>) -where - W: PedersenWindow, - C: ProjectiveCurve; - -/// Merkle Tree Path Wrapper -#[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""))] -pub struct PathWrapper<W, C, GG>(Path<W, C>, PhantomData<GG>) -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; - -/// Merkle Tree Path Variable Inner Type -type PathVarInnerType<W, C, GG> = - ArkPathVar<Configuration<W, C>, CRHGadget<C, GG, W>, CRHGadget<C, GG, W>, ConstraintField<C>>; - -/// Merkle Tree Path Variable -pub struct PathVar<W, C, GG>(PathVarInnerType<W, C, GG>) -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; - -impl<W, C, GG> Variable<ProofSystem<C>> for PathVar<W, C, GG> -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, -{ - type Type = PathWrapper<W, C, GG>; - - type Mode = Secret; - - #[inline] - fn new(ps: &mut ProofSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - PathVar( - match allocation.known() { - Some((this, _)) => PathVarInnerType::new_witness(ns!(ps.cs, ""), full(&(this.0).0)), - _ => PathVarInnerType::new_witness(ns!(ps.cs, ""), empty::<PathInnerType<W, C>>), - } - .expect("Variable allocation is not allowed to fail."), - ) - } -} - -impl<W, C, GG> HasAllocation<ProofSystem<C>> for PathWrapper<W, C, GG> -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, -{ - type Variable = PathVar<W, C, GG>; - type Mode = Secret; -} - -/// Merkle Tree -#[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""))] -pub struct MerkleTree<W, C>(ArkMerkleTree<Configuration<W, C>>) -where - W: PedersenWindow, - C: ProjectiveCurve; - -impl<W, C> MerkleTree<W, C> -where - W: PedersenWindow, - C: ProjectiveCurve, -{ - /// Builds a new [`MerkleTree`]. - /// - /// # Panics - /// - /// The length of `leaves` must be a power of 2 or this function will panic. - #[inline] - pub fn new<T>(parameters: &Parameters<W, C>, leaves: &[T]) -> Option<Self> - where - T: Concat<Item = u8>, - { - Some(Self( - ArkMerkleTree::new( - &parameters.leaf, - &parameters.two_to_one, - &leaves - .iter() - .map(move |leaf| as_bytes!(leaf)) - .collect::<Vec<_>>(), - ) - .ok()?, - )) - } - - /// Builds a new [`MerkleTree`]. - /// - /// # Panics - /// - /// The length of `leaves` must be a power of 2 or this function will panic. - #[inline] - pub fn from_wrapped<GG, T>( - parameters: &ParametersWrapper<W, C, GG>, - leaves: &[T], - ) -> Option<Self> - where - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - T: Concat<Item = u8>, - { - Self::new(&parameters.0, leaves) - } - - /// Computes the [`Root`] of the [`MerkleTree`] built from the `leaves`. - #[inline] - pub fn build_root<T>(parameters: &Parameters<W, C>, leaves: &[T]) -> Option<Root<W, C>> - where - T: Concat<Item = u8>, - { - Some(Self::new(parameters, leaves)?.root()) - } - - /// Computes the [`RootWrapper`] of the [`MerkleTree`] built from the `leaves`. - #[inline] - pub fn build_root_wrapped<GG, T>( - parameters: &ParametersWrapper<W, C, GG>, - leaves: &[T], - ) -> Option<RootWrapper<W, C, GG>> - where - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - T: Concat<Item = u8>, - { - Self::build_root(&parameters.0, leaves).map(move |r| RootWrapper(r, PhantomData)) - } - - /// Returns the [`Root`] of this [`MerkleTree`]. - #[inline] - pub fn root(&self) -> Root<W, C> { - Root(self.0.root()) - } - - /// Returns the [`RootWrapper`] of this [`MerkleTree`]. - #[inline] - pub fn root_wrapped<GG>(&self) -> RootWrapper<W, C, GG> - where - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - { - RootWrapper(self.root(), PhantomData) - } - - /// Computes the root and the path for the leaf at the given `index`. - #[inline] - fn compute_containment_proof(&self, index: usize) -> Option<(Root<W, C>, Path<W, C>)> { - Some((self.root(), Path(self.0.generate_proof(index).ok()?))) - } - - /// Builds a containment proof (i.e. merkle root and path) for the leaf at the given `index`. - #[inline] - pub fn get_containment_proof<S>(&self, index: usize) -> Option<ContainmentProof<S>> - where - S: VerifiedSet<Public = Root<W, C>, Secret = Path<W, C>>, - { - let (root, path) = self.compute_containment_proof(index)?; - Some(ContainmentProof::new(root, path)) - } - - /// Builds a containment proof (i.e. merkle root and path) for the leaf at the given `index`. - #[inline] - pub fn get_wrapped_containment_proof<GG, S>(&self, index: usize) -> Option<ContainmentProof<S>> - where - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - S: VerifiedSet<Public = RootWrapper<W, C, GG>, Secret = PathWrapper<W, C, GG>>, - { - let (root, path) = self.compute_containment_proof(index)?; - Some(ContainmentProof::new( - RootWrapper(root, PhantomData), - PathWrapper(path, PhantomData), - )) - } -} +pub use basic::*; +pub use incremental::*; diff --git a/manta-pay/src/crypto/prf/blake2s.rs b/manta-pay/src/crypto/prf/blake2s.rs index 13d8cd0ec..9f48b1ca9 100644 --- a/manta-pay/src/crypto/prf/blake2s.rs +++ b/manta-pay/src/crypto/prf/blake2s.rs @@ -93,6 +93,11 @@ impl PseudorandomFunctionFamily for Blake2s { .expect("As of arkworks 0.3.0, this never fails."), ) } + + #[inline] + fn evaluate_zero(seed: &Self::Seed) -> Self::Output { + Self::evaluate(seed, &Default::default()) + } } /// Blake2s PRF Constraint System Implementations @@ -177,17 +182,6 @@ pub mod constraint { } } - impl<F> Default for Blake2sInputVar<F> - where - F: PrimeField, - { - #[inline] - fn default() -> Self { - // TODO: Should be secret values! - todo!() - } - } - impl<F> Variable<ProofSystem<F>> for Blake2sInputVar<F> where F: PrimeField, @@ -340,5 +334,17 @@ pub mod constraint { .expect("Failure outcomes are not accepted."), ) } + + #[inline] + fn evaluate_zero(seed: &Self::Seed) -> Self::Output { + // FIXME: This is super hacky! Find a more sustainable way to do this. + Self::evaluate( + seed, + &Blake2sInputVar(ByteArrayVar::allocate( + &seed.0.constraint_system_ref(), + Allocation::Known(&[0; 32], Secret.into()), + )), + ) + } } } diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index c754ec9ec..0b11d5fc4 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -16,7 +16,7 @@ //! Manta Pay Implementation -#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(not(any(feature = "std", test)), no_std)] #![cfg_attr(doc_cfg, feature(doc_cfg))] #![forbid(rustdoc::broken_intra_doc_links)] #![forbid(missing_docs)] From c00a18a377a8de14e38eb85cd89514d21e78b30b Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 16 Sep 2021 19:31:59 -0400 Subject: [PATCH 037/275] fix proof systems to make them groth16-compatible --- manta-accounting/src/asset.rs | 48 +-- manta-accounting/src/identity.rs | 82 ++--- manta-accounting/src/ledger.rs | 2 + manta-accounting/src/transfer.rs | 172 ++++++----- manta-crypto/src/constraint.rs | 288 +++++++++--------- manta-crypto/src/set/constraint.rs | 70 ++--- manta-pay/Cargo.toml | 2 +- manta-pay/src/accounting/config.rs | 11 +- manta-pay/src/accounting/ledger.rs | 30 +- manta-pay/src/crypto/commitment/pedersen.rs | 54 ++-- .../{proof_system.rs => constraint_system.rs} | 114 +++---- manta-pay/src/crypto/constraint/mod.rs | 8 +- .../constraint/proof_systems/groth16.rs | 99 ++++++ .../crypto/constraint/proof_systems/mod.rs | 21 ++ manta-pay/src/crypto/merkle_tree/basic.rs | 36 +-- manta-pay/src/crypto/prf/blake2s.rs | 61 ++-- 16 files changed, 647 insertions(+), 451 deletions(-) rename manta-pay/src/crypto/constraint/{proof_system.rs => constraint_system.rs} (79%) create mode 100644 manta-pay/src/crypto/constraint/proof_systems/groth16.rs create mode 100644 manta-pay/src/crypto/constraint/proof_systems/mod.rs diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 35f176569..dd5be116d 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -338,47 +338,47 @@ impl Distribution<Asset> for Standard { } /// Asset Id Variable -pub type AssetIdVar<P> = Var<AssetId, P>; +pub type AssetIdVar<C> = Var<AssetId, C>; /// Asset Balance Variable -pub type AssetBalanceVar<P> = Var<AssetBalance, P>; +pub type AssetBalanceVar<C> = Var<AssetBalance, C>; /// Asset Variable -pub struct AssetVar<P> +pub struct AssetVar<C> where - P: HasVariable<AssetId, Mode = PublicOrSecret> + C: HasVariable<AssetId, Mode = PublicOrSecret> + HasVariable<AssetBalance, Mode = PublicOrSecret> + ?Sized, { /// Asset Id - pub id: AssetIdVar<P>, + pub id: AssetIdVar<C>, /// Asset Value - pub value: AssetBalanceVar<P>, + pub value: AssetBalanceVar<C>, } -impl<P> AssetVar<P> +impl<C> AssetVar<C> where - P: HasVariable<AssetId, Mode = PublicOrSecret> + C: HasVariable<AssetId, Mode = PublicOrSecret> + HasVariable<AssetBalance, Mode = PublicOrSecret> + ?Sized, { /// Builds a new [`AssetVar`] from an `id` and a `value`. #[inline] - pub fn new(id: AssetIdVar<P>, value: AssetBalanceVar<P>) -> Self { + pub fn new(id: AssetIdVar<C>, value: AssetBalanceVar<C>) -> Self { Self { id, value } } } -impl<P> Concat for AssetVar<P> +impl<C> Concat for AssetVar<C> where - P: HasVariable<AssetId, Mode = PublicOrSecret> + C: HasVariable<AssetId, Mode = PublicOrSecret> + HasVariable<AssetBalance, Mode = PublicOrSecret> + ?Sized, - AssetIdVar<P>: Concat, - AssetBalanceVar<P>: Concat<Item = <AssetIdVar<P> as Concat>::Item>, + AssetIdVar<C>: Concat, + AssetBalanceVar<C>: Concat<Item = <AssetIdVar<C> as Concat>::Item>, { - type Item = <AssetIdVar<P> as Concat>::Item; + type Item = <AssetIdVar<C> as Concat>::Item; #[inline] fn concat<A>(&self, accumulator: &mut A) @@ -390,9 +390,9 @@ where } } -impl<P> Variable<P> for AssetVar<P> +impl<C> Variable<C> for AssetVar<C> where - P: HasVariable<AssetId, Mode = PublicOrSecret> + C: HasVariable<AssetId, Mode = PublicOrSecret> + HasVariable<AssetBalance, Mode = PublicOrSecret> + ?Sized, { @@ -401,27 +401,27 @@ where type Mode = Secret; #[inline] - fn new(ps: &mut P, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new(cs: &mut C, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self::new( - ps.new_known_allocation(&this.id, mode), - ps.new_known_allocation(&this.value, mode), + cs.new_known_allocation(&this.id, mode), + cs.new_known_allocation(&this.value, mode), ), Allocation::Unknown(mode) => Self::new( - unknown::<AssetId, _>(ps, mode.into()), - unknown::<AssetBalance, _>(ps, mode.into()), + unknown::<AssetId, _>(cs, mode.into()), + unknown::<AssetBalance, _>(cs, mode.into()), ), } } } -impl<P> HasAllocation<P> for Asset +impl<C> HasAllocation<C> for Asset where - P: HasVariable<AssetId, Mode = PublicOrSecret> + C: HasVariable<AssetId, Mode = PublicOrSecret> + HasVariable<AssetBalance, Mode = PublicOrSecret> + ?Sized, { - type Variable = AssetVar<P>; + type Variable = AssetVar<C>; type Mode = Secret; } diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 12c13c4d4..fa80bf9ae 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -395,21 +395,21 @@ where type Mode = Secret; #[inline] - fn new(ps: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self::new( - VoidNumberGeneratorVar::<C>::new_known(ps, &this.void_number_generator, mode), + VoidNumberGeneratorVar::<C>::new_known(cs, &this.void_number_generator, mode), VoidNumberCommitmentRandomnessVar::<C>::new_known( - ps, + cs, &this.void_number_commitment_randomness, mode, ), - UtxoRandomnessVar::<C>::new_known(ps, &this.utxo_randomness, mode), + UtxoRandomnessVar::<C>::new_known(cs, &this.utxo_randomness, mode), ), Allocation::Unknown(mode) => Self::new( - VoidNumberGeneratorVar::<C>::new_unknown(ps, mode), - VoidNumberCommitmentRandomnessVar::<C>::new_unknown(ps, mode), - UtxoRandomnessVar::<C>::new_unknown(ps, mode), + VoidNumberGeneratorVar::<C>::new_unknown(cs, mode), + VoidNumberCommitmentRandomnessVar::<C>::new_unknown(cs, mode), + UtxoRandomnessVar::<C>::new_unknown(cs, mode), ), } } @@ -1232,7 +1232,7 @@ where #[inline] pub fn get_well_formed_asset( self, - ps: &mut C::ConstraintSystem, + cs: &mut C::ConstraintSystem, commitment_scheme: &C::CommitmentSchemeVar, utxo_set: &Var<S, C::ConstraintSystem>, ) -> AssetVar<C::ConstraintSystem> @@ -1255,7 +1255,7 @@ where // pk = PRF(sk, 0) // ``` // where public: {}, secret: {pk, sk}. - ps.assert_eq( + cs.assert_eq( &self.public_key, &C::PseudorandomFunctionFamilyVar::evaluate_zero(&self.secret_key), ); @@ -1265,7 +1265,7 @@ where // vn = PRF(sk, rho) // ``` // where public: {vn}, secret: {sk, rho}. - ps.assert_eq( + cs.assert_eq( &self.void_number, &C::PseudorandomFunctionFamilyVar::evaluate( &self.secret_key, @@ -1278,7 +1278,7 @@ where // k = COM(pk || rho, r) // ``` // where public: {k}, secret: {pk, rho, r}. - ps.assert_eq( + cs.assert_eq( &self.void_number_commitment, &generate_void_number_commitment( commitment_scheme, @@ -1293,7 +1293,7 @@ where // cm = COM(asset || k, s) // ``` // where public: {}, secret: {cm, asset, k, s}. - ps.assert_eq( + cs.assert_eq( &self.utxo, &generate_utxo( commitment_scheme, @@ -1309,7 +1309,7 @@ where // ``` // where public: {root}, secret: {cm, path}. self.utxo_containment_proof - .assert_validity(utxo_set, &self.utxo, ps); + .assert_validity(utxo_set, &self.utxo, cs); self.asset } @@ -1327,31 +1327,31 @@ where type Mode = Derived; #[inline] - fn new(ps: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self { - secret_key: SecretKeyVar::<C>::new_known(ps, &this.secret_key, mode), - public_key: PublicKeyVar::<C>::new_known(ps, &this.public_key, Secret), - asset: this.asset.known(ps, mode), - parameters: this.parameters.known(ps, mode), - void_number: VoidNumberVar::<C>::new_known(ps, &this.void_number, Public), + secret_key: SecretKeyVar::<C>::new_known(cs, &this.secret_key, mode), + public_key: PublicKeyVar::<C>::new_known(cs, &this.public_key, Secret), + asset: this.asset.known(cs, mode), + parameters: this.parameters.known(cs, mode), + void_number: VoidNumberVar::<C>::new_known(cs, &this.void_number, Public), void_number_commitment: VoidNumberCommitmentVar::<C>::new_known( - ps, + cs, &this.void_number_commitment, Public, ), - utxo: UtxoVar::<C>::new_known(ps, &this.utxo, Secret), - utxo_containment_proof: this.utxo_containment_proof.known(ps, mode), + utxo: UtxoVar::<C>::new_known(cs, &this.utxo, Secret), + utxo_containment_proof: this.utxo_containment_proof.known(cs, mode), }, Allocation::Unknown(mode) => Self { - secret_key: SecretKeyVar::<C>::new_unknown(ps, mode), - public_key: PublicKeyVar::<C>::new_unknown(ps, Secret), - asset: Asset::unknown(ps, mode), - parameters: AssetParameters::unknown(ps, mode), - void_number: VoidNumberVar::<C>::new_unknown(ps, Public), - void_number_commitment: VoidNumberCommitmentVar::<C>::new_unknown(ps, Public), - utxo: UtxoVar::<C>::new_unknown(ps, Secret), - utxo_containment_proof: ContainmentProof::<S>::unknown(ps, mode), + secret_key: SecretKeyVar::<C>::new_unknown(cs, mode), + public_key: PublicKeyVar::<C>::new_unknown(cs, Secret), + asset: Asset::unknown(cs, mode), + parameters: AssetParameters::unknown(cs, mode), + void_number: VoidNumberVar::<C>::new_unknown(cs, Public), + void_number_commitment: VoidNumberCommitmentVar::<C>::new_unknown(cs, Public), + utxo: UtxoVar::<C>::new_unknown(cs, Secret), + utxo_containment_proof: ContainmentProof::<S>::unknown(cs, mode), }, } } @@ -1561,10 +1561,10 @@ where #[inline] pub fn get_well_formed_asset( self, - ps: &mut C::ConstraintSystem, + cs: &mut C::ConstraintSystem, commitment_scheme: &C::CommitmentSchemeVar, ) -> AssetVar<C::ConstraintSystem> { - ps.assert_eq( + cs.assert_eq( &self.utxo, &generate_utxo( commitment_scheme, @@ -1587,24 +1587,24 @@ where type Mode = Derived; #[inline] - fn new(ps: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self { - asset: AssetVar::new_known(ps, &this.asset, mode), - utxo_randomness: UtxoRandomnessVar::<C>::new_known(ps, &this.utxo_randomness, mode), + asset: AssetVar::new_known(cs, &this.asset, mode), + utxo_randomness: UtxoRandomnessVar::<C>::new_known(cs, &this.utxo_randomness, mode), void_number_commitment: VoidNumberCommitmentVar::<C>::new_known( - ps, + cs, &this.void_number_commitment, Secret, ), - utxo: UtxoVar::<C>::new_known(ps, &this.utxo, Public), + utxo: UtxoVar::<C>::new_known(cs, &this.utxo, Public), __: PhantomData, }, Allocation::Unknown(mode) => Self { - asset: AssetVar::new_unknown(ps, mode), - utxo_randomness: UtxoRandomnessVar::<C>::new_unknown(ps, mode), - void_number_commitment: VoidNumberCommitmentVar::<C>::new_unknown(ps, Secret), - utxo: UtxoVar::<C>::new_unknown(ps, Public), + asset: AssetVar::new_unknown(cs, mode), + utxo_randomness: UtxoRandomnessVar::<C>::new_unknown(cs, mode), + void_number_commitment: VoidNumberCommitmentVar::<C>::new_unknown(cs, Secret), + utxo: UtxoVar::<C>::new_unknown(cs, Public), __: PhantomData, }, } diff --git a/manta-accounting/src/ledger.rs b/manta-accounting/src/ledger.rs index 516d12bac..23e3dc3d2 100644 --- a/manta-accounting/src/ledger.rs +++ b/manta-accounting/src/ledger.rs @@ -108,6 +108,8 @@ where InvalidProof( /// Proof <L::ProofSystem as ProofSystem>::Proof, + /// Proof Verification Error + Option<<L::ProofSystem as ProofSystem>::Error>, ), } diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 16f4e8432..fc3781c0d 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -28,8 +28,9 @@ use core::{ }; use manta_crypto::{ constraint::{ + self, reflection::{HasAllocation, HasVariable}, - Allocation, Constant, ConstraintSystem, Derived, Equal, ProofSystem, Public, + Allocation, Constant, ConstraintSystem as _, Derived, Equal, ProofSystem, Public, PublicOrSecret, Secret, Variable, VariableSource, }, ies::{EncryptedMessage, IntegratedEncryptionScheme}, @@ -38,7 +39,7 @@ use manta_crypto::{ use manta_util::{array_map, mixed_chain, Either}; use rand::{ distributions::{Distribution, Standard}, - Rng, RngCore, + CryptoRng, Rng, RngCore, }; /// Returns `true` if the transfer with this shape would have no public side. @@ -141,22 +142,25 @@ impl<const SOURCES: usize, const SINKS: usize> Distribution<PublicTransfer<SOURC /// Transfer Configuration pub trait TransferConfiguration: - IdentityConstraintSystemConfiguration<ConstraintSystem = Self::ProofSystem> + IdentityConstraintSystemConfiguration<ConstraintSystem = ConstraintSystem<Self>> { - /// Proof System - type ProofSystem: ProofSystem + /// Constraint System + type ConstraintSystem: constraint::ConstraintSystem + HasVariable<AssetId, Variable = Self::AssetIdVar, Mode = PublicOrSecret> + HasVariable<AssetBalance, Variable = Self::AssetBalanceVar, Mode = PublicOrSecret> + HasVariable<<Self::UtxoSet as VerifiedSet>::Public, Mode = Public> + HasVariable<<Self::UtxoSet as VerifiedSet>::Secret, Mode = Secret>; + /// Proof System + type ProofSystem: ProofSystem<ConstraintSystem = ConstraintSystem<Self>>; + /// Asset Id Variable - type AssetIdVar: Variable<Self::ProofSystem, Mode = PublicOrSecret, Type = AssetId> - + Equal<Self::ProofSystem>; + type AssetIdVar: Variable<ConstraintSystem<Self>, Mode = PublicOrSecret, Type = AssetId> + + Equal<ConstraintSystem<Self>>; /// Asset Balance Variable - type AssetBalanceVar: Variable<Self::ProofSystem, Mode = PublicOrSecret, Type = AssetBalance> - + Equal<Self::ProofSystem> + type AssetBalanceVar: Variable<ConstraintSystem<Self>, Mode = PublicOrSecret, Type = AssetBalance> + + Equal<ConstraintSystem<Self>> + AddAssign; /// Integrated Encryption Scheme for [`Asset`] @@ -164,39 +168,47 @@ pub trait TransferConfiguration: /// Verified Set for [`Utxo`] type UtxoSet: VerifiedSet<Item = Utxo<Self>> - + HasAllocation<Self::ProofSystem, Variable = Self::UtxoSetVar, Mode = Constant>; + + HasAllocation<ConstraintSystem<Self>, Variable = Self::UtxoSetVar, Mode = Constant>; /// Verified Set Variable for [`Utxo`] type UtxoSetVar: VerifiedSetVariable< - Self::ProofSystem, + ConstraintSystem<Self>, ItemVar = UtxoVar<Self>, Type = Self::UtxoSet, Mode = Constant, >; } -/// Sender Type +/// Transfer Sender Type pub type Sender<T> = identity::Sender<T, <T as TransferConfiguration>::UtxoSet>; -/// Receiver Type +/// Transfer Receiver Type pub type Receiver<T> = identity::Receiver<T, <T as TransferConfiguration>::IntegratedEncryptionScheme>; -/// Sender Variable Type +/// Transfer Sender Variable Type pub type SenderVar<T> = identity::SenderVar<T, <T as TransferConfiguration>::UtxoSet>; -/// Receiver Type +/// Transfer Receiver Type pub type ReceiverVar<T> = identity::ReceiverVar<T, <T as TransferConfiguration>::IntegratedEncryptionScheme>; -/// Secret Transfer Proof Error Type -pub type ProofError<T> = <<T as TransferConfiguration>::ProofSystem as ProofSystem>::Error; +/// Transfer Constraint System Type +pub type ConstraintSystem<T> = <T as TransferConfiguration>::ConstraintSystem; -/// Secret Transfer Proof Type +/// Transfer Proving Context Type +pub type ProvingContext<T> = + <<T as TransferConfiguration>::ProofSystem as ProofSystem>::ProvingContext; + +/// Transfer Verifying Context Type +pub type VerifyingContext<T> = + <<T as TransferConfiguration>::ProofSystem as ProofSystem>::VerifyingContext; + +/// Transfer Proof Type pub type Proof<T> = <<T as TransferConfiguration>::ProofSystem as ProofSystem>::Proof; -/// Secret Transfer Verifier Type -pub type Verifier<T> = <<T as TransferConfiguration>::ProofSystem as ProofSystem>::Verifier; +/// Transfer Proof System Error Type +pub type ProofSystemError<T> = <<T as TransferConfiguration>::ProofSystem as ProofSystem>::Error; /// Secret Transfer Protocol pub struct SecretTransfer<T, const SENDERS: usize, const RECEIVERS: usize> @@ -304,13 +316,18 @@ where /// Converts `self` into its ledger post. #[inline] - pub fn into_post( + pub fn into_post<R>( self, commitment_scheme: &T::CommitmentScheme, utxo_set: &T::UtxoSet, - ) -> Result<SecretTransferPost<T, SENDERS, RECEIVERS>, ProofError<T>> { + context: &ProvingContext<T>, + rng: &mut R, + ) -> Result<SecretTransferPost<T, SENDERS, RECEIVERS>, ProofSystemError<T>> + where + R: CryptoRng + RngCore + ?Sized, + { match Transfer::from(self) - .into_post(commitment_scheme, utxo_set)? + .into_post(commitment_scheme, utxo_set, context, rng)? .try_into() { Ok(post) => Ok(post), @@ -521,7 +538,7 @@ where fn unknown_variables( commitment_scheme: &T::CommitmentScheme, utxo_set: &T::UtxoSet, - ps: &mut T::ProofSystem, + cs: &mut ConstraintSystem<T>, ) -> ( Option<T::AssetIdVar>, TransferParticipantsVar<T, SOURCES, SENDERS, RECEIVERS, SINKS>, @@ -534,10 +551,10 @@ where Some(()) }; ( - base_asset_id.map(|_| T::AssetIdVar::new_unknown(ps, Public)), - TransferParticipantsVar::new_unknown(ps, Derived), - commitment_scheme.as_known(ps, Public), - utxo_set.as_known(ps, Public), + base_asset_id.map(|_| T::AssetIdVar::new_unknown(cs, Public)), + TransferParticipantsVar::new_unknown(cs, Derived), + commitment_scheme.as_known(cs, Public), + utxo_set.as_known(cs, Public), ) } @@ -547,7 +564,7 @@ where &self, commitment_scheme: &T::CommitmentScheme, utxo_set: &T::UtxoSet, - ps: &mut T::ProofSystem, + cs: &mut ConstraintSystem<T>, ) -> ( Option<T::AssetIdVar>, TransferParticipantsVar<T, SOURCES, SENDERS, RECEIVERS, SINKS>, @@ -555,10 +572,10 @@ where T::UtxoSetVar, ) { ( - self.public.asset_id.map(|id| id.as_known(ps, Public)), - TransferParticipantsVar::new_known(ps, self, Derived), - commitment_scheme.as_known(ps, Public), - utxo_set.as_known(ps, Public), + self.public.asset_id.map(|id| id.as_known(cs, Public)), + TransferParticipantsVar::new_known(cs, self, Derived), + commitment_scheme.as_known(cs, Public), + utxo_set.as_known(cs, Public), ) } @@ -569,10 +586,10 @@ where participants: TransferParticipantsVar<T, SOURCES, SENDERS, RECEIVERS, SINKS>, commitment_scheme: T::CommitmentSchemeVar, utxo_set: T::UtxoSetVar, - ps: &mut T::ProofSystem, + cs: &mut ConstraintSystem<T>, ) { - let mut sender_sum = T::AssetBalanceVar::from_default(ps, Secret); - let mut receiver_sum = T::AssetBalanceVar::from_default(ps, Secret); + let mut sender_sum = T::AssetBalanceVar::from_default(cs, Secret); + let mut receiver_sum = T::AssetBalanceVar::from_default(cs, Secret); participants .sources @@ -584,18 +601,18 @@ where .into_iter() .for_each(|sink| receiver_sum += sink); - #[allow(clippy::needless_collect)] // NOTE: `ps` is being mutated, we need to collect. + #[allow(clippy::needless_collect)] // NOTE: `cs` is being mutated, we need to collect. let secret_asset_ids = mixed_chain( participants.senders.into_iter(), participants.receivers.into_iter(), |c| match c { Either::Left(sender) => { - let asset = sender.get_well_formed_asset(ps, &commitment_scheme, &utxo_set); + let asset = sender.get_well_formed_asset(cs, &commitment_scheme, &utxo_set); sender_sum += asset.value; asset.id } Either::Right(receiver) => { - let asset = receiver.get_well_formed_asset(ps, &commitment_scheme); + let asset = receiver.get_well_formed_asset(cs, &commitment_scheme); receiver_sum += asset.value; asset.id } @@ -604,71 +621,78 @@ where .collect::<Vec<_>>(); match base_asset_id { - Some(asset_id) => ps.assert_all_eq_to_base(&asset_id, secret_asset_ids.iter()), - _ => ps.assert_all_eq(secret_asset_ids.iter()), + Some(asset_id) => cs.assert_all_eq_to_base(&asset_id, secret_asset_ids.iter()), + _ => cs.assert_all_eq(secret_asset_ids.iter()), } - ps.assert_eq(&sender_sum, &receiver_sum); + cs.assert_eq(&sender_sum, &receiver_sum); } /// Generates a verifier for this transfer shape. - /// - /// Given a transfer object of the same shape, if the same `commitment_scheme` and `utxo_set` - /// are passed in to [`generate_proof`](Self::generate_proof) and proof is successfully - /// generated, then the verifier returned by this function will be able to verify that proof. #[inline] - pub fn generate_verifier( + pub fn generate_context<R>( commitment_scheme: &T::CommitmentScheme, utxo_set: &T::UtxoSet, - ) -> Result<Verifier<T>, ProofError<T>> { - let mut ps = T::ProofSystem::setup_to_verify(); + rng: &mut R, + ) -> Result<(ProvingContext<T>, VerifyingContext<T>), ProofSystemError<T>> + where + R: CryptoRng + RngCore + ?Sized, + { + let mut cs = T::ProofSystem::for_unknown(); let (base_asset_id, participants, commitment_scheme, utxo_set) = - Self::unknown_variables(commitment_scheme, utxo_set, &mut ps); + Self::unknown_variables(commitment_scheme, utxo_set, &mut cs); Self::build_constraints( base_asset_id, participants, commitment_scheme, utxo_set, - &mut ps, + &mut cs, ); - ps.into_verifier() + T::ProofSystem::generate_context(cs, rng) } /// Generates a validity proof for this transfer. - /// - /// To verify this proof, run [`generate_verifier`](Self::generate_verifier) with the same - /// `commitment_scheme` and `utxo_set` and use the returned verifier. #[inline] - pub fn generate_proof( + pub fn generate_proof<R>( &self, commitment_scheme: &T::CommitmentScheme, utxo_set: &T::UtxoSet, - ) -> Result<Proof<T>, ProofError<T>> { - let mut ps = T::ProofSystem::setup_to_prove(); + context: &ProvingContext<T>, + rng: &mut R, + ) -> Result<Proof<T>, ProofSystemError<T>> + where + R: CryptoRng + RngCore + ?Sized, + { + let mut cs = T::ProofSystem::for_known(); let (base_asset_id, participants, commitment_scheme, utxo_set) = - self.known_variables(commitment_scheme, utxo_set, &mut ps); + self.known_variables(commitment_scheme, utxo_set, &mut cs); Self::build_constraints( base_asset_id, participants, commitment_scheme, utxo_set, - &mut ps, + &mut cs, ); - ps.into_proof() + T::ProofSystem::generate_proof(cs, context, rng) } /// Converts `self` into its ledger post. #[inline] - pub fn into_post( + pub fn into_post<R>( self, commitment_scheme: &T::CommitmentScheme, utxo_set: &T::UtxoSet, - ) -> Result<TransferPost<T, SOURCES, SENDERS, RECEIVERS, SINKS>, ProofError<T>> { + context: &ProvingContext<T>, + rng: &mut R, + ) -> Result<TransferPost<T, SOURCES, SENDERS, RECEIVERS, SINKS>, ProofSystemError<T>> + where + R: CryptoRng + RngCore + ?Sized, + { Ok(TransferPost { validity_proof: if SENDERS == 0 { None } else { - Some(self.generate_proof(commitment_scheme, utxo_set)?) + Some(self.generate_proof(commitment_scheme, utxo_set, context, rng)?) }, sender_posts: array_map(self.secret.senders, Sender::into_post), receiver_posts: array_map(self.secret.receivers, Receiver::into_post), @@ -700,7 +724,7 @@ struct TransferParticipantsVar< } impl<T, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> - Variable<T::ProofSystem> for TransferParticipantsVar<T, SOURCES, SENDERS, RECEIVERS, SINKS> + Variable<ConstraintSystem<T>> for TransferParticipantsVar<T, SOURCES, SENDERS, RECEIVERS, SINKS> where T: TransferConfiguration, { @@ -709,50 +733,50 @@ where type Mode = Derived; #[inline] - fn new(ps: &mut T::ProofSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new(cs: &mut ConstraintSystem<T>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self { sources: this .public .sources .iter() - .map(|source| source.as_known(ps, Public)) + .map(|source| source.as_known(cs, Public)) .collect(), senders: this .secret .senders .iter() - .map(|sender| sender.known(ps, mode)) + .map(|sender| sender.known(cs, mode)) .collect(), receivers: this .secret .receivers .iter() - .map(|receiver| receiver.known(ps, mode)) + .map(|receiver| receiver.known(cs, mode)) .collect(), sinks: this .public .sinks .iter() - .map(|sink| sink.as_known(ps, Public)) + .map(|sink| sink.as_known(cs, Public)) .collect(), }, Allocation::Unknown(mode) => Self { sources: (0..SOURCES) .into_iter() - .map(|_| T::AssetBalanceVar::new_unknown(ps, Public)) + .map(|_| T::AssetBalanceVar::new_unknown(cs, Public)) .collect(), senders: (0..SENDERS) .into_iter() - .map(|_| SenderVar::new_unknown(ps, mode)) + .map(|_| SenderVar::new_unknown(cs, mode)) .collect(), receivers: (0..RECEIVERS) .into_iter() - .map(|_| ReceiverVar::new_unknown(ps, mode)) + .map(|_| ReceiverVar::new_unknown(cs, mode)) .collect(), sinks: (0..SINKS) .into_iter() - .map(|_| T::AssetBalanceVar::new_unknown(ps, Public)) + .map(|_| T::AssetBalanceVar::new_unknown(cs, Public)) .collect(), }, } diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index f18892b6e..c40f6f608 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -14,12 +14,12 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Constraint Proof Systems +//! Constraint Systems and Proof Systems // TODO: Add derive macros to all the enums/structs here. // TODO: Add derive trait to implement `HasAllocation` for structs (and enums?). // TODO: Add more convenience functions for allocating unknown variables. -// FIXME: Leverage the type system to constraint allocation to only unknown modes for verifier +// FIXME: Leverage the type system to constrain allocation to only unknown modes for verifier // generation and only known modes for proof generation, instead of relying on the `setup_*` // methods to "do the right thing". @@ -29,6 +29,7 @@ use core::{ hash::Hash, marker::PhantomData, }; +use rand::{CryptoRng, RngCore}; /// Allocation Mode pub trait AllocationMode { @@ -163,37 +164,37 @@ where } } - /// Allocates a variable with `self` as the allocation entry into `ps`. + /// Allocates a variable with `self` as the allocation entry into `cs`. #[inline] - pub fn allocate<P, V>(self, ps: &mut P) -> V + pub fn allocate<C, V>(self, cs: &mut C) -> V where - P: ?Sized, - V: Variable<P, Type = T, Mode = Mode>, + C: ?Sized, + V: Variable<C, Type = T, Mode = Mode>, { - V::new(ps, self) + V::new(cs, self) } - /// Allocates a variable into `ps` after mapping over `self`. + /// Allocates a variable into `cs` after mapping over `self`. #[inline] - pub fn map_allocate<P, V, F>(self, ps: &mut P, f: F) -> V + pub fn map_allocate<C, V, F>(self, cs: &mut C, f: F) -> V where Mode::Known: Into<<V::Mode as AllocationMode>::Known>, Mode::Unknown: Into<<V::Mode as AllocationMode>::Unknown>, - P: ?Sized, - V: Variable<P>, + C: ?Sized, + V: Variable<C>, F: FnOnce(&'t T) -> V::Type, { match self { - Self::Known(value, mode) => V::new_known(ps, &f(value), mode), - Self::Unknown(mode) => V::new_unknown(ps, mode), + Self::Known(value, mode) => V::new_known(cs, &f(value), mode), + Self::Unknown(mode) => V::new_unknown(cs, mode), } } } /// Variable Allocation Trait -pub trait Variable<P>: Sized +pub trait Variable<C>: Sized where - P: ?Sized, + C: ?Sized, { /// Origin Type of the Variable type Type; @@ -201,132 +202,132 @@ where /// Allocation Mode type Mode: AllocationMode; - /// Allocates a new variable into `ps` with the given `allocation`. - fn new(ps: &mut P, allocation: Allocation<Self::Type, Self::Mode>) -> Self; + /// Allocates a new variable into `cs` with the given `allocation`. + fn new(cs: &mut C, allocation: Allocation<Self::Type, Self::Mode>) -> Self; - /// Allocates a new known variable into `ps` with the given `mode`. + /// Allocates a new known variable into `cs` with the given `mode`. #[inline] fn new_known( - ps: &mut P, + cs: &mut C, value: &Self::Type, mode: impl Into<<Self::Mode as AllocationMode>::Known>, ) -> Self { - Self::new(ps, Allocation::Known(value, mode.into())) + Self::new(cs, Allocation::Known(value, mode.into())) } - /// Allocates a new unknown variable into `ps` with the given `mode`. + /// Allocates a new unknown variable into `cs` with the given `mode`. #[inline] - fn new_unknown(ps: &mut P, mode: impl Into<<Self::Mode as AllocationMode>::Unknown>) -> Self { - Self::new(ps, Allocation::Unknown(mode.into())) + fn new_unknown(cs: &mut C, mode: impl Into<<Self::Mode as AllocationMode>::Unknown>) -> Self { + Self::new(cs, Allocation::Unknown(mode.into())) } - /// Allocates a new known variable into `ps` with the given `mode` which holds the default + /// Allocates a new known variable into `cs` with the given `mode` which holds the default /// value of [`Self::Type`]. #[inline] - fn from_default(ps: &mut P, mode: impl Into<<Self::Mode as AllocationMode>::Known>) -> Self + fn from_default(cs: &mut C, mode: impl Into<<Self::Mode as AllocationMode>::Known>) -> Self where Self::Type: Default, { - Self::new_known(ps, &Default::default(), mode) + Self::new_known(cs, &Default::default(), mode) } - /// Allocates a new known variable into `ps` with the given `mode` which holds the default + /// Allocates a new known variable into `cs` with the given `mode` which holds the default /// value of [`&Self::Type`](Self::Type). #[inline] fn from_default_ref<'t>( - ps: &mut P, + cs: &mut C, mode: impl Into<<Self::Mode as AllocationMode>::Known>, ) -> Self where Self::Type: 't, &'t Self::Type: Default, { - Self::new_known(ps, Default::default(), mode) + Self::new_known(cs, Default::default(), mode) } } /// Variable Source pub trait VariableSource { - /// Allocates a new variable into `ps` with the given `allocation`. + /// Allocates a new variable into `cs` with the given `allocation`. #[inline] - fn as_variable<P, V>(ps: &mut P, allocation: Allocation<Self, V::Mode>) -> V + fn as_variable<C, V>(cs: &mut C, allocation: Allocation<Self, V::Mode>) -> V where - P: ?Sized, - V: Variable<P, Type = Self>, + C: ?Sized, + V: Variable<C, Type = Self>, { - V::new(ps, allocation) + V::new(cs, allocation) } - /// Allocates a new known variable into `ps` with the given `mode`. + /// Allocates a new known variable into `cs` with the given `mode`. #[inline] - fn as_known<P, V>(&self, ps: &mut P, mode: impl Into<<V::Mode as AllocationMode>::Known>) -> V + fn as_known<C, V>(&self, cs: &mut C, mode: impl Into<<V::Mode as AllocationMode>::Known>) -> V where - P: ?Sized, - V: Variable<P, Type = Self>, + C: ?Sized, + V: Variable<C, Type = Self>, { - V::new_known(ps, self, mode) + V::new_known(cs, self, mode) } - /// Allocates a new unknown variable into `ps` with the given `mode`. + /// Allocates a new unknown variable into `cs` with the given `mode`. #[inline] - fn as_unknown<P, V>(ps: &mut P, mode: impl Into<<V::Mode as AllocationMode>::Unknown>) -> V + fn as_unknown<C, V>(cs: &mut C, mode: impl Into<<V::Mode as AllocationMode>::Unknown>) -> V where - P: ?Sized, - V: Variable<P, Type = Self>, + C: ?Sized, + V: Variable<C, Type = Self>, { - V::new_unknown(ps, mode) + V::new_unknown(cs, mode) } } impl<T> VariableSource for T where T: ?Sized {} -impl<T, P> Variable<P> for PhantomData<T> +impl<T, C> Variable<C> for PhantomData<T> where T: ?Sized, - P: ?Sized, + C: ?Sized, { type Type = PhantomData<T>; type Mode = (); #[inline] - fn new(ps: &mut P, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - let _ = (ps, allocation); + fn new(cs: &mut C, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + let _ = (cs, allocation); PhantomData } } -impl<T, P> reflection::HasAllocation<P> for PhantomData<T> +impl<T, C> reflection::HasAllocation<C> for PhantomData<T> where T: ?Sized, - P: ?Sized, + C: ?Sized, { type Variable = PhantomData<T>; type Mode = (); } -/// Allocates a new known variable into `ps` with the given `mode`. +/// Allocates a new known variable into `cs` with the given `mode`. #[inline] -pub fn known<P, V>( - ps: &mut P, +pub fn known<C, V>( + cs: &mut C, value: &V::Type, mode: impl Into<<V::Mode as AllocationMode>::Known>, ) -> V where - P: ?Sized, - V: Variable<P>, + C: ?Sized, + V: Variable<C>, { - V::new_known(ps, value, mode) + V::new_known(cs, value, mode) } -/// Allocates a new unknown variable into `ps` with the given `mode`. +/// Allocates a new unknown variable into `cs` with the given `mode`. #[inline] -pub fn unknown<P, V>(ps: &mut P, mode: impl Into<<V::Mode as AllocationMode>::Unknown>) -> V +pub fn unknown<C, V>(cs: &mut C, mode: impl Into<<V::Mode as AllocationMode>::Unknown>) -> V where - P: ?Sized, - V: Variable<P>, + C: ?Sized, + V: Variable<C>, { - V::new_unknown(ps, mode) + V::new_unknown(cs, mode) } /// Allocation System @@ -363,7 +364,7 @@ pub trait AllocationSystem { } } -impl<P> AllocationSystem for P where P: ?Sized {} +impl<C> AllocationSystem for C where C: ?Sized {} /// Constraint System pub trait ConstraintSystem { @@ -422,49 +423,55 @@ pub trait ConstraintSystem { } /// Equality Trait -pub trait Equal<P>: Variable<P> +pub trait Equal<C>: Variable<C> where - P: ConstraintSystem + ?Sized, + C: ConstraintSystem + ?Sized, { /// Generates a boolean that represents the fact that `lhs` and `rhs` may be equal. - fn eq(ps: &mut P, lhs: &Self, rhs: &Self) -> P::Bool; + fn eq(cs: &mut C, lhs: &Self, rhs: &Self) -> C::Bool; /// Asserts that `lhs` and `rhs` are equal. #[inline] - fn assert_eq(ps: &mut P, lhs: &Self, rhs: &Self) { - let boolean = Self::eq(ps, lhs, rhs); - ps.assert(boolean) + fn assert_eq(cs: &mut C, lhs: &Self, rhs: &Self) { + let boolean = Self::eq(cs, lhs, rhs); + cs.assert(boolean) } /// Asserts that all the elements in `iter` are equal to some `base` element. #[inline] - fn assert_all_eq_to_base<'t, I>(ps: &mut P, base: &'t Self, iter: I) + fn assert_all_eq_to_base<'t, I>(cs: &mut C, base: &'t Self, iter: I) where I: IntoIterator<Item = &'t Self>, { for item in iter { - Self::assert_eq(ps, base, item) + Self::assert_eq(cs, base, item) } } /// Asserts that all the elements in `iter` are equal. #[inline] - fn assert_all_eq<'t, I>(ps: &mut P, iter: I) + fn assert_all_eq<'t, I>(cs: &mut C, iter: I) where Self: 't, I: IntoIterator<Item = &'t Self>, { let mut iter = iter.into_iter(); if let Some(base) = iter.next() { - Self::assert_all_eq_to_base(ps, base, iter) + Self::assert_all_eq_to_base(cs, base, iter) } } } /// Proof System -pub trait ProofSystem: ConstraintSystem { - /// Verifier Type - type Verifier: Verifier<Self>; +pub trait ProofSystem { + /// Constraint System + type ConstraintSystem: ConstraintSystem; + + /// Proving Context Type + type ProvingContext; + + /// Verifying Context Type + type VerifyingContext; /// Proof Type type Proof; @@ -472,29 +479,34 @@ pub trait ProofSystem: ConstraintSystem { /// Error Type type Error; - /// Returns a proof system which is setup to build a verifier. - fn setup_to_verify() -> Self; + /// Returns a constraint system which is setup to build proving and verifying contexts. + fn for_unknown() -> Self::ConstraintSystem; - /// Returns a proof system which is setup to build a proof. - fn setup_to_prove() -> Self; + /// Returns a constraint system which is setup to build a proof. + fn for_known() -> Self::ConstraintSystem; - /// Returns a verifier object for the constraints contained in `self`. - fn into_verifier(self) -> Result<Self::Verifier, Self::Error>; + /// Returns proving and verifying contexts for the constraints contained in `self`. + fn generate_context<R>( + cs: Self::ConstraintSystem, + rng: &mut R, + ) -> Result<(Self::ProvingContext, Self::VerifyingContext), Self::Error> + where + R: CryptoRng + RngCore + ?Sized; /// Returns a proof that the constraint system `self` is consistent. - fn into_proof(self) -> Result<Self::Proof, Self::Error>; -} - -/// Proof System Verifier -pub trait Verifier<P> -where - P: ProofSystem + ?Sized, -{ - /// Error Type - type Error; + fn generate_proof<R>( + cs: Self::ConstraintSystem, + context: &Self::ProvingContext, + rng: &mut R, + ) -> Result<Self::Proof, Self::Error> + where + R: CryptoRng + RngCore + ?Sized; - /// Verifies that a proof generated from a proof system is valid. - fn verify(&self, proof: &P::Proof) -> Result<bool, Self::Error>; + /// Verifies that a proof generated from this proof system is valid. + fn verify_proof( + context: &Self::VerifyingContext, + proof: &Self::Proof, + ) -> Result<bool, Self::Error>; } /// Derived Allocation Mode @@ -696,71 +708,71 @@ pub mod reflection { /// Variable Type /// /// Requires a [`HasAllocation`] implementation for `T`. - pub type Var<T, P> = <P as HasVariable<T>>::Variable; + pub type Var<T, C> = <C as HasVariable<T>>::Variable; /// Allocation Mode Type /// /// Requires a [`HasAllocation`] implementation for `T`. - pub type Mode<T, P> = <Var<T, P> as Variable<P>>::Mode; + pub type Mode<T, C> = <Var<T, C> as Variable<C>>::Mode; /// Known Allocation Mode Type /// /// Requires a [`HasAllocation`] implementation for `T`. - pub type KnownMode<T, P> = <Mode<T, P> as AllocationMode>::Known; + pub type KnownMode<T, C> = <Mode<T, C> as AllocationMode>::Known; /// Known Allocation Mode Type /// /// Requires a [`HasAllocation`] implementation for `T`. - pub type UnknownMode<T, P> = <Mode<T, P> as AllocationMode>::Unknown; + pub type UnknownMode<T, C> = <Mode<T, C> as AllocationMode>::Unknown; /// Allocation Entry Type /// /// Requires a [`HasAllocation`] implementation for `T`. - pub type Alloc<'t, T, P> = Allocation<'t, T, Mode<T, P>>; + pub type Alloc<'t, T, C> = Allocation<'t, T, Mode<T, C>>; /// Variable Existence Reflection Trait /// /// This trait can be optionally implemented by any type `T` which has an existing variable - /// type that implements [`Variable<P, Type = T>`](Variable). Implementing this trait unlocks + /// type that implements [`Variable<C, Type = T>`](Variable). Implementing this trait unlocks /// all of the reflection capabilities in this module. /// /// Whenever possible, library authors should implement [`HasAllocation`] on their types which /// have associated variables but should minimize their use of [`HasVariable`] so that users /// can take advantage of as much of a library as possible while implementing as little as /// possible. - pub trait HasAllocation<P> + pub trait HasAllocation<C> where - P: ?Sized, + C: ?Sized, { /// Variable Object Type - type Variable: Variable<P, Mode = Self::Mode, Type = Self>; + type Variable: Variable<C, Mode = Self::Mode, Type = Self>; /// Allocation Mode type Mode: AllocationMode; - /// Allocates a new variable into `ps` with the given `allocation`. + /// Allocates a new variable into `cs` with the given `allocation`. #[inline] - fn variable(ps: &mut P, allocation: Allocation<Self, Self::Mode>) -> Self::Variable { - Self::Variable::new(ps, allocation) + fn variable(cs: &mut C, allocation: Allocation<Self, Self::Mode>) -> Self::Variable { + Self::Variable::new(cs, allocation) } - /// Allocates a new known variable into `ps` with the given `mode`. + /// Allocates a new known variable into `cs` with the given `mode`. #[inline] fn known( &self, - ps: &mut P, + cs: &mut C, mode: impl Into<<Self::Mode as AllocationMode>::Known>, ) -> Self::Variable { - Self::Variable::new_known(ps, self, mode) + Self::Variable::new_known(cs, self, mode) } - /// Allocates a new unknown variable into `ps` with the given `mode`. + /// Allocates a new unknown variable into `cs` with the given `mode`. #[inline] fn unknown( - ps: &mut P, + cs: &mut C, mode: impl Into<<Self::Mode as AllocationMode>::Unknown>, ) -> Self::Variable { - Self::Variable::new_unknown(ps, mode) + Self::Variable::new_unknown(cs, mode) } } @@ -805,33 +817,33 @@ pub mod reflection { } } - impl<P, T> HasVariable<T> for P + impl<C, T> HasVariable<T> for C where - P: ?Sized, - T: HasAllocation<P> + ?Sized, + C: ?Sized, + T: HasAllocation<C> + ?Sized, { type Variable = T::Variable; type Mode = T::Mode; } - /// Allocates a new unknown variable into `ps` with the given `mode`. + /// Allocates a new unknown variable into `cs` with the given `mode`. #[inline] - pub fn known<T, P>(ps: &mut P, value: &T, mode: KnownMode<T, P>) -> Var<T, P> + pub fn known<T, C>(cs: &mut C, value: &T, mode: KnownMode<T, C>) -> Var<T, C> where T: ?Sized, - P: HasVariable<T> + ?Sized, + C: HasVariable<T> + ?Sized, { - ps.new_known_allocation(value, mode) + cs.new_known_allocation(value, mode) } - /// Allocates a new unknown variable into `ps` with the given `mode`. + /// Allocates a new unknown variable into `cs` with the given `mode`. #[inline] - pub fn unknown<T, P>(ps: &mut P, mode: UnknownMode<T, P>) -> Var<T, P> + pub fn unknown<T, C>(cs: &mut C, mode: UnknownMode<T, C>) -> Var<T, C> where T: ?Sized, - P: HasVariable<T> + ?Sized, + C: HasVariable<T> + ?Sized, { - ps.new_unknown_allocation(mode) + cs.new_unknown_allocation(mode) } } @@ -842,50 +854,50 @@ pub mod types { use super::reflection::Var; /// Boolean Variable Type - pub type Bool<P> = Var<bool, P>; + pub type Bool<C> = Var<bool, C>; /// Character Variable Type - pub type Char<P> = Var<char, P>; + pub type Char<C> = Var<char, C>; /// 32-bit Floating Point Variable Type - pub type F32<P> = Var<f32, P>; + pub type F32<C> = Var<f32, C>; /// 64-bit Floating Point Variable Type - pub type F64<P> = Var<f64, P>; + pub type F64<C> = Var<f64, C>; /// Signed 8-bit Integer Variable Type - pub type I8<P> = Var<i8, P>; + pub type I8<C> = Var<i8, C>; /// Signed 16-bit Integer Variable Type - pub type I16<P> = Var<i16, P>; + pub type I16<C> = Var<i16, C>; /// Signed 32-bit Integer Variable Type - pub type I32<P> = Var<i32, P>; + pub type I32<C> = Var<i32, C>; /// Signed 64-bit Integer Variable Type - pub type I64<P> = Var<i64, P>; + pub type I64<C> = Var<i64, C>; /// Signed 128-bit Integer Variable Type - pub type I128<P> = Var<i128, P>; + pub type I128<C> = Var<i128, C>; /// Pointer-Sized Integer Variable Type - pub type Isize<P> = Var<isize, P>; + pub type Isize<C> = Var<isize, C>; /// Unsigned 8-bit Integer Variable Type - pub type U8<P> = Var<u8, P>; + pub type U8<C> = Var<u8, C>; /// Unsigned 16-bit Integer Variable Type - pub type U16<P> = Var<u16, P>; + pub type U16<C> = Var<u16, C>; /// Unsigned 32-bit Integer Variable Type - pub type U32<P> = Var<u32, P>; + pub type U32<C> = Var<u32, C>; /// Unsigned 64-bit Integer Variable Type - pub type U64<P> = Var<u64, P>; + pub type U64<C> = Var<u64, C>; /// Unsigned 128-bit Integer Variable Type - pub type U128<P> = Var<u128, P>; + pub type U128<C> = Var<u128, C>; /// Pointer-Sized Unsigned Integer Variable Type - pub type Usize<P> = Var<usize, P>; + pub type Usize<C> = Var<usize, C>; } diff --git a/manta-crypto/src/set/constraint.rs b/manta-crypto/src/set/constraint.rs index 2d435879f..e02f0cb3c 100644 --- a/manta-crypto/src/set/constraint.rs +++ b/manta-crypto/src/set/constraint.rs @@ -73,26 +73,26 @@ where } /// Containment Proof Variable -pub struct ContainmentProofVar<S, P> +pub struct ContainmentProofVar<S, C> where S: VerifiedSet + ?Sized, - P: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, + C: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, { /// Public Input - public_input: Var<S::Public, P>, + public_input: Var<S::Public, C>, /// Secret Witness - secret_witness: Var<S::Secret, P>, + secret_witness: Var<S::Secret, C>, } -impl<S, P> ContainmentProofVar<S, P> +impl<S, C> ContainmentProofVar<S, C> where S: VerifiedSet + ?Sized, - P: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, + C: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, { /// Builds a new [`ContainmentProofVar`] from `public_input` and `secret_witness`. #[inline] - pub fn new(public_input: Var<S::Public, P>, secret_witness: Var<S::Secret, P>) -> Self { + pub fn new(public_input: Var<S::Public, C>, secret_witness: Var<S::Secret, C>) -> Self { Self { public_input, secret_witness, @@ -101,79 +101,79 @@ where /// Asserts that `self` is a valid proof to the fact that `item` is stored in the verified set. #[inline] - pub fn assert_validity<V>(&self, set: &V, item: &V::ItemVar, ps: &mut P) + pub fn assert_validity<V>(&self, set: &V, item: &V::ItemVar, cs: &mut C) where - P: ConstraintSystem, - V: VerifiedSetVariable<P, Type = S>, + C: ConstraintSystem, + V: VerifiedSetVariable<C, Type = S>, { - set.assert_valid_containment_proof(&self.public_input, &self.secret_witness, item, ps) + set.assert_valid_containment_proof(&self.public_input, &self.secret_witness, item, cs) } } -impl<S, P> Variable<P> for ContainmentProofVar<S, P> +impl<S, C> Variable<C> for ContainmentProofVar<S, C> where S: VerifiedSet + ?Sized, - P: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, + C: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, { type Type = ContainmentProof<S>; - type Mode = ContainmentProofMode<Mode<S::Public, P>, Mode<S::Secret, P>>; + type Mode = ContainmentProofMode<Mode<S::Public, C>, Mode<S::Secret, C>>; #[inline] - fn new(ps: &mut P, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new(cs: &mut C, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self::new( - ps.allocate_known(&this.public_input, mode.public), - ps.allocate_known(&this.secret_witness, mode.secret), + cs.allocate_known(&this.public_input, mode.public), + cs.allocate_known(&this.secret_witness, mode.secret), ), Allocation::Unknown(mode) => Self::new( - unknown::<S::Public, _>(ps, mode.public), - unknown::<S::Secret, _>(ps, mode.secret), + unknown::<S::Public, _>(cs, mode.public), + unknown::<S::Secret, _>(cs, mode.secret), ), } } } -impl<S, P> HasAllocation<P> for ContainmentProof<S> +impl<S, C> HasAllocation<C> for ContainmentProof<S> where S: VerifiedSet + ?Sized, - P: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, + C: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, { - type Variable = ContainmentProofVar<S, P>; - type Mode = ContainmentProofMode<Mode<S::Public, P>, Mode<S::Secret, P>>; + type Variable = ContainmentProofVar<S, C>; + type Mode = ContainmentProofMode<Mode<S::Public, C>, Mode<S::Secret, C>>; } /// Public Input Type for [`VerifiedSetVariable`] -pub type PublicInputType<V, P> = <<V as Variable<P>>::Type as VerifiedSet>::Public; +pub type PublicInputType<V, C> = <<V as Variable<C>>::Type as VerifiedSet>::Public; /// Secret Witness Type for [`VerifiedSetVariable`] -pub type SecretWitnessType<V, P> = <<V as Variable<P>>::Type as VerifiedSet>::Secret; +pub type SecretWitnessType<V, C> = <<V as Variable<C>>::Type as VerifiedSet>::Secret; /// Public Input Variable for [`VerifiedSetVariable`] -pub type PublicInputVar<V, P> = Var<PublicInputType<V, P>, P>; +pub type PublicInputVar<V, C> = Var<PublicInputType<V, C>, C>; /// Secret Witness Variable for [`VerifiedSetVariable`] -pub type SecretWitnessVar<V, P> = Var<SecretWitnessType<V, P>, P>; +pub type SecretWitnessVar<V, C> = Var<SecretWitnessType<V, C>, C>; /// Verified Set Variable -pub trait VerifiedSetVariable<P>: Variable<P> +pub trait VerifiedSetVariable<C>: Variable<C> where - P: ConstraintSystem - + HasVariable<PublicInputType<Self, P>> - + HasVariable<SecretWitnessType<Self, P>> + C: ConstraintSystem + + HasVariable<PublicInputType<Self, C>> + + HasVariable<SecretWitnessType<Self, C>> + ?Sized, Self::Type: VerifiedSet, { /// Item Variable - type ItemVar: Variable<P, Type = <Self::Type as Set>::Item>; + type ItemVar: Variable<C, Type = <Self::Type as Set>::Item>; /// Asserts that `public_input` and `secret_witness` form a proof to the fact that `item` is /// stored in `self`. fn assert_valid_containment_proof( &self, - public_input: &PublicInputVar<Self, P>, - secret_witness: &SecretWitnessVar<Self, P>, + public_input: &PublicInputVar<Self, C>, + secret_witness: &SecretWitnessVar<Self, C>, item: &Self::ItemVar, - ps: &mut P, + cs: &mut C, ); } diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 279c7c551..ac9fd0ed5 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -30,7 +30,7 @@ std = [] [dependencies] aes-gcm = { version = "0.9.4", default-features = false } -ark-bls12-381 = { version = "0.3.0", default-features = false } +ark-bls12-381 = { version = "0.3.0", default-features = false, features = ["curve"] } ark-crypto-primitives = { version = "0.3.0", default-features = false, features = ["r1cs"] } ark-ec = { version = "0.3.0", default-features = false } ark-ed-on-bls12-381 = { version = "0.3.0", default-features = false, features = ["r1cs"] } diff --git a/manta-pay/src/accounting/config.rs b/manta-pay/src/accounting/config.rs index 1e3a29763..ee77b4775 100644 --- a/manta-pay/src/accounting/config.rs +++ b/manta-pay/src/accounting/config.rs @@ -20,12 +20,13 @@ use crate::{ accounting::ledger::{UtxoSet, UtxoSetVar}, crypto::{ commitment::pedersen::{self, PedersenWindow}, - constraint::{ArkProofSystem, AssetBalanceVar, AssetIdVar}, + constraint::{proof_systems::Groth16, ArkConstraintSystem, AssetBalanceVar, AssetIdVar}, ies::IES, prf::blake2s::{constraint::Blake2sVar, Blake2s}, rand::ChaCha20RngBlake2sSeedable, }, }; +use ark_bls12_381::Bls12_381; use ark_ed_on_bls12_381::{constraints::EdwardsVar, EdwardsProjective, Fq}; use manta_accounting::{ identity::{IdentityConfiguration, IdentityConstraintSystemConfiguration}, @@ -65,8 +66,11 @@ pub type PedersenCommitmentVar = pedersen::constraint::PedersenCommitmentVar< /// Constraint Field pub type ConstraintField = Fq; +/// Constraint System +pub type ConstraintSystem = ArkConstraintSystem<ConstraintField>; + /// Proof System -pub type ProofSystem = ArkProofSystem<ConstraintField>; +pub type ProofSystem = Groth16<Bls12_381>; /// Manta Pay Configuration #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -80,7 +84,7 @@ impl IdentityConfiguration for Configuration { } impl IdentityConstraintSystemConfiguration for Configuration { - type ConstraintSystem = ProofSystem; + type ConstraintSystem = ConstraintSystem; type SecretKeyVar = <Blake2sVar<ConstraintField> as PseudorandomFunctionFamily>::Seed; type PseudorandomFunctionFamilyInputVar = <Blake2sVar<ConstraintField> as PseudorandomFunctionFamily>::Input; @@ -93,6 +97,7 @@ impl IdentityConstraintSystemConfiguration for Configuration { } impl TransferConfiguration for Configuration { + type ConstraintSystem = ConstraintSystem; type ProofSystem = ProofSystem; type AssetIdVar = AssetIdVar<ConstraintField>; type AssetBalanceVar = AssetBalanceVar<ConstraintField>; diff --git a/manta-pay/src/accounting/ledger.rs b/manta-pay/src/accounting/ledger.rs index e09e4dd2a..f7f4fa2ff 100644 --- a/manta-pay/src/accounting/ledger.rs +++ b/manta-pay/src/accounting/ledger.rs @@ -20,8 +20,8 @@ use crate::{ accounting::config::{ - Configuration, PedersenCommitmentProjectiveCurve, PedersenCommitmentProjectiveCurveVar, - PedersenCommitmentWindowParameters, ProofSystem, + Configuration, ConstraintSystem, PedersenCommitmentProjectiveCurve, + PedersenCommitmentProjectiveCurveVar, PedersenCommitmentWindowParameters, ProofSystem, }, crypto::{ ies::EncryptedAsset, @@ -35,7 +35,9 @@ use blake2::{ }; use manta_accounting::{identity, Ledger as LedgerTrait, ProofPostError}; use manta_crypto::{ - constraint::{self, reflection::HasAllocation, Allocation, Constant, Variable}, + constraint::{ + self, reflection::HasAllocation, Allocation, Constant, ProofSystem as _, Variable, + }, set::{constraint::VerifiedSetVariable, ContainmentProof, Set, VerifiedSet}, }; use manta_util::{as_bytes, concatenate, into_array_unchecked}; @@ -222,24 +224,24 @@ impl VerifiedSet for UtxoSet { #[derive(Clone)] pub struct UtxoSetVar(ParametersVar); -impl Variable<ProofSystem> for UtxoSetVar { +impl Variable<ConstraintSystem> for UtxoSetVar { type Type = UtxoSet; type Mode = Constant; #[inline] - fn new(ps: &mut ProofSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new(ps: &mut ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { let (this, mode) = allocation.into_known(); Self(this.parameters.known(ps, mode)) } } -impl HasAllocation<ProofSystem> for UtxoSet { +impl HasAllocation<ConstraintSystem> for UtxoSet { type Variable = UtxoSetVar; type Mode = Constant; } -impl VerifiedSetVariable<ProofSystem> for UtxoSetVar { +impl VerifiedSetVariable<ConstraintSystem> for UtxoSetVar { type ItemVar = UtxoVar; #[inline] @@ -248,9 +250,9 @@ impl VerifiedSetVariable<ProofSystem> for UtxoSetVar { public_input: &RootVar, secret_witness: &PathVar, item: &UtxoVar, - ps: &mut ProofSystem, + cs: &mut ConstraintSystem, ) { - let _ = ps; + let _ = cs; self.0 .assert_verified(public_input, secret_witness, &concatenate!(item)) } @@ -266,6 +268,9 @@ pub struct Ledger { /// Encrypted Assets encrypted_assets: Vec<EncryptedAsset>, + + /// Verifying Context + verifying_context: <ProofSystem as constraint::ProofSystem>::VerifyingContext, } impl LedgerTrait for Ledger { @@ -320,7 +325,10 @@ impl LedgerTrait for Ledger { &self, proof: <Self::ProofSystem as constraint::ProofSystem>::Proof, ) -> Result<(), ProofPostError<Self>> { - let _ = proof; - todo!() + match Self::ProofSystem::verify_proof(&self.verifying_context, &proof) { + Ok(true) => Ok(()), + Ok(false) => Err(ProofPostError::InvalidProof(proof, None)), + Err(err) => Err(ProofPostError::InvalidProof(proof, Some(err))), + } } } diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs index d3eec83d4..a7e230d0e 100644 --- a/manta-pay/src/crypto/commitment/pedersen.rs +++ b/manta-pay/src/crypto/commitment/pedersen.rs @@ -137,7 +137,7 @@ where /// Pedersen Commitment Scheme Constraint System Implementation pub mod constraint { use super::*; - use crate::crypto::constraint::{empty, full, ArkProofSystem}; + use crate::crypto::constraint::{empty, full, ArkConstraintSystem}; use ark_crypto_primitives::{ commitment::pedersen::constraints::{CommGadget, ParametersVar, RandomnessVar}, CommitmentGadget, @@ -158,8 +158,8 @@ pub mod constraint { /// Constraint Field Type pub type ConstraintField<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField; - /// Proof System Type - type ProofSystem<C> = ArkProofSystem<ConstraintField<C>>; + /// Constraint System Type + pub type ConstraintSystem<C> = ArkConstraintSystem<ConstraintField<C>>; /// Input Buffer Type type InputBuffer<F> = Vec<UInt8<F>>; @@ -308,7 +308,7 @@ pub mod constraint { W: PedersenWindow, C: ProjectiveCurve; - impl<W, C> Variable<ProofSystem<C>> for PedersenCommitmentRandomnessVar<W, C> + impl<W, C> Variable<ConstraintSystem<C>> for PedersenCommitmentRandomnessVar<W, C> where W: PedersenWindow, C: ProjectiveCurve, @@ -318,15 +318,18 @@ pub mod constraint { type Mode = Secret; #[inline] - fn new(ps: &mut ProofSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new( + cs: &mut ConstraintSystem<C>, + allocation: Allocation<Self::Type, Self::Mode>, + ) -> Self { Self( match allocation.known() { Some((this, _)) => RandomnessVar::new_witness( - ns!(ps.cs, "pedersen commitment randomness secret witness"), + ns!(cs.cs, "pedersen commitment randomness secret witness"), full(&this.0), ), _ => RandomnessVar::new_witness( - ns!(ps.cs, "pedersen commitment randomness secret witness"), + ns!(cs.cs, "pedersen commitment randomness secret witness"), empty::<ArkPedersenCommitmentRandomness<W, C>>, ), } @@ -336,7 +339,7 @@ pub mod constraint { } } - impl<W, C> HasAllocation<ProofSystem<C>> for PedersenCommitmentRandomness<W, C> + impl<W, C> HasAllocation<ConstraintSystem<C>> for PedersenCommitmentRandomness<W, C> where W: PedersenWindow, C: ProjectiveCurve, @@ -387,7 +390,7 @@ pub mod constraint { } } - impl<W, C, GG> Variable<ProofSystem<C>> for PedersenCommitmentOutputVar<W, C, GG> + impl<W, C, GG> Variable<ConstraintSystem<C>> for PedersenCommitmentOutputVar<W, C, GG> where W: PedersenWindow, C: ProjectiveCurve, @@ -399,27 +402,30 @@ pub mod constraint { type Mode = PublicOrSecret; #[inline] - fn new(ps: &mut ProofSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new( + cs: &mut ConstraintSystem<C>, + allocation: Allocation<Self::Type, Self::Mode>, + ) -> Self { Self( match allocation { Allocation::Known(this, PublicOrSecret::Public) => { AllocVar::<ArkPedersenCommitmentOutput<W, C>, _>::new_input( - ns!(ps.cs, "pedersen commitment output public input"), + ns!(cs.cs, "pedersen commitment output public input"), full(&(this.0).0), ) } Allocation::Known(this, PublicOrSecret::Secret) => { AllocVar::<ArkPedersenCommitmentOutput<W, C>, _>::new_witness( - ns!(ps.cs, "pedersen commitment output secret witness"), + ns!(cs.cs, "pedersen commitment output secret witness"), full(&(this.0).0), ) } Allocation::Unknown(PublicOrSecret::Public) => GG::new_input( - ns!(ps.cs, "pedersen commitment output public input"), + ns!(cs.cs, "pedersen commitment output public input"), empty::<ArkPedersenCommitmentOutput<W, C>>, ), Allocation::Unknown(PublicOrSecret::Secret) => GG::new_witness( - ns!(ps.cs, "pedersen commitment output secret witness"), + ns!(cs.cs, "pedersen commitment output secret witness"), empty::<ArkPedersenCommitmentOutput<W, C>>, ), } @@ -429,7 +435,7 @@ pub mod constraint { } } - impl<W, C, GG> HasAllocation<ProofSystem<C>> for PedersenCommitmentOutputWrapper<W, C, GG> + impl<W, C, GG> HasAllocation<ConstraintSystem<C>> for PedersenCommitmentOutputWrapper<W, C, GG> where W: PedersenWindow, C: ProjectiveCurve, @@ -440,7 +446,7 @@ pub mod constraint { type Mode = PublicOrSecret; } - impl<W, C, GG> Equal<ProofSystem<C>> for PedersenCommitmentOutputVar<W, C, GG> + impl<W, C, GG> Equal<ConstraintSystem<C>> for PedersenCommitmentOutputVar<W, C, GG> where W: PedersenWindow, C: ProjectiveCurve, @@ -448,8 +454,8 @@ pub mod constraint { for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, { #[inline] - fn eq(ps: &mut ProofSystem<C>, lhs: &Self, rhs: &Self) -> Bool<ProofSystem<C>> { - let _ = ps; + fn eq(cs: &mut ConstraintSystem<C>, lhs: &Self, rhs: &Self) -> Bool<ConstraintSystem<C>> { + let _ = cs; lhs.0 .is_eq(&rhs.0) .expect("Equality checking is not allowed to fail.") @@ -466,7 +472,7 @@ pub mod constraint { GG: CurveVar<C, ConstraintField<C>>, for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; - impl<W, C, GG> Variable<ProofSystem<C>> for PedersenCommitmentVar<W, C, GG> + impl<W, C, GG> Variable<ConstraintSystem<C>> for PedersenCommitmentVar<W, C, GG> where W: PedersenWindow, C: ProjectiveCurve, @@ -478,11 +484,14 @@ pub mod constraint { type Mode = Constant; #[inline] - fn new(ps: &mut ProofSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new( + cs: &mut ConstraintSystem<C>, + allocation: Allocation<Self::Type, Self::Mode>, + ) -> Self { let (this, _) = allocation.into_known(); Self( ParametersVar::new_constant( - ns!(ps.cs, "pedersen commitment paramters constant"), + ns!(cs.cs, "pedersen commitment paramters constant"), &(this.0).0, ) .expect("Variable allocation is not allowed to fail."), @@ -491,8 +500,7 @@ pub mod constraint { } } - impl<W, C, GG> HasAllocation<ArkProofSystem<ConstraintField<C>>> - for PedersenCommitmentWrapper<W, C, GG> + impl<W, C, GG> HasAllocation<ConstraintSystem<C>> for PedersenCommitmentWrapper<W, C, GG> where W: PedersenWindow, C: ProjectiveCurve, diff --git a/manta-pay/src/crypto/constraint/proof_system.rs b/manta-pay/src/crypto/constraint/constraint_system.rs similarity index 79% rename from manta-pay/src/crypto/constraint/proof_system.rs rename to manta-pay/src/crypto/constraint/constraint_system.rs index 6430821ca..943088497 100644 --- a/manta-pay/src/crypto/constraint/proof_system.rs +++ b/manta-pay/src/crypto/constraint/constraint_system.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Arkworks Proof System Implementation +//! Arkworks Constraint System Implementation use alloc::{vec, vec::Vec}; use ark_ff::{fields::Field, PrimeField}; @@ -22,28 +22,22 @@ use ark_r1cs_std::{ alloc::AllocVar, bits::boolean::Boolean, eq::EqGadget, fields::fp::FpVar, uint8::UInt8, R1CSVar, ToBytesGadget, }; -use ark_relations::{ - ns, - r1cs::{ - ConstraintSystem as ArkConstraintSystem, ConstraintSystemRef as ArkConstraintSystemRef, - SynthesisError, - }, -}; +use ark_relations::{ns, r1cs as ark_r1cs}; use core::{borrow::Borrow, ops::AddAssign}; use manta_accounting::{AssetBalance, AssetId}; use manta_crypto::constraint::{ reflection::HasAllocation, types::Bool, Allocation, AllocationMode, ConstraintSystem, Equal, - ProofSystem, Public, PublicOrSecret, Secret, Variable, Verifier, + Public, PublicOrSecret, Secret, Variable, }; use manta_util::{Concat, ConcatAccumulator}; /// Synthesis Result -type SynthesisResult<T> = Result<T, SynthesisError>; +type SynthesisResult<T> = Result<T, ark_r1cs::SynthesisError>; /// Returns an empty variable assignment. #[inline] pub(crate) const fn empty<T>() -> SynthesisResult<T> { - Err(SynthesisError::AssignmentMissing) + Err(ark_r1cs::SynthesisError::AssignmentMissing) } /// Returns a filled variable assignment. @@ -96,16 +90,16 @@ impl From<PublicOrSecret> for ArkAllocationMode { } } -/// Arkworks Proof System -pub struct ArkProofSystem<F> +/// Arkworks Constraint System +pub struct ArkConstraintSystem<F> where F: Field, { /// Constraint System - pub(crate) cs: ArkConstraintSystemRef<F>, + pub(crate) cs: ark_r1cs::ConstraintSystemRef<F>, } -impl<F> ConstraintSystem for ArkProofSystem<F> +impl<F> ConstraintSystem for ArkConstraintSystem<F> where F: Field, { @@ -119,13 +113,14 @@ where } } -impl<F> ProofSystem for ArkProofSystem<F> +/* TODO: +impl<F> ProofSystem for Groth16ProofSystem<F> where F: Field, { type Verifier = Groth16Verifier; - type Proof = (); + type Proof = (Vec<F>, ()); type Error = (); @@ -146,29 +141,19 @@ where #[inline] fn into_proof(self) -> Result<Self::Proof, Self::Error> { + let input = self.cs.borrow().ok_or(())?.instance_assignment; + use rand::SeedableRng; + let mut rng = rand_chacha::ChaCha20Rng::from_seed([0; 32]); + let _ = ark_groth16::create_random_proof(|| {}, (), &mut rng); todo!() } } -/// Arkworks Groth 16 Verifier -pub struct Groth16Verifier; - -impl<F> Verifier<ArkProofSystem<F>> for Groth16Verifier -where - F: Field, -{ - type Error = (); - - #[inline] - fn verify( - &self, - proof: &<ArkProofSystem<F> as ProofSystem>::Proof, - ) -> Result<bool, Self::Error> { - todo!() - } -} +/// Arkworks Groth 16 Proof System +pub struct Groth16ProofSystem; +*/ -impl<F> Variable<ArkProofSystem<F>> for Boolean<F> +impl<F> Variable<ArkConstraintSystem<F>> for Boolean<F> where F: Field, { @@ -177,7 +162,10 @@ where type Mode = ArkAllocationMode; #[inline] - fn new(ps: &mut ArkProofSystem<F>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new( + ps: &mut ArkConstraintSystem<F>, + allocation: Allocation<Self::Type, Self::Mode>, + ) -> Self { match allocation { Allocation::Known(this, mode) => match mode { ArkAllocationMode::Constant => { @@ -203,7 +191,7 @@ where } } -impl<F> HasAllocation<ArkProofSystem<F>> for bool +impl<F> HasAllocation<ArkConstraintSystem<F>> for bool where F: Field, { @@ -211,12 +199,12 @@ where type Mode = ArkAllocationMode; } -impl<F> Equal<ArkProofSystem<F>> for Boolean<F> +impl<F> Equal<ArkConstraintSystem<F>> for Boolean<F> where F: Field, { #[inline] - fn eq(ps: &mut ArkProofSystem<F>, lhs: &Self, rhs: &Self) -> Boolean<F> { + fn eq(ps: &mut ArkConstraintSystem<F>, lhs: &Self, rhs: &Self) -> Boolean<F> { let _ = ps; lhs.is_eq(rhs) .expect("Equality checking is not allowed to fail.") @@ -230,7 +218,7 @@ pub struct Fp<F>(F) where F: PrimeField; -impl<F> Variable<ArkProofSystem<F>> for FpVar<F> +impl<F> Variable<ArkConstraintSystem<F>> for FpVar<F> where F: PrimeField, { @@ -239,7 +227,10 @@ where type Mode = ArkAllocationMode; #[inline] - fn new(ps: &mut ArkProofSystem<F>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new( + ps: &mut ArkConstraintSystem<F>, + allocation: Allocation<Self::Type, Self::Mode>, + ) -> Self { match allocation { Allocation::Known(this, ArkAllocationMode::Constant) => { Self::new_constant(ns!(ps.cs, "prime field constant"), this.0) @@ -261,7 +252,7 @@ where } } -impl<F> HasAllocation<ArkProofSystem<F>> for Fp<F> +impl<F> HasAllocation<ArkConstraintSystem<F>> for Fp<F> where F: PrimeField, { @@ -282,14 +273,14 @@ where { /// Returns an reference to the internal arkworks constriant system. #[inline] - pub(crate) fn constraint_system_ref(&self) -> ArkConstraintSystemRef<F> { + pub(crate) fn constraint_system_ref(&self) -> ark_r1cs::ConstraintSystemRef<F> { self.0.cs() } /// Allocates a new byte vector according to the `allocation` entry. #[inline] pub(crate) fn allocate( - cs: &ArkConstraintSystemRef<F>, + cs: &ark_r1cs::ConstraintSystemRef<F>, allocation: Allocation<[u8; N], PublicOrSecret>, ) -> Self where @@ -351,7 +342,7 @@ where } } -impl<F, const N: usize> Variable<ArkProofSystem<F>> for ByteArrayVar<F, N> +impl<F, const N: usize> Variable<ArkConstraintSystem<F>> for ByteArrayVar<F, N> where F: PrimeField, { @@ -360,12 +351,15 @@ where type Mode = PublicOrSecret; #[inline] - fn new(ps: &mut ArkProofSystem<F>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new( + ps: &mut ArkConstraintSystem<F>, + allocation: Allocation<Self::Type, Self::Mode>, + ) -> Self { Self::allocate(&ps.cs, allocation) } } -impl<F, const N: usize> HasAllocation<ArkProofSystem<F>> for [u8; N] +impl<F, const N: usize> HasAllocation<ArkConstraintSystem<F>> for [u8; N] where F: PrimeField, { @@ -395,7 +389,7 @@ where } } -impl<F> Variable<ArkProofSystem<F>> for AssetIdVar<F> +impl<F> Variable<ArkConstraintSystem<F>> for AssetIdVar<F> where F: PrimeField, { @@ -404,12 +398,15 @@ where type Mode = PublicOrSecret; #[inline] - fn new(ps: &mut ArkProofSystem<F>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new( + ps: &mut ArkConstraintSystem<F>, + allocation: Allocation<Self::Type, Self::Mode>, + ) -> Self { Self(allocation.map_allocate(ps, move |this| Fp(F::from(this.0)))) } } -impl<F> HasAllocation<ArkProofSystem<F>> for AssetId +impl<F> HasAllocation<ArkConstraintSystem<F>> for AssetId where F: PrimeField, { @@ -417,12 +414,12 @@ where type Mode = PublicOrSecret; } -impl<F> Equal<ArkProofSystem<F>> for AssetIdVar<F> +impl<F> Equal<ArkConstraintSystem<F>> for AssetIdVar<F> where F: PrimeField, { #[inline] - fn eq(ps: &mut ArkProofSystem<F>, lhs: &Self, rhs: &Self) -> Boolean<F> { + fn eq(ps: &mut ArkConstraintSystem<F>, lhs: &Self, rhs: &Self) -> Boolean<F> { let _ = ps; lhs.0 .is_eq(&rhs.0) @@ -452,7 +449,7 @@ where } } -impl<F> Variable<ArkProofSystem<F>> for AssetBalanceVar<F> +impl<F> Variable<ArkConstraintSystem<F>> for AssetBalanceVar<F> where F: PrimeField, { @@ -461,12 +458,15 @@ where type Mode = PublicOrSecret; #[inline] - fn new(ps: &mut ArkProofSystem<F>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new( + ps: &mut ArkConstraintSystem<F>, + allocation: Allocation<Self::Type, Self::Mode>, + ) -> Self { Self(allocation.map_allocate(ps, move |this| Fp(F::from(this.0)))) } } -impl<F> HasAllocation<ArkProofSystem<F>> for AssetBalance +impl<F> HasAllocation<ArkConstraintSystem<F>> for AssetBalance where F: PrimeField, { @@ -474,12 +474,12 @@ where type Mode = PublicOrSecret; } -impl<F> Equal<ArkProofSystem<F>> for AssetBalanceVar<F> +impl<F> Equal<ArkConstraintSystem<F>> for AssetBalanceVar<F> where F: PrimeField, { #[inline] - fn eq(ps: &mut ArkProofSystem<F>, lhs: &Self, rhs: &Self) -> Boolean<F> { + fn eq(ps: &mut ArkConstraintSystem<F>, lhs: &Self, rhs: &Self) -> Boolean<F> { let _ = ps; lhs.0 .is_eq(&rhs.0) diff --git a/manta-pay/src/crypto/constraint/mod.rs b/manta-pay/src/crypto/constraint/mod.rs index c04b76623..551a479b5 100644 --- a/manta-pay/src/crypto/constraint/mod.rs +++ b/manta-pay/src/crypto/constraint/mod.rs @@ -14,8 +14,10 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Constraint System Implementations +//! Constraint System and Proof System Implementations -mod proof_system; +mod constraint_system; -pub use proof_system::*; +pub mod proof_systems; + +pub use constraint_system::*; diff --git a/manta-pay/src/crypto/constraint/proof_systems/groth16.rs b/manta-pay/src/crypto/constraint/proof_systems/groth16.rs new file mode 100644 index 000000000..850017413 --- /dev/null +++ b/manta-pay/src/crypto/constraint/proof_systems/groth16.rs @@ -0,0 +1,99 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Arkworks Groth16 Implementation + +use crate::crypto::constraint::ArkConstraintSystem; +use alloc::vec::Vec; +use ark_ec::PairingEngine; +use ark_groth16::{ + create_random_proof, generate_random_parameters, verify_proof, PreparedVerifyingKey, Proof, + ProvingKey, +}; +use ark_relations::r1cs::SynthesisError; +use core::marker::PhantomData; +use manta_crypto::constraint::ProofSystem; +use rand::{CryptoRng, RngCore}; + +/// Arkworks Groth 16 Proof System +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Groth16<E>(PhantomData<E>) +where + E: PairingEngine; + +impl<E> ProofSystem for Groth16<E> +where + E: PairingEngine, +{ + type ConstraintSystem = ArkConstraintSystem<E::Fr>; + + type ProvingContext = ProvingKey<E>; + + type VerifyingContext = PreparedVerifyingKey<E>; + + type Proof = (Vec<E::Fr>, Proof<E>); + + type Error = SynthesisError; + + #[inline] + fn for_unknown() -> Self::ConstraintSystem { + todo!() + } + + #[inline] + fn for_known() -> Self::ConstraintSystem { + todo!() + } + + #[inline] + fn generate_context<R>( + cs: Self::ConstraintSystem, + rng: &mut R, + ) -> Result<(Self::ProvingContext, Self::VerifyingContext), Self::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + /* TODO: + let _ = generate_random_parameters(|| {}, rng)?; + */ + todo!() + } + + #[inline] + fn generate_proof<R>( + cs: Self::ConstraintSystem, + context: &Self::ProvingContext, + rng: &mut R, + ) -> Result<Self::Proof, Self::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + /* TODO: + let input = self.cs.borrow().ok_or(())?.instance_assignment; + let _ = create_random_proof(|| {}, context, &mut rng)?; + */ + todo!() + } + + #[inline] + fn verify_proof( + context: &Self::VerifyingContext, + proof: &Self::Proof, + ) -> Result<bool, Self::Error> { + verify_proof(context, &proof.1, &proof.0) + } +} diff --git a/manta-pay/src/crypto/constraint/proof_systems/mod.rs b/manta-pay/src/crypto/constraint/proof_systems/mod.rs new file mode 100644 index 000000000..0e7aa4087 --- /dev/null +++ b/manta-pay/src/crypto/constraint/proof_systems/mod.rs @@ -0,0 +1,21 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Arkworks Proof System Implementations + +mod groth16; + +pub use groth16::*; diff --git a/manta-pay/src/crypto/merkle_tree/basic.rs b/manta-pay/src/crypto/merkle_tree/basic.rs index 389b07ecf..6f9100eed 100644 --- a/manta-pay/src/crypto/merkle_tree/basic.rs +++ b/manta-pay/src/crypto/merkle_tree/basic.rs @@ -26,7 +26,7 @@ use crate::crypto::{ commitment::pedersen::{PedersenWindow, ProjectiveCurve}, - constraint::{empty, full, ArkProofSystem}, + constraint::{empty, full, ArkConstraintSystem}, }; use alloc::vec::Vec; use ark_crypto_primitives::{ @@ -59,8 +59,8 @@ use manta_util::{as_bytes, Concat}; /// Constraint Field Type pub type ConstraintField<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField; -/// Proof System -type ProofSystem<C> = ArkProofSystem<ConstraintField<C>>; +/// Constraint System Type +pub type ContraintSystem<C> = ArkConstraintSystem<ConstraintField<C>>; /// Merkle Tree Configuration #[derive(derivative::Derivative)] @@ -216,7 +216,7 @@ where } } -impl<W, C, GG> Variable<ProofSystem<C>> for ParametersVar<W, C, GG> +impl<W, C, GG> Variable<ContraintSystem<C>> for ParametersVar<W, C, GG> where W: PedersenWindow, C: ProjectiveCurve, @@ -228,16 +228,16 @@ where type Mode = Constant; #[inline] - fn new(ps: &mut ProofSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new(cs: &mut ContraintSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { let (this, _) = allocation.into_known(); ParametersVar { leaf: LeafParamVar::<W, _, _>::new_constant( - ns!(ps.cs, "leaf hash parameter constant"), + ns!(cs.cs, "leaf hash parameter constant"), &this.0.leaf, ) .expect("Variable allocation is not allowed to fail."), two_to_one: TwoToOneParamVar::<W, _, _>::new_constant( - ns!(ps.cs, "two-to-one hash parameter constant"), + ns!(cs.cs, "two-to-one hash parameter constant"), &this.0.two_to_one, ) .expect("Variable allocation is not allowed to fail."), @@ -245,7 +245,7 @@ where } } -impl<W, C, GG> HasAllocation<ProofSystem<C>> for ParametersWrapper<W, C, GG> +impl<W, C, GG> HasAllocation<ContraintSystem<C>> for ParametersWrapper<W, C, GG> where W: PedersenWindow, C: ProjectiveCurve, @@ -288,7 +288,7 @@ where GG: CurveVar<C, ConstraintField<C>>, for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; -impl<W, C, GG> Variable<ProofSystem<C>> for RootVar<W, C, GG> +impl<W, C, GG> Variable<ContraintSystem<C>> for RootVar<W, C, GG> where W: PedersenWindow, C: ProjectiveCurve, @@ -300,15 +300,15 @@ where type Mode = Public; #[inline] - fn new(ps: &mut ProofSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new(cs: &mut ContraintSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { RootVar( match allocation.known() { Some((this, _)) => AllocVar::<RootInnerType<W, C>, _>::new_input( - ns!(ps.cs, "merkle tree root public input"), + ns!(cs.cs, "merkle tree root public input"), full((this.0).0), ), _ => AllocVar::<RootInnerType<W, C>, _>::new_input( - ns!(ps.cs, "merkle tree root public input"), + ns!(cs.cs, "merkle tree root public input"), empty::<RootInnerType<W, C>>, ), } @@ -318,7 +318,7 @@ where } } -impl<W, C, GG> HasAllocation<ProofSystem<C>> for RootWrapper<W, C, GG> +impl<W, C, GG> HasAllocation<ContraintSystem<C>> for RootWrapper<W, C, GG> where W: PedersenWindow, C: ProjectiveCurve, @@ -362,7 +362,7 @@ where GG: CurveVar<C, ConstraintField<C>>, for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; -impl<W, C, GG> Variable<ProofSystem<C>> for PathVar<W, C, GG> +impl<W, C, GG> Variable<ContraintSystem<C>> for PathVar<W, C, GG> where W: PedersenWindow, C: ProjectiveCurve, @@ -374,18 +374,18 @@ where type Mode = Secret; #[inline] - fn new(ps: &mut ProofSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new(cs: &mut ContraintSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { PathVar( match allocation.known() { - Some((this, _)) => PathVarInnerType::new_witness(ns!(ps.cs, ""), full(&(this.0).0)), - _ => PathVarInnerType::new_witness(ns!(ps.cs, ""), empty::<PathInnerType<W, C>>), + Some((this, _)) => PathVarInnerType::new_witness(ns!(cs.cs, ""), full(&(this.0).0)), + _ => PathVarInnerType::new_witness(ns!(cs.cs, ""), empty::<PathInnerType<W, C>>), } .expect("Variable allocation is not allowed to fail."), ) } } -impl<W, C, GG> HasAllocation<ProofSystem<C>> for PathWrapper<W, C, GG> +impl<W, C, GG> HasAllocation<ContraintSystem<C>> for PathWrapper<W, C, GG> where W: PedersenWindow, C: ProjectiveCurve, diff --git a/manta-pay/src/crypto/prf/blake2s.rs b/manta-pay/src/crypto/prf/blake2s.rs index 9f48b1ca9..e5873c139 100644 --- a/manta-pay/src/crypto/prf/blake2s.rs +++ b/manta-pay/src/crypto/prf/blake2s.rs @@ -103,7 +103,7 @@ impl PseudorandomFunctionFamily for Blake2s { /// Blake2s PRF Constraint System Implementations pub mod constraint { use super::*; - use crate::crypto::constraint::{empty, full, ArkProofSystem as ProofSystem, ByteArrayVar}; + use crate::crypto::constraint::{empty, full, ByteArrayVar}; use ark_crypto_primitives::{ prf::blake2s::constraints::Blake2sGadget as ArkBlake2sVar, PRFGadget, }; @@ -116,6 +116,9 @@ pub mod constraint { Secret, Variable, }; + /// Constraint System Type + pub use crate::crypto::constraint::ArkConstraintSystem as ConstraintSystem; + /// Blake2s Pseudorandom Function Family Seed Variable #[derive(derivative::Derivative)] #[derivative(Clone)] @@ -138,7 +141,7 @@ pub mod constraint { } } - impl<F> Variable<ProofSystem<F>> for Blake2sSeedVar<F> + impl<F> Variable<ConstraintSystem<F>> for Blake2sSeedVar<F> where F: PrimeField, { @@ -147,12 +150,15 @@ pub mod constraint { type Mode = Secret; #[inline] - fn new(ps: &mut ProofSystem<F>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - Self(allocation.map_allocate(ps, move |this| this.0)) + fn new( + cs: &mut ConstraintSystem<F>, + allocation: Allocation<Self::Type, Self::Mode>, + ) -> Self { + Self(allocation.map_allocate(cs, move |this| this.0)) } } - impl<F> HasAllocation<ProofSystem<F>> for Blake2sSeed + impl<F> HasAllocation<ConstraintSystem<F>> for Blake2sSeed where F: PrimeField, { @@ -182,7 +188,7 @@ pub mod constraint { } } - impl<F> Variable<ProofSystem<F>> for Blake2sInputVar<F> + impl<F> Variable<ConstraintSystem<F>> for Blake2sInputVar<F> where F: PrimeField, { @@ -191,12 +197,15 @@ pub mod constraint { type Mode = Secret; #[inline] - fn new(ps: &mut ProofSystem<F>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - Self(allocation.map_allocate(ps, move |this| this.0)) + fn new( + cs: &mut ConstraintSystem<F>, + allocation: Allocation<Self::Type, Self::Mode>, + ) -> Self { + Self(allocation.map_allocate(cs, move |this| this.0)) } } - impl<F> HasAllocation<ProofSystem<F>> for Blake2sInput + impl<F> HasAllocation<ConstraintSystem<F>> for Blake2sInput where F: PrimeField, { @@ -229,7 +238,7 @@ pub mod constraint { } } - impl<F> Variable<ProofSystem<F>> for Blake2sOutputVar<F> + impl<F> Variable<ConstraintSystem<F>> for Blake2sOutputVar<F> where F: PrimeField, { @@ -238,26 +247,29 @@ pub mod constraint { type Mode = PublicOrSecret; #[inline] - fn new(ps: &mut ProofSystem<F>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new( + cs: &mut ConstraintSystem<F>, + allocation: Allocation<Self::Type, Self::Mode>, + ) -> Self { Self( match allocation { Allocation::Known(this, mode) => match mode { PublicOrSecret::Public => Blake2sOutputVarInnerType::new_input( - ns!(ps.cs, "blake2s output public input"), + ns!(cs.cs, "blake2s output public input"), full(this.0), ), PublicOrSecret::Secret => Blake2sOutputVarInnerType::new_witness( - ns!(ps.cs, "blake2s output secret witness"), + ns!(cs.cs, "blake2s output secret witness"), full(this.0), ), }, Allocation::Unknown(mode) => match mode { PublicOrSecret::Public => Blake2sOutputVarInnerType::new_input( - ns!(ps.cs, "blake2s output public input"), + ns!(cs.cs, "blake2s output public input"), empty::<<ArkBlake2s as PRF>::Output>, ), PublicOrSecret::Secret => Blake2sOutputVarInnerType::new_witness( - ns!(ps.cs, "blake2s output secret witness"), + ns!(cs.cs, "blake2s output secret witness"), empty::<<ArkBlake2s as PRF>::Output>, ), }, @@ -267,7 +279,7 @@ pub mod constraint { } } - impl<F> HasAllocation<ProofSystem<F>> for Blake2sOutput + impl<F> HasAllocation<ConstraintSystem<F>> for Blake2sOutput where F: PrimeField, { @@ -275,13 +287,13 @@ pub mod constraint { type Mode = PublicOrSecret; } - impl<F> Equal<ProofSystem<F>> for Blake2sOutputVar<F> + impl<F> Equal<ConstraintSystem<F>> for Blake2sOutputVar<F> where F: PrimeField, { #[inline] - fn eq(ps: &mut ProofSystem<F>, lhs: &Self, rhs: &Self) -> Bool<ProofSystem<F>> { - let _ = ps; + fn eq(cs: &mut ConstraintSystem<F>, lhs: &Self, rhs: &Self) -> Bool<ConstraintSystem<F>> { + let _ = cs; lhs.0 .is_eq(&rhs.0) .expect("Equality checking is not allowed to fail.") @@ -295,7 +307,7 @@ pub mod constraint { where F: PrimeField; - impl<F> Variable<ProofSystem<F>> for Blake2sVar<F> + impl<F> Variable<ConstraintSystem<F>> for Blake2sVar<F> where F: PrimeField, { @@ -304,13 +316,16 @@ pub mod constraint { type Mode = Constant; #[inline] - fn new(ps: &mut ProofSystem<F>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - let _ = (ps, allocation); + fn new( + cs: &mut ConstraintSystem<F>, + allocation: Allocation<Self::Type, Self::Mode>, + ) -> Self { + let _ = (cs, allocation); Default::default() } } - impl<F> HasAllocation<ProofSystem<F>> for Blake2s + impl<F> HasAllocation<ConstraintSystem<F>> for Blake2s where F: PrimeField, { From 6f494bbc5c029f0bbaf05cabbffd6e732d1a5274 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 17 Sep 2021 03:08:21 -0400 Subject: [PATCH 038/275] implement groth16 backend, cocoon wallet adapters --- manta-accounting/Cargo.toml | 8 + manta-accounting/src/asset.rs | 30 ++ manta-accounting/src/fs.rs | 293 ++++++++++++++++++ manta-accounting/src/identity.rs | 4 +- manta-accounting/src/lib.rs | 6 +- manta-accounting/src/transfer.rs | 8 +- manta-accounting/src/wallet.rs | 41 +++ manta-crypto/src/commitment.rs | 51 +++ manta-crypto/src/constraint.rs | 39 ++- manta-crypto/src/ies.rs | 6 +- manta-crypto/src/set/mod.rs | 5 + .../crypto/constraint/constraint_system.rs | 62 ++-- .../constraint/proof_systems/groth16.rs | 86 +++-- manta-pay/src/crypto/ies.rs | 7 +- 14 files changed, 567 insertions(+), 79 deletions(-) create mode 100644 manta-accounting/src/fs.rs diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 637a546ef..6adcfe3fd 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -25,11 +25,19 @@ is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } [features] +cocoon-adapters = ["cocoon/std", "zeroize"] +std = ["cocoon-adapters"] test = [] [dependencies] +cocoon = { version = "0.2.4", optional = true, default-features = false } derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } derive_more = { version = "0.99.16", default-features = false, features = ["add", "add_assign", "display", "from", "mul", "mul_assign", "sum"] } manta-crypto = { path = "../manta-crypto", default-features = false } manta-util = { path = "../manta-util", default-features = false } rand = { version = "0.8.4", default-features = false } +zeroize = { version = "1.3.0", optional = true, default-features = false, features = ["alloc", "zeroize_derive"] } + +[dev-dependencies] +rand = "0.8.4" +tempfile = "3.2.0" diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index dd5be116d..5a4fd1b34 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -488,3 +488,33 @@ impl<const N: usize> TryFrom<[Asset; N]> for AssetCollection<N> { } } } + +/// Testing Suite +#[cfg(test)] +mod test { + use super::*; + use rand::{thread_rng, Rng}; + + /// Tests asset conversion into and from bytes. + #[test] + fn asset_into_and_from_bytes() { + let mut rng = thread_rng(); + let asset = rng.gen::<Asset>(); + assert_eq!(asset, Asset::from_bytes(asset.into_bytes())); + let mut asset_bytes = [0; Asset::SIZE]; + rng.fill_bytes(&mut asset_bytes); + assert_eq!(asset_bytes, Asset::from_bytes(asset_bytes).into_bytes()); + } + + /// Tests asset arithmetic. + #[test] + fn asset_arithmetic() { + let mut rng = thread_rng(); + let mut asset = Asset::new(rng.gen(), AssetBalance(0)); + let value = rng.gen::<AssetBalance>(); + let _ = asset + value; + asset += value; + let _ = asset - value; + asset -= value; + } +} diff --git a/manta-accounting/src/fs.rs b/manta-accounting/src/fs.rs new file mode 100644 index 000000000..ced71e5a7 --- /dev/null +++ b/manta-accounting/src/fs.rs @@ -0,0 +1,293 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Encrypted Filesystem Primitives + +/// Filesystem Encrypted Loading +pub trait Load: Sized { + /// Path Type + type Path: ?Sized; + + /// Loading Key Type + type LoadingKey: ?Sized; + + /// Load Error Type + type Error; + + /// Loads an element of type `Self` from `path` unlocking it with the `loading_key`. + fn load<P>(path: P, loading_key: &Self::LoadingKey) -> Result<Self, Self::Error> + where + P: AsRef<Self::Path>; +} + +/// Filesystem Encrypted Saving +pub trait Save { + /// Path Type + type Path: ?Sized; + + /// Saving Key Type + type SavingKey: ?Sized; + + /// Save Error Type + type Error; + + /// Saves `self` to `path` locking it with the `saving_key`. + fn save<P>(self, path: P, saving_key: &Self::SavingKey) -> Result<(), Self::Error> + where + P: AsRef<Self::Path>; +} + +/// Cocoon [`Load`] and [`Save`] Adapters +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +pub mod cocoon { + use super::*; + use cocoon_crate::{Cocoon, Error as CocoonError}; + use core::{ + convert::{Infallible, TryInto}, + fmt, + ops::Drop, + }; + use manta_util::from_variant_impl; + use std::{fs::OpenOptions, io::Error as IoError, path::Path}; + use zeroize::Zeroize; + + /// Cocoon Loading/Saving Error + #[derive(Debug)] + pub enum Error { + /// I/O Error + Io(IoError), + + /// Cocoon Error + Cocoon(CocoonError), + } + + from_variant_impl!(Error, Io, IoError); + from_variant_impl!(Error, Cocoon, CocoonError); + + impl fmt::Display for Error { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Io(err) => write!(f, "I/O Error: {}", err), + Self::Cocoon(err) => write!(f, "Cocoon Error: {:?}", err), + } + } + } + + impl std::error::Error for Error {} + + /// Payload Parsing + pub trait FromPayload: Sized { + /// Parsing Error Type + type Error; + + /// Converts the `payload` into an element of type `Self`. + fn from_payload(payload: &[u8]) -> Result<Self, Self::Error>; + } + + impl FromPayload for Vec<u8> { + type Error = Infallible; + + #[inline] + fn from_payload(payload: &[u8]) -> Result<Self, Self::Error> { + Ok(payload.to_vec()) + } + } + + impl<const N: usize> FromPayload for [u8; N] { + type Error = core::array::TryFromSliceError; + + #[inline] + fn from_payload(payload: &[u8]) -> Result<Self, Self::Error> { + (*payload).try_into() + } + } + + /// Cocoon [`Load`] Adapter + #[derive(Zeroize)] + #[zeroize(drop)] + pub struct Loader(Vec<u8>); + + impl Loader { + /// Parses the loaded data into an element of type `T`. + #[inline] + pub fn parse<T>(self) -> Result<T, T::Error> + where + T: FromPayload, + { + T::from_payload(&self.0) + } + } + + impl Load for Loader { + type Path = Path; + + type LoadingKey = [u8]; + + type Error = Error; + + #[inline] + fn load<P>(path: P, loading_key: &Self::LoadingKey) -> Result<Self, Self::Error> + where + P: AsRef<Self::Path>, + { + Ok(Self( + Cocoon::new(loading_key).parse(&mut OpenOptions::new().read(true).open(path)?)?, + )) + } + } + + /// Payload Extraction + pub trait Payload { + /// Extracts a byte vector payload from `self`. + fn payload(&self) -> Vec<u8>; + } + + impl Payload for Vec<u8> { + #[inline] + fn payload(&self) -> Vec<u8> { + self.clone() + } + } + + impl<const N: usize> Payload for [u8; N] { + #[inline] + fn payload(&self) -> Vec<u8> { + (*self).into() + } + } + + /// Cocoon [`Save`] Borrowed Data Adapter + #[derive(Clone, Copy)] + pub struct Saver<'t, T>(pub &'t T) + where + T: Payload; + + impl<'t, T> Save for Saver<'t, T> + where + T: Payload, + { + type Path = Path; + + type SavingKey = [u8]; + + type Error = Error; + + #[inline] + fn save<P>(self, path: P, saving_key: &Self::SavingKey) -> Result<(), Self::Error> + where + P: AsRef<Self::Path>, + { + save_payload(self.0, path, saving_key) + } + } + + /// Cocoon [`Save`] Owned Data Adapter + #[derive(Zeroize)] + pub struct SaverOwned<T>(T) + where + T: Payload + Zeroize; + + impl<T> Drop for SaverOwned<T> + where + T: Payload + Zeroize, + { + #[inline] + fn drop(&mut self) { + self.0.zeroize(); + } + } + + impl<T> Save for SaverOwned<T> + where + T: Payload + Zeroize, + { + type Path = Path; + + type SavingKey = [u8]; + + type Error = Error; + + #[inline] + fn save<P>(mut self, path: P, saving_key: &Self::SavingKey) -> Result<(), Self::Error> + where + P: AsRef<Self::Path>, + { + save_payload(&self.0, path, saving_key)?; + self.0.zeroize(); + Ok(()) + } + } + + /// Saves the payload generated from `source` to `path` using the `saving_key`. + #[inline] + fn save_payload<T, P>(source: &T, path: P, saving_key: &[u8]) -> Result<(), Error> + where + T: Payload, + P: AsRef<Path>, + { + // NOTE: We want to check that the file can be opened and that we can write to it + // before we extract the sensitive payload out of `self`. + let mut file = OpenOptions::new() + .create(true) + .truncate(true) + .write(true) + .open(path)?; + Ok(Cocoon::new(saving_key).dump(source.payload(), &mut file)?) + } + + /// Testing Suite + #[cfg(test)] + mod test { + use super::*; + use rand::{thread_rng, RngCore}; + + /// Tests loading some saved data using the [`Saver`] and [`Loader`] adapters. + #[test] + fn load_saved() { + let dir = tempfile::tempdir().expect("Temporary directory should have been created."); + let path = dir.path().join("load_saved.data"); + let mut rng = thread_rng(); + + // Generate random password to save to and load from the file system. + let mut password = [0; 256]; + rng.fill_bytes(&mut password); + + // Generate random payload. + let mut expected = [0; 2048]; + rng.fill_bytes(&mut expected); + + // Save the target payload to the file system. + Saver(&expected) + .save(&path, &password) + .expect("Payload should have been saved."); + + // Load the payload from the file system. + let observed = Loader::load(&path, &password) + .expect("Payload should have been loaded.") + .parse::<[u8; 2048]>() + .expect("Payload should have been parsed properly."); + + // Check that the payload matches. + assert_eq!(expected, observed); + + // Close the testing directory. + dir.close() + .expect("Temporary directory should have closed.") + } + } +} diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index fa80bf9ae..533ec0e3e 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -49,8 +49,8 @@ use rand::{ pub(crate) mod prelude { #[doc(inline)] pub use super::{ - Identity, IdentityConfiguration, Receiver, Sender, SenderError, ShieldedIdentity, Spend, - SpendError, Utxo, VoidNumber, + Identity, IdentityConfiguration, IdentityConstraintSystemConfiguration, Receiver, Sender, + SenderError, ShieldedIdentity, Spend, SpendError, Utxo, VoidNumber, }; } diff --git a/manta-accounting/src/lib.rs b/manta-accounting/src/lib.rs index f19d2bb42..b7b44c754 100644 --- a/manta-accounting/src/lib.rs +++ b/manta-accounting/src/lib.rs @@ -16,7 +16,7 @@ //! Accounting Primitives -#![no_std] +#![cfg_attr(not(any(feature = "std", test)), no_std)] #![cfg_attr(doc_cfg, feature(doc_cfg))] #![forbid(rustdoc::broken_intra_doc_links)] #![forbid(missing_docs)] @@ -24,9 +24,13 @@ extern crate alloc; extern crate derive_more; +#[cfg(feature = "cocoon")] +extern crate cocoon as cocoon_crate; + mod asset; mod ledger; +pub mod fs; pub mod identity; pub mod keys; pub mod transfer; diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index fc3781c0d..6c7df6e43 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -324,7 +324,7 @@ where rng: &mut R, ) -> Result<SecretTransferPost<T, SENDERS, RECEIVERS>, ProofSystemError<T>> where - R: CryptoRng + RngCore + ?Sized, + R: CryptoRng + RngCore, { match Transfer::from(self) .into_post(commitment_scheme, utxo_set, context, rng)? @@ -636,7 +636,7 @@ where rng: &mut R, ) -> Result<(ProvingContext<T>, VerifyingContext<T>), ProofSystemError<T>> where - R: CryptoRng + RngCore + ?Sized, + R: CryptoRng + RngCore, { let mut cs = T::ProofSystem::for_unknown(); let (base_asset_id, participants, commitment_scheme, utxo_set) = @@ -661,7 +661,7 @@ where rng: &mut R, ) -> Result<Proof<T>, ProofSystemError<T>> where - R: CryptoRng + RngCore + ?Sized, + R: CryptoRng + RngCore, { let mut cs = T::ProofSystem::for_known(); let (base_asset_id, participants, commitment_scheme, utxo_set) = @@ -686,7 +686,7 @@ where rng: &mut R, ) -> Result<TransferPost<T, SOURCES, SENDERS, RECEIVERS, SINKS>, ProofSystemError<T>> where - R: CryptoRng + RngCore + ?Sized, + R: CryptoRng + RngCore, { Ok(TransferPost { validity_proof: if SENDERS == 0 { diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index 518080549..759fba8a0 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -23,6 +23,7 @@ use crate::{ asset::{Asset, AssetBalance, AssetId}, + fs::{Load, Save}, identity::{ AssetParameters, Identity, IdentityConfiguration, OpenSpend, Receiver, ShieldedIdentity, Spend, @@ -403,6 +404,46 @@ where } } +impl<D, M> Load for Wallet<D, M> +where + D: DerivedSecretKeyGenerator + Load, + M: AssetMap + Default, +{ + type Path = D::Path; + + type LoadingKey = D::LoadingKey; + + type Error = <D as Load>::Error; + + #[inline] + fn load<P>(path: P, loading_key: &Self::LoadingKey) -> Result<Self, Self::Error> + where + P: AsRef<Self::Path>, + { + Ok(Self::new(D::load(path, loading_key)?, Default::default())) + } +} + +impl<D, M> Save for Wallet<D, M> +where + D: DerivedSecretKeyGenerator + Save, + M: AssetMap + Default, +{ + type Path = D::Path; + + type SavingKey = D::SavingKey; + + type Error = <D as Save>::Error; + + #[inline] + fn save<P>(self, path: P, saving_key: &Self::SavingKey) -> Result<(), Self::Error> + where + P: AsRef<Self::Path>, + { + self.secret_key_source.save(path, saving_key) + } +} + /// Internal Receiver Error /// /// This `enum` is the error state for the [`generate_internal_receiver`] method on [`Wallet`]. diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index b74474225..ed4053aaf 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -16,6 +16,7 @@ //! Commitment Schemes +use core::{fmt::Debug, hash::Hash}; use manta_util::{Concat, ConcatAccumulator}; pub(crate) mod prelude { @@ -42,6 +43,23 @@ pub trait CommitmentScheme { /// Commits the `input` buffer with the given `randomness` parameter. fn commit(&self, input: Self::InputBuffer, randomness: &Self::Randomness) -> Self::Output; + + /// Commits with an empty input using the given `randomness` parameter. + #[inline] + fn commit_none(&self, randomness: &Self::Randomness) -> Self::Output { + self.start().commit(randomness) + } + + /// Commits the single `input` by filling a new input buffer and then commiting with the given + /// `randomness` parameter. + #[inline] + fn commit_one<T>(&self, input: &T, randomness: &Self::Randomness) -> Self::Output + where + T: ?Sized, + Self: Input<T>, + { + self.start().update(input).commit(randomness) + } } /// Commitment Input @@ -66,6 +84,15 @@ where } /// Commitment Builder +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "C::InputBuffer: Clone"), + Copy(bound = "C::InputBuffer: Copy"), + Debug(bound = "C: Debug, C::InputBuffer: Debug"), + Eq(bound = "C: Eq, C::InputBuffer: Eq"), + Hash(bound = "C: Hash, C::InputBuffer: Hash"), + PartialEq(bound = "C: PartialEq, C::InputBuffer: PartialEq") +)] pub struct Builder<'c, C> where C: CommitmentScheme + ?Sized, @@ -107,3 +134,27 @@ where self.commitment_scheme.commit(self.input_buffer, randomness) } } + +/// Testing Framework +#[cfg(feature = "test")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] +pub mod test { + use super::*; + use core::fmt::Debug; + + /// Asserts that the given commitment `output` is equal to commiting `input` with `randomness` + /// using the `commitment_scheme`. + #[inline] + pub fn assert_commitment_matches<T, C>( + commitment_scheme: &C, + input: &T, + randomness: &C::Randomness, + output: &C::Output, + ) where + T: ?Sized, + C: CommitmentScheme + Input<T> + ?Sized, + C::Output: Debug + PartialEq, + { + assert_eq!(&commitment_scheme.commit_one(input, randomness), output) + } +} diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index c40f6f608..b99d90c0f 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -22,6 +22,8 @@ // FIXME: Leverage the type system to constrain allocation to only unknown modes for verifier // generation and only known modes for proof generation, instead of relying on the `setup_*` // methods to "do the right thing". +// FIXME: Post a fix to arkworks so that we can put `?Sized` on our random number generator +// paramters as conventionally used by `rand`. use core::{ convert::{Infallible, TryFrom}, @@ -476,6 +478,11 @@ pub trait ProofSystem { /// Proof Type type Proof; + /// Verification Type + /// + /// Usually this is just `bool`. + type Verification; + /// Error Type type Error; @@ -491,7 +498,7 @@ pub trait ProofSystem { rng: &mut R, ) -> Result<(Self::ProvingContext, Self::VerifyingContext), Self::Error> where - R: CryptoRng + RngCore + ?Sized; + R: CryptoRng + RngCore; /// Returns a proof that the constraint system `self` is consistent. fn generate_proof<R>( @@ -500,13 +507,13 @@ pub trait ProofSystem { rng: &mut R, ) -> Result<Self::Proof, Self::Error> where - R: CryptoRng + RngCore + ?Sized; + R: CryptoRng + RngCore; /// Verifies that a proof generated from this proof system is valid. fn verify_proof( context: &Self::VerifyingContext, proof: &Self::Proof, - ) -> Result<bool, Self::Error>; + ) -> Result<Self::Verification, Self::Error>; } /// Derived Allocation Mode @@ -901,3 +908,29 @@ pub mod types { /// Pointer-Sized Unsigned Integer Variable Type pub type Usize<C> = Var<usize, C>; } + +/// Testing Framework +#[cfg(feature = "test")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] +pub mod test { + use super::*; + + /// Builds a proof from `cs` and the `proving_context` and then tries to verify it with + /// the `verifying_context`. + #[inline] + pub fn verify_constructed_proof<P, R>( + proving_context: &P::ProvingContext, + verifying_context: &P::VerifyingContext, + cs: P::ConstraintSystem, + rng: &mut R, + ) -> Result<P::Verification, P::Error> + where + P: ProofSystem, + R: CryptoRng + RngCore, + { + P::verify_proof( + verifying_context, + &P::generate_proof(cs, proving_context, rng)?, + ) + } +} diff --git a/manta-crypto/src/ies.rs b/manta-crypto/src/ies.rs index 95eb52196..ae8bc81c0 100644 --- a/manta-crypto/src/ies.rs +++ b/manta-crypto/src/ies.rs @@ -289,7 +289,7 @@ pub mod test { use core::fmt::Debug; /// Tests encryption/decryption of a sample `plaintext`. - pub fn encryption_decryption<I, R>(plaintext: I::Plaintext, rng: &mut R) + pub fn assert_decryption_of_encryption<I, R>(plaintext: &I::Plaintext, rng: &mut R) where I: IntegratedEncryptionScheme, I::Plaintext: Debug + PartialEq, @@ -300,10 +300,10 @@ pub mod test { let reconstructed_plaintext = secret_key .decrypt( &public_key - .encrypt(&plaintext, rng) + .encrypt(plaintext, rng) .expect("Unable to encrypt plaintext."), ) .expect("Unable to decrypt plaintext."); - assert_eq!(plaintext, reconstructed_plaintext) + assert_eq!(plaintext, &reconstructed_plaintext) } } diff --git a/manta-crypto/src/set/mod.rs b/manta-crypto/src/set/mod.rs index 48027bd46..ce19b3f54 100644 --- a/manta-crypto/src/set/mod.rs +++ b/manta-crypto/src/set/mod.rs @@ -16,6 +16,11 @@ //! Sets and Verified Sets +// FIXME: We should probably have something like a "verified set verification handle" which a +// verified set can give to someone who wants to check a containment proof, since in general +// we don't actually need access to the set itself, or having access to the set would be be +// possible in any real implementation. + pub mod constraint; pub(crate) mod prelude { diff --git a/manta-pay/src/crypto/constraint/constraint_system.rs b/manta-pay/src/crypto/constraint/constraint_system.rs index 943088497..eb970fce1 100644 --- a/manta-pay/src/crypto/constraint/constraint_system.rs +++ b/manta-pay/src/crypto/constraint/constraint_system.rs @@ -32,17 +32,17 @@ use manta_crypto::constraint::{ use manta_util::{Concat, ConcatAccumulator}; /// Synthesis Result -type SynthesisResult<T> = Result<T, ark_r1cs::SynthesisError>; +pub type SynthesisResult<T = ()> = Result<T, ark_r1cs::SynthesisError>; /// Returns an empty variable assignment. #[inline] -pub(crate) const fn empty<T>() -> SynthesisResult<T> { +pub const fn empty<T>() -> SynthesisResult<T> { Err(ark_r1cs::SynthesisError::AssignmentMissing) } /// Returns a filled variable assignment. #[inline] -pub(crate) fn full<T>(t: T) -> impl FnOnce() -> SynthesisResult<T> { +pub fn full<T>(t: T) -> impl FnOnce() -> SynthesisResult<T> { move || Ok(t) } @@ -99,60 +99,40 @@ where pub(crate) cs: ark_r1cs::ConstraintSystemRef<F>, } -impl<F> ConstraintSystem for ArkConstraintSystem<F> +impl<F> ArkConstraintSystem<F> where F: Field, { - type Bool = Boolean<F>; + /// Constructs a new constraint system which is ready for unknown variables. + #[inline] + pub fn for_unknown() -> Self { + let cs = ark_r1cs::ConstraintSystem::new_ref(); + cs.set_mode(ark_r1cs::SynthesisMode::Setup); + Self { cs } + } + /// Constructs a new constraint system which is ready for known variables. #[inline] - fn assert(&mut self, b: Bool<Self>) { - // FIXME: Is there a more direct way to do assertions? - b.enforce_equal(&Boolean::TRUE) - .expect("This should never fail.") + pub fn for_known() -> Self { + let cs = ark_r1cs::ConstraintSystem::new_ref(); + Self { cs } } } -/* TODO: -impl<F> ProofSystem for Groth16ProofSystem<F> +impl<F> ConstraintSystem for ArkConstraintSystem<F> where F: Field, { - type Verifier = Groth16Verifier; - - type Proof = (Vec<F>, ()); - - type Error = (); - - #[inline] - fn setup_to_verify() -> Self { - todo!() - } - - #[inline] - fn setup_to_prove() -> Self { - todo!() - } - - #[inline] - fn into_verifier(self) -> Result<Self::Verifier, Self::Error> { - todo!() - } + type Bool = Boolean<F>; #[inline] - fn into_proof(self) -> Result<Self::Proof, Self::Error> { - let input = self.cs.borrow().ok_or(())?.instance_assignment; - use rand::SeedableRng; - let mut rng = rand_chacha::ChaCha20Rng::from_seed([0; 32]); - let _ = ark_groth16::create_random_proof(|| {}, (), &mut rng); - todo!() + fn assert(&mut self, b: Bool<Self>) { + // FIXME: Is there a more direct way to do assertions? + b.enforce_equal(&Boolean::TRUE) + .expect("This should never fail.") } } -/// Arkworks Groth 16 Proof System -pub struct Groth16ProofSystem; -*/ - impl<F> Variable<ArkConstraintSystem<F>> for Boolean<F> where F: Field, diff --git a/manta-pay/src/crypto/constraint/proof_systems/groth16.rs b/manta-pay/src/crypto/constraint/proof_systems/groth16.rs index 850017413..fff177b86 100644 --- a/manta-pay/src/crypto/constraint/proof_systems/groth16.rs +++ b/manta-pay/src/crypto/constraint/proof_systems/groth16.rs @@ -16,14 +16,13 @@ //! Arkworks Groth16 Implementation -use crate::crypto::constraint::ArkConstraintSystem; +use crate::crypto::constraint::{constraint_system::SynthesisResult, ArkConstraintSystem}; use alloc::vec::Vec; +use ark_crypto_primitives::SNARK; use ark_ec::PairingEngine; -use ark_groth16::{ - create_random_proof, generate_random_parameters, verify_proof, PreparedVerifyingKey, Proof, - ProvingKey, -}; -use ark_relations::r1cs::SynthesisError; +use ark_ff::Field; +use ark_groth16::{Groth16 as ArkGroth16, PreparedVerifyingKey, Proof, ProvingKey}; +use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError}; use core::marker::PhantomData; use manta_crypto::constraint::ProofSystem; use rand::{CryptoRng, RngCore}; @@ -47,16 +46,18 @@ where type Proof = (Vec<E::Fr>, Proof<E>); + type Verification = bool; + type Error = SynthesisError; #[inline] fn for_unknown() -> Self::ConstraintSystem { - todo!() + Self::ConstraintSystem::for_unknown() } #[inline] fn for_known() -> Self::ConstraintSystem { - todo!() + Self::ConstraintSystem::for_known() } #[inline] @@ -65,12 +66,11 @@ where rng: &mut R, ) -> Result<(Self::ProvingContext, Self::VerifyingContext), Self::Error> where - R: CryptoRng + RngCore + ?Sized, + R: CryptoRng + RngCore, { - /* TODO: - let _ = generate_random_parameters(|| {}, rng)?; - */ - todo!() + let (proving_key, verifying_key) = + ArkGroth16::circuit_specific_setup(ConstraintSynthesizerWrapper(cs), rng)?; + Ok((proving_key, ArkGroth16::process_vk(&verifying_key)?)) } #[inline] @@ -80,20 +80,64 @@ where rng: &mut R, ) -> Result<Self::Proof, Self::Error> where - R: CryptoRng + RngCore + ?Sized, + R: CryptoRng + RngCore, { - /* TODO: - let input = self.cs.borrow().ok_or(())?.instance_assignment; - let _ = create_random_proof(|| {}, context, &mut rng)?; - */ - todo!() + let input = cs + .cs + .borrow() + .ok_or(SynthesisError::MissingCS)? + .instance_assignment + .clone(); + let proof = ArkGroth16::prove(context, ConstraintSynthesizerWrapper(cs), rng)?; + Ok((input, proof)) } #[inline] fn verify_proof( context: &Self::VerifyingContext, proof: &Self::Proof, - ) -> Result<bool, Self::Error> { - verify_proof(context, &proof.1, &proof.0) + ) -> Result<Self::Verification, Self::Error> { + ArkGroth16::verify_with_processed_vk(context, &proof.0, &proof.1) + } +} + +/// Constraint Synthesizer Wrapper +struct ConstraintSynthesizerWrapper<F>(ArkConstraintSystem<F>) +where + F: Field; + +impl<F> ConstraintSynthesizer<F> for ConstraintSynthesizerWrapper<F> +where + F: Field, +{ + #[inline] + fn generate_constraints(self, cs: ConstraintSystemRef<F>) -> SynthesisResult { + let precomputed_cs = self + .0 + .cs + .into_inner() + .expect("We own this constraint system so we can consume it."); + let mut target_cs = cs + .borrow_mut() + .expect("This is given to us to mutate so it can't be borrowed by anyone else."); + *target_cs = precomputed_cs; + Ok(()) } } + +#[cfg(test)] +mod test { + use super::*; + + /// Tests the generation of proving/verifying contexts. + #[test] + fn generate_context() {} + + /// Tests the generation of proofs. + #[test] + fn generate_proof() {} + + /// Tests the verification of proofs. + #[test] + fn verify_proof() {} +} diff --git a/manta-pay/src/crypto/ies.rs b/manta-pay/src/crypto/ies.rs index 802351316..b0bd4aeb1 100644 --- a/manta-pay/src/crypto/ies.rs +++ b/manta-pay/src/crypto/ies.rs @@ -172,13 +172,12 @@ impl IntegratedEncryptionScheme for IES { mod test { use super::*; use manta_crypto::ies::test as ies_test; + use rand::{thread_rng, Rng}; /// Tests encryption/decryption of a random asset. #[test] fn encryption_decryption() { - use rand::{thread_rng, Rng, SeedableRng}; - use rand_chacha::ChaCha20Rng; - let mut rng = ChaCha20Rng::from_rng(&mut thread_rng()).expect("failed to seed ChaCha20Rng"); - ies_test::encryption_decryption::<IES, _>(rng.gen(), &mut rng); + let mut rng = thread_rng(); + ies_test::assert_decryption_of_encryption::<IES, _>(&rng.gen(), &mut rng); } } From a87b596de1b7f04146ebab85accd46aafc2e19db Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 17 Sep 2021 19:31:09 -0400 Subject: [PATCH 039/275] simplify MT interfaces, start IMT integration --- manta-accounting/Cargo.toml | 8 + manta-accounting/src/asset.rs | 6 +- manta-accounting/src/identity.rs | 9 +- manta-accounting/src/transfer.rs | 38 +- manta-crypto/Cargo.toml | 1 + manta-crypto/src/constraint.rs | 6 +- manta-crypto/src/set.rs | 285 +++++++++ manta-crypto/src/set/constraint.rs | 179 ------ manta-crypto/src/set/mod.rs | 123 ---- manta-pay/Cargo.toml | 2 +- manta-pay/src/accounting/config.rs | 33 +- manta-pay/src/accounting/identity.rs | 55 ++ manta-pay/src/accounting/ledger.rs | 51 +- manta-pay/src/accounting/mod.rs | 2 + manta-pay/src/accounting/transfer.rs | 38 ++ manta-pay/src/crypto/commitment/pedersen.rs | 35 +- .../crypto/constraint/constraint_system.rs | 11 +- .../constraint/proof_systems/groth16.rs | 54 +- manta-pay/src/crypto/merkle_tree/basic.rs | 522 ----------------- .../src/crypto/merkle_tree/incremental.rs | 2 +- manta-pay/src/crypto/merkle_tree/mod.rs | 542 +++++++++++++++++- manta-pay/src/crypto/prf/blake2s.rs | 2 + manta-util/Cargo.toml | 3 + manta-util/src/lib.rs | 4 + manta-util/src/rand.rs | 66 +++ 25 files changed, 1171 insertions(+), 906 deletions(-) create mode 100644 manta-crypto/src/set.rs delete mode 100644 manta-crypto/src/set/constraint.rs delete mode 100644 manta-crypto/src/set/mod.rs create mode 100644 manta-pay/src/accounting/identity.rs create mode 100644 manta-pay/src/accounting/transfer.rs delete mode 100644 manta-pay/src/crypto/merkle_tree/basic.rs create mode 100644 manta-util/src/rand.rs diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 6adcfe3fd..51fb115e4 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -25,8 +25,16 @@ is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } [features] +# Constraint System Gadgets +constraints = [] + +# Cocoon Filesystem Adapters cocoon-adapters = ["cocoon/std", "zeroize"] + +# Standard Library std = ["cocoon-adapters"] + +# Testing Framework test = [] [dependencies] diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 5a4fd1b34..dbcd3340f 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -76,7 +76,7 @@ impl AssetId { impl Distribution<AssetId> for Standard { #[inline] fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> AssetId { - AssetId(rng.gen()) + AssetId(self.sample(rng)) } } @@ -161,7 +161,7 @@ impl AssetBalance { impl Distribution<AssetBalance> for Standard { #[inline] fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> AssetBalance { - AssetBalance(rng.gen()) + AssetBalance(self.sample(rng)) } } @@ -333,7 +333,7 @@ impl From<Asset> for (AssetId, AssetBalance) { impl Distribution<Asset> for Standard { #[inline] fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> Asset { - Asset::new(rng.gen(), rng.gen()) + Asset::new(self.sample(rng), self.sample(rng)) } } diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 533ec0e3e..f11982d20 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -20,6 +20,7 @@ // FIXME: Should the identity types have methods that expose their members? Or should it be // completely opaque, and let the internal APIs handle all the logic? // TODO: Since `IdentityConfiguration::SecretKey: Clone`, should `Identity: Clone`? +// TODO: Separate "constraint" types into another module just like `manta_crypto::set`. use crate::{ asset::{Asset, AssetBalance, AssetId, AssetVar}, @@ -43,7 +44,7 @@ use manta_crypto::{ }; use rand::{ distributions::{Distribution, Standard}, - CryptoRng, Rng, RngCore, SeedableRng, + CryptoRng, RngCore, SeedableRng, }; pub(crate) mod prelude { @@ -348,7 +349,7 @@ where { #[inline] fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> AssetParameters<C> { - AssetParameters::new(rng.gen(), rng.gen(), rng.gen()) + AssetParameters::new(self.sample(rng), self.sample(rng), self.sample(rng)) } } @@ -479,7 +480,7 @@ where Standard: Distribution<AssetParameters<C>>, { let mut rng = C::Rng::from_seed(self.secret_key.clone()); - let parameters = rng.gen(); + let parameters = Standard.sample(&mut rng); (rng, parameters) } @@ -755,7 +756,7 @@ where { #[inline] fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> Identity<C> { - Identity::new(rng.gen()) + Identity::new(self.sample(rng)) } } diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 6c7df6e43..89a16847d 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -39,7 +39,7 @@ use manta_crypto::{ use manta_util::{array_map, mixed_chain, Either}; use rand::{ distributions::{Distribution, Standard}, - CryptoRng, Rng, RngCore, + CryptoRng, RngCore, }; /// Returns `true` if the transfer with this shape would have no public side. @@ -133,7 +133,7 @@ impl<const SOURCES: usize, const SINKS: usize> Distribution<PublicTransfer<SOURC #[inline] fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> PublicTransfer<SOURCES, SINKS> { PublicTransfer::new( - rng.gen(), + self.sample(rng), sample_asset_balances(rng), sample_asset_balances(rng), ) @@ -324,7 +324,7 @@ where rng: &mut R, ) -> Result<SecretTransferPost<T, SENDERS, RECEIVERS>, ProofSystemError<T>> where - R: CryptoRng + RngCore, + R: CryptoRng + RngCore + ?Sized, { match Transfer::from(self) .into_post(commitment_scheme, utxo_set, context, rng)? @@ -629,15 +629,21 @@ where } /// Generates a verifier for this transfer shape. + /// + /// Returns `None` if proof generation does not apply for this kind of transfer. + #[allow(clippy::type_complexity)] // FIXME: We will have to refactor this at some point. #[inline] pub fn generate_context<R>( commitment_scheme: &T::CommitmentScheme, utxo_set: &T::UtxoSet, rng: &mut R, - ) -> Result<(ProvingContext<T>, VerifyingContext<T>), ProofSystemError<T>> + ) -> Option<Result<(ProvingContext<T>, VerifyingContext<T>), ProofSystemError<T>>> where - R: CryptoRng + RngCore, + R: CryptoRng + RngCore + ?Sized, { + if SENDERS == 0 { + return None; + } let mut cs = T::ProofSystem::for_unknown(); let (base_asset_id, participants, commitment_scheme, utxo_set) = Self::unknown_variables(commitment_scheme, utxo_set, &mut cs); @@ -648,10 +654,12 @@ where utxo_set, &mut cs, ); - T::ProofSystem::generate_context(cs, rng) + Some(T::ProofSystem::generate_context(cs, rng)) } /// Generates a validity proof for this transfer. + /// + /// Returns `None` if proof generation does not apply for this kind of transfer. #[inline] pub fn generate_proof<R>( &self, @@ -659,10 +667,13 @@ where utxo_set: &T::UtxoSet, context: &ProvingContext<T>, rng: &mut R, - ) -> Result<Proof<T>, ProofSystemError<T>> + ) -> Option<Result<Proof<T>, ProofSystemError<T>>> where - R: CryptoRng + RngCore, + R: CryptoRng + RngCore + ?Sized, { + if SENDERS == 0 { + return None; + } let mut cs = T::ProofSystem::for_known(); let (base_asset_id, participants, commitment_scheme, utxo_set) = self.known_variables(commitment_scheme, utxo_set, &mut cs); @@ -673,7 +684,7 @@ where utxo_set, &mut cs, ); - T::ProofSystem::generate_proof(cs, context, rng) + Some(T::ProofSystem::generate_proof(cs, context, rng)) } /// Converts `self` into its ledger post. @@ -686,13 +697,12 @@ where rng: &mut R, ) -> Result<TransferPost<T, SOURCES, SENDERS, RECEIVERS, SINKS>, ProofSystemError<T>> where - R: CryptoRng + RngCore, + R: CryptoRng + RngCore + ?Sized, { Ok(TransferPost { - validity_proof: if SENDERS == 0 { - None - } else { - Some(self.generate_proof(commitment_scheme, utxo_set, context, rng)?) + validity_proof: match self.generate_proof(commitment_scheme, utxo_set, context, rng) { + Some(result) => Some(result?), + _ => None, }, sender_posts: array_map(self.secret.senders, Sender::into_post), receiver_posts: array_map(self.secret.receivers, Receiver::into_post), diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index cac935052..c2ab040ec 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -25,6 +25,7 @@ is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } [features] +constraints = [] default = [] test = [] diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index b99d90c0f..ecfa92ed5 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -22,8 +22,6 @@ // FIXME: Leverage the type system to constrain allocation to only unknown modes for verifier // generation and only known modes for proof generation, instead of relying on the `setup_*` // methods to "do the right thing". -// FIXME: Post a fix to arkworks so that we can put `?Sized` on our random number generator -// paramters as conventionally used by `rand`. use core::{ convert::{Infallible, TryFrom}, @@ -498,7 +496,7 @@ pub trait ProofSystem { rng: &mut R, ) -> Result<(Self::ProvingContext, Self::VerifyingContext), Self::Error> where - R: CryptoRng + RngCore; + R: CryptoRng + RngCore + ?Sized; /// Returns a proof that the constraint system `self` is consistent. fn generate_proof<R>( @@ -507,7 +505,7 @@ pub trait ProofSystem { rng: &mut R, ) -> Result<Self::Proof, Self::Error> where - R: CryptoRng + RngCore; + R: CryptoRng + RngCore + ?Sized; /// Verifies that a proof generated from this proof system is valid. fn verify_proof( diff --git a/manta-crypto/src/set.rs b/manta-crypto/src/set.rs new file mode 100644 index 000000000..3bc39cef1 --- /dev/null +++ b/manta-crypto/src/set.rs @@ -0,0 +1,285 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Sets and Verified Sets + +// FIXME: We should probably have something like a "verified set verification handle" which a +// verified set can give to someone who wants to check a containment proof, since in general +// we don't actually need access to the set itself, or having access to the set would be be +// possible in any real implementation. + +pub(crate) mod prelude { + #[doc(inline)] + pub use super::{Set, VerifiedSet}; +} + +/// Set Trait +pub trait Set { + /// Item Stored in the [`Set`] + type Item; + + /// Returns `true` if `item` is stored in `self`. + fn contains(&self, item: &Self::Item) -> bool; + + /// Tries to insert the `item` into `self`, returning the item back if it was already + /// contained in `self`. + fn try_insert(&mut self, item: Self::Item) -> Result<(), Self::Item>; + + /// Inserts the `item` into `self`, returning `true` if the `item` was not contained and + /// `false` if the item was already contained in `self`. + #[inline] + fn insert(&mut self, item: Self::Item) -> bool { + self.try_insert(item).is_err() + } +} + +/// Containment Proof for a [`VerifiedSet`] +pub struct ContainmentProof<S> +where + S: VerifiedSet + ?Sized, +{ + /// Public Input + public_input: S::Public, + + /// Secret Witness + secret_witness: S::Secret, +} + +impl<S> ContainmentProof<S> +where + S: VerifiedSet + ?Sized, +{ + /// Builds a new [`ContainmentProof`] from `public_input` and `secret_witness`. + #[inline] + pub fn new(public_input: S::Public, secret_witness: S::Secret) -> Self { + Self { + public_input, + secret_witness, + } + } + + /// Returns [`S::Public`](VerifiedSet::Public) discarding the [`ContainmentProof`]. + #[inline] + pub fn into_public_input(self) -> S::Public { + self.public_input + } + + /// Verifies that the `item` is contained in some [`VerifiedSet`]. + #[inline] + pub fn verify(&self, set: &S, item: &S::Item) -> bool { + set.check_containment_proof(&self.public_input, &self.secret_witness, item) + } + + /// Returns `true` if `self.public_input` is a valid input for the current state of `set`. + #[inline] + pub fn check_public_input(&self, set: &S) -> bool { + set.check_public_input(&self.public_input) + } +} + +/// Verified Set Trait +pub trait VerifiedSet: Set { + /// Public Input for [`Item`](Set::Item) Containment + type Public; + + /// Secret Witness for [`Item`](Set::Item) Containment + type Secret; + + /// Error Generating a [`ContainmentProof`] + type ContainmentError; + + /// Returns `true` if `public_input` is a valid input for the current state of `self`. + fn check_public_input(&self, public_input: &Self::Public) -> bool; + + /// Returns `true` if `public_input` and `secret_witness` make up a valid proof that `item` + /// is stored in `self`. + fn check_containment_proof( + &self, + public_input: &Self::Public, + secret_witness: &Self::Secret, + item: &Self::Item, + ) -> bool; + + /// Generates a proof that the given `item` is stored in `self`. + fn get_containment_proof( + &self, + item: &Self::Item, + ) -> Result<ContainmentProof<Self>, Self::ContainmentError>; +} + +/// Constraint System Gadgets for Sets and Verified Sets +pub mod constraint { + use super::*; + use crate::constraint::{ + reflection::{unknown, HasAllocation, HasVariable, Mode, Var}, + Allocation, AllocationMode, AllocationSystem, ConstraintSystem, Derived, Variable, + }; + use core::marker::PhantomData; + + /// Containment Proof Allocation Mode Entry + #[derive(derivative::Derivative)] + #[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] + pub struct ContainmentProofModeEntry<PublicMode, SecretMode> { + /// Public Input Allocation Mode + pub public: PublicMode, + + /// Secret Witness Allocation Mode + pub secret: SecretMode, + } + + impl<PublicMode, SecretMode> ContainmentProofModeEntry<PublicMode, SecretMode> { + /// Builds a new [`ContainmentProofModeEntry`] from a `public` mode and a `secret` mode. + #[inline] + pub fn new(public: PublicMode, secret: SecretMode) -> Self { + Self { public, secret } + } + } + + impl<PublicMode, SecretMode> From<Derived> for ContainmentProofModeEntry<PublicMode, SecretMode> + where + PublicMode: From<Derived>, + SecretMode: From<Derived>, + { + #[inline] + fn from(d: Derived) -> Self { + Self::new(d.into(), d.into()) + } + } + + /// Containment Proof Allocation Mode + #[derive(derivative::Derivative)] + #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct ContainmentProofMode<PublicMode, SecretMode>(PhantomData<(PublicMode, SecretMode)>) + where + PublicMode: AllocationMode, + SecretMode: AllocationMode; + + impl<PublicMode, SecretMode> AllocationMode for ContainmentProofMode<PublicMode, SecretMode> + where + PublicMode: AllocationMode, + SecretMode: AllocationMode, + { + type Known = ContainmentProofModeEntry<PublicMode::Known, SecretMode::Known>; + type Unknown = ContainmentProofModeEntry<PublicMode::Unknown, SecretMode::Unknown>; + } + + /// Containment Proof Variable + pub struct ContainmentProofVar<S, C> + where + S: VerifiedSet + ?Sized, + C: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, + { + /// Public Input + public_input: Var<S::Public, C>, + + /// Secret Witness + secret_witness: Var<S::Secret, C>, + } + + impl<S, C> ContainmentProofVar<S, C> + where + S: VerifiedSet + ?Sized, + C: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, + { + /// Builds a new [`ContainmentProofVar`] from `public_input` and `secret_witness`. + #[inline] + pub fn new(public_input: Var<S::Public, C>, secret_witness: Var<S::Secret, C>) -> Self { + Self { + public_input, + secret_witness, + } + } + + /// Asserts that `self` is a valid proof to the fact that `item` is stored in the + /// verified set. + #[inline] + pub fn assert_validity<V>(&self, set: &V, item: &V::ItemVar, cs: &mut C) + where + C: ConstraintSystem, + V: VerifiedSetVariable<C, Type = S>, + { + set.assert_valid_containment_proof(&self.public_input, &self.secret_witness, item, cs) + } + } + + impl<S, C> Variable<C> for ContainmentProofVar<S, C> + where + S: VerifiedSet + ?Sized, + C: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, + { + type Type = ContainmentProof<S>; + + type Mode = ContainmentProofMode<Mode<S::Public, C>, Mode<S::Secret, C>>; + + #[inline] + fn new(cs: &mut C, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + match allocation { + Allocation::Known(this, mode) => Self::new( + cs.allocate_known(&this.public_input, mode.public), + cs.allocate_known(&this.secret_witness, mode.secret), + ), + Allocation::Unknown(mode) => Self::new( + unknown::<S::Public, _>(cs, mode.public), + unknown::<S::Secret, _>(cs, mode.secret), + ), + } + } + } + + impl<S, C> HasAllocation<C> for ContainmentProof<S> + where + S: VerifiedSet + ?Sized, + C: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, + { + type Variable = ContainmentProofVar<S, C>; + type Mode = ContainmentProofMode<Mode<S::Public, C>, Mode<S::Secret, C>>; + } + + /// Public Input Type for [`VerifiedSetVariable`] + pub type PublicInputType<V, C> = <<V as Variable<C>>::Type as VerifiedSet>::Public; + + /// Secret Witness Type for [`VerifiedSetVariable`] + pub type SecretWitnessType<V, C> = <<V as Variable<C>>::Type as VerifiedSet>::Secret; + + /// Public Input Variable for [`VerifiedSetVariable`] + pub type PublicInputVar<V, C> = Var<PublicInputType<V, C>, C>; + + /// Secret Witness Variable for [`VerifiedSetVariable`] + pub type SecretWitnessVar<V, C> = Var<SecretWitnessType<V, C>, C>; + + /// Verified Set Variable + pub trait VerifiedSetVariable<C>: Variable<C> + where + C: ConstraintSystem + + HasVariable<PublicInputType<Self, C>> + + HasVariable<SecretWitnessType<Self, C>> + + ?Sized, + Self::Type: VerifiedSet, + { + /// Item Variable + type ItemVar: Variable<C, Type = <Self::Type as Set>::Item>; + + /// Asserts that `public_input` and `secret_witness` form a proof to the fact that `item` + /// is stored in `self`. + fn assert_valid_containment_proof( + &self, + public_input: &PublicInputVar<Self, C>, + secret_witness: &SecretWitnessVar<Self, C>, + item: &Self::ItemVar, + cs: &mut C, + ); + } +} diff --git a/manta-crypto/src/set/constraint.rs b/manta-crypto/src/set/constraint.rs deleted file mode 100644 index e02f0cb3c..000000000 --- a/manta-crypto/src/set/constraint.rs +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Sets and Verified Set Proof Systems - -use crate::{ - constraint::{ - reflection::{unknown, HasAllocation, HasVariable, Mode, Var}, - Allocation, AllocationMode, AllocationSystem, ConstraintSystem, Derived, Variable, - }, - set::{ContainmentProof, Set, VerifiedSet}, -}; -use core::marker::PhantomData; - -/// Containment Proof Allocation Mode Entry -#[derive(derivative::Derivative)] -#[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] -pub struct ContainmentProofModeEntry<PublicMode, SecretMode> { - /// Public Input Allocation Mode - pub public: PublicMode, - - /// Secret Witness Allocation Mode - pub secret: SecretMode, -} - -impl<PublicMode, SecretMode> ContainmentProofModeEntry<PublicMode, SecretMode> { - /// Builds a new [`ContainmentProofModeEntry`] from a `public` mode and a `secret` mode. - #[inline] - pub fn new(public: PublicMode, secret: SecretMode) -> Self { - Self { public, secret } - } -} - -impl<PublicMode, SecretMode> From<Derived> for ContainmentProofModeEntry<PublicMode, SecretMode> -where - PublicMode: From<Derived>, - SecretMode: From<Derived>, -{ - #[inline] - fn from(d: Derived) -> Self { - Self::new(d.into(), d.into()) - } -} - -/// Containment Proof Allocation Mode -#[derive(derivative::Derivative)] -#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct ContainmentProofMode<PublicMode, SecretMode>(PhantomData<(PublicMode, SecretMode)>) -where - PublicMode: AllocationMode, - SecretMode: AllocationMode; - -impl<PublicMode, SecretMode> AllocationMode for ContainmentProofMode<PublicMode, SecretMode> -where - PublicMode: AllocationMode, - SecretMode: AllocationMode, -{ - type Known = ContainmentProofModeEntry<PublicMode::Known, SecretMode::Known>; - type Unknown = ContainmentProofModeEntry<PublicMode::Unknown, SecretMode::Unknown>; -} - -/// Containment Proof Variable -pub struct ContainmentProofVar<S, C> -where - S: VerifiedSet + ?Sized, - C: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, -{ - /// Public Input - public_input: Var<S::Public, C>, - - /// Secret Witness - secret_witness: Var<S::Secret, C>, -} - -impl<S, C> ContainmentProofVar<S, C> -where - S: VerifiedSet + ?Sized, - C: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, -{ - /// Builds a new [`ContainmentProofVar`] from `public_input` and `secret_witness`. - #[inline] - pub fn new(public_input: Var<S::Public, C>, secret_witness: Var<S::Secret, C>) -> Self { - Self { - public_input, - secret_witness, - } - } - - /// Asserts that `self` is a valid proof to the fact that `item` is stored in the verified set. - #[inline] - pub fn assert_validity<V>(&self, set: &V, item: &V::ItemVar, cs: &mut C) - where - C: ConstraintSystem, - V: VerifiedSetVariable<C, Type = S>, - { - set.assert_valid_containment_proof(&self.public_input, &self.secret_witness, item, cs) - } -} - -impl<S, C> Variable<C> for ContainmentProofVar<S, C> -where - S: VerifiedSet + ?Sized, - C: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, -{ - type Type = ContainmentProof<S>; - - type Mode = ContainmentProofMode<Mode<S::Public, C>, Mode<S::Secret, C>>; - - #[inline] - fn new(cs: &mut C, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - match allocation { - Allocation::Known(this, mode) => Self::new( - cs.allocate_known(&this.public_input, mode.public), - cs.allocate_known(&this.secret_witness, mode.secret), - ), - Allocation::Unknown(mode) => Self::new( - unknown::<S::Public, _>(cs, mode.public), - unknown::<S::Secret, _>(cs, mode.secret), - ), - } - } -} - -impl<S, C> HasAllocation<C> for ContainmentProof<S> -where - S: VerifiedSet + ?Sized, - C: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, -{ - type Variable = ContainmentProofVar<S, C>; - type Mode = ContainmentProofMode<Mode<S::Public, C>, Mode<S::Secret, C>>; -} - -/// Public Input Type for [`VerifiedSetVariable`] -pub type PublicInputType<V, C> = <<V as Variable<C>>::Type as VerifiedSet>::Public; - -/// Secret Witness Type for [`VerifiedSetVariable`] -pub type SecretWitnessType<V, C> = <<V as Variable<C>>::Type as VerifiedSet>::Secret; - -/// Public Input Variable for [`VerifiedSetVariable`] -pub type PublicInputVar<V, C> = Var<PublicInputType<V, C>, C>; - -/// Secret Witness Variable for [`VerifiedSetVariable`] -pub type SecretWitnessVar<V, C> = Var<SecretWitnessType<V, C>, C>; - -/// Verified Set Variable -pub trait VerifiedSetVariable<C>: Variable<C> -where - C: ConstraintSystem - + HasVariable<PublicInputType<Self, C>> - + HasVariable<SecretWitnessType<Self, C>> - + ?Sized, - Self::Type: VerifiedSet, -{ - /// Item Variable - type ItemVar: Variable<C, Type = <Self::Type as Set>::Item>; - - /// Asserts that `public_input` and `secret_witness` form a proof to the fact that `item` is - /// stored in `self`. - fn assert_valid_containment_proof( - &self, - public_input: &PublicInputVar<Self, C>, - secret_witness: &SecretWitnessVar<Self, C>, - item: &Self::ItemVar, - cs: &mut C, - ); -} diff --git a/manta-crypto/src/set/mod.rs b/manta-crypto/src/set/mod.rs deleted file mode 100644 index ce19b3f54..000000000 --- a/manta-crypto/src/set/mod.rs +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Sets and Verified Sets - -// FIXME: We should probably have something like a "verified set verification handle" which a -// verified set can give to someone who wants to check a containment proof, since in general -// we don't actually need access to the set itself, or having access to the set would be be -// possible in any real implementation. - -pub mod constraint; - -pub(crate) mod prelude { - #[doc(inline)] - pub use super::{Set, VerifiedSet}; -} - -/// Set Trait -pub trait Set { - /// Item Stored in the [`Set`] - type Item; - - /// Returns `true` if `item` is stored in `self`. - fn contains(&self, item: &Self::Item) -> bool; - - /// Tries to insert the `item` into `self`, returning the item back if it was already - /// contained in `self`. - fn try_insert(&mut self, item: Self::Item) -> Result<(), Self::Item>; - - /// Inserts the `item` into `self`, returning `true` if the `item` was not contained and - /// `false` if the item was already contained in `self`. - #[inline] - fn insert(&mut self, item: Self::Item) -> bool { - self.try_insert(item).is_err() - } -} - -/// Containment Proof for a [`VerifiedSet`] -pub struct ContainmentProof<S> -where - S: VerifiedSet + ?Sized, -{ - /// Public Input - public_input: S::Public, - - /// Secret Witness - secret_witness: S::Secret, -} - -impl<S> ContainmentProof<S> -where - S: VerifiedSet + ?Sized, -{ - /// Builds a new [`ContainmentProof`] from `public_input` and `secret_witness`. - #[inline] - pub fn new(public_input: S::Public, secret_witness: S::Secret) -> Self { - Self { - public_input, - secret_witness, - } - } - - /// Returns [`S::Public`](VerifiedSet::Public) discarding the [`ContainmentProof`]. - #[inline] - pub fn into_public_input(self) -> S::Public { - self.public_input - } - - /// Verifies that the `item` is contained in some [`VerifiedSet`]. - #[inline] - pub fn verify(&self, set: &S, item: &S::Item) -> bool { - set.check_containment_proof(&self.public_input, &self.secret_witness, item) - } - - /// Returns `true` if `self.public_input` is a valid input for the current state of `set`. - #[inline] - pub fn check_public_input(&self, set: &S) -> bool { - set.check_public_input(&self.public_input) - } -} - -/// Verified Set Trait -pub trait VerifiedSet: Set { - /// Public Input for [`Item`](Set::Item) Containment - type Public; - - /// Secret Witness for [`Item`](Set::Item) Containment - type Secret; - - /// Error Generating a [`ContainmentProof`] - type ContainmentError; - - /// Returns `true` if `public_input` is a valid input for the current state of `self`. - fn check_public_input(&self, public_input: &Self::Public) -> bool; - - /// Returns `true` if `public_input` and `secret_witness` make up a valid proof that `item` - /// is stored in `self`. - fn check_containment_proof( - &self, - public_input: &Self::Public, - secret_witness: &Self::Secret, - item: &Self::Item, - ) -> bool; - - /// Generates a proof that the given `item` is stored in `self`. - fn get_containment_proof( - &self, - item: &Self::Item, - ) -> Result<ContainmentProof<Self>, Self::ContainmentError>; -} diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index ac9fd0ed5..3bc5bec55 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -45,7 +45,7 @@ derivative = { version = "2.2.0", default-features = false } generic-array = { version = "0.14.4", default-features = false } manta-accounting = { path = "../manta-accounting", default-features = false } manta-crypto = { path = "../manta-crypto", default-features = false } -manta-util = { path = "../manta-util", default-features = false } +manta-util = { path = "../manta-util", default-features = false, features = ["rand_core"] } rand = { version = "0.8.4", default-features = false } rand_chacha = { version = "0.3.1", default-features = false } x25519-dalek = { git = "https://github.com/Manta-Network/x25519-dalek", default-features = false, features = ["u64_backend"] } diff --git a/manta-pay/src/accounting/config.rs b/manta-pay/src/accounting/config.rs index ee77b4775..413d107fd 100644 --- a/manta-pay/src/accounting/config.rs +++ b/manta-pay/src/accounting/config.rs @@ -22,6 +22,7 @@ use crate::{ commitment::pedersen::{self, PedersenWindow}, constraint::{proof_systems::Groth16, ArkConstraintSystem, AssetBalanceVar, AssetIdVar}, ies::IES, + merkle_tree, prf::blake2s::{constraint::Blake2sVar, Blake2s}, rand::ChaCha20RngBlake2sSeedable, }, @@ -72,10 +73,40 @@ pub type ConstraintSystem = ArkConstraintSystem<ConstraintField>; /// Proof System pub type ProofSystem = Groth16<Bls12_381>; -/// Manta Pay Configuration +/// Configuration Structure #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Configuration; +impl merkle_tree::Configuration for Configuration { + type LeafHash = ark_crypto_primitives::crh::pedersen::CRH< + PedersenCommitmentProjectiveCurve, + PedersenCommitmentWindowParameters, + >; + + type InnerHash = ark_crypto_primitives::crh::pedersen::CRH< + PedersenCommitmentProjectiveCurve, + PedersenCommitmentWindowParameters, + >; + + const HEIGHT: u32 = 32; +} + +impl merkle_tree::constraint::Configuration for Configuration { + type ConstraintField = ConstraintField; + + type LeafHashVar = ark_crypto_primitives::crh::pedersen::constraints::CRHGadget< + PedersenCommitmentProjectiveCurve, + PedersenCommitmentProjectiveCurveVar, + PedersenCommitmentWindowParameters, + >; + + type InnerHashVar = ark_crypto_primitives::crh::pedersen::constraints::CRHGadget< + PedersenCommitmentProjectiveCurve, + PedersenCommitmentProjectiveCurveVar, + PedersenCommitmentWindowParameters, + >; +} + impl IdentityConfiguration for Configuration { type SecretKey = <Blake2s as PseudorandomFunctionFamily>::Seed; type PseudorandomFunctionFamily = Blake2s; diff --git a/manta-pay/src/accounting/identity.rs b/manta-pay/src/accounting/identity.rs new file mode 100644 index 000000000..88709766c --- /dev/null +++ b/manta-pay/src/accounting/identity.rs @@ -0,0 +1,55 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Identity Implementations + +use crate::accounting::config::Configuration; +use manta_accounting::{identity, transfer::TransferConfiguration}; + +/// Asset Parameters +pub type AssetParameters = identity::AssetParameters<Configuration>; + +/// Sender Type +pub type Sender = + identity::Sender<Configuration, <Configuration as TransferConfiguration>::UtxoSet>; + +/// Receiver Type +pub type Receiver = identity::Receiver< + Configuration, + <Configuration as TransferConfiguration>::IntegratedEncryptionScheme, +>; + +/// Shielded Identity Type +pub type ShieldedIdentity = identity::ShieldedIdentity< + Configuration, + <Configuration as TransferConfiguration>::IntegratedEncryptionScheme, +>; + +/// Spend Type +pub type Spend = identity::Spend< + Configuration, + <Configuration as TransferConfiguration>::IntegratedEncryptionScheme, +>; + +/// Sender Post Type +pub type SenderPost = + identity::SenderPost<Configuration, <Configuration as TransferConfiguration>::UtxoSet>; + +/// Receiver Post Type +pub type ReceiverPost = identity::ReceiverPost< + Configuration, + <Configuration as TransferConfiguration>::IntegratedEncryptionScheme, +>; diff --git a/manta-pay/src/accounting/ledger.rs b/manta-pay/src/accounting/ledger.rs index f7f4fa2ff..e88387458 100644 --- a/manta-pay/src/accounting/ledger.rs +++ b/manta-pay/src/accounting/ledger.rs @@ -19,10 +19,7 @@ // FIXME: How should we handle serdes? use crate::{ - accounting::config::{ - Configuration, ConstraintSystem, PedersenCommitmentProjectiveCurve, - PedersenCommitmentProjectiveCurveVar, PedersenCommitmentWindowParameters, ProofSystem, - }, + accounting::config::{Configuration, ConstraintSystem, ProofSystem}, crypto::{ ies::EncryptedAsset, merkle_tree::{self, MerkleTree}, @@ -52,46 +49,22 @@ type Utxo = identity::Utxo<Configuration>; type UtxoVar = identity::UtxoVar<Configuration>; /// UTXO Shard Root -type Root = merkle_tree::RootWrapper< - PedersenCommitmentWindowParameters, - PedersenCommitmentProjectiveCurve, - PedersenCommitmentProjectiveCurveVar, ->; +type Root = merkle_tree::Root<Configuration>; /// UTXO Shard Root Variable -type RootVar = merkle_tree::RootVar< - PedersenCommitmentWindowParameters, - PedersenCommitmentProjectiveCurve, - PedersenCommitmentProjectiveCurveVar, ->; +type RootVar = merkle_tree::constraint::RootVar<Configuration>; /// UTXO Set Parameters -type Parameters = merkle_tree::ParametersWrapper< - PedersenCommitmentWindowParameters, - PedersenCommitmentProjectiveCurve, - PedersenCommitmentProjectiveCurveVar, ->; +type Parameters = merkle_tree::Parameters<Configuration>; /// UTXO Set Parameters Variable -type ParametersVar = merkle_tree::ParametersVar< - PedersenCommitmentWindowParameters, - PedersenCommitmentProjectiveCurve, - PedersenCommitmentProjectiveCurveVar, ->; +type ParametersVar = merkle_tree::constraint::ParametersVar<Configuration>; /// UTXO Set Path -type Path = merkle_tree::PathWrapper< - PedersenCommitmentWindowParameters, - PedersenCommitmentProjectiveCurve, - PedersenCommitmentProjectiveCurveVar, ->; +type Path = merkle_tree::Path<Configuration>; /// UTXO Set Path Variable -type PathVar = merkle_tree::PathVar< - PedersenCommitmentWindowParameters, - PedersenCommitmentProjectiveCurve, - PedersenCommitmentProjectiveCurveVar, ->; +type PathVar = merkle_tree::constraint::PathVar<Configuration>; /// UTXO Shard #[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] @@ -164,13 +137,12 @@ impl Set for UtxoSet { #[inline] fn try_insert(&mut self, item: Self::Item) -> Result<(), Self::Item> { - // FIXME: This will panic because `shard.utxos.len()` is not always a power of two. let shard = &mut self.shards[Self::shard_index(&item)]; if shard.utxos.contains(&item) { return Err(item); } shard.utxos.push(item); - match MerkleTree::build_root_wrapped(&self.parameters, &shard.utxos) { + match MerkleTree::new_root(&self.parameters, &shard.utxos) { Some(root) => { shard.root = root; Ok(()) @@ -185,7 +157,6 @@ impl VerifiedSet for UtxoSet { type Secret = Path; - // TODO: Give a more informative error. type ContainmentError = (); #[inline] @@ -208,12 +179,12 @@ impl VerifiedSet for UtxoSet { &self, item: &Self::Item, ) -> Result<ContainmentProof<Self>, Self::ContainmentError> { - // FIXME: This will panic because `utxos.len()` is not always a power of two. + // TODO: Return a more informative error. let utxos = &self.shards[Self::shard_index(item)].utxos; match utxos.iter().position(move |u| u == item) { - Some(index) => MerkleTree::from_wrapped(&self.parameters, utxos) + Some(index) => MerkleTree::new(&self.parameters, utxos) .ok_or(())? - .get_wrapped_containment_proof(index) + .get_containment_proof(index) .ok_or(()), _ => Err(()), } diff --git a/manta-pay/src/accounting/mod.rs b/manta-pay/src/accounting/mod.rs index d926fe54d..196963849 100644 --- a/manta-pay/src/accounting/mod.rs +++ b/manta-pay/src/accounting/mod.rs @@ -17,6 +17,8 @@ //! Accounting Implementations pub mod config; +pub mod identity; pub mod keys; pub mod ledger; +pub mod transfer; pub mod wallet; diff --git a/manta-pay/src/accounting/transfer.rs b/manta-pay/src/accounting/transfer.rs new file mode 100644 index 000000000..b70a76c23 --- /dev/null +++ b/manta-pay/src/accounting/transfer.rs @@ -0,0 +1,38 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Transfer Implementations + +use crate::accounting::config::Configuration; +use manta_accounting::transfer::canonical; + +/// Mint Transaction Type +pub type Mint = canonical::Mint<Configuration>; + +/// Mint Transaction Post Type +pub type MintPost = canonical::MintPost<Configuration>; + +/// Private Transfer Transaction Type +pub type PrivateTransfer = canonical::PrivateTransfer<Configuration>; + +/// Private Transfer Transaction Post Type +pub type PrivateTransferPost = canonical::PrivateTransferPost<Configuration>; + +/// Reclaim Transaction Type +pub type Reclaim = canonical::Reclaim<Configuration>; + +/// Reclaim Transaction Post Type +pub type ReclaimPost = canonical::ReclaimPost<Configuration>; diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs index a7e230d0e..e906357bc 100644 --- a/manta-pay/src/crypto/commitment/pedersen.rs +++ b/manta-pay/src/crypto/commitment/pedersen.rs @@ -22,7 +22,11 @@ use ark_crypto_primitives::commitment::{ }; use ark_ff::bytes::ToBytes; use manta_crypto::commitment::CommitmentScheme; -use manta_util::{Concat, ConcatAccumulator}; +use manta_util::{rand::SizedRng, Concat, ConcatAccumulator}; +use rand::{ + distributions::{Distribution, Standard}, + RngCore, +}; /// Pedersen Window Parameters Trait // TODO: Remove this comment once `arkworks` writes their own. @@ -134,6 +138,20 @@ where } } +impl<W, C> Distribution<PedersenCommitment<W, C>> for Standard +where + W: PedersenWindow, + C: ProjectiveCurve, +{ + #[inline] + fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> PedersenCommitment<W, C> { + PedersenCommitment( + ArkPedersenCommitment::<_, W>::setup(&mut SizedRng(rng)) + .expect("Sampling is not allowed to fail."), + ) + } +} + /// Pedersen Commitment Scheme Constraint System Implementation pub mod constraint { use super::*; @@ -297,6 +315,19 @@ pub mod constraint { } } + impl<W, C, GG> Distribution<PedersenCommitmentWrapper<W, C, GG>> for Standard + where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + { + #[inline] + fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> PedersenCommitmentWrapper<W, C, GG> { + PedersenCommitmentWrapper(self.sample(rng), PhantomData) + } + } + /// Pedersen Commitment Randomness Variable #[derive(derivative::Derivative)] #[derivative(Clone(bound = ""))] @@ -322,6 +353,8 @@ pub mod constraint { cs: &mut ConstraintSystem<C>, allocation: Allocation<Self::Type, Self::Mode>, ) -> Self { + // SAFETY: We can use `empty` here because `RandomnessVar` has an internal default and + // so its allocation never fails. Self( match allocation.known() { Some((this, _)) => RandomnessVar::new_witness( diff --git a/manta-pay/src/crypto/constraint/constraint_system.rs b/manta-pay/src/crypto/constraint/constraint_system.rs index eb970fce1..c2b62e865 100644 --- a/manta-pay/src/crypto/constraint/constraint_system.rs +++ b/manta-pay/src/crypto/constraint/constraint_system.rs @@ -34,9 +34,16 @@ use manta_util::{Concat, ConcatAccumulator}; /// Synthesis Result pub type SynthesisResult<T = ()> = Result<T, ark_r1cs::SynthesisError>; -/// Returns an empty variable assignment. +/// Returns an empty variable assignment for setup mode. +/// +/// # Warning +/// +/// This does not work for all variable assignments! For some assignemnts, the variable inherits +/// some structure from its input even though the input itself will not form part of the proving +/// key and verifying key that we produce after compiling the constraint system. For those cases, +/// some mocking is required. #[inline] -pub const fn empty<T>() -> SynthesisResult<T> { +pub fn empty<T>() -> SynthesisResult<T> { Err(ark_r1cs::SynthesisError::AssignmentMissing) } diff --git a/manta-pay/src/crypto/constraint/proof_systems/groth16.rs b/manta-pay/src/crypto/constraint/proof_systems/groth16.rs index fff177b86..7bc95f222 100644 --- a/manta-pay/src/crypto/constraint/proof_systems/groth16.rs +++ b/manta-pay/src/crypto/constraint/proof_systems/groth16.rs @@ -16,6 +16,8 @@ //! Arkworks Groth16 Implementation +// FIXME: Move these tests elsewhere since they are rather general. + use crate::crypto::constraint::{constraint_system::SynthesisResult, ArkConstraintSystem}; use alloc::vec::Vec; use ark_crypto_primitives::SNARK; @@ -25,6 +27,7 @@ use ark_groth16::{Groth16 as ArkGroth16, PreparedVerifyingKey, Proof, ProvingKey use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError}; use core::marker::PhantomData; use manta_crypto::constraint::ProofSystem; +use manta_util::rand::SizedRng; use rand::{CryptoRng, RngCore}; /// Arkworks Groth 16 Proof System @@ -66,10 +69,12 @@ where rng: &mut R, ) -> Result<(Self::ProvingContext, Self::VerifyingContext), Self::Error> where - R: CryptoRng + RngCore, + R: CryptoRng + RngCore + ?Sized, { - let (proving_key, verifying_key) = - ArkGroth16::circuit_specific_setup(ConstraintSynthesizerWrapper(cs), rng)?; + let (proving_key, verifying_key) = ArkGroth16::circuit_specific_setup( + ConstraintSynthesizerWrapper(cs), + &mut SizedRng(rng), + )?; Ok((proving_key, ArkGroth16::process_vk(&verifying_key)?)) } @@ -80,7 +85,7 @@ where rng: &mut R, ) -> Result<Self::Proof, Self::Error> where - R: CryptoRng + RngCore, + R: CryptoRng + RngCore + ?Sized, { let input = cs .cs @@ -88,7 +93,11 @@ where .ok_or(SynthesisError::MissingCS)? .instance_assignment .clone(); - let proof = ArkGroth16::prove(context, ConstraintSynthesizerWrapper(cs), rng)?; + let proof = ArkGroth16::prove( + context, + ConstraintSynthesizerWrapper(cs), + &mut SizedRng(rng), + )?; Ok((input, proof)) } @@ -127,11 +136,40 @@ where #[cfg(test)] mod test { - use super::*; + use crate::accounting::{ + ledger::UtxoSet, + transfer::{Mint, PrivateTransfer, Reclaim}, + }; + use rand::{thread_rng, Rng}; + + /// Tests the generation of proving/verifying keys for [`PrivateTransfer`]. + #[test] + fn generate_private_transfer_keys() { + let mut rng = thread_rng(); + PrivateTransfer::generate_context(&rng.gen(), &UtxoSet::new(rng.gen()), &mut rng) + .unwrap() + .unwrap(); + } - /// Tests the generation of proving/verifying contexts. + /// Tests the generation of proving/verifying keys for [`Reclaim`]. #[test] - fn generate_context() {} + fn generate_reclaim_keys() { + let mut rng = thread_rng(); + Reclaim::generate_context(&rng.gen(), &UtxoSet::new(rng.gen()), &mut rng) + .unwrap() + .unwrap(); + } + + /// Tries to generate proving/verifying keys for [`Mint`] but this does not work because + /// [`Mint`] does not require a proof. + #[test] + #[should_panic] + fn generate_mint_keys_is_impossible() { + let mut rng = thread_rng(); + Mint::generate_context(&rng.gen(), &UtxoSet::new(rng.gen()), &mut rng) + .unwrap() + .unwrap(); + } /// Tests the generation of proofs. #[test] diff --git a/manta-pay/src/crypto/merkle_tree/basic.rs b/manta-pay/src/crypto/merkle_tree/basic.rs deleted file mode 100644 index 6f9100eed..000000000 --- a/manta-pay/src/crypto/merkle_tree/basic.rs +++ /dev/null @@ -1,522 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Basic Merkle Tree Implementation - -// NOTE: Most if not all of the fallible interfaces in this file never actually fail. We use -// faillible interfaces so that we don't have to depend explicitly on implementation -// details of the `arkworks` project. - -// TODO: We use the Pedersen commitment settings for `CRH` and `TwoToOneCRH`. We should write our -// own `CRH` and `TwoToOneCRH` traits and then in the configuration we align them with the -// Pedersen settings. - -use crate::crypto::{ - commitment::pedersen::{PedersenWindow, ProjectiveCurve}, - constraint::{empty, full, ArkConstraintSystem}, -}; -use alloc::vec::Vec; -use ark_crypto_primitives::{ - crh::{ - constraints::{CRHGadget as CRHGadgetTrait, TwoToOneCRHGadget as TwoToOneCRHGadgetTrait}, - pedersen::{constraints::CRHGadget, CRH}, - }, - merkle_tree::{ - constraints::PathVar as ArkPathVar, Config, LeafParam as ArkLeafParam, - MerkleTree as ArkMerkleTree, Path as ArkPath, TwoToOneDigest, - TwoToOneParam as ArkTwoToOneParam, - }, -}; -use ark_ff::Field; -use ark_r1cs_std::{ - alloc::AllocVar, - boolean::Boolean, - eq::EqGadget, - groups::{CurveVar, GroupOpsBounds}, - uint8::UInt8, -}; -use ark_relations::ns; -use core::marker::PhantomData; -use manta_crypto::{ - constraint::{reflection::HasAllocation, Allocation, Constant, Public, Secret, Variable}, - set::{ContainmentProof, VerifiedSet}, -}; -use manta_util::{as_bytes, Concat}; - -/// Constraint Field Type -pub type ConstraintField<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField; - -/// Constraint System Type -pub type ContraintSystem<C> = ArkConstraintSystem<ConstraintField<C>>; - -/// Merkle Tree Configuration -#[derive(derivative::Derivative)] -#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Configuration<W, C>(PhantomData<(W, C)>) -where - W: PedersenWindow, - C: ProjectiveCurve; - -impl<W, C> Config for Configuration<W, C> -where - W: PedersenWindow, - C: ProjectiveCurve, -{ - type LeafHash = CRH<C, W>; - type TwoToOneHash = CRH<C, W>; -} - -/// Leaf Hash Parameters Type -type LeafParam<W, C> = ArkLeafParam<Configuration<W, C>>; - -/// Two-to-One Hash Parameters Type -type TwoToOneParam<W, C> = ArkTwoToOneParam<Configuration<W, C>>; - -/// Leaf Hash Parameters Variable -type LeafParamVar<W, C, GG> = <CRHGadget<C, GG, W> as CRHGadgetTrait< - <Configuration<W, C> as Config>::LeafHash, - ConstraintField<C>, ->>::ParametersVar; - -/// Two-to-One Hash Parameters Variable -type TwoToOneParamVar<W, C, GG> = <CRHGadget<C, GG, W> as TwoToOneCRHGadgetTrait< - <Configuration<W, C> as Config>::TwoToOneHash, - ConstraintField<C>, ->>::ParametersVar; - -/// Merkle Tree Parameters -#[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""))] -pub struct Parameters<W, C> -where - W: PedersenWindow, - C: ProjectiveCurve, -{ - /// Leaf Hash Parameters - leaf: LeafParam<W, C>, - - /// Two-to-One Hash Parameters - two_to_one: TwoToOneParam<W, C>, -} - -impl<W, C> Parameters<W, C> -where - W: PedersenWindow, - C: ProjectiveCurve, -{ - /// Verifies that `path` constitutes a proof that `item` is contained in the Merkle Tree - /// with the given `root`. - #[inline] - pub fn verify<T>(&self, root: &Root<W, C>, path: &Path<W, C>, item: &T) -> bool - where - T: Concat<Item = u8>, - { - path.0 - .verify(&self.leaf, &self.two_to_one, &root.0, &as_bytes!(item)) - .expect("As of arkworks 0.3.0, this never fails.") - } -} - -/// Merkle Tree Parameters Wrapper -#[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""))] -pub struct ParametersWrapper<W, C, GG>(Parameters<W, C>, PhantomData<GG>) -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; - -impl<W, C, GG> ParametersWrapper<W, C, GG> -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, -{ - /// Verifies that `path` constitutes a proof that `item` is contained in the Merkle Tree - /// with the given `root`. - #[inline] - pub fn verify<T>( - &self, - root: &RootWrapper<W, C, GG>, - path: &PathWrapper<W, C, GG>, - item: &T, - ) -> bool - where - T: Concat<Item = u8>, - { - self.0.verify(&root.0, &path.0, item) - } -} - -/// Merkle Tree Parameters Variable -#[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""))] -pub struct ParametersVar<W, C, GG> -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, -{ - /// Leaf Hash Parameters Variable - leaf: LeafParamVar<W, C, GG>, - - /// Two-to-One Hash Parameters Variable - two_to_one: TwoToOneParamVar<W, C, GG>, -} - -impl<W, C, GG> ParametersVar<W, C, GG> -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, -{ - /// Verifies that `path` constitutes a proof that `item` is contained in the Merkle Tree - /// with the given `root`. - #[inline] - pub fn verify( - &self, - root: &RootVar<W, C, GG>, - path: &PathVar<W, C, GG>, - item: &[UInt8<ConstraintField<C>>], - ) -> Boolean<ConstraintField<C>> { - path.0 - .verify_membership(&self.leaf, &self.two_to_one, &root.0, &item) - .expect("This is not allowed to fail.") - } - - /// Asserts that `path` constitutes a proof that `item` is contained in the Merkle Tree - /// with the given `root`. - #[inline] - pub fn assert_verified( - &self, - root: &RootVar<W, C, GG>, - path: &PathVar<W, C, GG>, - item: &[UInt8<ConstraintField<C>>], - ) { - self.verify(root, path, item) - .enforce_equal(&Boolean::TRUE) - .expect("This is not allowed to fail.") - } -} - -impl<W, C, GG> Variable<ContraintSystem<C>> for ParametersVar<W, C, GG> -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, -{ - type Type = ParametersWrapper<W, C, GG>; - - type Mode = Constant; - - #[inline] - fn new(cs: &mut ContraintSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - let (this, _) = allocation.into_known(); - ParametersVar { - leaf: LeafParamVar::<W, _, _>::new_constant( - ns!(cs.cs, "leaf hash parameter constant"), - &this.0.leaf, - ) - .expect("Variable allocation is not allowed to fail."), - two_to_one: TwoToOneParamVar::<W, _, _>::new_constant( - ns!(cs.cs, "two-to-one hash parameter constant"), - &this.0.two_to_one, - ) - .expect("Variable allocation is not allowed to fail."), - } - } -} - -impl<W, C, GG> HasAllocation<ContraintSystem<C>> for ParametersWrapper<W, C, GG> -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, -{ - type Variable = ParametersVar<W, C, GG>; - - type Mode = Constant; -} - -/// Merkle Tree Root Inner Type -type RootInnerType<W, C> = TwoToOneDigest<Configuration<W, C>>; - -/// Merkle Tree Root -#[derive(derivative::Derivative)] -#[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] -pub struct Root<W, C>(RootInnerType<W, C>) -where - W: PedersenWindow, - C: ProjectiveCurve; - -/// Merkle Tree Root Wrapper -#[derive(derivative::Derivative)] -#[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] -pub struct RootWrapper<W, C, GG>(Root<W, C>, PhantomData<GG>) -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; - -/// Merkle Tree Root Variable -#[derive(derivative::Derivative)] -#[derivative(Clone)] -pub struct RootVar<W, C, GG>(GG, PhantomData<(W, C)>) -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; - -impl<W, C, GG> Variable<ContraintSystem<C>> for RootVar<W, C, GG> -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, -{ - type Type = RootWrapper<W, C, GG>; - - type Mode = Public; - - #[inline] - fn new(cs: &mut ContraintSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - RootVar( - match allocation.known() { - Some((this, _)) => AllocVar::<RootInnerType<W, C>, _>::new_input( - ns!(cs.cs, "merkle tree root public input"), - full((this.0).0), - ), - _ => AllocVar::<RootInnerType<W, C>, _>::new_input( - ns!(cs.cs, "merkle tree root public input"), - empty::<RootInnerType<W, C>>, - ), - } - .expect("Variable allocation is not allowed to fail."), - PhantomData, - ) - } -} - -impl<W, C, GG> HasAllocation<ContraintSystem<C>> for RootWrapper<W, C, GG> -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, -{ - type Variable = RootVar<W, C, GG>; - type Mode = Public; -} - -/// Merkle Tree Path Inner Type -type PathInnerType<W, C> = ArkPath<Configuration<W, C>>; - -/// Merkle Tree Path -#[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""))] -pub struct Path<W, C>(PathInnerType<W, C>) -where - W: PedersenWindow, - C: ProjectiveCurve; - -/// Merkle Tree Path Wrapper -#[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""))] -pub struct PathWrapper<W, C, GG>(Path<W, C>, PhantomData<GG>) -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; - -/// Merkle Tree Path Variable Inner Type -type PathVarInnerType<W, C, GG> = - ArkPathVar<Configuration<W, C>, CRHGadget<C, GG, W>, CRHGadget<C, GG, W>, ConstraintField<C>>; - -/// Merkle Tree Path Variable -pub struct PathVar<W, C, GG>(PathVarInnerType<W, C, GG>) -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; - -impl<W, C, GG> Variable<ContraintSystem<C>> for PathVar<W, C, GG> -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, -{ - type Type = PathWrapper<W, C, GG>; - - type Mode = Secret; - - #[inline] - fn new(cs: &mut ContraintSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - PathVar( - match allocation.known() { - Some((this, _)) => PathVarInnerType::new_witness(ns!(cs.cs, ""), full(&(this.0).0)), - _ => PathVarInnerType::new_witness(ns!(cs.cs, ""), empty::<PathInnerType<W, C>>), - } - .expect("Variable allocation is not allowed to fail."), - ) - } -} - -impl<W, C, GG> HasAllocation<ContraintSystem<C>> for PathWrapper<W, C, GG> -where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, -{ - type Variable = PathVar<W, C, GG>; - type Mode = Secret; -} - -/// Merkle Tree -#[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""))] -pub struct MerkleTree<W, C>(ArkMerkleTree<Configuration<W, C>>) -where - W: PedersenWindow, - C: ProjectiveCurve; - -impl<W, C> MerkleTree<W, C> -where - W: PedersenWindow, - C: ProjectiveCurve, -{ - /// Builds a new [`MerkleTree`]. - /// - /// # Panics - /// - /// The length of `leaves` must be a power of 2 or this function will panic. - #[inline] - pub fn new<T>(parameters: &Parameters<W, C>, leaves: &[T]) -> Option<Self> - where - T: Concat<Item = u8>, - { - Some(Self( - ArkMerkleTree::new( - &parameters.leaf, - &parameters.two_to_one, - &leaves - .iter() - .map(move |leaf| as_bytes!(leaf)) - .collect::<Vec<_>>(), - ) - .ok()?, - )) - } - - /// Builds a new [`MerkleTree`]. - /// - /// # Panics - /// - /// The length of `leaves` must be a power of 2 or this function will panic. - #[inline] - pub fn from_wrapped<GG, T>( - parameters: &ParametersWrapper<W, C, GG>, - leaves: &[T], - ) -> Option<Self> - where - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - T: Concat<Item = u8>, - { - Self::new(&parameters.0, leaves) - } - - /// Computes the [`Root`] of the [`MerkleTree`] built from the `leaves`. - #[inline] - pub fn build_root<T>(parameters: &Parameters<W, C>, leaves: &[T]) -> Option<Root<W, C>> - where - T: Concat<Item = u8>, - { - Some(Self::new(parameters, leaves)?.root()) - } - - /// Computes the [`RootWrapper`] of the [`MerkleTree`] built from the `leaves`. - #[inline] - pub fn build_root_wrapped<GG, T>( - parameters: &ParametersWrapper<W, C, GG>, - leaves: &[T], - ) -> Option<RootWrapper<W, C, GG>> - where - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - T: Concat<Item = u8>, - { - Self::build_root(&parameters.0, leaves).map(move |r| RootWrapper(r, PhantomData)) - } - - /// Returns the [`Root`] of this [`MerkleTree`]. - #[inline] - pub fn root(&self) -> Root<W, C> { - Root(self.0.root()) - } - - /// Returns the [`RootWrapper`] of this [`MerkleTree`]. - #[inline] - pub fn root_wrapped<GG>(&self) -> RootWrapper<W, C, GG> - where - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - { - RootWrapper(self.root(), PhantomData) - } - - /// Computes the root and the path for the leaf at the given `index`. - #[inline] - fn compute_containment_proof(&self, index: usize) -> Option<(Root<W, C>, Path<W, C>)> { - Some((self.root(), Path(self.0.generate_proof(index).ok()?))) - } - - /// Builds a containment proof (i.e. merkle root and path) for the leaf at the given `index`. - #[inline] - pub fn get_containment_proof<S>(&self, index: usize) -> Option<ContainmentProof<S>> - where - S: VerifiedSet<Public = Root<W, C>, Secret = Path<W, C>>, - { - let (root, path) = self.compute_containment_proof(index)?; - Some(ContainmentProof::new(root, path)) - } - - /// Builds a containment proof (i.e. merkle root and path) for the leaf at the given `index`. - #[inline] - pub fn get_wrapped_containment_proof<GG, S>(&self, index: usize) -> Option<ContainmentProof<S>> - where - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - S: VerifiedSet<Public = RootWrapper<W, C, GG>, Secret = PathWrapper<W, C, GG>>, - { - let (root, path) = self.compute_containment_proof(index)?; - Some(ContainmentProof::new( - RootWrapper(root, PhantomData), - PathWrapper(path, PhantomData), - )) - } -} diff --git a/manta-pay/src/crypto/merkle_tree/incremental.rs b/manta-pay/src/crypto/merkle_tree/incremental.rs index c1963f2cc..535951bf3 100644 --- a/manta-pay/src/crypto/merkle_tree/incremental.rs +++ b/manta-pay/src/crypto/merkle_tree/incremental.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Incremental Merkle Tree Implementation +//! Arkworks Incremental Merkle Tree Implementation use alloc::vec::Vec; use ark_crypto_primitives::{ diff --git a/manta-pay/src/crypto/merkle_tree/mod.rs b/manta-pay/src/crypto/merkle_tree/mod.rs index 4d0a4ce96..0c95b41bc 100644 --- a/manta-pay/src/crypto/merkle_tree/mod.rs +++ b/manta-pay/src/crypto/merkle_tree/mod.rs @@ -16,8 +16,544 @@ //! Merkle Tree Implementations -mod basic; +// NOTE: Most if not all of the fallible interfaces in this file never actually fail. We use +// faillible interfaces so that we don't have to depend explicitly on implementation +// details of the `arkworks` project. + +// TODO: Improve the interface so that we don't have to worry about panicking interfaces from +// arkworks (automatic padding, etc.). +// TODO: We should think about how much of this file can be moved to `manta_crypto`. + +// NOTE: This is meant to be a full implementation of the incremental merkle tree type suitable for +// merging into arkworks itself. Therefore, even if we don't use all of the functionality +// available in this module, we want to preserve the code anyway. +#[allow(dead_code)] mod incremental; -pub use basic::*; -pub use incremental::*; +use crate::crypto::constraint::{empty, full, ArkConstraintSystem}; +use alloc::vec::Vec; +use ark_crypto_primitives::{ + crh::{TwoToOneCRH, CRH}, + merkle_tree::{Config, MerkleTree as ArkMerkleTree, Path as ArkPath, TwoToOneDigest}, +}; +use core::marker::PhantomData; +use manta_crypto::set::{ContainmentProof, VerifiedSet}; +use manta_util::{as_bytes, rand::SizedRng, Concat}; +use rand::{ + distributions::{Distribution, Standard}, + RngCore, +}; + +/// Merkle Tree Configuration +pub trait Configuration { + /// Leaf Hash Type + type LeafHash: CRH; + + /// Inner Hash Type + type InnerHash: TwoToOneCRH; + + /// Merkle Tree Height + const HEIGHT: u32; +} + +/// Computes the Merkle Tree capacity given the `height`. +#[inline] +pub const fn capacity(height: u32) -> usize { + 2usize.pow(height) +} + +/// Computes the necessary padding required to fill the capacity of a Merkle Tree with the +/// given `height`. +/// +/// Returns `None` if `length` is larger than the capacity of the tree. +#[inline] +pub const fn padding_length(height: u32, length: usize) -> Option<usize> { + let capacity = capacity(height); + if length > capacity { + return None; + } + Some(capacity - length) +} + +/// Arkworks Configuration Converter +/// +/// Given any `C: Configuration`, this struct can be used as `ArkConfigConverter<C>` instead of `C` +/// in places where we need an implementation of the arkworks [`Config`] trait. +/// +/// This `struct` is meant only to be used in place of the type `C`, so any values of this `struct` +/// have no meaning. +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct ArkConfigConverter<C>(PhantomData<C>) +where + C: Configuration; + +impl<C> Config for ArkConfigConverter<C> +where + C: Configuration, +{ + type LeafHash = C::LeafHash; + type TwoToOneHash = C::InnerHash; +} + +/// Leaf Hash Type +type LeafHash<C> = <C as Configuration>::LeafHash; + +/// Inner Hash Type +type InnerHash<C> = <C as Configuration>::InnerHash; + +/// Leaf Hash Parameters Type +type LeafHashParameters<C> = <LeafHash<C> as CRH>::Parameters; + +/// Inner Hash Parameters Type +type InnerHashParameters<C> = <InnerHash<C> as TwoToOneCRH>::Parameters; + +/// Merkle Tree Parameters +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""))] +pub struct Parameters<C> +where + C: Configuration, +{ + /// Leaf Hash Parameters + pub leaf: LeafHashParameters<C>, + + /// Inner Hash Parameters + pub inner: InnerHashParameters<C>, +} + +impl<C> Parameters<C> +where + C: Configuration, +{ + /// Builds a new [`Parameters`] from `leaf` and `inner` parameters. + #[inline] + pub fn new(leaf: LeafHashParameters<C>, inner: InnerHashParameters<C>) -> Self { + Self { leaf, inner } + } + + /// Verifies that `path` constitutes a proof that `item` is contained in the Merkle Tree + /// with the given `root`. + #[inline] + pub fn verify<T>(&self, root: &Root<C>, path: &Path<C>, item: &T) -> bool + where + T: Concat<Item = u8>, + { + path.0 + .verify(&self.leaf, &self.inner, &root.0, &as_bytes!(item)) + .expect("As of arkworks 0.3.0, this never fails.") + } +} + +impl<C> Distribution<Parameters<C>> for Standard +where + C: Configuration, +{ + #[inline] + fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> Parameters<C> { + Parameters { + leaf: LeafHash::<C>::setup(&mut SizedRng(rng)) + .expect("Sampling is not allowed to fail."), + inner: InnerHash::<C>::setup(&mut SizedRng(rng)) + .expect("Sampling is not allowed to fail."), + } + } +} + +/// Merkle Tree Root Inner Type +type RootInnerType<C> = TwoToOneDigest<ArkConfigConverter<C>>; + +/// Merkle Tree Root +#[derive(derivative::Derivative)] +#[derivative(Clone, Debug, Default, Eq, Hash, PartialEq)] +pub struct Root<C>(RootInnerType<C>) +where + C: Configuration; + +/// Merkle Tree Path Inner Type +type PathInnerType<C> = ArkPath<ArkConfigConverter<C>>; + +/// Merkle Tree Path +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""))] +pub struct Path<C>(PathInnerType<C>) +where + C: Configuration; + +/// Merkle Tree Inner Type +type MerkleTreeInnerType<C> = ArkMerkleTree<ArkConfigConverter<C>>; + +/// Merkle Tree +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""))] +pub struct MerkleTree<C>(MerkleTreeInnerType<C>) +where + C: Configuration; + +impl<C> MerkleTree<C> +where + C: Configuration, +{ + /// Builds a new [`MerkleTree`]. + #[inline] + pub fn new<T>(parameters: &Parameters<C>, leaves: &[T]) -> Option<Self> + where + T: Concat<Item = u8>, + { + // FIXME: Add additional padding so we can be compatible with IMT. + + let leaves = leaves + .iter() + .map(move |leaf| as_bytes!(leaf)) + .collect::<Vec<_>>(); + + Some(Self( + ArkMerkleTree::new(&parameters.leaf, &parameters.inner, &leaves).ok()?, + )) + } + + /// Computes the [`Root`] of the [`MerkleTree`] built from the `leaves`. + #[inline] + pub fn new_root<T>(parameters: &Parameters<C>, leaves: &[T]) -> Option<Root<C>> + where + T: Concat<Item = u8>, + { + Some(Self::new(parameters, leaves)?.root()) + } + + /// Returns the capacity of this merkle tree. + #[inline] + pub fn capacity(&self) -> usize { + capacity(C::HEIGHT) + } + + /// Returns the height of this merkle tree. + #[inline] + pub fn height(&self) -> u32 { + C::HEIGHT + } + + /// Returns the [`Root`] of this merkle tree. + #[inline] + pub fn root(&self) -> Root<C> { + Root(self.0.root()) + } + + /// Builds a containment proof (i.e. merkle root and path) for the leaf at the given `index`. + #[inline] + pub fn get_containment_proof<S>(&self, index: usize) -> Option<ContainmentProof<S>> + where + S: VerifiedSet<Public = Root<C>, Secret = Path<C>>, + { + Some(ContainmentProof::new( + self.root(), + Path(self.0.generate_proof(index).ok()?), + )) + } +} + +/// Incremental Merkle Tree +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""))] +pub struct IncrementalMerkleTree<C>(incremental::IncrementalMerkleTree<ArkConfigConverter<C>>) +where + C: Configuration; + +impl<C> IncrementalMerkleTree<C> +where + C: Configuration, +{ + /// Builds a new [`IncrementalMerkleTree`]. + #[inline] + pub fn new(parameters: &Parameters<C>) -> Self { + Self(incremental::IncrementalMerkleTree::blank( + &parameters.leaf, + &parameters.inner, + C::HEIGHT as usize, + )) + } + + /// Returns the length of this incremental merkle tree. + #[inline] + pub fn len(&self) -> usize { + todo!() + } + + /// Returns `true` if this incremental merkle tree is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Returns the capacity of this incremental merkle tree. + #[inline] + pub fn capacity(&self) -> usize { + capacity(C::HEIGHT) + } + + /// Returns the height of this incremental merkle tree. + #[inline] + pub fn height(&self) -> u32 { + C::HEIGHT + } + + /// Appends an element to the incremental merkle tree, returning `false` if it has already + /// reached its capacity and can no longer accept new elements. + #[inline] + pub fn append<T>(&mut self, leaf: &T) -> bool + where + T: Concat<Item = u8>, + { + let _ = leaf; + todo!() + } + + /// Returns the [`Root`] of this incremental merkle tree. + #[inline] + pub fn root(&self) -> Root<C> { + Root(self.0.root().clone()) + } +} + +/// Merkle Tree Constraint System Variables +pub mod constraint { + use super::*; + use ark_crypto_primitives::{ + crh::constraints::{CRHGadget, TwoToOneCRHGadget}, + merkle_tree::constraints::PathVar as ArkPathVar, + }; + use ark_ff::Field; + use ark_r1cs_std::{alloc::AllocVar, boolean::Boolean, eq::EqGadget, uint8::UInt8}; + use ark_relations::ns; + use manta_crypto::constraint::{ + reflection::HasAllocation, Allocation, Constant, Public, Secret, Variable, + }; + + /// Merkle Tree Constraint System Configuration + pub trait Configuration: super::Configuration { + /// Constraint Field Type + type ConstraintField: Field; + + /// Leaf Hash Variable Type + type LeafHashVar: CRHGadget<Self::LeafHash, Self::ConstraintField>; + + /// Inner Hash Variable Type + type InnerHashVar: TwoToOneCRHGadget<Self::InnerHash, Self::ConstraintField>; + } + + /// Constraint Field Type + pub type ConstraintField<C> = <C as Configuration>::ConstraintField; + + /// Constraint System Type + pub type ContraintSystem<C> = ArkConstraintSystem<ConstraintField<C>>; + + /// Leaf Hash Type + type LeafHashVar<C> = <C as Configuration>::LeafHashVar; + + /// Inner Hash Type + type InnerHashVar<C> = <C as Configuration>::InnerHashVar; + + /// Leaf Hash Parameters Type + type LeafHashParametersVar<C> = + <LeafHashVar<C> as CRHGadget<LeafHash<C>, ConstraintField<C>>>::ParametersVar; + + /// Inner Hash Parameters Type + type InnerHashParametersVar<C> = + <InnerHashVar<C> as TwoToOneCRHGadget<InnerHash<C>, ConstraintField<C>>>::ParametersVar; + + /// Merkle Tree Parameters Variable + #[derive(derivative::Derivative)] + #[derivative(Clone(bound = ""))] + pub struct ParametersVar<C> + where + C: Configuration, + { + /// Leaf Hash Parameters Variable + pub leaf: LeafHashParametersVar<C>, + + /// Inner Hash Parameters Variable + pub inner: InnerHashParametersVar<C>, + } + + impl<C> ParametersVar<C> + where + C: Configuration, + { + /// Builds a new [`ParametersVar`] from `leaf` and `inner` parameters. + #[inline] + pub fn new(leaf: LeafHashParametersVar<C>, inner: InnerHashParametersVar<C>) -> Self { + Self { leaf, inner } + } + + /// Verifies that `path` constitutes a proof that `item` is contained in the Merkle Tree + /// with the given `root`. + #[inline] + pub fn verify( + &self, + root: &RootVar<C>, + path: &PathVar<C>, + item: &[UInt8<ConstraintField<C>>], + ) -> Boolean<ConstraintField<C>> { + path.0 + .verify_membership(&self.leaf, &self.inner, &root.0, &item) + .expect("This is not allowed to fail.") + } + + /// Asserts that `path` constitutes a proof that `item` is contained in the Merkle Tree + /// with the given `root`. + #[inline] + pub fn assert_verified( + &self, + root: &RootVar<C>, + path: &PathVar<C>, + item: &[UInt8<ConstraintField<C>>], + ) { + self.verify(root, path, item) + .enforce_equal(&Boolean::TRUE) + .expect("This is not allowed to fail.") + } + } + + impl<C> Variable<ContraintSystem<C>> for ParametersVar<C> + where + C: Configuration, + { + type Type = Parameters<C>; + + type Mode = Constant; + + #[inline] + fn new( + cs: &mut ContraintSystem<C>, + allocation: Allocation<Self::Type, Self::Mode>, + ) -> Self { + let (this, _) = allocation.into_known(); + ParametersVar::new( + LeafHashParametersVar::<C>::new_constant( + ns!(cs.cs, "leaf hash parameter constant"), + &this.leaf, + ) + .expect("Variable allocation is not allowed to fail."), + InnerHashParametersVar::<C>::new_constant( + ns!(cs.cs, "two-to-one hash parameter constant"), + &this.inner, + ) + .expect("Variable allocation is not allowed to fail."), + ) + } + } + + impl<C> HasAllocation<ContraintSystem<C>> for Parameters<C> + where + C: Configuration, + { + type Variable = ParametersVar<C>; + type Mode = Constant; + } + + /// Merkle Tree Root Variable Inner Type + type RootVarInnerType<C> = + <InnerHashVar<C> as TwoToOneCRHGadget<InnerHash<C>, ConstraintField<C>>>::OutputVar; + + /// Merkle Tree Root Variable + #[derive(derivative::Derivative)] + #[derivative(Clone)] + pub struct RootVar<C>(RootVarInnerType<C>) + where + C: Configuration; + + impl<C> Variable<ContraintSystem<C>> for RootVar<C> + where + C: Configuration, + { + type Type = Root<C>; + + type Mode = Public; + + #[inline] + fn new( + cs: &mut ContraintSystem<C>, + allocation: Allocation<Self::Type, Self::Mode>, + ) -> Self { + RootVar( + match allocation.known() { + Some((this, _)) => AllocVar::<RootInnerType<C>, _>::new_input( + ns!(cs.cs, "merkle tree root public input"), + full(&this.0), + ), + _ => AllocVar::<RootInnerType<C>, _>::new_input( + ns!(cs.cs, "merkle tree root public input"), + empty::<RootInnerType<C>>, + ), + } + .expect("Variable allocation is not allowed to fail."), + ) + } + } + + impl<C> HasAllocation<ContraintSystem<C>> for Root<C> + where + C: Configuration, + { + type Variable = RootVar<C>; + type Mode = Public; + } + + /// Merkle Tree Path Variable Inner Type + type PathVarInnerType<C> = + ArkPathVar<ArkConfigConverter<C>, LeafHashVar<C>, InnerHashVar<C>, ConstraintField<C>>; + + /// Merkle Tree Path Variable + pub struct PathVar<C>(PathVarInnerType<C>) + where + C: Configuration; + + impl<C> Variable<ContraintSystem<C>> for PathVar<C> + where + C: Configuration, + { + type Type = Path<C>; + + type Mode = Secret; + + #[inline] + fn new( + cs: &mut ContraintSystem<C>, + allocation: Allocation<Self::Type, Self::Mode>, + ) -> Self { + PathVar( + match allocation.known() { + Some((this, _)) => PathVarInnerType::new_witness( + ns!(cs.cs, "path variable secret witness"), + full(&this.0), + ), + _ => { + // FIXME: We can't use `empty` here. What do we do? + // + // > The circuit we output must contain the height of the merkle tree + // we are using for containment proofs. Since this is mandatory, + // arkworks just forces you to build a path variable from a real path + // even if you are just trying to build the circuit keys. So to solve + // this, we need to find a way to mock the path of the correct height + // (sample it from some distribution) so that when we create the + // variable, it will have the necessary constraints to build the keys. + // + PathVarInnerType::new_witness( + ns!(cs.cs, "path variable secret witness"), + empty::<PathInnerType<C>>, + ) + } + } + .expect("Variable allocation is not allowed to fail."), + ) + } + } + + impl<C> HasAllocation<ContraintSystem<C>> for Path<C> + where + C: Configuration, + { + type Variable = PathVar<C>; + type Mode = Secret; + } +} diff --git a/manta-pay/src/crypto/prf/blake2s.rs b/manta-pay/src/crypto/prf/blake2s.rs index e5873c139..b9ea8ee31 100644 --- a/manta-pay/src/crypto/prf/blake2s.rs +++ b/manta-pay/src/crypto/prf/blake2s.rs @@ -251,6 +251,8 @@ pub mod constraint { cs: &mut ConstraintSystem<F>, allocation: Allocation<Self::Type, Self::Mode>, ) -> Self { + // SAFETY: We can use `empty` here because `Blake2sOutputVarInnerType` has an internal + // default and so its allocation never fails. Self( match allocation { Allocation::Known(this, mode) => match mode { diff --git a/manta-util/Cargo.toml b/manta-util/Cargo.toml index a14a9a7f7..ebbf6638f 100644 --- a/manta-util/Cargo.toml +++ b/manta-util/Cargo.toml @@ -23,3 +23,6 @@ rustdoc-args = ["--cfg", "doc_cfg"] is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } + +[dependencies] +rand_core = { version = "0.6.3", optional = true, default-features = false } diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index efbfbd07d..e118857c9 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -27,6 +27,10 @@ mod array; mod concat; mod mixed_chain; +#[cfg(feature = "rand_core")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "rand_core")))] +pub mod rand; + pub use array::*; pub use concat::*; pub use mixed_chain::*; diff --git a/manta-util/src/rand.rs b/manta-util/src/rand.rs new file mode 100644 index 000000000..ed700335d --- /dev/null +++ b/manta-util/src/rand.rs @@ -0,0 +1,66 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Random Number Generator Utilities + +use rand_core::{block::BlockRngCore, CryptoRng, Error, RngCore}; + +/// Random Number Generator Sized Wrapper +#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct SizedRng<'r, R>(pub &'r mut R) +where + R: ?Sized; + +impl<'r, R> CryptoRng for SizedRng<'r, R> where R: CryptoRng + ?Sized {} + +impl<'r, R> RngCore for SizedRng<'r, R> +where + R: RngCore + ?Sized, +{ + #[inline] + fn next_u32(&mut self) -> u32 { + self.0.next_u32() + } + + #[inline] + fn next_u64(&mut self) -> u64 { + self.0.next_u64() + } + + #[inline] + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.0.fill_bytes(dest) + } + + #[inline] + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + self.0.try_fill_bytes(dest) + } +} + +impl<'r, R> BlockRngCore for SizedRng<'r, R> +where + R: BlockRngCore + ?Sized, +{ + type Item = R::Item; + + type Results = R::Results; + + #[inline] + fn generate(&mut self, results: &mut Self::Results) { + self.0.generate(results) + } +} From 848d0a1d16c23dde5889b6778bc56d48be8f0f85 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 17 Sep 2021 23:51:17 -0400 Subject: [PATCH 040/275] improve naming conventions; move items to utils --- manta-accounting/Cargo.toml | 2 +- manta-accounting/src/identity.rs | 1000 +++++++++++++------------- manta-accounting/src/transfer.rs | 59 +- manta-accounting/src/wallet.rs | 25 +- manta-crypto/Cargo.toml | 2 +- manta-pay/src/accounting/config.rs | 16 +- manta-pay/src/accounting/identity.rs | 14 +- manta-pay/src/accounting/ledger.rs | 2 +- manta-pay/src/crypto/mod.rs | 1 - manta-pay/src/crypto/prf/blake2s.rs | 7 + manta-pay/src/crypto/rand.rs | 73 -- manta-util/src/rand.rs | 94 ++- 12 files changed, 666 insertions(+), 629 deletions(-) delete mode 100644 manta-pay/src/crypto/rand.rs diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 51fb115e4..8f9422f70 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -26,7 +26,7 @@ maintenance = { status = "actively-developed" } [features] # Constraint System Gadgets -constraints = [] +# TODO: constraints = [] # Cocoon Filesystem Adapters cocoon-adapters = ["cocoon/std", "zeroize"] diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index f11982d20..367247052 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -19,7 +19,7 @@ // FIXME: Check the secret key APIs. // FIXME: Should the identity types have methods that expose their members? Or should it be // completely opaque, and let the internal APIs handle all the logic? -// TODO: Since `IdentityConfiguration::SecretKey: Clone`, should `Identity: Clone`? +// TODO: Since `Configuration::SecretKey: Clone`, should `Identity: Clone`? // TODO: Separate "constraint" types into another module just like `manta_crypto::set`. use crate::{ @@ -30,16 +30,8 @@ use crate::{ use core::{convert::Infallible, fmt::Debug, hash::Hash, marker::PhantomData}; use manta_crypto::{ commitment::{CommitmentScheme, Input as CommitmentInput}, - constraint::{ - reflection::{HasAllocation, HasVariable, Var}, - Allocation, Constant, ConstraintSystem, Derived, Equal, Public, PublicOrSecret, Secret, - Variable, - }, ies::{self, EncryptedMessage, IntegratedEncryptionScheme}, - set::{ - constraint::{ContainmentProofVar, VerifiedSetVariable}, - ContainmentProof, VerifiedSet, - }, + set::{ContainmentProof, VerifiedSet}, PseudorandomFunctionFamily, }; use rand::{ @@ -50,13 +42,13 @@ use rand::{ pub(crate) mod prelude { #[doc(inline)] pub use super::{ - Identity, IdentityConfiguration, IdentityConstraintSystemConfiguration, Receiver, Sender, - SenderError, ShieldedIdentity, Spend, SpendError, Utxo, VoidNumber, + Configuration, Identity, Receiver, Sender, SenderError, ShieldedIdentity, Spend, + SpendError, Utxo, VoidNumber, }; } /// [`Identity`] Configuration -pub trait IdentityConfiguration { +pub trait Configuration { /// Secret Key Type type SecretKey: Clone; @@ -74,80 +66,24 @@ pub trait IdentityConfiguration { type Rng: CryptoRng + RngCore + SeedableRng<Seed = Self::SecretKey>; } -/// [`Identity`] Constraint System Configuration -pub trait IdentityConstraintSystemConfiguration: IdentityConfiguration { - /// Constraint System - type ConstraintSystem: ConstraintSystem - + HasVariable<AssetId, Mode = PublicOrSecret> - + HasVariable<AssetBalance, Mode = PublicOrSecret>; - - /// Secret Key Variable - type SecretKeyVar: Variable<Self::ConstraintSystem, Type = Self::SecretKey, Mode = Secret>; - - /// Pseudorandom Function Family Input Variable - type PseudorandomFunctionFamilyInputVar: Variable< - Self::ConstraintSystem, - Type = <Self::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Input, - Mode = Secret, - >; - - /// Pseudorandom Function Family Output Variable - type PseudorandomFunctionFamilyOutputVar: Variable< - Self::ConstraintSystem, - Type = <Self::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Output, - Mode = PublicOrSecret, - > + Equal<Self::ConstraintSystem>; - - /// Pseudorandom Function Family Variable - type PseudorandomFunctionFamilyVar: PseudorandomFunctionFamily< - Seed = Self::SecretKeyVar, - Input = Self::PseudorandomFunctionFamilyInputVar, - Output = Self::PseudorandomFunctionFamilyOutputVar, - > + Variable<Self::ConstraintSystem, Type = Self::PseudorandomFunctionFamily, Mode = Constant>; - - /// Commitment Scheme Randomness Variable - type CommitmentSchemeRandomnessVar: Variable< - Self::ConstraintSystem, - Type = <Self::CommitmentScheme as CommitmentScheme>::Randomness, - Mode = Secret, - >; - - /// Commitment Scheme Output Variable - type CommitmentSchemeOutputVar: Variable< - Self::ConstraintSystem, - Type = <Self::CommitmentScheme as CommitmentScheme>::Output, - Mode = PublicOrSecret, - > + Equal<Self::ConstraintSystem>; - - /// Commitment Scheme Variable - type CommitmentSchemeVar: CommitmentScheme< - Randomness = Self::CommitmentSchemeRandomnessVar, - Output = Self::CommitmentSchemeOutputVar, - > + CommitmentInput<PublicKeyVar<Self>> - + CommitmentInput<VoidNumberGeneratorVar<Self>> - + CommitmentInput<AssetVar<Self::ConstraintSystem>> - + CommitmentInput<VoidNumberCommitmentVar<Self>> - + Variable<Self::ConstraintSystem, Type = Self::CommitmentScheme, Mode = Constant>; -} - /// [`PseudorandomFunctionFamily::Input`] Type type PseudorandomFunctionFamilyInput<C> = - <<C as IdentityConfiguration>::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Input; + <<C as Configuration>::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Input; /// [`PseudorandomFunctionFamily::Output`] Type type PseudorandomFunctionFamilyOutput<C> = - <<C as IdentityConfiguration>::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Output; + <<C as Configuration>::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Output; /// [`CommitmentScheme::Randomness`] Type type CommitmentSchemeRandomness<C> = - <<C as IdentityConfiguration>::CommitmentScheme as CommitmentScheme>::Randomness; + <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Randomness; /// [`CommitmentScheme::Output`] Type type CommitmentSchemeOutput<C> = - <<C as IdentityConfiguration>::CommitmentScheme as CommitmentScheme>::Output; + <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Output; /// Secret Key Type -pub type SecretKey<C> = <C as IdentityConfiguration>::SecretKey; +pub type SecretKey<C> = <C as Configuration>::SecretKey; /// Public Key Type pub type PublicKey<C> = PseudorandomFunctionFamilyOutput<C>; @@ -170,50 +106,6 @@ pub type UtxoRandomness<C> = CommitmentSchemeRandomness<C>; /// UTXO Type pub type Utxo<C> = CommitmentSchemeOutput<C>; -/// [`PseudorandomFunctionFamily::Input`] Variable Type -type PseudorandomFunctionFamilyInputVar<C> = - <C as IdentityConstraintSystemConfiguration>::PseudorandomFunctionFamilyInputVar; - -/// [`PseudorandomFunctionFamily::Output`] Variable Type -type PseudorandomFunctionFamilyOutputVar<C> = - <C as IdentityConstraintSystemConfiguration>::PseudorandomFunctionFamilyOutputVar; - -/// [`CommitmentScheme::Randomness`] Variable Type -type CommitmentSchemeRandomnessVar<C> = - <C as IdentityConstraintSystemConfiguration>::CommitmentSchemeRandomnessVar; - -/// [`CommitmentScheme::Output`] Variable Type -type CommitmentSchemeOutputVar<C> = - <C as IdentityConstraintSystemConfiguration>::CommitmentSchemeOutputVar; - -/// Secret Key Variable Type -pub type SecretKeyVar<C> = <C as IdentityConstraintSystemConfiguration>::SecretKeyVar; - -/// Public Key Variable Type -pub type PublicKeyVar<C> = PseudorandomFunctionFamilyOutputVar<C>; - -/// Void Number Generator Variable Type -pub type VoidNumberGeneratorVar<C> = PseudorandomFunctionFamilyInputVar<C>; - -/// Void Number Variable Type -pub type VoidNumberVar<C> = PseudorandomFunctionFamilyOutputVar<C>; - -/// Void Number Commitment Randomness Variable Type -pub type VoidNumberCommitmentRandomnessVar<C> = CommitmentSchemeRandomnessVar<C>; - -/// Void Number Commitment Variable Type -pub type VoidNumberCommitmentVar<C> = CommitmentSchemeOutputVar<C>; - -/// UTXO Randomness Variable Type -pub type UtxoRandomnessVar<C> = CommitmentSchemeRandomnessVar<C>; - -/// UTXO Variable Type -pub type UtxoVar<C> = CommitmentSchemeOutputVar<C>; - -/// UTXO Containment Proof Variable Type -pub type UtxoContainmentProofVar<C, S> = - ContainmentProofVar<S, <C as IdentityConstraintSystemConfiguration>::ConstraintSystem>; - /// Generates a void number commitment from `public_key`, `void_number_generator`, and /// `void_number_commitment_randomness`. #[inline] @@ -255,7 +147,7 @@ where #[derive(derivative::Derivative)] #[derivative( Clone( - bound = "VoidNumberGenerator<C>: Clone, VoidNumberCommitmentRandomness<C>: Clone, UtxoRandomness<C>: Copy" + bound = "VoidNumberGenerator<C>: Clone, VoidNumberCommitmentRandomness<C>: Clone, UtxoRandomness<C>: Clone" ), Copy( bound = "VoidNumberGenerator<C>: Copy, VoidNumberCommitmentRandomness<C>: Copy, UtxoRandomness<C>: Copy" @@ -278,7 +170,7 @@ where )] pub struct AssetParameters<C> where - C: IdentityConfiguration, + C: Configuration, { /// Void Number Generator pub void_number_generator: VoidNumberGenerator<C>, @@ -292,7 +184,7 @@ where impl<C> AssetParameters<C> where - C: IdentityConfiguration, + C: Configuration, { /// Builds a new [`AssetParameters`]. #[inline] @@ -342,7 +234,7 @@ where impl<C> Distribution<AssetParameters<C>> for Standard where - C: IdentityConfiguration, + C: Configuration, Standard: Distribution<VoidNumberGenerator<C>> + Distribution<VoidNumberCommitmentRandomness<C>> + Distribution<UtxoRandomness<C>>, @@ -353,81 +245,10 @@ where } } -/// Asset Parameters Variable -pub struct AssetParametersVar<C> -where - C: IdentityConstraintSystemConfiguration, -{ - /// Void Number Generator - pub void_number_generator: VoidNumberGeneratorVar<C>, - - /// Void Number Commitment Randomness - pub void_number_commitment_randomness: VoidNumberCommitmentRandomnessVar<C>, - - /// UTXO Randomness - pub utxo_randomness: UtxoRandomnessVar<C>, -} - -impl<C> AssetParametersVar<C> -where - C: IdentityConstraintSystemConfiguration, -{ - /// Builds a new [`AssetParametersVar`] from parameter variables. - #[inline] - pub fn new( - void_number_generator: VoidNumberGeneratorVar<C>, - void_number_commitment_randomness: VoidNumberCommitmentRandomnessVar<C>, - utxo_randomness: UtxoRandomnessVar<C>, - ) -> Self { - Self { - void_number_generator, - void_number_commitment_randomness, - utxo_randomness, - } - } -} - -impl<C> Variable<C::ConstraintSystem> for AssetParametersVar<C> -where - C: IdentityConstraintSystemConfiguration, -{ - type Type = AssetParameters<C>; - - type Mode = Secret; - - #[inline] - fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - match allocation { - Allocation::Known(this, mode) => Self::new( - VoidNumberGeneratorVar::<C>::new_known(cs, &this.void_number_generator, mode), - VoidNumberCommitmentRandomnessVar::<C>::new_known( - cs, - &this.void_number_commitment_randomness, - mode, - ), - UtxoRandomnessVar::<C>::new_known(cs, &this.utxo_randomness, mode), - ), - Allocation::Unknown(mode) => Self::new( - VoidNumberGeneratorVar::<C>::new_unknown(cs, mode), - VoidNumberCommitmentRandomnessVar::<C>::new_unknown(cs, mode), - UtxoRandomnessVar::<C>::new_unknown(cs, mode), - ), - } - } -} - -impl<C> HasAllocation<C::ConstraintSystem> for AssetParameters<C> -where - C: IdentityConstraintSystemConfiguration, -{ - type Variable = AssetParametersVar<C>; - type Mode = Secret; -} - /// Account Identity pub struct Identity<C> where - C: IdentityConfiguration, + C: Configuration, { /// Secret Key secret_key: SecretKey<C>, @@ -435,7 +256,7 @@ where impl<C> Identity<C> where - C: IdentityConfiguration, + C: Configuration, { /// Generates a new [`Identity`] from a [`SecretKey`]. #[inline] @@ -751,7 +572,7 @@ where impl<C> Distribution<Identity<C>> for Standard where - C: IdentityConfiguration, + C: Configuration, Standard: Distribution<SecretKey<C>>, { #[inline] @@ -763,7 +584,7 @@ where /// Shielded Identity pub struct ShieldedIdentity<C, I> where - C: IdentityConfiguration, + C: Configuration, I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// UTXO Randomness @@ -778,7 +599,7 @@ where impl<C, I> ShieldedIdentity<C, I> where - C: IdentityConfiguration, + C: Configuration, I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// Builds a new [`ShieldedIdentity`] from `identity` and `commitment_scheme`. @@ -880,7 +701,7 @@ mod spend_error { )] pub enum SpendError<C, I, S> where - C: IdentityConfiguration, + C: Configuration, I: IntegratedEncryptionScheme<Plaintext = Asset>, S: VerifiedSet<Item = Utxo<C>>, { @@ -902,7 +723,7 @@ pub use spend_error::SpendError; /// Spending Information pub struct Spend<C, I> where - C: IdentityConfiguration, + C: Configuration, I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// Spender Identity @@ -914,7 +735,7 @@ where impl<C, I> Spend<C, I> where - C: IdentityConfiguration, + C: Configuration, I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// Generates a new `Spend` from an `Identity` and an `ies::SecretKey<I>`. @@ -981,7 +802,7 @@ where impl<C, I> From<Identity<C>> for Spend<C, I> where - C: IdentityConfiguration, + C: Configuration, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { @@ -994,7 +815,7 @@ where /// Open [`Spend`] pub struct OpenSpend<C> where - C: IdentityConfiguration, + C: Configuration, { /// Spender Identity identity: Identity<C>, @@ -1005,7 +826,7 @@ where impl<C> OpenSpend<C> where - C: IdentityConfiguration, + C: Configuration, { /// Builds a new [`Sender`] for `self`. #[inline] @@ -1048,7 +869,7 @@ mod sender_error { )] pub enum SenderError<C, G, S> where - C: IdentityConfiguration, + C: Configuration, G: SecretKeyGenerator<SecretKey = SecretKey<C>>, S: VerifiedSet<Item = Utxo<C>>, { @@ -1070,7 +891,7 @@ pub use sender_error::SenderError; /// Sender pub struct Sender<C, S> where - C: IdentityConfiguration, + C: Configuration, S: VerifiedSet<Item = Utxo<C>>, { /// Secret Key @@ -1100,7 +921,7 @@ where impl<C, S> Sender<C, S> where - C: IdentityConfiguration, + C: Configuration, S: VerifiedSet<Item = Utxo<C>>, { /// Builds a new [`Sender`] for this `asset` from an `identity`. @@ -1191,184 +1012,6 @@ where } } -/// Sender Variable -pub struct SenderVar<C, S> -where - C: IdentityConstraintSystemConfiguration, - S: VerifiedSet<Item = Utxo<C>>, - C::ConstraintSystem: HasVariable<S::Public> + HasVariable<S::Secret>, -{ - /// Secret Key - secret_key: SecretKeyVar<C>, - - /// Public Key - public_key: PublicKeyVar<C>, - - /// Asset - asset: AssetVar<C::ConstraintSystem>, - - /// Asset Parameters - parameters: AssetParametersVar<C>, - - /// Void Number - void_number: VoidNumberVar<C>, - - /// Void Number Commitment - void_number_commitment: VoidNumberCommitmentVar<C>, - - /// Unspent Transaction Output - utxo: UtxoVar<C>, - - /// UTXO Containment Proof - utxo_containment_proof: UtxoContainmentProofVar<C, S>, -} - -impl<C, S> SenderVar<C, S> -where - C: IdentityConstraintSystemConfiguration, - S: VerifiedSet<Item = Utxo<C>>, - C::ConstraintSystem: HasVariable<S::Public> + HasVariable<S::Secret>, -{ - /// Checks if `self` is a well-formed sender and returns its asset. - #[inline] - pub fn get_well_formed_asset( - self, - cs: &mut C::ConstraintSystem, - commitment_scheme: &C::CommitmentSchemeVar, - utxo_set: &Var<S, C::ConstraintSystem>, - ) -> AssetVar<C::ConstraintSystem> - where - S: HasAllocation<C::ConstraintSystem>, - S::Variable: VerifiedSetVariable<C::ConstraintSystem, ItemVar = UtxoVar<C>>, - { - // Well-formed check: - // - // 1. pk = PRF(sk, 0) [public: (), secret: (pk, sk)] - // 2. vn = PRF(sk, rho) [public: (vn), secret: (sk, rho)] - // 3. k = COM(pk || rho, r) [public: (k), secret: (pk, rho, r)] - // 4. cm = COM(asset || k, s) [public: (), secret: (cm, asset, k, s)] - // 5. is_path(cm, path, root) == true [public: (root), secret: (cm, path)] - // - // FIXME: should `k` be private or not? - - // 1. Check public key: - // ``` - // pk = PRF(sk, 0) - // ``` - // where public: {}, secret: {pk, sk}. - cs.assert_eq( - &self.public_key, - &C::PseudorandomFunctionFamilyVar::evaluate_zero(&self.secret_key), - ); - - // 2. Check void number: - // ``` - // vn = PRF(sk, rho) - // ``` - // where public: {vn}, secret: {sk, rho}. - cs.assert_eq( - &self.void_number, - &C::PseudorandomFunctionFamilyVar::evaluate( - &self.secret_key, - &self.parameters.void_number_generator, - ), - ); - - // 3. Check void number commitment: - // ``` - // k = COM(pk || rho, r) - // ``` - // where public: {k}, secret: {pk, rho, r}. - cs.assert_eq( - &self.void_number_commitment, - &generate_void_number_commitment( - commitment_scheme, - &self.public_key, - &self.parameters.void_number_generator, - &self.parameters.void_number_commitment_randomness, - ), - ); - - // 4. Check UTXO: - // ``` - // cm = COM(asset || k, s) - // ``` - // where public: {}, secret: {cm, asset, k, s}. - cs.assert_eq( - &self.utxo, - &generate_utxo( - commitment_scheme, - &self.asset, - &self.void_number_commitment, - &self.parameters.utxo_randomness, - ), - ); - - // 5. Check UTXO containment proof: - // ``` - // is_path(cm, path, root) == true - // ``` - // where public: {root}, secret: {cm, path}. - self.utxo_containment_proof - .assert_validity(utxo_set, &self.utxo, cs); - - self.asset - } -} - -impl<C, S> Variable<C::ConstraintSystem> for SenderVar<C, S> -where - C: IdentityConstraintSystemConfiguration, - S: VerifiedSet<Item = Utxo<C>>, - C::ConstraintSystem: - HasVariable<S::Public, Mode = Public> + HasVariable<S::Secret, Mode = Secret>, -{ - type Type = Sender<C, S>; - - type Mode = Derived; - - #[inline] - fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - match allocation { - Allocation::Known(this, mode) => Self { - secret_key: SecretKeyVar::<C>::new_known(cs, &this.secret_key, mode), - public_key: PublicKeyVar::<C>::new_known(cs, &this.public_key, Secret), - asset: this.asset.known(cs, mode), - parameters: this.parameters.known(cs, mode), - void_number: VoidNumberVar::<C>::new_known(cs, &this.void_number, Public), - void_number_commitment: VoidNumberCommitmentVar::<C>::new_known( - cs, - &this.void_number_commitment, - Public, - ), - utxo: UtxoVar::<C>::new_known(cs, &this.utxo, Secret), - utxo_containment_proof: this.utxo_containment_proof.known(cs, mode), - }, - Allocation::Unknown(mode) => Self { - secret_key: SecretKeyVar::<C>::new_unknown(cs, mode), - public_key: PublicKeyVar::<C>::new_unknown(cs, Secret), - asset: Asset::unknown(cs, mode), - parameters: AssetParameters::unknown(cs, mode), - void_number: VoidNumberVar::<C>::new_unknown(cs, Public), - void_number_commitment: VoidNumberCommitmentVar::<C>::new_unknown(cs, Public), - utxo: UtxoVar::<C>::new_unknown(cs, Secret), - utxo_containment_proof: ContainmentProof::<S>::unknown(cs, mode), - }, - } - } -} - -impl<C, S> HasAllocation<C::ConstraintSystem> for Sender<C, S> -where - C: IdentityConstraintSystemConfiguration, - S: VerifiedSet<Item = Utxo<C>>, - C::ConstraintSystem: - HasVariable<S::Public, Mode = Public> + HasVariable<S::Secret, Mode = Secret>, -{ - type Variable = SenderVar<C, S>; - type Mode = Derived; -} - /// Sender Post Error pub enum SenderPostError<L> where @@ -1389,7 +1032,7 @@ where /// Sender Post pub struct SenderPost<C, S> where - C: IdentityConfiguration, + C: Configuration, S: VerifiedSet<Item = Utxo<C>>, { /// Void Number @@ -1401,7 +1044,7 @@ where impl<C, S> SenderPost<C, S> where - C: IdentityConfiguration, + C: Configuration, S: VerifiedSet<Item = Utxo<C>>, { /// Posts the [`SenderPost`] data to the `ledger`. @@ -1422,7 +1065,7 @@ where impl<C, S> From<Sender<C, S>> for SenderPost<C, S> where - C: IdentityConfiguration, + C: Configuration, S: VerifiedSet<Item = Utxo<C>>, { #[inline] @@ -1434,7 +1077,7 @@ where /// Receiver pub struct Receiver<C, I> where - C: IdentityConfiguration, + C: Configuration, I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// Asset @@ -1455,7 +1098,7 @@ where impl<C, I> Receiver<C, I> where - C: IdentityConfiguration, + C: Configuration, I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// Build a [`Receiver`] from a [`ShieldedIdentity`] for the `asset`. @@ -1524,103 +1167,6 @@ where } } -/// Receiver Variable -pub struct ReceiverVar<C, I> -where - C: IdentityConstraintSystemConfiguration, - I: IntegratedEncryptionScheme<Plaintext = Asset>, -{ - /// Asset - asset: AssetVar<C::ConstraintSystem>, - - /// UTXO Randomness - utxo_randomness: UtxoRandomnessVar<C>, - - /// Void Number Commitment - void_number_commitment: VoidNumberCommitmentVar<C>, - - /// Unspent Transaction Output - utxo: UtxoVar<C>, - - /// Type Parameter Marker - __: PhantomData<I>, -} - -impl<C, I> ReceiverVar<C, I> -where - C: IdentityConstraintSystemConfiguration, - I: IntegratedEncryptionScheme<Plaintext = Asset>, -{ - /// Checks if `self` is a well-formed receiver and returns its asset. - /// - /// This [`ReceiverVar`] is well-formed whenever: - /// ```text - /// utxo = COM(asset || k, s) - /// ``` - /// where `k` is `self.void_number_commitment` and `s` is `self.utxo_randomness`. In this - /// equation we have `{ utxo } : Public`, `{ asset, k, s } : Secret`. - #[inline] - pub fn get_well_formed_asset( - self, - cs: &mut C::ConstraintSystem, - commitment_scheme: &C::CommitmentSchemeVar, - ) -> AssetVar<C::ConstraintSystem> { - cs.assert_eq( - &self.utxo, - &generate_utxo( - commitment_scheme, - &self.asset, - &self.void_number_commitment, - &self.utxo_randomness, - ), - ); - self.asset - } -} - -impl<C, I> Variable<C::ConstraintSystem> for ReceiverVar<C, I> -where - C: IdentityConstraintSystemConfiguration, - I: IntegratedEncryptionScheme<Plaintext = Asset>, -{ - type Type = Receiver<C, I>; - - type Mode = Derived; - - #[inline] - fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - match allocation { - Allocation::Known(this, mode) => Self { - asset: AssetVar::new_known(cs, &this.asset, mode), - utxo_randomness: UtxoRandomnessVar::<C>::new_known(cs, &this.utxo_randomness, mode), - void_number_commitment: VoidNumberCommitmentVar::<C>::new_known( - cs, - &this.void_number_commitment, - Secret, - ), - utxo: UtxoVar::<C>::new_known(cs, &this.utxo, Public), - __: PhantomData, - }, - Allocation::Unknown(mode) => Self { - asset: AssetVar::new_unknown(cs, mode), - utxo_randomness: UtxoRandomnessVar::<C>::new_unknown(cs, mode), - void_number_commitment: VoidNumberCommitmentVar::<C>::new_unknown(cs, Secret), - utxo: UtxoVar::<C>::new_unknown(cs, Public), - __: PhantomData, - }, - } - } -} - -impl<C, I> HasAllocation<C::ConstraintSystem> for Receiver<C, I> -where - C: IdentityConstraintSystemConfiguration, - I: IntegratedEncryptionScheme<Plaintext = Asset>, -{ - type Variable = ReceiverVar<C, I>; - type Mode = Derived; -} - /// Receiver Post Error pub enum ReceiverPostError<L> where @@ -1641,7 +1187,7 @@ where /// Receiver Post pub struct ReceiverPost<C, I> where - C: IdentityConfiguration, + C: Configuration, I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// Unspent Transaction Output @@ -1653,7 +1199,7 @@ where impl<C, I> ReceiverPost<C, I> where - C: IdentityConfiguration, + C: Configuration, I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// Posts the [`ReceiverPost`] data to the `ledger`. @@ -1674,7 +1220,7 @@ where impl<C, I> From<Receiver<C, I>> for ReceiverPost<C, I> where - C: IdentityConfiguration, + C: Configuration, I: IntegratedEncryptionScheme<Plaintext = Asset>, { #[inline] @@ -1682,3 +1228,477 @@ where receiver.into_post() } } + +/// Constraint System Gadgets for Identities +pub mod constraint { + use super::*; + use manta_crypto::{ + constraint::{ + reflection::{HasAllocation, HasVariable, Var}, + Allocation, Constant, ConstraintSystem, Derived, Equal, Public, PublicOrSecret, Secret, + Variable, + }, + set::constraint::{ContainmentProofVar, VerifiedSetVariable}, + }; + + /// [`Identity`] Constraint System Configuration + pub trait Configuration: super::Configuration { + /// Constraint System + type ConstraintSystem: ConstraintSystem + + HasVariable<AssetId, Mode = PublicOrSecret> + + HasVariable<AssetBalance, Mode = PublicOrSecret>; + + /// Secret Key Variable + type SecretKeyVar: Variable<Self::ConstraintSystem, Type = Self::SecretKey, Mode = Secret>; + + /// Pseudorandom Function Family Input Variable + type PseudorandomFunctionFamilyInputVar: Variable< + Self::ConstraintSystem, + Type = <Self::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Input, + Mode = Secret, + >; + + /// Pseudorandom Function Family Output Variable + type PseudorandomFunctionFamilyOutputVar: Variable< + Self::ConstraintSystem, + Type = <Self::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Output, + Mode = PublicOrSecret, + > + Equal<Self::ConstraintSystem>; + + /// Pseudorandom Function Family Variable + type PseudorandomFunctionFamilyVar: PseudorandomFunctionFamily< + Seed = Self::SecretKeyVar, + Input = Self::PseudorandomFunctionFamilyInputVar, + Output = Self::PseudorandomFunctionFamilyOutputVar, + > + Variable< + Self::ConstraintSystem, + Type = Self::PseudorandomFunctionFamily, + Mode = Constant, + >; + + /// Commitment Scheme Randomness Variable + type CommitmentSchemeRandomnessVar: Variable< + Self::ConstraintSystem, + Type = <Self::CommitmentScheme as CommitmentScheme>::Randomness, + Mode = Secret, + >; + + /// Commitment Scheme Output Variable + type CommitmentSchemeOutputVar: Variable< + Self::ConstraintSystem, + Type = <Self::CommitmentScheme as CommitmentScheme>::Output, + Mode = PublicOrSecret, + > + Equal<Self::ConstraintSystem>; + + /// Commitment Scheme Variable + type CommitmentSchemeVar: CommitmentScheme< + Randomness = Self::CommitmentSchemeRandomnessVar, + Output = Self::CommitmentSchemeOutputVar, + > + CommitmentInput<PublicKeyVar<Self>> + + CommitmentInput<VoidNumberGeneratorVar<Self>> + + CommitmentInput<AssetVar<Self::ConstraintSystem>> + + CommitmentInput<VoidNumberCommitmentVar<Self>> + + Variable<Self::ConstraintSystem, Type = Self::CommitmentScheme, Mode = Constant>; + } + + /// [`PseudorandomFunctionFamily::Input`] Variable Type + type PseudorandomFunctionFamilyInputVar<C> = + <C as Configuration>::PseudorandomFunctionFamilyInputVar; + + /// [`PseudorandomFunctionFamily::Output`] Variable Type + type PseudorandomFunctionFamilyOutputVar<C> = + <C as Configuration>::PseudorandomFunctionFamilyOutputVar; + + /// [`CommitmentScheme::Randomness`] Variable Type + type CommitmentSchemeRandomnessVar<C> = <C as Configuration>::CommitmentSchemeRandomnessVar; + + /// [`CommitmentScheme::Output`] Variable Type + type CommitmentSchemeOutputVar<C> = <C as Configuration>::CommitmentSchemeOutputVar; + + /// Secret Key Variable Type + pub type SecretKeyVar<C> = <C as Configuration>::SecretKeyVar; + + /// Public Key Variable Type + pub type PublicKeyVar<C> = PseudorandomFunctionFamilyOutputVar<C>; + + /// Void Number Generator Variable Type + pub type VoidNumberGeneratorVar<C> = PseudorandomFunctionFamilyInputVar<C>; + + /// Void Number Variable Type + pub type VoidNumberVar<C> = PseudorandomFunctionFamilyOutputVar<C>; + + /// Void Number Commitment Randomness Variable Type + pub type VoidNumberCommitmentRandomnessVar<C> = CommitmentSchemeRandomnessVar<C>; + + /// Void Number Commitment Variable Type + pub type VoidNumberCommitmentVar<C> = CommitmentSchemeOutputVar<C>; + + /// UTXO Randomness Variable Type + pub type UtxoRandomnessVar<C> = CommitmentSchemeRandomnessVar<C>; + + /// UTXO Variable Type + pub type UtxoVar<C> = CommitmentSchemeOutputVar<C>; + + /// UTXO Containment Proof Variable Type + pub type UtxoContainmentProofVar<C, S> = + ContainmentProofVar<S, <C as Configuration>::ConstraintSystem>; + + /// Asset Parameters Variable + pub struct AssetParametersVar<C> + where + C: Configuration, + { + /// Void Number Generator + pub void_number_generator: VoidNumberGeneratorVar<C>, + + /// Void Number Commitment Randomness + pub void_number_commitment_randomness: VoidNumberCommitmentRandomnessVar<C>, + + /// UTXO Randomness + pub utxo_randomness: UtxoRandomnessVar<C>, + } + + impl<C> AssetParametersVar<C> + where + C: Configuration, + { + /// Builds a new [`AssetParametersVar`] from parameter variables. + #[inline] + pub fn new( + void_number_generator: VoidNumberGeneratorVar<C>, + void_number_commitment_randomness: VoidNumberCommitmentRandomnessVar<C>, + utxo_randomness: UtxoRandomnessVar<C>, + ) -> Self { + Self { + void_number_generator, + void_number_commitment_randomness, + utxo_randomness, + } + } + } + + impl<C> Variable<C::ConstraintSystem> for AssetParametersVar<C> + where + C: Configuration, + { + type Type = AssetParameters<C>; + + type Mode = Secret; + + #[inline] + fn new( + cs: &mut C::ConstraintSystem, + allocation: Allocation<Self::Type, Self::Mode>, + ) -> Self { + match allocation { + Allocation::Known(this, mode) => Self::new( + VoidNumberGeneratorVar::<C>::new_known(cs, &this.void_number_generator, mode), + VoidNumberCommitmentRandomnessVar::<C>::new_known( + cs, + &this.void_number_commitment_randomness, + mode, + ), + UtxoRandomnessVar::<C>::new_known(cs, &this.utxo_randomness, mode), + ), + Allocation::Unknown(mode) => Self::new( + VoidNumberGeneratorVar::<C>::new_unknown(cs, mode), + VoidNumberCommitmentRandomnessVar::<C>::new_unknown(cs, mode), + UtxoRandomnessVar::<C>::new_unknown(cs, mode), + ), + } + } + } + + impl<C> HasAllocation<C::ConstraintSystem> for AssetParameters<C> + where + C: Configuration, + { + type Variable = AssetParametersVar<C>; + type Mode = Secret; + } + + /// Sender Variable + pub struct SenderVar<C, S> + where + C: Configuration, + S: VerifiedSet<Item = Utxo<C>>, + C::ConstraintSystem: HasVariable<S::Public> + HasVariable<S::Secret>, + { + /// Secret Key + secret_key: SecretKeyVar<C>, + + /// Public Key + public_key: PublicKeyVar<C>, + + /// Asset + asset: AssetVar<C::ConstraintSystem>, + + /// Asset Parameters + parameters: AssetParametersVar<C>, + + /// Void Number + void_number: VoidNumberVar<C>, + + /// Void Number Commitment + void_number_commitment: VoidNumberCommitmentVar<C>, + + /// Unspent Transaction Output + utxo: UtxoVar<C>, + + /// UTXO Containment Proof + utxo_containment_proof: UtxoContainmentProofVar<C, S>, + } + + impl<C, S> SenderVar<C, S> + where + C: Configuration, + S: VerifiedSet<Item = Utxo<C>>, + C::ConstraintSystem: HasVariable<S::Public> + HasVariable<S::Secret>, + { + /// Checks if `self` is a well-formed sender and returns its asset. + #[inline] + pub fn get_well_formed_asset( + self, + cs: &mut C::ConstraintSystem, + commitment_scheme: &C::CommitmentSchemeVar, + utxo_set: &Var<S, C::ConstraintSystem>, + ) -> AssetVar<C::ConstraintSystem> + where + S: HasAllocation<C::ConstraintSystem>, + S::Variable: VerifiedSetVariable<C::ConstraintSystem, ItemVar = UtxoVar<C>>, + { + // Well-formed check: + // + // 1. pk = PRF(sk, 0) [public: (), secret: (pk, sk)] + // 2. vn = PRF(sk, rho) [public: (vn), secret: (sk, rho)] + // 3. k = COM(pk || rho, r) [public: (k), secret: (pk, rho, r)] + // 4. cm = COM(asset || k, s) [public: (), secret: (cm, asset, k, s)] + // 5. is_path(cm, path, root) == true [public: (root), secret: (cm, path)] + // + // FIXME: should `k` be private or not? + + // 1. Check public key: + // ``` + // pk = PRF(sk, 0) + // ``` + // where public: {}, secret: {pk, sk}. + cs.assert_eq( + &self.public_key, + &C::PseudorandomFunctionFamilyVar::evaluate_zero(&self.secret_key), + ); + + // 2. Check void number: + // ``` + // vn = PRF(sk, rho) + // ``` + // where public: {vn}, secret: {sk, rho}. + cs.assert_eq( + &self.void_number, + &C::PseudorandomFunctionFamilyVar::evaluate( + &self.secret_key, + &self.parameters.void_number_generator, + ), + ); + + // 3. Check void number commitment: + // ``` + // k = COM(pk || rho, r) + // ``` + // where public: {k}, secret: {pk, rho, r}. + cs.assert_eq( + &self.void_number_commitment, + &generate_void_number_commitment( + commitment_scheme, + &self.public_key, + &self.parameters.void_number_generator, + &self.parameters.void_number_commitment_randomness, + ), + ); + + // 4. Check UTXO: + // ``` + // cm = COM(asset || k, s) + // ``` + // where public: {}, secret: {cm, asset, k, s}. + cs.assert_eq( + &self.utxo, + &generate_utxo( + commitment_scheme, + &self.asset, + &self.void_number_commitment, + &self.parameters.utxo_randomness, + ), + ); + + // 5. Check UTXO containment proof: + // ``` + // is_path(cm, path, root) == true + // ``` + // where public: {root}, secret: {cm, path}. + self.utxo_containment_proof + .assert_validity(utxo_set, &self.utxo, cs); + + self.asset + } + } + + impl<C, S> Variable<C::ConstraintSystem> for SenderVar<C, S> + where + C: Configuration, + S: VerifiedSet<Item = Utxo<C>>, + C::ConstraintSystem: + HasVariable<S::Public, Mode = Public> + HasVariable<S::Secret, Mode = Secret>, + { + type Type = Sender<C, S>; + + type Mode = Derived; + + #[inline] + fn new( + cs: &mut C::ConstraintSystem, + allocation: Allocation<Self::Type, Self::Mode>, + ) -> Self { + match allocation { + Allocation::Known(this, mode) => Self { + secret_key: SecretKeyVar::<C>::new_known(cs, &this.secret_key, mode), + public_key: PublicKeyVar::<C>::new_known(cs, &this.public_key, Secret), + asset: this.asset.known(cs, mode), + parameters: this.parameters.known(cs, mode), + void_number: VoidNumberVar::<C>::new_known(cs, &this.void_number, Public), + void_number_commitment: VoidNumberCommitmentVar::<C>::new_known( + cs, + &this.void_number_commitment, + Public, + ), + utxo: UtxoVar::<C>::new_known(cs, &this.utxo, Secret), + utxo_containment_proof: this.utxo_containment_proof.known(cs, mode), + }, + Allocation::Unknown(mode) => Self { + secret_key: SecretKeyVar::<C>::new_unknown(cs, mode), + public_key: PublicKeyVar::<C>::new_unknown(cs, Secret), + asset: Asset::unknown(cs, mode), + parameters: AssetParameters::unknown(cs, mode), + void_number: VoidNumberVar::<C>::new_unknown(cs, Public), + void_number_commitment: VoidNumberCommitmentVar::<C>::new_unknown(cs, Public), + utxo: UtxoVar::<C>::new_unknown(cs, Secret), + utxo_containment_proof: ContainmentProof::<S>::unknown(cs, mode), + }, + } + } + } + + impl<C, S> HasAllocation<C::ConstraintSystem> for Sender<C, S> + where + C: Configuration, + S: VerifiedSet<Item = Utxo<C>>, + C::ConstraintSystem: + HasVariable<S::Public, Mode = Public> + HasVariable<S::Secret, Mode = Secret>, + { + type Variable = SenderVar<C, S>; + type Mode = Derived; + } + + /// Receiver Variable + pub struct ReceiverVar<C, I> + where + C: Configuration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + { + /// Asset + asset: AssetVar<C::ConstraintSystem>, + + /// UTXO Randomness + utxo_randomness: UtxoRandomnessVar<C>, + + /// Void Number Commitment + void_number_commitment: VoidNumberCommitmentVar<C>, + + /// Unspent Transaction Output + utxo: UtxoVar<C>, + + /// Type Parameter Marker + __: PhantomData<I>, + } + + impl<C, I> ReceiverVar<C, I> + where + C: Configuration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + { + /// Checks if `self` is a well-formed receiver and returns its asset. + /// + /// This [`ReceiverVar`] is well-formed whenever: + /// ```text + /// utxo = COM(asset || k, s) + /// ``` + /// where `k` is `self.void_number_commitment` and `s` is `self.utxo_randomness`. In this + /// equation we have `{ utxo } : Public`, `{ asset, k, s } : Secret`. + #[inline] + pub fn get_well_formed_asset( + self, + cs: &mut C::ConstraintSystem, + commitment_scheme: &C::CommitmentSchemeVar, + ) -> AssetVar<C::ConstraintSystem> { + cs.assert_eq( + &self.utxo, + &generate_utxo( + commitment_scheme, + &self.asset, + &self.void_number_commitment, + &self.utxo_randomness, + ), + ); + self.asset + } + } + + impl<C, I> Variable<C::ConstraintSystem> for ReceiverVar<C, I> + where + C: Configuration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + { + type Type = Receiver<C, I>; + + type Mode = Derived; + + #[inline] + fn new( + cs: &mut C::ConstraintSystem, + allocation: Allocation<Self::Type, Self::Mode>, + ) -> Self { + match allocation { + Allocation::Known(this, mode) => Self { + asset: AssetVar::new_known(cs, &this.asset, mode), + utxo_randomness: UtxoRandomnessVar::<C>::new_known( + cs, + &this.utxo_randomness, + mode, + ), + void_number_commitment: VoidNumberCommitmentVar::<C>::new_known( + cs, + &this.void_number_commitment, + Secret, + ), + utxo: UtxoVar::<C>::new_known(cs, &this.utxo, Public), + __: PhantomData, + }, + Allocation::Unknown(mode) => Self { + asset: AssetVar::new_unknown(cs, mode), + utxo_randomness: UtxoRandomnessVar::<C>::new_unknown(cs, mode), + void_number_commitment: VoidNumberCommitmentVar::<C>::new_unknown(cs, Secret), + utxo: UtxoVar::<C>::new_unknown(cs, Public), + __: PhantomData, + }, + } + } + } + + impl<C, I> HasAllocation<C::ConstraintSystem> for Receiver<C, I> + where + C: Configuration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + { + type Variable = ReceiverVar<C, I>; + type Mode = Derived; + } +} diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 89a16847d..0475253bd 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -18,7 +18,7 @@ use crate::{ asset::{sample_asset_balances, Asset, AssetBalance, AssetBalances, AssetId}, - identity::{self, IdentityConstraintSystemConfiguration, Utxo, UtxoVar, VoidNumber}, + identity::{self, constraint::UtxoVar, Utxo, VoidNumber}, ledger::{Ledger, PostError}, }; use alloc::vec::Vec; @@ -140,9 +140,9 @@ impl<const SOURCES: usize, const SINKS: usize> Distribution<PublicTransfer<SOURC } } -/// Transfer Configuration -pub trait TransferConfiguration: - IdentityConstraintSystemConfiguration<ConstraintSystem = ConstraintSystem<Self>> +/// [`Transfer`] Configuration +pub trait Configuration: + identity::constraint::Configuration<ConstraintSystem = ConstraintSystem<Self>> { /// Constraint System type ConstraintSystem: constraint::ConstraintSystem @@ -180,40 +180,37 @@ pub trait TransferConfiguration: } /// Transfer Sender Type -pub type Sender<T> = identity::Sender<T, <T as TransferConfiguration>::UtxoSet>; +pub type Sender<T> = identity::Sender<T, <T as Configuration>::UtxoSet>; /// Transfer Receiver Type -pub type Receiver<T> = - identity::Receiver<T, <T as TransferConfiguration>::IntegratedEncryptionScheme>; +pub type Receiver<T> = identity::Receiver<T, <T as Configuration>::IntegratedEncryptionScheme>; /// Transfer Sender Variable Type -pub type SenderVar<T> = identity::SenderVar<T, <T as TransferConfiguration>::UtxoSet>; +pub type SenderVar<T> = identity::constraint::SenderVar<T, <T as Configuration>::UtxoSet>; /// Transfer Receiver Type pub type ReceiverVar<T> = - identity::ReceiverVar<T, <T as TransferConfiguration>::IntegratedEncryptionScheme>; + identity::constraint::ReceiverVar<T, <T as Configuration>::IntegratedEncryptionScheme>; /// Transfer Constraint System Type -pub type ConstraintSystem<T> = <T as TransferConfiguration>::ConstraintSystem; +pub type ConstraintSystem<T> = <T as Configuration>::ConstraintSystem; /// Transfer Proving Context Type -pub type ProvingContext<T> = - <<T as TransferConfiguration>::ProofSystem as ProofSystem>::ProvingContext; +pub type ProvingContext<T> = <<T as Configuration>::ProofSystem as ProofSystem>::ProvingContext; /// Transfer Verifying Context Type -pub type VerifyingContext<T> = - <<T as TransferConfiguration>::ProofSystem as ProofSystem>::VerifyingContext; +pub type VerifyingContext<T> = <<T as Configuration>::ProofSystem as ProofSystem>::VerifyingContext; /// Transfer Proof Type -pub type Proof<T> = <<T as TransferConfiguration>::ProofSystem as ProofSystem>::Proof; +pub type Proof<T> = <<T as Configuration>::ProofSystem as ProofSystem>::Proof; /// Transfer Proof System Error Type -pub type ProofSystemError<T> = <<T as TransferConfiguration>::ProofSystem as ProofSystem>::Error; +pub type ProofSystemError<T> = <<T as Configuration>::ProofSystem as ProofSystem>::Error; /// Secret Transfer Protocol pub struct SecretTransfer<T, const SENDERS: usize, const RECEIVERS: usize> where - T: TransferConfiguration, + T: Configuration, { /// Senders pub senders: [Sender<T>; SENDERS], @@ -224,7 +221,7 @@ where impl<T, const SENDERS: usize, const RECEIVERS: usize> SecretTransfer<T, SENDERS, RECEIVERS> where - T: TransferConfiguration, + T: Configuration, { /// Maximum Number of Senders pub const MAXIMUM_SENDER_COUNT: usize = 32; @@ -339,7 +336,7 @@ where impl<T, const SENDERS: usize, const RECEIVERS: usize> From<SecretTransfer<T, SENDERS, RECEIVERS>> for Transfer<T, 0, SENDERS, RECEIVERS, 0> where - T: TransferConfiguration, + T: Configuration, { #[inline] fn from(transfer: SecretTransfer<T, SENDERS, RECEIVERS>) -> Self { @@ -351,16 +348,16 @@ where } /// Sender Post Type -pub type SenderPost<T> = identity::SenderPost<T, <T as TransferConfiguration>::UtxoSet>; +pub type SenderPost<T> = identity::SenderPost<T, <T as Configuration>::UtxoSet>; /// Receiver Post Type pub type ReceiverPost<T> = - identity::ReceiverPost<T, <T as TransferConfiguration>::IntegratedEncryptionScheme>; + identity::ReceiverPost<T, <T as Configuration>::IntegratedEncryptionScheme>; /// Secret Transfer Post pub struct SecretTransferPost<T, const SENDERS: usize, const RECEIVERS: usize> where - T: TransferConfiguration, + T: Configuration, { /// Sender Posts pub sender_posts: [SenderPost<T>; SENDERS], @@ -374,7 +371,7 @@ where impl<T, const SENDERS: usize, const RECEIVERS: usize> SecretTransferPost<T, SENDERS, RECEIVERS> where - T: TransferConfiguration, + T: Configuration, { /// Posts the [`SecretTransferPost`] to the `ledger`. #[inline] @@ -395,7 +392,7 @@ where impl<T, const SENDERS: usize, const RECEIVERS: usize> From<SecretTransferPost<T, SENDERS, RECEIVERS>> for TransferPost<T, 0, SENDERS, RECEIVERS, 0> where - T: TransferConfiguration, + T: Configuration, { #[inline] fn from(post: SecretTransferPost<T, SENDERS, RECEIVERS>) -> Self { @@ -410,7 +407,7 @@ where impl<T, const SENDERS: usize, const RECEIVERS: usize> TryFrom<TransferPost<T, 0, SENDERS, RECEIVERS, 0>> for SecretTransferPost<T, SENDERS, RECEIVERS> where - T: TransferConfiguration, + T: Configuration, { type Error = (); @@ -432,7 +429,7 @@ pub struct Transfer< const RECEIVERS: usize, const SINKS: usize, > where - T: TransferConfiguration, + T: Configuration, { /// Public Part of the Transfer public: PublicTransfer<SOURCES, SINKS>, @@ -444,7 +441,7 @@ pub struct Transfer< impl<T, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> Transfer<T, SOURCES, SENDERS, RECEIVERS, SINKS> where - T: TransferConfiguration, + T: Configuration, { /// Builds a new [`Transfer`] from a [`PublicTransfer`] and a [`SecretTransfer`]. #[inline] @@ -718,7 +715,7 @@ struct TransferParticipantsVar< const RECEIVERS: usize, const SINKS: usize, > where - T: TransferConfiguration, + T: Configuration, { /// Source Variables sources: Vec<T::AssetBalanceVar>, @@ -736,7 +733,7 @@ struct TransferParticipantsVar< impl<T, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> Variable<ConstraintSystem<T>> for TransferParticipantsVar<T, SOURCES, SENDERS, RECEIVERS, SINKS> where - T: TransferConfiguration, + T: Configuration, { type Type = Transfer<T, SOURCES, SENDERS, RECEIVERS, SINKS>; @@ -801,7 +798,7 @@ pub struct TransferPost< const RECEIVERS: usize, const SINKS: usize, > where - T: TransferConfiguration, + T: Configuration, { /// Sender Posts pub sender_posts: [SenderPost<T>; SENDERS], @@ -816,7 +813,7 @@ pub struct TransferPost< impl<T, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> TransferPost<T, SOURCES, SENDERS, RECEIVERS, SINKS> where - T: TransferConfiguration, + T: Configuration, { /// Posts the [`TransferPost`] to the `ledger`. #[inline] diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index 759fba8a0..eb3ef13db 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -24,13 +24,10 @@ use crate::{ asset::{Asset, AssetBalance, AssetId}, fs::{Load, Save}, - identity::{ - AssetParameters, Identity, IdentityConfiguration, OpenSpend, Receiver, ShieldedIdentity, - Spend, - }, + identity::{self, AssetParameters, Identity, OpenSpend, Receiver, ShieldedIdentity, Spend}, keys::{self, DerivedSecretKeyGenerator, ExternalKeys, InternalKeys}, ledger::Ledger, - transfer::{SecretTransfer, TransferConfiguration}, + transfer::{self, SecretTransfer}, }; use core::{convert::Infallible, fmt::Debug, hash::Hash}; use manta_crypto::ies::{EncryptedMessage, IntegratedEncryptionScheme}; @@ -209,7 +206,7 @@ where #[inline] fn next_external_identity<C>(&mut self) -> Result<Identity<C>, D::Error> where - C: IdentityConfiguration<SecretKey = D::SecretKey>, + C: identity::Configuration<SecretKey = D::SecretKey>, { self.next_external_key().map(Identity::new) } @@ -218,7 +215,7 @@ where #[inline] fn next_internal_identity<C>(&mut self) -> Result<Identity<C>, D::Error> where - C: IdentityConfiguration<SecretKey = D::SecretKey>, + C: identity::Configuration<SecretKey = D::SecretKey>, { self.next_internal_key().map(Identity::new) } @@ -246,7 +243,7 @@ where iter: Iter, ) -> Option<OpenSpend<C>> where - C: IdentityConfiguration<SecretKey = D::SecretKey>, + C: identity::Configuration<SecretKey = D::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Iter: IntoIterator<Item = D::SecretKey>, Standard: Distribution<AssetParameters<C>>, @@ -265,7 +262,7 @@ where gap_limit: usize, ) -> Option<OpenSpend<C>> where - C: IdentityConfiguration<SecretKey = D::SecretKey>, + C: identity::Configuration<SecretKey = D::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { @@ -290,7 +287,7 @@ where gap_limit: usize, ) -> Option<OpenSpend<C>> where - C: IdentityConfiguration<SecretKey = D::SecretKey>, + C: identity::Configuration<SecretKey = D::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { @@ -310,7 +307,7 @@ where gap_limit: usize, ) -> Option<OpenSpend<C>> where - C: IdentityConfiguration<SecretKey = D::SecretKey>, + C: identity::Configuration<SecretKey = D::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { @@ -343,7 +340,7 @@ where commitment_scheme: &C::CommitmentScheme, ) -> Result<ShieldedIdentity<C, I>, D::Error> where - C: IdentityConfiguration<SecretKey = D::SecretKey>, + C: identity::Configuration<SecretKey = D::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { @@ -362,7 +359,7 @@ where rng: &mut R, ) -> Result<(Receiver<C, I>, Spend<C, I>), InternalReceiverError<D, I>> where - C: IdentityConfiguration<SecretKey = D::SecretKey>, + C: identity::Configuration<SecretKey = D::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, R: CryptoRng + RngCore + ?Sized, Standard: Distribution<AssetParameters<C>>, @@ -388,7 +385,7 @@ where rng: &mut R, ) -> Option<SecretTransfer<T, 2, 2>> where - T: TransferConfiguration, + T: transfer::Configuration, R: CryptoRng + RngCore + ?Sized, { // TODO: spec: diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index c2ab040ec..0a594a7cf 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -25,7 +25,7 @@ is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } [features] -constraints = [] +# TODO: constraints = [] default = [] test = [] diff --git a/manta-pay/src/accounting/config.rs b/manta-pay/src/accounting/config.rs index 413d107fd..d1abbc7f1 100644 --- a/manta-pay/src/accounting/config.rs +++ b/manta-pay/src/accounting/config.rs @@ -24,16 +24,14 @@ use crate::{ ies::IES, merkle_tree, prf::blake2s::{constraint::Blake2sVar, Blake2s}, - rand::ChaCha20RngBlake2sSeedable, }, }; use ark_bls12_381::Bls12_381; use ark_ed_on_bls12_381::{constraints::EdwardsVar, EdwardsProjective, Fq}; -use manta_accounting::{ - identity::{IdentityConfiguration, IdentityConstraintSystemConfiguration}, - transfer::TransferConfiguration, -}; +use manta_accounting::{identity, transfer}; use manta_crypto::{commitment::CommitmentScheme, PseudorandomFunctionFamily}; +use manta_util::rand::SeedIntoRng; +use rand_chacha::ChaCha20Rng; /// Pedersen Window Parameters #[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -107,14 +105,14 @@ impl merkle_tree::constraint::Configuration for Configuration { >; } -impl IdentityConfiguration for Configuration { +impl identity::Configuration for Configuration { type SecretKey = <Blake2s as PseudorandomFunctionFamily>::Seed; type PseudorandomFunctionFamily = Blake2s; type CommitmentScheme = PedersenCommitment; - type Rng = ChaCha20RngBlake2sSeedable; + type Rng = SeedIntoRng<<Blake2s as PseudorandomFunctionFamily>::Seed, ChaCha20Rng>; } -impl IdentityConstraintSystemConfiguration for Configuration { +impl identity::constraint::Configuration for Configuration { type ConstraintSystem = ConstraintSystem; type SecretKeyVar = <Blake2sVar<ConstraintField> as PseudorandomFunctionFamily>::Seed; type PseudorandomFunctionFamilyInputVar = @@ -127,7 +125,7 @@ impl IdentityConstraintSystemConfiguration for Configuration { type CommitmentSchemeVar = PedersenCommitmentVar; } -impl TransferConfiguration for Configuration { +impl transfer::Configuration for Configuration { type ConstraintSystem = ConstraintSystem; type ProofSystem = ProofSystem; type AssetIdVar = AssetIdVar<ConstraintField>; diff --git a/manta-pay/src/accounting/identity.rs b/manta-pay/src/accounting/identity.rs index 88709766c..ef7288cf4 100644 --- a/manta-pay/src/accounting/identity.rs +++ b/manta-pay/src/accounting/identity.rs @@ -17,39 +17,39 @@ //! Identity Implementations use crate::accounting::config::Configuration; -use manta_accounting::{identity, transfer::TransferConfiguration}; +use manta_accounting::{identity, transfer}; /// Asset Parameters pub type AssetParameters = identity::AssetParameters<Configuration>; /// Sender Type pub type Sender = - identity::Sender<Configuration, <Configuration as TransferConfiguration>::UtxoSet>; + identity::Sender<Configuration, <Configuration as transfer::Configuration>::UtxoSet>; /// Receiver Type pub type Receiver = identity::Receiver< Configuration, - <Configuration as TransferConfiguration>::IntegratedEncryptionScheme, + <Configuration as transfer::Configuration>::IntegratedEncryptionScheme, >; /// Shielded Identity Type pub type ShieldedIdentity = identity::ShieldedIdentity< Configuration, - <Configuration as TransferConfiguration>::IntegratedEncryptionScheme, + <Configuration as transfer::Configuration>::IntegratedEncryptionScheme, >; /// Spend Type pub type Spend = identity::Spend< Configuration, - <Configuration as TransferConfiguration>::IntegratedEncryptionScheme, + <Configuration as transfer::Configuration>::IntegratedEncryptionScheme, >; /// Sender Post Type pub type SenderPost = - identity::SenderPost<Configuration, <Configuration as TransferConfiguration>::UtxoSet>; + identity::SenderPost<Configuration, <Configuration as transfer::Configuration>::UtxoSet>; /// Receiver Post Type pub type ReceiverPost = identity::ReceiverPost< Configuration, - <Configuration as TransferConfiguration>::IntegratedEncryptionScheme, + <Configuration as transfer::Configuration>::IntegratedEncryptionScheme, >; diff --git a/manta-pay/src/accounting/ledger.rs b/manta-pay/src/accounting/ledger.rs index e88387458..bf0259f14 100644 --- a/manta-pay/src/accounting/ledger.rs +++ b/manta-pay/src/accounting/ledger.rs @@ -46,7 +46,7 @@ type VoidNumber = identity::VoidNumber<Configuration>; type Utxo = identity::Utxo<Configuration>; /// UTXO Variable -type UtxoVar = identity::UtxoVar<Configuration>; +type UtxoVar = identity::constraint::UtxoVar<Configuration>; /// UTXO Shard Root type Root = merkle_tree::Root<Configuration>; diff --git a/manta-pay/src/crypto/mod.rs b/manta-pay/src/crypto/mod.rs index 55e4f7807..603696d1c 100644 --- a/manta-pay/src/crypto/mod.rs +++ b/manta-pay/src/crypto/mod.rs @@ -21,4 +21,3 @@ pub mod constraint; pub mod ies; pub mod merkle_tree; pub mod prf; -pub mod rand; diff --git a/manta-pay/src/crypto/prf/blake2s.rs b/manta-pay/src/crypto/prf/blake2s.rs index b9ea8ee31..3f2710bb8 100644 --- a/manta-pay/src/crypto/prf/blake2s.rs +++ b/manta-pay/src/crypto/prf/blake2s.rs @@ -47,6 +47,13 @@ impl Concat for Blake2sSeed { } } +impl From<Blake2sSeed> for [u8; 32] { + #[inline] + fn from(seed: Blake2sSeed) -> Self { + seed.0 + } +} + /// Blake2s Pseudorandom Function Family Input #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Blake2sInput(<ArkBlake2s as PRF>::Input); diff --git a/manta-pay/src/crypto/rand.rs b/manta-pay/src/crypto/rand.rs deleted file mode 100644 index e75d99491..000000000 --- a/manta-pay/src/crypto/rand.rs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Random Number Generator Wrappers - -use crate::crypto::prf::blake2s::Blake2sSeed; -use rand::{CryptoRng, Error, RngCore, SeedableRng}; -use rand_chacha::ChaCha20Rng; - -/// Cha-Cha 20 RNG Seedable from a Blake2s Seed -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ChaCha20RngBlake2sSeedable(ChaCha20Rng); - -impl CryptoRng for ChaCha20RngBlake2sSeedable {} - -impl RngCore for ChaCha20RngBlake2sSeedable { - #[inline] - fn next_u32(&mut self) -> u32 { - self.0.next_u32() - } - - #[inline] - fn next_u64(&mut self) -> u64 { - self.0.next_u64() - } - - #[inline] - fn fill_bytes(&mut self, dest: &mut [u8]) { - self.0.fill_bytes(dest) - } - - #[inline] - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.0.try_fill_bytes(dest) - } -} - -impl SeedableRng for ChaCha20RngBlake2sSeedable { - type Seed = Blake2sSeed; - - #[inline] - fn from_seed(seed: Self::Seed) -> Self { - Self(ChaCha20Rng::from_seed(seed.0)) - } - - #[inline] - fn seed_from_u64(state: u64) -> Self { - Self(ChaCha20Rng::seed_from_u64(state)) - } - - #[inline] - fn from_rng<R: RngCore>(rng: R) -> Result<Self, Error> { - ChaCha20Rng::from_rng(rng).map(Self) - } - - #[inline] - fn from_entropy() -> Self { - Self(ChaCha20Rng::from_entropy()) - } -} diff --git a/manta-util/src/rand.rs b/manta-util/src/rand.rs index ed700335d..f260da955 100644 --- a/manta-util/src/rand.rs +++ b/manta-util/src/rand.rs @@ -16,7 +16,8 @@ //! Random Number Generator Utilities -use rand_core::{block::BlockRngCore, CryptoRng, Error, RngCore}; +use core::marker::PhantomData; +use rand_core::{block::BlockRngCore, CryptoRng, Error, RngCore, SeedableRng}; /// Random Number Generator Sized Wrapper #[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -64,3 +65,94 @@ where self.0.generate(results) } } + +/// Seed Into Random Number Generator +pub struct SeedIntoRng<S, R> { + /// Inner Rng + inner: R, + + /// Type Parameter Marker + __: PhantomData<S>, +} + +impl<S, R> SeedIntoRng<S, R> { + /// Builds a new [`SeedIntoRng`] from an existing `inner` random number generator. + #[inline] + fn new(inner: R) -> Self { + Self { + inner, + __: PhantomData, + } + } +} + +impl<S, R> CryptoRng for SeedIntoRng<S, R> where R: CryptoRng {} + +impl<S, R> RngCore for SeedIntoRng<S, R> +where + R: RngCore, +{ + #[inline] + fn next_u32(&mut self) -> u32 { + self.inner.next_u32() + } + + #[inline] + fn next_u64(&mut self) -> u64 { + self.inner.next_u64() + } + + #[inline] + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.inner.fill_bytes(dest) + } + + #[inline] + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + self.inner.try_fill_bytes(dest) + } +} + +impl<S, R> BlockRngCore for SeedIntoRng<S, R> +where + R: BlockRngCore, +{ + type Item = R::Item; + + type Results = R::Results; + + #[inline] + fn generate(&mut self, results: &mut Self::Results) { + self.inner.generate(results) + } +} + +impl<S, R> SeedableRng for SeedIntoRng<S, R> +where + S: Into<R::Seed> + Default + AsMut<[u8]>, + R: SeedableRng, +{ + type Seed = S; + + #[inline] + fn from_seed(seed: Self::Seed) -> Self { + Self::new(R::from_seed(seed.into())) + } + + #[inline] + fn seed_from_u64(state: u64) -> Self { + Self::new(R::seed_from_u64(state)) + } + + #[inline] + fn from_rng<T: RngCore>(rng: T) -> Result<Self, Error> { + R::from_rng(rng).map(Self::new) + } + + #[cfg(feature = "getrandom")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "getrandom")))] + #[inline] + fn from_entropy() -> Self { + Self::new(R::from_entropy()) + } +} From c7ef7c2c97c9df8b9c24cb0b4850b20b1a97c0b4 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sat, 18 Sep 2021 04:40:08 -0400 Subject: [PATCH 041/275] start building unit testing framework for transfers --- manta-accounting/src/identity.rs | 11 +- manta-accounting/src/transfer.rs | 60 +++++++- manta-accounting/src/wallet.rs | 132 +++++++++++++----- manta-pay/src/accounting/config.rs | 19 +-- .../crypto/constraint/constraint_system.rs | 4 + .../constraint/proof_systems/groth16.rs | 33 ++++- manta-pay/src/crypto/merkle_tree/mod.rs | 48 +++++-- 7 files changed, 238 insertions(+), 69 deletions(-) diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 367247052..16b7cc022 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -365,23 +365,20 @@ where /// Returns the public key associated with this identity. #[inline] - pub(crate) fn public_key(&self) -> PublicKey<C> { + fn public_key(&self) -> PublicKey<C> { C::PseudorandomFunctionFamily::evaluate_zero(&self.secret_key) } /// Generates a new void number using the `void_number_generator` parameter. #[inline] - pub(crate) fn void_number( - &self, - void_number_generator: &VoidNumberGenerator<C>, - ) -> VoidNumber<C> { + fn void_number(&self, void_number_generator: &VoidNumberGenerator<C>) -> VoidNumber<C> { C::PseudorandomFunctionFamily::evaluate(&self.secret_key, void_number_generator) } /// Generates a new void number commitment using the `void_number_generator` and /// `void_number_commitment_randomness`. #[inline] - pub(crate) fn void_number_commitment( + fn void_number_commitment( &self, commitment_scheme: &C::CommitmentScheme, parameters: &AssetParameters<C>, @@ -391,7 +388,7 @@ where /// Returns the [`PublicKey`], [`VoidNumberCommitment`], and [`Utxo`] for this identity. #[inline] - pub(crate) fn construct_utxo( + fn construct_utxo( &self, commitment_scheme: &C::CommitmentScheme, asset: &Asset, diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 0475253bd..41aa42516 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -443,7 +443,7 @@ impl<T, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, cons where T: Configuration, { - /// Builds a new [`Transfer`] from a [`PublicTransfer`] and a [`SecretTransfer`]. + /// Builds a new universal [`Transfer`] from public and secret information. #[inline] pub fn new( asset_id: AssetId, @@ -925,6 +925,23 @@ pub mod canonical { /// Mint Transaction Ledger Post pub type MintPost<T> = transfer_post_alias!(T, MintShape); + impl<T> Mint<T> + where + T: Configuration, + { + /// Builds a [`Mint`] from `asset` and `receiver`. + #[inline] + pub fn build(asset: Asset, receiver: Receiver<T>) -> Self { + Self::new( + asset.id, + [asset.value], + Default::default(), + [receiver], + Default::default(), + ) + } + } + /// Private Transfer Transaction Shape /// /// ```text @@ -941,6 +958,26 @@ pub mod canonical { /// Private Transfer Transaction Post pub type PrivateTransferPost<T> = transfer_post_alias!(T, PrivateTransferShape); + impl<T> PrivateTransfer<T> + where + T: Configuration, + { + /// Builds a [`PrivateTransfer`] from `senders` and `receivers`. + #[inline] + pub fn build( + senders: [Sender<T>; PrivateTransferShape::SENDERS], + receivers: [Receiver<T>; PrivateTransferShape::RECEIVERS], + ) -> Self { + Self::new( + Default::default(), + Default::default(), + senders, + receivers, + Default::default(), + ) + } + } + /// Reclaim Transaction Shape /// /// ```text @@ -956,4 +993,25 @@ pub mod canonical { /// Reclaim Transaction Post pub type ReclaimPost<T> = transfer_post_alias!(T, ReclaimShape); + + impl<T> Reclaim<T> + where + T: Configuration, + { + /// Builds a [`Reclaim`] from `senders`, `receiver`, and `reclaim`. + #[inline] + pub fn build( + senders: [Sender<T>; ReclaimShape::SENDERS], + receiver: Receiver<T>, + reclaim: Asset, + ) -> Self { + Self::new( + reclaim.id, + Default::default(), + senders, + [receiver], + [reclaim.value], + ) + } + } } diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index eb3ef13db..cd905900b 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -27,8 +27,12 @@ use crate::{ identity::{self, AssetParameters, Identity, OpenSpend, Receiver, ShieldedIdentity, Spend}, keys::{self, DerivedSecretKeyGenerator, ExternalKeys, InternalKeys}, ledger::Ledger, - transfer::{self, SecretTransfer}, + transfer::{ + self, + canonical::{Mint, PrivateTransfer, Reclaim}, + }, }; +use alloc::vec::Vec; use core::{convert::Infallible, fmt::Debug, hash::Hash}; use manta_crypto::ies::{EncryptedMessage, IntegratedEncryptionScheme}; use rand::{ @@ -239,7 +243,7 @@ where #[inline] fn find_open_spend_from_iter<C, I, Iter>( &self, - encrypted_asset: EncryptedMessage<I>, + encrypted_asset: &EncryptedMessage<I>, iter: Iter, ) -> Option<OpenSpend<C>> where @@ -249,15 +253,15 @@ where Standard: Distribution<AssetParameters<C>>, { iter.into_iter() - .find_map(move |k| Identity::new(k).try_open(&encrypted_asset).ok()) + .find_map(move |k| Identity::new(k).try_open(encrypted_asset).ok()) } /// Looks for an [`OpenSpend`] for this `encrypted_asset`, only trying `gap_limit`-many - /// external and internal keys starting from `index`. + /// external keys starting from `index`. #[inline] - pub fn find_open_spend<C, I>( + pub fn find_external_open_spend<C, I>( &self, - encrypted_asset: EncryptedMessage<I>, + encrypted_asset: &EncryptedMessage<I>, index: D::Index, gap_limit: usize, ) -> Option<OpenSpend<C>> @@ -266,23 +270,18 @@ where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { - // TODO: Return which kind of spend it was, internal or external. - - let interleaved_keys = self - .external_keys_from_index(index.clone()) - .zip(self.internal_keys_from_index(index)) - .flat_map(move |(e, i)| [e, i]) - .take(2 * gap_limit); - - self.find_open_spend_from_iter(encrypted_asset, interleaved_keys) + self.find_open_spend_from_iter( + encrypted_asset, + self.external_keys_from_index(index).take(gap_limit), + ) } /// Looks for an [`OpenSpend`] for this `encrypted_asset`, only trying `gap_limit`-many - /// external keys starting from `index`. + /// internal keys starting from `index`. #[inline] - pub fn find_external_open_spend<C, I>( + pub fn find_internal_open_spend<C, I>( &self, - encrypted_asset: EncryptedMessage<I>, + encrypted_asset: &EncryptedMessage<I>, index: D::Index, gap_limit: usize, ) -> Option<OpenSpend<C>> @@ -293,28 +292,37 @@ where { self.find_open_spend_from_iter( encrypted_asset, - self.external_keys_from_index(index).take(gap_limit), + self.internal_keys_from_index(index).take(gap_limit), ) } /// Looks for an [`OpenSpend`] for this `encrypted_asset`, only trying `gap_limit`-many - /// internal keys starting from `index`. + /// external and internal keys starting from `index`. #[inline] - pub fn find_internal_open_spend<C, I>( + pub fn find_open_spend<C, I>( &self, - encrypted_asset: EncryptedMessage<I>, - index: D::Index, + encrypted_asset: &EncryptedMessage<I>, + external_index: D::Index, + internal_index: D::Index, gap_limit: usize, - ) -> Option<OpenSpend<C>> + ) -> Option<(OpenSpend<C>, SpendKind)> where C: identity::Configuration<SecretKey = D::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { - self.find_open_spend_from_iter( - encrypted_asset, - self.internal_keys_from_index(index).take(gap_limit), - ) + // TODO: Find a way to either interleave these or parallelize these. + if let Some(spend) = + self.find_external_open_spend(encrypted_asset, external_index, gap_limit) + { + return Some((spend, SpendKind::External)); + } + if let Some(spend) = + self.find_internal_open_spend(encrypted_asset, internal_index, gap_limit) + { + return Some((spend, SpendKind::Internal)); + } + None } /// Updates `self` with new information from the ledger. @@ -376,14 +384,32 @@ where )) } - /// Builds a [`SecretTransfer`] transaction to send `asset` to an `external_receiver`. - pub fn secret_send<T, R>( + /// Builds a [`Mint`] transaction to mint `asset` and returns the [`Spend`] for that asset. + #[inline] + pub fn mint<T, R>( + &mut self, + commitment_scheme: &T::CommitmentScheme, + asset: Asset, + rng: &mut R, + ) -> WalletMintResult<D, T> + where + T: transfer::Configuration<SecretKey = D::SecretKey>, + R: CryptoRng + RngCore + ?Sized, + Standard: Distribution<AssetParameters<T>>, + { + let (receiver, spend) = self.generate_internal_receiver(commitment_scheme, asset, rng)?; + Ok((Mint::build(asset, receiver), spend)) + } + + /// TODO: Builds [`PrivateTransfer`] transactions to send `asset` to an `external_receiver`. + #[inline] + pub fn batch_private_transfer_external<T, R>( &self, commitment_scheme: &T::CommitmentScheme, asset: Asset, external_receiver: ShieldedIdentity<T, T::IntegratedEncryptionScheme>, rng: &mut R, - ) -> Option<SecretTransfer<T, 2, 2>> + ) -> Option<Vec<PrivateTransfer<T>>> where T: transfer::Configuration, R: CryptoRng + RngCore + ?Sized, @@ -393,10 +419,25 @@ where // 2. find out which keys have control over `asset` // 3. build two senders and build a receiver and a change receiver for the extra change - /* - let sender = Sender::generate(self.secret_key_source, commitment_scheme); - */ let _ = external_receiver.into_receiver(commitment_scheme, asset, rng); + // TODO: PrivateTransfer::build([fst, snd], [external_receiver, change]) + todo!() + } + + /// TODO: Builds a [`Reclaim`] transaction. + #[inline] + pub fn reclaim<T, R>( + &self, + commitment_scheme: &T::CommitmentScheme, + asset: Asset, + rng: &mut R, + ) -> Option<Reclaim<T>> + where + T: transfer::Configuration, + R: CryptoRng + RngCore + ?Sized, + { + let _ = (commitment_scheme, asset, rng); + // TODO: Reclaim::build(senders, receiver, reclaim); todo!() } } @@ -441,6 +482,16 @@ where } } +/// Spend Kind +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum SpendKind { + /// External Spend + External, + + /// Internal Spend + Internal, +} + /// Internal Receiver Error /// /// This `enum` is the error state for the [`generate_internal_receiver`] method on [`Wallet`]. @@ -467,3 +518,16 @@ where /// Encryption Error EncryptionError(I::Error), } + +/// Internal Receiver Result Type +pub type InternalReceiverResult<D, I, OK> = Result<OK, InternalReceiverError<D, I>>; + +/// Wallet Mint Result Type +pub type WalletMintResult<D, T> = InternalReceiverResult< + D, + <T as transfer::Configuration>::IntegratedEncryptionScheme, + ( + Mint<T>, + Spend<T, <T as transfer::Configuration>::IntegratedEncryptionScheme>, + ), +>; diff --git a/manta-pay/src/accounting/config.rs b/manta-pay/src/accounting/config.rs index d1abbc7f1..f49749110 100644 --- a/manta-pay/src/accounting/config.rs +++ b/manta-pay/src/accounting/config.rs @@ -27,6 +27,7 @@ use crate::{ }, }; use ark_bls12_381::Bls12_381; +use ark_crypto_primitives::crh::pedersen::{constraints::CRHGadget, CRH}; use ark_ed_on_bls12_381::{constraints::EdwardsVar, EdwardsProjective, Fq}; use manta_accounting::{identity, transfer}; use manta_crypto::{commitment::CommitmentScheme, PseudorandomFunctionFamily}; @@ -76,29 +77,23 @@ pub type ProofSystem = Groth16<Bls12_381>; pub struct Configuration; impl merkle_tree::Configuration for Configuration { - type LeafHash = ark_crypto_primitives::crh::pedersen::CRH< - PedersenCommitmentProjectiveCurve, - PedersenCommitmentWindowParameters, - >; + type LeafHash = CRH<PedersenCommitmentProjectiveCurve, PedersenCommitmentWindowParameters>; - type InnerHash = ark_crypto_primitives::crh::pedersen::CRH< - PedersenCommitmentProjectiveCurve, - PedersenCommitmentWindowParameters, - >; + type InnerHash = CRH<PedersenCommitmentProjectiveCurve, PedersenCommitmentWindowParameters>; - const HEIGHT: u32 = 32; + const HEIGHT: merkle_tree::Height = merkle_tree::Height::new(20); } impl merkle_tree::constraint::Configuration for Configuration { type ConstraintField = ConstraintField; - type LeafHashVar = ark_crypto_primitives::crh::pedersen::constraints::CRHGadget< + type LeafHashVar = CRHGadget< PedersenCommitmentProjectiveCurve, PedersenCommitmentProjectiveCurveVar, PedersenCommitmentWindowParameters, >; - type InnerHashVar = ark_crypto_primitives::crh::pedersen::constraints::CRHGadget< + type InnerHashVar = CRHGadget< PedersenCommitmentProjectiveCurve, PedersenCommitmentProjectiveCurveVar, PedersenCommitmentWindowParameters, @@ -109,7 +104,7 @@ impl identity::Configuration for Configuration { type SecretKey = <Blake2s as PseudorandomFunctionFamily>::Seed; type PseudorandomFunctionFamily = Blake2s; type CommitmentScheme = PedersenCommitment; - type Rng = SeedIntoRng<<Blake2s as PseudorandomFunctionFamily>::Seed, ChaCha20Rng>; + type Rng = SeedIntoRng<Self::SecretKey, ChaCha20Rng>; } impl identity::constraint::Configuration for Configuration { diff --git a/manta-pay/src/crypto/constraint/constraint_system.rs b/manta-pay/src/crypto/constraint/constraint_system.rs index c2b62e865..0c0869e17 100644 --- a/manta-pay/src/crypto/constraint/constraint_system.rs +++ b/manta-pay/src/crypto/constraint/constraint_system.rs @@ -113,7 +113,9 @@ where /// Constructs a new constraint system which is ready for unknown variables. #[inline] pub fn for_unknown() -> Self { + // FIXME: This might not be the right setup for all proof systems. let cs = ark_r1cs::ConstraintSystem::new_ref(); + cs.set_optimization_goal(ark_r1cs::OptimizationGoal::Constraints); cs.set_mode(ark_r1cs::SynthesisMode::Setup); Self { cs } } @@ -121,7 +123,9 @@ where /// Constructs a new constraint system which is ready for known variables. #[inline] pub fn for_known() -> Self { + // FIXME: This might not be the right setup for all proof systems. let cs = ark_r1cs::ConstraintSystem::new_ref(); + cs.set_optimization_goal(ark_r1cs::OptimizationGoal::Constraints); Self { cs } } } diff --git a/manta-pay/src/crypto/constraint/proof_systems/groth16.rs b/manta-pay/src/crypto/constraint/proof_systems/groth16.rs index 7bc95f222..23fef8fd3 100644 --- a/manta-pay/src/crypto/constraint/proof_systems/groth16.rs +++ b/manta-pay/src/crypto/constraint/proof_systems/groth16.rs @@ -171,11 +171,32 @@ mod test { .unwrap(); } - /// Tests the generation of proofs. + /// #[test] - fn generate_proof() {} - - /// Tests the verification of proofs. - #[test] - fn verify_proof() {} + fn test_private_transfer() { + /* TODO: + let mut rng = thread_rng(); + let commitment_scheme = rng.gen(); + let mut utxo_set = UtxoSet::new(rng.gen()); + + let base = rng.gen::<Identity>(); + let shielded = base.into_shielded(); + + let mint_asset = rng.gen(); + let mint = Mint::build( + mint_asset, + shielded.into_receiver(&commitment_scheme, mint_asset, &mut rng), + ); + + let (proving_key, verifying_key) = + PrivateTransfer::generate_context(&commitment_scheme, &utxo_set, &mut rng) + .unwrap() + .unwrap(); + + let secret_transfer = PrivateTransfer::build( + [rng.gen().into_sender(), rng.gen().into_sender()], + [rng.gen().into_receiver(), rng.gen().into_receiver()], + ); + */ + } } diff --git a/manta-pay/src/crypto/merkle_tree/mod.rs b/manta-pay/src/crypto/merkle_tree/mod.rs index 0c95b41bc..6536a6aff 100644 --- a/manta-pay/src/crypto/merkle_tree/mod.rs +++ b/manta-pay/src/crypto/merkle_tree/mod.rs @@ -31,7 +31,7 @@ mod incremental; use crate::crypto::constraint::{empty, full, ArkConstraintSystem}; -use alloc::vec::Vec; +use alloc::{vec, vec::Vec}; use ark_crypto_primitives::{ crh::{TwoToOneCRH, CRH}, merkle_tree::{Config, MerkleTree as ArkMerkleTree, Path as ArkPath, TwoToOneDigest}, @@ -44,6 +44,32 @@ use rand::{ RngCore, }; +/// Merkle Tree Height Type +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Height(u16); + +impl Height { + /// Builds a Merkle Tree [`Height`] whenever `height >= 2`. + #[inline] + pub const fn new(height: u16) -> Self { + let height = Self(height); + height.inner(); + height + } + + /// Returns the height as a `u16`. + #[inline] + pub const fn get(&self) -> u16 { + self.0 + } + + /// Returns the inner height as a `u16`. + #[inline] + pub const fn inner(&self) -> u16 { + self.0 - 2 + } +} + /// Merkle Tree Configuration pub trait Configuration { /// Leaf Hash Type @@ -53,13 +79,13 @@ pub trait Configuration { type InnerHash: TwoToOneCRH; /// Merkle Tree Height - const HEIGHT: u32; + const HEIGHT: Height; } /// Computes the Merkle Tree capacity given the `height`. #[inline] -pub const fn capacity(height: u32) -> usize { - 2usize.pow(height) +pub const fn capacity(height: Height) -> usize { + 2usize.pow(height.0 as u32) } /// Computes the necessary padding required to fill the capacity of a Merkle Tree with the @@ -67,7 +93,7 @@ pub const fn capacity(height: u32) -> usize { /// /// Returns `None` if `length` is larger than the capacity of the tree. #[inline] -pub const fn padding_length(height: u32, length: usize) -> Option<usize> { +pub const fn padding_length(height: Height, length: usize) -> Option<usize> { let capacity = capacity(height); if length > capacity { return None; @@ -229,7 +255,7 @@ where /// Returns the height of this merkle tree. #[inline] - pub fn height(&self) -> u32 { + pub fn height(&self) -> Height { C::HEIGHT } @@ -269,7 +295,7 @@ where Self(incremental::IncrementalMerkleTree::blank( &parameters.leaf, &parameters.inner, - C::HEIGHT as usize, + C::HEIGHT.0 as usize, )) } @@ -293,7 +319,7 @@ where /// Returns the height of this incremental merkle tree. #[inline] - pub fn height(&self) -> u32 { + pub fn height(&self) -> Height { C::HEIGHT } @@ -540,7 +566,11 @@ pub mod constraint { // PathVarInnerType::new_witness( ns!(cs.cs, "path variable secret witness"), - empty::<PathInnerType<C>>, + full(PathInnerType { + leaf_sibling_hash: Default::default(), + auth_path: vec![Default::default(); C::HEIGHT.inner() as usize], + leaf_index: Default::default(), + }), ) } } From 6a8456926a129b77ec1ec5318cd235e6e2a16915 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 19 Sep 2021 13:18:43 -0400 Subject: [PATCH 042/275] fix dependencies, solve tag size issue --- manta-accounting/Cargo.toml | 2 +- manta-pay/Cargo.toml | 3 +-- manta-pay/src/crypto/ies.rs | 7 ++++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 8f9422f70..ec27ec799 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -38,7 +38,7 @@ std = ["cocoon-adapters"] test = [] [dependencies] -cocoon = { version = "0.2.4", optional = true, default-features = false } +cocoon = { version = "0.2.5", optional = true, default-features = false } derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } derive_more = { version = "0.99.16", default-features = false, features = ["add", "add_assign", "display", "from", "mul", "mul_assign", "sum"] } manta-crypto = { path = "../manta-crypto", default-features = false } diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 3bc5bec55..7139784dd 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -29,7 +29,7 @@ default = [] std = [] [dependencies] -aes-gcm = { version = "0.9.4", default-features = false } +aes-gcm = { version = "0.9.4", default-features = false, features = ["aes", "alloc"] } ark-bls12-381 = { version = "0.3.0", default-features = false, features = ["curve"] } ark-crypto-primitives = { version = "0.3.0", default-features = false, features = ["r1cs"] } ark-ec = { version = "0.3.0", default-features = false } @@ -40,7 +40,6 @@ ark-r1cs-std = { version = "0.3.1", default-features = false } ark-relations = { version = "0.3.0", default-features = false } bip32 = { version = "0.2.2", default-features = false, features = ["bip39", "secp256k1"] } blake2 = { version = "0.9.2", default-features = false } -cocoon = { version = "0.2.4", default-features = false } derivative = { version = "2.2.0", default-features = false } generic-array = { version = "0.14.4", default-features = false } manta-accounting = { path = "../manta-accounting", default-features = false } diff --git a/manta-pay/src/crypto/ies.rs b/manta-pay/src/crypto/ies.rs index b0bd4aeb1..ec18cf15a 100644 --- a/manta-pay/src/crypto/ies.rs +++ b/manta-pay/src/crypto/ies.rs @@ -39,10 +39,11 @@ pub type PublicKey = [u8; 32]; /// Secret Key Type pub type SecretKey = [u8; 32]; +/// `GCM` Tag Size +const GCM_TAG_SIZE: usize = (aes_gcm::C_MAX - aes_gcm::P_MAX) as usize; + /// Asset Ciphertext Type -// FIXME: This should be automatically calculated from [`Asset`]. -// FIXME: Is this calculation correct and how do we know? -pub type AssetCiphertext = [u8; Asset::SIZE + 16]; +pub type AssetCiphertext = [u8; Asset::SIZE + GCM_TAG_SIZE]; /// Ephemeral Public Key Type pub type EphemeralPublicKey = PublicKey; From cc66e21a6f30a441aad43f78414c3a7ffdffbaa7 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 19 Sep 2021 14:02:35 -0400 Subject: [PATCH 043/275] fix some cocoon interfaces --- manta-accounting/src/fs.rs | 76 ++++++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 15 deletions(-) diff --git a/manta-accounting/src/fs.rs b/manta-accounting/src/fs.rs index ced71e5a7..3933f1c35 100644 --- a/manta-accounting/src/fs.rs +++ b/manta-accounting/src/fs.rs @@ -58,7 +58,7 @@ pub mod cocoon { use cocoon_crate::{Cocoon, Error as CocoonError}; use core::{ convert::{Infallible, TryInto}, - fmt, + fmt, mem, ops::Drop, }; use manta_util::from_variant_impl; @@ -68,21 +68,21 @@ pub mod cocoon { /// Cocoon Loading/Saving Error #[derive(Debug)] pub enum Error { - /// I/O Error - Io(IoError), + /// File Opening Error + UnableToOpenFile(IoError), /// Cocoon Error Cocoon(CocoonError), } - from_variant_impl!(Error, Io, IoError); + from_variant_impl!(Error, UnableToOpenFile, IoError); from_variant_impl!(Error, Cocoon, CocoonError); impl fmt::Display for Error { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - Self::Io(err) => write!(f, "I/O Error: {}", err), + Self::UnableToOpenFile(err) => write!(f, "File Opening Error: {}", err), Self::Cocoon(err) => write!(f, "Cocoon Error: {:?}", err), } } @@ -99,6 +99,15 @@ pub mod cocoon { fn from_payload(payload: &[u8]) -> Result<Self, Self::Error>; } + impl<const N: usize> FromPayload for [u8; N] { + type Error = core::array::TryFromSliceError; + + #[inline] + fn from_payload(payload: &[u8]) -> Result<Self, Self::Error> { + (*payload).try_into() + } + } + impl FromPayload for Vec<u8> { type Error = Infallible; @@ -108,12 +117,30 @@ pub mod cocoon { } } - impl<const N: usize> FromPayload for [u8; N] { - type Error = core::array::TryFromSliceError; + /// Owned Payload Parsing + pub trait FromPayloadOwned: Sized { + /// Parsing Error Type + type Error; + + /// Converts the `payload` into an element of type `Self`. + fn from_payload_owned(payload: Vec<u8>) -> Result<Self, Self::Error>; + } + + impl<const N: usize> FromPayloadOwned for [u8; N] { + type Error = Vec<u8>; #[inline] - fn from_payload(payload: &[u8]) -> Result<Self, Self::Error> { - (*payload).try_into() + fn from_payload_owned(payload: Vec<u8>) -> Result<Self, Self::Error> { + payload.try_into() + } + } + + impl FromPayloadOwned for Vec<u8> { + type Error = Infallible; + + #[inline] + fn from_payload_owned(payload: Vec<u8>) -> Result<Self, Self::Error> { + Ok(payload) } } @@ -123,7 +150,7 @@ pub mod cocoon { pub struct Loader(Vec<u8>); impl Loader { - /// Parses the loaded data into an element of type `T`. + /// Parses the loaded data into an element of type `T` by taking a referece to the payload. #[inline] pub fn parse<T>(self) -> Result<T, T::Error> where @@ -131,6 +158,15 @@ pub mod cocoon { { T::from_payload(&self.0) } + + /// Parses the loaded data into an element of type `T` by taking ownership of the payload. + #[inline] + pub fn parse_owned<T>(mut self) -> Result<T, T::Error> + where + T: FromPayloadOwned, + { + T::from_payload_owned(mem::take(&mut self.0)) + } } impl Load for Loader { @@ -157,23 +193,33 @@ pub mod cocoon { fn payload(&self) -> Vec<u8>; } - impl Payload for Vec<u8> { + impl<const N: usize> Payload for [u8; N] { #[inline] fn payload(&self) -> Vec<u8> { - self.clone() + (*self).into() } } - impl<const N: usize> Payload for [u8; N] { + impl Payload for &[u8] { #[inline] fn payload(&self) -> Vec<u8> { - (*self).into() + self.to_vec() + } + } + + impl Payload for Vec<u8> { + #[inline] + fn payload(&self) -> Vec<u8> { + self.clone() } } /// Cocoon [`Save`] Borrowed Data Adapter #[derive(Clone, Copy)] - pub struct Saver<'t, T>(pub &'t T) + pub struct Saver<'t, T>( + /// Payload Source + pub &'t T, + ) where T: Payload; From b78180934d6ca6955fbd7392dcb48ab003c30c2e Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 19 Sep 2021 17:55:39 -0400 Subject: [PATCH 044/275] improve key interface, start transaction test framework --- manta-accounting/src/identity.rs | 12 ++++++ manta-accounting/src/keys.rs | 34 ++++++++++++++--- manta-accounting/src/transfer.rs | 63 ++++++++++++++++++++++++++++++-- manta-accounting/src/wallet.rs | 18 ++------- manta-pay/src/accounting/keys.rs | 17 +++------ 5 files changed, 110 insertions(+), 34 deletions(-) diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 16b7cc022..aebc4081d 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -769,6 +769,18 @@ where Identity::generate_spend(source) } + /// Opens a [`Spend`] without having to decrypt an encrypted asset. + #[inline] + pub fn open(self, asset: Asset) -> OpenSpend<C> { + // FIXME: Have a convenience function on [`Identity`] which is an alternative to + // `into_receiver` when we know the `asset`, so that we don't have to use a [`Spend`] and + // we can go strait to [`OpenSpend`]. + OpenSpend { + asset, + identity: self.identity, + } + } + /// Tries to open an `encrypted_asset`, returning an [`OpenSpend`] if successful. #[inline] pub fn try_open(self, encrypted_asset: &EncryptedMessage<I>) -> Result<OpenSpend<C>, I::Error> { diff --git a/manta-accounting/src/keys.rs b/manta-accounting/src/keys.rs index 72b9c5cfa..3824b353a 100644 --- a/manta-accounting/src/keys.rs +++ b/manta-accounting/src/keys.rs @@ -58,7 +58,7 @@ pub trait DerivedSecretKeyGenerator { /// the given `index`. fn generate_key( &self, - is_external: bool, + kind: KeyKind, account: &Self::Account, index: &Self::Index, ) -> Result<Self::SecretKey, Self::Error>; @@ -98,19 +98,43 @@ pub trait DerivedSecretKeyGenerator { } } +/// Key Kind +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum KeyKind { + /// External Key + External, + + /// Internal Key + Internal, +} + +impl KeyKind { + /// Returns `true` if `self` matches [`External`](Self::External). + #[inline] + pub const fn is_external(&self) -> bool { + matches!(self, Self::External) + } + + /// Returns `true` if `self` matches [`Internal`](Self::Internal). + #[inline] + pub const fn is_internal(&self) -> bool { + matches!(self, Self::Internal) + } +} + /// Generates an internal or external secret key according to the [`DerivedSecretKeyGenerator`] /// protocol and increments the running `index`. #[inline] fn next_key<D>( source: &D, - is_external: bool, + kind: KeyKind, account: &D::Account, index: &mut D::Index, ) -> Result<D::SecretKey, D::Error> where D: DerivedSecretKeyGenerator + ?Sized, { - let secret_key = source.generate_key(is_external, account, index)?; + let secret_key = source.generate_key(kind, account, index)?; index.increment(); Ok(secret_key) } @@ -126,7 +150,7 @@ pub fn next_external<D>( where D: DerivedSecretKeyGenerator + ?Sized, { - next_key(source, true, account, index) + next_key(source, KeyKind::External, account, index) } /// Generates an internal secret key according to the [`DerivedSecretKeyGenerator`] protocol @@ -140,7 +164,7 @@ pub fn next_internal<D>( where D: DerivedSecretKeyGenerator + ?Sized, { - next_key(source, false, account, index) + next_key(source, KeyKind::Internal, account, index) } /// Keys diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 41aa42516..31ea61b59 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -179,12 +179,26 @@ pub trait Configuration: >; } +/// Transfer Shielded Identity Type +pub type ShieldedIdentity<T> = + identity::ShieldedIdentity<T, <T as Configuration>::IntegratedEncryptionScheme>; + +/// Transfer Spend Type +pub type Spend<T> = identity::Spend<T, <T as Configuration>::IntegratedEncryptionScheme>; + /// Transfer Sender Type pub type Sender<T> = identity::Sender<T, <T as Configuration>::UtxoSet>; /// Transfer Receiver Type pub type Receiver<T> = identity::Receiver<T, <T as Configuration>::IntegratedEncryptionScheme>; +/// Transfer Integrated Encryption Scheme Error +pub type IntegratedEncryptionSchemeError<T> = + <<T as Configuration>::IntegratedEncryptionScheme as IntegratedEncryptionScheme>::Error; + +/// Transfer Constraint System Type +pub type ConstraintSystem<T> = <T as Configuration>::ConstraintSystem; + /// Transfer Sender Variable Type pub type SenderVar<T> = identity::constraint::SenderVar<T, <T as Configuration>::UtxoSet>; @@ -192,9 +206,6 @@ pub type SenderVar<T> = identity::constraint::SenderVar<T, <T as Configuration>: pub type ReceiverVar<T> = identity::constraint::ReceiverVar<T, <T as Configuration>::IntegratedEncryptionScheme>; -/// Transfer Constraint System Type -pub type ConstraintSystem<T> = <T as Configuration>::ConstraintSystem; - /// Transfer Proving Context Type pub type ProvingContext<T> = <<T as Configuration>::ProofSystem as ProofSystem>::ProvingContext; @@ -868,6 +879,7 @@ pub trait Shape: sealed::Sealed { /// Canonical Transaction Types pub mod canonical { use super::*; + use crate::identity::{AssetParameters, Identity, OpenSpend}; /// Implements [`Shape`] for a given shape type. macro_rules! impl_shape { @@ -940,6 +952,28 @@ pub mod canonical { Default::default(), ) } + + /// Builds a [`Mint`] from an `identity` and an `asset`. + #[inline] + pub fn from_identity<R>( + identity: Identity<T>, + commitment_scheme: &T::CommitmentScheme, + asset: Asset, + rng: &mut R, + ) -> Result<(Mint<T>, OpenSpend<T>), IntegratedEncryptionSchemeError<T>> + where + R: CryptoRng + RngCore + ?Sized, + Standard: Distribution<AssetParameters<T>>, + { + let (shielded_identity, spend) = identity.into_receiver(commitment_scheme); + Ok(( + Mint::build( + asset, + shielded_identity.into_receiver(commitment_scheme, asset, rng)?, + ), + spend.open(asset), + )) + } } /// Private Transfer Transaction Shape @@ -1015,3 +1049,26 @@ pub mod canonical { } } } + +/* TODO: +/// Testing Framework +#[cfg(feature = "test")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] +pub mod test { + use super::*; + use canonical::Mint; + use rand::Rng; + + /// + #[inline] + pub fn sample_sender<T, R>(commitment_scheme: &T::CommitmentScheme, rng: &mut R) + where + T: Configuration, + R: CryptoRng + RngCore + ?Sized, + { + // TODO: let _ = Mint::from_identity(rng.gen(), commitment_scheme, rng.gen(), rng); + let _ = (commitment_scheme, rng); + todo!() + } +} +*/ diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index cd905900b..79ef79756 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -25,7 +25,7 @@ use crate::{ asset::{Asset, AssetBalance, AssetId}, fs::{Load, Save}, identity::{self, AssetParameters, Identity, OpenSpend, Receiver, ShieldedIdentity, Spend}, - keys::{self, DerivedSecretKeyGenerator, ExternalKeys, InternalKeys}, + keys::{self, DerivedSecretKeyGenerator, ExternalKeys, InternalKeys, KeyKind}, ledger::Ledger, transfer::{ self, @@ -305,7 +305,7 @@ where external_index: D::Index, internal_index: D::Index, gap_limit: usize, - ) -> Option<(OpenSpend<C>, SpendKind)> + ) -> Option<(OpenSpend<C>, KeyKind)> where C: identity::Configuration<SecretKey = D::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, @@ -315,12 +315,12 @@ where if let Some(spend) = self.find_external_open_spend(encrypted_asset, external_index, gap_limit) { - return Some((spend, SpendKind::External)); + return Some((spend, KeyKind::External)); } if let Some(spend) = self.find_internal_open_spend(encrypted_asset, internal_index, gap_limit) { - return Some((spend, SpendKind::Internal)); + return Some((spend, KeyKind::Internal)); } None } @@ -482,16 +482,6 @@ where } } -/// Spend Kind -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum SpendKind { - /// External Spend - External, - - /// Internal Spend - Internal, -} - /// Internal Receiver Error /// /// This `enum` is the error state for the [`generate_internal_receiver`] method on [`Wallet`]. diff --git a/manta-pay/src/accounting/keys.rs b/manta-pay/src/accounting/keys.rs index 4d3f2a6d4..128f5796e 100644 --- a/manta-pay/src/accounting/keys.rs +++ b/manta-pay/src/accounting/keys.rs @@ -26,7 +26,7 @@ use alloc::{format, string::String}; use bip32::Seed; use core::{marker::PhantomData, num::ParseIntError, str::FromStr}; -use manta_accounting::keys::{DerivedSecretKeyGenerator, DerivedSecretKeyParameter}; +use manta_accounting::keys::{DerivedSecretKeyGenerator, DerivedSecretKeyParameter, KeyKind}; pub use bip32::{Error, Mnemonic, XPrv as SecretKey}; @@ -181,11 +181,7 @@ where /// Computes the [`BIP-0044`] path string for the given coin settings. /// /// [`BIP-0044`]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki -pub fn path_string<C>( - is_external: bool, - account: &AccountParameter, - index: &IndexParameter, -) -> String +pub fn path_string<C>(kind: KeyKind, account: &AccountParameter, index: &IndexParameter) -> String where C: CoinType, { @@ -195,7 +191,7 @@ where BIP_44_PURPOSE_ID, C::COIN_TYPE_ID, account.0, - if is_external { 0 } else { 1 }, + if kind.is_external() { 0 } else { 1 }, index.0 ) } @@ -215,13 +211,10 @@ where #[inline] fn generate_key( &self, - is_external: bool, + kind: KeyKind, account: &Self::Account, index: &Self::Index, ) -> Result<Self::SecretKey, Self::Error> { - SecretKey::derive_from_path( - &self.seed, - &path_string::<C>(is_external, account, index).parse()?, - ) + SecretKey::derive_from_path(&self.seed, &path_string::<C>(kind, account, index).parse()?) } } From 035e813ad84f4864bdca75a9a650498aa135fd63 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 20 Sep 2021 14:45:19 -0400 Subject: [PATCH 045/275] add optimization paths for asset encryption --- manta-accounting/Cargo.toml | 4 +- manta-accounting/src/identity.rs | 504 ++++++++++++++++--------------- manta-accounting/src/transfer.rs | 19 +- manta-accounting/src/wallet.rs | 82 +++-- manta-crypto/src/ies.rs | 276 ++++++++++++----- manta-pay/src/crypto/ies.rs | 20 +- 6 files changed, 531 insertions(+), 374 deletions(-) diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index ec27ec799..17ea97f4f 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -38,13 +38,13 @@ std = ["cocoon-adapters"] test = [] [dependencies] -cocoon = { version = "0.2.5", optional = true, default-features = false } +cocoon = { version = "0.3.0", optional = true, default-features = false } derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } derive_more = { version = "0.99.16", default-features = false, features = ["add", "add_assign", "display", "from", "mul", "mul_assign", "sum"] } manta-crypto = { path = "../manta-crypto", default-features = false } manta-util = { path = "../manta-util", default-features = false } rand = { version = "0.8.4", default-features = false } -zeroize = { version = "1.3.0", optional = true, default-features = false, features = ["alloc", "zeroize_derive"] } +zeroize = { version = "1.4.1", optional = true, default-features = false, features = ["alloc", "zeroize_derive"] } [dev-dependencies] rand = "0.8.4" diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index aebc4081d..8908e15f5 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -17,17 +17,14 @@ //! Identities, Senders, and Receivers // FIXME: Check the secret key APIs. -// FIXME: Should the identity types have methods that expose their members? Or should it be -// completely opaque, and let the internal APIs handle all the logic? // TODO: Since `Configuration::SecretKey: Clone`, should `Identity: Clone`? -// TODO: Separate "constraint" types into another module just like `manta_crypto::set`. use crate::{ asset::{Asset, AssetBalance, AssetId, AssetVar}, keys::SecretKeyGenerator, ledger::Ledger, }; -use core::{convert::Infallible, fmt::Debug, hash::Hash, marker::PhantomData}; +use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use manta_crypto::{ commitment::{CommitmentScheme, Input as CommitmentInput}, ies::{self, EncryptedMessage, IntegratedEncryptionScheme}, @@ -258,7 +255,7 @@ impl<C> Identity<C> where C: Configuration, { - /// Generates a new [`Identity`] from a [`SecretKey`]. + /// Builds a new [`Identity`] from a [`SecretKey`]. #[inline] pub fn new(secret_key: SecretKey<C>) -> Self { Self { secret_key } @@ -315,52 +312,40 @@ where parameters } - /// Generates the associated `C::Rng`, `AssetParameters<C>`, and `ies::KeyPair<I>` for + /// Generates the associated [`AssetParameters`] and asset [`KeyPair`](ies::KeyPair) for /// this identity. - /// - /// # API Note - /// - /// This function is intentionally private so that the internal random number generator is - /// not part of the public interface. See [`Self::parameters_and_asset_keypair`] for access to - /// the associated `parameters` and `asset_keypair`. - /// - /// # Implementation Note - /// - /// See [`Self::rng_and_parameters`]. #[inline] - fn rng_and_parameters_and_asset_keypair<I>( - &self, - ) -> (C::Rng, AssetParameters<C>, ies::KeyPair<I>) + fn parameters_and_asset_keypair<I>(&self) -> (AssetParameters<C>, ies::KeyPair<I>) where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { let (mut rng, parameters) = self.rng_and_parameters(); - let asset_keypair = I::keygen(&mut rng); - (rng, parameters, asset_keypair) + (parameters, I::generate_keys(&mut rng)) } - /// Generates [`AssetParameters`] and a [`KeyPair`](ies::KeyPair) for assets that are used by + /// Generates the associated [`AssetParameters`] and asset [`PublicKey`](ies::PublicKey) for /// this identity. #[inline] - fn parameters_and_asset_keypair<I>(&self) -> (AssetParameters<C>, ies::KeyPair<I>) + fn parameters_and_asset_public_key<I>(&self) -> (AssetParameters<C>, ies::PublicKey<I>) where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { - let (_, parameters, asset_keypair) = self.rng_and_parameters_and_asset_keypair(); - (parameters, asset_keypair) + let (mut rng, parameters) = self.rng_and_parameters(); + (parameters, I::generate_public_key(&mut rng)) } - /// Generates a [`KeyPair`](ies::KeyPair) for assets that are used by this identity. + /// Generates the associated [`AssetParameters`] and asset [`SecretKey`](ies::SecretKey) for + /// this identity. #[inline] - fn asset_keypair<I>(&self) -> ies::KeyPair<I> + fn parameters_and_asset_secret_key<I>(&self) -> (AssetParameters<C>, ies::SecretKey<I>) where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { - let (_, asset_keypair) = self.parameters_and_asset_keypair(); - asset_keypair + let (mut rng, parameters) = self.rng_and_parameters(); + (parameters, I::generate_secret_key(&mut rng)) } /// Returns the public key associated with this identity. @@ -431,12 +416,12 @@ where /// Generates a new [`Identity`] from a secret key generation source and builds a new /// [`Sender`] from it. #[inline] - fn generate_sender<G, S>( + pub fn generate_sender<G, S>( source: &mut G, commitment_scheme: &C::CommitmentScheme, asset: Asset, utxo_set: &S, - ) -> Result<Sender<C, S>, SenderError<C, G, S>> + ) -> Result<Sender<C, S>, SenderError<G, S>> where G: SecretKeyGenerator<SecretKey = SecretKey<C>>, S: VerifiedSet<Item = Utxo<C>>, @@ -475,14 +460,14 @@ where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { - let (parameters, asset_keypair) = self.parameters_and_asset_keypair(); - self.build_shielded_identity(commitment_scheme, parameters, asset_keypair.into_public()) + let (parameters, asset_public_key) = self.parameters_and_asset_public_key(); + self.build_shielded_identity(commitment_scheme, parameters, asset_public_key) } /// Generates a new [`Identity`] from a secret key generation source and builds a new /// [`ShieldedIdentity`] from it. #[inline] - fn generate_shielded<G, I>( + pub fn generate_shielded<G, I>( source: &mut G, commitment_scheme: &C::CommitmentScheme, ) -> Result<ShieldedIdentity<C, I>, G::Error> @@ -501,16 +486,14 @@ where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { - Spend { - asset_secret_key: self.asset_keypair().into_secret(), - identity: self, - } + let (_, asset_secret_key) = self.parameters_and_asset_secret_key(); + Spend::new(self, asset_secret_key) } /// Generates a new [`Identity`] from a secret key generation source and builds a new /// [`Spend`] from it. #[inline] - fn generate_spend<G, I>(source: &mut G) -> Result<Spend<C, I>, G::Error> + pub fn generate_spend<G, I>(source: &mut G) -> Result<Spend<C, I>, G::Error> where G: SecretKeyGenerator<SecretKey = SecretKey<C>>, I: IntegratedEncryptionScheme<Plaintext = Asset>, @@ -519,51 +502,97 @@ where Ok(Self::generate(source)?.into_spend()) } - /// Builds a new [`ShieldedIdentity`]-[`Spend`] pair. + /// Tries to open an `encrypted_asset`, returning an [`OpenSpend`] if successful. + #[inline] + pub fn try_open<I>( + self, + encrypted_asset: &EncryptedMessage<I>, + ) -> Result<OpenSpend<C>, I::Error> + where + I: IntegratedEncryptionScheme<Plaintext = Asset>, + Standard: Distribution<AssetParameters<C>>, + { + self.into_spend().try_open(encrypted_asset) + } + + /// Builds a new [`ExternalReceiver`]. #[inline] - pub fn into_receiver<I>( + pub fn into_external_receiver<I>( self, commitment_scheme: &C::CommitmentScheme, - ) -> (ShieldedIdentity<C, I>, Spend<C, I>) + ) -> ExternalReceiver<C, I> where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { let (parameters, asset_keypair) = self.parameters_and_asset_keypair(); let (asset_public_key, asset_secret_key) = asset_keypair.into(); - ( - self.build_shielded_identity(commitment_scheme, parameters, asset_public_key), - Spend::new(self, asset_secret_key), - ) + ExternalReceiver { + shielded_identity: self.build_shielded_identity( + commitment_scheme, + parameters, + asset_public_key, + ), + spend: Spend::new(self, asset_secret_key), + } } /// Generates a new [`Identity`] from a secret key generation source and builds a new - /// [`ShieldedIdentity`]-[`Spend`] pair from it. - #[allow(clippy::type_complexity)] // NOTE: This is not very complex. + /// [`ExternalReceiver`] from it. #[inline] - pub fn generate_receiver<G, I>( + pub fn generate_external_receiver<G, I>( source: &mut G, commitment_scheme: &C::CommitmentScheme, - ) -> Result<(ShieldedIdentity<C, I>, Spend<C, I>), G::Error> + ) -> Result<ExternalReceiver<C, I>, G::Error> where G: SecretKeyGenerator<SecretKey = SecretKey<C>>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { - Ok(Self::generate(source)?.into_receiver(commitment_scheme)) + Ok(Self::generate(source)?.into_external_receiver(commitment_scheme)) } - /// Tries to open an `encrypted_asset`, returning an [`OpenSpend`] if successful. + /// Builds a new [`InternalReceiver`]. #[inline] - pub fn try_open<I>( + pub fn into_internal_receiver<I, R>( self, - encrypted_asset: &EncryptedMessage<I>, - ) -> Result<OpenSpend<C>, I::Error> + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + rng: &mut R, + ) -> Result<InternalReceiver<C, I>, I::Error> where I: IntegratedEncryptionScheme<Plaintext = Asset>, + R: CryptoRng + RngCore + ?Sized, Standard: Distribution<AssetParameters<C>>, { - self.into_spend().try_open(encrypted_asset) + let (parameters, asset_public_key) = self.parameters_and_asset_public_key(); + Ok(InternalReceiver { + receiver: self + .build_shielded_identity(commitment_scheme, parameters, asset_public_key) + .into_receiver(commitment_scheme, asset, rng)?, + open_spend: OpenSpend::new(self, asset), + }) + } + + /// Generates a new [`Identity`] from a secret key generation source and builds a new + /// [`InternalReceiver`] from it. + #[inline] + pub fn generate_internal_receiver<G, I, R>( + source: &mut G, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + rng: &mut R, + ) -> Result<InternalReceiver<C, I>, InternalReceiverError<G, I>> + where + G: SecretKeyGenerator<SecretKey = SecretKey<C>>, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + R: CryptoRng + RngCore + ?Sized, + Standard: Distribution<AssetParameters<C>>, + { + Self::generate(source) + .map_err(InternalReceiverError::SecretKeyError)? + .into_internal_receiver(commitment_scheme, asset, rng) + .map_err(InternalReceiverError::EncryptionError) } } @@ -624,24 +653,6 @@ where Identity::generate_shielded(source, commitment_scheme) } - /// Returns the UTXO randomness for this shielded identity. - #[inline] - pub fn utxo_randomness(&self) -> &UtxoRandomness<C> { - &self.utxo_randomness - } - - /// Returns the void number commitment for this shielded identity. - #[inline] - pub fn void_number_commitment(&self) -> &VoidNumberCommitment<C> { - &self.void_number_commitment - } - - /// Returns the asset public key for this shielded identity. - #[inline] - pub fn asset_public_key(&self) -> &ies::PublicKey<I> { - &self.asset_public_key - } - /// Generates a [`Receiver`] from a [`ShieldedIdentity`]. #[inline] pub fn into_receiver<R>( @@ -675,48 +686,31 @@ where /// Spend Error /// -/// This is a work-around for a `clippy` overreaction bug. -/// See <https://github.com/mcarton/rust-derivative/issues/100>. -#[allow(unreachable_code)] -mod spend_error { - use super::*; - - /// Spend Error - /// - /// This `enum` is the error state for the [`into_sender`] method on [`Spend`]. - /// See its documentation for more. - /// - /// [`into_sender`]: Spend::into_sender - #[derive(derivative::Derivative)] - #[derivative( - Clone(bound = "I::Error: Clone, S::ContainmentError: Clone"), - Copy(bound = "I::Error: Copy, S::ContainmentError: Copy"), - Debug(bound = "I::Error: Debug, S::ContainmentError: Debug"), - Eq(bound = "I::Error: Eq, S::ContainmentError: Eq"), - Hash(bound = "I::Error: Hash, S::ContainmentError: Hash"), - PartialEq(bound = "I::Error: PartialEq, S::ContainmentError: PartialEq") - )] - pub enum SpendError<C, I, S> - where - C: Configuration, - I: IntegratedEncryptionScheme<Plaintext = Asset>, - S: VerifiedSet<Item = Utxo<C>>, - { - /// Encryption Error - EncryptionError(I::Error), - - /// Missing UTXO Containment Proof - MissingUtxo(S::ContainmentError), +/// This `enum` is the error state for the [`into_sender`] method on [`Spend`]. +/// See its documentation for more. +/// +/// [`into_sender`]: Spend::into_sender +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "I::Error: Clone, S::ContainmentError: Clone"), + Copy(bound = "I::Error: Copy, S::ContainmentError: Copy"), + Debug(bound = "I::Error: Debug, S::ContainmentError: Debug"), + Eq(bound = "I::Error: Eq, S::ContainmentError: Eq"), + Hash(bound = "I::Error: Hash, S::ContainmentError: Hash"), + PartialEq(bound = "I::Error: PartialEq, S::ContainmentError: PartialEq") +)] +pub enum SpendError<I, S> +where + I: IntegratedEncryptionScheme<Plaintext = Asset>, + S: VerifiedSet, +{ + /// Encryption Error + EncryptionError(I::Error), - /// Type Parameter Marker - #[doc(hidden)] - __(Infallible, PhantomData<C>), - } + /// Missing UTXO Containment Proof + MissingUtxo(S::ContainmentError), } -#[doc(inline)] -pub use spend_error::SpendError; - /// Spending Information pub struct Spend<C, I> where @@ -735,12 +729,12 @@ where C: Configuration, I: IntegratedEncryptionScheme<Plaintext = Asset>, { - /// Generates a new `Spend` from an `Identity` and an `ies::SecretKey<I>`. + /// Builds a new `Spend` from an `Identity` and an `ies::SecretKey<I>`. /// /// # API Note /// - /// This function is intentionally private so that the `asset_secret_key` is not part - /// of the public interface. + /// This function is intentionally private so that `identity` and `asset_secret_key` are + /// not part of the public interface. #[inline] fn new(identity: Identity<C>, asset_secret_key: ies::SecretKey<I>) -> Self { Self { @@ -769,25 +763,13 @@ where Identity::generate_spend(source) } - /// Opens a [`Spend`] without having to decrypt an encrypted asset. - #[inline] - pub fn open(self, asset: Asset) -> OpenSpend<C> { - // FIXME: Have a convenience function on [`Identity`] which is an alternative to - // `into_receiver` when we know the `asset`, so that we don't have to use a [`Spend`] and - // we can go strait to [`OpenSpend`]. - OpenSpend { - asset, - identity: self.identity, - } - } - /// Tries to open an `encrypted_asset`, returning an [`OpenSpend`] if successful. #[inline] pub fn try_open(self, encrypted_asset: &EncryptedMessage<I>) -> Result<OpenSpend<C>, I::Error> { - Ok(OpenSpend { - asset: self.asset_secret_key.decrypt(encrypted_asset)?, - identity: self.identity, - }) + Ok(OpenSpend::new( + self.identity, + self.asset_secret_key.decrypt(encrypted_asset)?, + )) } /// Builds a new [`Sender`] for the given `encrypted_asset`. @@ -797,7 +779,7 @@ where commitment_scheme: &C::CommitmentScheme, encrypted_asset: EncryptedMessage<I>, utxo_set: &S, - ) -> Result<Sender<C, S>, SpendError<C, I, S>> + ) -> Result<Sender<C, S>, SpendError<I, S>> where S: VerifiedSet<Item = Utxo<C>>, Standard: Distribution<AssetParameters<C>>, @@ -821,6 +803,50 @@ where } } +/// External Receiver +pub struct ExternalReceiver<C, I> +where + C: Configuration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, +{ + /// Shielded Identity + pub shielded_identity: ShieldedIdentity<C, I>, + + /// Spend + pub spend: Spend<C, I>, +} + +impl<C, I> ExternalReceiver<C, I> +where + C: Configuration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, +{ + /// Generates a new [`Identity`] from a secret key generation source and builds a new + /// [`ExternalReceiver`] from it. + #[inline] + pub fn generate<G>( + source: &mut G, + commitment_scheme: &C::CommitmentScheme, + ) -> Result<Self, G::Error> + where + G: SecretKeyGenerator<SecretKey = SecretKey<C>>, + Standard: Distribution<AssetParameters<C>>, + { + Identity::generate_external_receiver(source, commitment_scheme) + } +} + +impl<C, I> From<ExternalReceiver<C, I>> for (ShieldedIdentity<C, I>, Spend<C, I>) +where + C: Configuration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, +{ + #[inline] + fn from(external: ExternalReceiver<C, I>) -> Self { + (external.shielded_identity, external.spend) + } +} + /// Open [`Spend`] pub struct OpenSpend<C> where @@ -837,6 +863,17 @@ impl<C> OpenSpend<C> where C: Configuration, { + /// Builds a new `OpenSpend` from an `Identity` and an `Asset`. + /// + /// # API Note + /// + /// This function is intentionally private so that `identity` and `asset` are + /// not part of the public interface. + #[inline] + fn new(identity: Identity<C>, asset: Asset) -> Self { + Self { identity, asset } + } + /// Builds a new [`Sender`] for `self`. #[inline] pub fn into_sender<S>( @@ -853,49 +890,106 @@ where } } -/// Sender Error +/// Internal Receiver Error /// -/// This is a work-around for a `clippy` overreaction bug. -/// See <https://github.com/mcarton/rust-derivative/issues/100>. -#[allow(unreachable_code)] -mod sender_error { - use super::*; +/// This `enum` is the error state for the [`generate`] method on [`InternalReceiver`]. +/// See its documentation for more. +/// +/// [`generate`]: InternalReceiver::generate +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "G::Error: Clone, I::Error: Clone"), + Copy(bound = "G::Error: Copy, I::Error: Copy"), + Debug(bound = "G::Error: Debug, I::Error: Debug"), + Eq(bound = "G::Error: Eq, I::Error: Eq"), + Hash(bound = "G::Error: Hash, I::Error: Hash"), + PartialEq(bound = "G::Error: PartialEq, I::Error: PartialEq") +)] +pub enum InternalReceiverError<G, I> +where + G: SecretKeyGenerator, + I: IntegratedEncryptionScheme<Plaintext = Asset>, +{ + /// Secret Key Generator Error + SecretKeyError(G::Error), - /// Sender Error - /// - /// This `enum` is the error state for the [`generate`] method on [`Sender`]. - /// See its documentation for more. - /// - /// [`generate`]: Sender::generate - #[derive(derivative::Derivative)] - #[derivative( - Clone(bound = "G::Error: Clone, S::ContainmentError: Clone"), - Copy(bound = "G::Error: Copy, S::ContainmentError: Copy"), - Debug(bound = "G::Error: Debug, S::ContainmentError: Debug"), - Eq(bound = "G::Error: Eq, S::ContainmentError: Eq"), - Hash(bound = "G::Error: Hash, S::ContainmentError: Hash"), - PartialEq(bound = "G::Error: PartialEq, S::ContainmentError: PartialEq") - )] - pub enum SenderError<C, G, S> + /// Encryption Error + EncryptionError(I::Error), +} + +/// Internal Receiver +pub struct InternalReceiver<C, I> +where + C: Configuration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, +{ + /// Receiver + pub receiver: Receiver<C, I>, + + /// Open Spend + pub open_spend: OpenSpend<C>, +} + +impl<C, I> InternalReceiver<C, I> +where + C: Configuration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, +{ + /// Generates a new [`Identity`] from a secret key generation source and builds a new + /// [`InternalReceiver`] from it. + #[inline] + pub fn generate<G, R>( + source: &mut G, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + rng: &mut R, + ) -> Result<Self, InternalReceiverError<G, I>> where - C: Configuration, G: SecretKeyGenerator<SecretKey = SecretKey<C>>, - S: VerifiedSet<Item = Utxo<C>>, + R: CryptoRng + RngCore + ?Sized, + Standard: Distribution<AssetParameters<C>>, { - /// Secret Key Generator Error - SecretKeyError(G::Error), - - /// Containment Error - MissingUtxo(S::ContainmentError), + Identity::generate_internal_receiver(source, commitment_scheme, asset, rng) + } +} - /// Type Parameter Marker - #[doc(hidden)] - __(Infallible, PhantomData<C>), +impl<C, I> From<InternalReceiver<C, I>> for (Receiver<C, I>, OpenSpend<C>) +where + C: Configuration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, +{ + #[inline] + fn from(internal: InternalReceiver<C, I>) -> Self { + (internal.receiver, internal.open_spend) } } -#[doc(inline)] -pub use sender_error::SenderError; +/// Sender Error +/// +/// This `enum` is the error state for the [`generate`] method on [`Sender`]. +/// See its documentation for more. +/// +/// [`generate`]: Sender::generate +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "G::Error: Clone, S::ContainmentError: Clone"), + Copy(bound = "G::Error: Copy, S::ContainmentError: Copy"), + Debug(bound = "G::Error: Debug, S::ContainmentError: Debug"), + Eq(bound = "G::Error: Eq, S::ContainmentError: Eq"), + Hash(bound = "G::Error: Hash, S::ContainmentError: Hash"), + PartialEq(bound = "G::Error: PartialEq, S::ContainmentError: PartialEq") +)] +pub enum SenderError<G, S> +where + G: SecretKeyGenerator, + S: VerifiedSet, +{ + /// Secret Key Generator Error + SecretKeyError(G::Error), + + /// Containment Error + MissingUtxo(S::ContainmentError), +} /// Sender pub struct Sender<C, S> @@ -955,7 +1049,7 @@ where commitment_scheme: &C::CommitmentScheme, asset: Asset, utxo_set: &S, - ) -> Result<Self, SenderError<C, G, S>> + ) -> Result<Self, SenderError<G, S>> where G: SecretKeyGenerator<SecretKey = SecretKey<C>>, Standard: Distribution<AssetParameters<C>>, @@ -963,54 +1057,18 @@ where Identity::generate_sender(source, commitment_scheme, asset, utxo_set) } - /// Returns the asset for this sender. - #[inline] - pub fn asset(&self) -> Asset { - self.asset - } - /// Returns the asset id for this sender. #[inline] - pub fn asset_id(&self) -> AssetId { + pub(crate) fn asset_id(&self) -> AssetId { self.asset.id } /// Returns the asset value for this sender. #[inline] - pub fn asset_value(&self) -> AssetBalance { + pub(crate) fn asset_value(&self) -> AssetBalance { self.asset.value } - /// Returns the asset parameters for this sender. - #[inline] - pub fn parameters(&self) -> &AssetParameters<C> { - &self.parameters - } - - /// Returns the void number for this sender. - #[inline] - pub fn void_number(&self) -> &VoidNumber<C> { - &self.void_number - } - - /// Returns the void number commitment for this sender. - #[inline] - pub fn void_number_commitment(&self) -> &VoidNumberCommitment<C> { - &self.void_number_commitment - } - - /// Returns the UTXO for this sender. - #[inline] - pub fn utxo(&self) -> &Utxo<C> { - &self.utxo - } - - /// Returns the UTXO containment proof for this sender. - #[inline] - pub fn utxo_containment_proof(&self) -> &ContainmentProof<S> { - &self.utxo_containment_proof - } - /// Extracts ledger posting data for this sender. #[inline] pub fn into_post(self) -> SenderPost<C, S> { @@ -1124,48 +1182,18 @@ where identity.into_receiver(commitment_scheme, asset, rng) } - /// Returns the asset for this receiver. - #[inline] - pub fn asset(&self) -> Asset { - self.asset - } - /// Returns the asset id for this receiver. #[inline] - pub fn asset_id(&self) -> AssetId { + pub(crate) fn asset_id(&self) -> AssetId { self.asset.id } /// Returns the asset value for this receiver. #[inline] - pub fn asset_value(&self) -> AssetBalance { + pub(crate) fn asset_value(&self) -> AssetBalance { self.asset.value } - /// Returns the UTXO randomness for this receiver. - #[inline] - pub fn utxo_randomness(&self) -> &UtxoRandomness<C> { - &self.utxo_randomness - } - - /// Returns the void number commitment for this receiver. - #[inline] - pub fn void_number_commitment(&self) -> &VoidNumberCommitment<C> { - &self.void_number_commitment - } - - /// Returns the UTXO for this reciever. - #[inline] - pub fn utxo(&self) -> &Utxo<C> { - &self.utxo - } - - /// Returns the encrypted asset for this receiver. - #[inline] - pub fn encrypted_asset(&self) -> &EncryptedMessage<I> { - &self.encrypted_asset - } - /// Extracts ledger posting data for this receiver. #[inline] pub fn into_post(self) -> ReceiverPost<C, I> { diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 31ea61b59..ec3e9d619 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -879,7 +879,7 @@ pub trait Shape: sealed::Sealed { /// Canonical Transaction Types pub mod canonical { use super::*; - use crate::identity::{AssetParameters, Identity, OpenSpend}; + use crate::identity::{AssetParameters, Identity, InternalReceiver, OpenSpend}; /// Implements [`Shape`] for a given shape type. macro_rules! impl_shape { @@ -953,7 +953,7 @@ pub mod canonical { ) } - /// Builds a [`Mint`] from an `identity` and an `asset`. + /// Builds a [`Mint`]-[`OpenSpend`] pair from an `identity` and an `asset`. #[inline] pub fn from_identity<R>( identity: Identity<T>, @@ -965,14 +965,11 @@ pub mod canonical { R: CryptoRng + RngCore + ?Sized, Standard: Distribution<AssetParameters<T>>, { - let (shielded_identity, spend) = identity.into_receiver(commitment_scheme); - Ok(( - Mint::build( - asset, - shielded_identity.into_receiver(commitment_scheme, asset, rng)?, - ), - spend.open(asset), - )) + let InternalReceiver { + receiver, + open_spend, + } = identity.into_internal_receiver(commitment_scheme, asset, rng)?; + Ok((Mint::build(asset, receiver), open_spend)) } } @@ -1050,7 +1047,6 @@ pub mod canonical { } } -/* TODO: /// Testing Framework #[cfg(feature = "test")] #[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] @@ -1071,4 +1067,3 @@ pub mod test { todo!() } } -*/ diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index 79ef79756..b3e724efd 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -24,12 +24,16 @@ use crate::{ asset::{Asset, AssetBalance, AssetId}, fs::{Load, Save}, - identity::{self, AssetParameters, Identity, OpenSpend, Receiver, ShieldedIdentity, Spend}, + identity::{ + self, AssetParameters, Identity, InternalReceiver, InternalReceiverError, OpenSpend, + ShieldedIdentity, + }, keys::{self, DerivedSecretKeyGenerator, ExternalKeys, InternalKeys, KeyKind}, ledger::Ledger, transfer::{ self, canonical::{Mint, PrivateTransfer, Reclaim}, + IntegratedEncryptionSchemeError, }, }; use alloc::vec::Vec; @@ -343,7 +347,7 @@ where /// Generates a new [`ShieldedIdentity`] to receive assets to this wallet via an external /// transaction. #[inline] - pub fn generate_external_receiver<C, I>( + pub fn generate_shielded_identity<C, I>( &mut self, commitment_scheme: &C::CommitmentScheme, ) -> Result<ShieldedIdentity<C, I>, D::Error> @@ -356,49 +360,48 @@ where .map(move |identity| identity.into_shielded(commitment_scheme)) } - /// Generates a new [`Receiver`]-[`Spend`] pair to receive `asset` to this wallet via an + /// Generates a new [`InternalReceiver`] to receive `asset` to this wallet via an /// internal transaction. - #[allow(clippy::type_complexity)] // NOTE: This is not very complex. #[inline] pub fn generate_internal_receiver<C, I, R>( &mut self, commitment_scheme: &C::CommitmentScheme, asset: Asset, rng: &mut R, - ) -> Result<(Receiver<C, I>, Spend<C, I>), InternalReceiverError<D, I>> + ) -> Result<InternalReceiver<C, I>, InternalReceiverError<InternalKeys<D>, I>> where C: identity::Configuration<SecretKey = D::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, R: CryptoRng + RngCore + ?Sized, Standard: Distribution<AssetParameters<C>>, { - let (shielded_identity, spend) = self - .next_internal_identity() - .map_err(InternalReceiverError::SecretKeyGenerationError)? - .into_receiver(commitment_scheme); - Ok(( - shielded_identity - .into_receiver(commitment_scheme, asset, rng) - .map_err(InternalReceiverError::EncryptionError)?, - spend, - )) + self.next_internal_identity() + .map_err(InternalReceiverError::SecretKeyError)? + .into_internal_receiver(commitment_scheme, asset, rng) + .map_err(InternalReceiverError::EncryptionError) } - /// Builds a [`Mint`] transaction to mint `asset` and returns the [`Spend`] for that asset. + /// Builds a [`Mint`] transaction to mint `asset` and returns the [`OpenSpend`] for that asset. #[inline] pub fn mint<T, R>( &mut self, commitment_scheme: &T::CommitmentScheme, asset: Asset, rng: &mut R, - ) -> WalletMintResult<D, T> + ) -> Result<(Mint<T>, OpenSpend<T>), MintError<D, T>> where T: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, Standard: Distribution<AssetParameters<T>>, { - let (receiver, spend) = self.generate_internal_receiver(commitment_scheme, asset, rng)?; - Ok((Mint::build(asset, receiver), spend)) + Mint::from_identity( + self.next_internal_identity() + .map_err(MintError::SecretKeyError)?, + commitment_scheme, + asset, + rng, + ) + .map_err(MintError::EncryptionError) } /// TODO: Builds [`PrivateTransfer`] transactions to send `asset` to an `external_receiver`. @@ -482,42 +485,29 @@ where } } -/// Internal Receiver Error +/// Mint Error /// -/// This `enum` is the error state for the [`generate_internal_receiver`] method on [`Wallet`]. +/// This `enum` is the error state for the [`mint`] method on [`Wallet`]. /// See its documentation for more. /// -/// [`generate_internal_receiver`]: Wallet::generate_internal_receiver +/// [`mint`]: Wallet::mint #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "D::Error: Clone, I::Error: Clone"), - Copy(bound = "D::Error: Copy, I::Error: Copy"), - Debug(bound = "D::Error: Debug, I::Error: Debug"), - Eq(bound = "D::Error: Eq, I::Error: Eq"), - Hash(bound = "D::Error: Hash, I::Error: Hash"), - PartialEq(bound = "D::Error: PartialEq, I::Error: PartialEq") + Clone(bound = "D::Error: Clone, IntegratedEncryptionSchemeError<T>: Clone"), + Copy(bound = "D::Error: Copy, IntegratedEncryptionSchemeError<T>: Copy"), + Debug(bound = "D::Error: Debug, IntegratedEncryptionSchemeError<T>: Debug"), + Eq(bound = "D::Error: Eq, IntegratedEncryptionSchemeError<T>: Eq"), + Hash(bound = "D::Error: Hash, IntegratedEncryptionSchemeError<T>: Hash"), + PartialEq(bound = "D::Error: PartialEq, IntegratedEncryptionSchemeError<T>: PartialEq") )] -pub enum InternalReceiverError<D, I> +pub enum MintError<D, T> where D: DerivedSecretKeyGenerator, - I: IntegratedEncryptionScheme<Plaintext = Asset>, + T: transfer::Configuration, { - /// Secret Key Generation Error - SecretKeyGenerationError(D::Error), + /// Secret Key Generator Error + SecretKeyError(D::Error), /// Encryption Error - EncryptionError(I::Error), + EncryptionError(IntegratedEncryptionSchemeError<T>), } - -/// Internal Receiver Result Type -pub type InternalReceiverResult<D, I, OK> = Result<OK, InternalReceiverError<D, I>>; - -/// Wallet Mint Result Type -pub type WalletMintResult<D, T> = InternalReceiverResult< - D, - <T as transfer::Configuration>::IntegratedEncryptionScheme, - ( - Mint<T>, - Spend<T, <T as transfer::Configuration>::IntegratedEncryptionScheme>, - ), ->; diff --git a/manta-crypto/src/ies.rs b/manta-crypto/src/ies.rs index ae8bc81c0..a0274eca7 100644 --- a/manta-crypto/src/ies.rs +++ b/manta-crypto/src/ies.rs @@ -26,6 +26,110 @@ pub(crate) mod prelude { pub use super::IntegratedEncryptionScheme; } +/// Integrated Encryption Scheme Trait +pub trait IntegratedEncryptionScheme { + /// Public Key Type + type PublicKey; + + /// Secret Key Type + type SecretKey; + + /// Plaintext Type + type Plaintext; + + /// Ciphertext Type + type Ciphertext; + + /// Encryption/Decryption Error Type + type Error; + + /// Generates a public/secret keypair. + fn generate_keys<R>(rng: &mut R) -> KeyPair<Self> + where + R: CryptoRng + RngCore + ?Sized; + + /// Generates a public key. + /// + /// This enables an optimization path whenever decryption is not necessary. + #[inline] + fn generate_public_key<R>(rng: &mut R) -> PublicKey<Self> + where + R: CryptoRng + RngCore + ?Sized, + { + Self::generate_keys(rng).into_public() + } + + /// Generates a secret key. + /// + /// This enables an optimization path whenever encryption is not necessary. + #[inline] + fn generate_secret_key<R>(rng: &mut R) -> SecretKey<Self> + where + R: CryptoRng + RngCore + ?Sized, + { + Self::generate_keys(rng).into_secret() + } + + /// Encrypts the `plaintext` with the `public_key`, generating an [`EncryptedMessage`]. + fn encrypt<R>( + plaintext: &Self::Plaintext, + public_key: Self::PublicKey, + rng: &mut R, + ) -> Result<EncryptedMessage<Self>, Self::Error> + where + R: CryptoRng + RngCore + ?Sized; + + /// Generates a public/secret keypair and then encrypts the `plaintext` with the generated + /// public key, returning an [`EncryptedMessage`] and a [`SecretKey`]. + #[inline] + fn generate_keys_and_encrypt<R>( + plaintext: &Self::Plaintext, + rng: &mut R, + ) -> Result<(EncryptedMessage<Self>, SecretKey<Self>), Self::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + Self::generate_keys(rng).encrypt(plaintext, rng) + } + + /// Generates a public key and then encrypts the `plaintext` with the generated public key, + /// returning an [`EncryptedMessage`]. + /// + /// This enables an optimization path whenever decryption is not necessary. + #[inline] + fn generate_public_key_and_encrypt<R>( + plaintext: &Self::Plaintext, + rng: &mut R, + ) -> Result<EncryptedMessage<Self>, Self::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + Self::generate_public_key(rng).encrypt(plaintext, rng) + } + + /// Decrypts the `ciphertext` with the `secret_key`, returning the + /// [`Plaintext`](Self::Plaintext). + fn decrypt( + ciphertext: &Self::Ciphertext, + secret_key: Self::SecretKey, + ) -> Result<Self::Plaintext, Self::Error>; + + /// Generates a secret key and then decrypts the `ciphertext` with the generated secret key, + /// returing the [`Plaintext`](Self::Plaintext). + /// + /// This enables an optimization path whenever encryption is not necessary. + #[inline] + fn generate_secret_key_and_decrypt<R>( + ciphertext: &Self::Ciphertext, + rng: &mut R, + ) -> Result<Self::Plaintext, Self::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + Self::decrypt(ciphertext, Self::generate_secret_key(rng).secret_key) + } +} + /// [`IntegratedEncryptionScheme`] Public Key pub struct PublicKey<I> where @@ -39,17 +143,24 @@ impl<I> PublicKey<I> where I: IntegratedEncryptionScheme + ?Sized, { - /// Generates a new [`PublicKey`] from `I::PublicKey`. - /// - /// # API Note - /// - /// This function is intentionally private so that only [`KeyPair`] can create this type. + /// Builds a new [`PublicKey`] from `I::PublicKey`. #[inline] - fn new(public_key: I::PublicKey) -> Self { + pub fn new(public_key: I::PublicKey) -> Self { Self { public_key } } - /// Encrypts the `plaintext` with `self`, generating an [`EncryptedMessage`]. + /// Generates a public key. + /// + /// This enables an optimization path whenever decryption is not necessary. + #[inline] + pub fn generate<R>(rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + I::generate_public_key(rng) + } + + /// Encrypts the `plaintext` with `self`, returning an [`EncryptedMessage`]. #[inline] pub fn encrypt<R>( self, @@ -61,6 +172,21 @@ where { I::encrypt(plaintext, self.public_key, rng) } + + /// Generates a public key and then encrypts the `plaintext` with the generated public key, + /// returning an [`EncryptedMessage`]. + /// + /// This enables an optimization path whenever decryption is not necessary. + #[inline] + pub fn generate_and_encrypt<R>( + plaintext: &I::Plaintext, + rng: &mut R, + ) -> Result<EncryptedMessage<I>, I::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + I::generate_public_key_and_encrypt(plaintext, rng) + } } /// [`IntegratedEncryptionScheme`] Secret Key @@ -76,21 +202,44 @@ impl<I> SecretKey<I> where I: IntegratedEncryptionScheme + ?Sized, { - /// Generates a new [`SecretKey`] from `I::SecretKey`. - /// - /// # API Note - /// - /// This function is intentionally private, so that only `KeyPair` can create this type. + /// Builds a new [`SecretKey`] from `I::SecretKey`. #[inline] - fn new(secret_key: I::SecretKey) -> Self { + pub fn new(secret_key: I::SecretKey) -> Self { Self { secret_key } } - /// Decrypts the `message` with `self`. + /// Generates a secret key. + /// + /// This enables an optimization path whenever encryption is not necessary. + #[inline] + pub fn generate<R>(rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + I::generate_secret_key(rng) + } + + /// Decrypts the `message` with `self` returning the + /// [`Plaintext`](IntegratedEncryptionScheme::Plaintext). #[inline] pub fn decrypt(self, message: &EncryptedMessage<I>) -> Result<I::Plaintext, I::Error> { message.decrypt(self) } + + /// Generates a secret key and then decrypts the `message` with the generated secret key, + /// returing the [`Plaintext`](IntegratedEncryptionScheme::Plaintext). + /// + /// This enables an optimization path whenever encryption is not necessary. + #[inline] + pub fn generate_and_decrypt<R>( + message: &EncryptedMessage<I>, + rng: &mut R, + ) -> Result<I::Plaintext, I::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + I::generate_secret_key(rng).decrypt(message) + } } /// [`IntegratedEncryptionScheme`] Key Pair @@ -124,22 +273,22 @@ where where R: CryptoRng + RngCore + ?Sized, { - I::keygen(rng) + I::generate_keys(rng) } /// Returns the public side of the key pair. #[inline] - pub fn into_public(self) -> PublicKey<I> { + fn into_public(self) -> PublicKey<I> { PublicKey::new(self.public_key) } /// Returns the secret side of the key pair. #[inline] - pub fn into_secret(self) -> SecretKey<I> { + fn into_secret(self) -> SecretKey<I> { SecretKey::new(self.secret_key) } - /// Encrypts the `plaintext` with `self, generating an [`EncryptedMessage`]. + /// Encrypts the `plaintext` with `self, returning an [`EncryptedMessage`]. #[inline] pub fn encrypt<R>( self, @@ -167,57 +316,6 @@ where } } -/// Integrated Encryption Scheme Trait -pub trait IntegratedEncryptionScheme { - /// Public Key Type - type PublicKey; - - /// Secret Key Type - type SecretKey; - - /// Plaintext Type - type Plaintext; - - /// Ciphertext Type - type Ciphertext; - - /// Encryption/Decryption Error Type - type Error; - - /// Generates a public/secret keypair. - fn keygen<R>(rng: &mut R) -> KeyPair<Self> - where - R: CryptoRng + RngCore + ?Sized; - - /// Encrypts the `plaintext` with the `public_key`, generating an [`EncryptedMessage`]. - fn encrypt<R>( - plaintext: &Self::Plaintext, - public_key: Self::PublicKey, - rng: &mut R, - ) -> Result<EncryptedMessage<Self>, Self::Error> - where - R: CryptoRng + RngCore + ?Sized; - - /// Generates a public/secret keypair and then encrypts the `plaintext` with the generated - /// public key, generating an [`EncryptedMessage`] and a [`SecretKey`]. - #[inline] - fn keygen_encrypt<R>( - plaintext: &Self::Plaintext, - rng: &mut R, - ) -> Result<(EncryptedMessage<Self>, SecretKey<Self>), Self::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - Self::keygen(rng).encrypt(plaintext, rng) - } - - /// Decrypts the `ciphertext` with the `secret_key`. - fn decrypt( - ciphertext: &Self::Ciphertext, - secret_key: Self::SecretKey, - ) -> Result<Self::Plaintext, Self::Error>; -} - /// Encrypted Message #[derive(derivative::Derivative)] #[derivative( @@ -248,7 +346,7 @@ where Self { ciphertext } } - /// Encrypts the `plaintext` with the `public_key`, generating an [`EncryptedMessage`]. + /// Encrypts the `plaintext` with the `public_key`, returning an [`EncryptedMessage`]. #[inline] pub fn encrypt<R>( plaintext: &I::Plaintext, @@ -262,23 +360,51 @@ where } /// Generates a public/secret keypair and then encrypts the `plaintext` with the generated - /// public key, generating an [`EncryptedMessage`] and a [`SecretKey`]. + /// public key, returning an [`EncryptedMessage`] and a [`SecretKey`]. #[inline] - pub fn keygen_encrypt<R>( + pub fn generate_keys_and_encrypt<R>( plaintext: &I::Plaintext, rng: &mut R, ) -> Result<(Self, SecretKey<I>), I::Error> where R: CryptoRng + RngCore + ?Sized, { - I::keygen_encrypt(plaintext, rng) + I::generate_keys_and_encrypt(plaintext, rng) + } + + /// Generates a public key and then encrypts the `plaintext` with the generated public key, + /// returning an [`EncryptedMessage`]. + /// + /// This enables an optimization path whenever decryption is not necessary. + #[inline] + pub fn generate_public_key_and_encrypt<R>( + plaintext: &I::Plaintext, + rng: &mut R, + ) -> Result<Self, I::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + I::generate_public_key_and_encrypt(plaintext, rng) } - /// Decrypts `self` with the `secret_key`. + /// Decrypts `self` with the `secret_key` returning the + /// [`Plaintext`](IntegratedEncryptionScheme::Plaintext). #[inline] pub fn decrypt(&self, secret_key: SecretKey<I>) -> Result<I::Plaintext, I::Error> { I::decrypt(&self.ciphertext, secret_key.secret_key) } + + /// Generates a secret key and then decrypts `self` with the generated secret key, + /// returing the [`Plaintext`](IntegratedEncryptionScheme::Plaintext). + /// + /// This enables an optimization path whenever encryption is not necessary. + #[inline] + pub fn generate_secret_key_and_decrypt<R>(&self, rng: &mut R) -> Result<I::Plaintext, I::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + I::generate_secret_key_and_decrypt(&self.ciphertext, rng) + } } /// Testing Framework @@ -296,7 +422,7 @@ pub mod test { I::Error: Debug, R: CryptoRng + RngCore + ?Sized, { - let (public_key, secret_key) = I::keygen(rng).into(); + let (public_key, secret_key) = I::generate_keys(rng).into(); let reconstructed_plaintext = secret_key .decrypt( &public_key diff --git a/manta-pay/src/crypto/ies.rs b/manta-pay/src/crypto/ies.rs index ec18cf15a..26ed9d427 100644 --- a/manta-pay/src/crypto/ies.rs +++ b/manta-pay/src/crypto/ies.rs @@ -16,6 +16,7 @@ //! IES Implementation +// FIXME: Don't use raw bytes as secret and public key. // FIXME: Make sure secret keys are protected. use aes_gcm::{ @@ -109,7 +110,7 @@ impl IntegratedEncryptionScheme for IES { type Error = aes_gcm::Error; #[inline] - fn keygen<R>(rng: &mut R) -> KeyPair<Self> + fn generate_keys<R>(rng: &mut R) -> KeyPair<Self> where R: CryptoRng + RngCore + ?Sized, { @@ -118,6 +119,23 @@ impl IntegratedEncryptionScheme for IES { KeyPair::new(pk.to_bytes(), sk.to_bytes()) } + #[inline] + fn generate_public_key<R>(rng: &mut R) -> ies::PublicKey<Self> + where + R: CryptoRng + RngCore + ?Sized, + { + // TODO: Is there an even more efficient way to do this? + ies::PublicKey::new(PubKey::from(&StaticSecret::new(rng)).to_bytes()) + } + + #[inline] + fn generate_secret_key<R>(rng: &mut R) -> ies::SecretKey<Self> + where + R: CryptoRng + RngCore + ?Sized, + { + ies::SecretKey::new(StaticSecret::new(rng).to_bytes()) + } + fn encrypt<R>( plaintext: &Self::Plaintext, public_key: Self::PublicKey, From b487c545f67fa4c227391d4cf0cfce310796c987 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 20 Sep 2021 18:41:33 -0400 Subject: [PATCH 046/275] WIP: add preliminary merkle tree abstraction --- manta-crypto/src/lib.rs | 1 + manta-crypto/src/merkle_tree.rs | 356 ++++++++++++++++++++++++++++++++ 2 files changed, 357 insertions(+) create mode 100644 manta-crypto/src/merkle_tree.rs diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index 81687116b..e438792de 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -26,6 +26,7 @@ mod prf; pub mod commitment; pub mod constraint; pub mod ies; +pub mod merkle_tree; pub mod set; pub use commitment::prelude::*; diff --git a/manta-crypto/src/merkle_tree.rs b/manta-crypto/src/merkle_tree.rs new file mode 100644 index 000000000..a50ad3578 --- /dev/null +++ b/manta-crypto/src/merkle_tree.rs @@ -0,0 +1,356 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Merkle Trees + +// TODO: The following optimizations/configurations must be possible: +// +// 1. Subtree Computation/Memoization +// 2. Root computation from only the past +// 3. Path computation from only the past +// 4. Incremental Update +// 5. Only store relevant history subset +// + +extern crate alloc; + +use alloc::vec::Vec; + +/// Merkle Tree Leaf Hash +pub trait LeafHash { + /// Leaf Type + type Leaf: ?Sized; + + /// Leaf Hash Parameters Type + type Parameters; + + /// Leaf Hash Output Type + type Output: Default; + + /// Computes the digest of the `leaf` using `parameters`. + fn digest(parameters: &Self::Parameters, leaf: &Self::Leaf) -> Self::Output; +} + +/// Merkle Tree Inner Hash +pub trait InnerHash { + /// Leaf Hash Type + type LeafHash: LeafHash; + + /// Inner Hash Parameters Type + type Parameters; + + /// Inner Hash Output Type + type Output: Default + PartialEq; + + /// Combines two inner digests into a new inner digest using `parameters`. + fn join(parameters: &Self::Parameters, lhs: &Self::Output, rhs: &Self::Output) -> Self::Output; + + /// Combines two [`LeafHash`](Self::LeafHash) digests into an inner digest. + fn join_leaves( + parameters: &Self::Parameters, + lhs: &<Self::LeafHash as LeafHash>::Output, + rhs: &<Self::LeafHash as LeafHash>::Output, + ) -> Self::Output; +} + +/// Merkle Tree Configuration +pub trait Configuration { + /// Leaf Hash Type + type LeafHash: LeafHash; + + /// Inner Hash Type + type InnerHash: InnerHash<LeafHash = Self::LeafHash>; + + /// Merkle Tree Structure Type + type Tree: Tree<Self>; +} + +/// Merkle Tree Structure +pub trait Tree<C>: Sized +where + C: Configuration + ?Sized, +{ + /// Height Type + type Height: Copy; + + /// Path Query Type + type PathQuery; + + /// Builds a new merkle tree with the given `height`. + fn new(height: Self::Height) -> Self; + + /// Builds a new merkle tree with the given `height` and pre-existing `leaves`. + fn with_leaves(height: Self::Height, leaves: &[Leaf<C>]) -> Option<Self> + where + Leaf<C>: Sized; + + /// Returns the [`Root`] of the merkle tree. + fn root(&self) -> Root<C>; + + /// Returns the [`Path`] to some element of the merkle tree given by the `path_query`. + fn path(&self, path_query: Self::PathQuery) -> Path<C>; +} + +/// Merkle Tree Append Mixin +pub trait Append<C> +where + C: Configuration + ?Sized, +{ + /// Inserts `leaf_digest` at the next avaiable leaf node of the tree. + fn append(&mut self, parameters: &Parameters<C>, leaf_digest: LeafDigest<C>); +} + +/// Merkle Tree Update Mixin +pub trait Update<C> +where + C: Configuration + ?Sized, +{ + /// Modifies the leaf node at the given `index` to `leaf_digest`. + fn update(&mut self, parameters: &Parameters<C>, index: usize, leaf_digest: LeafDigest<C>); +} + +/// Leaf Type +pub type Leaf<C> = <<C as Configuration>::LeafHash as LeafHash>::Leaf; + +/// Leaf Hash Parameters Type +pub type LeafHashParamters<C> = <<C as Configuration>::LeafHash as LeafHash>::Parameters; + +/// Leaf Hash Digest Type +pub type LeafDigest<C> = <<C as Configuration>::LeafHash as LeafHash>::Output; + +/// Inner Hash Parameters Type +pub type InnerHashParameters<C> = <<C as Configuration>::InnerHash as InnerHash>::Parameters; + +/// Inner Hash Digest Type +pub type InnerDigest<C> = <<C as Configuration>::InnerHash as InnerHash>::Output; + +/// Merkle Tree Root Type +pub type Root<C> = InnerDigest<C>; + +/// Left or Right Side of a Subtree +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum Parity { + /// Left Side of the Subtree + Left, + + /// Right Side of the Subtree + Right, +} + +impl Default for Parity { + #[inline] + fn default() -> Self { + Self::Left + } +} + +impl Parity { + /// Computes the [`Parity`] of the given `index`. + #[inline] + pub const fn from_index(index: usize) -> Self { + if index % 2 == 0 { + Self::Left + } else { + Self::Right + } + } + + /// Returns `true` if `self` represents the left side of a subtree. + #[inline] + pub const fn is_left(&self) -> bool { + matches!(self, Self::Left) + } + + /// Returns `true` if `self` represents the right side of a subtree. + #[inline] + pub const fn is_right(&self) -> bool { + matches!(self, Self::Right) + } +} + +/// Node Location +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +pub struct Node(usize); + +impl Node { + /// Builds a [`Node`] for this `index`. + #[inline] + pub const fn from_index(index: usize) -> Self { + Self(index) + } + + /// Returns the [`Parity`] of this node. + #[inline] + pub const fn parity(&self) -> Parity { + Parity::from_index(self.0) + } + + /// Returns the parent [`Node`] of this node. + #[inline] + pub const fn parent(&self) -> Self { + Self(self.0 >> 1) + } + + /// Converts `self` into its parent, returning the parent [`Node`]. + #[inline] + pub fn into_parent(&mut self) -> Self { + *self = self.parent(); + *self + } + + /// Combines two inner digests into a new inner digest using `parameters`, swapping the order + /// of `lhs` and `rhs` depending on the location of `self` in its subtree. + #[inline] + pub fn join<C>( + &self, + parameters: &InnerHashParameters<C>, + lhs: &InnerDigest<C>, + rhs: &InnerDigest<C>, + ) -> InnerDigest<C> + where + C: Configuration + ?Sized, + { + match self.parity() { + Parity::Left => C::InnerHash::join(parameters, lhs, rhs), + Parity::Right => C::InnerHash::join(parameters, rhs, lhs), + } + } + + /// Combines two leaf digests into a new inner digest using `parameters`, swapping the order + /// of `lhs` and `rhs` depending on the location of `self` in its subtree. + #[inline] + pub fn join_leaves<C>( + &self, + parameters: &InnerHashParameters<C>, + lhs: &LeafDigest<C>, + rhs: &LeafDigest<C>, + ) -> InnerDigest<C> + where + C: Configuration + ?Sized, + { + match self.parity() { + Parity::Left => C::InnerHash::join_leaves(parameters, lhs, rhs), + Parity::Right => C::InnerHash::join_leaves(parameters, rhs, lhs), + } + } +} + +/// Merkle Tree Parameters +pub struct Parameters<C> +where + C: Configuration + ?Sized, +{ + /// Leaf Hash Parameters + pub leaf: LeafHashParamters<C>, + + /// Inner Hash Parameters + pub inner: InnerHashParameters<C>, +} + +/// Merkle Tree Path +pub struct Path<C> +where + C: Configuration + ?Sized, +{ + /// Inner Path + inner_path: Vec<InnerDigest<C>>, + + /// Sibling Digest + sibling_digest: LeafDigest<C>, + + /// Leaf Node + leaf_node: Node, +} + +impl<C> Path<C> +where + C: Configuration + ?Sized, +{ + /// Returns `true` if `self` is a witness to the fact that `leaf` is stored in a merkle tree + /// with the given `root`. + #[inline] + pub fn is_valid(&self, parameters: &Parameters<C>, root: &Root<C>, leaf: &Leaf<C>) -> bool { + let mut node = self.leaf_node; + let first_inner_digest = node.join_leaves::<C>( + &parameters.inner, + &C::LeafHash::digest(&parameters.leaf, leaf), + &self.sibling_digest, + ); + root == &self + .inner_path + .iter() + .fold(first_inner_digest, move |acc, d| { + node.into_parent().join::<C>(&parameters.inner, &acc, d) + }) + } +} + +/// Merkle Tree +pub struct MerkleTree<C> +where + C: Configuration + ?Sized, +{ + /// Underlying Tree Structure + tree: C::Tree, +} + +impl<C> MerkleTree<C> +where + C: Configuration + ?Sized, +{ + /// Builds a new [`MerkleTree`] with the given `height`. + #[inline] + pub fn new(height: <C::Tree as Tree<C>>::Height) -> Self { + Self { + tree: C::Tree::new(height), + } + } + + /// Returns the [`Root`] of the merkle tree. + #[inline] + pub fn root(&self) -> Root<C> { + self.tree.root() + } + + /// Returns the [`Path`] to some element of the merkle tree given by the `path_query`. + #[inline] + pub fn path(&self, path_query: <C::Tree as Tree<C>>::PathQuery) -> Path<C> { + self.tree.path(path_query) + } + + /// Inserts `leaf` at the next avaiable leaf node of the tree. + #[inline] + pub fn append(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) + where + C::Tree: Append<C>, + { + self.tree + .append(parameters, C::LeafHash::digest(&parameters.leaf, leaf)) + } + + /// Modifies the leaf node at the given `index` to `leaf`. + #[inline] + pub fn update(&mut self, parameters: &Parameters<C>, index: usize, leaf: &Leaf<C>) + where + C::Tree: Update<C>, + { + self.tree.update( + parameters, + index, + C::LeafHash::digest(&parameters.leaf, leaf), + ) + } +} From 8a9e77ffdb4f09d49ec518cc249138b9b9b767cc Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 21 Sep 2021 04:12:58 -0400 Subject: [PATCH 047/275] WIP: merkle tree implementations --- manta-crypto/src/merkle_tree.rs | 502 +++++++++++++++++++++++++++----- 1 file changed, 422 insertions(+), 80 deletions(-) diff --git a/manta-crypto/src/merkle_tree.rs b/manta-crypto/src/merkle_tree.rs index a50ad3578..54286e841 100644 --- a/manta-crypto/src/merkle_tree.rs +++ b/manta-crypto/src/merkle_tree.rs @@ -28,6 +28,7 @@ extern crate alloc; use alloc::vec::Vec; +use core::{convert::Infallible, fmt::Debug, hash::Hash}; /// Merkle Tree Leaf Hash pub trait LeafHash { @@ -76,6 +77,48 @@ pub trait Configuration { /// Merkle Tree Structure Type type Tree: Tree<Self>; + + /// Height Type + type Height: Copy + Into<usize>; + + /// Fixed Height of the Merkle Tree + const HEIGHT: Self::Height; +} + +/// Leaf Type +pub type Leaf<C> = <<C as Configuration>::LeafHash as LeafHash>::Leaf; + +/// Leaf Hash Parameters Type +pub type LeafHashParamters<C> = <<C as Configuration>::LeafHash as LeafHash>::Parameters; + +/// Leaf Hash Digest Type +pub type LeafDigest<C> = <<C as Configuration>::LeafHash as LeafHash>::Output; + +/// Inner Hash Parameters Type +pub type InnerHashParameters<C> = <<C as Configuration>::InnerHash as InnerHash>::Parameters; + +/// Inner Hash Digest Type +pub type InnerDigest<C> = <<C as Configuration>::InnerHash as InnerHash>::Output; + +/// Merkle Tree Root Type +pub type Root<C> = InnerDigest<C>; + +/// +#[inline] +pub fn capacity<C>() -> usize +where + C: Configuration + ?Sized, +{ + 1usize << C::HEIGHT.into() +} + +/// +#[inline] +pub fn path_length<C>() -> usize +where + C: Configuration + ?Sized, +{ + C::HEIGHT.into() - 2 } /// Merkle Tree Structure @@ -83,62 +126,249 @@ pub trait Tree<C>: Sized where C: Configuration + ?Sized, { - /// Height Type - type Height: Copy; - /// Path Query Type - type PathQuery; + type Query; - /// Builds a new merkle tree with the given `height`. - fn new(height: Self::Height) -> Self; + /// Path Error Type + type Error; - /// Builds a new merkle tree with the given `height` and pre-existing `leaves`. - fn with_leaves(height: Self::Height, leaves: &[Leaf<C>]) -> Option<Self> + /// Builds a new merkle tree. + fn new(parameters: &Parameters<C>) -> Self; + + /// Builds a new merkle tree with the given `leaves`. + #[inline] + fn from_leaves<L>(parameters: &Parameters<C>, leaves: L) -> Option<Self> where - Leaf<C>: Sized; + L: IntoIterator<Item = LeafDigest<C>>, + { + let capacity = capacity::<C>(); + let leaves = leaves.into_iter(); + if leaves.size_hint().0 > capacity { + return None; + } + let mut tree = Self::new(parameters); + for leaf in leaves { + if !tree.append(parameters, leaf) { + return None; + } + } + Some(tree) + } + + /// Returns the length of `self`. + fn len(&self) -> usize; + + /// Returns `true` if the length of `self` is zero. + #[inline] + fn is_empty(&self) -> bool { + self.len() == 0 + } /// Returns the [`Root`] of the merkle tree. - fn root(&self) -> Root<C>; + fn root(&self, parameters: &Parameters<C>) -> Root<C>; + + /// Returns the [`Path`] to some element of the merkle tree given by the `query`. + fn path(&self, parameters: &Parameters<C>, query: Self::Query) -> Result<Path<C>, Self::Error>; - /// Returns the [`Path`] to some element of the merkle tree given by the `path_query`. - fn path(&self, path_query: Self::PathQuery) -> Path<C>; + /// Inserts `leaf_digest` at the next avaiable leaf node of the tree, returning `false` if the + /// leaf could not be inserted because the tree has exhausted its capacity. + fn append(&mut self, parameters: &Parameters<C>, leaf_digest: LeafDigest<C>) -> bool; } -/// Merkle Tree Append Mixin -pub trait Append<C> +/// Full Merkle Tree Backing Structure +pub struct FullTree<C> where C: Configuration + ?Sized, { - /// Inserts `leaf_digest` at the next avaiable leaf node of the tree. - fn append(&mut self, parameters: &Parameters<C>, leaf_digest: LeafDigest<C>); + /// Leaf Digests + leaf_digests: Vec<LeafDigest<C>>, + + /// Inner Digests + inner_digests: Vec<InnerDigest<C>>, } -/// Merkle Tree Update Mixin -pub trait Update<C> +impl<C> FullTree<C> where C: Configuration + ?Sized, { - /// Modifies the leaf node at the given `index` to `leaf_digest`. - fn update(&mut self, parameters: &Parameters<C>, index: usize, leaf_digest: LeafDigest<C>); + /// + #[inline] + fn sibling_leaf(&self, index: NodeIndex) -> Option<&LeafDigest<C>> { + self.leaf_digests.get(index.sibling().0) + } + + /// + #[inline] + fn get_inner_digest(&self, depth: usize, index: NodeIndex) -> Option<&InnerDigest<C>> { + // TODO: self.inner_digests.get((1 << (depth + 1)) + index.0 - 1) + todo!() + } + + /// + #[inline] + fn construct_inner_path(&self, mut index: NodeIndex) -> Vec<InnerDigest<C>> + where + InnerDigest<C>: Clone, + { + /* TODO: + (0..path_length::<C>()) + .into_iter() + .rev() + .map(|depth| { + self.get_inner_digest(depth, index.into_parent().sibling()) + .map(Clone::clone) + .unwrap_or_default() + }) + .collect() + */ + todo!() + } } -/// Leaf Type -pub type Leaf<C> = <<C as Configuration>::LeafHash as LeafHash>::Leaf; +impl<C> Tree<C> for FullTree<C> +where + C: Configuration + ?Sized, + LeafDigest<C>: Clone, + InnerDigest<C>: Clone, +{ + type Query = usize; -/// Leaf Hash Parameters Type -pub type LeafHashParamters<C> = <<C as Configuration>::LeafHash as LeafHash>::Parameters; + type Error = (); -/// Leaf Hash Digest Type -pub type LeafDigest<C> = <<C as Configuration>::LeafHash as LeafHash>::Output; + #[inline] + fn new(parameters: &Parameters<C>) -> Self { + let _ = parameters; + Self { + leaf_digests: Vec::default(), + inner_digests: Vec::default(), + } + } -/// Inner Hash Parameters Type -pub type InnerHashParameters<C> = <<C as Configuration>::InnerHash as InnerHash>::Parameters; + #[inline] + fn len(&self) -> usize { + self.leaf_digests.len() + } -/// Inner Hash Digest Type -pub type InnerDigest<C> = <<C as Configuration>::InnerHash as InnerHash>::Output; + #[inline] + fn root(&self, parameters: &Parameters<C>) -> Root<C> { + self.inner_digests + .get(0) + .map(Clone::clone) + .unwrap_or_default() + } -/// Merkle Tree Root Type -pub type Root<C> = InnerDigest<C>; + #[inline] + fn path(&self, parameters: &Parameters<C>, query: Self::Query) -> Result<Path<C>, Self::Error> { + let base_index = NodeIndex(query); + Ok(Path::new( + base_index, + self.sibling_leaf(base_index).ok_or(())?.clone(), + self.construct_inner_path(base_index), + )) + } + + #[inline] + fn append(&mut self, parameters: &Parameters<C>, leaf_digest: LeafDigest<C>) -> bool { + todo!() + } +} + +/// Latest Node Merkle Tree Backing Structure +pub struct LatestNodeTree<C> +where + C: Configuration + ?Sized, +{ + /// Leaf Digests + leaf_digest: Option<LeafDigest<C>>, + + /// Path + path: Path<C>, + + /// Root + root: Root<C>, +} + +impl<C> LatestNodeTree<C> +where + C: Configuration + ?Sized, +{ + /// + #[inline] + fn len(&self) -> usize { + if self.leaf_digest.is_none() { + 0 + } else { + Into::<usize>::into(self.path.leaf_node_index) + 1 + } + } + + /// + #[inline] + fn next_index(&self) -> Option<NodeIndex> { + let len = self.len(); + if len == 0 { + Some(NodeIndex(0)) + } else if len < capacity::<C>() { + Some(NodeIndex(len + 1)) + } else { + None + } + } +} + +impl<C> Tree<C> for LatestNodeTree<C> +where + C: Configuration + ?Sized, + Root<C>: Clone, + Path<C>: Clone, +{ + type Query = (); + + type Error = Infallible; + + #[inline] + fn new(parameters: &Parameters<C>) -> Self { + Self { + leaf_digest: Default::default(), + path: Default::default(), + root: Default::default(), + } + } + + #[inline] + fn len(&self) -> usize { + self.len() + } + + #[inline] + fn root(&self, parameters: &Parameters<C>) -> Root<C> { + let _ = parameters; + self.root.clone() + } + + #[inline] + fn path(&self, parameters: &Parameters<C>, query: Self::Query) -> Result<Path<C>, Self::Error> { + let _ = (parameters, query); + Ok(self.path.clone()) + } + + #[inline] + fn append(&mut self, parameters: &Parameters<C>, leaf_digest: LeafDigest<C>) -> bool { + let index = match self.next_index() { + Some(index) => index, + _ => return false, + }; + + if index.is_zero() { + self.root = self.path.root_relative_to(parameters, &leaf_digest); + self.leaf_digest = Some(leaf_digest); + } else { + todo!() + } + + true + } +} /// Left or Right Side of a Subtree #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -181,30 +411,45 @@ impl Parity { } } -/// Node Location +/// Node Index #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] -pub struct Node(usize); +pub struct NodeIndex(usize); -impl Node { - /// Builds a [`Node`] for this `index`. +impl NodeIndex { + /// Builds a [`NodeIndex`] for this `index`. #[inline] pub const fn from_index(index: usize) -> Self { Self(index) } + /// Returns `true` if `self` is the left-most index. + #[inline] + pub const fn is_zero(&self) -> bool { + self.0 == 0 + } + /// Returns the [`Parity`] of this node. #[inline] pub const fn parity(&self) -> Parity { Parity::from_index(self.0) } - /// Returns the parent [`Node`] of this node. + /// Returns the [`NodeIndex`] which is the sibling to `self`. + #[inline] + pub const fn sibling(&self) -> Self { + match self.parity() { + Parity::Left => Self(self.0 + 1), + Parity::Right => Self(self.0 - 1), + } + } + + /// Returns the parent [`NodeIndex`] of this node. #[inline] pub const fn parent(&self) -> Self { Self(self.0 >> 1) } - /// Converts `self` into its parent, returning the parent [`Node`]. + /// Converts `self` into its parent, returning the parent [`NodeIndex`]. #[inline] pub fn into_parent(&mut self) -> Self { *self = self.parent(); @@ -248,7 +493,24 @@ impl Node { } } +impl From<NodeIndex> for usize { + #[inline] + fn from(index: NodeIndex) -> Self { + index.0 + } +} + /// Merkle Tree Parameters +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "LeafHashParamters<C>: Clone, InnerHashParameters<C>: Clone"), + Copy(bound = "LeafHashParamters<C>: Copy, InnerHashParameters<C>: Copy"), + Debug(bound = "LeafHashParamters<C>: Debug, InnerHashParameters<C>: Debug"), + Default(bound = "LeafHashParamters<C>: Default, InnerHashParameters<C>: Default"), + Eq(bound = "LeafHashParamters<C>: Eq, InnerHashParameters<C>: Eq"), + Hash(bound = "LeafHashParamters<C>: Hash, InnerHashParameters<C>: Hash"), + PartialEq(bound = "LeafHashParamters<C>: PartialEq, InnerHashParameters<C>: PartialEq") +)] pub struct Parameters<C> where C: Configuration + ?Sized, @@ -260,42 +522,108 @@ where pub inner: InnerHashParameters<C>, } +impl<C> Parameters<C> +where + C: Configuration + ?Sized, +{ + /// Computes the leaf digest of `leaf` using `self`. + #[inline] + pub fn digest(&self, leaf: &Leaf<C>) -> LeafDigest<C> { + C::LeafHash::digest(&self.leaf, leaf) + } + + /// Combines two inner digests into a new inner digest using `self`. + #[inline] + pub fn join(&self, lhs: &InnerDigest<C>, rhs: &InnerDigest<C>) -> InnerDigest<C> { + C::InnerHash::join(&self.inner, lhs, rhs) + } + + /// Combines two leaf digests into a new inner digest using `self`. + #[inline] + pub fn join_leaves(&self, lhs: &LeafDigest<C>, rhs: &LeafDigest<C>) -> InnerDigest<C> { + C::InnerHash::join_leaves(&self.inner, lhs, rhs) + } +} + /// Merkle Tree Path +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), + Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), + Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), + Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), + PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") +)] pub struct Path<C> where C: Configuration + ?Sized, { - /// Inner Path - inner_path: Vec<InnerDigest<C>>, + /// Leaf Node Index + leaf_node_index: NodeIndex, /// Sibling Digest sibling_digest: LeafDigest<C>, - /// Leaf Node - leaf_node: Node, + /// Inner Path + inner_path: Vec<InnerDigest<C>>, } impl<C> Path<C> where C: Configuration + ?Sized, { - /// Returns `true` if `self` is a witness to the fact that `leaf` is stored in a merkle tree - /// with the given `root`. + /// Builds a new [`Path`] from `leaf_node_index`, `sibling_digest`, and `inner_path`. #[inline] - pub fn is_valid(&self, parameters: &Parameters<C>, root: &Root<C>, leaf: &Leaf<C>) -> bool { - let mut node = self.leaf_node; - let first_inner_digest = node.join_leaves::<C>( - &parameters.inner, - &C::LeafHash::digest(&parameters.leaf, leaf), - &self.sibling_digest, - ); - root == &self - .inner_path + pub fn new( + leaf_node_index: NodeIndex, + sibling_digest: LeafDigest<C>, + inner_path: Vec<InnerDigest<C>>, + ) -> Self { + Self { + leaf_node_index, + sibling_digest, + inner_path, + } + } + + /// Computes the root of the merkle tree relative to `leaf_digest` using `parameters`. + #[inline] + pub fn root_relative_to( + &self, + parameters: &Parameters<C>, + leaf_digest: &LeafDigest<C>, + ) -> Root<C> { + let mut node_index = self.leaf_node_index; + let first_inner_digest = + node_index.join_leaves::<C>(&parameters.inner, leaf_digest, &self.sibling_digest); + self.inner_path .iter() .fold(first_inner_digest, move |acc, d| { - node.into_parent().join::<C>(&parameters.inner, &acc, d) + node_index + .into_parent() + .join::<C>(&parameters.inner, &acc, d) }) } + + /// Returns `true` if `self` is a witness to the fact that `leaf` is stored in a merkle tree + /// with the given `root`. + #[inline] + pub fn is_valid(&self, parameters: &Parameters<C>, root: &Root<C>, leaf: &Leaf<C>) -> bool { + root == &self.root_relative_to(parameters, &parameters.digest(leaf)) + } +} + +impl<C> Default for Path<C> +where + C: Configuration + ?Sized, +{ + #[inline] + fn default() -> Self { + let path_length = path_length::<C>(); + let mut inner_path = Vec::with_capacity(path_length); + inner_path.resize_with(path_length, InnerDigest::<C>::default); + Self::new(Default::default(), Default::default(), inner_path) + } } /// Merkle Tree @@ -303,6 +631,9 @@ pub struct MerkleTree<C> where C: Configuration + ?Sized, { + /// Merkle Tree Parameters + parameters: Parameters<C>, + /// Underlying Tree Structure tree: C::Tree, } @@ -311,46 +642,57 @@ impl<C> MerkleTree<C> where C: Configuration + ?Sized, { - /// Builds a new [`MerkleTree`] with the given `height`. + /// Builds a new [`MerkleTree`]. #[inline] - pub fn new(height: <C::Tree as Tree<C>>::Height) -> Self { + pub fn new(parameters: Parameters<C>) -> Self { Self { - tree: C::Tree::new(height), + tree: C::Tree::new(&parameters), + parameters, } } - /// Returns the [`Root`] of the merkle tree. + /// Builds a new merkle tree with the given `leaves`. #[inline] - pub fn root(&self) -> Root<C> { - self.tree.root() + pub fn from_leaves<'l, L>(parameters: Parameters<C>, leaves: L) -> Option<Self> + where + Leaf<C>: 'l, + L: IntoIterator<Item = &'l Leaf<C>>, + { + Some(Self { + tree: C::Tree::from_leaves( + &parameters, + leaves.into_iter().map(|l| parameters.digest(l)), + )?, + parameters, + }) } - /// Returns the [`Path`] to some element of the merkle tree given by the `path_query`. + /// #[inline] - pub fn path(&self, path_query: <C::Tree as Tree<C>>::PathQuery) -> Path<C> { - self.tree.path(path_query) + pub fn parameters(&self) -> &Parameters<C> { + &self.parameters } - /// Inserts `leaf` at the next avaiable leaf node of the tree. + /// Returns the [`Root`] of the merkle tree. #[inline] - pub fn append(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) - where - C::Tree: Append<C>, - { - self.tree - .append(parameters, C::LeafHash::digest(&parameters.leaf, leaf)) + pub fn root(&self) -> Root<C> { + self.tree.root(&self.parameters) } - /// Modifies the leaf node at the given `index` to `leaf`. + /// Returns the [`Path`] to some element of the merkle tree given by the `query`. #[inline] - pub fn update(&mut self, parameters: &Parameters<C>, index: usize, leaf: &Leaf<C>) - where - C::Tree: Update<C>, - { - self.tree.update( - parameters, - index, - C::LeafHash::digest(&parameters.leaf, leaf), - ) + pub fn path( + &self, + query: <C::Tree as Tree<C>>::Query, + ) -> Result<Path<C>, <C::Tree as Tree<C>>::Error> { + self.tree.path(&self.parameters, query) + } + + /// Inserts `leaf` at the next avaiable leaf node of the tree, returning `false` if the + /// leaf could not be inserted because the tree has exhausted its capacity. + #[inline] + pub fn append(&mut self, leaf: &Leaf<C>) -> bool { + self.tree + .append(&self.parameters, self.parameters.digest(leaf)) } } From 1b00b67b6597db41ae46e148145701b711ff4b39 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 21 Sep 2021 12:53:45 -0400 Subject: [PATCH 048/275] WIP: add implementation for client merkle tree --- manta-crypto/src/merkle_tree.rs | 213 ++++++++++++++++++++++---------- 1 file changed, 146 insertions(+), 67 deletions(-) diff --git a/manta-crypto/src/merkle_tree.rs b/manta-crypto/src/merkle_tree.rs index 54286e841..1c5b19a72 100644 --- a/manta-crypto/src/merkle_tree.rs +++ b/manta-crypto/src/merkle_tree.rs @@ -27,8 +27,8 @@ extern crate alloc; -use alloc::vec::Vec; -use core::{convert::Infallible, fmt::Debug, hash::Hash}; +use alloc::{collections::BTreeMap, vec::Vec}; +use core::{borrow::Borrow, convert::Infallible, fmt::Debug, hash::Hash, iter::Rev, ops::Range}; /// Merkle Tree Leaf Hash pub trait LeafHash { @@ -103,16 +103,18 @@ pub type InnerDigest<C> = <<C as Configuration>::InnerHash as InnerHash>::Output /// Merkle Tree Root Type pub type Root<C> = InnerDigest<C>; -/// +/// Returns the capacity of the merkle tree with the given [`C::HEIGHT`](Configuration::HEIGHT) +/// parameter. #[inline] pub fn capacity<C>() -> usize where C: Configuration + ?Sized, { - 1usize << C::HEIGHT.into() + 1usize << (C::HEIGHT.into() - 1) } -/// +/// Returns the path length of the merkle tree with the given [`C::HEIGHT`](Configuration::HEIGHT) +/// parameter. #[inline] pub fn path_length<C>() -> usize where @@ -121,6 +123,16 @@ where C::HEIGHT.into() - 2 } +/// Returns an iterator over the depth of an inner path in reverse order, from the leaves of the +/// tree to the root. +#[inline] +pub fn path_iter<C>() -> Rev<Range<usize>> +where + C: Configuration + ?Sized, +{ + (0..path_length::<C>()).into_iter().rev() +} + /// Merkle Tree Structure pub trait Tree<C>: Sized where @@ -141,9 +153,8 @@ where where L: IntoIterator<Item = LeafDigest<C>>, { - let capacity = capacity::<C>(); let leaves = leaves.into_iter(); - if leaves.size_hint().0 > capacity { + if leaves.size_hint().0 > capacity::<C>() { return None; } let mut tree = Self::new(parameters); @@ -184,44 +195,86 @@ where leaf_digests: Vec<LeafDigest<C>>, /// Inner Digests - inner_digests: Vec<InnerDigest<C>>, + inner_digests: BTreeMap<usize, InnerDigest<C>>, } impl<C> FullTree<C> where C: Configuration + ?Sized, { - /// + /// Returns the sibling leaf node to `index`. #[inline] - fn sibling_leaf(&self, index: NodeIndex) -> Option<&LeafDigest<C>> { + fn sibling_leaf(&self, index: Node) -> Option<&LeafDigest<C>> { self.leaf_digests.get(index.sibling().0) } + /// Computes the index into the `inner_digests` map for a node of the given `depth` and `index`. + #[inline] + fn inner_digest_index(depth: usize, index: Node) -> usize { + (1 << (depth + 1)) + index.0 - 1 + } + + /// Returns the inner digest at the given `depth` and `index` of the merkle tree. /// + /// # Note + /// + /// The `depth` of the tree tracks the inner nodes not including the root. The root of the tree + /// is at `depth := -1` and `index := 0`. #[inline] - fn get_inner_digest(&self, depth: usize, index: NodeIndex) -> Option<&InnerDigest<C>> { - // TODO: self.inner_digests.get((1 << (depth + 1)) + index.0 - 1) - todo!() + fn get_inner_digest(&self, depth: usize, index: Node) -> Option<&InnerDigest<C>> { + self.inner_digests + .get(&Self::inner_digest_index(depth, index)) } + /// Sets the inner digest at the given `depth` and `index` of the merkle tree to the + /// `inner_digest` value. + /// + /// # Note /// + /// The `depth` of the tree tracks the inner nodes not including the root. The root of the tree + /// is at `depth := -1` and `index := 0`. #[inline] - fn construct_inner_path(&self, mut index: NodeIndex) -> Vec<InnerDigest<C>> + fn set_inner_digest(&mut self, depth: usize, index: Node, inner_digest: InnerDigest<C>) { + self.inner_digests + .insert(Self::inner_digest_index(depth, index), inner_digest); + } + + /// Computes the inner path of a node starting at the leaf given by `index`. + #[inline] + fn compute_inner_path(&self, mut index: Node) -> Vec<InnerDigest<C>> where InnerDigest<C>: Clone, { - /* TODO: - (0..path_length::<C>()) - .into_iter() - .rev() - .map(|depth| { + path_iter::<C>() + .map(move |depth| { self.get_inner_digest(depth, index.into_parent().sibling()) .map(Clone::clone) .unwrap_or_default() }) .collect() - */ - todo!() + } + + /// Computes the root of the merkle tree, modifying the inner tree in-place, starting at the + /// leaf given by `index`. + #[inline] + fn compute_root( + &mut self, + parameters: &Parameters<C>, + mut index: Node, + base: InnerDigest<C>, + ) -> Root<C> + where + InnerDigest<C>: Clone, + { + path_iter::<C>().fold(base, |acc, depth| { + self.set_inner_digest(depth, index.into_parent(), acc.clone()); + index.join( + parameters, + &acc, + self.get_inner_digest(depth, index.sibling()) + .unwrap_or(&Default::default()), + ) + }) } } @@ -239,8 +292,8 @@ where fn new(parameters: &Parameters<C>) -> Self { let _ = parameters; Self { - leaf_digests: Vec::default(), - inner_digests: Vec::default(), + leaf_digests: Default::default(), + inner_digests: Default::default(), } } @@ -251,25 +304,48 @@ where #[inline] fn root(&self, parameters: &Parameters<C>) -> Root<C> { + let _ = parameters; self.inner_digests - .get(0) + .get(&0) .map(Clone::clone) .unwrap_or_default() } #[inline] fn path(&self, parameters: &Parameters<C>, query: Self::Query) -> Result<Path<C>, Self::Error> { - let base_index = NodeIndex(query); + let _ = parameters; + if query > capacity::<C>() { + return Err(()); + } + let leaf_index = Node(query); Ok(Path::new( - base_index, - self.sibling_leaf(base_index).ok_or(())?.clone(), - self.construct_inner_path(base_index), + leaf_index, + self.sibling_leaf(leaf_index) + .map(Clone::clone) + .unwrap_or_default(), + self.compute_inner_path(leaf_index), )) } #[inline] fn append(&mut self, parameters: &Parameters<C>, leaf_digest: LeafDigest<C>) -> bool { - todo!() + let len = self.len(); + if len >= capacity::<C>() { + return false; + } + let leaf_index = Node(len); + let root = self.compute_root( + parameters, + leaf_index, + leaf_index.join_leaves( + parameters, + &leaf_digest, + self.sibling_leaf(leaf_index).unwrap_or(&Default::default()), + ), + ); + self.inner_digests.insert(0, root); + self.leaf_digests.push(leaf_digest); + true } } @@ -278,7 +354,7 @@ pub struct LatestNodeTree<C> where C: Configuration + ?Sized, { - /// Leaf Digests + /// Leaf Digest leaf_digest: Option<LeafDigest<C>>, /// Path @@ -292,24 +368,24 @@ impl<C> LatestNodeTree<C> where C: Configuration + ?Sized, { - /// + /// Returns the number of leaves in the merkle tree. #[inline] fn len(&self) -> usize { if self.leaf_digest.is_none() { 0 } else { - Into::<usize>::into(self.path.leaf_node_index) + 1 + self.path.leaf_node_index.0 + 1 } } - /// + /// Returns the next avaiable index or `None` if the merkle tree is full. #[inline] - fn next_index(&self) -> Option<NodeIndex> { + fn next_index(&self) -> Option<Node> { let len = self.len(); if len == 0 { - Some(NodeIndex(0)) - } else if len < capacity::<C>() { - Some(NodeIndex(len + 1)) + Some(Node(0)) + } else if len < capacity::<C>() - 1 { + Some(Node(len + 1)) } else { None } @@ -328,6 +404,7 @@ where #[inline] fn new(parameters: &Parameters<C>) -> Self { + let _ = parameters; Self { leaf_digest: Default::default(), path: Default::default(), @@ -413,15 +490,9 @@ impl Parity { /// Node Index #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] -pub struct NodeIndex(usize); - -impl NodeIndex { - /// Builds a [`NodeIndex`] for this `index`. - #[inline] - pub const fn from_index(index: usize) -> Self { - Self(index) - } +pub struct Node(pub usize); +impl Node { /// Returns `true` if `self` is the left-most index. #[inline] pub const fn is_zero(&self) -> bool { @@ -434,7 +505,7 @@ impl NodeIndex { Parity::from_index(self.0) } - /// Returns the [`NodeIndex`] which is the sibling to `self`. + /// Returns the [`Node`] which is the sibling to `self`. #[inline] pub const fn sibling(&self) -> Self { match self.parity() { @@ -443,13 +514,13 @@ impl NodeIndex { } } - /// Returns the parent [`NodeIndex`] of this node. + /// Returns the parent [`Node`] of this node. #[inline] pub const fn parent(&self) -> Self { Self(self.0 >> 1) } - /// Converts `self` into its parent, returning the parent [`NodeIndex`]. + /// Converts `self` into its parent, returning the parent [`Node`]. #[inline] pub fn into_parent(&mut self) -> Self { *self = self.parent(); @@ -461,7 +532,7 @@ impl NodeIndex { #[inline] pub fn join<C>( &self, - parameters: &InnerHashParameters<C>, + parameters: &Parameters<C>, lhs: &InnerDigest<C>, rhs: &InnerDigest<C>, ) -> InnerDigest<C> @@ -469,8 +540,8 @@ impl NodeIndex { C: Configuration + ?Sized, { match self.parity() { - Parity::Left => C::InnerHash::join(parameters, lhs, rhs), - Parity::Right => C::InnerHash::join(parameters, rhs, lhs), + Parity::Left => C::InnerHash::join(&parameters.inner, lhs, rhs), + Parity::Right => C::InnerHash::join(&parameters.inner, rhs, lhs), } } @@ -479,7 +550,7 @@ impl NodeIndex { #[inline] pub fn join_leaves<C>( &self, - parameters: &InnerHashParameters<C>, + parameters: &Parameters<C>, lhs: &LeafDigest<C>, rhs: &LeafDigest<C>, ) -> InnerDigest<C> @@ -487,19 +558,30 @@ impl NodeIndex { C: Configuration + ?Sized, { match self.parity() { - Parity::Left => C::InnerHash::join_leaves(parameters, lhs, rhs), - Parity::Right => C::InnerHash::join_leaves(parameters, rhs, lhs), + Parity::Left => { + C::InnerHash::join_leaves(&parameters.inner, lhs.borrow(), rhs.borrow()) + } + Parity::Right => { + C::InnerHash::join_leaves(&parameters.inner, rhs.borrow(), lhs.borrow()) + } } } } -impl From<NodeIndex> for usize { +impl From<Node> for usize { #[inline] - fn from(index: NodeIndex) -> Self { + fn from(index: Node) -> Self { index.0 } } +impl From<usize> for Node { + #[inline] + fn from(index: usize) -> Self { + Self(index) + } +} + /// Merkle Tree Parameters #[derive(derivative::Derivative)] #[derivative( @@ -559,12 +641,14 @@ where C: Configuration + ?Sized, { /// Leaf Node Index - leaf_node_index: NodeIndex, + leaf_node_index: Node, /// Sibling Digest sibling_digest: LeafDigest<C>, /// Inner Path + /// + /// The inner path is stored in the direction leaf-to-root. inner_path: Vec<InnerDigest<C>>, } @@ -575,7 +659,7 @@ where /// Builds a new [`Path`] from `leaf_node_index`, `sibling_digest`, and `inner_path`. #[inline] pub fn new( - leaf_node_index: NodeIndex, + leaf_node_index: Node, sibling_digest: LeafDigest<C>, inner_path: Vec<InnerDigest<C>>, ) -> Self { @@ -594,15 +678,10 @@ where leaf_digest: &LeafDigest<C>, ) -> Root<C> { let mut node_index = self.leaf_node_index; - let first_inner_digest = - node_index.join_leaves::<C>(&parameters.inner, leaf_digest, &self.sibling_digest); - self.inner_path - .iter() - .fold(first_inner_digest, move |acc, d| { - node_index - .into_parent() - .join::<C>(&parameters.inner, &acc, d) - }) + self.inner_path.iter().fold( + node_index.join_leaves(parameters, leaf_digest, &self.sibling_digest), + move |acc, d| node_index.into_parent().join(parameters, &acc, d), + ) } /// Returns `true` if `self` is a witness to the fact that `leaf` is stored in a merkle tree From e610ad13e8eb05232d664f9d0e98077394c65a3c Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 21 Sep 2021 17:47:21 -0400 Subject: [PATCH 049/275] WIP: update merkle tree implementations --- manta-accounting/Cargo.toml | 2 +- manta-crypto/src/merkle_tree.rs | 951 +++++++++++++++++++++----------- manta-util/src/lib.rs | 2 + manta-util/src/num.rs | 53 ++ 4 files changed, 682 insertions(+), 326 deletions(-) create mode 100644 manta-util/src/num.rs diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 17ea97f4f..a5c154ad5 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -44,7 +44,7 @@ derive_more = { version = "0.99.16", default-features = false, features = ["add" manta-crypto = { path = "../manta-crypto", default-features = false } manta-util = { path = "../manta-util", default-features = false } rand = { version = "0.8.4", default-features = false } -zeroize = { version = "1.4.1", optional = true, default-features = false, features = ["alloc", "zeroize_derive"] } +zeroize = { version = "1.4.2", optional = true, default-features = false, features = ["alloc", "zeroize_derive"] } [dev-dependencies] rand = "0.8.4" diff --git a/manta-crypto/src/merkle_tree.rs b/manta-crypto/src/merkle_tree.rs index 1c5b19a72..e4668a705 100644 --- a/manta-crypto/src/merkle_tree.rs +++ b/manta-crypto/src/merkle_tree.rs @@ -16,19 +16,16 @@ //! Merkle Trees -// TODO: The following optimizations/configurations must be possible: -// -// 1. Subtree Computation/Memoization -// 2. Root computation from only the past -// 3. Path computation from only the past -// 4. Incremental Update -// 5. Only store relevant history subset -// - extern crate alloc; -use alloc::{collections::BTreeMap, vec::Vec}; -use core::{borrow::Borrow, convert::Infallible, fmt::Debug, hash::Hash, iter::Rev, ops::Range}; +use alloc::vec::Vec; +use core::{ + borrow::{Borrow, BorrowMut}, + fmt::Debug, + hash::Hash, + iter::Rev, + ops::{Add, Range, Sub}, +}; /// Merkle Tree Leaf Hash pub trait LeafHash { @@ -75,9 +72,6 @@ pub trait Configuration { /// Inner Hash Type type InnerHash: InnerHash<LeafHash = Self::LeafHash>; - /// Merkle Tree Structure Type - type Tree: Tree<Self>; - /// Height Type type Height: Copy + Into<usize>; @@ -147,11 +141,13 @@ where /// Builds a new merkle tree. fn new(parameters: &Parameters<C>) -> Self; - /// Builds a new merkle tree with the given `leaves`. + /// Builds a new merkle tree with the given `leaves` returning `None` if the iterator + /// would overflow the capacity of the tree. #[inline] - fn from_leaves<L>(parameters: &Parameters<C>, leaves: L) -> Option<Self> + fn from_leaves<'l, L>(parameters: &Parameters<C>, leaves: L) -> Option<Self> where - L: IntoIterator<Item = LeafDigest<C>>, + Leaf<C>: 'l, + L: IntoIterator<Item = &'l Leaf<C>>, { let leaves = leaves.into_iter(); if leaves.size_hint().0 > capacity::<C>() { @@ -181,270 +177,9 @@ where /// Returns the [`Path`] to some element of the merkle tree given by the `query`. fn path(&self, parameters: &Parameters<C>, query: Self::Query) -> Result<Path<C>, Self::Error>; - /// Inserts `leaf_digest` at the next avaiable leaf node of the tree, returning `false` if the - /// leaf could not be inserted because the tree has exhausted its capacity. - fn append(&mut self, parameters: &Parameters<C>, leaf_digest: LeafDigest<C>) -> bool; -} - -/// Full Merkle Tree Backing Structure -pub struct FullTree<C> -where - C: Configuration + ?Sized, -{ - /// Leaf Digests - leaf_digests: Vec<LeafDigest<C>>, - - /// Inner Digests - inner_digests: BTreeMap<usize, InnerDigest<C>>, -} - -impl<C> FullTree<C> -where - C: Configuration + ?Sized, -{ - /// Returns the sibling leaf node to `index`. - #[inline] - fn sibling_leaf(&self, index: Node) -> Option<&LeafDigest<C>> { - self.leaf_digests.get(index.sibling().0) - } - - /// Computes the index into the `inner_digests` map for a node of the given `depth` and `index`. - #[inline] - fn inner_digest_index(depth: usize, index: Node) -> usize { - (1 << (depth + 1)) + index.0 - 1 - } - - /// Returns the inner digest at the given `depth` and `index` of the merkle tree. - /// - /// # Note - /// - /// The `depth` of the tree tracks the inner nodes not including the root. The root of the tree - /// is at `depth := -1` and `index := 0`. - #[inline] - fn get_inner_digest(&self, depth: usize, index: Node) -> Option<&InnerDigest<C>> { - self.inner_digests - .get(&Self::inner_digest_index(depth, index)) - } - - /// Sets the inner digest at the given `depth` and `index` of the merkle tree to the - /// `inner_digest` value. - /// - /// # Note - /// - /// The `depth` of the tree tracks the inner nodes not including the root. The root of the tree - /// is at `depth := -1` and `index := 0`. - #[inline] - fn set_inner_digest(&mut self, depth: usize, index: Node, inner_digest: InnerDigest<C>) { - self.inner_digests - .insert(Self::inner_digest_index(depth, index), inner_digest); - } - - /// Computes the inner path of a node starting at the leaf given by `index`. - #[inline] - fn compute_inner_path(&self, mut index: Node) -> Vec<InnerDigest<C>> - where - InnerDigest<C>: Clone, - { - path_iter::<C>() - .map(move |depth| { - self.get_inner_digest(depth, index.into_parent().sibling()) - .map(Clone::clone) - .unwrap_or_default() - }) - .collect() - } - - /// Computes the root of the merkle tree, modifying the inner tree in-place, starting at the - /// leaf given by `index`. - #[inline] - fn compute_root( - &mut self, - parameters: &Parameters<C>, - mut index: Node, - base: InnerDigest<C>, - ) -> Root<C> - where - InnerDigest<C>: Clone, - { - path_iter::<C>().fold(base, |acc, depth| { - self.set_inner_digest(depth, index.into_parent(), acc.clone()); - index.join( - parameters, - &acc, - self.get_inner_digest(depth, index.sibling()) - .unwrap_or(&Default::default()), - ) - }) - } -} - -impl<C> Tree<C> for FullTree<C> -where - C: Configuration + ?Sized, - LeafDigest<C>: Clone, - InnerDigest<C>: Clone, -{ - type Query = usize; - - type Error = (); - - #[inline] - fn new(parameters: &Parameters<C>) -> Self { - let _ = parameters; - Self { - leaf_digests: Default::default(), - inner_digests: Default::default(), - } - } - - #[inline] - fn len(&self) -> usize { - self.leaf_digests.len() - } - - #[inline] - fn root(&self, parameters: &Parameters<C>) -> Root<C> { - let _ = parameters; - self.inner_digests - .get(&0) - .map(Clone::clone) - .unwrap_or_default() - } - - #[inline] - fn path(&self, parameters: &Parameters<C>, query: Self::Query) -> Result<Path<C>, Self::Error> { - let _ = parameters; - if query > capacity::<C>() { - return Err(()); - } - let leaf_index = Node(query); - Ok(Path::new( - leaf_index, - self.sibling_leaf(leaf_index) - .map(Clone::clone) - .unwrap_or_default(), - self.compute_inner_path(leaf_index), - )) - } - - #[inline] - fn append(&mut self, parameters: &Parameters<C>, leaf_digest: LeafDigest<C>) -> bool { - let len = self.len(); - if len >= capacity::<C>() { - return false; - } - let leaf_index = Node(len); - let root = self.compute_root( - parameters, - leaf_index, - leaf_index.join_leaves( - parameters, - &leaf_digest, - self.sibling_leaf(leaf_index).unwrap_or(&Default::default()), - ), - ); - self.inner_digests.insert(0, root); - self.leaf_digests.push(leaf_digest); - true - } -} - -/// Latest Node Merkle Tree Backing Structure -pub struct LatestNodeTree<C> -where - C: Configuration + ?Sized, -{ - /// Leaf Digest - leaf_digest: Option<LeafDigest<C>>, - - /// Path - path: Path<C>, - - /// Root - root: Root<C>, -} - -impl<C> LatestNodeTree<C> -where - C: Configuration + ?Sized, -{ - /// Returns the number of leaves in the merkle tree. - #[inline] - fn len(&self) -> usize { - if self.leaf_digest.is_none() { - 0 - } else { - self.path.leaf_node_index.0 + 1 - } - } - - /// Returns the next avaiable index or `None` if the merkle tree is full. - #[inline] - fn next_index(&self) -> Option<Node> { - let len = self.len(); - if len == 0 { - Some(Node(0)) - } else if len < capacity::<C>() - 1 { - Some(Node(len + 1)) - } else { - None - } - } -} - -impl<C> Tree<C> for LatestNodeTree<C> -where - C: Configuration + ?Sized, - Root<C>: Clone, - Path<C>: Clone, -{ - type Query = (); - - type Error = Infallible; - - #[inline] - fn new(parameters: &Parameters<C>) -> Self { - let _ = parameters; - Self { - leaf_digest: Default::default(), - path: Default::default(), - root: Default::default(), - } - } - - #[inline] - fn len(&self) -> usize { - self.len() - } - - #[inline] - fn root(&self, parameters: &Parameters<C>) -> Root<C> { - let _ = parameters; - self.root.clone() - } - - #[inline] - fn path(&self, parameters: &Parameters<C>, query: Self::Query) -> Result<Path<C>, Self::Error> { - let _ = (parameters, query); - Ok(self.path.clone()) - } - - #[inline] - fn append(&mut self, parameters: &Parameters<C>, leaf_digest: LeafDigest<C>) -> bool { - let index = match self.next_index() { - Some(index) => index, - _ => return false, - }; - - if index.is_zero() { - self.root = self.path.root_relative_to(parameters, &leaf_digest); - self.leaf_digest = Some(leaf_digest); - } else { - todo!() - } - - true - } + /// Inserts the digest of `leaf` at the next avaiable leaf node of the tree, returning + /// `false` if the leaf could not be inserted because the tree has exhausted its capacity. + fn append(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> bool; } /// Left or Right Side of a Subtree @@ -467,7 +202,7 @@ impl Default for Parity { impl Parity { /// Computes the [`Parity`] of the given `index`. #[inline] - pub const fn from_index(index: usize) -> Self { + pub fn from_index(index: usize) -> Self { if index % 2 == 0 { Self::Left } else { @@ -486,37 +221,115 @@ impl Parity { pub const fn is_right(&self) -> bool { matches!(self, Self::Right) } + + /// Maps `self` to the output of `lhs` and `rhs` depending on its parity. + #[inline] + pub fn map<T, L, R>(self, lhs: L, rhs: R) -> T + where + L: FnOnce() -> T, + R: FnOnce() -> T, + { + match self { + Self::Left => lhs(), + Self::Right => rhs(), + } + } + + /// Combines two inner digests into a new inner digest using `parameters`, swapping the order + /// of `lhs` and `rhs` depending on the parity of `self` in its subtree. + #[inline] + pub fn join<C>( + &self, + parameters: &Parameters<C>, + lhs: &InnerDigest<C>, + rhs: &InnerDigest<C>, + ) -> InnerDigest<C> + where + C: Configuration + ?Sized, + { + match self { + Self::Left => C::InnerHash::join(&parameters.inner, lhs, rhs), + Self::Right => C::InnerHash::join(&parameters.inner, rhs, lhs), + } + } + + /// Combines two leaf digests into a new inner digest using `parameters`, choosing the left + /// pair `(center, rhs)` if `self` has left parity or choosing the right pair `(lhs, center)` + /// if `self` has right parity. + #[inline] + pub fn join_opposite_pair<C>( + &self, + parameters: &Parameters<C>, + lhs: &InnerDigest<C>, + center: &InnerDigest<C>, + rhs: &InnerDigest<C>, + ) -> InnerDigest<C> + where + C: Configuration + ?Sized, + { + match self { + Self::Left => C::InnerHash::join(&parameters.inner, center, rhs), + Self::Right => C::InnerHash::join(&parameters.inner, lhs, center), + } + } + + /// Combines two leaf digests into a new inner digest using `parameters`, swapping the order + /// of `lhs` and `rhs` depending on the parity of `self` in its subtree. + #[inline] + pub fn join_leaves<C>( + &self, + parameters: &Parameters<C>, + lhs: &LeafDigest<C>, + rhs: &LeafDigest<C>, + ) -> InnerDigest<C> + where + C: Configuration + ?Sized, + { + match self { + Self::Left => C::InnerHash::join_leaves(&parameters.inner, lhs, rhs), + Self::Right => C::InnerHash::join_leaves(&parameters.inner, rhs, lhs), + } + } } /// Node Index #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] -pub struct Node(pub usize); +pub struct Node<Idx = usize>( + /// Level-wise Index to a node in a Binary Tree + pub Idx, +); impl Node { - /// Returns `true` if `self` is the left-most index. + /// Returns the [`Parity`] of this node. #[inline] - pub const fn is_zero(&self) -> bool { - self.0 == 0 + pub fn parity(&self) -> Parity { + Parity::from_index(self.0) } - /// Returns the [`Parity`] of this node. + /// Returns `true` if this node has left parity. #[inline] - pub const fn parity(&self) -> Parity { - Parity::from_index(self.0) + pub fn is_left(&self) -> bool { + self.parity().is_left() + } + + /// Returns `true` if this node has right parity. + #[inline] + pub fn is_right(&self) -> bool { + self.parity().is_right() } /// Returns the [`Node`] which is the sibling to `self`. #[inline] - pub const fn sibling(&self) -> Self { + pub fn sibling(&self) -> Self { match self.parity() { - Parity::Left => Self(self.0 + 1), - Parity::Right => Self(self.0 - 1), + Parity::Left => *self + 1, + Parity::Right => *self - 1, } } /// Returns the parent [`Node`] of this node. #[inline] - pub const fn parent(&self) -> Self { + pub fn parent(&self) -> Self { Self(self.0 >> 1) } @@ -539,10 +352,25 @@ impl Node { where C: Configuration + ?Sized, { - match self.parity() { - Parity::Left => C::InnerHash::join(&parameters.inner, lhs, rhs), - Parity::Right => C::InnerHash::join(&parameters.inner, rhs, lhs), - } + self.parity().join(parameters, lhs, rhs) + } + + /// Combines two leaf digests into a new inner digest using `parameters`, choosing the left + /// pair `(center, rhs)` if `self` has left parity or choosing the right pair `(lhs, center)` + /// if `self` has right parity. + #[inline] + pub fn join_opposite_pair<C>( + &self, + parameters: &Parameters<C>, + lhs: &InnerDigest<C>, + center: &InnerDigest<C>, + rhs: &InnerDigest<C>, + ) -> InnerDigest<C> + where + C: Configuration + ?Sized, + { + self.parity() + .join_opposite_pair(parameters, lhs, center, rhs) } /// Combines two leaf digests into a new inner digest using `parameters`, swapping the order @@ -557,31 +385,75 @@ impl Node { where C: Configuration + ?Sized, { - match self.parity() { - Parity::Left => { - C::InnerHash::join_leaves(&parameters.inner, lhs.borrow(), rhs.borrow()) - } - Parity::Right => { - C::InnerHash::join_leaves(&parameters.inner, rhs.borrow(), lhs.borrow()) - } - } + self.parity().join_leaves(parameters, lhs, rhs) } } -impl From<Node> for usize { +impl<Idx> Add<Idx> for Node<Idx> +where + Idx: Add<Output = Idx>, +{ + type Output = Self; + + #[inline] + fn add(self, rhs: Idx) -> Self::Output { + Self(self.0 + rhs) + } +} + +impl<'i, Idx> Add<&'i Idx> for &'i Node<Idx> +where + &'i Idx: Add<Output = Idx>, +{ + type Output = Node<Idx>; + + #[inline] + fn add(self, rhs: &'i Idx) -> Self::Output { + Node(&self.0 + rhs) + } +} + +impl<Idx> Sub<Idx> for Node<Idx> +where + Idx: Sub<Output = Idx>, +{ + type Output = Self; + + #[inline] + fn sub(self, rhs: Idx) -> Self::Output { + Self(self.0 - rhs) + } +} + +impl<'i, Idx> Sub<&'i Idx> for &'i Node<Idx> +where + &'i Idx: Sub<Output = Idx>, +{ + type Output = Node<Idx>; + #[inline] - fn from(index: Node) -> Self { - index.0 + fn sub(self, rhs: &'i Idx) -> Self::Output { + Node(&self.0 - rhs) } } -impl From<usize> for Node { +impl<Idx> From<Idx> for Node<Idx> { #[inline] - fn from(index: usize) -> Self { + fn from(index: Idx) -> Self { Self(index) } } +impl<Idx> PartialEq<Idx> for Node<Idx> +where + Idx: PartialEq, +{ + #[inline] + fn eq(&self, rhs: &Idx) -> bool { + self.0 == *rhs + } +} + /// Merkle Tree Parameters #[derive(derivative::Derivative)] #[derivative( @@ -706,26 +578,28 @@ where } /// Merkle Tree -pub struct MerkleTree<C> +pub struct MerkleTree<C, T> where C: Configuration + ?Sized, + T: Tree<C>, { /// Merkle Tree Parameters parameters: Parameters<C>, /// Underlying Tree Structure - tree: C::Tree, + tree: T, } -impl<C> MerkleTree<C> +impl<C, T> MerkleTree<C, T> where C: Configuration + ?Sized, + T: Tree<C>, { /// Builds a new [`MerkleTree`]. #[inline] pub fn new(parameters: Parameters<C>) -> Self { Self { - tree: C::Tree::new(&parameters), + tree: T::new(&parameters), parameters, } } @@ -738,15 +612,12 @@ where L: IntoIterator<Item = &'l Leaf<C>>, { Some(Self { - tree: C::Tree::from_leaves( - &parameters, - leaves.into_iter().map(|l| parameters.digest(l)), - )?, + tree: T::from_leaves(&parameters, leaves)?, parameters, }) } - /// + /// Returns a reference to the parameters used by this merkle tree. #[inline] pub fn parameters(&self) -> &Parameters<C> { &self.parameters @@ -760,10 +631,7 @@ where /// Returns the [`Path`] to some element of the merkle tree given by the `query`. #[inline] - pub fn path( - &self, - query: <C::Tree as Tree<C>>::Query, - ) -> Result<Path<C>, <C::Tree as Tree<C>>::Error> { + pub fn path(&self, query: T::Query) -> Result<Path<C>, T::Error> { self.tree.path(&self.parameters, query) } @@ -771,7 +639,440 @@ where /// leaf could not be inserted because the tree has exhausted its capacity. #[inline] pub fn append(&mut self, leaf: &Leaf<C>) -> bool { - self.tree - .append(&self.parameters, self.parameters.digest(leaf)) + self.tree.append(&self.parameters, leaf) + } +} + +impl<C, T> AsRef<T> for MerkleTree<C, T> +where + C: Configuration + ?Sized, + T: Tree<C>, +{ + #[inline] + fn as_ref(&self) -> &T { + &self.tree + } +} + +impl<C, T> AsMut<T> for MerkleTree<C, T> +where + C: Configuration + ?Sized, + T: Tree<C>, +{ + #[inline] + fn as_mut(&mut self) -> &mut T { + &mut self.tree + } +} + +impl<C, T> Borrow<T> for MerkleTree<C, T> +where + C: Configuration + ?Sized, + T: Tree<C>, +{ + #[inline] + fn borrow(&self) -> &T { + &self.tree + } +} + +impl<C, T> BorrowMut<T> for MerkleTree<C, T> +where + C: Configuration + ?Sized, + T: Tree<C>, +{ + #[inline] + fn borrow_mut(&mut self) -> &mut T { + &mut self.tree + } +} + +/// Full Merkle Tree Storage +pub mod full { + use super::*; + use alloc::collections::BTreeMap; + + /// Path Query Error Type + /// + /// Querying for paths beyond the current length of a [`Full`] tree is an error. + #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct Unknown; + + /// Full Merkle Tree Backing Structure + #[derive(derivative::Derivative)] + #[derivative( + Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), + Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), + Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default"), + Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), + PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") + )] + pub struct Full<C> + where + C: Configuration + ?Sized, + { + /// Leaf Digests + leaf_digests: Vec<LeafDigest<C>>, + + /// Inner Digests + /// + /// See [`Self::inner_digest_index`] for the encoding of tree coordinates. + inner_digests: BTreeMap<usize, InnerDigest<C>>, + } + + impl<C> Full<C> + where + C: Configuration + ?Sized, + { + /// Returns the sibling leaf node to `index`. + #[inline] + fn sibling_leaf(&self, index: Node) -> Option<&LeafDigest<C>> { + self.leaf_digests.get(index.sibling().0) + } + + /// Computes the index into the `inner_digests` map for a node of the given `depth` and `index`. + #[inline] + fn inner_digest_index(depth: usize, index: Node) -> usize { + (1 << (depth + 1)) + index.0 - 1 + } + + /// Returns the inner digest at the given `depth` and `index` of the merkle tree. + /// + /// # Note + /// + /// The `depth` of the tree tracks the inner nodes not including the root. The root of the tree + /// is at `depth := -1` and `index := 0`. + #[inline] + fn get_inner_digest(&self, depth: usize, index: Node) -> Option<&InnerDigest<C>> { + self.inner_digests + .get(&Self::inner_digest_index(depth, index)) + } + + /// Sets the inner digest at the given `depth` and `index` of the merkle tree to the + /// `inner_digest` value. + /// + /// # Note + /// + /// The `depth` of the tree tracks the inner nodes not including the root. The root of the tree + /// is at `depth := -1` and `index := 0`. + #[inline] + fn set_inner_digest(&mut self, depth: usize, index: Node, inner_digest: InnerDigest<C>) { + self.inner_digests + .insert(Self::inner_digest_index(depth, index), inner_digest); + } + + /// Computes the inner path of a node starting at the leaf given by `index`. + #[inline] + fn compute_inner_path(&self, mut index: Node) -> Vec<InnerDigest<C>> + where + InnerDigest<C>: Clone, + { + path_iter::<C>() + .map(move |depth| { + self.get_inner_digest(depth, index.into_parent().sibling()) + .map(Clone::clone) + .unwrap_or_default() + }) + .collect() + } + + /// Computes the root of the merkle tree, modifying the inner tree in-place, starting at the + /// leaf given by `index`. + #[inline] + fn compute_root( + &mut self, + parameters: &Parameters<C>, + mut index: Node, + base: InnerDigest<C>, + ) -> Root<C> + where + InnerDigest<C>: Clone, + { + // TODO: Use `core::lazy::Lazy` when it is stabilized. + let default_inner_digest = Default::default(); + path_iter::<C>().fold(base, |acc, depth| { + self.set_inner_digest(depth, index.into_parent(), acc.clone()); + index.join( + parameters, + &acc, + self.get_inner_digest(depth, index.sibling()) + .unwrap_or(&default_inner_digest), + ) + }) + } + } + + impl<C> Tree<C> for Full<C> + where + C: Configuration + ?Sized, + LeafDigest<C>: Clone, + InnerDigest<C>: Clone, + { + type Query = usize; + + type Error = Unknown; + + #[inline] + fn new(parameters: &Parameters<C>) -> Self { + let _ = parameters; + Self { + leaf_digests: Default::default(), + inner_digests: Default::default(), + } + } + + #[inline] + fn len(&self) -> usize { + self.leaf_digests.len() + } + + #[inline] + fn root(&self, parameters: &Parameters<C>) -> Root<C> { + let _ = parameters; + self.inner_digests + .get(&0) + .map(Clone::clone) + .unwrap_or_default() + } + + #[inline] + fn path( + &self, + parameters: &Parameters<C>, + query: Self::Query, + ) -> Result<Path<C>, Self::Error> { + let _ = parameters; + if query > capacity::<C>() { + return Err(Unknown); + } + let leaf_index = Node(query); + Ok(Path::new( + leaf_index, + self.sibling_leaf(leaf_index) + .map(Clone::clone) + .unwrap_or_default(), + self.compute_inner_path(leaf_index), + )) + } + + #[inline] + fn append(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> bool { + let len = self.len(); + if len >= capacity::<C>() { + return false; + } + let leaf_digest = parameters.digest(leaf); + let leaf_index = Node(len); + let root = self.compute_root( + parameters, + leaf_index, + leaf_index.join_leaves( + parameters, + &leaf_digest, + self.sibling_leaf(leaf_index).unwrap_or(&Default::default()), + ), + ); + self.inner_digests.insert(0, root); + self.leaf_digests.push(leaf_digest); + true + } + } +} + +/// Latest Node Merkle Tree Storage +pub mod latest_node { + use super::*; + use core::convert::Infallible; + + /// Path Query Type + /// + /// Since the [`LatestNode`] tree only stores one node and one path, we can only query the tree + /// for the current path. + #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct Current; + + /// Latest Node Merkle Tree Backing Structure + #[derive(derivative::Derivative)] + #[derivative( + Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), + Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), + Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default"), + Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), + PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") + )] + pub struct LatestNode<C> + where + C: Configuration + ?Sized, + { + /// Leaf Digest + leaf_digest: Option<LeafDigest<C>>, + + /// Path + path: Path<C>, + + /// Root + root: Root<C>, + } + + impl<C> LatestNode<C> + where + C: Configuration + ?Sized, + { + /// Returns the number of leaves in the merkle tree. + #[inline] + fn len(&self) -> usize { + if self.leaf_digest.is_none() { + 0 + } else { + self.path.leaf_node_index.0 + 1 + } + } + + /// Returns the next avaiable index or `None` if the merkle tree is full. + #[inline] + fn next_index(&self) -> Option<Node> { + let len = self.len(); + if len == 0 { + Some(Node(0)) + } else if len < capacity::<C>() - 1 { + Some(Node(len + 1)) + } else { + None + } + } + + /// Returns the current merkle tree root. + #[inline] + pub fn root(&self) -> &Root<C> { + &self.root + } + + /// Returns the current merkle tree path for the current leaf. + #[inline] + pub fn path(&self) -> &Path<C> { + &self.path + } + } + + impl<C> Tree<C> for LatestNode<C> + where + C: Configuration + ?Sized, + LeafDigest<C>: Clone, + InnerDigest<C>: Clone, + { + type Query = Current; + + type Error = Infallible; + + #[inline] + fn new(parameters: &Parameters<C>) -> Self { + let _ = parameters; + Self { + leaf_digest: Default::default(), + path: Default::default(), + root: Default::default(), + } + } + + #[inline] + fn len(&self) -> usize { + self.len() + } + + #[inline] + fn root(&self, parameters: &Parameters<C>) -> Root<C> { + let _ = parameters; + self.root.clone() + } + + #[inline] + fn path( + &self, + parameters: &Parameters<C>, + query: Self::Query, + ) -> Result<Path<C>, Self::Error> { + let _ = (parameters, query); + Ok(self.path.clone()) + } + + #[inline] + fn append(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> bool { + let index = match self.next_index() { + Some(index) => index, + _ => return false, + }; + + let leaf_digest = parameters.digest(leaf); + + if index == 0 { + self.root = self.path.root_relative_to(parameters, &leaf_digest); + } else { + let mut next_index = index; + + let default_leaf_digest = Default::default(); + let default_inner_digest = Default::default(); + + let current_leaf_digest = self.leaf_digest.as_ref().unwrap(); + + let (mut accumulator, sibling_digest) = match next_index.parity() { + Parity::Left => ( + parameters.join_leaves(&leaf_digest, &default_leaf_digest), + default_leaf_digest, + ), + Parity::Right => ( + parameters.join_leaves(current_leaf_digest, &leaf_digest), + current_leaf_digest.clone(), + ), + }; + + let mut prev_index = next_index - 1; + let mut prev_digest = prev_index.join_leaves( + parameters, + current_leaf_digest, + &self.path.sibling_digest, + ); + + let inner_path = self + .path + .inner_path + .iter() + .map(|digest| { + if prev_index.into_parent() == next_index.into_parent() { + accumulator = next_index.join_opposite_pair( + parameters, + digest, + &accumulator, + &default_inner_digest, + ); + digest.clone() + } else { + let next_inner_path_digest = next_index + .parity() + .map(|| default_inner_digest.clone(), || prev_digest.clone()); + + accumulator = next_index.join_opposite_pair( + parameters, + &prev_digest, + &accumulator, + &default_inner_digest, + ); + + if prev_index.is_right() { + prev_digest = parameters.join(digest, &prev_digest); + } + + next_inner_path_digest + } + }) + .collect(); + + self.path = Path::new(index, sibling_digest, inner_path); + self.root = accumulator; + } + + self.leaf_digest = Some(leaf_digest); + + true + } } } diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index e118857c9..f03ab78ea 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -27,6 +27,8 @@ mod array; mod concat; mod mixed_chain; +pub mod num; + #[cfg(feature = "rand_core")] #[cfg_attr(doc_cfg, doc(cfg(feature = "rand_core")))] pub mod rand; diff --git a/manta-util/src/num.rs b/manta-util/src/num.rs new file mode 100644 index 000000000..22bb88bdb --- /dev/null +++ b/manta-util/src/num.rs @@ -0,0 +1,53 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Numeric Utilities + +use core::num::Wrapping; + +/// Parity Trait +pub trait HasParity { + /// Returns `true` if `self` represents an even integer. + fn is_even(&self) -> bool; + + /// Returns `true` if `self` does not represent an even integer. + #[inline] + fn is_odd(&self) -> bool { + !self.is_even() + } +} + +macro_rules! impl_has_parity { + ($($type:tt),+) => { + $( + impl HasParity for $type { + #[inline] + fn is_even(&self) -> bool { + self % 2 == 0 + } + } + + impl HasParity for Wrapping<$type> { + #[inline] + fn is_even(&self) -> bool { + self.0 % 2 == 0 + } + } + )* + }; +} + +impl_has_parity!(i8, i16, i32, i64, i128, isize, u8, u16, u64, u128, usize); From a80fbc6cb5e30c7dc7970593a7d43534a8e87431 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 21 Sep 2021 19:18:39 -0400 Subject: [PATCH 050/275] some cleanup; setup manta-pay to build dusk-plonk backend --- manta-accounting/Cargo.toml | 2 +- manta-codec/Cargo.toml | 6 +- manta-crypto/Cargo.toml | 4 +- manta-crypto/src/ies.rs | 6 +- manta-crypto/src/merkle_tree.rs | 97 ++++++++++++++++++- manta-crypto/src/set.rs | 2 + manta-pay/Cargo.toml | 23 ++++- manta-pay/src/accounting/config.rs | 7 +- manta-pay/src/crypto/commitment/pedersen.rs | 2 +- .../{ => arkworks}/constraint_system.rs | 0 .../src/crypto/constraint/arkworks/mod.rs | 23 +++++ .../{ => arkworks}/proof_systems/groth16.rs | 4 +- .../constraint/arkworks/proof_systems/mod.rs | 21 ++++ .../dusk_network/constraint_system.rs | 17 ++++ .../src/crypto/constraint/dusk_network/mod.rs | 23 +++++ .../{ => dusk_network}/proof_systems/mod.rs | 6 +- manta-pay/src/crypto/constraint/mod.rs | 10 +- manta-pay/src/crypto/merkle_tree/mod.rs | 16 +-- manta-pay/src/crypto/prf/blake2s.rs | 4 +- manta/Cargo.toml | 5 + manta/src/lib.rs | 5 +- 21 files changed, 248 insertions(+), 35 deletions(-) rename manta-pay/src/crypto/constraint/{ => arkworks}/constraint_system.rs (100%) create mode 100644 manta-pay/src/crypto/constraint/arkworks/mod.rs rename manta-pay/src/crypto/constraint/{ => arkworks}/proof_systems/groth16.rs (98%) create mode 100644 manta-pay/src/crypto/constraint/arkworks/proof_systems/mod.rs create mode 100644 manta-pay/src/crypto/constraint/dusk_network/constraint_system.rs create mode 100644 manta-pay/src/crypto/constraint/dusk_network/mod.rs rename manta-pay/src/crypto/constraint/{ => dusk_network}/proof_systems/mod.rs (90%) diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index a5c154ad5..43d2acf43 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -34,7 +34,7 @@ cocoon-adapters = ["cocoon/std", "zeroize"] # Standard Library std = ["cocoon-adapters"] -# Testing Framework +# Testing Frameworks test = [] [dependencies] diff --git a/manta-codec/Cargo.toml b/manta-codec/Cargo.toml index aaa36aba3..d09319cae 100644 --- a/manta-codec/Cargo.toml +++ b/manta-codec/Cargo.toml @@ -25,10 +25,12 @@ is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } [features] -default = [] -std = [] +# Scale Codec Derive Traits derive = ["scale-codec/derive"] +# Standard Library +std = [] + [dependencies] ark-serialize = { version = "0.3.0", default-features = false } ark-std = { version = "0.3.0", default-features = false } diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index 0a594a7cf..a8ac4fe60 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -25,8 +25,10 @@ is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } [features] +# Constraint System Gadgets # TODO: constraints = [] -default = [] + +# Testing Frameworks test = [] [dependencies] diff --git a/manta-crypto/src/ies.rs b/manta-crypto/src/ies.rs index a0274eca7..ad0e78ab3 100644 --- a/manta-crypto/src/ies.rs +++ b/manta-crypto/src/ies.rs @@ -415,6 +415,7 @@ pub mod test { use core::fmt::Debug; /// Tests encryption/decryption of a sample `plaintext`. + #[inline] pub fn assert_decryption_of_encryption<I, R>(plaintext: &I::Plaintext, rng: &mut R) where I: IntegratedEncryptionScheme, @@ -430,6 +431,9 @@ pub mod test { .expect("Unable to encrypt plaintext."), ) .expect("Unable to decrypt plaintext."); - assert_eq!(plaintext, &reconstructed_plaintext) + assert_eq!( + plaintext, &reconstructed_plaintext, + "Plaintext didn't match decrypted ciphertext." + ) } } diff --git a/manta-crypto/src/merkle_tree.rs b/manta-crypto/src/merkle_tree.rs index e4668a705..3c746aa70 100644 --- a/manta-crypto/src/merkle_tree.rs +++ b/manta-crypto/src/merkle_tree.rs @@ -16,6 +16,10 @@ //! Merkle Trees +// TODO: Implement [`crate::VerifiedSet`] for [`MerkleTree`]. +// TODO: Maybe we should require `INNER_HEIGHT` instead of `HEIGHT` so that we don't have to rely +// on the user to check that `HEIGHT >= 2`. + extern crate alloc; use alloc::vec::Vec; @@ -76,6 +80,10 @@ pub trait Configuration { type Height: Copy + Into<usize>; /// Fixed Height of the Merkle Tree + /// + /// # Contract + /// + /// Trees must always have height at least `2`. const HEIGHT: Self::Height; } @@ -556,11 +564,23 @@ where ) } + /// Returns `true` if `self` is a witness to the fact that `leaf_digest` is stored in a + /// merkle tree with the given `root`. + #[inline] + pub fn verify_digest( + &self, + parameters: &Parameters<C>, + root: &Root<C>, + leaf_digest: &LeafDigest<C>, + ) -> bool { + root == &self.root_relative_to(parameters, leaf_digest) + } + /// Returns `true` if `self` is a witness to the fact that `leaf` is stored in a merkle tree /// with the given `root`. #[inline] - pub fn is_valid(&self, parameters: &Parameters<C>, root: &Root<C>, leaf: &Leaf<C>) -> bool { - root == &self.root_relative_to(parameters, &parameters.digest(leaf)) + pub fn verify(&self, parameters: &Parameters<C>, root: &Root<C>, leaf: &Leaf<C>) -> bool { + self.verify_digest(parameters, root, &parameters.digest(leaf)) } } @@ -641,6 +661,12 @@ where pub fn append(&mut self, leaf: &Leaf<C>) -> bool { self.tree.append(&self.parameters, leaf) } + + /// Extracts the parameters of the merkle tree, dropping the internal tree. + #[inline] + pub fn into_parameters(self) -> Parameters<C> { + self.parameters + } } impl<C, T> AsRef<T> for MerkleTree<C, T> @@ -692,6 +718,9 @@ pub mod full { use super::*; use alloc::collections::BTreeMap; + /// Full Merkle Tree Type + pub type FullMerkleTree<C> = MerkleTree<C, Full<C>>; + /// Path Query Error Type /// /// Querying for paths beyond the current length of a [`Full`] tree is an error. @@ -724,6 +753,12 @@ pub mod full { where C: Configuration + ?Sized, { + /// Returns the leaf digests currently stored in the merkle tree. + #[inline] + pub fn leaf_digests(&self) -> &[LeafDigest<C>] { + &self.leaf_digests + } + /// Returns the sibling leaf node to `index`. #[inline] fn sibling_leaf(&self, index: Node) -> Option<&LeafDigest<C>> { @@ -884,6 +919,9 @@ pub mod latest_node { use super::*; use core::convert::Infallible; + /// Latest Node Merkle Tree Type + pub type LatestNodeMerkleTree<C> = MerkleTree<C, LatestNode<C>>; + /// Path Query Type /// /// Since the [`LatestNode`] tree only stores one node and one path, we can only query the tree @@ -952,6 +990,12 @@ pub mod latest_node { pub fn path(&self) -> &Path<C> { &self.path } + + /// Returns the currently stored leaf digest, returning `None` if the tree is empty. + #[inline] + pub fn leaf_digest(&self) -> Option<&LeafDigest<C>> { + self.leaf_digest.as_ref() + } } impl<C> Tree<C> for LatestNode<C> @@ -1076,3 +1120,52 @@ pub mod latest_node { } } } + +/// Testing Framework +#[cfg(feature = "test")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] +pub mod test { + use super::*; + use core::fmt::Debug; + + /// Tests that a tree constructed with `parameters` can accept at least two leaves without + /// failing. + #[inline] + pub fn append_twice_to_empty_tree_succeeds<C, T>( + parameters: Parameters<C>, + lhs: &Leaf<C>, + rhs: &Leaf<C>, + ) -> Parameters<C> + where + C: Configuration + ?Sized, + T: Tree<C>, + { + let mut tree = MerkleTree::<C, T>::new(parameters); + assert!( + tree.append(lhs), + "Trees always have a capacity of at least two." + ); + assert!( + tree.append(rhs), + "Trees always have a capacity of at least two." + ); + tree.into_parameters() + } + + /// Tests path construction by checking that the path generated by `query` on `tree` is a valid + /// [`Path`] for `leaf`. + #[inline] + pub fn assert_valid_path<C, T>(tree: &MerkleTree<C, T>, query: T::Query, leaf: &Leaf<C>) + where + C: Configuration + ?Sized, + T: Tree<C>, + T::Error: Debug, + { + assert!( + tree.path(query) + .expect("Only valid queries are accepted.") + .verify(&tree.parameters, &tree.root(), leaf), + "Path returned from tree was not valid." + ) + } +} diff --git a/manta-crypto/src/set.rs b/manta-crypto/src/set.rs index 3bc39cef1..7a82c1722 100644 --- a/manta-crypto/src/set.rs +++ b/manta-crypto/src/set.rs @@ -20,6 +20,8 @@ // verified set can give to someone who wants to check a containment proof, since in general // we don't actually need access to the set itself, or having access to the set would be be // possible in any real implementation. +// FIXME: The `Set::contains` method is not really something we can always implement properly. +// FIXME: Should we just get rid of `Set` and just ensure we can get proofs working? pub(crate) mod prelude { #[doc(inline)] diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 7139784dd..720a83f5e 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -25,7 +25,24 @@ is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } [features] -default = [] +# Default Features +default = ["arkworks-groth16"] + +# Enable Arkworks Zero-Knowledge Proof Backends +arkworks-zkp = [] +# Arkworks Groth16 Backend +arkworks-groth16 = ["arkworks-zkp", "ark-groth16"] +# Enable All Arkworks Zero-Knowledge Proof Backends +arkworks-zkp-all = ["arkworks-groth16"] + +# Enable Dusk-Netowrk Zero-Knowledge Proof Backends +dusk-network-zkp = [] +# Dusk-Network Plonk Backend +dusk-network-plonk = ["dusk-network-zkp", "dusk-plonk"] +# Enable All Dusk-Network Zero-Knowledge Proof Backends +dusk-network-zkp-all = ["dusk-network-plonk"] + +# Standard Library std = [] [dependencies] @@ -35,12 +52,13 @@ ark-crypto-primitives = { version = "0.3.0", default-features = false, features ark-ec = { version = "0.3.0", default-features = false } ark-ed-on-bls12-381 = { version = "0.3.0", default-features = false, features = ["r1cs"] } ark-ff = { version = "0.3.0", default-features = false } -ark-groth16 = { version = "0.3.0", default-features = false } +ark-groth16 = { version = "0.3.0", optional = true, default-features = false } ark-r1cs-std = { version = "0.3.1", default-features = false } ark-relations = { version = "0.3.0", default-features = false } bip32 = { version = "0.2.2", default-features = false, features = ["bip39", "secp256k1"] } blake2 = { version = "0.9.2", default-features = false } derivative = { version = "2.2.0", default-features = false } +dusk-plonk = { version = "0.8.2", optional = true, default-features = false } generic-array = { version = "0.14.4", default-features = false } manta-accounting = { path = "../manta-accounting", default-features = false } manta-crypto = { path = "../manta-crypto", default-features = false } @@ -51,4 +69,5 @@ x25519-dalek = { git = "https://github.com/Manta-Network/x25519-dalek", default- [dev-dependencies] manta-crypto = { path = "../manta-crypto", features = ["test"] } +manta-accounting = { path = "../manta-accounting", features = ["test"] } rand = "0.8.4" diff --git a/manta-pay/src/accounting/config.rs b/manta-pay/src/accounting/config.rs index f49749110..1826e7941 100644 --- a/manta-pay/src/accounting/config.rs +++ b/manta-pay/src/accounting/config.rs @@ -16,11 +16,16 @@ //! Identity and Transfer Configurations +// TODO: Make this generic over the backend we use. Automatically compute which features are +// enabled when using whichever backend. + use crate::{ accounting::ledger::{UtxoSet, UtxoSetVar}, crypto::{ commitment::pedersen::{self, PedersenWindow}, - constraint::{proof_systems::Groth16, ArkConstraintSystem, AssetBalanceVar, AssetIdVar}, + constraint::arkworks::{ + proof_systems::groth16::Groth16, ArkConstraintSystem, AssetBalanceVar, AssetIdVar, + }, ies::IES, merkle_tree, prf::blake2s::{constraint::Blake2sVar, Blake2s}, diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs index e906357bc..cedd60940 100644 --- a/manta-pay/src/crypto/commitment/pedersen.rs +++ b/manta-pay/src/crypto/commitment/pedersen.rs @@ -155,7 +155,7 @@ where /// Pedersen Commitment Scheme Constraint System Implementation pub mod constraint { use super::*; - use crate::crypto::constraint::{empty, full, ArkConstraintSystem}; + use crate::crypto::constraint::arkworks::{empty, full, ArkConstraintSystem}; use ark_crypto_primitives::{ commitment::pedersen::constraints::{CommGadget, ParametersVar, RandomnessVar}, CommitmentGadget, diff --git a/manta-pay/src/crypto/constraint/constraint_system.rs b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs similarity index 100% rename from manta-pay/src/crypto/constraint/constraint_system.rs rename to manta-pay/src/crypto/constraint/arkworks/constraint_system.rs diff --git a/manta-pay/src/crypto/constraint/arkworks/mod.rs b/manta-pay/src/crypto/constraint/arkworks/mod.rs new file mode 100644 index 000000000..1a7fa9b69 --- /dev/null +++ b/manta-pay/src/crypto/constraint/arkworks/mod.rs @@ -0,0 +1,23 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Arkworks Constraint System and Proof System Implementations + +mod constraint_system; + +pub mod proof_systems; + +pub use constraint_system::*; diff --git a/manta-pay/src/crypto/constraint/proof_systems/groth16.rs b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs similarity index 98% rename from manta-pay/src/crypto/constraint/proof_systems/groth16.rs rename to manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs index 23fef8fd3..ff1018cb3 100644 --- a/manta-pay/src/crypto/constraint/proof_systems/groth16.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs @@ -18,7 +18,9 @@ // FIXME: Move these tests elsewhere since they are rather general. -use crate::crypto::constraint::{constraint_system::SynthesisResult, ArkConstraintSystem}; +use crate::crypto::constraint::arkworks::{ + constraint_system::SynthesisResult, ArkConstraintSystem, +}; use alloc::vec::Vec; use ark_crypto_primitives::SNARK; use ark_ec::PairingEngine; diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_systems/mod.rs b/manta-pay/src/crypto/constraint/arkworks/proof_systems/mod.rs new file mode 100644 index 000000000..bd0636544 --- /dev/null +++ b/manta-pay/src/crypto/constraint/arkworks/proof_systems/mod.rs @@ -0,0 +1,21 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Arkworks Proof System Implementations + +#[cfg(feature = "arkworks-groth16")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks-groth16")))] +pub mod groth16; diff --git a/manta-pay/src/crypto/constraint/dusk_network/constraint_system.rs b/manta-pay/src/crypto/constraint/dusk_network/constraint_system.rs new file mode 100644 index 000000000..e674c4153 --- /dev/null +++ b/manta-pay/src/crypto/constraint/dusk_network/constraint_system.rs @@ -0,0 +1,17 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Dusk-Network Constraint System Implementation diff --git a/manta-pay/src/crypto/constraint/dusk_network/mod.rs b/manta-pay/src/crypto/constraint/dusk_network/mod.rs new file mode 100644 index 000000000..2c9e35e6d --- /dev/null +++ b/manta-pay/src/crypto/constraint/dusk_network/mod.rs @@ -0,0 +1,23 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Dusk-Network Constraint System and Proof System Implementations + +mod constraint_system; + +pub mod proof_systems; + +pub use constraint_system::*; diff --git a/manta-pay/src/crypto/constraint/proof_systems/mod.rs b/manta-pay/src/crypto/constraint/dusk_network/proof_systems/mod.rs similarity index 90% rename from manta-pay/src/crypto/constraint/proof_systems/mod.rs rename to manta-pay/src/crypto/constraint/dusk_network/proof_systems/mod.rs index 0e7aa4087..5a558f8a2 100644 --- a/manta-pay/src/crypto/constraint/proof_systems/mod.rs +++ b/manta-pay/src/crypto/constraint/dusk_network/proof_systems/mod.rs @@ -14,8 +14,4 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Arkworks Proof System Implementations - -mod groth16; - -pub use groth16::*; +//! Dusk-Network Proof System Implementations diff --git a/manta-pay/src/crypto/constraint/mod.rs b/manta-pay/src/crypto/constraint/mod.rs index 551a479b5..f53060e3d 100644 --- a/manta-pay/src/crypto/constraint/mod.rs +++ b/manta-pay/src/crypto/constraint/mod.rs @@ -16,8 +16,10 @@ //! Constraint System and Proof System Implementations -mod constraint_system; +#[cfg(feature = "arkworks-zkp")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks-zkp")))] +pub mod arkworks; -pub mod proof_systems; - -pub use constraint_system::*; +#[cfg(feature = "dusk-network-zkp")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "dusk-network-zkp")))] +pub mod dusk_network; diff --git a/manta-pay/src/crypto/merkle_tree/mod.rs b/manta-pay/src/crypto/merkle_tree/mod.rs index 6536a6aff..394993b8a 100644 --- a/manta-pay/src/crypto/merkle_tree/mod.rs +++ b/manta-pay/src/crypto/merkle_tree/mod.rs @@ -16,21 +16,14 @@ //! Merkle Tree Implementations -// NOTE: Most if not all of the fallible interfaces in this file never actually fail. We use -// faillible interfaces so that we don't have to depend explicitly on implementation -// details of the `arkworks` project. +// FIXME: Move to `manta_crypto::merkle_tree` implementation! -// TODO: Improve the interface so that we don't have to worry about panicking interfaces from -// arkworks (automatic padding, etc.). -// TODO: We should think about how much of this file can be moved to `manta_crypto`. - -// NOTE: This is meant to be a full implementation of the incremental merkle tree type suitable for -// merging into arkworks itself. Therefore, even if we don't use all of the functionality -// available in this module, we want to preserve the code anyway. +// NOTE: This is meant to be a full implementation of the incremental merkle tree type suitable +// for merging into arkworks itself. Therefore, even if we don't use all of the +// functionality available in this module, we want to preserve the code anyway. #[allow(dead_code)] mod incremental; -use crate::crypto::constraint::{empty, full, ArkConstraintSystem}; use alloc::{vec, vec::Vec}; use ark_crypto_primitives::{ crh::{TwoToOneCRH, CRH}, @@ -344,6 +337,7 @@ where /// Merkle Tree Constraint System Variables pub mod constraint { use super::*; + use crate::crypto::constraint::arkworks::{empty, full, ArkConstraintSystem}; use ark_crypto_primitives::{ crh::constraints::{CRHGadget, TwoToOneCRHGadget}, merkle_tree::constraints::PathVar as ArkPathVar, diff --git a/manta-pay/src/crypto/prf/blake2s.rs b/manta-pay/src/crypto/prf/blake2s.rs index 3f2710bb8..0f546940a 100644 --- a/manta-pay/src/crypto/prf/blake2s.rs +++ b/manta-pay/src/crypto/prf/blake2s.rs @@ -110,7 +110,7 @@ impl PseudorandomFunctionFamily for Blake2s { /// Blake2s PRF Constraint System Implementations pub mod constraint { use super::*; - use crate::crypto::constraint::{empty, full, ByteArrayVar}; + use crate::crypto::constraint::arkworks::{empty, full, ByteArrayVar}; use ark_crypto_primitives::{ prf::blake2s::constraints::Blake2sGadget as ArkBlake2sVar, PRFGadget, }; @@ -124,7 +124,7 @@ pub mod constraint { }; /// Constraint System Type - pub use crate::crypto::constraint::ArkConstraintSystem as ConstraintSystem; + pub use crate::crypto::constraint::arkworks::ArkConstraintSystem as ConstraintSystem; /// Blake2s Pseudorandom Function Family Seed Variable #[derive(derivative::Derivative)] diff --git a/manta/Cargo.toml b/manta/Cargo.toml index b78f0b712..edb52a53a 100644 --- a/manta/Cargo.toml +++ b/manta/Cargo.toml @@ -25,8 +25,13 @@ is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } [features] +# Default Features default = [] + +# Standard Library std = [] + +# Test Frameworks test = ["manta-accounting/test", "manta-crypto/test"] [dependencies] diff --git a/manta/src/lib.rs b/manta/src/lib.rs index b49ae0d21..761ba2b2c 100644 --- a/manta/src/lib.rs +++ b/manta/src/lib.rs @@ -16,7 +16,10 @@ //! The Manta Network -#![cfg_attr(not(feature = "std"), no_std)] +// TODO: Given the structure of `manta-pay` does this crate make sense anymore? Maybe only as +// `accounting` + `crypto`? + +#![cfg_attr(not(any(feature = "std", test)), no_std)] #![cfg_attr(doc_cfg, feature(doc_cfg))] #![forbid(rustdoc::broken_intra_doc_links)] #![forbid(missing_docs)] From 254c45f8d42d90c15221298c0d6f70f9135174bd Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 22 Sep 2021 01:23:28 -0400 Subject: [PATCH 051/275] WIP: remove old merkle tree wrapper from manta-pay --- manta-crypto/src/merkle_tree.rs | 73 ++- manta-pay/src/accounting/config.rs | 34 +- manta-pay/src/accounting/ledger.rs | 26 +- .../arkworks/proof_systems/groth16.rs | 2 + manta-pay/src/crypto/merkle_tree/mod.rs | 455 +++++++----------- 5 files changed, 265 insertions(+), 325 deletions(-) diff --git a/manta-crypto/src/merkle_tree.rs b/manta-crypto/src/merkle_tree.rs index 3c746aa70..fa45c63d7 100644 --- a/manta-crypto/src/merkle_tree.rs +++ b/manta-crypto/src/merkle_tree.rs @@ -16,6 +16,8 @@ //! Merkle Trees +// TODO: Should `Leaf` move into `Tree`/`Configuration` since we might want the tree to have +// special kinds of leaf input (metadata along with just the digest)? // TODO: Implement [`crate::VerifiedSet`] for [`MerkleTree`]. // TODO: Maybe we should require `INNER_HEIGHT` instead of `HEIGHT` so that we don't have to rely // on the user to check that `HEIGHT >= 2`. @@ -102,9 +104,6 @@ pub type InnerHashParameters<C> = <<C as Configuration>::InnerHash as InnerHash> /// Inner Hash Digest Type pub type InnerDigest<C> = <<C as Configuration>::InnerHash as InnerHash>::Output; -/// Merkle Tree Root Type -pub type Root<C> = InnerDigest<C>; - /// Returns the capacity of the merkle tree with the given [`C::HEIGHT`](Configuration::HEIGHT) /// parameter. #[inline] @@ -505,8 +504,33 @@ where pub fn join_leaves(&self, lhs: &LeafDigest<C>, rhs: &LeafDigest<C>) -> InnerDigest<C> { C::InnerHash::join_leaves(&self.inner, lhs, rhs) } + + /// Verify that `path` witnesses the fact that `leaf` is a member of a merkle tree with the + /// given `root`. + #[inline] + pub fn verify_path(&self, path: &Path<C>, root: &Root<C>, leaf: &Leaf<C>) -> bool { + path.verify(self, root, leaf) + } } +/// Merkle Tree Root Wrapper Type +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "InnerDigest<C>: Clone"), + Copy(bound = "InnerDigest<C>: Copy"), + Debug(bound = "InnerDigest<C>: Debug"), + Default(bound = "InnerDigest<C>: Default"), + Eq(bound = "InnerDigest<C>: Eq"), + Hash(bound = "InnerDigest<C>: Hash"), + PartialEq(bound = "InnerDigest<C>: PartialEq") +)] +pub struct Root<C>( + /// Root Inner Digest + pub InnerDigest<C>, +) +where + C: Configuration + ?Sized; + /// Merkle Tree Path #[derive(derivative::Derivative)] #[derivative( @@ -520,31 +544,31 @@ pub struct Path<C> where C: Configuration + ?Sized, { - /// Leaf Node Index - leaf_node_index: Node, + /// Leaf Index + pub leaf_index: Node, /// Sibling Digest - sibling_digest: LeafDigest<C>, + pub sibling_digest: LeafDigest<C>, /// Inner Path /// - /// The inner path is stored in the direction leaf-to-root. - inner_path: Vec<InnerDigest<C>>, + /// Inner digests are stored from leaf to root, not including the root. + pub inner_path: Vec<InnerDigest<C>>, } impl<C> Path<C> where C: Configuration + ?Sized, { - /// Builds a new [`Path`] from `leaf_node_index`, `sibling_digest`, and `inner_path`. + /// Builds a new [`Path`] from `leaf_index`, `sibling_digest`, and `inner_path`. #[inline] pub fn new( - leaf_node_index: Node, + leaf_index: Node, sibling_digest: LeafDigest<C>, inner_path: Vec<InnerDigest<C>>, ) -> Self { Self { - leaf_node_index, + leaf_index, sibling_digest, inner_path, } @@ -557,11 +581,11 @@ where parameters: &Parameters<C>, leaf_digest: &LeafDigest<C>, ) -> Root<C> { - let mut node_index = self.leaf_node_index; - self.inner_path.iter().fold( - node_index.join_leaves(parameters, leaf_digest, &self.sibling_digest), - move |acc, d| node_index.into_parent().join(parameters, &acc, d), - ) + let mut index = self.leaf_index; + Root(self.inner_path.iter().fold( + index.join_leaves(parameters, leaf_digest, &self.sibling_digest), + move |acc, d| index.into_parent().join(parameters, &acc, d), + )) } /// Returns `true` if `self` is a witness to the fact that `leaf_digest` is stored in a @@ -819,13 +843,14 @@ pub mod full { parameters: &Parameters<C>, mut index: Node, base: InnerDigest<C>, - ) -> Root<C> + ) -> InnerDigest<C> where InnerDigest<C>: Clone, { // TODO: Use `core::lazy::Lazy` when it is stabilized. let default_inner_digest = Default::default(); path_iter::<C>().fold(base, |acc, depth| { + // FIXME: Get rid of this clone. self.set_inner_digest(depth, index.into_parent(), acc.clone()); index.join( parameters, @@ -864,10 +889,12 @@ pub mod full { #[inline] fn root(&self, parameters: &Parameters<C>) -> Root<C> { let _ = parameters; - self.inner_digests - .get(&0) - .map(Clone::clone) - .unwrap_or_default() + Root( + self.inner_digests + .get(&0) + .map(Clone::clone) + .unwrap_or_default(), + ) } #[inline] @@ -962,7 +989,7 @@ pub mod latest_node { if self.leaf_digest.is_none() { 0 } else { - self.path.leaf_node_index.0 + 1 + self.path.leaf_index.0 + 1 } } @@ -1111,7 +1138,7 @@ pub mod latest_node { .collect(); self.path = Path::new(index, sibling_digest, inner_path); - self.root = accumulator; + self.root = Root(accumulator); } self.leaf_digest = Some(leaf_digest); diff --git a/manta-pay/src/accounting/config.rs b/manta-pay/src/accounting/config.rs index 1826e7941..07ab67ac9 100644 --- a/manta-pay/src/accounting/config.rs +++ b/manta-pay/src/accounting/config.rs @@ -27,7 +27,10 @@ use crate::{ proof_systems::groth16::Groth16, ArkConstraintSystem, AssetBalanceVar, AssetIdVar, }, ies::IES, - merkle_tree, + merkle_tree::{ + constraint as merkle_tree_constraint, ConfigConverter as ArkMerkleTreeConfigConverter, + Configuration as ArkMerkleTreeConfiguration, + }, prf::blake2s::{constraint::Blake2sVar, Blake2s}, }, }; @@ -35,7 +38,7 @@ use ark_bls12_381::Bls12_381; use ark_crypto_primitives::crh::pedersen::{constraints::CRHGadget, CRH}; use ark_ed_on_bls12_381::{constraints::EdwardsVar, EdwardsProjective, Fq}; use manta_accounting::{identity, transfer}; -use manta_crypto::{commitment::CommitmentScheme, PseudorandomFunctionFamily}; +use manta_crypto::{commitment::CommitmentScheme, merkle_tree, PseudorandomFunctionFamily}; use manta_util::rand::SeedIntoRng; use rand_chacha::ChaCha20Rng; @@ -68,6 +71,10 @@ pub type PedersenCommitmentVar = pedersen::constraint::PedersenCommitmentVar< PedersenCommitmentProjectiveCurveVar, >; +/// Arkworks Pedersen Commitment Scheme +type ArkPedersenCommitment = + CRH<PedersenCommitmentProjectiveCurve, PedersenCommitmentWindowParameters>; + /// Constraint Field pub type ConstraintField = Fq; @@ -81,15 +88,28 @@ pub type ProofSystem = Groth16<Bls12_381>; #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Configuration; -impl merkle_tree::Configuration for Configuration { - type LeafHash = CRH<PedersenCommitmentProjectiveCurve, PedersenCommitmentWindowParameters>; +impl ArkMerkleTreeConfiguration for Configuration { + type Leaf = [u8]; + type LeafHash = ArkPedersenCommitment; + type InnerHash = ArkPedersenCommitment; + type Height = u8; - type InnerHash = CRH<PedersenCommitmentProjectiveCurve, PedersenCommitmentWindowParameters>; + const HEIGHT: Self::Height = 20; +} - const HEIGHT: merkle_tree::Height = merkle_tree::Height::new(20); +impl merkle_tree::Configuration for Configuration { + type LeafHash = + <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::Configuration>::LeafHash; + type InnerHash = + <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::Configuration>::InnerHash; + type Height = + <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::Configuration>::Height; + + const HEIGHT: Self::Height = + <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::Configuration>::HEIGHT; } -impl merkle_tree::constraint::Configuration for Configuration { +impl merkle_tree_constraint::Configuration for Configuration { type ConstraintField = ConstraintField; type LeafHashVar = CRHGadget< diff --git a/manta-pay/src/accounting/ledger.rs b/manta-pay/src/accounting/ledger.rs index bf0259f14..2748b0110 100644 --- a/manta-pay/src/accounting/ledger.rs +++ b/manta-pay/src/accounting/ledger.rs @@ -17,12 +17,13 @@ //! Ledger Implementation // FIXME: How should we handle serdes? +// FIXME: Use "incremental merkle tree". use crate::{ accounting::config::{Configuration, ConstraintSystem, ProofSystem}, crypto::{ ies::EncryptedAsset, - merkle_tree::{self, MerkleTree}, + merkle_tree::{constraint as merkle_tree_constraint, ConfigConverter}, }, }; use alloc::{vec, vec::Vec}; @@ -35,6 +36,7 @@ use manta_crypto::{ constraint::{ self, reflection::HasAllocation, Allocation, Constant, ProofSystem as _, Variable, }, + merkle_tree::{self, MerkleTree}, set::{constraint::VerifiedSetVariable, ContainmentProof, Set, VerifiedSet}, }; use manta_util::{as_bytes, concatenate, into_array_unchecked}; @@ -49,22 +51,22 @@ type Utxo = identity::Utxo<Configuration>; type UtxoVar = identity::constraint::UtxoVar<Configuration>; /// UTXO Shard Root -type Root = merkle_tree::Root<Configuration>; +type Root = merkle_tree::Root<ConfigConverter<Configuration>>; /// UTXO Shard Root Variable -type RootVar = merkle_tree::constraint::RootVar<Configuration>; +type RootVar = merkle_tree_constraint::RootVar<Configuration>; /// UTXO Set Parameters -type Parameters = merkle_tree::Parameters<Configuration>; +type Parameters = merkle_tree::Parameters<ConfigConverter<Configuration>>; /// UTXO Set Parameters Variable -type ParametersVar = merkle_tree::constraint::ParametersVar<Configuration>; +type ParametersVar = merkle_tree_constraint::ParametersVar<Configuration>; /// UTXO Set Path -type Path = merkle_tree::Path<Configuration>; +type Path = merkle_tree::Path<ConfigConverter<Configuration>>; /// UTXO Set Path Variable -type PathVar = merkle_tree::constraint::PathVar<Configuration>; +type PathVar = merkle_tree_constraint::PathVar<Configuration>; /// UTXO Shard #[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] @@ -137,6 +139,7 @@ impl Set for UtxoSet { #[inline] fn try_insert(&mut self, item: Self::Item) -> Result<(), Self::Item> { + /* FIXME: let shard = &mut self.shards[Self::shard_index(&item)]; if shard.utxos.contains(&item) { return Err(item); @@ -149,6 +152,8 @@ impl Set for UtxoSet { } _ => Err(shard.utxos.pop().unwrap()), } + */ + todo!() } } @@ -171,7 +176,9 @@ impl VerifiedSet for UtxoSet { secret_witness: &Self::Secret, item: &Self::Item, ) -> bool { - self.parameters.verify(public_input, secret_witness, item) + // FIXME: Leaf should be `Utxo` not `[u8]`. + self.parameters + .verify_path(secret_witness, public_input, &as_bytes!(item)) } #[inline] @@ -180,6 +187,7 @@ impl VerifiedSet for UtxoSet { item: &Self::Item, ) -> Result<ContainmentProof<Self>, Self::ContainmentError> { // TODO: Return a more informative error. + /* FIXME: let utxos = &self.shards[Self::shard_index(item)].utxos; match utxos.iter().position(move |u| u == item) { Some(index) => MerkleTree::new(&self.parameters, utxos) @@ -188,6 +196,8 @@ impl VerifiedSet for UtxoSet { .ok_or(()), _ => Err(()), } + */ + todo!() } } diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs index ff1018cb3..475bb4c10 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs @@ -136,6 +136,7 @@ where } } +/* FIXME: #[cfg(test)] mod test { use crate::accounting::{ @@ -202,3 +203,4 @@ mod test { */ } } +*/ diff --git a/manta-pay/src/crypto/merkle_tree/mod.rs b/manta-pay/src/crypto/merkle_tree/mod.rs index 394993b8a..578dcf367 100644 --- a/manta-pay/src/crypto/merkle_tree/mod.rs +++ b/manta-pay/src/crypto/merkle_tree/mod.rs @@ -14,10 +14,9 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Merkle Tree Implementations - -// FIXME: Move to `manta_crypto::merkle_tree` implementation! +//! Arkworks Merkle Tree Wrappers +// FIXME: Remove this old implementation: // NOTE: This is meant to be a full implementation of the incremental merkle tree type suitable // for merging into arkworks itself. Therefore, even if we don't use all of the // functionality available in this module, we want to preserve the code anyway. @@ -27,311 +26,158 @@ mod incremental; use alloc::{vec, vec::Vec}; use ark_crypto_primitives::{ crh::{TwoToOneCRH, CRH}, - merkle_tree::{Config, MerkleTree as ArkMerkleTree, Path as ArkPath, TwoToOneDigest}, + merkle_tree::Config, }; +use ark_ff::{to_bytes, ToBytes}; use core::marker::PhantomData; -use manta_crypto::set::{ContainmentProof, VerifiedSet}; -use manta_util::{as_bytes, rand::SizedRng, Concat}; -use rand::{ - distributions::{Distribution, Standard}, - RngCore, -}; - -/// Merkle Tree Height Type -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Height(u16); - -impl Height { - /// Builds a Merkle Tree [`Height`] whenever `height >= 2`. - #[inline] - pub const fn new(height: u16) -> Self { - let height = Self(height); - height.inner(); - height - } - - /// Returns the height as a `u16`. - #[inline] - pub const fn get(&self) -> u16 { - self.0 - } - - /// Returns the inner height as a `u16`. - #[inline] - pub const fn inner(&self) -> u16 { - self.0 - 2 - } -} - -/// Merkle Tree Configuration -pub trait Configuration { - /// Leaf Hash Type - type LeafHash: CRH; - - /// Inner Hash Type - type InnerHash: TwoToOneCRH; - - /// Merkle Tree Height - const HEIGHT: Height; -} - -/// Computes the Merkle Tree capacity given the `height`. -#[inline] -pub const fn capacity(height: Height) -> usize { - 2usize.pow(height.0 as u32) -} - -/// Computes the necessary padding required to fill the capacity of a Merkle Tree with the -/// given `height`. -/// -/// Returns `None` if `length` is larger than the capacity of the tree. -#[inline] -pub const fn padding_length(height: Height, length: usize) -> Option<usize> { - let capacity = capacity(height); - if length > capacity { - return None; - } - Some(capacity - length) -} +use manta_crypto::merkle_tree::{self, InnerHash, LeafHash}; +use manta_util::{as_bytes, Concat}; -/// Arkworks Configuration Converter -/// -/// Given any `C: Configuration`, this struct can be used as `ArkConfigConverter<C>` instead of `C` -/// in places where we need an implementation of the arkworks [`Config`] trait. -/// -/// This `struct` is meant only to be used in place of the type `C`, so any values of this `struct` -/// have no meaning. +/// Arkworks Leaf Hash Converter #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct ArkConfigConverter<C>(PhantomData<C>) +pub struct LeafHashConverter<L, LH>(PhantomData<L>, PhantomData<LH>) where - C: Configuration; + L: Concat<Item = u8> + ?Sized, + LH: CRH; -impl<C> Config for ArkConfigConverter<C> +impl<L, LH> LeafHash for LeafHashConverter<L, LH> where - C: Configuration, + L: Concat<Item = u8> + ?Sized, + LH: CRH, { - type LeafHash = C::LeafHash; - type TwoToOneHash = C::InnerHash; -} - -/// Leaf Hash Type -type LeafHash<C> = <C as Configuration>::LeafHash; + type Leaf = L; -/// Inner Hash Type -type InnerHash<C> = <C as Configuration>::InnerHash; + type Parameters = LH::Parameters; -/// Leaf Hash Parameters Type -type LeafHashParameters<C> = <LeafHash<C> as CRH>::Parameters; + type Output = LH::Output; -/// Inner Hash Parameters Type -type InnerHashParameters<C> = <InnerHash<C> as TwoToOneCRH>::Parameters; + #[inline] + fn digest(parameters: &Self::Parameters, leaf: &Self::Leaf) -> Self::Output { + LH::evaluate(parameters, &as_bytes!(leaf)) + .expect("Leaf digest computation is not allowed to fail.") + } +} -/// Merkle Tree Parameters +/// Arkworks Inner Hash Converter #[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""))] -pub struct Parameters<C> +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct InnerHashConverter<L, LH, IH>(PhantomData<L>, PhantomData<(LH, IH)>) where - C: Configuration, -{ - /// Leaf Hash Parameters - pub leaf: LeafHashParameters<C>, + L: Concat<Item = u8> + ?Sized, + LH: CRH, + IH: TwoToOneCRH; - /// Inner Hash Parameters - pub inner: InnerHashParameters<C>, -} - -impl<C> Parameters<C> +impl<L, LH, IH> InnerHashConverter<L, LH, IH> where - C: Configuration, + L: Concat<Item = u8> + ?Sized, + LH: CRH, + IH: TwoToOneCRH, { - /// Builds a new [`Parameters`] from `leaf` and `inner` parameters. + /// Evaluates the inner hash function for `IH` using `parameters`. #[inline] - pub fn new(leaf: LeafHashParameters<C>, inner: InnerHashParameters<C>) -> Self { - Self { leaf, inner } - } - - /// Verifies that `path` constitutes a proof that `item` is contained in the Merkle Tree - /// with the given `root`. - #[inline] - pub fn verify<T>(&self, root: &Root<C>, path: &Path<C>, item: &T) -> bool + fn evaluate<T>(parameters: &IH::Parameters, lhs: &T, rhs: &T) -> IH::Output where - T: Concat<Item = u8>, + T: ToBytes, { - path.0 - .verify(&self.leaf, &self.inner, &root.0, &as_bytes!(item)) - .expect("As of arkworks 0.3.0, this never fails.") + IH::evaluate( + parameters, + &to_bytes!(lhs).expect("Conversion to bytes is not allowed to fail."), + &to_bytes!(rhs).expect("Conversion to bytes is not allowed to fail."), + ) + .expect("Inner digest computation is not allowed to fail.") } } -impl<C> Distribution<Parameters<C>> for Standard +impl<L, LH, IH> InnerHash for InnerHashConverter<L, LH, IH> where - C: Configuration, + L: Concat<Item = u8> + ?Sized, + LH: CRH, + IH: TwoToOneCRH, { - #[inline] - fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> Parameters<C> { - Parameters { - leaf: LeafHash::<C>::setup(&mut SizedRng(rng)) - .expect("Sampling is not allowed to fail."), - inner: InnerHash::<C>::setup(&mut SizedRng(rng)) - .expect("Sampling is not allowed to fail."), - } - } -} - -/// Merkle Tree Root Inner Type -type RootInnerType<C> = TwoToOneDigest<ArkConfigConverter<C>>; - -/// Merkle Tree Root -#[derive(derivative::Derivative)] -#[derivative(Clone, Debug, Default, Eq, Hash, PartialEq)] -pub struct Root<C>(RootInnerType<C>) -where - C: Configuration; - -/// Merkle Tree Path Inner Type -type PathInnerType<C> = ArkPath<ArkConfigConverter<C>>; - -/// Merkle Tree Path -#[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""))] -pub struct Path<C>(PathInnerType<C>) -where - C: Configuration; + type LeafHash = LeafHashConverter<L, LH>; -/// Merkle Tree Inner Type -type MerkleTreeInnerType<C> = ArkMerkleTree<ArkConfigConverter<C>>; + type Parameters = IH::Parameters; -/// Merkle Tree -#[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""))] -pub struct MerkleTree<C>(MerkleTreeInnerType<C>) -where - C: Configuration; + type Output = IH::Output; -impl<C> MerkleTree<C> -where - C: Configuration, -{ - /// Builds a new [`MerkleTree`]. #[inline] - pub fn new<T>(parameters: &Parameters<C>, leaves: &[T]) -> Option<Self> - where - T: Concat<Item = u8>, - { - // FIXME: Add additional padding so we can be compatible with IMT. - - let leaves = leaves - .iter() - .map(move |leaf| as_bytes!(leaf)) - .collect::<Vec<_>>(); - - Some(Self( - ArkMerkleTree::new(&parameters.leaf, &parameters.inner, &leaves).ok()?, - )) + fn join(parameters: &Self::Parameters, lhs: &Self::Output, rhs: &Self::Output) -> Self::Output { + Self::evaluate(parameters, lhs, rhs) } - /// Computes the [`Root`] of the [`MerkleTree`] built from the `leaves`. #[inline] - pub fn new_root<T>(parameters: &Parameters<C>, leaves: &[T]) -> Option<Root<C>> - where - T: Concat<Item = u8>, - { - Some(Self::new(parameters, leaves)?.root()) + fn join_leaves( + parameters: &Self::Parameters, + lhs: &<Self::LeafHash as LeafHash>::Output, + rhs: &<Self::LeafHash as LeafHash>::Output, + ) -> Self::Output { + Self::evaluate(parameters, lhs, rhs) } +} - /// Returns the capacity of this merkle tree. - #[inline] - pub fn capacity(&self) -> usize { - capacity(C::HEIGHT) - } +/// Arkworks Merkle Tree Configuration +pub trait Configuration { + /// Leaf Type + type Leaf: Concat<Item = u8> + ?Sized; - /// Returns the height of this merkle tree. - #[inline] - pub fn height(&self) -> Height { - C::HEIGHT - } + /// Leaf Hash Type + type LeafHash: CRH; - /// Returns the [`Root`] of this merkle tree. - #[inline] - pub fn root(&self) -> Root<C> { - Root(self.0.root()) - } + /// Inner Hash Type + type InnerHash: TwoToOneCRH; - /// Builds a containment proof (i.e. merkle root and path) for the leaf at the given `index`. - #[inline] - pub fn get_containment_proof<S>(&self, index: usize) -> Option<ContainmentProof<S>> - where - S: VerifiedSet<Public = Root<C>, Secret = Path<C>>, - { - Some(ContainmentProof::new( - self.root(), - Path(self.0.generate_proof(index).ok()?), - )) - } + /// Merkle Tree Height Type + type Height: Copy + Into<usize>; + + /// Merkle Tree Height + const HEIGHT: Self::Height; } -/// Incremental Merkle Tree +/// Configuration Converter +/// +/// Given any `L` and [`C: Configuration`](Configuration), this struct can be used as +/// `ConfigConverter<L, C>` instead of `C` in places where we need an implementation of the +/// `arkworks` [`Config`] trait or the `manta_crypto` [`Configuration`](merkle_tree::Configuration) +/// trait. +/// +/// This `struct` is meant only to be used in place of the type `C`, so any values of this `struct` +/// have no meaning. #[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""))] -pub struct IncrementalMerkleTree<C>(incremental::IncrementalMerkleTree<ArkConfigConverter<C>>) +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct ConfigConverter<C>(PhantomData<C>) where C: Configuration; -impl<C> IncrementalMerkleTree<C> +impl<C> Configuration for ConfigConverter<C> where C: Configuration, { - /// Builds a new [`IncrementalMerkleTree`]. - #[inline] - pub fn new(parameters: &Parameters<C>) -> Self { - Self(incremental::IncrementalMerkleTree::blank( - &parameters.leaf, - &parameters.inner, - C::HEIGHT.0 as usize, - )) - } - - /// Returns the length of this incremental merkle tree. - #[inline] - pub fn len(&self) -> usize { - todo!() - } - - /// Returns `true` if this incremental merkle tree is empty. - #[inline] - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } + type Leaf = C::Leaf; + type LeafHash = C::LeafHash; + type InnerHash = C::InnerHash; + type Height = C::Height; - /// Returns the capacity of this incremental merkle tree. - #[inline] - pub fn capacity(&self) -> usize { - capacity(C::HEIGHT) - } + const HEIGHT: Self::Height = C::HEIGHT; +} - /// Returns the height of this incremental merkle tree. - #[inline] - pub fn height(&self) -> Height { - C::HEIGHT - } +impl<C> merkle_tree::Configuration for ConfigConverter<C> +where + C: Configuration, +{ + type LeafHash = LeafHashConverter<C::Leaf, C::LeafHash>; + type InnerHash = InnerHashConverter<C::Leaf, C::LeafHash, C::InnerHash>; + type Height = C::Height; - /// Appends an element to the incremental merkle tree, returning `false` if it has already - /// reached its capacity and can no longer accept new elements. - #[inline] - pub fn append<T>(&mut self, leaf: &T) -> bool - where - T: Concat<Item = u8>, - { - let _ = leaf; - todo!() - } + const HEIGHT: Self::Height = C::HEIGHT; +} - /// Returns the [`Root`] of this incremental merkle tree. - #[inline] - pub fn root(&self) -> Root<C> { - Root(self.0.root().clone()) - } +impl<C> Config for ConfigConverter<C> +where + C: Configuration, +{ + type LeafHash = C::LeafHash; + type TwoToOneHash = C::InnerHash; } /// Merkle Tree Constraint System Variables @@ -340,13 +186,14 @@ pub mod constraint { use crate::crypto::constraint::arkworks::{empty, full, ArkConstraintSystem}; use ark_crypto_primitives::{ crh::constraints::{CRHGadget, TwoToOneCRHGadget}, - merkle_tree::constraints::PathVar as ArkPathVar, + merkle_tree::{constraints::PathVar as ArkPathVar, Path as ArkPath}, }; use ark_ff::Field; use ark_r1cs_std::{alloc::AllocVar, boolean::Boolean, eq::EqGadget, uint8::UInt8}; use ark_relations::ns; - use manta_crypto::constraint::{ - reflection::HasAllocation, Allocation, Constant, Public, Secret, Variable, + use manta_crypto::{ + constraint::{reflection::HasAllocation, Allocation, Constant, Public, Secret, Variable}, + merkle_tree::{Parameters, Path, Root}, }; /// Merkle Tree Constraint System Configuration @@ -368,18 +215,22 @@ pub mod constraint { pub type ContraintSystem<C> = ArkConstraintSystem<ConstraintField<C>>; /// Leaf Hash Type - type LeafHashVar<C> = <C as Configuration>::LeafHashVar; + pub type LeafHashVar<C> = <C as Configuration>::LeafHashVar; /// Inner Hash Type - type InnerHashVar<C> = <C as Configuration>::InnerHashVar; + pub type InnerHashVar<C> = <C as Configuration>::InnerHashVar; /// Leaf Hash Parameters Type - type LeafHashParametersVar<C> = - <LeafHashVar<C> as CRHGadget<LeafHash<C>, ConstraintField<C>>>::ParametersVar; + pub type LeafHashParametersVar<C> = <LeafHashVar<C> as CRHGadget< + <C as super::Configuration>::LeafHash, + ConstraintField<C>, + >>::ParametersVar; /// Inner Hash Parameters Type - type InnerHashParametersVar<C> = - <InnerHashVar<C> as TwoToOneCRHGadget<InnerHash<C>, ConstraintField<C>>>::ParametersVar; + pub type InnerHashParametersVar<C> = <InnerHashVar<C> as TwoToOneCRHGadget< + <C as super::Configuration>::InnerHash, + ConstraintField<C>, + >>::ParametersVar; /// Merkle Tree Parameters Variable #[derive(derivative::Derivative)] @@ -405,30 +256,30 @@ pub mod constraint { Self { leaf, inner } } - /// Verifies that `path` constitutes a proof that `item` is contained in the Merkle Tree + /// Verifies that `path` constitutes a proof that `leaf` is contained in the Merkle Tree /// with the given `root`. #[inline] pub fn verify( &self, root: &RootVar<C>, path: &PathVar<C>, - item: &[UInt8<ConstraintField<C>>], + leaf: &[UInt8<ConstraintField<C>>], ) -> Boolean<ConstraintField<C>> { path.0 - .verify_membership(&self.leaf, &self.inner, &root.0, &item) + .verify_membership(&self.leaf, &self.inner, &root.0, &leaf) .expect("This is not allowed to fail.") } - /// Asserts that `path` constitutes a proof that `item` is contained in the Merkle Tree + /// Asserts that `path` constitutes a proof that `leaf` is contained in the Merkle Tree /// with the given `root`. #[inline] pub fn assert_verified( &self, root: &RootVar<C>, path: &PathVar<C>, - item: &[UInt8<ConstraintField<C>>], + leaf: &[UInt8<ConstraintField<C>>], ) { - self.verify(root, path, item) + self.verify(root, path, leaf) .enforce_equal(&Boolean::TRUE) .expect("This is not allowed to fail.") } @@ -438,7 +289,7 @@ pub mod constraint { where C: Configuration, { - type Type = Parameters<C>; + type Type = Parameters<ConfigConverter<C>>; type Mode = Constant; @@ -463,7 +314,7 @@ pub mod constraint { } } - impl<C> HasAllocation<ContraintSystem<C>> for Parameters<C> + impl<C> HasAllocation<ContraintSystem<C>> for Parameters<ConfigConverter<C>> where C: Configuration, { @@ -471,9 +322,14 @@ pub mod constraint { type Mode = Constant; } + /// Merkle Tree Root Inner Type + type RootInnerType<C> = <<C as super::Configuration>::InnerHash as TwoToOneCRH>::Output; + /// Merkle Tree Root Variable Inner Type - type RootVarInnerType<C> = - <InnerHashVar<C> as TwoToOneCRHGadget<InnerHash<C>, ConstraintField<C>>>::OutputVar; + type RootVarInnerType<C> = <InnerHashVar<C> as TwoToOneCRHGadget< + <C as super::Configuration>::InnerHash, + ConstraintField<C>, + >>::OutputVar; /// Merkle Tree Root Variable #[derive(derivative::Derivative)] @@ -486,7 +342,7 @@ pub mod constraint { where C: Configuration, { - type Type = Root<C>; + type Type = Root<ConfigConverter<C>>; type Mode = Public; @@ -511,7 +367,7 @@ pub mod constraint { } } - impl<C> HasAllocation<ContraintSystem<C>> for Root<C> + impl<C> HasAllocation<ContraintSystem<C>> for Root<ConfigConverter<C>> where C: Configuration, { @@ -519,20 +375,49 @@ pub mod constraint { type Mode = Public; } + /// Merkle Tree Path Inner Type + type PathInnerType<C> = ArkPath<ConfigConverter<C>>; + /// Merkle Tree Path Variable Inner Type type PathVarInnerType<C> = - ArkPathVar<ArkConfigConverter<C>, LeafHashVar<C>, InnerHashVar<C>, ConstraintField<C>>; + ArkPathVar<ConfigConverter<C>, LeafHashVar<C>, InnerHashVar<C>, ConstraintField<C>>; /// Merkle Tree Path Variable pub struct PathVar<C>(PathVarInnerType<C>) where C: Configuration; + impl<C> PathVar<C> + where + C: Configuration, + { + /// Converts a [`Path`] to a [`PathInnerType`]. + #[inline] + fn convert_path(path: &Path<ConfigConverter<C>>) -> PathInnerType<C> { + // FIXME: Does the `auth_path` need to be reversed? + PathInnerType { + leaf_sibling_hash: path.sibling_digest.clone(), + auth_path: path.inner_path.clone(), + leaf_index: path.leaf_index.0, + } + } + + /// Builds a default [`PathInnerType`] for use as an unknown variable value. + #[inline] + fn default_path() -> PathInnerType<C> { + PathInnerType { + leaf_sibling_hash: Default::default(), + auth_path: vec![Default::default(); C::HEIGHT.into() - 2], + leaf_index: Default::default(), + } + } + } + impl<C> Variable<ContraintSystem<C>> for PathVar<C> where C: Configuration, { - type Type = Path<C>; + type Type = Path<ConfigConverter<C>>; type Mode = Secret; @@ -545,7 +430,7 @@ pub mod constraint { match allocation.known() { Some((this, _)) => PathVarInnerType::new_witness( ns!(cs.cs, "path variable secret witness"), - full(&this.0), + full(&Self::convert_path(this)), ), _ => { // FIXME: We can't use `empty` here. What do we do? @@ -560,11 +445,7 @@ pub mod constraint { // PathVarInnerType::new_witness( ns!(cs.cs, "path variable secret witness"), - full(PathInnerType { - leaf_sibling_hash: Default::default(), - auth_path: vec![Default::default(); C::HEIGHT.inner() as usize], - leaf_index: Default::default(), - }), + full(&Self::default_path()), ) } } @@ -573,7 +454,7 @@ pub mod constraint { } } - impl<C> HasAllocation<ContraintSystem<C>> for Path<C> + impl<C> HasAllocation<ContraintSystem<C>> for Path<ConfigConverter<C>> where C: Configuration, { From a5b5736ccff1497087392c55230030bb7d7287b0 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 22 Sep 2021 14:08:55 -0400 Subject: [PATCH 052/275] WIP: clean up merkle tree --- manta-crypto/src/merkle_tree.rs | 327 +++++++++++++++++++++----------- 1 file changed, 220 insertions(+), 107 deletions(-) diff --git a/manta-crypto/src/merkle_tree.rs b/manta-crypto/src/merkle_tree.rs index fa45c63d7..71e9e0c73 100644 --- a/manta-crypto/src/merkle_tree.rs +++ b/manta-crypto/src/merkle_tree.rs @@ -26,10 +26,9 @@ extern crate alloc; use alloc::vec::Vec; use core::{ - borrow::{Borrow, BorrowMut}, fmt::Debug, hash::Hash, - iter::Rev, + iter::{FusedIterator, Rev}, ops::{Add, Range, Sub}, }; @@ -127,7 +126,7 @@ where /// Returns an iterator over the depth of an inner path in reverse order, from the leaves of the /// tree to the root. #[inline] -pub fn path_iter<C>() -> Rev<Range<usize>> +pub fn depth_iter<C>() -> Rev<Range<usize>> where C: Configuration + ?Sized, { @@ -199,13 +198,6 @@ pub enum Parity { Right, } -impl Default for Parity { - #[inline] - fn default() -> Self { - Self::Left - } -} - impl Parity { /// Computes the [`Parity`] of the given `index`. #[inline] @@ -242,6 +234,15 @@ impl Parity { } } + /// Returns the arguments in the order according to the parity of `self`. + #[inline] + pub fn order<T>(&self, lhs: T, rhs: T) -> (T, T) { + match self { + Self::Left => (lhs, rhs), + Self::Right => (rhs, lhs), + } + } + /// Combines two inner digests into a new inner digest using `parameters`, swapping the order /// of `lhs` and `rhs` depending on the parity of `self` in its subtree. #[inline] @@ -254,14 +255,12 @@ impl Parity { where C: Configuration + ?Sized, { - match self { - Self::Left => C::InnerHash::join(&parameters.inner, lhs, rhs), - Self::Right => C::InnerHash::join(&parameters.inner, rhs, lhs), - } + let (lhs, rhs) = self.order(lhs, rhs); + C::InnerHash::join(&parameters.inner, lhs, rhs) } - /// Combines two leaf digests into a new inner digest using `parameters`, choosing the left - /// pair `(center, rhs)` if `self` has left parity or choosing the right pair `(lhs, center)` + /// Combines two leaf digests into a new inner digest using `parameters`, choosing the right + /// pair `(center, rhs)` if `self` has left parity or choosing the left pair `(lhs, center)` /// if `self` has right parity. #[inline] pub fn join_opposite_pair<C>( @@ -292,10 +291,15 @@ impl Parity { where C: Configuration + ?Sized, { - match self { - Self::Left => C::InnerHash::join_leaves(&parameters.inner, lhs, rhs), - Self::Right => C::InnerHash::join_leaves(&parameters.inner, rhs, lhs), - } + let (lhs, rhs) = self.order(lhs, rhs); + C::InnerHash::join_leaves(&parameters.inner, lhs, rhs) + } +} + +impl Default for Parity { + #[inline] + fn default() -> Self { + Self::Left } } @@ -328,9 +332,15 @@ impl Node { /// Returns the [`Node`] which is the sibling to `self`. #[inline] pub fn sibling(&self) -> Self { + self.parity().map(move || *self + 1, move || *self - 1) + } + + /// Returns `self` with its sibling in parity order. + #[inline] + pub fn with_sibling(self) -> (Self, Self) { match self.parity() { - Parity::Left => *self + 1, - Parity::Right => *self - 1, + Parity::Left => (self, self + 1), + Parity::Right => (self - 1, self), } } @@ -347,6 +357,12 @@ impl Node { *self } + /// Returns an iterator over the parents of `self`. + #[inline] + pub fn parents(&self) -> NodeParents { + NodeParents { index: *self } + } + /// Combines two inner digests into a new inner digest using `parameters`, swapping the order /// of `lhs` and `rhs` depending on the location of `self` in its subtree. #[inline] @@ -362,8 +378,8 @@ impl Node { self.parity().join(parameters, lhs, rhs) } - /// Combines two leaf digests into a new inner digest using `parameters`, choosing the left - /// pair `(center, rhs)` if `self` has left parity or choosing the right pair `(lhs, center)` + /// Combines two leaf digests into a new inner digest using `parameters`, choosing the right + /// pair `(center, rhs)` if `self` has left parity or choosing the left pair `(lhs, center)` /// if `self` has right parity. #[inline] pub fn join_opposite_pair<C>( @@ -461,6 +477,63 @@ where } } +/// Node Parent Iterator +/// +/// An iterator over the parents of a [`Node`]. +/// +/// This struct is created by the [`parents`](Node::parents) method on [`Node`]. +/// See its documentation for more. +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +pub struct NodeParents { + /// Current Index + index: Node, +} + +impl NodeParents { + /// Stops the iterator and returns the current node index. + #[inline] + pub fn stop(self) -> Node { + self.index + } + + /// Returns the sibling of the current parent node. + #[inline] + pub fn sibling(&self) -> Node { + self.index.sibling() + } +} + +impl AsRef<Node> for NodeParents { + #[inline] + fn as_ref(&self) -> &Node { + &self.index + } +} + +// TODO: Add all methods which can be optimized. +impl Iterator for NodeParents { + type Item = Node; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + Some(self.index.into_parent()) + } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + (usize::MAX, None) + } + + #[inline] + fn last(self) -> Option<Self::Item> { + // NOTE: Although this iterator can never be completed, it has a well-defined + // final element "at infinity". + Some(Default::default()) + } +} + +impl FusedIterator for NodeParents {} + /// Merkle Tree Parameters #[derive(derivative::Derivative)] #[derivative( @@ -531,6 +604,26 @@ pub struct Root<C>( where C: Configuration + ?Sized; +impl<C> AsRef<InnerDigest<C>> for Root<C> +where + C: Configuration + ?Sized, +{ + #[inline] + fn as_ref(&self) -> &InnerDigest<C> { + &self.0 + } +} + +impl<C> AsMut<InnerDigest<C>> for Root<C> +where + C: Configuration + ?Sized, +{ + #[inline] + fn as_mut(&mut self) -> &mut InnerDigest<C> { + &mut self.0 + } +} + /// Merkle Tree Path #[derive(derivative::Derivative)] #[derivative( @@ -622,16 +715,26 @@ where } /// Merkle Tree +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "Parameters<C>: Clone, T: Clone"), + Copy(bound = "Parameters<C>: Copy, T: Copy"), + Debug(bound = "Parameters<C>: Debug, T: Debug"), + Default(bound = "Parameters<C>: Default, T: Default"), + Eq(bound = "Parameters<C>: Eq, T: Eq"), + Hash(bound = "Parameters<C>: Hash, T: Hash"), + PartialEq(bound = "Parameters<C>: PartialEq, T: PartialEq") +)] pub struct MerkleTree<C, T> where C: Configuration + ?Sized, T: Tree<C>, { - /// Merkle Tree Parameters - parameters: Parameters<C>, - /// Underlying Tree Structure tree: T, + + /// Merkle Tree Parameters + parameters: Parameters<C>, } impl<C, T> MerkleTree<C, T> @@ -642,23 +745,26 @@ where /// Builds a new [`MerkleTree`]. #[inline] pub fn new(parameters: Parameters<C>) -> Self { - Self { - tree: T::new(&parameters), - parameters, - } + Self::from_tree(T::new(&parameters), parameters) } - /// Builds a new merkle tree with the given `leaves`. + /// Builds a new [`MerkleTree`] with the given `leaves`. #[inline] pub fn from_leaves<'l, L>(parameters: Parameters<C>, leaves: L) -> Option<Self> where Leaf<C>: 'l, L: IntoIterator<Item = &'l Leaf<C>>, { - Some(Self { - tree: T::from_leaves(&parameters, leaves)?, + Some(Self::from_tree( + T::from_leaves(&parameters, leaves)?, parameters, - }) + )) + } + + /// Builds a new [`MerkleTree`] from a pre-constructed `tree` and `parameters`. + #[inline] + pub fn from_tree(tree: T, parameters: Parameters<C>) -> Self { + Self { tree, parameters } } /// Returns a reference to the parameters used by this merkle tree. @@ -715,28 +821,6 @@ where } } -impl<C, T> Borrow<T> for MerkleTree<C, T> -where - C: Configuration + ?Sized, - T: Tree<C>, -{ - #[inline] - fn borrow(&self) -> &T { - &self.tree - } -} - -impl<C, T> BorrowMut<T> for MerkleTree<C, T> -where - C: Configuration + ?Sized, - T: Tree<C>, -{ - #[inline] - fn borrow_mut(&mut self) -> &mut T { - &mut self.tree - } -} - /// Full Merkle Tree Storage pub mod full { use super::*; @@ -758,6 +842,7 @@ pub mod full { Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default"), Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), + Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") )] pub struct Full<C> @@ -783,41 +868,81 @@ pub mod full { &self.leaf_digests } + /// Returns a reference to the root inner digest, returning `None` if the tree is empty. + #[inline] + pub fn root(&self) -> Option<&InnerDigest<C>> { + self.inner_digests.get(&0) + } + /// Returns the sibling leaf node to `index`. #[inline] - fn sibling_leaf(&self, index: Node) -> Option<&LeafDigest<C>> { + fn get_leaf_sibling(&self, index: Node) -> Option<&LeafDigest<C>> { + // TODO: Add `Index` methods to accept `Node` as indices. self.leaf_digests.get(index.sibling().0) } - /// Computes the index into the `inner_digests` map for a node of the given `depth` and `index`. + /// Computes the starting index for the given `depth` in the `inner_digests` map. + /// + /// # Note + /// + /// The `depth` of the tree tracks the inner nodes not including the root. The root of the + /// tree is at `depth := -1` and `index := 0`. + #[inline] + fn depth_starting_index(depth: usize) -> usize { + (1 << (depth + 1)) - 1 + } + + /// Computes the index into the `inner_digests` map for a node of the given `depth` and + /// `index`. + /// + /// # Note + /// + /// The `depth` of the tree tracks the inner nodes not including the root. The root of the + /// tree is at `depth := -1` and `index := 0`. #[inline] fn inner_digest_index(depth: usize, index: Node) -> usize { - (1 << (depth + 1)) + index.0 - 1 + Self::depth_starting_index(depth) + index.0 } /// Returns the inner digest at the given `depth` and `index` of the merkle tree. /// /// # Note /// - /// The `depth` of the tree tracks the inner nodes not including the root. The root of the tree - /// is at `depth := -1` and `index := 0`. + /// The `depth` of the tree tracks the inner nodes not including the root. The root of the + /// tree is at `depth := -1` and `index := 0`. #[inline] fn get_inner_digest(&self, depth: usize, index: Node) -> Option<&InnerDigest<C>> { self.inner_digests .get(&Self::inner_digest_index(depth, index)) } - /// Sets the inner digest at the given `depth` and `index` of the merkle tree to the - /// `inner_digest` value. + /// Sets the new `inner_digest` at `(depth, index)` in the inner digest map, and returns + /// the back a reference to `inner_digest` and its sibling in the tree in parity order. /// /// # Note /// - /// The `depth` of the tree tracks the inner nodes not including the root. The root of the tree - /// is at `depth := -1` and `index := 0`. + /// The `depth` of the tree tracks the inner nodes not including the root. The root of the + /// tree is at `depth := -1` and `index := 0`. #[inline] - fn set_inner_digest(&mut self, depth: usize, index: Node, inner_digest: InnerDigest<C>) { + fn set_and_get_inner_pair<'s>( + &'s mut self, + depth: usize, + index: Node, + inner_digest: InnerDigest<C>, + default: &'s InnerDigest<C>, + ) -> (&'s InnerDigest<C>, &'s InnerDigest<C>) { + let depth_starting_index = Self::depth_starting_index(depth); self.inner_digests - .insert(Self::inner_digest_index(depth, index), inner_digest); + .insert(depth_starting_index + index.0, inner_digest); + let (lhs_index, rhs_index) = index.with_sibling(); + ( + self.inner_digests + .get(&(depth_starting_index + lhs_index.0)) + .unwrap_or(default), + self.inner_digests + .get(&(depth_starting_index + rhs_index.0)) + .unwrap_or(default), + ) } /// Computes the inner path of a node starting at the leaf given by `index`. @@ -826,7 +951,7 @@ pub mod full { where InnerDigest<C>: Clone, { - path_iter::<C>() + depth_iter::<C>() .map(move |depth| { self.get_inner_digest(depth, index.into_parent().sibling()) .map(Clone::clone) @@ -835,29 +960,24 @@ pub mod full { .collect() } - /// Computes the root of the merkle tree, modifying the inner tree in-place, starting at the - /// leaf given by `index`. + /// Computes the root of the merkle tree, modifying the inner tree in-place, starting at + /// the leaf given by `index`. #[inline] fn compute_root( &mut self, parameters: &Parameters<C>, mut index: Node, base: InnerDigest<C>, - ) -> InnerDigest<C> - where - InnerDigest<C>: Clone, - { - // TODO: Use `core::lazy::Lazy` when it is stabilized. + ) -> InnerDigest<C> { let default_inner_digest = Default::default(); - path_iter::<C>().fold(base, |acc, depth| { - // FIXME: Get rid of this clone. - self.set_inner_digest(depth, index.into_parent(), acc.clone()); - index.join( - parameters, - &acc, - self.get_inner_digest(depth, index.sibling()) - .unwrap_or(&default_inner_digest), - ) + depth_iter::<C>().fold(base, move |acc, depth| { + let (lhs, rhs) = self.set_and_get_inner_pair( + depth, + index.into_parent(), + acc, + &default_inner_digest, + ); + parameters.join(lhs, rhs) }) } } @@ -875,10 +995,7 @@ pub mod full { #[inline] fn new(parameters: &Parameters<C>) -> Self { let _ = parameters; - Self { - leaf_digests: Default::default(), - inner_digests: Default::default(), - } + Default::default() } #[inline] @@ -889,12 +1006,7 @@ pub mod full { #[inline] fn root(&self, parameters: &Parameters<C>) -> Root<C> { let _ = parameters; - Root( - self.inner_digests - .get(&0) - .map(Clone::clone) - .unwrap_or_default(), - ) + Root(self.root().map(Clone::clone).unwrap_or_default()) } #[inline] @@ -910,7 +1022,7 @@ pub mod full { let leaf_index = Node(query); Ok(Path::new( leaf_index, - self.sibling_leaf(leaf_index) + self.get_leaf_sibling(leaf_index) .map(Clone::clone) .unwrap_or_default(), self.compute_inner_path(leaf_index), @@ -931,7 +1043,8 @@ pub mod full { leaf_index.join_leaves( parameters, &leaf_digest, - self.sibling_leaf(leaf_index).unwrap_or(&Default::default()), + self.get_leaf_sibling(leaf_index) + .unwrap_or(&Default::default()), ), ); self.inner_digests.insert(0, root); @@ -951,8 +1064,8 @@ pub mod latest_node { /// Path Query Type /// - /// Since the [`LatestNode`] tree only stores one node and one path, we can only query the tree - /// for the current path. + /// Since the [`LatestNode`] tree only stores one node and one path, we can only query the + /// tree for the current path. #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Current; @@ -963,6 +1076,7 @@ pub mod latest_node { Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default"), Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), + Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") )] pub struct LatestNode<C> @@ -1038,11 +1152,7 @@ pub mod latest_node { #[inline] fn new(parameters: &Parameters<C>) -> Self { let _ = parameters; - Self { - leaf_digest: Default::default(), - path: Default::default(), - root: Default::default(), - } + Default::default() } #[inline] @@ -1103,6 +1213,8 @@ pub mod latest_node { &self.path.sibling_digest, ); + // TODO: Mutate the path in place. + let inner_path = self .path .inner_path @@ -1117,11 +1229,12 @@ pub mod latest_node { ); digest.clone() } else { - let next_inner_path_digest = next_index - .parity() + let next_index_parity = next_index.parity(); + + let next_inner_path_digest = next_index_parity .map(|| default_inner_digest.clone(), || prev_digest.clone()); - accumulator = next_index.join_opposite_pair( + accumulator = next_index_parity.join_opposite_pair( parameters, &prev_digest, &accumulator, From 52e52f2344c758ce55ebcf4eb706e2fd88315043 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 22 Sep 2021 18:40:26 -0400 Subject: [PATCH 053/275] WIP: fixing ledger/wallet abstraction --- manta-accounting/src/wallet.rs | 8 ++++- manta-crypto/src/merkle_tree.rs | 34 +++++++++++++------ manta-pay/src/accounting/ledger.rs | 43 ++++++++++++++----------- manta-pay/src/crypto/merkle_tree/mod.rs | 3 +- 4 files changed, 58 insertions(+), 30 deletions(-) diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index b3e724efd..d8989f552 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -334,12 +334,18 @@ where where L: Ledger, { - // TODO: pull updates from the ledger + // TODO: Pull updates from the ledger: // 1. Download the new encrypted notes and try to decrypt them using the latest // keys that haven't been used. // 2. Download the new vns and utxos and check that we can still spend all the // tokens we think we can spend. // 3. compute the new deposits and withdrawls + + // TODO: Have something which represents a "local-ledger" state. This ledger state can + // exist "at" the wallet, or can exist elsewhere, but the wallet must be able to + // send/recv messages to/from it, i.e. the `Ledger` abstraction may not be correct, + // it may have to be a different trait or two traits. + let _ = ledger; todo!() } diff --git a/manta-crypto/src/merkle_tree.rs b/manta-crypto/src/merkle_tree.rs index 71e9e0c73..67649836c 100644 --- a/manta-crypto/src/merkle_tree.rs +++ b/manta-crypto/src/merkle_tree.rs @@ -188,7 +188,7 @@ where fn append(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> bool; } -/// Left or Right Side of a Subtree +/// Parity of a Subtree #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub enum Parity { /// Left Side of the Subtree @@ -243,6 +243,20 @@ impl Parity { } } + /// Returns the `center` placed in the pair at the location given by `self`, placing `lhs` and + /// `rhs` in the left or right empty slot of the pair respectively. + #[inline] + pub fn triple_order<T, L, R>(&self, center: T, lhs: L, rhs: R) -> (T, T) + where + L: FnOnce() -> T, + R: FnOnce() -> T, + { + match self { + Self::Left => (center, rhs()), + Self::Right => (lhs(), center), + } + } + /// Combines two inner digests into a new inner digest using `parameters`, swapping the order /// of `lhs` and `rhs` depending on the parity of `self` in its subtree. #[inline] @@ -273,10 +287,8 @@ impl Parity { where C: Configuration + ?Sized, { - match self { - Self::Left => C::InnerHash::join(&parameters.inner, center, rhs), - Self::Right => C::InnerHash::join(&parameters.inner, lhs, center), - } + let (lhs, rhs) = self.triple_order(center, move || lhs, move || rhs); + C::InnerHash::join(&parameters.inner, lhs, rhs) } /// Combines two leaf digests into a new inner digest using `parameters`, swapping the order @@ -338,10 +350,8 @@ impl Node { /// Returns `self` with its sibling in parity order. #[inline] pub fn with_sibling(self) -> (Self, Self) { - match self.parity() { - Parity::Left => (self, self + 1), - Parity::Right => (self - 1, self), - } + self.parity() + .triple_order(self, move || self - 1, move || self + 1) } /// Returns the parent [`Node`] of this node. @@ -560,6 +570,12 @@ impl<C> Parameters<C> where C: Configuration + ?Sized, { + /// Builds a new [`Parameters`] from `leaf` and `inner` parameters. + #[inline] + pub fn new(leaf: LeafHashParamters<C>, inner: InnerHashParameters<C>) -> Self { + Self { leaf, inner } + } + /// Computes the leaf digest of `leaf` using `self`. #[inline] pub fn digest(&self, leaf: &Leaf<C>) -> LeafDigest<C> { diff --git a/manta-pay/src/accounting/ledger.rs b/manta-pay/src/accounting/ledger.rs index 2748b0110..3cfdf23d9 100644 --- a/manta-pay/src/accounting/ledger.rs +++ b/manta-pay/src/accounting/ledger.rs @@ -26,7 +26,7 @@ use crate::{ merkle_tree::{constraint as merkle_tree_constraint, ConfigConverter}, }, }; -use alloc::{vec, vec::Vec}; +use alloc::{collections::BTreeSet, vec, vec::Vec}; use blake2::{ digest::{Update, VariableOutput}, VarBlake2s, @@ -36,7 +36,7 @@ use manta_crypto::{ constraint::{ self, reflection::HasAllocation, Allocation, Constant, ProofSystem as _, Variable, }, - merkle_tree::{self, MerkleTree}, + merkle_tree::{self, latest_node::LatestNode, MerkleTree, Tree}, set::{constraint::VerifiedSetVariable, ContainmentProof, Set, VerifiedSet}, }; use manta_util::{as_bytes, concatenate, into_array_unchecked}; @@ -75,7 +75,7 @@ pub struct UtxoShard { root: Root, /// Unspent Transaction Outputs - utxos: Vec<Utxo>, + utxos: LatestNode<ConfigConverter<Configuration>>, } /// UTXO Set @@ -84,6 +84,9 @@ pub struct UtxoSet { /// UTXO Shards shards: [UtxoShard; Self::SHARD_COUNT], + /// UTXO Set + utxos: BTreeSet<Utxo>, + /// Merkle Tree Parameters parameters: Parameters, } @@ -96,6 +99,7 @@ impl UtxoSet { pub fn new(parameters: Parameters) -> Self { Self { shards: into_array_unchecked(vec![Default::default(); Self::SHARD_COUNT]), + utxos: Default::default(), parameters, } } @@ -125,7 +129,8 @@ impl UtxoSet { /// Returns `true` if the `utxo` belongs to the shard it would be stored in. #[inline] pub fn utxo_exists(&self, utxo: &Utxo) -> bool { - self.shard(utxo).utxos.iter().any(move |u| u == utxo) + // TODO: self.utxos.contains(utxo) + todo!() } } @@ -139,21 +144,18 @@ impl Set for UtxoSet { #[inline] fn try_insert(&mut self, item: Self::Item) -> Result<(), Self::Item> { - /* FIXME: - let shard = &mut self.shards[Self::shard_index(&item)]; - if shard.utxos.contains(&item) { + // TODO: Distinguish between both kinds of errors. + if self.utxo_exists(&item) { return Err(item); } - shard.utxos.push(item); - match MerkleTree::new_root(&self.parameters, &shard.utxos) { - Some(root) => { - shard.root = root; - Ok(()) - } - _ => Err(shard.utxos.pop().unwrap()), + if !self.shards[Self::shard_index(&item)] + .utxos + .append(&self.parameters, &as_bytes!(&item)) + { + return Err(item); } - */ - todo!() + // FIXME: self.utxos.insert(item); + Ok(()) } } @@ -187,7 +189,10 @@ impl VerifiedSet for UtxoSet { item: &Self::Item, ) -> Result<ContainmentProof<Self>, Self::ContainmentError> { // TODO: Return a more informative error. - /* FIXME: + + /* FIXME: This is not implementable! Need to split this functionality into a better + * abstraction. + let utxos = &self.shards[Self::shard_index(item)].utxos; match utxos.iter().position(move |u| u == item) { Some(index) => MerkleTree::new(&self.parameters, utxos) @@ -196,8 +201,10 @@ impl VerifiedSet for UtxoSet { .ok_or(()), _ => Err(()), } + */ - todo!() + + unimplemented!() } } diff --git a/manta-pay/src/crypto/merkle_tree/mod.rs b/manta-pay/src/crypto/merkle_tree/mod.rs index 578dcf367..f50f1db8b 100644 --- a/manta-pay/src/crypto/merkle_tree/mod.rs +++ b/manta-pay/src/crypto/merkle_tree/mod.rs @@ -394,10 +394,9 @@ pub mod constraint { /// Converts a [`Path`] to a [`PathInnerType`]. #[inline] fn convert_path(path: &Path<ConfigConverter<C>>) -> PathInnerType<C> { - // FIXME: Does the `auth_path` need to be reversed? PathInnerType { leaf_sibling_hash: path.sibling_digest.clone(), - auth_path: path.inner_path.clone(), + auth_path: path.inner_path.iter().rev().cloned().collect(), leaf_index: path.leaf_index.0, } } From 2f36be8a0ce4d7e2eb935de61d2856c5ab0693ea Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 23 Sep 2021 02:17:29 -0400 Subject: [PATCH 054/275] rewrite of ledger abstraction, enforcing validation order --- manta-accounting/src/identity.rs | 315 +++++++++++++--- manta-accounting/src/ledger.rs | 159 -------- manta-accounting/src/lib.rs | 2 - manta-accounting/src/transfer.rs | 564 +++++++++++++++++------------ manta-accounting/src/wallet.rs | 3 +- manta-pay/src/accounting/ledger.rs | 94 +---- 6 files changed, 624 insertions(+), 513 deletions(-) delete mode 100644 manta-accounting/src/ledger.rs diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 8908e15f5..ad0bf98c5 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -22,7 +22,6 @@ use crate::{ asset::{Asset, AssetBalance, AssetId, AssetVar}, keys::SecretKeyGenerator, - ledger::Ledger, }; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use manta_crypto::{ @@ -398,6 +397,19 @@ where S: VerifiedSet<Item = Utxo<C>>, Standard: Distribution<AssetParameters<C>>, { + // TODO: Generate pre-sender data structure in case the `utxo_set` is behind the + // current ledger state and we need to have everything ready for the `utxo_set`. + // + // > pub struct PreSender<C> { + // secret_key, + // public_key, + // asset, + // parameters, + // void_number_commitment, + // void_number, + // utxo + // } + // let parameters = self.parameters(); let (public_key, void_number_commitment, utxo) = self.construct_utxo(commitment_scheme, &asset, &parameters); @@ -1079,21 +1091,105 @@ where } } +/// Sender Ledger +pub trait SenderLedger<C, S> +where + C: Configuration, + S: VerifiedSet<Item = Utxo<C>>, +{ + /// Valid [`VoidNumber`] Posting Key + /// + /// # Safety + /// + /// This type must be some wrapper around [`VoidNumber`] which can only be constructed by this + /// implementation of [`SenderLedger`]. This is to prevent that [`post`] is called before + /// [`validate`]. + /// + /// [`post`]: SenderPostingKey::post + /// [`validate`]: SenderPost::validate + type ValidVoidNumber; + + /// Valid Utxo State Posting Key + /// + /// # Safety + /// + /// This type must be some wrapper around [`S::Public`] which can only be constructed by this + /// implementation of [`SenderLedger`]. This is to prevent that [`post`] is called before + /// [`validate`]. + /// + /// [`S::Public`]: VerifiedSet::Public + /// [`post`]: SenderPostingKey::post + /// [`validate`]: SenderPost::validate + type ValidUtxoState; + + /// Super Posting Key + /// + /// Type that allows super-traits of [`SenderLedger`] to customize posting key behavior. + type SuperPostingKey: Copy; + + /// Ledger Error + type Error; + + /// Checks if the ledger already contains the `void_number` in its set of void numbers. + /// + /// Existence of such a void number could indicate a possible double-spend. + fn is_unspent( + &self, + void_number: VoidNumber<C>, + ) -> Result<Option<Self::ValidVoidNumber>, Self::Error>; + + /// Checks if the `public_input` is up-to-date with the current state of the UTXO set that is + /// stored on the ledger. + /// + /// Failure to match the ledger state means that the sender was constructed under an invalid or + /// older state of the ledger. + fn has_same_utxo_state( + &self, + public_input: S::Public, + ) -> Result<Option<Self::ValidUtxoState>, Self::Error>; + + /// Posts the `void_number` to the ledger, spending the asset. + /// + /// # Safety + /// + /// This function can only be called once we check that `void_number` is not already stored + /// on the ledger. See [`is_unspent`](Self::is_unspent). + fn spend( + &mut self, + void_number: Self::ValidVoidNumber, + utxo_state: Self::ValidUtxoState, + super_key: &Self::SuperPostingKey, + ) -> Result<(), Self::Error>; +} + /// Sender Post Error -pub enum SenderPostError<L> +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "L::Error: Clone"), + Copy(bound = "L::Error: Copy"), + Debug(bound = "L::Error: Debug"), + Eq(bound = "L::Error: Eq"), + Hash(bound = "L::Error: Hash"), + PartialEq(bound = "L::Error: PartialEq") +)] +pub enum SenderPostError<C, S, L> where - L: Ledger + ?Sized, + C: Configuration, + S: VerifiedSet<Item = Utxo<C>>, + L: SenderLedger<C, S>, { - /// Asset has already been spent - AssetSpent( - /// Void Number - L::VoidNumber, - ), - /// Utxo [`ContainmentProof`](manta_crypto::set::ContainmentProof) has an invalid public input - InvalidUtxoState( - /// UTXO Containment Proof Public Input - <L::UtxoSet as VerifiedSet>::Public, - ), + /// Asset Spent Error + /// + /// The asset has already been spent. + AssetSpent, + + /// Invalid UTXO State Error + /// + /// The sender was not constructed under the current state of the UTXO set. + InvalidUtxoState, + + /// Ledger Error + LedgerError(L::Error), } /// Sender Post @@ -1114,19 +1210,31 @@ where C: Configuration, S: VerifiedSet<Item = Utxo<C>>, { - /// Posts the [`SenderPost`] data to the `ledger`. + /// Validates `self` on the sender `ledger`. #[inline] - pub fn post<L>(self, ledger: &mut L) -> Result<(), SenderPostError<L>> + pub fn validate<L>( + self, + ledger: &L, + ) -> Result<SenderPostingKey<C, S, L>, SenderPostError<C, S, L>> where - L: Ledger<VoidNumber = VoidNumber<C>, UtxoSet = S> + ?Sized, + L: SenderLedger<C, S>, { - ledger - .try_post_void_number(self.void_number) - .map_err(SenderPostError::AssetSpent)?; - ledger - .check_utxo_containment_proof_public_input(self.utxo_containment_proof_public_input) - .map_err(SenderPostError::InvalidUtxoState)?; - Ok(()) + Ok(SenderPostingKey { + void_number: match ledger + .is_unspent(self.void_number) + .map_err(SenderPostError::LedgerError)? + { + Some(key) => key, + _ => return Err(SenderPostError::AssetSpent), + }, + utxo_containment_proof_public_input: match ledger + .has_same_utxo_state(self.utxo_containment_proof_public_input) + .map_err(SenderPostError::LedgerError)? + { + Some(key) => key, + _ => return Err(SenderPostError::InvalidUtxoState), + }, + }) } } @@ -1141,6 +1249,37 @@ where } } +/// Sender Posting Key +pub struct SenderPostingKey<C, S, L> +where + C: Configuration, + S: VerifiedSet<Item = Utxo<C>>, + L: SenderLedger<C, S>, +{ + /// Void Number Posting Key + void_number: L::ValidVoidNumber, + + /// UTXO Containment Proof Public Input Posting Key + utxo_containment_proof_public_input: L::ValidUtxoState, +} + +impl<C, S, L> SenderPostingKey<C, S, L> +where + C: Configuration, + S: VerifiedSet<Item = Utxo<C>>, + L: SenderLedger<C, S>, +{ + /// Posts `self` to the sender `ledger`. + #[inline] + pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) -> Result<(), L::Error> { + ledger.spend( + self.void_number, + self.utxo_containment_proof_public_input, + super_key, + ) + } +} + /// Receiver pub struct Receiver<C, I> where @@ -1204,21 +1343,74 @@ where } } +/// Receiver Ledger +pub trait ReceiverLedger<C, I> +where + C: Configuration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, +{ + /// Valid [`Utxo`] Posting Key + /// + /// # Safety + /// + /// This type must be some wrapper around [`Utxo`] which can only be constructed by this + /// implementation of [`ReceiverLedger`]. This is to prevent that [`post`] is called + /// before [`validate`]. + /// + /// [`post`]: ReceiverPostingKey::post + /// [`validate`]: ReceiverPost::validate + type ValidUtxo; + + /// Super Posting Key + /// + /// Type that allows super-traits of [`ReceiverLedger`] to customize posting key behavior. + type SuperPostingKey: Copy; + + /// Ledger Error + type Error; + + /// Checks if the ledger already contains the `utxo` in its set of UTXOs. + /// + /// Existence of such a UTXO could indicate a possible double-spend. + fn is_not_registered(&self, utxo: Utxo<C>) -> Result<Option<Self::ValidUtxo>, Self::Error>; + + /// Posts the `utxo` and `encrypted_asset` to the ledger, registering the asset. + /// + /// # Safety + /// + /// This function can only be called once we check that `utxo` is not already stored + /// on the ledger. See [`is_not_registered`](Self::is_not_registered). + fn register( + &mut self, + utxo: Self::ValidUtxo, + encrypted_asset: EncryptedMessage<I>, + super_key: &Self::SuperPostingKey, + ) -> Result<(), Self::Error>; +} + /// Receiver Post Error -pub enum ReceiverPostError<L> +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "L::Error: Clone"), + Copy(bound = "L::Error: Copy"), + Debug(bound = "L::Error: Debug"), + Eq(bound = "L::Error: Eq"), + Hash(bound = "L::Error: Hash"), + PartialEq(bound = "L::Error: PartialEq") +)] +pub enum ReceiverPostError<C, I, L> where - L: Ledger + ?Sized, + C: Configuration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + L: ReceiverLedger<C, I>, { - /// Asset has already been registered - AssetRegistered( - /// Unspent Transaction Output - L::Utxo, - ), - /// Encrypted Asset has already been stored - EncryptedAssetStored( - /// Encrypted [`Asset`](crate::asset::Asset) - L::EncryptedAsset, - ), + /// Asset Registered Error + /// + /// The asset has already been registered with the ledger. + AssetRegistered, + + /// Ledger Error + LedgerError(L::Error), } /// Receiver Post @@ -1239,19 +1431,25 @@ where C: Configuration, I: IntegratedEncryptionScheme<Plaintext = Asset>, { - /// Posts the [`ReceiverPost`] data to the `ledger`. + /// Validates `self` on the receiver `ledger`. #[inline] - pub fn post<L>(self, ledger: &mut L) -> Result<(), ReceiverPostError<L>> + pub fn validate<L>( + self, + ledger: &L, + ) -> Result<ReceiverPostingKey<C, I, L>, ReceiverPostError<C, I, L>> where - L: Ledger<Utxo = Utxo<C>, EncryptedAsset = EncryptedMessage<I>> + ?Sized, + L: ReceiverLedger<C, I>, { - ledger - .try_post_utxo(self.utxo) - .map_err(ReceiverPostError::AssetRegistered)?; - ledger - .try_post_encrypted_asset(self.encrypted_asset) - .map_err(ReceiverPostError::EncryptedAssetStored)?; - Ok(()) + Ok(ReceiverPostingKey { + utxo: match ledger + .is_not_registered(self.utxo) + .map_err(ReceiverPostError::LedgerError)? + { + Some(key) => key, + _ => return Err(ReceiverPostError::AssetRegistered), + }, + encrypted_asset: self.encrypted_asset, + }) } } @@ -1266,6 +1464,33 @@ where } } +/// Receiver Posting Key +pub struct ReceiverPostingKey<C, I, L> +where + C: Configuration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + L: ReceiverLedger<C, I>, +{ + /// Utxo Posting Key + utxo: L::ValidUtxo, + + /// Encrypted Asset + encrypted_asset: EncryptedMessage<I>, +} + +impl<C, I, L> ReceiverPostingKey<C, I, L> +where + C: Configuration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + L: ReceiverLedger<C, I>, +{ + /// Posts `self` to the receiver `ledger`. + #[inline] + pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) -> Result<(), L::Error> { + ledger.register(self.utxo, self.encrypted_asset, super_key) + } +} + /// Constraint System Gadgets for Identities pub mod constraint { use super::*; diff --git a/manta-accounting/src/ledger.rs b/manta-accounting/src/ledger.rs deleted file mode 100644 index 23e3dc3d2..000000000 --- a/manta-accounting/src/ledger.rs +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Ledger Abstraction - -use crate::identity::{ReceiverPostError, SenderPostError}; -use manta_crypto::{ - constraint::ProofSystem, - set::{Set, VerifiedSet}, -}; - -/// Ledger Trait -pub trait Ledger { - /// Void Number Type - type VoidNumber; - - /// Unspent Transaction Output Type - type Utxo; - - /// UTXO Set Type - type UtxoSet: VerifiedSet<Item = Self::Utxo>; - - /// Encrypted Asset Type - type EncryptedAsset; - - /// Proof System - type ProofSystem: ProofSystem; - - /// Returns a shared reference to the [`UtxoSet`](Self::UtxoSet). - fn utxos(&self) -> &Self::UtxoSet; - - /// Returns `true` if the `void_number` corresponding to some asset - /// __is not stored__ on `self`. - fn is_unspent(&self, void_number: &Self::VoidNumber) -> bool; - - /// Returns `true` if the `utxo` corresponding to some asset - /// __is stored__ on `self`. - #[inline] - fn is_registered(&self, utxo: &Self::Utxo) -> bool { - self.utxos().contains(utxo) - } - - /// Returns `true` if an asset's `utxo` __is stored__ on the `ledger` and that - /// its `void_number` __is not stored__ on `self`. - #[inline] - fn is_spendable(&self, utxo: &Self::Utxo, void_number: &Self::VoidNumber) -> bool { - self.is_registered(utxo) && self.is_unspent(void_number) - } - - /// Checks if the `public_input` corresponding to a UTXO containment proof represents the current - /// state of the [`UtxoSet`](Self::UtxoSet), returning it back if not. - #[inline] - fn check_utxo_containment_proof_public_input( - &self, - public_input: <Self::UtxoSet as VerifiedSet>::Public, - ) -> Result<(), <Self::UtxoSet as VerifiedSet>::Public> { - if self.utxos().check_public_input(&public_input) { - Ok(()) - } else { - Err(public_input) - } - } - - /// Tries to post the `void_number` to `self` returning it back if the - /// `void_number` was already stored on `self`. - fn try_post_void_number( - &mut self, - void_number: Self::VoidNumber, - ) -> Result<(), Self::VoidNumber>; - - /// Tries to post the `utxo` to `self` returning it back if the - /// `utxo` was already stored on `self`. - fn try_post_utxo(&mut self, utxo: Self::Utxo) -> Result<(), Self::Utxo>; - - /// Tries to post the `encrypted_asset` to `self` returning it back - /// if the `encrypted_asset` was already stored on `self`. - fn try_post_encrypted_asset( - &mut self, - encrypted_asset: Self::EncryptedAsset, - ) -> Result<(), Self::EncryptedAsset>; - - /// Checks that the given `proof` is valid. - fn check_proof( - &self, - proof: <Self::ProofSystem as ProofSystem>::Proof, - ) -> Result<(), ProofPostError<Self>>; -} - -/// Proof Post Error -pub enum ProofPostError<L> -where - L: Ledger + ?Sized, -{ - /// Proof was invalid - InvalidProof( - /// Proof - <L::ProofSystem as ProofSystem>::Proof, - /// Proof Verification Error - Option<<L::ProofSystem as ProofSystem>::Error>, - ), -} - -/// Ledger Post Error -pub enum PostError<L> -where - L: Ledger + ?Sized, -{ - /// Sender Post Error - Sender(SenderPostError<L>), - - /// Receiver Post Error - Receiver(ReceiverPostError<L>), - - /// Proof Post Error - Proof(ProofPostError<L>), -} - -impl<L> From<SenderPostError<L>> for PostError<L> -where - L: Ledger + ?Sized, -{ - #[inline] - fn from(err: SenderPostError<L>) -> Self { - Self::Sender(err) - } -} - -impl<L> From<ReceiverPostError<L>> for PostError<L> -where - L: Ledger + ?Sized, -{ - #[inline] - fn from(err: ReceiverPostError<L>) -> Self { - Self::Receiver(err) - } -} - -impl<L> From<ProofPostError<L>> for PostError<L> -where - L: Ledger + ?Sized, -{ - #[inline] - fn from(err: ProofPostError<L>) -> Self { - Self::Proof(err) - } -} diff --git a/manta-accounting/src/lib.rs b/manta-accounting/src/lib.rs index b7b44c754..5f3431edc 100644 --- a/manta-accounting/src/lib.rs +++ b/manta-accounting/src/lib.rs @@ -28,7 +28,6 @@ extern crate derive_more; extern crate cocoon as cocoon_crate; mod asset; -mod ledger; pub mod fs; pub mod identity; @@ -38,4 +37,3 @@ pub mod wallet; pub use asset::*; pub use identity::prelude::*; -pub use ledger::*; diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index ec3e9d619..3147f9699 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -16,16 +16,18 @@ //! Transfer Protocols +// TODO: See if we can get rid of the `Copy` restriction on `ValidProof` and `SuperPostingKey`. +// TODO: Add `generate_context`/`generate_proof` logic to `SecretTransfer`. +// TODO: Have a compile-time way to check if proof generation is used for a certain shape, +// so that the `generate_context`/`generate_proof` functions can only exist on the right +// shape implementations, instead of failing at runtime with `None`. + use crate::{ asset::{sample_asset_balances, Asset, AssetBalance, AssetBalances, AssetId}, - identity::{self, constraint::UtxoVar, Utxo, VoidNumber}, - ledger::{Ledger, PostError}, + identity::{self, constraint::UtxoVar, ReceiverLedger, SenderLedger, Utxo}, }; use alloc::vec::Vec; -use core::{ - convert::{TryFrom, TryInto}, - ops::AddAssign, -}; +use core::{fmt::Debug, hash::Hash, ops::AddAssign}; use manta_crypto::{ constraint::{ self, @@ -36,7 +38,7 @@ use manta_crypto::{ ies::{EncryptedMessage, IntegratedEncryptionScheme}, set::{constraint::VerifiedSetVariable, VerifiedSet}, }; -use manta_util::{array_map, mixed_chain, Either}; +use manta_util::{array_map, fallible_array_map, mixed_chain, Either}; use rand::{ distributions::{Distribution, Standard}, CryptoRng, RngCore, @@ -180,59 +182,135 @@ pub trait Configuration: } /// Transfer Shielded Identity Type -pub type ShieldedIdentity<T> = - identity::ShieldedIdentity<T, <T as Configuration>::IntegratedEncryptionScheme>; +pub type ShieldedIdentity<C> = + identity::ShieldedIdentity<C, <C as Configuration>::IntegratedEncryptionScheme>; /// Transfer Spend Type -pub type Spend<T> = identity::Spend<T, <T as Configuration>::IntegratedEncryptionScheme>; +pub type Spend<C> = identity::Spend<C, <C as Configuration>::IntegratedEncryptionScheme>; /// Transfer Sender Type -pub type Sender<T> = identity::Sender<T, <T as Configuration>::UtxoSet>; +pub type Sender<C> = identity::Sender<C, <C as Configuration>::UtxoSet>; + +/// Sender Post Type +pub type SenderPost<C> = identity::SenderPost<C, <C as Configuration>::UtxoSet>; + +/// Sender Post Error Type +pub type SenderPostError<C, L> = identity::SenderPostError<C, <C as Configuration>::UtxoSet, L>; + +/// Sender Posting Key Type +pub type SenderPostingKey<C, L> = identity::SenderPostingKey<C, <C as Configuration>::UtxoSet, L>; /// Transfer Receiver Type -pub type Receiver<T> = identity::Receiver<T, <T as Configuration>::IntegratedEncryptionScheme>; +pub type Receiver<C> = identity::Receiver<C, <C as Configuration>::IntegratedEncryptionScheme>; + +/// Receiver Post Type +pub type ReceiverPost<C> = + identity::ReceiverPost<C, <C as Configuration>::IntegratedEncryptionScheme>; + +/// Receiver Post Error Type +pub type ReceiverPostError<C, L> = + identity::ReceiverPostError<C, <C as Configuration>::IntegratedEncryptionScheme, L>; + +/// Receiver Posting Key Type +pub type ReceiverPostingKey<C, L> = + identity::ReceiverPostingKey<C, <C as Configuration>::IntegratedEncryptionScheme, L>; + +/// Transfer Encrypted Asset Type +pub type EncryptedAsset<C> = EncryptedMessage<<C as Configuration>::IntegratedEncryptionScheme>; -/// Transfer Integrated Encryption Scheme Error -pub type IntegratedEncryptionSchemeError<T> = - <<T as Configuration>::IntegratedEncryptionScheme as IntegratedEncryptionScheme>::Error; +/// Transfer Integrated Encryption Scheme Error Type +pub type IntegratedEncryptionSchemeError<C> = + <<C as Configuration>::IntegratedEncryptionScheme as IntegratedEncryptionScheme>::Error; /// Transfer Constraint System Type -pub type ConstraintSystem<T> = <T as Configuration>::ConstraintSystem; +pub type ConstraintSystem<C> = <C as Configuration>::ConstraintSystem; /// Transfer Sender Variable Type -pub type SenderVar<T> = identity::constraint::SenderVar<T, <T as Configuration>::UtxoSet>; +pub type SenderVar<C> = identity::constraint::SenderVar<C, <C as Configuration>::UtxoSet>; /// Transfer Receiver Type -pub type ReceiverVar<T> = - identity::constraint::ReceiverVar<T, <T as Configuration>::IntegratedEncryptionScheme>; +pub type ReceiverVar<C> = + identity::constraint::ReceiverVar<C, <C as Configuration>::IntegratedEncryptionScheme>; /// Transfer Proving Context Type -pub type ProvingContext<T> = <<T as Configuration>::ProofSystem as ProofSystem>::ProvingContext; +pub type ProvingContext<C> = <<C as Configuration>::ProofSystem as ProofSystem>::ProvingContext; /// Transfer Verifying Context Type -pub type VerifyingContext<T> = <<T as Configuration>::ProofSystem as ProofSystem>::VerifyingContext; +pub type VerifyingContext<C> = <<C as Configuration>::ProofSystem as ProofSystem>::VerifyingContext; /// Transfer Proof Type -pub type Proof<T> = <<T as Configuration>::ProofSystem as ProofSystem>::Proof; +pub type Proof<C> = <<C as Configuration>::ProofSystem as ProofSystem>::Proof; /// Transfer Proof System Error Type -pub type ProofSystemError<T> = <<T as Configuration>::ProofSystem as ProofSystem>::Error; +pub type ProofSystemError<C> = <<C as Configuration>::ProofSystem as ProofSystem>::Error; + +/// Transfer Ledger Super Posting Key Type +pub type TransferLedgerSuperPostingKey<C, L> = <L as TransferLedger<C>>::SuperPostingKey; + +/// Transfer Ledger Error Type +pub type TransferLedgerError<C, L> = <L as TransferLedger<C>>::Error; + +/// Transfer Ledger +pub trait TransferLedger<C>: + SenderLedger< + C, + C::UtxoSet, + SuperPostingKey = (Self::ValidProof, TransferLedgerSuperPostingKey<C, Self>), + Error = TransferLedgerError<C, Self>, + > + ReceiverLedger< + C, + C::IntegratedEncryptionScheme, + SuperPostingKey = (Self::ValidProof, TransferLedgerSuperPostingKey<C, Self>), + Error = TransferLedgerError<C, Self>, + > +where + C: Configuration, +{ + /// Valid [`Proof`] Posting Key + /// + /// # Safety + /// + /// This type must be restricted so that it can only be constructed by this implementation + /// of [`TransferLedger`]. This is to prevent that [`SenderPostingKey::post`] and + /// [`ReceiverPostingKey::post`] are called before [`is_valid`](Self::is_valid), + /// [`SenderPost::validate`], and [`ReceiverPost::validate`]. + type ValidProof: Copy; + + /// Super Posting Key + /// + /// Type that allows super-traits of [`TransferLedger`] to customize posting key behavior. + type SuperPostingKey: Copy; + + /// Ledger Error + type Error; + + /// Checks that the transfer proof is valid. + /// + /// # Implementation Note + /// + /// This should always succeed on `None` inputs which correspond to transfers which do not + /// require a validation proof. + fn is_valid( + &self, + proof: Option<Proof<C>>, + ) -> Result<Option<Self::ValidProof>, TransferLedgerError<C, Self>>; +} /// Secret Transfer Protocol -pub struct SecretTransfer<T, const SENDERS: usize, const RECEIVERS: usize> +pub struct SecretTransfer<C, const SENDERS: usize, const RECEIVERS: usize> where - T: Configuration, + C: Configuration, { /// Senders - pub senders: [Sender<T>; SENDERS], + pub senders: [Sender<C>; SENDERS], /// Receivers - pub receivers: [Receiver<T>; RECEIVERS], + pub receivers: [Receiver<C>; RECEIVERS], } -impl<T, const SENDERS: usize, const RECEIVERS: usize> SecretTransfer<T, SENDERS, RECEIVERS> +impl<C, const SENDERS: usize, const RECEIVERS: usize> SecretTransfer<C, SENDERS, RECEIVERS> where - T: Configuration, + C: Configuration, { /// Maximum Number of Senders pub const MAXIMUM_SENDER_COUNT: usize = 32; @@ -242,7 +320,7 @@ where /// Builds a new [`SecretTransfer`]. #[inline] - pub fn new(senders: [Sender<T>; SENDERS], receivers: [Receiver<T>; RECEIVERS]) -> Self { + pub fn new(senders: [Sender<C>; SENDERS], receivers: [Receiver<C>; RECEIVERS]) -> Self { Self::check_sender_side(); Self::check_receiver_side(); Self::check_size_overflow(); @@ -282,7 +360,7 @@ where /// Builds a new [`SecretTransfer`] without checking the number of senders and receivers. #[inline] - fn new_unchecked(senders: [Sender<T>; SENDERS], receivers: [Receiver<T>; RECEIVERS]) -> Self { + fn new_unchecked(senders: [Sender<C>; SENDERS], receivers: [Receiver<C>; RECEIVERS]) -> Self { Self { senders, receivers } } @@ -326,31 +404,25 @@ where #[inline] pub fn into_post<R>( self, - commitment_scheme: &T::CommitmentScheme, - utxo_set: &T::UtxoSet, - context: &ProvingContext<T>, + commitment_scheme: &C::CommitmentScheme, + utxo_set: &C::UtxoSet, + context: &ProvingContext<C>, rng: &mut R, - ) -> Result<SecretTransferPost<T, SENDERS, RECEIVERS>, ProofSystemError<T>> + ) -> Result<TransferPost<C, 0, SENDERS, RECEIVERS, 0>, ProofSystemError<C>> where R: CryptoRng + RngCore + ?Sized, { - match Transfer::from(self) - .into_post(commitment_scheme, utxo_set, context, rng)? - .try_into() - { - Ok(post) => Ok(post), - _ => unreachable!("We convert there and back so we know that the proof exists."), - } + Transfer::from(self).into_post(commitment_scheme, utxo_set, context, rng) } } -impl<T, const SENDERS: usize, const RECEIVERS: usize> From<SecretTransfer<T, SENDERS, RECEIVERS>> - for Transfer<T, 0, SENDERS, RECEIVERS, 0> +impl<C, const SENDERS: usize, const RECEIVERS: usize> From<SecretTransfer<C, SENDERS, RECEIVERS>> + for Transfer<C, 0, SENDERS, RECEIVERS, 0> where - T: Configuration, + C: Configuration, { #[inline] - fn from(transfer: SecretTransfer<T, SENDERS, RECEIVERS>) -> Self { + fn from(transfer: SecretTransfer<C, SENDERS, RECEIVERS>) -> Self { Self { public: Default::default(), secret: transfer, @@ -358,114 +430,40 @@ where } } -/// Sender Post Type -pub type SenderPost<T> = identity::SenderPost<T, <T as Configuration>::UtxoSet>; - -/// Receiver Post Type -pub type ReceiverPost<T> = - identity::ReceiverPost<T, <T as Configuration>::IntegratedEncryptionScheme>; - -/// Secret Transfer Post -pub struct SecretTransferPost<T, const SENDERS: usize, const RECEIVERS: usize> -where - T: Configuration, -{ - /// Sender Posts - pub sender_posts: [SenderPost<T>; SENDERS], - - /// Receiver Posts - pub receiver_posts: [ReceiverPost<T>; RECEIVERS], - - /// Validity Proof - pub validity_proof: Proof<T>, -} - -impl<T, const SENDERS: usize, const RECEIVERS: usize> SecretTransferPost<T, SENDERS, RECEIVERS> -where - T: Configuration, -{ - /// Posts the [`SecretTransferPost`] to the `ledger`. - #[inline] - pub fn post<L>(self, ledger: &mut L) -> Result<(), PostError<L>> - where - L: Ledger< - VoidNumber = VoidNumber<T>, - Utxo = Utxo<T>, - UtxoSet = T::UtxoSet, - EncryptedAsset = EncryptedMessage<T::IntegratedEncryptionScheme>, - ProofSystem = T::ProofSystem, - > + ?Sized, - { - TransferPost::from(self).post(ledger) - } -} - -impl<T, const SENDERS: usize, const RECEIVERS: usize> - From<SecretTransferPost<T, SENDERS, RECEIVERS>> for TransferPost<T, 0, SENDERS, RECEIVERS, 0> -where - T: Configuration, -{ - #[inline] - fn from(post: SecretTransferPost<T, SENDERS, RECEIVERS>) -> Self { - Self { - sender_posts: post.sender_posts, - receiver_posts: post.receiver_posts, - validity_proof: Some(post.validity_proof), - } - } -} - -impl<T, const SENDERS: usize, const RECEIVERS: usize> - TryFrom<TransferPost<T, 0, SENDERS, RECEIVERS, 0>> for SecretTransferPost<T, SENDERS, RECEIVERS> -where - T: Configuration, -{ - type Error = (); - - #[inline] - fn try_from(post: TransferPost<T, 0, SENDERS, RECEIVERS, 0>) -> Result<Self, Self::Error> { - Ok(Self { - sender_posts: post.sender_posts, - receiver_posts: post.receiver_posts, - validity_proof: post.validity_proof.ok_or(())?, - }) - } -} - /// Transfer Protocol pub struct Transfer< - T, + C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize, > where - T: Configuration, + C: Configuration, { /// Public Part of the Transfer public: PublicTransfer<SOURCES, SINKS>, /// Secret Part of the Transfer - secret: SecretTransfer<T, SENDERS, RECEIVERS>, + secret: SecretTransfer<C, SENDERS, RECEIVERS>, } -impl<T, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> - Transfer<T, SOURCES, SENDERS, RECEIVERS, SINKS> +impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> + Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS> where - T: Configuration, + C: Configuration, { /// Builds a new universal [`Transfer`] from public and secret information. #[inline] pub fn new( asset_id: AssetId, sources: AssetBalances<SOURCES>, - senders: [Sender<T>; SENDERS], - receivers: [Receiver<T>; RECEIVERS], + senders: [Sender<C>; SENDERS], + receivers: [Receiver<C>; RECEIVERS], sinks: AssetBalances<SINKS>, ) -> Self { Self::check_sender_side(); Self::check_receiver_side(); - SecretTransfer::<T, SENDERS, RECEIVERS>::check_size_overflow(); + SecretTransfer::<C, SENDERS, RECEIVERS>::check_size_overflow(); Self::new_unchecked(asset_id, sources, senders, receivers, sinks) } @@ -491,8 +489,8 @@ where fn new_unchecked( asset_id: AssetId, sources: AssetBalances<SOURCES>, - senders: [Sender<T>; SENDERS], - receivers: [Receiver<T>; RECEIVERS], + senders: [Sender<C>; SENDERS], + receivers: [Receiver<C>; RECEIVERS], sinks: AssetBalances<SINKS>, ) -> Self { Self { @@ -544,14 +542,14 @@ where /// Generates the unknown variables for the validity proof. #[inline] fn unknown_variables( - commitment_scheme: &T::CommitmentScheme, - utxo_set: &T::UtxoSet, - cs: &mut ConstraintSystem<T>, + commitment_scheme: &C::CommitmentScheme, + utxo_set: &C::UtxoSet, + cs: &mut ConstraintSystem<C>, ) -> ( - Option<T::AssetIdVar>, - TransferParticipantsVar<T, SOURCES, SENDERS, RECEIVERS, SINKS>, - T::CommitmentSchemeVar, - T::UtxoSetVar, + Option<C::AssetIdVar>, + TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS>, + C::CommitmentSchemeVar, + C::UtxoSetVar, ) { let base_asset_id = if has_no_public_side::<SOURCES, SENDERS, RECEIVERS, SINKS>() { None @@ -559,7 +557,7 @@ where Some(()) }; ( - base_asset_id.map(|_| T::AssetIdVar::new_unknown(cs, Public)), + base_asset_id.map(|_| C::AssetIdVar::new_unknown(cs, Public)), TransferParticipantsVar::new_unknown(cs, Derived), commitment_scheme.as_known(cs, Public), utxo_set.as_known(cs, Public), @@ -570,14 +568,14 @@ where #[inline] fn known_variables( &self, - commitment_scheme: &T::CommitmentScheme, - utxo_set: &T::UtxoSet, - cs: &mut ConstraintSystem<T>, + commitment_scheme: &C::CommitmentScheme, + utxo_set: &C::UtxoSet, + cs: &mut ConstraintSystem<C>, ) -> ( - Option<T::AssetIdVar>, - TransferParticipantsVar<T, SOURCES, SENDERS, RECEIVERS, SINKS>, - T::CommitmentSchemeVar, - T::UtxoSetVar, + Option<C::AssetIdVar>, + TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS>, + C::CommitmentSchemeVar, + C::UtxoSetVar, ) { ( self.public.asset_id.map(|id| id.as_known(cs, Public)), @@ -590,14 +588,14 @@ where /// Builds constraints for transfer validity proof/verifier. #[inline] fn build_constraints( - base_asset_id: Option<T::AssetIdVar>, - participants: TransferParticipantsVar<T, SOURCES, SENDERS, RECEIVERS, SINKS>, - commitment_scheme: T::CommitmentSchemeVar, - utxo_set: T::UtxoSetVar, - cs: &mut ConstraintSystem<T>, + base_asset_id: Option<C::AssetIdVar>, + participants: TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS>, + commitment_scheme: C::CommitmentSchemeVar, + utxo_set: C::UtxoSetVar, + cs: &mut ConstraintSystem<C>, ) { - let mut sender_sum = T::AssetBalanceVar::from_default(cs, Secret); - let mut receiver_sum = T::AssetBalanceVar::from_default(cs, Secret); + let mut sender_sum = C::AssetBalanceVar::from_default(cs, Secret); + let mut receiver_sum = C::AssetBalanceVar::from_default(cs, Secret); participants .sources @@ -642,17 +640,17 @@ where #[allow(clippy::type_complexity)] // FIXME: We will have to refactor this at some point. #[inline] pub fn generate_context<R>( - commitment_scheme: &T::CommitmentScheme, - utxo_set: &T::UtxoSet, + commitment_scheme: &C::CommitmentScheme, + utxo_set: &C::UtxoSet, rng: &mut R, - ) -> Option<Result<(ProvingContext<T>, VerifyingContext<T>), ProofSystemError<T>>> + ) -> Option<Result<(ProvingContext<C>, VerifyingContext<C>), ProofSystemError<C>>> where R: CryptoRng + RngCore + ?Sized, { if SENDERS == 0 { return None; } - let mut cs = T::ProofSystem::for_unknown(); + let mut cs = C::ProofSystem::for_unknown(); let (base_asset_id, participants, commitment_scheme, utxo_set) = Self::unknown_variables(commitment_scheme, utxo_set, &mut cs); Self::build_constraints( @@ -662,7 +660,7 @@ where utxo_set, &mut cs, ); - Some(T::ProofSystem::generate_context(cs, rng)) + Some(C::ProofSystem::generate_context(cs, rng)) } /// Generates a validity proof for this transfer. @@ -671,18 +669,18 @@ where #[inline] pub fn generate_proof<R>( &self, - commitment_scheme: &T::CommitmentScheme, - utxo_set: &T::UtxoSet, - context: &ProvingContext<T>, + commitment_scheme: &C::CommitmentScheme, + utxo_set: &C::UtxoSet, + context: &ProvingContext<C>, rng: &mut R, - ) -> Option<Result<Proof<T>, ProofSystemError<T>>> + ) -> Option<Result<Proof<C>, ProofSystemError<C>>> where R: CryptoRng + RngCore + ?Sized, { if SENDERS == 0 { return None; } - let mut cs = T::ProofSystem::for_known(); + let mut cs = C::ProofSystem::for_known(); let (base_asset_id, participants, commitment_scheme, utxo_set) = self.known_variables(commitment_scheme, utxo_set, &mut cs); Self::build_constraints( @@ -692,18 +690,18 @@ where utxo_set, &mut cs, ); - Some(T::ProofSystem::generate_proof(cs, context, rng)) + Some(C::ProofSystem::generate_proof(cs, context, rng)) } /// Converts `self` into its ledger post. #[inline] pub fn into_post<R>( self, - commitment_scheme: &T::CommitmentScheme, - utxo_set: &T::UtxoSet, - context: &ProvingContext<T>, + commitment_scheme: &C::CommitmentScheme, + utxo_set: &C::UtxoSet, + context: &ProvingContext<C>, rng: &mut R, - ) -> Result<TransferPost<T, SOURCES, SENDERS, RECEIVERS, SINKS>, ProofSystemError<T>> + ) -> Result<TransferPost<C, SOURCES, SENDERS, RECEIVERS, SINKS>, ProofSystemError<C>> where R: CryptoRng + RngCore + ?Sized, { @@ -720,38 +718,38 @@ where /// Transfer Participants Variable struct TransferParticipantsVar< - T, + C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize, > where - T: Configuration, + C: Configuration, { /// Source Variables - sources: Vec<T::AssetBalanceVar>, + sources: Vec<C::AssetBalanceVar>, /// Sender Variables - senders: Vec<SenderVar<T>>, + senders: Vec<SenderVar<C>>, /// Receiver Variables - receivers: Vec<ReceiverVar<T>>, + receivers: Vec<ReceiverVar<C>>, /// Sink Variables - sinks: Vec<T::AssetBalanceVar>, + sinks: Vec<C::AssetBalanceVar>, } -impl<T, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> - Variable<ConstraintSystem<T>> for TransferParticipantsVar<T, SOURCES, SENDERS, RECEIVERS, SINKS> +impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> + Variable<ConstraintSystem<C>> for TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> where - T: Configuration, + C: Configuration, { - type Type = Transfer<T, SOURCES, SENDERS, RECEIVERS, SINKS>; + type Type = Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>; type Mode = Derived; #[inline] - fn new(cs: &mut ConstraintSystem<T>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new(cs: &mut ConstraintSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self { sources: this @@ -782,7 +780,7 @@ where Allocation::Unknown(mode) => Self { sources: (0..SOURCES) .into_iter() - .map(|_| T::AssetBalanceVar::new_unknown(cs, Public)) + .map(|_| C::AssetBalanceVar::new_unknown(cs, Public)) .collect(), senders: (0..SENDERS) .into_iter() @@ -794,58 +792,166 @@ where .collect(), sinks: (0..SINKS) .into_iter() - .map(|_| T::AssetBalanceVar::new_unknown(cs, Public)) + .map(|_| C::AssetBalanceVar::new_unknown(cs, Public)) .collect(), }, } } } +/// Transfer Post Error +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "TransferLedgerError<C, L>: Clone"), + Copy(bound = "TransferLedgerError<C, L>: Copy"), + Debug(bound = "TransferLedgerError<C, L>: Debug"), + Eq(bound = "TransferLedgerError<C, L>: Eq"), + Hash(bound = "TransferLedgerError<C, L>: Hash"), + PartialEq(bound = "TransferLedgerError<C, L>: PartialEq") +)] +pub enum TransferPostError<C, L> +where + C: Configuration, + L: TransferLedger<C>, +{ + /// Sender Post Error + Sender(SenderPostError<C, L>), + + /// Receiver Post Error + Receiver(ReceiverPostError<C, L>), + + /// Invalid Transfer Proof Error + /// + /// Validity of the transfer could not be proved by the ledger. + InvalidProof, + + /// Ledger Error + LedgerError(TransferLedgerError<C, L>), +} + +impl<C, L> From<SenderPostError<C, L>> for TransferPostError<C, L> +where + C: Configuration, + L: TransferLedger<C>, +{ + #[inline] + fn from(err: SenderPostError<C, L>) -> Self { + Self::Sender(err) + } +} + +impl<C, L> From<ReceiverPostError<C, L>> for TransferPostError<C, L> +where + C: Configuration, + L: TransferLedger<C>, +{ + #[inline] + fn from(err: ReceiverPostError<C, L>) -> Self { + Self::Receiver(err) + } +} + /// Transfer Post pub struct TransferPost< - T, + C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize, > where - T: Configuration, + C: Configuration, { /// Sender Posts - pub sender_posts: [SenderPost<T>; SENDERS], + sender_posts: [SenderPost<C>; SENDERS], /// Receiver Posts - pub receiver_posts: [ReceiverPost<T>; RECEIVERS], + receiver_posts: [ReceiverPost<C>; RECEIVERS], /// Validity Proof - pub validity_proof: Option<Proof<T>>, + /// + /// This value is only inhabited by a proof when the transfer shape requires one. + validity_proof: Option<Proof<C>>, } -impl<T, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> - TransferPost<T, SOURCES, SENDERS, RECEIVERS, SINKS> +impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> + TransferPost<C, SOURCES, SENDERS, RECEIVERS, SINKS> where - T: Configuration, + C: Configuration, { - /// Posts the [`TransferPost`] to the `ledger`. + /// Validates `self` on the transfer `ledger`. #[inline] - pub fn post<L>(self, ledger: &mut L) -> Result<(), PostError<L>> + pub fn validate<L>( + self, + ledger: &L, + ) -> Result<TransferPostingKey<C, L, SOURCES, SENDERS, RECEIVERS, SINKS>, TransferPostError<C, L>> where - L: Ledger< - VoidNumber = VoidNumber<T>, - Utxo = Utxo<T>, - EncryptedAsset = EncryptedMessage<T::IntegratedEncryptionScheme>, - UtxoSet = T::UtxoSet, - ProofSystem = T::ProofSystem, - > + ?Sized, + L: TransferLedger<C>, { - for sender_post in IntoIterator::into_iter(self.sender_posts) { - sender_post.post(ledger)?; - } - for receiver_post in IntoIterator::into_iter(self.receiver_posts) { - receiver_post.post(ledger)?; + let sender_posting_keys = + fallible_array_map(self.sender_posts, move |s| s.validate(ledger))?; + let receiver_posting_keys = + fallible_array_map(self.receiver_posts, move |s| s.validate(ledger))?; + let validity_proof = match ledger + .is_valid(self.validity_proof) + .map_err(TransferPostError::LedgerError)? + { + Some(key) => key, + _ => return Err(TransferPostError::InvalidProof), + }; + Ok(TransferPostingKey { + sender_posting_keys, + receiver_posting_keys, + validity_proof, + }) + } +} + +/// Transfer Posting Key +pub struct TransferPostingKey< + C, + L, + const SOURCES: usize, + const SENDERS: usize, + const RECEIVERS: usize, + const SINKS: usize, +> where + C: Configuration, + L: TransferLedger<C>, +{ + /// Sender Posting Keys + sender_posting_keys: [SenderPostingKey<C, L>; SENDERS], + + /// Receiver Posting Keys + receiver_posting_keys: [ReceiverPostingKey<C, L>; RECEIVERS], + + /// Validity Proof Posting Key + validity_proof: L::ValidProof, +} + +impl< + C, + L, + const SOURCES: usize, + const SENDERS: usize, + const RECEIVERS: usize, + const SINKS: usize, + > TransferPostingKey<C, L, SOURCES, SENDERS, RECEIVERS, SINKS> +where + C: Configuration, + L: TransferLedger<C>, +{ + /// Posts `self` to the transfer `ledger`. + #[inline] + pub fn post( + self, + super_key: &TransferLedgerSuperPostingKey<C, L>, + ledger: &mut L, + ) -> Result<(), TransferLedgerError<C, L>> { + for key in self.sender_posting_keys { + key.post(&(self.validity_proof, *super_key), ledger)?; } - if let Some(proof) = self.validity_proof { - ledger.check_proof(proof)?; + for key in self.receiver_posting_keys { + key.post(&(self.validity_proof, *super_key), ledger)?; } Ok(()) } @@ -932,18 +1038,18 @@ pub mod canonical { impl_shape!(MintShape, 1, 0, 1, 0); /// Mint Transaction - pub type Mint<T> = transfer_alias!(T, MintShape); + pub type Mint<C> = transfer_alias!(C, MintShape); /// Mint Transaction Ledger Post - pub type MintPost<T> = transfer_post_alias!(T, MintShape); + pub type MintPost<C> = transfer_post_alias!(C, MintShape); - impl<T> Mint<T> + impl<C> Mint<C> where - T: Configuration, + C: Configuration, { /// Builds a [`Mint`] from `asset` and `receiver`. #[inline] - pub fn build(asset: Asset, receiver: Receiver<T>) -> Self { + pub fn build(asset: Asset, receiver: Receiver<C>) -> Self { Self::new( asset.id, [asset.value], @@ -956,14 +1062,14 @@ pub mod canonical { /// Builds a [`Mint`]-[`OpenSpend`] pair from an `identity` and an `asset`. #[inline] pub fn from_identity<R>( - identity: Identity<T>, - commitment_scheme: &T::CommitmentScheme, + identity: Identity<C>, + commitment_scheme: &C::CommitmentScheme, asset: Asset, rng: &mut R, - ) -> Result<(Mint<T>, OpenSpend<T>), IntegratedEncryptionSchemeError<T>> + ) -> Result<(Mint<C>, OpenSpend<C>), IntegratedEncryptionSchemeError<C>> where R: CryptoRng + RngCore + ?Sized, - Standard: Distribution<AssetParameters<T>>, + Standard: Distribution<AssetParameters<C>>, { let InternalReceiver { receiver, @@ -984,20 +1090,20 @@ pub mod canonical { impl_shape!(PrivateTransferShape, 0, 2, 2, 0); /// Private Transfer Transaction - pub type PrivateTransfer<T> = transfer_alias!(T, PrivateTransferShape); + pub type PrivateTransfer<C> = transfer_alias!(C, PrivateTransferShape); /// Private Transfer Transaction Post - pub type PrivateTransferPost<T> = transfer_post_alias!(T, PrivateTransferShape); + pub type PrivateTransferPost<C> = transfer_post_alias!(C, PrivateTransferShape); - impl<T> PrivateTransfer<T> + impl<C> PrivateTransfer<C> where - T: Configuration, + C: Configuration, { /// Builds a [`PrivateTransfer`] from `senders` and `receivers`. #[inline] pub fn build( - senders: [Sender<T>; PrivateTransferShape::SENDERS], - receivers: [Receiver<T>; PrivateTransferShape::RECEIVERS], + senders: [Sender<C>; PrivateTransferShape::SENDERS], + receivers: [Receiver<C>; PrivateTransferShape::RECEIVERS], ) -> Self { Self::new( Default::default(), @@ -1020,20 +1126,20 @@ pub mod canonical { impl_shape!(ReclaimShape, 0, 2, 1, 1); /// Reclaim Transaction - pub type Reclaim<T> = transfer_alias!(T, ReclaimShape); + pub type Reclaim<C> = transfer_alias!(C, ReclaimShape); /// Reclaim Transaction Post - pub type ReclaimPost<T> = transfer_post_alias!(T, ReclaimShape); + pub type ReclaimPost<C> = transfer_post_alias!(C, ReclaimShape); - impl<T> Reclaim<T> + impl<C> Reclaim<C> where - T: Configuration, + C: Configuration, { /// Builds a [`Reclaim`] from `senders`, `receiver`, and `reclaim`. #[inline] pub fn build( - senders: [Sender<T>; ReclaimShape::SENDERS], - receiver: Receiver<T>, + senders: [Sender<C>; ReclaimShape::SENDERS], + receiver: Receiver<C>, reclaim: Asset, ) -> Self { Self::new( @@ -1047,6 +1153,7 @@ pub mod canonical { } } +/* TODO: /// Testing Framework #[cfg(feature = "test")] #[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] @@ -1057,9 +1164,9 @@ pub mod test { /// #[inline] - pub fn sample_sender<T, R>(commitment_scheme: &T::CommitmentScheme, rng: &mut R) + pub fn sample_sender<C, R>(commitment_scheme: &C::CommitmentScheme, rng: &mut R) where - T: Configuration, + C: Configuration, R: CryptoRng + RngCore + ?Sized, { // TODO: let _ = Mint::from_identity(rng.gen(), commitment_scheme, rng.gen(), rng); @@ -1067,3 +1174,4 @@ pub mod test { todo!() } } +*/ diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index d8989f552..b561df1f8 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -29,7 +29,6 @@ use crate::{ ShieldedIdentity, }, keys::{self, DerivedSecretKeyGenerator, ExternalKeys, InternalKeys, KeyKind}, - ledger::Ledger, transfer::{ self, canonical::{Mint, PrivateTransfer, Reclaim}, @@ -329,6 +328,7 @@ where None } + /* TODO: /// Updates `self` with new information from the ledger. pub fn pull_updates<L>(&mut self, ledger: &L) where @@ -349,6 +349,7 @@ where let _ = ledger; todo!() } + */ /// Generates a new [`ShieldedIdentity`] to receive assets to this wallet via an external /// transaction. diff --git a/manta-pay/src/accounting/ledger.rs b/manta-pay/src/accounting/ledger.rs index 3cfdf23d9..19ad7e3b5 100644 --- a/manta-pay/src/accounting/ledger.rs +++ b/manta-pay/src/accounting/ledger.rs @@ -16,8 +16,8 @@ //! Ledger Implementation -// FIXME: How should we handle serdes? -// FIXME: Use "incremental merkle tree". +// FIXME: Migrate this to new ledger abstraction. This will most likely go to `wallet` since it +// represents the "native" ledger rather than the blockchain ledger. use crate::{ accounting::config::{Configuration, ConstraintSystem, ProofSystem}, @@ -31,12 +31,10 @@ use blake2::{ digest::{Update, VariableOutput}, VarBlake2s, }; -use manta_accounting::{identity, Ledger as LedgerTrait, ProofPostError}; +use manta_accounting::identity; use manta_crypto::{ - constraint::{ - self, reflection::HasAllocation, Allocation, Constant, ProofSystem as _, Variable, - }, - merkle_tree::{self, latest_node::LatestNode, MerkleTree, Tree}, + constraint::{self, reflection::HasAllocation, Allocation, Constant, Variable}, + merkle_tree::{self, latest_node::LatestNode, Tree}, set::{constraint::VerifiedSetVariable, ContainmentProof, Set, VerifiedSet}, }; use manta_util::{as_bytes, concatenate, into_array_unchecked}; @@ -85,7 +83,7 @@ pub struct UtxoSet { shards: [UtxoShard; Self::SHARD_COUNT], /// UTXO Set - utxos: BTreeSet<Utxo>, + _utxos: BTreeSet<Utxo>, /// Merkle Tree Parameters parameters: Parameters, @@ -99,7 +97,7 @@ impl UtxoSet { pub fn new(parameters: Parameters) -> Self { Self { shards: into_array_unchecked(vec![Default::default(); Self::SHARD_COUNT]), - utxos: Default::default(), + _utxos: Default::default(), parameters, } } @@ -129,6 +127,7 @@ impl UtxoSet { /// Returns `true` if the `utxo` belongs to the shard it would be stored in. #[inline] pub fn utxo_exists(&self, utxo: &Utxo) -> bool { + let _ = utxo; // TODO: self.utxos.contains(utxo) todo!() } @@ -188,11 +187,11 @@ impl VerifiedSet for UtxoSet { &self, item: &Self::Item, ) -> Result<ContainmentProof<Self>, Self::ContainmentError> { - // TODO: Return a more informative error. + let _ = item; - /* FIXME: This is not implementable! Need to split this functionality into a better - * abstraction. + // TODO: Return a more informative error. + /* TODO: let utxos = &self.shards[Self::shard_index(item)].utxos; match utxos.iter().position(move |u| u == item) { Some(index) => MerkleTree::new(&self.parameters, utxos) @@ -201,10 +200,9 @@ impl VerifiedSet for UtxoSet { .ok_or(()), _ => Err(()), } - */ - unimplemented!() + todo!() } } @@ -249,74 +247,14 @@ impl VerifiedSetVariable<ConstraintSystem> for UtxoSetVar { /// Ledger pub struct Ledger { /// Void Numbers - void_numbers: Vec<VoidNumber>, + _void_numbers: Vec<VoidNumber>, /// Unspent Transaction Outputs - utxos: UtxoSet, + _utxos: UtxoSet, /// Encrypted Assets - encrypted_assets: Vec<EncryptedAsset>, + _encrypted_assets: Vec<EncryptedAsset>, /// Verifying Context - verifying_context: <ProofSystem as constraint::ProofSystem>::VerifyingContext, -} - -impl LedgerTrait for Ledger { - type VoidNumber = VoidNumber; - - type Utxo = Utxo; - - type UtxoSet = UtxoSet; - - type EncryptedAsset = EncryptedAsset; - - type ProofSystem = ProofSystem; - - #[inline] - fn utxos(&self) -> &Self::UtxoSet { - &self.utxos - } - - #[inline] - fn is_unspent(&self, void_number: &Self::VoidNumber) -> bool { - !self.void_numbers.contains(void_number) - } - - #[inline] - fn try_post_void_number( - &mut self, - void_number: Self::VoidNumber, - ) -> Result<(), Self::VoidNumber> { - if self.void_numbers.contains(&void_number) { - return Err(void_number); - } - self.void_numbers.push(void_number); - Ok(()) - } - - #[inline] - fn try_post_utxo(&mut self, utxo: Self::Utxo) -> Result<(), Self::Utxo> { - self.utxos.try_insert(utxo) - } - - #[inline] - fn try_post_encrypted_asset( - &mut self, - encrypted_asset: Self::EncryptedAsset, - ) -> Result<(), Self::EncryptedAsset> { - self.encrypted_assets.push(encrypted_asset); - Ok(()) - } - - #[inline] - fn check_proof( - &self, - proof: <Self::ProofSystem as constraint::ProofSystem>::Proof, - ) -> Result<(), ProofPostError<Self>> { - match Self::ProofSystem::verify_proof(&self.verifying_context, &proof) { - Ok(true) => Ok(()), - Ok(false) => Err(ProofPostError::InvalidProof(proof, None)), - Err(err) => Err(ProofPostError::InvalidProof(proof, Some(err))), - } - } + _verifying_context: <ProofSystem as constraint::ProofSystem>::VerifyingContext, } From 196689c097bbc23ab02b83d683faba311d0e0c5f Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 23 Sep 2021 15:40:13 -0400 Subject: [PATCH 055/275] start wallet-ledger interaction interface --- manta-accounting/src/identity.rs | 30 +- manta-accounting/src/transfer.rs | 521 ++++++++++++++++++--------- manta-accounting/src/wallet.rs | 177 +++++++-- manta-pay/src/accounting/transfer.rs | 12 +- 4 files changed, 496 insertions(+), 244 deletions(-) diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index ad0bf98c5..8d0eb69d4 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -38,8 +38,8 @@ use rand::{ pub(crate) mod prelude { #[doc(inline)] pub use super::{ - Configuration, Identity, Receiver, Sender, SenderError, ShieldedIdentity, Spend, - SpendError, Utxo, VoidNumber, + Identity, Receiver, Sender, SenderError, ShieldedIdentity, Spend, SpendError, Utxo, + VoidNumber, }; } @@ -1102,11 +1102,9 @@ where /// # Safety /// /// This type must be some wrapper around [`VoidNumber`] which can only be constructed by this - /// implementation of [`SenderLedger`]. This is to prevent that [`post`] is called before - /// [`validate`]. - /// - /// [`post`]: SenderPostingKey::post - /// [`validate`]: SenderPost::validate + /// implementation of [`SenderLedger`]. This is to prevent that [`spend`](Self::spend) is + /// called before [`is_unspent`](Self::is_unspent) and + /// [`is_valid_utxo_state`](Self::is_valid_utxo_state). type ValidVoidNumber; /// Valid Utxo State Posting Key @@ -1114,12 +1112,11 @@ where /// # Safety /// /// This type must be some wrapper around [`S::Public`] which can only be constructed by this - /// implementation of [`SenderLedger`]. This is to prevent that [`post`] is called before - /// [`validate`]. + /// implementation of [`SenderLedger`]. This is to prevent that [`spend`](Self::spend) is + /// called before [`is_unspent`](Self::is_unspent) and + /// [`is_valid_utxo_state`](Self::is_valid_utxo_state). /// /// [`S::Public`]: VerifiedSet::Public - /// [`post`]: SenderPostingKey::post - /// [`validate`]: SenderPost::validate type ValidUtxoState; /// Super Posting Key @@ -1143,7 +1140,7 @@ where /// /// Failure to match the ledger state means that the sender was constructed under an invalid or /// older state of the ledger. - fn has_same_utxo_state( + fn is_valid_utxo_state( &self, public_input: S::Public, ) -> Result<Option<Self::ValidUtxoState>, Self::Error>; @@ -1228,7 +1225,7 @@ where _ => return Err(SenderPostError::AssetSpent), }, utxo_containment_proof_public_input: match ledger - .has_same_utxo_state(self.utxo_containment_proof_public_input) + .is_valid_utxo_state(self.utxo_containment_proof_public_input) .map_err(SenderPostError::LedgerError)? { Some(key) => key, @@ -1354,11 +1351,8 @@ where /// # Safety /// /// This type must be some wrapper around [`Utxo`] which can only be constructed by this - /// implementation of [`ReceiverLedger`]. This is to prevent that [`post`] is called - /// before [`validate`]. - /// - /// [`post`]: ReceiverPostingKey::post - /// [`validate`]: ReceiverPost::validate + /// implementation of [`ReceiverLedger`]. This is to prevent that [`register`](Self::register) + /// is called before [`is_not_registered`](Self::is_not_registered). type ValidUtxo; /// Super Posting Key diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 3147f9699..8502353fd 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -16,11 +16,13 @@ //! Transfer Protocols -// TODO: See if we can get rid of the `Copy` restriction on `ValidProof` and `SuperPostingKey`. -// TODO: Add `generate_context`/`generate_proof` logic to `SecretTransfer`. -// TODO: Have a compile-time way to check if proof generation is used for a certain shape, -// so that the `generate_context`/`generate_proof` functions can only exist on the right -// shape implementations, instead of failing at runtime with `None`. +// FIXME: Make sure that either (a) no empty transfer can be built, or (b) empty transfers work +// properly i.e. do nothing. +// TODO: See if we can get rid of the `Copy` restriction on `ValidProof` and `SuperPostingKey`. +// TODO: Add `generate_context`/`generate_proof` logic to `SecretTransfer`. +// TODO: Have a compile-time way to check if proof generation is used for a certain shape, +// so that the `generate_context`/`generate_proof` functions can only exist on the right +// shape implementations, instead of failing at runtime with `None`. use crate::{ asset::{sample_asset_balances, Asset, AssetBalance, AssetBalances, AssetId}, @@ -38,7 +40,7 @@ use manta_crypto::{ ies::{EncryptedMessage, IntegratedEncryptionScheme}, set::{constraint::VerifiedSetVariable, VerifiedSet}, }; -use manta_util::{array_map, fallible_array_map, mixed_chain, Either}; +use manta_util::{mixed_chain, Either}; use rand::{ distributions::{Distribution, Standard}, CryptoRng, RngCore, @@ -46,100 +48,21 @@ use rand::{ /// Returns `true` if the transfer with this shape would have no public side. #[inline] -const fn has_no_public_side< - const SOURCES: usize, - const SENDERS: usize, - const RECEIVERS: usize, - const SINKS: usize, ->() -> bool { - SOURCES == 0 && SINKS == 0 -} - -/// Public Transfer Protocol -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct PublicTransfer<const SOURCES: usize, const SINKS: usize> { - /// Asset Id - pub asset_id: Option<AssetId>, - - /// Public Asset Sources - pub sources: AssetBalances<SOURCES>, - - /// Public Asset Sinks - pub sinks: AssetBalances<SINKS>, -} - -#[allow(clippy::derivable_impls)] // NOTE: We only want default on the `<0, 0>` setting. -impl Default for PublicTransfer<0, 0> { - #[inline] - fn default() -> Self { - Self::new_unchecked(None, [], []) - } -} - -impl<const SOURCES: usize, const SINKS: usize> PublicTransfer<SOURCES, SINKS> { - /// Builds a new [`PublicTransfer`]. - #[inline] - pub const fn new( - asset_id: AssetId, - sources: AssetBalances<SOURCES>, - sinks: AssetBalances<SINKS>, - ) -> Self { - Self::new_unchecked( - if has_no_public_side::<SOURCES, 0, 0, SINKS>() { - None - } else { - Some(asset_id) - }, - sources, - sinks, - ) - } - - /// Builds a new [`PublicTransfer`] without checking if the asset id should be `None`. - #[inline] - const fn new_unchecked( - asset_id: Option<AssetId>, - sources: AssetBalances<SOURCES>, - sinks: AssetBalances<SINKS>, - ) -> Self { - Self { - asset_id, - sources, - sinks, - } - } - - /// Returns the sum of the asset values of the sources in this transfer. - #[inline] - pub fn source_sum(&self) -> AssetBalance { - self.sources.iter().sum() - } - - /// Returns the sum of the asset values of the sinks in this transfer. - #[inline] - pub fn sink_sum(&self) -> AssetBalance { - self.sinks.iter().sum() - } - - /// Validates the transaction by checking that the [`source_sum`](Self::source_sum) - /// equals the [`sink_sum`](Self::sink_sum). - #[inline] - pub fn is_valid(&self) -> bool { - self.source_sum() == self.sink_sum() - } +const fn has_no_public_side( + sources: usize, + senders: usize, + receivers: usize, + sinks: usize, +) -> bool { + let _ = (senders, receivers); + sources == 0 && sinks == 0 } -impl<const SOURCES: usize, const SINKS: usize> Distribution<PublicTransfer<SOURCES, SINKS>> - for Standard -{ - #[inline] - fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> PublicTransfer<SOURCES, SINKS> { - PublicTransfer::new( - self.sample(rng), - sample_asset_balances(rng), - sample_asset_balances(rng), - ) - } +/// Returns `true` if the transfer with this shape requires a proof. +#[inline] +const fn requires_proof(sources: usize, senders: usize, receivers: usize, sinks: usize) -> bool { + let _ = (sources, receivers, sinks); + senders > 0 } /// [`Transfer`] Configuration @@ -284,18 +207,264 @@ where /// Ledger Error type Error; - /// Checks that the transfer proof is valid. + /// Checks that the transfer `proof` is valid. /// /// # Implementation Note /// - /// This should always succeed on `None` inputs which correspond to transfers which do not - /// require a validation proof. + /// This should always succeed on inputs that demonstrate that they do not require a + /// proof, by revealing their transaction shape. fn is_valid( &self, - proof: Option<Proof<C>>, + proof: ShapedProof<C>, ) -> Result<Option<Self::ValidProof>, TransferLedgerError<C, Self>>; } +/// Dynamic Transfer Shape +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +pub struct DynamicShape { + /// Number of Sources + pub sources: usize, + + /// Number of Senders + pub senders: usize, + + /// Number of Receivers + pub receivers: usize, + + /// Number of Sinks + pub sinks: usize, +} + +impl DynamicShape { + /// Builds a new [`DynamicShape`] from `sources`, `senders`, `receivers`, and `sinks`. + #[inline] + pub const fn new(sources: usize, senders: usize, receivers: usize, sinks: usize) -> Self { + Self { + sources, + senders, + receivers, + sinks, + } + } + + /// Returns `true` whenever a transfer of the given shape `self` requires a validity proof. + #[inline] + pub const fn requires_proof(&self) -> bool { + requires_proof(self.sources, self.senders, self.receivers, self.sinks) + } + + /// Checks if `self` matches the static [`Shape`] given by `S`. + #[inline] + pub fn matches<S>(&self) -> bool + where + S: Shape, + { + S::SOURCES == self.sources + && S::SENDERS == self.senders + && S::RECEIVERS == self.receivers + && S::SINKS == self.sinks + } +} + +impl<S> From<S> for DynamicShape +where + S: Shape, +{ + #[inline] + fn from(shape: S) -> Self { + let _ = shape; + Self { + sources: S::SOURCES, + senders: S::SENDERS, + receivers: S::RECEIVERS, + sinks: S::SINKS, + } + } +} + +/// Transfer Shape with Possible Validity [`Proof`] +pub enum ShapedProof<C> +where + C: Configuration, +{ + /// Shape with a Validity Proof + WithProof(ShapedProofEntry<C>), + + /// Shape with no Proof + NoProof(DynamicShape), +} + +impl<C> ShapedProof<C> +where + C: Configuration, +{ + /// Builds a new [`ShapedProof`] for the given `shape` and `proof`. + #[inline] + fn new_proof(shape: DynamicShape, proof: Proof<C>) -> Self { + Self::WithProof(ShapedProofEntry::new(shape, proof)) + } + + /// Returns the shape of the transfer which generated `self`. + #[inline] + pub fn shape(&self) -> &DynamicShape { + match self { + Self::WithProof(ShapedProofEntry { shape, .. }) => shape, + Self::NoProof(shape) => shape, + } + } + + /// Returns the validity proof for the transfer which generated `self`. + #[inline] + pub fn proof(&self) -> Option<&Proof<C>> { + match self { + Self::WithProof(ShapedProofEntry { proof, .. }) => Some(proof), + _ => None, + } + } +} + +impl<C> From<DynamicShape> for ShapedProof<C> +where + C: Configuration, +{ + #[inline] + fn from(shape: DynamicShape) -> Self { + Self::NoProof(shape) + } +} + +/// Entry for [`ShapedProof`] with a [`Proof`] +pub struct ShapedProofEntry<C> +where + C: Configuration, +{ + /// Transfer Shape + shape: DynamicShape, + + /// Validity Proof + proof: Proof<C>, +} + +impl<C> ShapedProofEntry<C> +where + C: Configuration, +{ + /// Builds a new [`ShapedProofEntry`] for the given `shape` and `proof`. + #[inline] + fn new(shape: DynamicShape, proof: Proof<C>) -> Self { + Self { shape, proof } + } + + /// Returns the validity `proof` along with its `shape`. + #[inline] + pub fn open(self) -> (DynamicShape, Proof<C>) { + (self.shape, self.proof) + } +} + +impl<C> From<ShapedProofEntry<C>> for (DynamicShape, Proof<C>) +where + C: Configuration, +{ + #[inline] + fn from(entry: ShapedProofEntry<C>) -> Self { + entry.open() + } +} + +/// Public Transfer Protocol +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct PublicTransfer<const SOURCES: usize, const SINKS: usize> { + /// Asset Id + pub asset_id: Option<AssetId>, + + /// Public Asset Sources + pub sources: AssetBalances<SOURCES>, + + /// Public Asset Sinks + pub sinks: AssetBalances<SINKS>, +} + +impl<const SOURCES: usize, const SINKS: usize> PublicTransfer<SOURCES, SINKS> { + /// Builds a new [`PublicTransfer`]. + #[inline] + pub const fn new( + asset_id: AssetId, + sources: AssetBalances<SOURCES>, + sinks: AssetBalances<SINKS>, + ) -> Self { + Self::new_unchecked( + if has_no_public_side(SOURCES, 0, 0, SINKS) { + None + } else { + Some(asset_id) + }, + sources, + sinks, + ) + } + + /// Builds a new [`PublicTransfer`] without checking if the asset id should be `None`. + #[inline] + const fn new_unchecked( + asset_id: Option<AssetId>, + sources: AssetBalances<SOURCES>, + sinks: AssetBalances<SINKS>, + ) -> Self { + Self { + asset_id, + sources, + sinks, + } + } + + /// Returns the shape of this public transfer. + #[inline] + pub fn shape(&self) -> DynamicShape { + DynamicShape::new(SOURCES, 0, 0, SINKS) + } + + /// Returns the sum of the asset values of the sources in this transfer. + #[inline] + pub fn source_sum(&self) -> AssetBalance { + self.sources.iter().sum() + } + + /// Returns the sum of the asset values of the sinks in this transfer. + #[inline] + pub fn sink_sum(&self) -> AssetBalance { + self.sinks.iter().sum() + } + + /// Validates the transaction by checking that the [`source_sum`](Self::source_sum) + /// equals the [`sink_sum`](Self::sink_sum). + #[inline] + pub fn is_valid(&self) -> bool { + self.source_sum() == self.sink_sum() + } +} + +#[allow(clippy::derivable_impls)] // NOTE: We only want default on the `<0, 0>` setting. +impl Default for PublicTransfer<0, 0> { + #[inline] + fn default() -> Self { + Self::new_unchecked(None, [], []) + } +} + +impl<const SOURCES: usize, const SINKS: usize> Distribution<PublicTransfer<SOURCES, SINKS>> + for Standard +{ + #[inline] + fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> PublicTransfer<SOURCES, SINKS> { + PublicTransfer::new( + self.sample(rng), + sample_asset_balances(rng), + sample_asset_balances(rng), + ) + } +} + /// Secret Transfer Protocol pub struct SecretTransfer<C, const SENDERS: usize, const RECEIVERS: usize> where @@ -364,6 +533,12 @@ where Self { senders, receivers } } + /// Returns the shape of this secret transfer. + #[inline] + pub fn shape(&self) -> DynamicShape { + DynamicShape::new(0, SENDERS, RECEIVERS, 0) + } + /// Returns an iterator over all the asset ids in this transfer. #[inline] fn asset_id_iter(&self) -> impl '_ + Iterator<Item = AssetId> { @@ -408,7 +583,7 @@ where utxo_set: &C::UtxoSet, context: &ProvingContext<C>, rng: &mut R, - ) -> Result<TransferPost<C, 0, SENDERS, RECEIVERS, 0>, ProofSystemError<C>> + ) -> Result<TransferPost<C>, ProofSystemError<C>> where R: CryptoRng + RngCore + ?Sized, { @@ -499,6 +674,12 @@ where } } + /// Returns the shape of this transfer. + #[inline] + pub fn shape(&self) -> DynamicShape { + DynamicShape::new(SOURCES, SENDERS, RECEIVERS, SINKS) + } + /// Checks that there is one unique asset id for all participants in this transfer. #[inline] pub fn has_unique_asset_id(&self) -> bool { @@ -551,13 +732,13 @@ where C::CommitmentSchemeVar, C::UtxoSetVar, ) { - let base_asset_id = if has_no_public_side::<SOURCES, SENDERS, RECEIVERS, SINKS>() { + let base_asset_id = if has_no_public_side(SOURCES, SENDERS, RECEIVERS, SINKS) { None } else { - Some(()) + Some(C::AssetIdVar::new_unknown(cs, Public)) }; ( - base_asset_id.map(|_| C::AssetIdVar::new_unknown(cs, Public)), + base_asset_id, TransferParticipantsVar::new_unknown(cs, Derived), commitment_scheme.as_known(cs, Public), utxo_set.as_known(cs, Public), @@ -647,7 +828,7 @@ where where R: CryptoRng + RngCore + ?Sized, { - if SENDERS == 0 { + if !requires_proof(SOURCES, SENDERS, RECEIVERS, SINKS) { return None; } let mut cs = C::ProofSystem::for_unknown(); @@ -665,7 +846,8 @@ where /// Generates a validity proof for this transfer. /// - /// Returns `None` if proof generation does not apply for this kind of transfer. + /// Returns `Ok(ShapedProof::NoProof(_))` if proof generation does not apply for this kind + /// of transfer. #[inline] pub fn generate_proof<R>( &self, @@ -673,12 +855,13 @@ where utxo_set: &C::UtxoSet, context: &ProvingContext<C>, rng: &mut R, - ) -> Option<Result<Proof<C>, ProofSystemError<C>>> + ) -> Result<ShapedProof<C>, ProofSystemError<C>> where R: CryptoRng + RngCore + ?Sized, { - if SENDERS == 0 { - return None; + let shape = DynamicShape::new(SOURCES, SENDERS, RECEIVERS, SINKS); + if !shape.requires_proof() { + return Ok(shape.into()); } let mut cs = C::ProofSystem::for_known(); let (base_asset_id, participants, commitment_scheme, utxo_set) = @@ -690,7 +873,10 @@ where utxo_set, &mut cs, ); - Some(C::ProofSystem::generate_proof(cs, context, rng)) + Ok(ShapedProof::new_proof( + shape, + C::ProofSystem::generate_proof(cs, context, rng)?, + )) } /// Converts `self` into its ledger post. @@ -701,17 +887,18 @@ where utxo_set: &C::UtxoSet, context: &ProvingContext<C>, rng: &mut R, - ) -> Result<TransferPost<C, SOURCES, SENDERS, RECEIVERS, SINKS>, ProofSystemError<C>> + ) -> Result<TransferPost<C>, ProofSystemError<C>> where R: CryptoRng + RngCore + ?Sized, { Ok(TransferPost { - validity_proof: match self.generate_proof(commitment_scheme, utxo_set, context, rng) { - Some(result) => Some(result?), - _ => None, - }, - sender_posts: array_map(self.secret.senders, Sender::into_post), - receiver_posts: array_map(self.secret.receivers, Receiver::into_post), + validity_proof: self.generate_proof(commitment_scheme, utxo_set, context, rng)?, + sender_posts: IntoIterator::into_iter(self.secret.senders) + .map(Sender::into_post) + .collect(), + receiver_posts: IntoIterator::into_iter(self.secret.receivers) + .map(Receiver::into_post) + .collect(), }) } } @@ -852,90 +1039,80 @@ where } /// Transfer Post -pub struct TransferPost< - C, - const SOURCES: usize, - const SENDERS: usize, - const RECEIVERS: usize, - const SINKS: usize, -> where +pub struct TransferPost<C> +where C: Configuration, { /// Sender Posts - sender_posts: [SenderPost<C>; SENDERS], + sender_posts: Vec<SenderPost<C>>, /// Receiver Posts - receiver_posts: [ReceiverPost<C>; RECEIVERS], + receiver_posts: Vec<ReceiverPost<C>>, /// Validity Proof /// /// This value is only inhabited by a proof when the transfer shape requires one. - validity_proof: Option<Proof<C>>, + validity_proof: ShapedProof<C>, } -impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> - TransferPost<C, SOURCES, SENDERS, RECEIVERS, SINKS> +impl<C> TransferPost<C> where C: Configuration, { + /// Returns the shape of the transfer which generated this post. + #[inline] + pub fn shape(&self) -> &DynamicShape { + self.validity_proof.shape() + } + /// Validates `self` on the transfer `ledger`. #[inline] pub fn validate<L>( self, ledger: &L, - ) -> Result<TransferPostingKey<C, L, SOURCES, SENDERS, RECEIVERS, SINKS>, TransferPostError<C, L>> + ) -> Result<TransferPostingKey<C, L>, TransferPostError<C, L>> where L: TransferLedger<C>, { - let sender_posting_keys = - fallible_array_map(self.sender_posts, move |s| s.validate(ledger))?; - let receiver_posting_keys = - fallible_array_map(self.receiver_posts, move |s| s.validate(ledger))?; - let validity_proof = match ledger - .is_valid(self.validity_proof) - .map_err(TransferPostError::LedgerError)? - { - Some(key) => key, - _ => return Err(TransferPostError::InvalidProof), - }; Ok(TransferPostingKey { - sender_posting_keys, - receiver_posting_keys, - validity_proof, + sender_posting_keys: self + .sender_posts + .into_iter() + .map(move |s| s.validate(ledger)) + .collect::<Result<_, _>>()?, + receiver_posting_keys: self + .receiver_posts + .into_iter() + .map(move |r| r.validate(ledger)) + .collect::<Result<_, _>>()?, + validity_proof: match ledger + .is_valid(self.validity_proof) + .map_err(TransferPostError::LedgerError)? + { + Some(key) => key, + _ => return Err(TransferPostError::InvalidProof), + }, }) } } /// Transfer Posting Key -pub struct TransferPostingKey< - C, - L, - const SOURCES: usize, - const SENDERS: usize, - const RECEIVERS: usize, - const SINKS: usize, -> where +pub struct TransferPostingKey<C, L> +where C: Configuration, L: TransferLedger<C>, { /// Sender Posting Keys - sender_posting_keys: [SenderPostingKey<C, L>; SENDERS], + sender_posting_keys: Vec<SenderPostingKey<C, L>>, /// Receiver Posting Keys - receiver_posting_keys: [ReceiverPostingKey<C, L>; RECEIVERS], + receiver_posting_keys: Vec<ReceiverPostingKey<C, L>>, /// Validity Proof Posting Key validity_proof: L::ValidProof, } -impl< - C, - L, - const SOURCES: usize, - const SENDERS: usize, - const RECEIVERS: usize, - const SINKS: usize, - > TransferPostingKey<C, L, SOURCES, SENDERS, RECEIVERS, SINKS> +impl<C, L> TransferPostingKey<C, L> where C: Configuration, L: TransferLedger<C>, @@ -1020,13 +1197,6 @@ pub mod canonical { }; } - /// Builds a new [`TransferPost`] alias using the given shape type. - macro_rules! transfer_post_alias { - ($t:ident, $shape:tt) => { - alias_type!(TransferPost, $t, $shape) - }; - } - /// Mint Transaction Shape /// /// ```text @@ -1040,9 +1210,6 @@ pub mod canonical { /// Mint Transaction pub type Mint<C> = transfer_alias!(C, MintShape); - /// Mint Transaction Ledger Post - pub type MintPost<C> = transfer_post_alias!(C, MintShape); - impl<C> Mint<C> where C: Configuration, @@ -1092,9 +1259,6 @@ pub mod canonical { /// Private Transfer Transaction pub type PrivateTransfer<C> = transfer_alias!(C, PrivateTransferShape); - /// Private Transfer Transaction Post - pub type PrivateTransferPost<C> = transfer_post_alias!(C, PrivateTransferShape); - impl<C> PrivateTransfer<C> where C: Configuration, @@ -1128,9 +1292,6 @@ pub mod canonical { /// Reclaim Transaction pub type Reclaim<C> = transfer_alias!(C, ReclaimShape); - /// Reclaim Transaction Post - pub type ReclaimPost<C> = transfer_post_alias!(C, ReclaimShape); - impl<C> Reclaim<C> where C: Configuration, diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index b561df1f8..3acbdf956 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -18,25 +18,28 @@ // TODO: How to manage accounts? Wallet should have a fixed account or not? // TODO: Is recovery different than just building a fresh `Wallet` instance? -// TODO: Add encrypt-to-disk and decrypt-from-disk methods to save the wallet state. // TODO: Add query builder for encrypted asset search (internal/external, gap_limit, start_index) +// TODO: Merge `AssetMap` and `LocalLedger` into one container and have it query `LedgerSource` +// instead of `Wallet`. Then `Wallet` just has access to local ledger (also async). +// Have then a "light wallet" which is just `Wallet` and a "heavy wallet" where the +// local ledger and asset map are built-in to it. use crate::{ asset::{Asset, AssetBalance, AssetId}, fs::{Load, Save}, identity::{ self, AssetParameters, Identity, InternalReceiver, InternalReceiverError, OpenSpend, - ShieldedIdentity, + ShieldedIdentity, Utxo, VoidNumber, }, keys::{self, DerivedSecretKeyGenerator, ExternalKeys, InternalKeys, KeyKind}, transfer::{ self, canonical::{Mint, PrivateTransfer, Reclaim}, - IntegratedEncryptionSchemeError, + EncryptedAsset, IntegratedEncryptionSchemeError, TransferPost, }, }; use alloc::vec::Vec; -use core::{convert::Infallible, fmt::Debug, hash::Hash}; +use core::{convert::Infallible, fmt::Debug, future::Future, hash::Hash}; use manta_crypto::ies::{EncryptedMessage, IntegratedEncryptionScheme}; use rand::{ distributions::{Distribution, Standard}, @@ -126,11 +129,94 @@ pub trait AssetMap { } } +/// Ledger Source +pub trait LedgerSource<C> +where + C: transfer::Configuration, +{ + /// Sync Future Type + /// + /// Future for the [`sync`](Self::sync) method. + type SyncFuture: Future<Output = Result<SyncResponse<C, Self>, Self::Error>>; + + /// Send Future Type + /// + /// Future for the [`send`](Self::send) method. + type SendFuture: Future<Output = Result<SendResponse<C, Self>, Self::Error>>; + + /// Ledger State Checkpoint Type + type Checkpoint: Default + Ord; + + /// Error Type + type Error; + + /// Pulls data from the ledger starting from `checkpoint`, returning the current + /// [`Checkpoint`](Self::Checkpoint). + fn sync(&self, checkpoint: &Self::Checkpoint) -> Self::SyncFuture; + + /// Pulls all of the data from the entire history of the ledger, returning the current + /// [`Checkpoint`](Self::Checkpoint). + #[inline] + fn sync_all(&self) -> Self::SyncFuture { + self.sync(&Default::default()) + } + + /// Sends `transfers` to the ledger, returning the current [`Checkpoint`](Self::Checkpoint) + /// and the status of the transfers. + fn send(&self, transfers: Vec<TransferPost<C>>) -> Self::SendFuture; +} + +/// Ledger Source Sync Response +/// +/// This `struct` is created by the [`sync`](LedgerSource::sync) method on [`LedgerSource`]. +/// See its documentation for more. +pub struct SyncResponse<C, LS> +where + C: transfer::Configuration, + LS: LedgerSource<C> + ?Sized, +{ + /// Current Ledger Checkpoint + pub checkpoint: LS::Checkpoint, + + /// New Void Numbers + pub void_numbers: Vec<VoidNumber<C>>, + + /// New UTXOS + pub utxos: Vec<Utxo<C>>, + + /// New Encrypted Assets + pub encrypted_assets: Vec<EncryptedAsset<C>>, +} + +/// Ledger Source Send Response +/// +/// This `struct` is created by the [`send`](LedgerSource::send) method on [`LedgerSource`]. +/// See its documentation for more. +pub struct SendResponse<C, LS> +where + C: transfer::Configuration, + LS: LedgerSource<C> + ?Sized, +{ + /// Current Ledger Checkpoint + pub checkpoint: LS::Checkpoint, + + /// Ledger Responses + // FIXME: Design responses. + pub responses: Vec<bool>, +} + +/// Local Ledger +pub trait LocalLedger { + /// Ledger State Checkpoint Type + type Checkpoint: Default + Ord; +} + /// Wallet -pub struct Wallet<D, M> +pub struct Wallet<D, M, LL> where D: DerivedSecretKeyGenerator, M: AssetMap, + LL: LocalLedger, { /// Secret Key Source secret_key_source: D, @@ -149,12 +235,16 @@ where /// Secret Asset Map secret_assets: M, + + /// Local Ledger Checkpoint + checkpoint: LL::Checkpoint, } -impl<D, M> Wallet<D, M> +impl<D, M, LL> Wallet<D, M, LL> where D: DerivedSecretKeyGenerator, M: AssetMap, + LL: LocalLedger, { /// Builds a new [`Wallet`] for `account` from a `secret_key_source`. #[inline] @@ -186,6 +276,7 @@ where internal_index: Default::default(), public_assets, secret_assets, + checkpoint: Default::default(), } } @@ -328,11 +419,12 @@ where None } - /* TODO: - /// Updates `self` with new information from the ledger. - pub fn pull_updates<L>(&mut self, ledger: &L) + /// Synchronize `self` with the `ledger`. + #[inline] + pub async fn sync<C, LS>(&mut self, ledger: &LS) -> Result<(), LS::Error> where - L: Ledger, + C: transfer::Configuration, + LS: LedgerSource<C, Checkpoint = LL::Checkpoint> + ?Sized, { // TODO: Pull updates from the ledger: // 1. Download the new encrypted notes and try to decrypt them using the latest @@ -346,10 +438,19 @@ where // send/recv messages to/from it, i.e. the `Ledger` abstraction may not be correct, // it may have to be a different trait or two traits. - let _ = ledger; + let SyncResponse { + checkpoint, + void_numbers, + utxos, + encrypted_assets, + } = ledger.sync(&self.checkpoint).await?; + + let _ = (void_numbers, utxos, encrypted_assets); + + self.checkpoint = checkpoint; + todo!() } - */ /// Generates a new [`ShieldedIdentity`] to receive assets to this wallet via an external /// transaction. @@ -390,16 +491,16 @@ where /// Builds a [`Mint`] transaction to mint `asset` and returns the [`OpenSpend`] for that asset. #[inline] - pub fn mint<T, R>( + pub fn mint<C, R>( &mut self, - commitment_scheme: &T::CommitmentScheme, + commitment_scheme: &C::CommitmentScheme, asset: Asset, rng: &mut R, - ) -> Result<(Mint<T>, OpenSpend<T>), MintError<D, T>> + ) -> Result<(Mint<C>, OpenSpend<C>), MintError<D, C>> where - T: transfer::Configuration<SecretKey = D::SecretKey>, + C: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, - Standard: Distribution<AssetParameters<T>>, + Standard: Distribution<AssetParameters<C>>, { Mint::from_identity( self.next_internal_identity() @@ -413,15 +514,15 @@ where /// TODO: Builds [`PrivateTransfer`] transactions to send `asset` to an `external_receiver`. #[inline] - pub fn batch_private_transfer_external<T, R>( + pub fn batch_private_transfer_external<C, R>( &self, - commitment_scheme: &T::CommitmentScheme, + commitment_scheme: &C::CommitmentScheme, asset: Asset, - external_receiver: ShieldedIdentity<T, T::IntegratedEncryptionScheme>, + external_receiver: ShieldedIdentity<C, C::IntegratedEncryptionScheme>, rng: &mut R, - ) -> Option<Vec<PrivateTransfer<T>>> + ) -> Option<Vec<PrivateTransfer<C>>> where - T: transfer::Configuration, + C: transfer::Configuration, R: CryptoRng + RngCore + ?Sized, { // TODO: spec: @@ -436,14 +537,14 @@ where /// TODO: Builds a [`Reclaim`] transaction. #[inline] - pub fn reclaim<T, R>( + pub fn reclaim<C, R>( &self, - commitment_scheme: &T::CommitmentScheme, + commitment_scheme: &C::CommitmentScheme, asset: Asset, rng: &mut R, - ) -> Option<Reclaim<T>> + ) -> Option<Reclaim<C>> where - T: transfer::Configuration, + C: transfer::Configuration, R: CryptoRng + RngCore + ?Sized, { let _ = (commitment_scheme, asset, rng); @@ -452,10 +553,11 @@ where } } -impl<D, M> Load for Wallet<D, M> +impl<D, M, LL> Load for Wallet<D, M, LL> where D: DerivedSecretKeyGenerator + Load, M: AssetMap + Default, + LL: LocalLedger, { type Path = D::Path; @@ -472,10 +574,11 @@ where } } -impl<D, M> Save for Wallet<D, M> +impl<D, M, LL> Save for Wallet<D, M, LL> where D: DerivedSecretKeyGenerator + Save, M: AssetMap + Default, + LL: LocalLedger, { type Path = D::Path; @@ -500,21 +603,21 @@ where /// [`mint`]: Wallet::mint #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "D::Error: Clone, IntegratedEncryptionSchemeError<T>: Clone"), - Copy(bound = "D::Error: Copy, IntegratedEncryptionSchemeError<T>: Copy"), - Debug(bound = "D::Error: Debug, IntegratedEncryptionSchemeError<T>: Debug"), - Eq(bound = "D::Error: Eq, IntegratedEncryptionSchemeError<T>: Eq"), - Hash(bound = "D::Error: Hash, IntegratedEncryptionSchemeError<T>: Hash"), - PartialEq(bound = "D::Error: PartialEq, IntegratedEncryptionSchemeError<T>: PartialEq") + Clone(bound = "D::Error: Clone, IntegratedEncryptionSchemeError<C>: Clone"), + Copy(bound = "D::Error: Copy, IntegratedEncryptionSchemeError<C>: Copy"), + Debug(bound = "D::Error: Debug, IntegratedEncryptionSchemeError<C>: Debug"), + Eq(bound = "D::Error: Eq, IntegratedEncryptionSchemeError<C>: Eq"), + Hash(bound = "D::Error: Hash, IntegratedEncryptionSchemeError<C>: Hash"), + PartialEq(bound = "D::Error: PartialEq, IntegratedEncryptionSchemeError<C>: PartialEq") )] -pub enum MintError<D, T> +pub enum MintError<D, C> where D: DerivedSecretKeyGenerator, - T: transfer::Configuration, + C: transfer::Configuration, { /// Secret Key Generator Error SecretKeyError(D::Error), /// Encryption Error - EncryptionError(IntegratedEncryptionSchemeError<T>), + EncryptionError(IntegratedEncryptionSchemeError<C>), } diff --git a/manta-pay/src/accounting/transfer.rs b/manta-pay/src/accounting/transfer.rs index b70a76c23..4791a52c2 100644 --- a/manta-pay/src/accounting/transfer.rs +++ b/manta-pay/src/accounting/transfer.rs @@ -17,22 +17,16 @@ //! Transfer Implementations use crate::accounting::config::Configuration; -use manta_accounting::transfer::canonical; +use manta_accounting::transfer::{self, canonical}; /// Mint Transaction Type pub type Mint = canonical::Mint<Configuration>; -/// Mint Transaction Post Type -pub type MintPost = canonical::MintPost<Configuration>; - /// Private Transfer Transaction Type pub type PrivateTransfer = canonical::PrivateTransfer<Configuration>; -/// Private Transfer Transaction Post Type -pub type PrivateTransferPost = canonical::PrivateTransferPost<Configuration>; - /// Reclaim Transaction Type pub type Reclaim = canonical::Reclaim<Configuration>; -/// Reclaim Transaction Post Type -pub type ReclaimPost = canonical::ReclaimPost<Configuration>; +/// Transfer Post Type +pub type TransferPost = transfer::TransferPost<Configuration>; From f5beddfc066dadf9a1d3f287731bc02e7dcb585c Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 23 Sep 2021 19:25:26 -0400 Subject: [PATCH 056/275] WIP: build abstractions for signer/wallet/ledger --- manta-accounting/src/fs.rs | 22 + manta-accounting/src/keys.rs | 161 ++++++- manta-accounting/src/wallet.rs | 761 ++++++++++++++++++++------------- 3 files changed, 640 insertions(+), 304 deletions(-) diff --git a/manta-accounting/src/fs.rs b/manta-accounting/src/fs.rs index 3933f1c35..70eda8368 100644 --- a/manta-accounting/src/fs.rs +++ b/manta-accounting/src/fs.rs @@ -33,6 +33,15 @@ pub trait Load: Sized { P: AsRef<Self::Path>; } +/// Filesystem Encrypted Loading with Extra Data +pub trait LoadWith<T>: Load { + /// Loads an element of type `Self` along with additional data from `path` unlocking it with + /// the `loading_key`. + fn load_with<P>(path: P, loading_key: &Self::LoadingKey) -> Result<(Self, T), Self::Error> + where + P: AsRef<Self::Path>; +} + /// Filesystem Encrypted Saving pub trait Save { /// Path Type @@ -50,6 +59,19 @@ pub trait Save { P: AsRef<Self::Path>; } +/// Filesystem Encrypted Saving with Extra Data +pub trait SaveWith<T>: Save { + /// Saves `self` along with `additional` data to `path` locking it with the `saving_key`. + fn save_with<P>( + self, + additional: T, + path: P, + saving_key: &Self::SavingKey, + ) -> Result<(), Self::Error> + where + P: AsRef<Self::Path>; +} + /// Cocoon [`Load`] and [`Save`] Adapters #[cfg(feature = "std")] #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] diff --git a/manta-accounting/src/keys.rs b/manta-accounting/src/keys.rs index 3824b353a..2fd33c06b 100644 --- a/manta-accounting/src/keys.rs +++ b/manta-accounting/src/keys.rs @@ -22,6 +22,8 @@ // TODO: Check to make sure we conform to the specification and then make a note about it in the // module documentation, and add a link to the specification. +use core::{fmt::Debug, hash::Hash}; + /// Secret Key Generator Trait pub trait SecretKeyGenerator { /// Secret Key Type @@ -54,7 +56,7 @@ pub trait DerivedSecretKeyGenerator { /// Key Generation Error type Error; - /// Generates a new secret key determined by `is_external` for the `account` with + /// Generates a new secret key determined by `kind` for the `account` with /// the given `index`. fn generate_key( &self, @@ -63,6 +65,26 @@ pub trait DerivedSecretKeyGenerator { index: &Self::Index, ) -> Result<Self::SecretKey, Self::Error>; + /// Generates a new external secret key for the `account` with the given `index`. + #[inline] + fn generate_external_key( + &self, + account: &Self::Account, + index: &Self::Index, + ) -> Result<Self::SecretKey, Self::Error> { + self.generate_key(KeyKind::External, account, index) + } + + /// Generates a new internal secret key for the `account` with the given `index`. + #[inline] + fn generate_internal_key( + &self, + account: &Self::Account, + index: &Self::Index, + ) -> Result<Self::SecretKey, Self::Error> { + self.generate_key(KeyKind::Internal, account, index) + } + /// Builds a [`SecretKeyGenerator`] for external keys associated to `account`. #[inline] fn external_keys<'s>(&'s self, account: &'s Self::Account) -> ExternalKeys<'s, Self> { @@ -122,10 +144,10 @@ impl KeyKind { } } -/// Generates an internal or external secret key according to the [`DerivedSecretKeyGenerator`] +/// Generates an external or internal secret key according to the [`DerivedSecretKeyGenerator`] /// protocol and increments the running `index`. #[inline] -fn next_key<D>( +pub fn next_key<D>( source: &D, kind: KeyKind, account: &D::Account, @@ -150,7 +172,9 @@ pub fn next_external<D>( where D: DerivedSecretKeyGenerator + ?Sized, { - next_key(source, KeyKind::External, account, index) + let secret_key = source.generate_external_key(account, index)?; + index.increment(); + Ok(secret_key) } /// Generates an internal secret key according to the [`DerivedSecretKeyGenerator`] protocol @@ -164,7 +188,9 @@ pub fn next_internal<D>( where D: DerivedSecretKeyGenerator + ?Sized, { - next_key(source, KeyKind::Internal, account, index) + let secret_key = source.generate_internal_key(account, index)?; + index.increment(); + Ok(secret_key) } /// Keys @@ -313,3 +339,128 @@ where self.generate_key().ok() } } + +/// Account Index Manager +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "D::Account: Clone, D::Index: Clone"), + Copy(bound = "D::Account: Copy, D::Index: Copy"), + Debug(bound = "D::Account: Debug, D::Index: Debug"), + Default(bound = "D::Account: Default, D::Index: Default"), + Eq(bound = "D::Account: Eq, D::Index: Eq"), + Hash(bound = "D::Account: Hash, D::Index: Hash"), + PartialEq(bound = "D::Account: PartialEq, D::Index: PartialEq") +)] +pub struct Account<D> +where + D: DerivedSecretKeyGenerator, +{ + /// Account Identifier + pub account: D::Account, + + /// External Transaction Running Index + pub external_index: D::Index, + + /// Internal Transaction Running Index + pub internal_index: D::Index, +} + +impl<D> Account<D> +where + D: DerivedSecretKeyGenerator, +{ + /// Builds a new [`Account`] for the given `account` identifier. + #[inline] + pub fn new(account: D::Account) -> Self { + Self::with_indices(account, Default::default(), Default::default()) + } + + /// Builds a new [`Account`] for the given `account` identifier with starting indices + /// `external_index` and `internal_index`. + #[inline] + pub fn with_indices( + account: D::Account, + external_index: D::Index, + internal_index: D::Index, + ) -> Self { + Self { + account, + external_index, + internal_index, + } + } + + /// Resets the external and internal running indices to their default values. + #[inline] + pub fn reset(&mut self) -> &mut Self { + self.external_index = Default::default(); + self.internal_index = Default::default(); + self + } + + /// Generates a new external key for this account. + #[inline] + pub fn external_key(&self, source: &D) -> Result<D::SecretKey, D::Error> { + source.generate_external_key(&self.account, &self.external_index) + } + + /// Generates a new internal key for this account. + #[inline] + pub fn internal_key(&self, source: &D) -> Result<D::SecretKey, D::Error> { + source.generate_internal_key(&self.account, &self.internal_index) + } + + /// Generates the next external key for this account, incrementing the `external_index`. + #[inline] + pub fn next_external_key(&mut self, source: &D) -> Result<D::SecretKey, D::Error> { + next_external(source, &self.account, &mut self.external_index) + } + + /// Generates the next internal key for this account, incrementing the `internal_index`. + #[inline] + pub fn next_internal_key(&mut self, source: &D) -> Result<D::SecretKey, D::Error> { + next_internal(source, &self.account, &mut self.internal_index) + } + + /// Returns an [`ExternalKeys`] generator starting from the current external index. + #[inline] + pub fn external_keys<'s>(&'s self, source: &'s D) -> ExternalKeys<'s, D> { + self.external_keys_from_index(source, self.external_index.clone()) + } + + /// Returns an [`InternalKeys`] generator starting from the current internal index. + #[inline] + pub fn internal_keys<'s>(&'s self, source: &'s D) -> InternalKeys<'s, D> { + self.internal_keys_from_index(source, self.internal_index.clone()) + } + + /// Returns an [`ExternalKeys`] generator starting from the given `index`. + #[inline] + pub fn external_keys_from_index<'s>( + &'s self, + source: &'s D, + index: D::Index, + ) -> ExternalKeys<'s, D> { + source.external_keys_from_index(&self.account, index) + } + + /// Returns an [`InternalKeys`] generator starting from the given `index`. + #[inline] + pub fn internal_keys_from_index<'s>( + &'s self, + source: &'s D, + index: D::Index, + ) -> InternalKeys<'s, D> { + source.internal_keys_from_index(&self.account, index) + } +} + +impl<D> AsRef<D::Account> for Account<D> +where + D: DerivedSecretKeyGenerator, +{ + #[inline] + fn as_ref(&self) -> &D::Account { + &self.account + } +} diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index 3acbdf956..bd309d393 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -26,310 +26,153 @@ use crate::{ asset::{Asset, AssetBalance, AssetId}, - fs::{Load, Save}, + fs::{Load, LoadWith, Save, SaveWith}, identity::{ self, AssetParameters, Identity, InternalReceiver, InternalReceiverError, OpenSpend, ShieldedIdentity, Utxo, VoidNumber, }, - keys::{self, DerivedSecretKeyGenerator, ExternalKeys, InternalKeys, KeyKind}, + keys::{Account, DerivedSecretKeyGenerator, ExternalKeys, InternalKeys, KeyKind}, transfer::{ self, canonical::{Mint, PrivateTransfer, Reclaim}, EncryptedAsset, IntegratedEncryptionSchemeError, TransferPost, }, }; -use alloc::vec::Vec; -use core::{convert::Infallible, fmt::Debug, future::Future, hash::Hash}; +use alloc::{vec, vec::Vec}; +use core::{convert::Infallible, fmt::Debug, future::Future, hash::Hash, marker::PhantomData}; use manta_crypto::ies::{EncryptedMessage, IntegratedEncryptionScheme}; use rand::{ distributions::{Distribution, Standard}, CryptoRng, RngCore, }; -/// Asset Map -pub trait AssetMap { - /// Returns the current balance associated with this `id`. - fn balance(&self, id: AssetId) -> AssetBalance; - - /// Returns `true` if `self` contains at least `asset.value` of the asset of kind `asset.id`. - #[inline] - fn contains(&self, asset: Asset) -> bool { - self.balance(asset.id) >= asset.value - } - - /// Sets the asset balance for `id` to `value`. - fn set_balance(&mut self, id: AssetId, value: AssetBalance); - - /// Sets the asset balance for `asset.id` to `asset.value`. - #[inline] - fn set_asset(&mut self, asset: Asset) { - self.set_balance(asset.id, asset.value) - } - - /// Mutates the asset balance for `id` to the result of `f` if it succeeds. - #[inline] - #[must_use = "this only modifies the stored value if the function call succeeded"] - fn mutate<F, E>(&mut self, id: AssetId, f: F) -> Result<AssetBalance, E> - where - F: FnOnce(AssetBalance) -> Result<AssetBalance, E>, - { - // TODO: use `try_trait_v2` when it comes out - f(self.balance(id)).map(move |v| { - self.set_balance(id, v); - v - }) - } - - /// Performs a deposit of value `asset.value` into the balance of `asset.id`, - /// returning the new balance for this `asset.id` if it did not overflow. - /// - /// To skip the overflow check, use [`deposit_unchecked`](Self::deposit_unchecked) instead. - #[inline] - #[must_use = "this only modifies the stored value if the addition did not overflow"] - fn deposit(&mut self, asset: Asset) -> Option<AssetBalance> { - self.mutate(asset.id, move |v| v.checked_add(asset.value).ok_or(())) - .ok() - } - - /// Performs a deposit of value `asset.value` into the balance of `asset.id`, - /// without checking for overflow, returning the new balance for this `asset.id`. - /// - /// # Panics - /// - /// This function panics on overflow. To explicitly check for overflow, use - /// [`deposit`](Self::deposit) instead. - #[inline] - fn deposit_unchecked(&mut self, asset: Asset) -> AssetBalance { - self.mutate::<_, Infallible>(asset.id, move |v| Ok(v + asset.value)) - .unwrap() - } - - /// Performs a withdrawl of value `asset.value` from the balance of `asset.id`, - /// returning the new balance for this `asset.id` if it did not overflow. - /// - /// To skip the overflow check, use [`withdraw_unchecked`](Self::withdraw_unchecked) instead. - #[inline] - #[must_use = "this only modifies the stored value if the subtraction did not overflow"] - fn withdraw(&mut self, asset: Asset) -> Option<AssetBalance> { - self.mutate(asset.id, move |v| v.checked_sub(asset.value).ok_or(())) - .ok() - } - - /// Performs a withdrawl of value `asset.value` from the balance of `asset.id`, - /// without checking for overflow, returning the new balance for this `asset.id`. - /// - /// # Panics - /// - /// This function panics on overflow. To explicitly check for overflow, use - /// [`withdraw`](Self::withdraw) instead. - #[inline] - fn withdraw_unchecked(&mut self, asset: Asset) -> AssetBalance { - self.mutate::<_, Infallible>(asset.id, move |v| Ok(v - asset.value)) - .unwrap() - } -} - -/// Ledger Source -pub trait LedgerSource<C> -where - C: transfer::Configuration, -{ - /// Sync Future Type - /// - /// Future for the [`sync`](Self::sync) method. - type SyncFuture: Future<Output = Result<SyncResponse<C, Self>, Self::Error>>; - - /// Send Future Type - /// - /// Future for the [`send`](Self::send) method. - type SendFuture: Future<Output = Result<SendResponse<C, Self>, Self::Error>>; - - /// Ledger State Checkpoint Type - type Checkpoint: Default + Ord; - - /// Error Type - type Error; - - /// Pulls data from the ledger starting from `checkpoint`, returning the current - /// [`Checkpoint`](Self::Checkpoint). - fn sync(&self, checkpoint: &Self::Checkpoint) -> Self::SyncFuture; - - /// Pulls all of the data from the entire history of the ledger, returning the current - /// [`Checkpoint`](Self::Checkpoint). - #[inline] - fn sync_all(&self) -> Self::SyncFuture { - self.sync(&Default::default()) - } - - /// Sends `transfers` to the ledger, returning the current [`Checkpoint`](Self::Checkpoint) - /// and the status of the transfers. - fn send(&self, transfers: Vec<TransferPost<C>>) -> Self::SendFuture; -} - -/// Ledger Source Sync Response -/// -/// This `struct` is created by the [`sync`](LedgerSource::sync) method on [`LedgerSource`]. -/// See its documentation for more. -pub struct SyncResponse<C, LS> -where - C: transfer::Configuration, - LS: LedgerSource<C> + ?Sized, -{ - /// Current Ledger Checkpoint - pub checkpoint: LS::Checkpoint, - - /// New Void Numbers - pub void_numbers: Vec<VoidNumber<C>>, - - /// New UTXOS - pub utxos: Vec<Utxo<C>>, - - /// New Encrypted Assets - pub encrypted_assets: Vec<EncryptedAsset<C>>, -} - -/// Ledger Source Send Response -/// -/// This `struct` is created by the [`send`](LedgerSource::send) method on [`LedgerSource`]. -/// See its documentation for more. -pub struct SendResponse<C, LS> -where - C: transfer::Configuration, - LS: LedgerSource<C> + ?Sized, -{ - /// Current Ledger Checkpoint - pub checkpoint: LS::Checkpoint, - - /// Ledger Responses - // FIXME: Design responses. - pub responses: Vec<bool>, -} - -/// Local Ledger -pub trait LocalLedger { - /// Ledger State Checkpoint Type - type Checkpoint: Default + Ord; -} - -/// Wallet -pub struct Wallet<D, M, LL> +/// Signer +pub struct Signer<D> where D: DerivedSecretKeyGenerator, - M: AssetMap, - LL: LocalLedger, { /// Secret Key Source secret_key_source: D, - /// Wallet Account - account: D::Account, - - /// External Transaction Running Index - external_index: D::Index, - - /// Internal Transaction Running Index - internal_index: D::Index, - - /// Public Asset Map - public_assets: M, - - /// Secret Asset Map - secret_assets: M, - - /// Local Ledger Checkpoint - checkpoint: LL::Checkpoint, + /// Signer Account + account: Account<D>, } -impl<D, M, LL> Wallet<D, M, LL> +impl<D> Signer<D> where D: DerivedSecretKeyGenerator, - M: AssetMap, - LL: LocalLedger, { - /// Builds a new [`Wallet`] for `account` from a `secret_key_source`. + /// Builds a new [`Signer`] for `account` from a `secret_key_source`. #[inline] - pub fn new(secret_key_source: D, account: D::Account) -> Self - where - M: Default, - { - Self::with_balances( + pub fn new(secret_key_source: D, account: D::Account) -> Self { + Self::with_account(secret_key_source, Account::new(account)) + } + + /// Builds a new [`Signer`] for `account` from a `secret_key_source`. + #[inline] + pub fn with_account(secret_key_source: D, account: Account<D>) -> Self { + Self { secret_key_source, account, - Default::default(), - Default::default(), - ) + } } - /// Builds a new [`Wallet`] for `account` from a `secret_key_source` and pre-built - /// `public_assets` map and `secret_assets` map. + /// Builds a new [`Signer`] for `account` from a `secret_key_source` with starting indices + /// `external_index` and `internal_index`. #[inline] - pub fn with_balances( + pub fn with_indices( secret_key_source: D, account: D::Account, - public_assets: M, - secret_assets: M, + external_index: D::Index, + internal_index: D::Index, ) -> Self { - Self { + Self::with_account( secret_key_source, - account, - external_index: Default::default(), - internal_index: Default::default(), - public_assets, - secret_assets, - checkpoint: Default::default(), - } + Account::with_indices(account, external_index, internal_index), + ) } - /// Generates the next external key for this wallet. + /// Generates the next external key for this signer. #[inline] fn next_external_key(&mut self) -> Result<D::SecretKey, D::Error> { - keys::next_external( - &self.secret_key_source, - &self.account, - &mut self.external_index, - ) + self.account.next_external_key(&self.secret_key_source) } - /// Generates the next internal key for this wallet. + /// Generates the next internal key for this signer. #[inline] fn next_internal_key(&mut self) -> Result<D::SecretKey, D::Error> { - keys::next_internal( - &self.secret_key_source, - &self.account, - &mut self.internal_index, - ) + self.account.next_internal_key(&self.secret_key_source) } - /// Generates the next external identity for this wallet. + /// Generates the next external identity for this signer. #[inline] - fn next_external_identity<C>(&mut self) -> Result<Identity<C>, D::Error> + pub fn next_external_identity<C>(&mut self) -> Result<Identity<C>, D::Error> where C: identity::Configuration<SecretKey = D::SecretKey>, { self.next_external_key().map(Identity::new) } - /// Generates the next internal identity for this wallet. + /// Generates the next internal identity for this signer. #[inline] - fn next_internal_identity<C>(&mut self) -> Result<Identity<C>, D::Error> + pub fn next_internal_identity<C>(&mut self) -> Result<Identity<C>, D::Error> where C: identity::Configuration<SecretKey = D::SecretKey>, { self.next_internal_key().map(Identity::new) } + /// Generates a new [`ShieldedIdentity`] to receive assets to this account via an external + /// transaction. + #[inline] + pub fn generate_shielded_identity<C, I>( + &mut self, + commitment_scheme: &C::CommitmentScheme, + ) -> Result<ShieldedIdentity<C, I>, D::Error> + where + C: identity::Configuration<SecretKey = D::SecretKey>, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + Standard: Distribution<AssetParameters<C>>, + { + self.next_external_identity() + .map(move |identity| identity.into_shielded(commitment_scheme)) + } + + /// Generates a new [`InternalReceiver`] to receive `asset` to this account via an + /// internal transaction. + #[inline] + pub fn generate_internal_receiver<C, I, R>( + &mut self, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + rng: &mut R, + ) -> Result<InternalReceiver<C, I>, InternalReceiverError<InternalKeys<D>, I>> + where + C: identity::Configuration<SecretKey = D::SecretKey>, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + R: CryptoRng + RngCore + ?Sized, + Standard: Distribution<AssetParameters<C>>, + { + self.next_internal_identity() + .map_err(InternalReceiverError::SecretKeyError)? + .into_internal_receiver(commitment_scheme, asset, rng) + .map_err(InternalReceiverError::EncryptionError) + } + + /* TODO: Revisit how this is designed (this is part of recovery/ledger-sync): + /// Returns an [`ExternalKeys`] generator starting from the given `index`. #[inline] fn external_keys_from_index(&self, index: D::Index) -> ExternalKeys<D> { self.secret_key_source - .external_keys_from_index(&self.account, index) + .external_keys_from_index(self.account.as_ref(), index) } /// Returns an [`InternalKeys`] generator starting from the given `index`. #[inline] fn internal_keys_from_index(&self, index: D::Index) -> InternalKeys<D> { self.secret_key_source - .internal_keys_from_index(&self.account, index) + .internal_keys_from_index(self.account.as_ref(), index) } /// Looks for an [`OpenSpend`] for this `encrypted_asset` by checking every secret key @@ -391,7 +234,7 @@ where } /// Looks for an [`OpenSpend`] for this `encrypted_asset`, only trying `gap_limit`-many - /// external and internal keys starting from `index`. + /// external and internal keys starting from `external_index` and `internal_index`. #[inline] pub fn find_open_spend<C, I>( &self, @@ -419,74 +262,391 @@ where None } - /// Synchronize `self` with the `ledger`. + */ +} + +impl<D> Load for Signer<D> +where + D: DerivedSecretKeyGenerator + LoadWith<Account<D>>, +{ + type Path = D::Path; + + type LoadingKey = D::LoadingKey; + + type Error = <D as Load>::Error; + #[inline] - pub async fn sync<C, LS>(&mut self, ledger: &LS) -> Result<(), LS::Error> + fn load<P>(path: P, loading_key: &Self::LoadingKey) -> Result<Self, Self::Error> where - C: transfer::Configuration, - LS: LedgerSource<C, Checkpoint = LL::Checkpoint> + ?Sized, + P: AsRef<Self::Path>, { - // TODO: Pull updates from the ledger: - // 1. Download the new encrypted notes and try to decrypt them using the latest - // keys that haven't been used. - // 2. Download the new vns and utxos and check that we can still spend all the - // tokens we think we can spend. - // 3. compute the new deposits and withdrawls - - // TODO: Have something which represents a "local-ledger" state. This ledger state can - // exist "at" the wallet, or can exist elsewhere, but the wallet must be able to - // send/recv messages to/from it, i.e. the `Ledger` abstraction may not be correct, - // it may have to be a different trait or two traits. + let (secret_key_source, account) = D::load_with(path, loading_key)?; + Ok(Self::with_account(secret_key_source, account)) + } +} - let SyncResponse { - checkpoint, - void_numbers, - utxos, - encrypted_assets, - } = ledger.sync(&self.checkpoint).await?; +impl<D> Save for Signer<D> +where + D: DerivedSecretKeyGenerator + SaveWith<Account<D>>, +{ + type Path = D::Path; - let _ = (void_numbers, utxos, encrypted_assets); + type SavingKey = D::SavingKey; - self.checkpoint = checkpoint; + type Error = <D as Save>::Error; - todo!() + #[inline] + fn save<P>(self, path: P, saving_key: &Self::SavingKey) -> Result<(), Self::Error> + where + P: AsRef<Self::Path>, + { + self.secret_key_source + .save_with(self.account, path, saving_key) } +} - /// Generates a new [`ShieldedIdentity`] to receive assets to this wallet via an external - /// transaction. +/// Ledger Source +pub trait LedgerSource<C> +where + C: transfer::Configuration, +{ + /// Sync Future Type + /// + /// Future for the [`sync`](Self::sync) method. + type SyncFuture: Future<Output = Result<SyncResponse<C, Self>, Self::Error>>; + + /// Send Future Type + /// + /// Future for the [`send`](Self::send) method. + type SendFuture: Future<Output = Result<SendResponse<C, Self>, Self::Error>>; + + /// Ledger State Checkpoint Type + type Checkpoint: Default + Ord; + + /// Error Type + type Error; + + /// Pulls data from the ledger starting from `checkpoint`, returning the current + /// [`Checkpoint`](Self::Checkpoint). + fn sync(&self, checkpoint: &Self::Checkpoint) -> Self::SyncFuture; + + /// Pulls all of the data from the entire history of the ledger, returning the current + /// [`Checkpoint`](Self::Checkpoint). #[inline] - pub fn generate_shielded_identity<C, I>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - ) -> Result<ShieldedIdentity<C, I>, D::Error> + fn sync_all(&self) -> Self::SyncFuture { + self.sync(&Default::default()) + } + + /// Sends `transfers` to the ledger, returning the current [`Checkpoint`](Self::Checkpoint) + /// and the status of the transfers. + fn send(&self, transfers: Vec<TransferPost<C>>) -> Self::SendFuture; + + /// Sends `transfer` to the ledger, returning the current [`Checkpoint`](Self::Checkpoint) + /// and the status of the transfer. + #[inline] + fn send_one(&self, transfer: TransferPost<C>) -> Self::SendFuture { + self.send(vec![transfer]) + } +} + +/// Ledger Source Sync Response +/// +/// This `struct` is created by the [`sync`](LedgerSource::sync) method on [`LedgerSource`]. +/// See its documentation for more. +pub struct SyncResponse<C, LS> +where + C: transfer::Configuration, + LS: LedgerSource<C> + ?Sized, +{ + /// Current Ledger Checkpoint + pub checkpoint: LS::Checkpoint, + + /// New Void Numbers + pub void_numbers: Vec<VoidNumber<C>>, + + /// New UTXOS + pub utxos: Vec<Utxo<C>>, + + /// New Encrypted Assets + pub encrypted_assets: Vec<EncryptedAsset<C>>, +} + +/// Ledger Source Send Response +/// +/// This `struct` is created by the [`send`](LedgerSource::send) method on [`LedgerSource`]. +/// See its documentation for more. +pub struct SendResponse<C, LS> +where + C: transfer::Configuration, + LS: LedgerSource<C> + ?Sized, +{ + /// Current Ledger Checkpoint + pub checkpoint: LS::Checkpoint, + + /// Ledger Responses + // FIXME: Design responses. + pub responses: Vec<bool>, +} + +/// Asset Map +pub trait AssetMap { + /// Returns the current balance associated with this `id`. + fn balance(&self, id: AssetId) -> AssetBalance; + + /// Returns `true` if `self` contains at least `asset.value` of the asset of kind `asset.id`. + #[inline] + fn contains(&self, asset: Asset) -> bool { + self.balance(asset.id) >= asset.value + } + + /// Sets the asset balance for `id` to `value`. + fn set_balance(&mut self, id: AssetId, value: AssetBalance); + + /// Sets the asset balance for `asset.id` to `asset.value`. + #[inline] + fn set_asset(&mut self, asset: Asset) { + self.set_balance(asset.id, asset.value) + } + + /// Mutates the asset balance for `id` to the result of `f` if it succeeds, returning the + /// new balance. + #[inline] + #[must_use = "this only modifies the stored value if the function call succeeded"] + fn mutate<F, E>(&mut self, id: AssetId, f: F) -> Result<AssetBalance, E> where - C: identity::Configuration<SecretKey = D::SecretKey>, - I: IntegratedEncryptionScheme<Plaintext = Asset>, - Standard: Distribution<AssetParameters<C>>, + F: FnOnce(AssetBalance) -> Result<AssetBalance, E>, { - self.next_external_identity() - .map(move |identity| identity.into_shielded(commitment_scheme)) + // TODO: use `try_trait_v2` when it comes out + f(self.balance(id)).map(move |v| { + self.set_balance(id, v); + v + }) } - /// Generates a new [`InternalReceiver`] to receive `asset` to this wallet via an - /// internal transaction. + /// Performs a deposit of value `asset.value` into the balance of `asset.id`, + /// returning the new balance for this `asset.id` if it did not overflow. + /// + /// To skip the overflow check, use [`deposit_unchecked`](Self::deposit_unchecked) instead. #[inline] - pub fn generate_internal_receiver<C, I, R>( + #[must_use = "this only modifies the stored value if the addition did not overflow"] + fn deposit(&mut self, asset: Asset) -> Option<AssetBalance> { + self.mutate(asset.id, move |v| v.checked_add(asset.value).ok_or(())) + .ok() + } + + /// Performs a deposit of value `asset.value` into the balance of `asset.id`, + /// without checking for overflow, returning the new balance for this `asset.id`. + /// + /// # Panics + /// + /// This function panics on overflow. To explicitly check for overflow, use + /// [`deposit`](Self::deposit) instead. + #[inline] + fn deposit_unchecked(&mut self, asset: Asset) -> AssetBalance { + self.mutate::<_, Infallible>(asset.id, move |v| Ok(v + asset.value)) + .unwrap() + } + + /// Performs a withdrawl of value `asset.value` from the balance of `asset.id`, + /// returning the new balance for this `asset.id` if it did not overflow. + /// + /// To skip the overflow check, use [`withdraw_unchecked`](Self::withdraw_unchecked) instead. + #[inline] + #[must_use = "this only modifies the stored value if the subtraction did not overflow"] + fn withdraw(&mut self, asset: Asset) -> Option<AssetBalance> { + self.mutate(asset.id, move |v| v.checked_sub(asset.value).ok_or(())) + .ok() + } + + /// Performs a withdrawl of value `asset.value` from the balance of `asset.id`, + /// without checking for overflow, returning the new balance for this `asset.id`. + /// + /// # Panics + /// + /// This function panics on overflow. To explicitly check for overflow, use + /// [`withdraw`](Self::withdraw) instead. + #[inline] + fn withdraw_unchecked(&mut self, asset: Asset) -> AssetBalance { + self.mutate::<_, Infallible>(asset.id, move |v| Ok(v - asset.value)) + .unwrap() + } +} + +/// Local Ledger +pub trait LocalLedger<C> +where + C: transfer::Configuration, +{ + /// Ledger State Checkpoint Type + type Checkpoint: Default + Ord; + + /// Returns the checkpoint of the local ledger. + fn checkpoint(&self) -> &Self::Checkpoint; + + /// Sets the checkpoint of the local ledger to `checkpoint`. + fn set_checkpoint(&mut self, checkpoint: Self::Checkpoint); + + /// Inserts the `void_number` into the local ledger. + fn insert_void_number(&mut self, void_number: VoidNumber<C>); + + /// Inserts the `utxo` into the local ledger. + fn insert_utxo(&mut self, utxo: Utxo<C>); + + /// Inserts the `encrypted_asset` into the local ledger. + fn insert_encrypted_asset(&mut self, encrypted_asset: EncryptedAsset<C>); +} + +/// Wallet Balance State +pub struct BalanceState<C, LL, SM, PM = SM> +where + C: transfer::Configuration, + LL: LocalLedger<C>, + SM: AssetMap, + PM: AssetMap, +{ + /// Secret Asset Map + secret_assets: SM, + + /// Public Asset Map + public_assets: PM, + + /// Local Ledger + local_ledger: LL, + + /// Type Parameter Marker + __: PhantomData<C>, +} + +impl<C, LL, SM, PM> BalanceState<C, LL, SM, PM> +where + C: transfer::Configuration, + LL: LocalLedger<C>, + SM: AssetMap, + PM: AssetMap, +{ + /// Synchronizes `self` with the `ledger`. + #[inline] + pub async fn sync<LS>(&mut self, ledger: &LS) -> Result<(), LS::Error> + where + LS: LedgerSource<C, Checkpoint = LL::Checkpoint> + ?Sized, + { + let SyncResponse { + checkpoint, + void_numbers, + utxos, + encrypted_assets, + } = ledger.sync(self.local_ledger.checkpoint()).await?; + + for void_number in void_numbers { + // TODO: Only keep the ones we care about. How do we alert the local ledger? We have to + // communicate with it when we try to send transactions to a ledger source. + // Do we care about keeping void numbers? Maybe only for recovery? + self.local_ledger.insert_void_number(void_number); + } + + for encrypted_asset in encrypted_assets { + // TODO: Decrypt them on the way in ... threads? For internal transactions we know the + // `encrypted_asset` ahead of time, and we know it decrypted too. For external + // transactions we have to keep all of them since we have to try and decrypt all + // of them. + self.local_ledger.insert_encrypted_asset(encrypted_asset); + } + + for utxo in utxos { + // TODO: Only keep the ones we care about. For internal transactions we know the `utxo` + // ahead of time. For external ones, we have to get the `asset` first. + self.local_ledger.insert_utxo(utxo); + } + + self.local_ledger.set_checkpoint(checkpoint); + + // FIXME: How do we update `secret_assets` and `public_assets`? Can we only "deposit" from + // a `sync`, and only "withdraw" from a `send`? + + Ok(()) + } + + /// Sends the `transfers` to the `ledger`. + #[inline] + pub async fn send<LS>( &mut self, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - rng: &mut R, - ) -> Result<InternalReceiver<C, I>, InternalReceiverError<InternalKeys<D>, I>> + transfers: Vec<TransferPost<C>>, + ledger: &LS, + ) -> Result<(), LS::Error> where - C: identity::Configuration<SecretKey = D::SecretKey>, - I: IntegratedEncryptionScheme<Plaintext = Asset>, - R: CryptoRng + RngCore + ?Sized, - Standard: Distribution<AssetParameters<C>>, + LS: LedgerSource<C, Checkpoint = LL::Checkpoint> + ?Sized, { - self.next_internal_identity() - .map_err(InternalReceiverError::SecretKeyError)? - .into_internal_receiver(commitment_scheme, asset, rng) - .map_err(InternalReceiverError::EncryptionError) + // TODO: When sending to the ledger, we really have to set up a backup state in case we + // missed the "send window", and we need to recover. We can also hint to the local + // ledger that we are about to send some transactions and so it should know which + // `utxo`s and such are important to keep when it sees them appear later in the real + // ledger state. + // + // Sender Posts: `void_number` + // Receiver Posts: `utxo`, `encrypted_asset` + // + let _ = ledger.send(transfers).await?; + todo!() + } +} + +/* TODO: +/// Wallet +pub struct Wallet<C, D, LL, SM, PM = SM> +where + C: transfer::Configuration, + D: DerivedSecretKeyGenerator<SecretKey = C::SecretKey>, + LL: LocalLedger<C>, + SM: AssetMap, + PM: AssetMap, +{ + /// Wallet Signer + signer: Signer<D>, + + /// Wallet Balance State + balance_state: BalanceState<C, LL, SM, PM>, +} +*/ + +/// Wallet +pub struct Wallet<D, M> +where + D: DerivedSecretKeyGenerator, + M: AssetMap, +{ + /// Wallet Signer + signer: Signer<D>, + + /// Public Asset Map + public_assets: M, + + /// Secret Asset Map + secret_assets: M, +} + +impl<D, M> Wallet<D, M> +where + D: DerivedSecretKeyGenerator, + M: AssetMap, +{ + /// Builds a new [`Wallet`] for `signer`. + #[inline] + pub fn new(signer: Signer<D>) -> Self + where + M: Default, + { + Self::with_balances(signer, Default::default(), Default::default()) + } + + /// Builds a new [`Wallet`] for `signer` and pre-built `public_assets` map and + /// `secret_assets` map. + #[inline] + pub fn with_balances(signer: Signer<D>, public_assets: M, secret_assets: M) -> Self { + Self { + signer, + public_assets, + secret_assets, + } } /// Builds a [`Mint`] transaction to mint `asset` and returns the [`OpenSpend`] for that asset. @@ -503,7 +663,8 @@ where Standard: Distribution<AssetParameters<C>>, { Mint::from_identity( - self.next_internal_identity() + self.signer + .next_internal_identity() .map_err(MintError::SecretKeyError)?, commitment_scheme, asset, @@ -553,45 +714,47 @@ where } } -impl<D, M, LL> Load for Wallet<D, M, LL> +impl<D, M> Load for Wallet<D, M> where - D: DerivedSecretKeyGenerator + Load, + D: DerivedSecretKeyGenerator, M: AssetMap + Default, - LL: LocalLedger, + Signer<D>: Load, { - type Path = D::Path; + type Path = <Signer<D> as Load>::Path; - type LoadingKey = D::LoadingKey; + type LoadingKey = <Signer<D> as Load>::LoadingKey; - type Error = <D as Load>::Error; + type Error = <Signer<D> as Load>::Error; #[inline] fn load<P>(path: P, loading_key: &Self::LoadingKey) -> Result<Self, Self::Error> where P: AsRef<Self::Path>, { - Ok(Self::new(D::load(path, loading_key)?, Default::default())) + // TODO: Also add a way to save `BalanceState`. + Ok(Self::new(Signer::<D>::load(path, loading_key)?)) } } -impl<D, M, LL> Save for Wallet<D, M, LL> +impl<D, M> Save for Wallet<D, M> where - D: DerivedSecretKeyGenerator + Save, + D: DerivedSecretKeyGenerator, M: AssetMap + Default, - LL: LocalLedger, + Signer<D>: Save, { - type Path = D::Path; + type Path = <Signer<D> as Save>::Path; - type SavingKey = D::SavingKey; + type SavingKey = <Signer<D> as Save>::SavingKey; - type Error = <D as Save>::Error; + type Error = <Signer<D> as Save>::Error; #[inline] fn save<P>(self, path: P, saving_key: &Self::SavingKey) -> Result<(), Self::Error> where P: AsRef<Self::Path>, { - self.secret_key_source.save(path, saving_key) + // TODO: Also add a way to save `BalanceState`. + self.signer.save(path, saving_key) } } From 34515ed5c4b0597549b07dd3222afade12006942 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 24 Sep 2021 00:28:59 -0400 Subject: [PATCH 057/275] WIP: start local ledger simulations --- manta-accounting/src/identity.rs | 204 +++++++++++++++++++++++++++++-- manta-accounting/src/transfer.rs | 4 +- manta-accounting/src/wallet.rs | 199 ++++++++++++++++++++---------- 3 files changed, 326 insertions(+), 81 deletions(-) diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 8d0eb69d4..af8508280 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -385,6 +385,45 @@ where (public_key, void_number_commitment, utxo) } + /// Builds a new [`PreSender`] for the given `asset`. + #[inline] + pub fn into_pre_sender( + self, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + ) -> PreSender<C> + where + Standard: Distribution<AssetParameters<C>>, + { + let parameters = self.parameters(); + let (public_key, void_number_commitment, utxo) = + self.construct_utxo(commitment_scheme, &asset, &parameters); + PreSender { + void_number: self.void_number(&parameters.void_number_generator), + secret_key: self.secret_key, + public_key, + asset, + parameters, + void_number_commitment, + utxo, + } + } + + /// Generates a new [`Identity`] from a secret key generation source and builds a new + /// [`PreSender`] from it. + #[inline] + pub fn generate_pre_sender<G>( + source: &mut G, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + ) -> Result<PreSender<C>, G::Error> + where + G: SecretKeyGenerator<SecretKey = SecretKey<C>>, + Standard: Distribution<AssetParameters<C>>, + { + Ok(Self::generate(source)?.into_pre_sender(commitment_scheme, asset)) + } + /// Builds a new [`Sender`] for the given `asset`. #[inline] pub fn into_sender<S>( @@ -397,19 +436,6 @@ where S: VerifiedSet<Item = Utxo<C>>, Standard: Distribution<AssetParameters<C>>, { - // TODO: Generate pre-sender data structure in case the `utxo_set` is behind the - // current ledger state and we need to have everything ready for the `utxo_set`. - // - // > pub struct PreSender<C> { - // secret_key, - // public_key, - // asset, - // parameters, - // void_number_commitment, - // void_number, - // utxo - // } - // let parameters = self.parameters(); let (public_key, void_number_commitment, utxo) = self.construct_utxo(commitment_scheme, &asset, &parameters); @@ -976,6 +1002,143 @@ where } } +/// Sender Proof +/// +/// This `struct` is created by the [`get_proof`](PreSender::get_proof) method on [`PreSender`]. +/// See its documentation for more. +pub struct SenderProof<C, S> +where + C: Configuration, + S: VerifiedSet<Item = Utxo<C>>, +{ + /// UTXO Containment Proof + utxo_containment_proof: ContainmentProof<S>, + + /// Type Parameter Marker + __: PhantomData<C>, +} + +impl<C, S> SenderProof<C, S> +where + C: Configuration, + S: VerifiedSet<Item = Utxo<C>>, +{ + /// Returns `true` if a [`PreSender`] could be upgraded using `self` given the `utxo_set`. + #[inline] + pub fn can_upgrade(&self, utxo_set: &S) -> bool { + self.utxo_containment_proof.check_public_input(utxo_set) + } + + /// Upgrades the `pre_sender` to a [`Sender`] by attaching `self` to it. + /// + /// # Note + /// + /// When using this function, be sure to check that [`can_upgrade`](Self::can_upgrade) returns + /// `true`. Otherwise, using the sender returned here will most likely return an error when + /// posting to the ledger. + #[inline] + pub fn upgrade(self, pre_sender: PreSender<C>) -> Sender<C, S> { + pre_sender.upgrade(self) + } +} + +/// Pre-Sender +pub struct PreSender<C> +where + C: Configuration, +{ + /// Secret Key + secret_key: SecretKey<C>, + + /// Public Key + public_key: PublicKey<C>, + + /// Asset + asset: Asset, + + /// Asset Parameters + parameters: AssetParameters<C>, + + /// Void Number + void_number: VoidNumber<C>, + + /// Void Number Commitment + void_number_commitment: VoidNumberCommitment<C>, + + /// Unspent Transaction Output + utxo: Utxo<C>, +} + +impl<C> PreSender<C> +where + C: Configuration, +{ + /// Builds a new [`PreSender`] for this `asset` from an `identity`. + #[inline] + pub fn from_identity( + identity: Identity<C>, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + ) -> Self + where + Standard: Distribution<AssetParameters<C>>, + { + identity.into_pre_sender(commitment_scheme, asset) + } + + /// Generates a new [`Identity`] from a secret key generation source and builds a new + /// [`PreSender`] from it. + #[inline] + pub fn generate<G>( + source: &mut G, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + ) -> Result<Self, G::Error> + where + G: SecretKeyGenerator<SecretKey = SecretKey<C>>, + Standard: Distribution<AssetParameters<C>>, + { + Identity::generate_pre_sender(source, commitment_scheme, asset) + } + + /// Requests the containment proof of `self.utxo` from `utxo_set` so that we can turn `self` + /// into a [`Sender`]. + #[inline] + pub fn get_proof<S>(&self, utxo_set: &S) -> Result<SenderProof<C, S>, S::ContainmentError> + where + S: VerifiedSet<Item = Utxo<C>>, + { + Ok(SenderProof { + utxo_containment_proof: utxo_set.get_containment_proof(&self.utxo)?, + __: PhantomData, + }) + } + + /// Converts `self` into a [`Sender`] by attaching `proof` to it. + /// + /// # Note + /// + /// When using this function, be sure to check that [`SenderProof::can_upgrade`] returns + /// `true`. Otherwise, using the sender returned here will most likely return an error when + /// posting to the ledger. + #[inline] + pub fn upgrade<S>(self, proof: SenderProof<C, S>) -> Sender<C, S> + where + S: VerifiedSet<Item = Utxo<C>>, + { + Sender { + secret_key: self.secret_key, + public_key: self.public_key, + asset: self.asset, + parameters: self.parameters, + void_number: self.void_number, + void_number_commitment: self.void_number_commitment, + utxo: self.utxo, + utxo_containment_proof: proof.utxo_containment_proof, + } + } +} + /// Sender Error /// /// This `enum` is the error state for the [`generate`] method on [`Sender`]. @@ -1081,6 +1244,21 @@ where self.asset.value } + /// Reverts `self` back into a [`PreSender`] if its [`Utxo`] containment proof was deemed + /// invalid or had expired. + #[inline] + pub fn downgrade(self) -> PreSender<C> { + PreSender { + secret_key: self.secret_key, + public_key: self.public_key, + asset: self.asset, + parameters: self.parameters, + void_number: self.void_number, + void_number_commitment: self.void_number_commitment, + utxo: self.utxo, + } + } + /// Extracts ledger posting data for this sender. #[inline] pub fn into_post(self) -> SenderPost<C, S> { diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 8502353fd..97781872e 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -1044,10 +1044,10 @@ where C: Configuration, { /// Sender Posts - sender_posts: Vec<SenderPost<C>>, + pub sender_posts: Vec<SenderPost<C>>, /// Receiver Posts - receiver_posts: Vec<ReceiverPost<C>>, + pub receiver_posts: Vec<ReceiverPost<C>>, /// Validity Proof /// diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index bd309d393..c6f205c19 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -31,16 +31,16 @@ use crate::{ self, AssetParameters, Identity, InternalReceiver, InternalReceiverError, OpenSpend, ShieldedIdentity, Utxo, VoidNumber, }, - keys::{Account, DerivedSecretKeyGenerator, ExternalKeys, InternalKeys, KeyKind}, + keys::{Account, DerivedSecretKeyGenerator, InternalKeys, KeyKind}, transfer::{ self, canonical::{Mint, PrivateTransfer, Reclaim}, - EncryptedAsset, IntegratedEncryptionSchemeError, TransferPost, + EncryptedAsset, IntegratedEncryptionSchemeError, ReceiverPost, SenderPost, TransferPost, }, }; use alloc::{vec, vec::Vec}; use core::{convert::Infallible, fmt::Debug, future::Future, hash::Hash, marker::PhantomData}; -use manta_crypto::ies::{EncryptedMessage, IntegratedEncryptionScheme}; +use manta_crypto::ies::IntegratedEncryptionScheme; use rand::{ distributions::{Distribution, Standard}, CryptoRng, RngCore, @@ -472,6 +472,50 @@ pub trait AssetMap { } } +/// Ledger Ownership Marker +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum Ownership { + /// Key Ownership + Key(KeyKind), + + /// Unknown Self-Ownership + Unknown, + + /// Ownership by Others + Other, +} + +impl Default for Ownership { + #[inline] + fn default() -> Self { + Self::Unknown + } +} + +/// Ledger Data +pub enum LedgerData<C> +where + C: transfer::Configuration, +{ + /// Sender Data + Sender(VoidNumber<C>), + + /// Receiver Data + Receiver(Utxo<C>, EncryptedAsset<C>), +} + +/// Ledger Post Entry +pub enum PostEntry<'t, C> +where + C: transfer::Configuration, +{ + /// Sender Entry + Sender(&'t SenderPost<C>), + + /// Receiver Entry + Receiver(&'t ReceiverPost<C>), +} + /// Local Ledger pub trait LocalLedger<C> where @@ -480,88 +524,103 @@ where /// Ledger State Checkpoint Type type Checkpoint: Default + Ord; + /// Data Preservation Key + type Key; + + /// Data Access Error + type Error; + /// Returns the checkpoint of the local ledger. fn checkpoint(&self) -> &Self::Checkpoint; /// Sets the checkpoint of the local ledger to `checkpoint`. fn set_checkpoint(&mut self, checkpoint: Self::Checkpoint); - /// Inserts the `void_number` into the local ledger. - fn insert_void_number(&mut self, void_number: VoidNumber<C>); + /// Saves `data` into the local ledger. + fn append(&mut self, data: LedgerData<C>) -> Result<(), Self::Error>; - /// Inserts the `utxo` into the local ledger. - fn insert_utxo(&mut self, utxo: Utxo<C>); + /// Prepares a `post` in the local ledger, returning a key to decide if the post should + /// be preserved by the ledger or not. + fn prepare(&mut self, post: PostEntry<C>, ownership: Ownership) -> Self::Key; - /// Inserts the `encrypted_asset` into the local ledger. - fn insert_encrypted_asset(&mut self, encrypted_asset: EncryptedAsset<C>); + /// Preserves the data associated to the `key`. + fn keep(&mut self, key: Self::Key); + + /// Drops the data associated to the `key`. + fn drop(&mut self, key: Self::Key); } /// Wallet Balance State -pub struct BalanceState<C, LL, SM, PM = SM> +pub struct BalanceState<C, L, M> where C: transfer::Configuration, - LL: LocalLedger<C>, - SM: AssetMap, - PM: AssetMap, + L: LocalLedger<C>, + M: AssetMap, { /// Secret Asset Map - secret_assets: SM, + secret_assets: M, /// Public Asset Map - public_assets: PM, + // TODO: Find a way to connect this. + _public_assets: M, /// Local Ledger - local_ledger: LL, + ledger: L, /// Type Parameter Marker __: PhantomData<C>, } -impl<C, LL, SM, PM> BalanceState<C, LL, SM, PM> +impl<C, L, M> BalanceState<C, L, M> where C: transfer::Configuration, - LL: LocalLedger<C>, - SM: AssetMap, - PM: AssetMap, + L: LocalLedger<C>, + M: AssetMap, { + /// Returns the [`Checkpoint`](LocalLedger::Checkpoint) associated with the local ledger. + #[inline] + pub fn checkpoint(&self) -> &L::Checkpoint { + self.ledger.checkpoint() + } + /// Synchronizes `self` with the `ledger`. #[inline] pub async fn sync<LS>(&mut self, ledger: &LS) -> Result<(), LS::Error> where - LS: LedgerSource<C, Checkpoint = LL::Checkpoint> + ?Sized, + LS: LedgerSource<C, Checkpoint = L::Checkpoint> + ?Sized, { let SyncResponse { checkpoint, void_numbers, utxos, encrypted_assets, - } = ledger.sync(self.local_ledger.checkpoint()).await?; + } = ledger.sync(self.checkpoint()).await?; for void_number in void_numbers { // TODO: Only keep the ones we care about. How do we alert the local ledger? We have to // communicate with it when we try to send transactions to a ledger source. // Do we care about keeping void numbers? Maybe only for recovery? - self.local_ledger.insert_void_number(void_number); + // + let _ = self.ledger.append(LedgerData::Sender(void_number)); } - for encrypted_asset in encrypted_assets { + for (utxo, encrypted_asset) in utxos.into_iter().zip(encrypted_assets) { + // TODO: Only keep the ones we care about. For internal transactions we know the `utxo` + // ahead of time. For external ones, we have to get the `asset` first. + // // TODO: Decrypt them on the way in ... threads? For internal transactions we know the // `encrypted_asset` ahead of time, and we know it decrypted too. For external // transactions we have to keep all of them since we have to try and decrypt all // of them. - self.local_ledger.insert_encrypted_asset(encrypted_asset); - } - - for utxo in utxos { - // TODO: Only keep the ones we care about. For internal transactions we know the `utxo` - // ahead of time. For external ones, we have to get the `asset` first. - self.local_ledger.insert_utxo(utxo); + // + let _ = self + .ledger + .append(LedgerData::Receiver(utxo, encrypted_asset)); } - self.local_ledger.set_checkpoint(checkpoint); + self.ledger.set_checkpoint(checkpoint); - // FIXME: How do we update `secret_assets` and `public_assets`? Can we only "deposit" from - // a `sync`, and only "withdraw" from a `send`? + // FIXME: Deposit into `secret_assets` and `public_assets`. Ok(()) } @@ -574,7 +633,7 @@ where ledger: &LS, ) -> Result<(), LS::Error> where - LS: LedgerSource<C, Checkpoint = LL::Checkpoint> + ?Sized, + LS: LedgerSource<C, Checkpoint = L::Checkpoint> + ?Sized, { // TODO: When sending to the ledger, we really have to set up a backup state in case we // missed the "send window", and we need to recover. We can also hint to the local @@ -585,7 +644,39 @@ where // Sender Posts: `void_number` // Receiver Posts: `utxo`, `encrypted_asset` // - let _ = ledger.send(transfers).await?; + + let mut keys = Vec::new(); + + for transfer in &transfers { + for sender in &transfer.sender_posts { + keys.push( + self.ledger + .prepare(PostEntry::Sender(sender), Default::default()), + ); + } + for receiver in &transfer.receiver_posts { + keys.push( + self.ledger + .prepare(PostEntry::Receiver(receiver), Default::default()), + ); + } + } + + keys.shrink_to_fit(); + + let SendResponse { + checkpoint, + responses, + } = ledger.send(transfers).await?; + + // TODO: Revoke keys if the transfer failed, otherwise recover from the failure. + + let _ = responses; + + self.ledger.set_checkpoint(checkpoint); + + // FIXME: Withdraw from `secret_assets` and `public_assets`. + todo!() } } @@ -609,44 +700,22 @@ where */ /// Wallet -pub struct Wallet<D, M> +pub struct Wallet<D> where D: DerivedSecretKeyGenerator, - M: AssetMap, { /// Wallet Signer signer: Signer<D>, - - /// Public Asset Map - public_assets: M, - - /// Secret Asset Map - secret_assets: M, } -impl<D, M> Wallet<D, M> +impl<D> Wallet<D> where D: DerivedSecretKeyGenerator, - M: AssetMap, { /// Builds a new [`Wallet`] for `signer`. #[inline] - pub fn new(signer: Signer<D>) -> Self - where - M: Default, - { - Self::with_balances(signer, Default::default(), Default::default()) - } - - /// Builds a new [`Wallet`] for `signer` and pre-built `public_assets` map and - /// `secret_assets` map. - #[inline] - pub fn with_balances(signer: Signer<D>, public_assets: M, secret_assets: M) -> Self { - Self { - signer, - public_assets, - secret_assets, - } + pub fn new(signer: Signer<D>) -> Self { + Self { signer } } /// Builds a [`Mint`] transaction to mint `asset` and returns the [`OpenSpend`] for that asset. @@ -714,10 +783,9 @@ where } } -impl<D, M> Load for Wallet<D, M> +impl<D> Load for Wallet<D> where D: DerivedSecretKeyGenerator, - M: AssetMap + Default, Signer<D>: Load, { type Path = <Signer<D> as Load>::Path; @@ -736,10 +804,9 @@ where } } -impl<D, M> Save for Wallet<D, M> +impl<D> Save for Wallet<D> where D: DerivedSecretKeyGenerator, - M: AssetMap + Default, Signer<D>: Save, { type Path = <Signer<D> as Save>::Path; From b27c75689a3a9a0574fc1cf2e450eee29ed60239 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 24 Sep 2021 14:13:55 -0400 Subject: [PATCH 058/275] WIP: add pop and extend_slice optimization paths in merkle tree --- manta-accounting/src/wallet.rs | 2 + manta-crypto/src/merkle_tree.rs | 166 +++++++++++++++++++++++++---- manta-pay/src/accounting/ledger.rs | 2 +- 3 files changed, 148 insertions(+), 22 deletions(-) diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index c6f205c19..6d5e1044b 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -635,6 +635,8 @@ where where LS: LedgerSource<C, Checkpoint = L::Checkpoint> + ?Sized, { + // FIXME: How do to batched transfers? Those above are not necessarily "batched-atomic". + // // TODO: When sending to the ledger, we really have to set up a backup state in case we // missed the "send window", and we need to recover. We can also hint to the local // ledger that we are about to send some transactions and so it should know which diff --git a/manta-crypto/src/merkle_tree.rs b/manta-crypto/src/merkle_tree.rs index 67649836c..a42d1b48f 100644 --- a/manta-crypto/src/merkle_tree.rs +++ b/manta-crypto/src/merkle_tree.rs @@ -150,22 +150,23 @@ where /// Builds a new merkle tree with the given `leaves` returning `None` if the iterator /// would overflow the capacity of the tree. #[inline] - fn from_leaves<'l, L>(parameters: &Parameters<C>, leaves: L) -> Option<Self> + fn from_iter<'l, L>(parameters: &Parameters<C>, leaves: L) -> Option<Self> where Leaf<C>: 'l, L: IntoIterator<Item = &'l Leaf<C>>, { - let leaves = leaves.into_iter(); - if leaves.size_hint().0 > capacity::<C>() { - return None; - } let mut tree = Self::new(parameters); - for leaf in leaves { - if !tree.append(parameters, leaf) { - return None; - } - } - Some(tree) + tree.extend(parameters, leaves).then(move || tree) + } + + /// Builds a new merkle tree with the given `leaves` returning `None` if the slice + /// would overflow the capacity of the tree. + #[inline] + fn from_slice(parameters: &Parameters<C>, slice: &[Leaf<C>]) -> Option<Self> + where + Leaf<C>: Sized, + { + Self::from_iter(parameters, slice) } /// Returns the length of `self`. @@ -185,7 +186,77 @@ where /// Inserts the digest of `leaf` at the next avaiable leaf node of the tree, returning /// `false` if the leaf could not be inserted because the tree has exhausted its capacity. - fn append(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> bool; + fn push(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> bool; + + /// Appends an iterator of leaves at the end of the tree, returning `false` if the + /// `leaves` could not be inserted because the tree has exhausted its capacity. + /// + /// # Implementation Note + /// + /// This operation is meant to be atomic, so if appending the iterator should fail, the + /// implementation must ensure that the tree returns to the same state before the insertion + /// occured. + #[inline] + fn extend<'l, L>(&mut self, parameters: &Parameters<C>, leaves: L) -> bool + where + Leaf<C>: 'l, + L: IntoIterator<Item = &'l Leaf<C>>, + { + let leaves = leaves.into_iter(); + if matches!(leaves.size_hint().1, Some(max) if max <= capacity::<C>() - self.len()) { + for leaf in leaves { + debug_assert!(self.push(parameters, leaf)); + } + return true; + } + false + } + + /// Appends a slice of leaves at the end of the tree, returning `false` if the + /// `leaves` could not be inserted because the tree has exhausted its capacity. + /// + /// # Implementation Note + /// + /// This operation is meant to be atomic, so if appending the slice should fail, the + /// implementation must ensure that the tree returns to the same state before the insertion + /// occured. + #[inline] + fn extend_slice(&mut self, parameters: &Parameters<C>, leaves: &[Leaf<C>]) -> bool + where + Leaf<C>: Sized, + { + self.extend(parameters, leaves) + } +} + +/// Merkle Tree Pop Mixin +pub trait Pop<C>: Tree<C> +where + C: Configuration + ?Sized, +{ + /// Removes the last element of the tree rolling it back to the state right before the + /// insertion of the last leaf, returning `None` if the tree is empty. + fn pop(&mut self, parameters: &Parameters<C>) -> Option<LeafDigest<C>>; + + /// Removes `n` elements of the tree rolling it back to the state before the insertion + /// of the last `n` leaves, returning `false` if `n` is greater than the current length + /// of the tree. + /// + /// # Implementation Note + /// + /// This operation is meant to be atomic, so if removing elements should fail, the + /// implementation must ensure that the tree returns to the same state before the removal + /// occured. + #[inline] + fn pop_many(&mut self, parameters: &Parameters<C>, n: usize) -> bool { + if n > self.len() { + return false; + } + for _ in 0..n { + debug_assert!(self.pop(parameters).is_some()); + } + true + } } /// Parity of a Subtree @@ -766,13 +837,25 @@ where /// Builds a new [`MerkleTree`] with the given `leaves`. #[inline] - pub fn from_leaves<'l, L>(parameters: Parameters<C>, leaves: L) -> Option<Self> + pub fn from_iter<'l, L>(parameters: Parameters<C>, leaves: L) -> Option<Self> where Leaf<C>: 'l, L: IntoIterator<Item = &'l Leaf<C>>, { Some(Self::from_tree( - T::from_leaves(&parameters, leaves)?, + T::from_iter(&parameters, leaves)?, + parameters, + )) + } + + /// Builds a new [`MerkleTree`] with the given `leaves`. + #[inline] + pub fn from_slice(parameters: Parameters<C>, leaves: &[Leaf<C>]) -> Option<Self> + where + Leaf<C>: Sized, + { + Some(Self::from_tree( + T::from_slice(&parameters, leaves)?, parameters, )) } @@ -804,8 +887,49 @@ where /// Inserts `leaf` at the next avaiable leaf node of the tree, returning `false` if the /// leaf could not be inserted because the tree has exhausted its capacity. #[inline] - pub fn append(&mut self, leaf: &Leaf<C>) -> bool { - self.tree.append(&self.parameters, leaf) + pub fn push(&mut self, leaf: &Leaf<C>) -> bool { + self.tree.push(&self.parameters, leaf) + } + + /// Appends an iterator of leaves at the end of the tree, returning `false` if the `leaves` + /// could not be inserted because the tree has exhausted its capacity. + #[inline] + pub fn extend<'l, L>(&mut self, leaves: L) -> bool + where + Leaf<C>: 'l, + L: IntoIterator<Item = &'l Leaf<C>>, + { + self.tree.extend(&self.parameters, leaves) + } + + /// Appends a slice of leaves at the end of the tree, returning `false` if the `leaves` could + /// not be inserted because the tree has exhausted its capacity. + #[inline] + pub fn extend_slice(&mut self, leaves: &[Leaf<C>]) -> bool + where + Leaf<C>: Sized, + { + self.tree.extend_slice(&self.parameters, leaves) + } + + /// Removes the last element of the tree rolling it back to the state right before the + /// insertion of the last leaf, returning `None` if the tree is empty. + #[inline] + pub fn pop(&mut self) -> Option<LeafDigest<C>> + where + T: Pop<C>, + { + self.tree.pop(&self.parameters) + } + + /// Removes `n` elements of the tree rolling it back to the state before the insertion of the + /// last `n` leaves, returning `false` if `n` is greater than the current length of the tree. + #[inline] + pub fn pop_many(&mut self, n: usize) -> bool + where + T: Pop<C>, + { + self.tree.pop_many(&self.parameters, n) } /// Extracts the parameters of the merkle tree, dropping the internal tree. @@ -1046,7 +1170,7 @@ pub mod full { } #[inline] - fn append(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> bool { + fn push(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> bool { let len = self.len(); if len >= capacity::<C>() { return false; @@ -1193,7 +1317,7 @@ pub mod latest_node { } #[inline] - fn append(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> bool { + fn push(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> bool { let index = match self.next_index() { Some(index) => index, _ => return false, @@ -1287,7 +1411,7 @@ pub mod test { /// Tests that a tree constructed with `parameters` can accept at least two leaves without /// failing. #[inline] - pub fn append_twice_to_empty_tree_succeeds<C, T>( + pub fn push_twice_to_empty_tree_succeeds<C, T>( parameters: Parameters<C>, lhs: &Leaf<C>, rhs: &Leaf<C>, @@ -1298,11 +1422,11 @@ pub mod test { { let mut tree = MerkleTree::<C, T>::new(parameters); assert!( - tree.append(lhs), + tree.push(lhs), "Trees always have a capacity of at least two." ); assert!( - tree.append(rhs), + tree.push(rhs), "Trees always have a capacity of at least two." ); tree.into_parameters() diff --git a/manta-pay/src/accounting/ledger.rs b/manta-pay/src/accounting/ledger.rs index 19ad7e3b5..7ba18c08a 100644 --- a/manta-pay/src/accounting/ledger.rs +++ b/manta-pay/src/accounting/ledger.rs @@ -149,7 +149,7 @@ impl Set for UtxoSet { } if !self.shards[Self::shard_index(&item)] .utxos - .append(&self.parameters, &as_bytes!(&item)) + .push(&self.parameters, &as_bytes!(&item)) { return Err(item); } From 7121744e8ccae1aff9d17b43e455e66a9fcb35e2 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sat, 25 Sep 2021 19:32:07 -0400 Subject: [PATCH 059/275] WIP: refactor merkle trees --- manta-crypto/src/merkle_tree.rs | 1451 ------------------- manta-crypto/src/merkle_tree/fork.rs | 188 +++ manta-crypto/src/merkle_tree/full.rs | 158 ++ manta-crypto/src/merkle_tree/inner_tree.rs | 349 +++++ manta-crypto/src/merkle_tree/mod.rs | 40 + manta-crypto/src/merkle_tree/node.rs | 394 +++++ manta-crypto/src/merkle_tree/single_leaf.rs | 220 +++ manta-crypto/src/merkle_tree/test.rs | 63 + manta-crypto/src/merkle_tree/tree.rs | 600 ++++++++ 9 files changed, 2012 insertions(+), 1451 deletions(-) delete mode 100644 manta-crypto/src/merkle_tree.rs create mode 100644 manta-crypto/src/merkle_tree/fork.rs create mode 100644 manta-crypto/src/merkle_tree/full.rs create mode 100644 manta-crypto/src/merkle_tree/inner_tree.rs create mode 100644 manta-crypto/src/merkle_tree/mod.rs create mode 100644 manta-crypto/src/merkle_tree/node.rs create mode 100644 manta-crypto/src/merkle_tree/single_leaf.rs create mode 100644 manta-crypto/src/merkle_tree/test.rs create mode 100644 manta-crypto/src/merkle_tree/tree.rs diff --git a/manta-crypto/src/merkle_tree.rs b/manta-crypto/src/merkle_tree.rs deleted file mode 100644 index a42d1b48f..000000000 --- a/manta-crypto/src/merkle_tree.rs +++ /dev/null @@ -1,1451 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Merkle Trees - -// TODO: Should `Leaf` move into `Tree`/`Configuration` since we might want the tree to have -// special kinds of leaf input (metadata along with just the digest)? -// TODO: Implement [`crate::VerifiedSet`] for [`MerkleTree`]. -// TODO: Maybe we should require `INNER_HEIGHT` instead of `HEIGHT` so that we don't have to rely -// on the user to check that `HEIGHT >= 2`. - -extern crate alloc; - -use alloc::vec::Vec; -use core::{ - fmt::Debug, - hash::Hash, - iter::{FusedIterator, Rev}, - ops::{Add, Range, Sub}, -}; - -/// Merkle Tree Leaf Hash -pub trait LeafHash { - /// Leaf Type - type Leaf: ?Sized; - - /// Leaf Hash Parameters Type - type Parameters; - - /// Leaf Hash Output Type - type Output: Default; - - /// Computes the digest of the `leaf` using `parameters`. - fn digest(parameters: &Self::Parameters, leaf: &Self::Leaf) -> Self::Output; -} - -/// Merkle Tree Inner Hash -pub trait InnerHash { - /// Leaf Hash Type - type LeafHash: LeafHash; - - /// Inner Hash Parameters Type - type Parameters; - - /// Inner Hash Output Type - type Output: Default + PartialEq; - - /// Combines two inner digests into a new inner digest using `parameters`. - fn join(parameters: &Self::Parameters, lhs: &Self::Output, rhs: &Self::Output) -> Self::Output; - - /// Combines two [`LeafHash`](Self::LeafHash) digests into an inner digest. - fn join_leaves( - parameters: &Self::Parameters, - lhs: &<Self::LeafHash as LeafHash>::Output, - rhs: &<Self::LeafHash as LeafHash>::Output, - ) -> Self::Output; -} - -/// Merkle Tree Configuration -pub trait Configuration { - /// Leaf Hash Type - type LeafHash: LeafHash; - - /// Inner Hash Type - type InnerHash: InnerHash<LeafHash = Self::LeafHash>; - - /// Height Type - type Height: Copy + Into<usize>; - - /// Fixed Height of the Merkle Tree - /// - /// # Contract - /// - /// Trees must always have height at least `2`. - const HEIGHT: Self::Height; -} - -/// Leaf Type -pub type Leaf<C> = <<C as Configuration>::LeafHash as LeafHash>::Leaf; - -/// Leaf Hash Parameters Type -pub type LeafHashParamters<C> = <<C as Configuration>::LeafHash as LeafHash>::Parameters; - -/// Leaf Hash Digest Type -pub type LeafDigest<C> = <<C as Configuration>::LeafHash as LeafHash>::Output; - -/// Inner Hash Parameters Type -pub type InnerHashParameters<C> = <<C as Configuration>::InnerHash as InnerHash>::Parameters; - -/// Inner Hash Digest Type -pub type InnerDigest<C> = <<C as Configuration>::InnerHash as InnerHash>::Output; - -/// Returns the capacity of the merkle tree with the given [`C::HEIGHT`](Configuration::HEIGHT) -/// parameter. -#[inline] -pub fn capacity<C>() -> usize -where - C: Configuration + ?Sized, -{ - 1usize << (C::HEIGHT.into() - 1) -} - -/// Returns the path length of the merkle tree with the given [`C::HEIGHT`](Configuration::HEIGHT) -/// parameter. -#[inline] -pub fn path_length<C>() -> usize -where - C: Configuration + ?Sized, -{ - C::HEIGHT.into() - 2 -} - -/// Returns an iterator over the depth of an inner path in reverse order, from the leaves of the -/// tree to the root. -#[inline] -pub fn depth_iter<C>() -> Rev<Range<usize>> -where - C: Configuration + ?Sized, -{ - (0..path_length::<C>()).into_iter().rev() -} - -/// Merkle Tree Structure -pub trait Tree<C>: Sized -where - C: Configuration + ?Sized, -{ - /// Path Query Type - type Query; - - /// Path Error Type - type Error; - - /// Builds a new merkle tree. - fn new(parameters: &Parameters<C>) -> Self; - - /// Builds a new merkle tree with the given `leaves` returning `None` if the iterator - /// would overflow the capacity of the tree. - #[inline] - fn from_iter<'l, L>(parameters: &Parameters<C>, leaves: L) -> Option<Self> - where - Leaf<C>: 'l, - L: IntoIterator<Item = &'l Leaf<C>>, - { - let mut tree = Self::new(parameters); - tree.extend(parameters, leaves).then(move || tree) - } - - /// Builds a new merkle tree with the given `leaves` returning `None` if the slice - /// would overflow the capacity of the tree. - #[inline] - fn from_slice(parameters: &Parameters<C>, slice: &[Leaf<C>]) -> Option<Self> - where - Leaf<C>: Sized, - { - Self::from_iter(parameters, slice) - } - - /// Returns the length of `self`. - fn len(&self) -> usize; - - /// Returns `true` if the length of `self` is zero. - #[inline] - fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Returns the [`Root`] of the merkle tree. - fn root(&self, parameters: &Parameters<C>) -> Root<C>; - - /// Returns the [`Path`] to some element of the merkle tree given by the `query`. - fn path(&self, parameters: &Parameters<C>, query: Self::Query) -> Result<Path<C>, Self::Error>; - - /// Inserts the digest of `leaf` at the next avaiable leaf node of the tree, returning - /// `false` if the leaf could not be inserted because the tree has exhausted its capacity. - fn push(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> bool; - - /// Appends an iterator of leaves at the end of the tree, returning `false` if the - /// `leaves` could not be inserted because the tree has exhausted its capacity. - /// - /// # Implementation Note - /// - /// This operation is meant to be atomic, so if appending the iterator should fail, the - /// implementation must ensure that the tree returns to the same state before the insertion - /// occured. - #[inline] - fn extend<'l, L>(&mut self, parameters: &Parameters<C>, leaves: L) -> bool - where - Leaf<C>: 'l, - L: IntoIterator<Item = &'l Leaf<C>>, - { - let leaves = leaves.into_iter(); - if matches!(leaves.size_hint().1, Some(max) if max <= capacity::<C>() - self.len()) { - for leaf in leaves { - debug_assert!(self.push(parameters, leaf)); - } - return true; - } - false - } - - /// Appends a slice of leaves at the end of the tree, returning `false` if the - /// `leaves` could not be inserted because the tree has exhausted its capacity. - /// - /// # Implementation Note - /// - /// This operation is meant to be atomic, so if appending the slice should fail, the - /// implementation must ensure that the tree returns to the same state before the insertion - /// occured. - #[inline] - fn extend_slice(&mut self, parameters: &Parameters<C>, leaves: &[Leaf<C>]) -> bool - where - Leaf<C>: Sized, - { - self.extend(parameters, leaves) - } -} - -/// Merkle Tree Pop Mixin -pub trait Pop<C>: Tree<C> -where - C: Configuration + ?Sized, -{ - /// Removes the last element of the tree rolling it back to the state right before the - /// insertion of the last leaf, returning `None` if the tree is empty. - fn pop(&mut self, parameters: &Parameters<C>) -> Option<LeafDigest<C>>; - - /// Removes `n` elements of the tree rolling it back to the state before the insertion - /// of the last `n` leaves, returning `false` if `n` is greater than the current length - /// of the tree. - /// - /// # Implementation Note - /// - /// This operation is meant to be atomic, so if removing elements should fail, the - /// implementation must ensure that the tree returns to the same state before the removal - /// occured. - #[inline] - fn pop_many(&mut self, parameters: &Parameters<C>, n: usize) -> bool { - if n > self.len() { - return false; - } - for _ in 0..n { - debug_assert!(self.pop(parameters).is_some()); - } - true - } -} - -/// Parity of a Subtree -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub enum Parity { - /// Left Side of the Subtree - Left, - - /// Right Side of the Subtree - Right, -} - -impl Parity { - /// Computes the [`Parity`] of the given `index`. - #[inline] - pub fn from_index(index: usize) -> Self { - if index % 2 == 0 { - Self::Left - } else { - Self::Right - } - } - - /// Returns `true` if `self` represents the left side of a subtree. - #[inline] - pub const fn is_left(&self) -> bool { - matches!(self, Self::Left) - } - - /// Returns `true` if `self` represents the right side of a subtree. - #[inline] - pub const fn is_right(&self) -> bool { - matches!(self, Self::Right) - } - - /// Maps `self` to the output of `lhs` and `rhs` depending on its parity. - #[inline] - pub fn map<T, L, R>(self, lhs: L, rhs: R) -> T - where - L: FnOnce() -> T, - R: FnOnce() -> T, - { - match self { - Self::Left => lhs(), - Self::Right => rhs(), - } - } - - /// Returns the arguments in the order according to the parity of `self`. - #[inline] - pub fn order<T>(&self, lhs: T, rhs: T) -> (T, T) { - match self { - Self::Left => (lhs, rhs), - Self::Right => (rhs, lhs), - } - } - - /// Returns the `center` placed in the pair at the location given by `self`, placing `lhs` and - /// `rhs` in the left or right empty slot of the pair respectively. - #[inline] - pub fn triple_order<T, L, R>(&self, center: T, lhs: L, rhs: R) -> (T, T) - where - L: FnOnce() -> T, - R: FnOnce() -> T, - { - match self { - Self::Left => (center, rhs()), - Self::Right => (lhs(), center), - } - } - - /// Combines two inner digests into a new inner digest using `parameters`, swapping the order - /// of `lhs` and `rhs` depending on the parity of `self` in its subtree. - #[inline] - pub fn join<C>( - &self, - parameters: &Parameters<C>, - lhs: &InnerDigest<C>, - rhs: &InnerDigest<C>, - ) -> InnerDigest<C> - where - C: Configuration + ?Sized, - { - let (lhs, rhs) = self.order(lhs, rhs); - C::InnerHash::join(&parameters.inner, lhs, rhs) - } - - /// Combines two leaf digests into a new inner digest using `parameters`, choosing the right - /// pair `(center, rhs)` if `self` has left parity or choosing the left pair `(lhs, center)` - /// if `self` has right parity. - #[inline] - pub fn join_opposite_pair<C>( - &self, - parameters: &Parameters<C>, - lhs: &InnerDigest<C>, - center: &InnerDigest<C>, - rhs: &InnerDigest<C>, - ) -> InnerDigest<C> - where - C: Configuration + ?Sized, - { - let (lhs, rhs) = self.triple_order(center, move || lhs, move || rhs); - C::InnerHash::join(&parameters.inner, lhs, rhs) - } - - /// Combines two leaf digests into a new inner digest using `parameters`, swapping the order - /// of `lhs` and `rhs` depending on the parity of `self` in its subtree. - #[inline] - pub fn join_leaves<C>( - &self, - parameters: &Parameters<C>, - lhs: &LeafDigest<C>, - rhs: &LeafDigest<C>, - ) -> InnerDigest<C> - where - C: Configuration + ?Sized, - { - let (lhs, rhs) = self.order(lhs, rhs); - C::InnerHash::join_leaves(&parameters.inner, lhs, rhs) - } -} - -impl Default for Parity { - #[inline] - fn default() -> Self { - Self::Left - } -} - -/// Node Index -#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] -pub struct Node<Idx = usize>( - /// Level-wise Index to a node in a Binary Tree - pub Idx, -); - -impl Node { - /// Returns the [`Parity`] of this node. - #[inline] - pub fn parity(&self) -> Parity { - Parity::from_index(self.0) - } - - /// Returns `true` if this node has left parity. - #[inline] - pub fn is_left(&self) -> bool { - self.parity().is_left() - } - - /// Returns `true` if this node has right parity. - #[inline] - pub fn is_right(&self) -> bool { - self.parity().is_right() - } - - /// Returns the [`Node`] which is the sibling to `self`. - #[inline] - pub fn sibling(&self) -> Self { - self.parity().map(move || *self + 1, move || *self - 1) - } - - /// Returns `self` with its sibling in parity order. - #[inline] - pub fn with_sibling(self) -> (Self, Self) { - self.parity() - .triple_order(self, move || self - 1, move || self + 1) - } - - /// Returns the parent [`Node`] of this node. - #[inline] - pub fn parent(&self) -> Self { - Self(self.0 >> 1) - } - - /// Converts `self` into its parent, returning the parent [`Node`]. - #[inline] - pub fn into_parent(&mut self) -> Self { - *self = self.parent(); - *self - } - - /// Returns an iterator over the parents of `self`. - #[inline] - pub fn parents(&self) -> NodeParents { - NodeParents { index: *self } - } - - /// Combines two inner digests into a new inner digest using `parameters`, swapping the order - /// of `lhs` and `rhs` depending on the location of `self` in its subtree. - #[inline] - pub fn join<C>( - &self, - parameters: &Parameters<C>, - lhs: &InnerDigest<C>, - rhs: &InnerDigest<C>, - ) -> InnerDigest<C> - where - C: Configuration + ?Sized, - { - self.parity().join(parameters, lhs, rhs) - } - - /// Combines two leaf digests into a new inner digest using `parameters`, choosing the right - /// pair `(center, rhs)` if `self` has left parity or choosing the left pair `(lhs, center)` - /// if `self` has right parity. - #[inline] - pub fn join_opposite_pair<C>( - &self, - parameters: &Parameters<C>, - lhs: &InnerDigest<C>, - center: &InnerDigest<C>, - rhs: &InnerDigest<C>, - ) -> InnerDigest<C> - where - C: Configuration + ?Sized, - { - self.parity() - .join_opposite_pair(parameters, lhs, center, rhs) - } - - /// Combines two leaf digests into a new inner digest using `parameters`, swapping the order - /// of `lhs` and `rhs` depending on the location of `self` in its subtree. - #[inline] - pub fn join_leaves<C>( - &self, - parameters: &Parameters<C>, - lhs: &LeafDigest<C>, - rhs: &LeafDigest<C>, - ) -> InnerDigest<C> - where - C: Configuration + ?Sized, - { - self.parity().join_leaves(parameters, lhs, rhs) - } -} - -impl<Idx> Add<Idx> for Node<Idx> -where - Idx: Add<Output = Idx>, -{ - type Output = Self; - - #[inline] - fn add(self, rhs: Idx) -> Self::Output { - Self(self.0 + rhs) - } -} - -impl<'i, Idx> Add<&'i Idx> for &'i Node<Idx> -where - &'i Idx: Add<Output = Idx>, -{ - type Output = Node<Idx>; - - #[inline] - fn add(self, rhs: &'i Idx) -> Self::Output { - Node(&self.0 + rhs) - } -} - -impl<Idx> Sub<Idx> for Node<Idx> -where - Idx: Sub<Output = Idx>, -{ - type Output = Self; - - #[inline] - fn sub(self, rhs: Idx) -> Self::Output { - Self(self.0 - rhs) - } -} - -impl<'i, Idx> Sub<&'i Idx> for &'i Node<Idx> -where - &'i Idx: Sub<Output = Idx>, -{ - type Output = Node<Idx>; - - #[inline] - fn sub(self, rhs: &'i Idx) -> Self::Output { - Node(&self.0 - rhs) - } -} - -impl<Idx> From<Idx> for Node<Idx> { - #[inline] - fn from(index: Idx) -> Self { - Self(index) - } -} - -impl<Idx> PartialEq<Idx> for Node<Idx> -where - Idx: PartialEq, -{ - #[inline] - fn eq(&self, rhs: &Idx) -> bool { - self.0 == *rhs - } -} - -/// Node Parent Iterator -/// -/// An iterator over the parents of a [`Node`]. -/// -/// This struct is created by the [`parents`](Node::parents) method on [`Node`]. -/// See its documentation for more. -#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] -pub struct NodeParents { - /// Current Index - index: Node, -} - -impl NodeParents { - /// Stops the iterator and returns the current node index. - #[inline] - pub fn stop(self) -> Node { - self.index - } - - /// Returns the sibling of the current parent node. - #[inline] - pub fn sibling(&self) -> Node { - self.index.sibling() - } -} - -impl AsRef<Node> for NodeParents { - #[inline] - fn as_ref(&self) -> &Node { - &self.index - } -} - -// TODO: Add all methods which can be optimized. -impl Iterator for NodeParents { - type Item = Node; - - #[inline] - fn next(&mut self) -> Option<Self::Item> { - Some(self.index.into_parent()) - } - - #[inline] - fn size_hint(&self) -> (usize, Option<usize>) { - (usize::MAX, None) - } - - #[inline] - fn last(self) -> Option<Self::Item> { - // NOTE: Although this iterator can never be completed, it has a well-defined - // final element "at infinity". - Some(Default::default()) - } -} - -impl FusedIterator for NodeParents {} - -/// Merkle Tree Parameters -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "LeafHashParamters<C>: Clone, InnerHashParameters<C>: Clone"), - Copy(bound = "LeafHashParamters<C>: Copy, InnerHashParameters<C>: Copy"), - Debug(bound = "LeafHashParamters<C>: Debug, InnerHashParameters<C>: Debug"), - Default(bound = "LeafHashParamters<C>: Default, InnerHashParameters<C>: Default"), - Eq(bound = "LeafHashParamters<C>: Eq, InnerHashParameters<C>: Eq"), - Hash(bound = "LeafHashParamters<C>: Hash, InnerHashParameters<C>: Hash"), - PartialEq(bound = "LeafHashParamters<C>: PartialEq, InnerHashParameters<C>: PartialEq") -)] -pub struct Parameters<C> -where - C: Configuration + ?Sized, -{ - /// Leaf Hash Parameters - pub leaf: LeafHashParamters<C>, - - /// Inner Hash Parameters - pub inner: InnerHashParameters<C>, -} - -impl<C> Parameters<C> -where - C: Configuration + ?Sized, -{ - /// Builds a new [`Parameters`] from `leaf` and `inner` parameters. - #[inline] - pub fn new(leaf: LeafHashParamters<C>, inner: InnerHashParameters<C>) -> Self { - Self { leaf, inner } - } - - /// Computes the leaf digest of `leaf` using `self`. - #[inline] - pub fn digest(&self, leaf: &Leaf<C>) -> LeafDigest<C> { - C::LeafHash::digest(&self.leaf, leaf) - } - - /// Combines two inner digests into a new inner digest using `self`. - #[inline] - pub fn join(&self, lhs: &InnerDigest<C>, rhs: &InnerDigest<C>) -> InnerDigest<C> { - C::InnerHash::join(&self.inner, lhs, rhs) - } - - /// Combines two leaf digests into a new inner digest using `self`. - #[inline] - pub fn join_leaves(&self, lhs: &LeafDigest<C>, rhs: &LeafDigest<C>) -> InnerDigest<C> { - C::InnerHash::join_leaves(&self.inner, lhs, rhs) - } - - /// Verify that `path` witnesses the fact that `leaf` is a member of a merkle tree with the - /// given `root`. - #[inline] - pub fn verify_path(&self, path: &Path<C>, root: &Root<C>, leaf: &Leaf<C>) -> bool { - path.verify(self, root, leaf) - } -} - -/// Merkle Tree Root Wrapper Type -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "InnerDigest<C>: Clone"), - Copy(bound = "InnerDigest<C>: Copy"), - Debug(bound = "InnerDigest<C>: Debug"), - Default(bound = "InnerDigest<C>: Default"), - Eq(bound = "InnerDigest<C>: Eq"), - Hash(bound = "InnerDigest<C>: Hash"), - PartialEq(bound = "InnerDigest<C>: PartialEq") -)] -pub struct Root<C>( - /// Root Inner Digest - pub InnerDigest<C>, -) -where - C: Configuration + ?Sized; - -impl<C> AsRef<InnerDigest<C>> for Root<C> -where - C: Configuration + ?Sized, -{ - #[inline] - fn as_ref(&self) -> &InnerDigest<C> { - &self.0 - } -} - -impl<C> AsMut<InnerDigest<C>> for Root<C> -where - C: Configuration + ?Sized, -{ - #[inline] - fn as_mut(&mut self) -> &mut InnerDigest<C> { - &mut self.0 - } -} - -/// Merkle Tree Path -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), - Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), - Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), - Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), - PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") -)] -pub struct Path<C> -where - C: Configuration + ?Sized, -{ - /// Leaf Index - pub leaf_index: Node, - - /// Sibling Digest - pub sibling_digest: LeafDigest<C>, - - /// Inner Path - /// - /// Inner digests are stored from leaf to root, not including the root. - pub inner_path: Vec<InnerDigest<C>>, -} - -impl<C> Path<C> -where - C: Configuration + ?Sized, -{ - /// Builds a new [`Path`] from `leaf_index`, `sibling_digest`, and `inner_path`. - #[inline] - pub fn new( - leaf_index: Node, - sibling_digest: LeafDigest<C>, - inner_path: Vec<InnerDigest<C>>, - ) -> Self { - Self { - leaf_index, - sibling_digest, - inner_path, - } - } - - /// Computes the root of the merkle tree relative to `leaf_digest` using `parameters`. - #[inline] - pub fn root_relative_to( - &self, - parameters: &Parameters<C>, - leaf_digest: &LeafDigest<C>, - ) -> Root<C> { - let mut index = self.leaf_index; - Root(self.inner_path.iter().fold( - index.join_leaves(parameters, leaf_digest, &self.sibling_digest), - move |acc, d| index.into_parent().join(parameters, &acc, d), - )) - } - - /// Returns `true` if `self` is a witness to the fact that `leaf_digest` is stored in a - /// merkle tree with the given `root`. - #[inline] - pub fn verify_digest( - &self, - parameters: &Parameters<C>, - root: &Root<C>, - leaf_digest: &LeafDigest<C>, - ) -> bool { - root == &self.root_relative_to(parameters, leaf_digest) - } - - /// Returns `true` if `self` is a witness to the fact that `leaf` is stored in a merkle tree - /// with the given `root`. - #[inline] - pub fn verify(&self, parameters: &Parameters<C>, root: &Root<C>, leaf: &Leaf<C>) -> bool { - self.verify_digest(parameters, root, &parameters.digest(leaf)) - } -} - -impl<C> Default for Path<C> -where - C: Configuration + ?Sized, -{ - #[inline] - fn default() -> Self { - let path_length = path_length::<C>(); - let mut inner_path = Vec::with_capacity(path_length); - inner_path.resize_with(path_length, InnerDigest::<C>::default); - Self::new(Default::default(), Default::default(), inner_path) - } -} - -/// Merkle Tree -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "Parameters<C>: Clone, T: Clone"), - Copy(bound = "Parameters<C>: Copy, T: Copy"), - Debug(bound = "Parameters<C>: Debug, T: Debug"), - Default(bound = "Parameters<C>: Default, T: Default"), - Eq(bound = "Parameters<C>: Eq, T: Eq"), - Hash(bound = "Parameters<C>: Hash, T: Hash"), - PartialEq(bound = "Parameters<C>: PartialEq, T: PartialEq") -)] -pub struct MerkleTree<C, T> -where - C: Configuration + ?Sized, - T: Tree<C>, -{ - /// Underlying Tree Structure - tree: T, - - /// Merkle Tree Parameters - parameters: Parameters<C>, -} - -impl<C, T> MerkleTree<C, T> -where - C: Configuration + ?Sized, - T: Tree<C>, -{ - /// Builds a new [`MerkleTree`]. - #[inline] - pub fn new(parameters: Parameters<C>) -> Self { - Self::from_tree(T::new(&parameters), parameters) - } - - /// Builds a new [`MerkleTree`] with the given `leaves`. - #[inline] - pub fn from_iter<'l, L>(parameters: Parameters<C>, leaves: L) -> Option<Self> - where - Leaf<C>: 'l, - L: IntoIterator<Item = &'l Leaf<C>>, - { - Some(Self::from_tree( - T::from_iter(&parameters, leaves)?, - parameters, - )) - } - - /// Builds a new [`MerkleTree`] with the given `leaves`. - #[inline] - pub fn from_slice(parameters: Parameters<C>, leaves: &[Leaf<C>]) -> Option<Self> - where - Leaf<C>: Sized, - { - Some(Self::from_tree( - T::from_slice(&parameters, leaves)?, - parameters, - )) - } - - /// Builds a new [`MerkleTree`] from a pre-constructed `tree` and `parameters`. - #[inline] - pub fn from_tree(tree: T, parameters: Parameters<C>) -> Self { - Self { tree, parameters } - } - - /// Returns a reference to the parameters used by this merkle tree. - #[inline] - pub fn parameters(&self) -> &Parameters<C> { - &self.parameters - } - - /// Returns the [`Root`] of the merkle tree. - #[inline] - pub fn root(&self) -> Root<C> { - self.tree.root(&self.parameters) - } - - /// Returns the [`Path`] to some element of the merkle tree given by the `query`. - #[inline] - pub fn path(&self, query: T::Query) -> Result<Path<C>, T::Error> { - self.tree.path(&self.parameters, query) - } - - /// Inserts `leaf` at the next avaiable leaf node of the tree, returning `false` if the - /// leaf could not be inserted because the tree has exhausted its capacity. - #[inline] - pub fn push(&mut self, leaf: &Leaf<C>) -> bool { - self.tree.push(&self.parameters, leaf) - } - - /// Appends an iterator of leaves at the end of the tree, returning `false` if the `leaves` - /// could not be inserted because the tree has exhausted its capacity. - #[inline] - pub fn extend<'l, L>(&mut self, leaves: L) -> bool - where - Leaf<C>: 'l, - L: IntoIterator<Item = &'l Leaf<C>>, - { - self.tree.extend(&self.parameters, leaves) - } - - /// Appends a slice of leaves at the end of the tree, returning `false` if the `leaves` could - /// not be inserted because the tree has exhausted its capacity. - #[inline] - pub fn extend_slice(&mut self, leaves: &[Leaf<C>]) -> bool - where - Leaf<C>: Sized, - { - self.tree.extend_slice(&self.parameters, leaves) - } - - /// Removes the last element of the tree rolling it back to the state right before the - /// insertion of the last leaf, returning `None` if the tree is empty. - #[inline] - pub fn pop(&mut self) -> Option<LeafDigest<C>> - where - T: Pop<C>, - { - self.tree.pop(&self.parameters) - } - - /// Removes `n` elements of the tree rolling it back to the state before the insertion of the - /// last `n` leaves, returning `false` if `n` is greater than the current length of the tree. - #[inline] - pub fn pop_many(&mut self, n: usize) -> bool - where - T: Pop<C>, - { - self.tree.pop_many(&self.parameters, n) - } - - /// Extracts the parameters of the merkle tree, dropping the internal tree. - #[inline] - pub fn into_parameters(self) -> Parameters<C> { - self.parameters - } -} - -impl<C, T> AsRef<T> for MerkleTree<C, T> -where - C: Configuration + ?Sized, - T: Tree<C>, -{ - #[inline] - fn as_ref(&self) -> &T { - &self.tree - } -} - -impl<C, T> AsMut<T> for MerkleTree<C, T> -where - C: Configuration + ?Sized, - T: Tree<C>, -{ - #[inline] - fn as_mut(&mut self) -> &mut T { - &mut self.tree - } -} - -/// Full Merkle Tree Storage -pub mod full { - use super::*; - use alloc::collections::BTreeMap; - - /// Full Merkle Tree Type - pub type FullMerkleTree<C> = MerkleTree<C, Full<C>>; - - /// Path Query Error Type - /// - /// Querying for paths beyond the current length of a [`Full`] tree is an error. - #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct Unknown; - - /// Full Merkle Tree Backing Structure - #[derive(derivative::Derivative)] - #[derivative( - Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), - Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), - Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default"), - Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), - Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), - PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") - )] - pub struct Full<C> - where - C: Configuration + ?Sized, - { - /// Leaf Digests - leaf_digests: Vec<LeafDigest<C>>, - - /// Inner Digests - /// - /// See [`Self::inner_digest_index`] for the encoding of tree coordinates. - inner_digests: BTreeMap<usize, InnerDigest<C>>, - } - - impl<C> Full<C> - where - C: Configuration + ?Sized, - { - /// Returns the leaf digests currently stored in the merkle tree. - #[inline] - pub fn leaf_digests(&self) -> &[LeafDigest<C>] { - &self.leaf_digests - } - - /// Returns a reference to the root inner digest, returning `None` if the tree is empty. - #[inline] - pub fn root(&self) -> Option<&InnerDigest<C>> { - self.inner_digests.get(&0) - } - - /// Returns the sibling leaf node to `index`. - #[inline] - fn get_leaf_sibling(&self, index: Node) -> Option<&LeafDigest<C>> { - // TODO: Add `Index` methods to accept `Node` as indices. - self.leaf_digests.get(index.sibling().0) - } - - /// Computes the starting index for the given `depth` in the `inner_digests` map. - /// - /// # Note - /// - /// The `depth` of the tree tracks the inner nodes not including the root. The root of the - /// tree is at `depth := -1` and `index := 0`. - #[inline] - fn depth_starting_index(depth: usize) -> usize { - (1 << (depth + 1)) - 1 - } - - /// Computes the index into the `inner_digests` map for a node of the given `depth` and - /// `index`. - /// - /// # Note - /// - /// The `depth` of the tree tracks the inner nodes not including the root. The root of the - /// tree is at `depth := -1` and `index := 0`. - #[inline] - fn inner_digest_index(depth: usize, index: Node) -> usize { - Self::depth_starting_index(depth) + index.0 - } - - /// Returns the inner digest at the given `depth` and `index` of the merkle tree. - /// - /// # Note - /// - /// The `depth` of the tree tracks the inner nodes not including the root. The root of the - /// tree is at `depth := -1` and `index := 0`. - #[inline] - fn get_inner_digest(&self, depth: usize, index: Node) -> Option<&InnerDigest<C>> { - self.inner_digests - .get(&Self::inner_digest_index(depth, index)) - } - - /// Sets the new `inner_digest` at `(depth, index)` in the inner digest map, and returns - /// the back a reference to `inner_digest` and its sibling in the tree in parity order. - /// - /// # Note - /// - /// The `depth` of the tree tracks the inner nodes not including the root. The root of the - /// tree is at `depth := -1` and `index := 0`. - #[inline] - fn set_and_get_inner_pair<'s>( - &'s mut self, - depth: usize, - index: Node, - inner_digest: InnerDigest<C>, - default: &'s InnerDigest<C>, - ) -> (&'s InnerDigest<C>, &'s InnerDigest<C>) { - let depth_starting_index = Self::depth_starting_index(depth); - self.inner_digests - .insert(depth_starting_index + index.0, inner_digest); - let (lhs_index, rhs_index) = index.with_sibling(); - ( - self.inner_digests - .get(&(depth_starting_index + lhs_index.0)) - .unwrap_or(default), - self.inner_digests - .get(&(depth_starting_index + rhs_index.0)) - .unwrap_or(default), - ) - } - - /// Computes the inner path of a node starting at the leaf given by `index`. - #[inline] - fn compute_inner_path(&self, mut index: Node) -> Vec<InnerDigest<C>> - where - InnerDigest<C>: Clone, - { - depth_iter::<C>() - .map(move |depth| { - self.get_inner_digest(depth, index.into_parent().sibling()) - .map(Clone::clone) - .unwrap_or_default() - }) - .collect() - } - - /// Computes the root of the merkle tree, modifying the inner tree in-place, starting at - /// the leaf given by `index`. - #[inline] - fn compute_root( - &mut self, - parameters: &Parameters<C>, - mut index: Node, - base: InnerDigest<C>, - ) -> InnerDigest<C> { - let default_inner_digest = Default::default(); - depth_iter::<C>().fold(base, move |acc, depth| { - let (lhs, rhs) = self.set_and_get_inner_pair( - depth, - index.into_parent(), - acc, - &default_inner_digest, - ); - parameters.join(lhs, rhs) - }) - } - } - - impl<C> Tree<C> for Full<C> - where - C: Configuration + ?Sized, - LeafDigest<C>: Clone, - InnerDigest<C>: Clone, - { - type Query = usize; - - type Error = Unknown; - - #[inline] - fn new(parameters: &Parameters<C>) -> Self { - let _ = parameters; - Default::default() - } - - #[inline] - fn len(&self) -> usize { - self.leaf_digests.len() - } - - #[inline] - fn root(&self, parameters: &Parameters<C>) -> Root<C> { - let _ = parameters; - Root(self.root().map(Clone::clone).unwrap_or_default()) - } - - #[inline] - fn path( - &self, - parameters: &Parameters<C>, - query: Self::Query, - ) -> Result<Path<C>, Self::Error> { - let _ = parameters; - if query > capacity::<C>() { - return Err(Unknown); - } - let leaf_index = Node(query); - Ok(Path::new( - leaf_index, - self.get_leaf_sibling(leaf_index) - .map(Clone::clone) - .unwrap_or_default(), - self.compute_inner_path(leaf_index), - )) - } - - #[inline] - fn push(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> bool { - let len = self.len(); - if len >= capacity::<C>() { - return false; - } - let leaf_digest = parameters.digest(leaf); - let leaf_index = Node(len); - let root = self.compute_root( - parameters, - leaf_index, - leaf_index.join_leaves( - parameters, - &leaf_digest, - self.get_leaf_sibling(leaf_index) - .unwrap_or(&Default::default()), - ), - ); - self.inner_digests.insert(0, root); - self.leaf_digests.push(leaf_digest); - true - } - } -} - -/// Latest Node Merkle Tree Storage -pub mod latest_node { - use super::*; - use core::convert::Infallible; - - /// Latest Node Merkle Tree Type - pub type LatestNodeMerkleTree<C> = MerkleTree<C, LatestNode<C>>; - - /// Path Query Type - /// - /// Since the [`LatestNode`] tree only stores one node and one path, we can only query the - /// tree for the current path. - #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct Current; - - /// Latest Node Merkle Tree Backing Structure - #[derive(derivative::Derivative)] - #[derivative( - Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), - Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), - Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default"), - Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), - Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), - PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") - )] - pub struct LatestNode<C> - where - C: Configuration + ?Sized, - { - /// Leaf Digest - leaf_digest: Option<LeafDigest<C>>, - - /// Path - path: Path<C>, - - /// Root - root: Root<C>, - } - - impl<C> LatestNode<C> - where - C: Configuration + ?Sized, - { - /// Returns the number of leaves in the merkle tree. - #[inline] - fn len(&self) -> usize { - if self.leaf_digest.is_none() { - 0 - } else { - self.path.leaf_index.0 + 1 - } - } - - /// Returns the next avaiable index or `None` if the merkle tree is full. - #[inline] - fn next_index(&self) -> Option<Node> { - let len = self.len(); - if len == 0 { - Some(Node(0)) - } else if len < capacity::<C>() - 1 { - Some(Node(len + 1)) - } else { - None - } - } - - /// Returns the current merkle tree root. - #[inline] - pub fn root(&self) -> &Root<C> { - &self.root - } - - /// Returns the current merkle tree path for the current leaf. - #[inline] - pub fn path(&self) -> &Path<C> { - &self.path - } - - /// Returns the currently stored leaf digest, returning `None` if the tree is empty. - #[inline] - pub fn leaf_digest(&self) -> Option<&LeafDigest<C>> { - self.leaf_digest.as_ref() - } - } - - impl<C> Tree<C> for LatestNode<C> - where - C: Configuration + ?Sized, - LeafDigest<C>: Clone, - InnerDigest<C>: Clone, - { - type Query = Current; - - type Error = Infallible; - - #[inline] - fn new(parameters: &Parameters<C>) -> Self { - let _ = parameters; - Default::default() - } - - #[inline] - fn len(&self) -> usize { - self.len() - } - - #[inline] - fn root(&self, parameters: &Parameters<C>) -> Root<C> { - let _ = parameters; - self.root.clone() - } - - #[inline] - fn path( - &self, - parameters: &Parameters<C>, - query: Self::Query, - ) -> Result<Path<C>, Self::Error> { - let _ = (parameters, query); - Ok(self.path.clone()) - } - - #[inline] - fn push(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> bool { - let index = match self.next_index() { - Some(index) => index, - _ => return false, - }; - - let leaf_digest = parameters.digest(leaf); - - if index == 0 { - self.root = self.path.root_relative_to(parameters, &leaf_digest); - } else { - let mut next_index = index; - - let default_leaf_digest = Default::default(); - let default_inner_digest = Default::default(); - - let current_leaf_digest = self.leaf_digest.as_ref().unwrap(); - - let (mut accumulator, sibling_digest) = match next_index.parity() { - Parity::Left => ( - parameters.join_leaves(&leaf_digest, &default_leaf_digest), - default_leaf_digest, - ), - Parity::Right => ( - parameters.join_leaves(current_leaf_digest, &leaf_digest), - current_leaf_digest.clone(), - ), - }; - - let mut prev_index = next_index - 1; - let mut prev_digest = prev_index.join_leaves( - parameters, - current_leaf_digest, - &self.path.sibling_digest, - ); - - // TODO: Mutate the path in place. - - let inner_path = self - .path - .inner_path - .iter() - .map(|digest| { - if prev_index.into_parent() == next_index.into_parent() { - accumulator = next_index.join_opposite_pair( - parameters, - digest, - &accumulator, - &default_inner_digest, - ); - digest.clone() - } else { - let next_index_parity = next_index.parity(); - - let next_inner_path_digest = next_index_parity - .map(|| default_inner_digest.clone(), || prev_digest.clone()); - - accumulator = next_index_parity.join_opposite_pair( - parameters, - &prev_digest, - &accumulator, - &default_inner_digest, - ); - - if prev_index.is_right() { - prev_digest = parameters.join(digest, &prev_digest); - } - - next_inner_path_digest - } - }) - .collect(); - - self.path = Path::new(index, sibling_digest, inner_path); - self.root = Root(accumulator); - } - - self.leaf_digest = Some(leaf_digest); - - true - } - } -} - -/// Testing Framework -#[cfg(feature = "test")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] -pub mod test { - use super::*; - use core::fmt::Debug; - - /// Tests that a tree constructed with `parameters` can accept at least two leaves without - /// failing. - #[inline] - pub fn push_twice_to_empty_tree_succeeds<C, T>( - parameters: Parameters<C>, - lhs: &Leaf<C>, - rhs: &Leaf<C>, - ) -> Parameters<C> - where - C: Configuration + ?Sized, - T: Tree<C>, - { - let mut tree = MerkleTree::<C, T>::new(parameters); - assert!( - tree.push(lhs), - "Trees always have a capacity of at least two." - ); - assert!( - tree.push(rhs), - "Trees always have a capacity of at least two." - ); - tree.into_parameters() - } - - /// Tests path construction by checking that the path generated by `query` on `tree` is a valid - /// [`Path`] for `leaf`. - #[inline] - pub fn assert_valid_path<C, T>(tree: &MerkleTree<C, T>, query: T::Query, leaf: &Leaf<C>) - where - C: Configuration + ?Sized, - T: Tree<C>, - T::Error: Debug, - { - assert!( - tree.path(query) - .expect("Only valid queries are accepted.") - .verify(&tree.parameters, &tree.root(), leaf), - "Path returned from tree was not valid." - ) - } -} diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs new file mode 100644 index 000000000..7462a6628 --- /dev/null +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -0,0 +1,188 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Merkle Tree Forks + +extern crate alloc; + +use crate::merkle_tree::{ + capacity, inner_tree::InnerTree, Configuration, InnerDigest, Leaf, LeafDigest, MerkleTree, + Parameters, Path, Root, Tree, +}; +use alloc::vec::Vec; +use core::{fmt::Debug, hash::Hash}; + +/// Merkle Tree Delta +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), + Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), + Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default"), + Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), + Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), + PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") +)] +pub struct Delta<C> +where + C: Configuration + ?Sized, +{ + /// Leaf Digests + pub(super) leaf_digests: Vec<LeafDigest<C>>, + + /// Inner Digests + pub(super) inner_digests: InnerTree<C>, +} + +impl<C> Delta<C> +where + C: Configuration + ?Sized, +{ + /// Builds a new [`Delta`] from `leaf_digests` and `inner_digests`. + #[inline] + pub fn new(leaf_digests: Vec<LeafDigest<C>>, inner_digests: InnerTree<C>) -> Self { + Self { + leaf_digests, + inner_digests, + } + } + + /// + #[inline] + pub fn len(&self) -> usize { + self.leaf_digests.len() + } + + /// + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// + #[inline] + fn root<T>(&self, parameters: &Parameters<C>, trunk: &T) -> Root<C> + where + T: Tree<C>, + { + todo!() + } + + /// + #[inline] + fn path<T>( + &self, + parameters: &Parameters<C>, + trunk: &T, + query: ForkQuery<C, T>, + ) -> Result<Path<C>, T::Error> + where + T: Tree<C>, + { + todo!() + } + + /// + #[inline] + fn push<T>(&mut self, parameters: &Parameters<C>, trunk: &T, leaf: &Leaf<C>) -> bool + where + T: Tree<C>, + { + if trunk.len() + self.len() >= capacity::<C>() { + return false; + } + + todo!() + } +} + +/// Merkle Tree Fork +#[derive(derivative::Derivative)] +/* TODO: +#[derivative( + Clone(bound = "Parameters<C>: Clone, T: Clone"), + Copy(bound = "Parameters<C>: Copy, T: Copy"), + Debug(bound = "Parameters<C>: Debug, T: Debug"), + Default(bound = "Parameters<C>: Default, T: Default"), + Eq(bound = "Parameters<C>: Eq, T: Eq"), + Hash(bound = "Parameters<C>: Hash, T: Hash"), + PartialEq(bound = "Parameters<C>: PartialEq, T: PartialEq") +)] +*/ +pub struct Fork<'t, C, T> +where + C: Configuration + ?Sized, + T: Tree<C>, +{ + /// Original Trunk + pub(super) trunk: &'t MerkleTree<C, T>, + + /// Delta + pub(super) delta: Delta<C>, +} + +impl<'t, C, T> Fork<'t, C, T> +where + C: Configuration + ?Sized, + T: Tree<C>, +{ + /// + #[inline] + pub fn new(trunk: &'t MerkleTree<C, T>) -> Self { + Self { + trunk, + delta: Default::default(), + } + } + + /// + #[inline] + pub fn parameters(&self) -> &Parameters<C> { + self.trunk.parameters() + } + + /// + #[inline] + pub fn root(&self) -> Root<C> { + self.delta.root(&self.trunk.parameters, &self.trunk.tree) + } + + /// + #[inline] + pub fn path(&self, query: ForkQuery<C, T>) -> Result<Path<C>, T::Error> { + self.delta + .path(&self.trunk.parameters, &self.trunk.tree, query) + } + + /// + #[inline] + pub fn push(&mut self, leaf: &Leaf<C>) -> bool { + self.delta + .push(&self.trunk.parameters, &self.trunk.tree, leaf) + } +} + +/// Fork Query Type +pub enum ForkQuery<C, T> +where + C: Configuration + ?Sized, + T: Tree<C>, +{ + /// Trunk Query + Trunk(T::Query), + + /// Branch Query + Branch(usize), +} diff --git a/manta-crypto/src/merkle_tree/full.rs b/manta-crypto/src/merkle_tree/full.rs new file mode 100644 index 000000000..187828307 --- /dev/null +++ b/manta-crypto/src/merkle_tree/full.rs @@ -0,0 +1,158 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Full Merkle Tree Storage + +extern crate alloc; + +use crate::merkle_tree::{ + capacity, inner_tree::InnerTree, Configuration, InnerDigest, LeafDigest, MerkleTree, Node, + Parameters, Path, Root, Tree, +}; +use alloc::vec::Vec; +use core::{fmt::Debug, hash::Hash}; + +/// Full Merkle Tree Type +pub type FullMerkleTree<C> = MerkleTree<C, Full<C>>; + +/// Path Query Error Type +/// +/// Querying for paths beyond the current length of a [`Full`] tree is an error. +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Unknown; + +/// Full Merkle Tree Backing Structure +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), + Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), + Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default"), + Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), + Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), + PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") +)] +pub struct Full<C> +where + C: Configuration + ?Sized, +{ + /// Leaf Digests + leaf_digests: Vec<LeafDigest<C>>, + + /// Inner Digests + inner_digests: InnerTree<C>, +} + +impl<C> Full<C> +where + C: Configuration + ?Sized, +{ + /// Returns the leaf digests currently stored in the merkle tree. + #[inline] + pub fn leaf_digests(&self) -> &[LeafDigest<C>] { + &self.leaf_digests + } + + /// Returns a reference to the root inner digest. + #[inline] + pub fn root(&self) -> &InnerDigest<C> { + self.inner_digests.previous_root() + } + + /// Returns the sibling leaf node to `index`. + #[inline] + fn get_leaf_sibling(&self, index: Node) -> Option<&LeafDigest<C>> { + // TODO: Add `Index` methods to accept `Node` as indices. + self.leaf_digests.get(index.sibling().0) + } + + /// Appends a `leaf_digest` with index given by `leaf_index` into the tree. + #[inline] + fn push_leaf_digest( + &mut self, + parameters: &Parameters<C>, + leaf_index: Node, + leaf_digest: LeafDigest<C>, + ) { + self.inner_digests.insert( + parameters, + leaf_index, + leaf_index.join_leaves( + parameters, + &leaf_digest, + self.get_leaf_sibling(leaf_index) + .unwrap_or(&Default::default()), + ), + ); + self.leaf_digests.push(leaf_digest); + } +} + +impl<C> Tree<C> for Full<C> +where + C: Configuration + ?Sized, + LeafDigest<C>: Clone, + InnerDigest<C>: Clone, +{ + type Query = usize; + + type Error = Unknown; + + #[inline] + fn new(parameters: &Parameters<C>) -> Self { + let _ = parameters; + Default::default() + } + + #[inline] + fn len(&self) -> usize { + self.leaf_digests.len() + } + + #[inline] + fn root(&self, parameters: &Parameters<C>) -> Root<C> { + let _ = parameters; + Root(self.root().clone()) + } + + #[inline] + fn path(&self, parameters: &Parameters<C>, query: Self::Query) -> Result<Path<C>, Self::Error> { + let _ = parameters; + if query > capacity::<C>() { + return Err(Unknown); + } + let leaf_index = Node(query); + Ok(Path::new( + leaf_index, + self.get_leaf_sibling(leaf_index) + .map(Clone::clone) + .unwrap_or_default(), + self.inner_digests.inner_path_for_leaf(leaf_index), + )) + } + + #[inline] + fn maybe_push_digest<F>(&mut self, parameters: &Parameters<C>, leaf_digest: F) -> Option<bool> + where + F: FnOnce() -> Option<LeafDigest<C>>, + { + let len = self.len(); + if len >= capacity::<C>() { + return Some(false); + } + self.push_leaf_digest(parameters, Node(len), leaf_digest()?); + Some(true) + } +} diff --git a/manta-crypto/src/merkle_tree/inner_tree.rs b/manta-crypto/src/merkle_tree/inner_tree.rs new file mode 100644 index 000000000..46d705a3b --- /dev/null +++ b/manta-crypto/src/merkle_tree/inner_tree.rs @@ -0,0 +1,349 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Inner Digest Tree + +extern crate alloc; + +use crate::merkle_tree::{path_length, Configuration, InnerDigest, Node, Parameters, Parity}; +use alloc::{collections::BTreeMap, vec::Vec}; +use core::{fmt::Debug, hash::Hash, iter::FusedIterator}; + +/// Inner Tree Node +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +pub struct InnerNode { + /// Depth + depth: usize, + + /// Index + index: Node, +} + +impl InnerNode { + /// Builds a new [`InnerNode`] from `depth` and `index`. + #[inline] + const fn new(depth: usize, index: Node) -> Self { + Self { depth, index } + } + + /// Builds an [`InnerNode`] as the parent of a `leaf_index`, returning `None` if the + /// parent of `leaf_index` is the root and not an inner node. + #[inline] + pub fn from_leaf<C>(leaf_index: Node) -> Option<Self> + where + C: Configuration + ?Sized, + { + Self::new(path_length::<C>(), leaf_index).parent() + } + + /// Returns the [`Parity`] of this inner node. + #[inline] + pub const fn parity(&self) -> Parity { + self.index.parity() + } + + /// Returns `true` if this inner node has left parity. + #[inline] + pub const fn is_left(&self) -> bool { + self.parity().is_left() + } + + /// Returns `true` if this inner node has right parity. + #[inline] + pub const fn is_right(&self) -> bool { + self.parity().is_right() + } + + /// Returns the [`InnerNode`] which is the sibling of `self`. + #[inline] + pub const fn sibling(&self) -> Self { + Self::new(self.depth, self.index.sibling()) + } + + /// Returns the parent [`InnerNode`] of this inner node. + #[inline] + pub const fn parent(&self) -> Option<Self> { + match self.depth.checked_sub(1) { + Some(depth) => Some(Self::new(depth, self.index.parent())), + _ => None, + } + } + + /// Converts `self` into its parent, if the parent exists, returning the parent [`InnerNode`]. + #[inline] + pub fn into_parent(&mut self) -> Option<Self> { + match self.parent() { + Some(parent) => { + *self = parent; + Some(*self) + } + _ => None, + } + } + + /// Returns an iterator over `self` and its parents. + #[inline] + pub const fn iter(&self) -> InnerNodeIter { + InnerNodeIter::new(Some(*self)) + } + + /// Computes the starting index for the given `self.depth` in the tree. + #[inline] + const fn depth_starting_index(&self) -> usize { + (1 << (self.depth + 1)) - 1 + } + + /// Computes the index into the tree map of `self`. + #[inline] + const fn map_index(&self) -> usize { + self.depth_starting_index() + self.index.0 + } +} + +impl From<InnerNode> for Node { + #[inline] + fn from(inner_node: InnerNode) -> Node { + inner_node.index + } +} + +/// Inner Node Iterator +/// +/// An iterator over the parents of an [`InnerNode`], including the node itself. +/// +/// This `struct` is created by the [`iter`](InnerNode::iter) method on [`InnerNode`]. +/// See its documentation for more. +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +pub struct InnerNodeIter { + /// Current Node + node: Option<InnerNode>, +} + +impl InnerNodeIter { + /// Builds a new [`InnerNodeIter`] from `node`. + #[inline] + const fn new(node: Option<InnerNode>) -> Self { + Self { node } + } + + /// Builds a new [`InnerNodeIter`] iterator over the parents of `leaf_index`. + #[inline] + pub fn from_leaf<C>(leaf_index: Node) -> Self + where + C: Configuration + ?Sized, + { + Self::new(InnerNode::from_leaf::<C>(leaf_index)) + } +} + +// TODO: Add all methods which can be optimized. +impl Iterator for InnerNodeIter { + type Item = InnerNode; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + let node = self.node.take()?; + self.node = node.parent(); + Some(node) + } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + let len = self.node.map(move |n| n.depth + 1).unwrap_or(0); + (len, Some(len)) + } +} + +impl ExactSizeIterator for InnerNodeIter {} + +impl FusedIterator for InnerNodeIter {} + +/// Inner Tree +/// +/// Tree data-structure for storing the inner digests of a merkle tree. +/// +/// # Coordinates +/// +/// Locations in the tree are indexed by two coordinates `depth` and `index`. The `depth` of a +/// node is given by its layer in the tree starting from `depth := -1` at the root increasing +/// downwards towards the leaves. The `index` of a node is its position from left to right along a +/// layer in the tree. +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "InnerDigest<C>: Clone"), + Debug(bound = "InnerDigest<C>: Debug"), + Default(bound = "InnerDigest<C>: Default"), + Eq(bound = "InnerDigest<C>: Eq"), + Hash(bound = "InnerDigest<C>: Hash"), + PartialEq(bound = "InnerDigest<C>: PartialEq") +)] +pub struct InnerTree<C> +where + C: Configuration + ?Sized, +{ + /// Inner Digest Map + /// + /// See [`inner_digest_index`](Self::inner_digest_index) for the definition of the tree + /// coordinate system. + map: BTreeMap<usize, InnerDigest<C>>, + + /// Inner Digest Default Value + default: InnerDigest<C>, +} + +impl<C> InnerTree<C> +where + C: Configuration + ?Sized, +{ + /// Builds a new [`InnerTree`]. + #[inline] + pub fn new() -> Self { + Default::default() + } + + /// Returns a reference to the root inner digest. + #[inline] + pub fn previous_root(&self) -> &InnerDigest<C> { + self.map.get(&0).unwrap_or(&self.default) + } + + /// Returns the inner digest at `node` or the default value if the inner digest is missing. + #[inline] + pub fn get(&self, node: InnerNode) -> &InnerDigest<C> { + self.map.get(&node.map_index()).unwrap_or(&self.default) + } + + /// Sets the new `inner_digest` at `(depth, index)` in the tree, and returns back a + /// reference to `inner_digest` and its sibling in the tree in parity order. + #[inline] + fn set_and_get_inner_pair( + &mut self, + node: InnerNode, + inner_digest: InnerDigest<C>, + ) -> (&InnerDigest<C>, &InnerDigest<C>) { + // TODO: Optimize this so we can remove the extra add and `unwrap_or`. + let depth_starting_index = node.depth_starting_index(); + self.map + .insert(depth_starting_index + node.index.0, inner_digest); + let (lhs_index, rhs_index) = node.index.with_sibling(); + ( + self.map + .get(&(depth_starting_index + lhs_index.0)) + .unwrap_or(&self.default), + self.map + .get(&(depth_starting_index + rhs_index.0)) + .unwrap_or(&self.default), + ) + } + + /// Inserts `inner_digest` into the tree at `node` and computes the join of `inner_digest` + /// and its sibling in the tree, using the `default` value if it's sibling is not stored in + /// the tree. + #[inline] + fn insert_and_join( + &mut self, + parameters: &Parameters<C>, + node: InnerNode, + inner_digest: InnerDigest<C>, + ) -> InnerDigest<C> { + let (lhs, rhs) = self.set_and_get_inner_pair(node, inner_digest); + parameters.join(lhs, rhs) + } + + /// Computes the new root of the tree after inserting `base` which corresponds to the leaf at + /// `leaf_index`. + #[inline] + fn compute_root( + &mut self, + parameters: &Parameters<C>, + leaf_index: Node, + base: InnerDigest<C>, + ) -> InnerDigest<C> { + InnerNodeIter::from_leaf::<C>(leaf_index).fold(base, move |acc, node| { + self.insert_and_join(parameters, node, acc) + }) + } + + /// Inserts the `base` inner digest corresponding to the leaf at `leaf_index` into the tree. + #[inline] + pub fn insert(&mut self, parameters: &Parameters<C>, leaf_index: Node, base: InnerDigest<C>) { + // TODO: Implement random insertion, not just for leaves. + let root = self.compute_root(parameters, leaf_index, base); + self.map.insert(0, root); + } + + /// Computes the inner path starting from `node`. + #[inline] + pub fn inner_path(&self, node: InnerNode) -> Vec<InnerDigest<C>> + where + InnerDigest<C>: Clone, + { + node.iter() + .map(move |node| self.get(node.sibling()).clone()) + .collect() + } + + /// Computes the inner path of the leaf given by `leaf_index`. + #[inline] + pub fn inner_path_for_leaf(&self, leaf_index: Node) -> Vec<InnerDigest<C>> + where + InnerDigest<C>: Clone, + { + InnerNodeIter::from_leaf::<C>(leaf_index) + .map(move |node| self.get(node.sibling()).clone()) + .collect() + } +} + +/// Inner Path Iterator +#[derive(derivative::Derivative)] +#[derivative( + Copy, + Clone, + Debug(bound = "InnerDigest<C>: Debug"), + Eq(bound = "InnerDigest<C>: Eq"), + Hash(bound = "InnerDigest<C>: Hash"), + PartialEq(bound = "InnerDigest<C>: PartialEq") +)] +pub struct InnerPathIter<'t, C> +where + C: Configuration + ?Sized, +{ + /// Inner Node Iterator + iter: InnerNodeIter, + + /// Inner Tree + inner_tree: &'t InnerTree<C>, +} + +impl<'t, C> Iterator for InnerPathIter<'t, C> +where + C: Configuration + ?Sized, +{ + type Item = &'t InnerDigest<C>; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + self.iter + .next() + .map(move |n| self.inner_tree.get(n.sibling())) + } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + self.iter.size_hint() + } +} diff --git a/manta-crypto/src/merkle_tree/mod.rs b/manta-crypto/src/merkle_tree/mod.rs new file mode 100644 index 000000000..f39deebc9 --- /dev/null +++ b/manta-crypto/src/merkle_tree/mod.rs @@ -0,0 +1,40 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Merkle Trees + +// TODO: Should `Leaf` move into `Tree`/`Configuration` since we might want the tree to have +// special kinds of leaf input (metadata along with just the digest)? +// TODO: Implement [`crate::VerifiedSet`] for [`MerkleTree`]? +// TODO: Maybe we should require `INNER_HEIGHT` instead of `HEIGHT` so that we don't have to rely +// on the user to check that `HEIGHT >= 2`. +// TODO: Look into optimizations related to default values. Ex: computing the default values once +// and caching them in the tree storage? + +mod node; +mod tree; + +pub mod fork; +pub mod full; +pub mod inner_tree; +pub mod single_leaf; + +#[cfg(feature = "test")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] +pub mod test; + +pub use node::*; +pub use tree::*; diff --git a/manta-crypto/src/merkle_tree/node.rs b/manta-crypto/src/merkle_tree/node.rs new file mode 100644 index 000000000..1ae0f24e0 --- /dev/null +++ b/manta-crypto/src/merkle_tree/node.rs @@ -0,0 +1,394 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Merkle Tree Node Abstractions + +use crate::merkle_tree::{Configuration, InnerDigest, InnerHash, LeafDigest, Parameters}; +use core::{ + iter::FusedIterator, + ops::{Add, Sub}, +}; + +/// Parity of a Subtree +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum Parity { + /// Left Side of the Subtree + Left, + + /// Right Side of the Subtree + Right, +} + +impl Parity { + /// Computes the [`Parity`] of the given `index`. + #[inline] + pub const fn from_index(index: usize) -> Self { + if index % 2 == 0 { + Self::Left + } else { + Self::Right + } + } + + /// Returns `true` if `self` represents the left side of a subtree. + #[inline] + pub const fn is_left(&self) -> bool { + matches!(self, Self::Left) + } + + /// Returns `true` if `self` represents the right side of a subtree. + #[inline] + pub const fn is_right(&self) -> bool { + matches!(self, Self::Right) + } + + /// Maps `self` to the output of `lhs` and `rhs` depending on its parity. + #[inline] + pub fn map<T, L, R>(self, lhs: L, rhs: R) -> T + where + L: FnOnce() -> T, + R: FnOnce() -> T, + { + match self { + Self::Left => lhs(), + Self::Right => rhs(), + } + } + + /// Returns the arguments in the order according to the parity of `self`. + #[inline] + pub const fn order<T>(&self, lhs: T, rhs: T) -> (T, T) { + match self { + Self::Left => (lhs, rhs), + Self::Right => (rhs, lhs), + } + } + + /// Returns the `center` placed in the pair at the location given by `self`, placing `lhs` and + /// `rhs` in the left or right empty slot of the pair respectively. + #[inline] + pub fn triple_order<T, L, R>(&self, center: T, lhs: L, rhs: R) -> (T, T) + where + L: FnOnce() -> T, + R: FnOnce() -> T, + { + match self { + Self::Left => (center, rhs()), + Self::Right => (lhs(), center), + } + } + + /// Combines two inner digests into a new inner digest using `parameters`, swapping the order + /// of `lhs` and `rhs` depending on the parity of `self` in its subtree. + #[inline] + pub fn join<C>( + &self, + parameters: &Parameters<C>, + lhs: &InnerDigest<C>, + rhs: &InnerDigest<C>, + ) -> InnerDigest<C> + where + C: Configuration + ?Sized, + { + let (lhs, rhs) = self.order(lhs, rhs); + C::InnerHash::join(&parameters.inner, lhs, rhs) + } + + /// Combines two leaf digests into a new inner digest using `parameters`, choosing the right + /// pair `(center, rhs)` if `self` has left parity or choosing the left pair `(lhs, center)` + /// if `self` has right parity. + #[inline] + pub fn join_opposite_pair<C>( + &self, + parameters: &Parameters<C>, + lhs: &InnerDigest<C>, + center: &InnerDigest<C>, + rhs: &InnerDigest<C>, + ) -> InnerDigest<C> + where + C: Configuration + ?Sized, + { + let (lhs, rhs) = self.triple_order(center, move || lhs, move || rhs); + C::InnerHash::join(&parameters.inner, lhs, rhs) + } + + /// Combines two leaf digests into a new inner digest using `parameters`, swapping the order + /// of `lhs` and `rhs` depending on the parity of `self` in its subtree. + #[inline] + pub fn join_leaves<C>( + &self, + parameters: &Parameters<C>, + lhs: &LeafDigest<C>, + rhs: &LeafDigest<C>, + ) -> InnerDigest<C> + where + C: Configuration + ?Sized, + { + let (lhs, rhs) = self.order(lhs, rhs); + C::InnerHash::join_leaves(&parameters.inner, lhs, rhs) + } +} + +impl Default for Parity { + #[inline] + fn default() -> Self { + Self::Left + } +} + +/// Node Index +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +pub struct Node<Idx = usize>( + /// Level-wise Index to a node in a Binary Tree + pub Idx, +); + +impl Node { + /// Returns the [`Parity`] of this node. + #[inline] + pub const fn parity(&self) -> Parity { + Parity::from_index(self.0) + } + + /// Returns `true` if this node has left parity. + #[inline] + pub const fn is_left(&self) -> bool { + self.parity().is_left() + } + + /// Returns `true` if this node has right parity. + #[inline] + pub const fn is_right(&self) -> bool { + self.parity().is_right() + } + + /// Returns the [`Node`] which is the sibling to `self`. + #[inline] + pub const fn sibling(&self) -> Self { + match self.parity() { + Parity::Left => Self(self.0 + 1), + Parity::Right => Self(self.0 - 1), + } + } + + /// Returns `self` with its sibling in parity order. + #[inline] + pub fn with_sibling(self) -> (Self, Self) { + self.parity() + .triple_order(self, move || self - 1, move || self + 1) + } + + /// Returns the left child [`Node`] of this node. + #[inline] + pub const fn left_child(&self) -> Self { + Self(self.0 << 1) + } + + /// Returns the right child [`Node`] fo this node. + #[inline] + pub const fn right_child(&self) -> Self { + Self(self.left_child().0 + 1) + } + + /// Returns the parent [`Node`] of this node. + #[inline] + pub const fn parent(&self) -> Self { + Self(self.0 >> 1) + } + + /// Converts `self` into its parent, returning the parent [`Node`]. + #[inline] + pub fn into_parent(&mut self) -> Self { + *self = self.parent(); + *self + } + + /// Returns an iterator over the parents of `self`. + #[inline] + pub const fn parents(&self) -> NodeParents { + NodeParents { index: *self } + } + + /// Combines two inner digests into a new inner digest using `parameters`, swapping the order + /// of `lhs` and `rhs` depending on the location of `self` in its subtree. + #[inline] + pub fn join<C>( + &self, + parameters: &Parameters<C>, + lhs: &InnerDigest<C>, + rhs: &InnerDigest<C>, + ) -> InnerDigest<C> + where + C: Configuration + ?Sized, + { + self.parity().join(parameters, lhs, rhs) + } + + /// Combines two leaf digests into a new inner digest using `parameters`, choosing the right + /// pair `(center, rhs)` if `self` has left parity or choosing the left pair `(lhs, center)` + /// if `self` has right parity. + #[inline] + pub fn join_opposite_pair<C>( + &self, + parameters: &Parameters<C>, + lhs: &InnerDigest<C>, + center: &InnerDigest<C>, + rhs: &InnerDigest<C>, + ) -> InnerDigest<C> + where + C: Configuration + ?Sized, + { + self.parity() + .join_opposite_pair(parameters, lhs, center, rhs) + } + + /// Combines two leaf digests into a new inner digest using `parameters`, swapping the order + /// of `lhs` and `rhs` depending on the location of `self` in its subtree. + #[inline] + pub fn join_leaves<C>( + &self, + parameters: &Parameters<C>, + lhs: &LeafDigest<C>, + rhs: &LeafDigest<C>, + ) -> InnerDigest<C> + where + C: Configuration + ?Sized, + { + self.parity().join_leaves(parameters, lhs, rhs) + } +} + +impl<Idx> Add<Idx> for Node<Idx> +where + Idx: Add<Output = Idx>, +{ + type Output = Self; + + #[inline] + fn add(self, rhs: Idx) -> Self::Output { + Self(self.0 + rhs) + } +} + +impl<'i, Idx> Add<&'i Idx> for &'i Node<Idx> +where + &'i Idx: Add<Output = Idx>, +{ + type Output = Node<Idx>; + + #[inline] + fn add(self, rhs: &'i Idx) -> Self::Output { + Node(&self.0 + rhs) + } +} + +impl<Idx> Sub<Idx> for Node<Idx> +where + Idx: Sub<Output = Idx>, +{ + type Output = Self; + + #[inline] + fn sub(self, rhs: Idx) -> Self::Output { + Self(self.0 - rhs) + } +} + +impl<'i, Idx> Sub<&'i Idx> for &'i Node<Idx> +where + &'i Idx: Sub<Output = Idx>, +{ + type Output = Node<Idx>; + + #[inline] + fn sub(self, rhs: &'i Idx) -> Self::Output { + Node(&self.0 - rhs) + } +} + +impl<Idx> From<Idx> for Node<Idx> { + #[inline] + fn from(index: Idx) -> Self { + Self(index) + } +} + +impl<Idx> PartialEq<Idx> for Node<Idx> +where + Idx: PartialEq, +{ + #[inline] + fn eq(&self, rhs: &Idx) -> bool { + self.0 == *rhs + } +} + +/// Node Parent Iterator +/// +/// An iterator over the parents of a [`Node`]. +/// +/// This `struct` is created by the [`parents`](Node::parents) method on [`Node`]. +/// See its documentation for more. +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +pub struct NodeParents { + /// Current Index + index: Node, +} + +impl NodeParents { + /// Stops the iterator and returns the current node index. + #[inline] + pub const fn stop(self) -> Node { + self.index + } + + /// Returns the sibling of the current parent node. + #[inline] + pub const fn sibling(&self) -> Node { + self.index.sibling() + } +} + +impl AsRef<Node> for NodeParents { + #[inline] + fn as_ref(&self) -> &Node { + &self.index + } +} + +// TODO: Add all methods which can be optimized. +impl Iterator for NodeParents { + type Item = Node; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + Some(self.index.into_parent()) + } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + (usize::MAX, None) + } + + #[inline] + fn last(self) -> Option<Self::Item> { + // NOTE: Although this iterator can never be completed, it has a well-defined + // final element "at infinity". + Some(Default::default()) + } +} + +impl FusedIterator for NodeParents {} diff --git a/manta-crypto/src/merkle_tree/single_leaf.rs b/manta-crypto/src/merkle_tree/single_leaf.rs new file mode 100644 index 000000000..b692db759 --- /dev/null +++ b/manta-crypto/src/merkle_tree/single_leaf.rs @@ -0,0 +1,220 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Single Leaf Merkle Tree Storage + +use crate::merkle_tree::{ + capacity, Configuration, InnerDigest, LeafDigest, MerkleTree, Node, Parameters, Parity, Path, + Root, Tree, +}; +use core::{convert::Infallible, fmt::Debug, hash::Hash}; + +/// Single Leaf Merkle Tree Type +pub type SingleLeafMerkleTree<C> = MerkleTree<C, SingleLeaf<C>>; + +/// Path Query Type +/// +/// Since the [`SingleLeaf`] tree only stores one leaf and one path, we can only query the +/// tree for the current path. +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Current; + +/// Single Leaf Merkle Tree Backing Structure +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), + Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), + Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default"), + Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), + Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), + PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") +)] +pub struct SingleLeaf<C> +where + C: Configuration + ?Sized, +{ + /// Leaf Digest + leaf_digest: Option<LeafDigest<C>>, + + /// Path + path: Path<C>, + + /// Root + root: Root<C>, +} + +impl<C> SingleLeaf<C> +where + C: Configuration + ?Sized, +{ + /// Returns the number of leaves in the merkle tree. + #[inline] + fn len(&self) -> usize { + if self.leaf_digest.is_none() { + 0 + } else { + self.path.leaf_index.0 + 1 + } + } + + /// Returns the next avaiable index or `None` if the merkle tree is full. + #[inline] + fn next_index(&self) -> Option<Node> { + let len = self.len(); + if len == 0 { + Some(Node(0)) + } else if len < capacity::<C>() - 1 { + Some(Node(len + 1)) + } else { + None + } + } + + /// Returns the current merkle tree root. + #[inline] + pub fn root(&self) -> &Root<C> { + &self.root + } + + /// Returns the current merkle tree path for the current leaf. + #[inline] + pub fn path(&self) -> &Path<C> { + &self.path + } + + /// Returns the currently stored leaf digest, returning `None` if the tree is empty. + #[inline] + pub fn leaf_digest(&self) -> Option<&LeafDigest<C>> { + self.leaf_digest.as_ref() + } +} + +impl<C> Tree<C> for SingleLeaf<C> +where + C: Configuration + ?Sized, + LeafDigest<C>: Clone, + InnerDigest<C>: Clone, +{ + type Query = Current; + + type Error = Infallible; + + #[inline] + fn new(parameters: &Parameters<C>) -> Self { + let _ = parameters; + Default::default() + } + + #[inline] + fn len(&self) -> usize { + self.len() + } + + #[inline] + fn root(&self, parameters: &Parameters<C>) -> Root<C> { + let _ = parameters; + self.root.clone() + } + + #[inline] + fn path(&self, parameters: &Parameters<C>, query: Self::Query) -> Result<Path<C>, Self::Error> { + let _ = (parameters, query); + Ok(self.path.clone()) + } + + #[inline] + fn maybe_push_digest<F>(&mut self, parameters: &Parameters<C>, leaf_digest: F) -> Option<bool> + where + F: FnOnce() -> Option<LeafDigest<C>>, + { + let index = match self.next_index() { + Some(index) => index, + _ => return Some(false), + }; + + let leaf_digest = leaf_digest()?; + + if index == 0 { + self.root = self.path.root_relative_to(parameters, &leaf_digest); + } else { + let mut next_index = index; + + let default_leaf_digest = Default::default(); + let default_inner_digest = Default::default(); + + let current_leaf_digest = self.leaf_digest.as_ref().unwrap(); + + let (mut accumulator, sibling_digest) = match next_index.parity() { + Parity::Left => ( + parameters.join_leaves(&leaf_digest, &default_leaf_digest), + default_leaf_digest, + ), + Parity::Right => ( + parameters.join_leaves(current_leaf_digest, &leaf_digest), + current_leaf_digest.clone(), + ), + }; + + let mut prev_index = next_index - 1; + let mut prev_digest = + prev_index.join_leaves(parameters, current_leaf_digest, &self.path.sibling_digest); + + // TODO: Mutate the path in place. + + let inner_path = self + .path + .inner_path + .iter() + .map(|digest| { + if prev_index.into_parent() == next_index.into_parent() { + accumulator = next_index.join_opposite_pair( + parameters, + digest, + &accumulator, + &default_inner_digest, + ); + digest.clone() + } else { + let next_index_parity = next_index.parity(); + + let next_inner_path_digest = next_index_parity + .map(|| default_inner_digest.clone(), || prev_digest.clone()); + + accumulator = next_index_parity.join_opposite_pair( + parameters, + &prev_digest, + &accumulator, + &default_inner_digest, + ); + + if prev_index.is_right() { + prev_digest = parameters.join(digest, &prev_digest); + } + + next_inner_path_digest + } + }) + .collect(); + + self.path = Path::new(index, sibling_digest, inner_path); + self.root = Root(accumulator); + } + + self.leaf_digest = Some(leaf_digest); + + Some(true) + } +} diff --git a/manta-crypto/src/merkle_tree/test.rs b/manta-crypto/src/merkle_tree/test.rs new file mode 100644 index 000000000..e9d7b7f88 --- /dev/null +++ b/manta-crypto/src/merkle_tree/test.rs @@ -0,0 +1,63 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Testing Framework + +use crate::merkle_tree::{Configuration, Leaf, MerkleTree, Parameters, Tree}; +use core::fmt::Debug; + +/// Tests that a tree constructed with `parameters` can accept at least two leaves without +/// failing. +#[inline] +pub fn push_twice_to_empty_tree_succeeds<C, T>( + parameters: Parameters<C>, + lhs: &Leaf<C>, + rhs: &Leaf<C>, +) -> Parameters<C> +where + C: Configuration + ?Sized, + T: Tree<C>, +{ + let mut tree = MerkleTree::<C, T>::new(parameters); + assert!( + tree.push(lhs), + "Trees always have a capacity of at least two." + ); + assert!( + tree.push(rhs), + "Trees always have a capacity of at least two." + ); + tree.into_parameters() +} + +/// Tests path construction by checking that the path generated by `query` on `tree` is a valid +/// [`Path`] for `leaf`. +/// +/// [`Path`]: crate::merkle_tree::Path +#[inline] +pub fn assert_valid_path<C, T>(tree: &MerkleTree<C, T>, query: T::Query, leaf: &Leaf<C>) +where + C: Configuration + ?Sized, + T: Tree<C>, + T::Error: Debug, +{ + assert!( + tree.path(query) + .expect("Only valid queries are accepted.") + .verify(&tree.parameters, &tree.root(), leaf), + "Path returned from tree was not valid." + ) +} diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs new file mode 100644 index 000000000..ee01905fe --- /dev/null +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -0,0 +1,600 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Merkle Tree Abstractions + +extern crate alloc; + +use crate::merkle_tree::{ + fork::{Delta, Fork}, + Node, +}; +use alloc::vec::Vec; +use core::{fmt::Debug, hash::Hash}; + +/// Merkle Tree Leaf Hash +pub trait LeafHash { + /// Leaf Type + type Leaf: ?Sized; + + /// Leaf Hash Parameters Type + type Parameters; + + /// Leaf Hash Output Type + type Output: Default; + + /// Computes the digest of the `leaf` using `parameters`. + fn digest(parameters: &Self::Parameters, leaf: &Self::Leaf) -> Self::Output; +} + +/// Merkle Tree Inner Hash +pub trait InnerHash { + /// Leaf Hash Type + type LeafHash: LeafHash; + + /// Inner Hash Parameters Type + type Parameters; + + /// Inner Hash Output Type + type Output: Default + PartialEq; + + /// Combines two inner digests into a new inner digest using `parameters`. + fn join(parameters: &Self::Parameters, lhs: &Self::Output, rhs: &Self::Output) -> Self::Output; + + /// Combines two [`LeafHash`](Self::LeafHash) digests into an inner digest. + fn join_leaves( + parameters: &Self::Parameters, + lhs: &<Self::LeafHash as LeafHash>::Output, + rhs: &<Self::LeafHash as LeafHash>::Output, + ) -> Self::Output; +} + +/// Merkle Tree Configuration +pub trait Configuration { + /// Leaf Hash Type + type LeafHash: LeafHash; + + /// Inner Hash Type + type InnerHash: InnerHash<LeafHash = Self::LeafHash>; + + /// Height Type + type Height: Copy + Into<usize>; + + /// Fixed Height of the Merkle Tree + /// + /// # Contract + /// + /// Trees must always have height at least `2`. + const HEIGHT: Self::Height; +} + +/// Leaf Type +pub type Leaf<C> = <<C as Configuration>::LeafHash as LeafHash>::Leaf; + +/// Leaf Hash Parameters Type +pub type LeafHashParamters<C> = <<C as Configuration>::LeafHash as LeafHash>::Parameters; + +/// Leaf Hash Digest Type +pub type LeafDigest<C> = <<C as Configuration>::LeafHash as LeafHash>::Output; + +/// Inner Hash Parameters Type +pub type InnerHashParameters<C> = <<C as Configuration>::InnerHash as InnerHash>::Parameters; + +/// Inner Hash Digest Type +pub type InnerDigest<C> = <<C as Configuration>::InnerHash as InnerHash>::Output; + +/// Returns the capacity of the merkle tree with the given [`C::HEIGHT`](Configuration::HEIGHT) +/// parameter. +#[inline] +pub fn capacity<C>() -> usize +where + C: Configuration + ?Sized, +{ + 1usize << (C::HEIGHT.into() - 1) +} + +/// Returns the path length of the merkle tree with the given [`C::HEIGHT`](Configuration::HEIGHT) +/// parameter. +#[inline] +pub fn path_length<C>() -> usize +where + C: Configuration + ?Sized, +{ + C::HEIGHT.into() - 2 +} + +/// Merkle Tree Structure +pub trait Tree<C>: Sized +where + C: Configuration + ?Sized, +{ + /// Path Query Type + type Query; + + /// Path Error Type + type Error; + + /// Builds a new merkle tree. + fn new(parameters: &Parameters<C>) -> Self; + + /// Builds a new merkle tree with the given `leaves` returning `None` if the iterator + /// would overflow the capacity of the tree. + #[inline] + fn from_iter<'l, L>(parameters: &Parameters<C>, leaves: L) -> Option<Self> + where + Leaf<C>: 'l, + L: IntoIterator<Item = &'l Leaf<C>>, + { + let mut tree = Self::new(parameters); + tree.extend(parameters, leaves).then(|| tree) + } + + /// Builds a new merkle tree with the given `leaves` returning `None` if the slice + /// would overflow the capacity of the tree. + #[inline] + fn from_slice(parameters: &Parameters<C>, slice: &[Leaf<C>]) -> Option<Self> + where + Leaf<C>: Sized, + { + Self::from_iter(parameters, slice) + } + + /// Returns the length of `self`. + fn len(&self) -> usize; + + /// Returns `true` if the length of `self` is zero. + #[inline] + fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Returns the [`Root`] of the merkle tree. + fn root(&self, parameters: &Parameters<C>) -> Root<C>; + + /// Returns the [`Path`] to some element of the merkle tree given by the `query`. + fn path(&self, parameters: &Parameters<C>, query: Self::Query) -> Result<Path<C>, Self::Error>; + + /// Checks if a leaf can be inserted into the tree and if it can, it runs `leaf_digest` to + /// extract a leaf digest to insert, returning `None` if there was no leaf digest. + fn maybe_push_digest<F>(&mut self, parameters: &Parameters<C>, leaf_digest: F) -> Option<bool> + where + F: FnOnce() -> Option<LeafDigest<C>>; + + /// Inserts the `leaf_digest` at the next available leaf node of the tree, returning `false` + /// if the leaf could not be inserted because the tree has exhausted its capacity. + #[inline] + fn push_digest<F>(&mut self, parameters: &Parameters<C>, leaf_digest: F) -> bool + where + F: FnOnce() -> LeafDigest<C>, + { + match self.maybe_push_digest(parameters, || Some(leaf_digest())) { + Some(result) => result, + _ => unreachable!(), + } + } + + /// Inserts the digest of `leaf` at the next available leaf node of the tree, returning + /// `false` if the leaf could not be inserted because the tree has exhausted its capacity. + #[inline] + fn push(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> bool { + self.push_digest(parameters, || parameters.digest(leaf)) + } + + /// Appends an iterator of leaf digests at the end of the tree, returning the iterator back + /// if it could not be inserted because the tree has exhausted its capacity. + /// + /// # Implementation Note + /// + /// This operation is meant to be atomic, so if appending the iterator should fail, the + /// implementation must ensure that the tree returns to the same state before the insertion + /// occured. + #[inline] + fn extend_digests<L>( + &mut self, + parameters: &Parameters<C>, + leaf_digests: L, + ) -> Result<(), L::IntoIter> + where + L: IntoIterator<Item = LeafDigest<C>>, + { + let mut leaf_digests = leaf_digests.into_iter(); + if matches!(leaf_digests.size_hint().1, Some(max) if max <= capacity::<C>() - self.len()) { + loop { + match self.maybe_push_digest(parameters, || leaf_digests.next()) { + Some(result) => debug_assert!(result), + _ => return Ok(()), + } + } + } + Err(leaf_digests) + } + + /// Appends an iterator of leaves at the end of the tree, returning `false` if the `leaves` + /// could not be inserted because the tree has exhausted its capacity. + /// + /// # Implementation Note + /// + /// This operation is meant to be atomic, so if appending the iterator should fail, the + /// implementation must ensure that the tree returns to the same state before the insertion + /// occured. + #[inline] + fn extend<'l, L>(&mut self, parameters: &Parameters<C>, leaves: L) -> bool + where + Leaf<C>: 'l, + L: IntoIterator<Item = &'l Leaf<C>>, + { + self.extend_digests(parameters, leaves.into_iter().map(|l| parameters.digest(l))) + .is_ok() + } + + /// Appends a slice of leaves at the end of the tree, returning `false` if the + /// `leaves` could not be inserted because the tree has exhausted its capacity. + /// + /// # Implementation Note + /// + /// This operation is meant to be atomic, so if appending the slice should fail, the + /// implementation must ensure that the tree returns to the same state before the insertion + /// occured. + #[inline] + fn extend_slice(&mut self, parameters: &Parameters<C>, leaves: &[Leaf<C>]) -> bool + where + Leaf<C>: Sized, + { + self.extend(parameters, leaves) + } + + /// Tries to merge the `delta` into the tree, returning it back if it would exceed the capacity + /// of the tree. + #[inline] + fn merge_delta(&mut self, parameters: &Parameters<C>, delta: Delta<C>) -> Result<(), Delta<C>> { + let Delta { + leaf_digests, + inner_digests, + } = delta; + match self.extend_digests(parameters, leaf_digests) { + Err(leaf_digests) => Err(Delta::new(leaf_digests.collect(), inner_digests)), + _ => Ok(()), + } + } +} + +/// Merkle Tree Parameters +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "LeafHashParamters<C>: Clone, InnerHashParameters<C>: Clone"), + Copy(bound = "LeafHashParamters<C>: Copy, InnerHashParameters<C>: Copy"), + Debug(bound = "LeafHashParamters<C>: Debug, InnerHashParameters<C>: Debug"), + Default(bound = "LeafHashParamters<C>: Default, InnerHashParameters<C>: Default"), + Eq(bound = "LeafHashParamters<C>: Eq, InnerHashParameters<C>: Eq"), + Hash(bound = "LeafHashParamters<C>: Hash, InnerHashParameters<C>: Hash"), + PartialEq(bound = "LeafHashParamters<C>: PartialEq, InnerHashParameters<C>: PartialEq") +)] +pub struct Parameters<C> +where + C: Configuration + ?Sized, +{ + /// Leaf Hash Parameters + pub leaf: LeafHashParamters<C>, + + /// Inner Hash Parameters + pub inner: InnerHashParameters<C>, +} + +impl<C> Parameters<C> +where + C: Configuration + ?Sized, +{ + /// Builds a new [`Parameters`] from `leaf` and `inner` parameters. + #[inline] + pub fn new(leaf: LeafHashParamters<C>, inner: InnerHashParameters<C>) -> Self { + Self { leaf, inner } + } + + /// Computes the leaf digest of `leaf` using `self`. + #[inline] + pub fn digest(&self, leaf: &Leaf<C>) -> LeafDigest<C> { + C::LeafHash::digest(&self.leaf, leaf) + } + + /// Combines two inner digests into a new inner digest using `self`. + #[inline] + pub fn join(&self, lhs: &InnerDigest<C>, rhs: &InnerDigest<C>) -> InnerDigest<C> { + C::InnerHash::join(&self.inner, lhs, rhs) + } + + /// Combines two leaf digests into a new inner digest using `self`. + #[inline] + pub fn join_leaves(&self, lhs: &LeafDigest<C>, rhs: &LeafDigest<C>) -> InnerDigest<C> { + C::InnerHash::join_leaves(&self.inner, lhs, rhs) + } + + /// Verify that `path` witnesses the fact that `leaf` is a member of a merkle tree with the + /// given `root`. + #[inline] + pub fn verify_path(&self, path: &Path<C>, root: &Root<C>, leaf: &Leaf<C>) -> bool { + path.verify(self, root, leaf) + } +} + +/// Merkle Tree Root Wrapper Type +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "InnerDigest<C>: Clone"), + Copy(bound = "InnerDigest<C>: Copy"), + Debug(bound = "InnerDigest<C>: Debug"), + Default(bound = "InnerDigest<C>: Default"), + Eq(bound = "InnerDigest<C>: Eq"), + Hash(bound = "InnerDigest<C>: Hash"), + PartialEq(bound = "InnerDigest<C>: PartialEq") +)] +pub struct Root<C>( + /// Root Inner Digest + pub InnerDigest<C>, +) +where + C: Configuration + ?Sized; + +impl<C> AsRef<InnerDigest<C>> for Root<C> +where + C: Configuration + ?Sized, +{ + #[inline] + fn as_ref(&self) -> &InnerDigest<C> { + &self.0 + } +} + +/// Merkle Tree Path +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), + Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), + Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), + Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), + PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") +)] +pub struct Path<C> +where + C: Configuration + ?Sized, +{ + /// Leaf Index + pub leaf_index: Node, + + /// Sibling Digest + pub sibling_digest: LeafDigest<C>, + + /// Inner Path + /// + /// Inner digests are stored from leaf to root, not including the root. + pub inner_path: Vec<InnerDigest<C>>, +} + +impl<C> Path<C> +where + C: Configuration + ?Sized, +{ + /// Builds a new [`Path`] from `leaf_index`, `sibling_digest`, and `inner_path`. + #[inline] + pub fn new( + leaf_index: Node, + sibling_digest: LeafDigest<C>, + inner_path: Vec<InnerDigest<C>>, + ) -> Self { + Self { + leaf_index, + sibling_digest, + inner_path, + } + } + + /// Computes the root of the merkle tree relative to `leaf_digest` using `parameters`. + #[inline] + pub fn root_relative_to( + &self, + parameters: &Parameters<C>, + leaf_digest: &LeafDigest<C>, + ) -> Root<C> { + let mut index = self.leaf_index; + Root(self.inner_path.iter().fold( + index.join_leaves(parameters, leaf_digest, &self.sibling_digest), + move |acc, d| index.into_parent().join(parameters, &acc, d), + )) + } + + /// Returns `true` if `self` is a witness to the fact that `leaf_digest` is stored in a + /// merkle tree with the given `root`. + #[inline] + pub fn verify_digest( + &self, + parameters: &Parameters<C>, + root: &Root<C>, + leaf_digest: &LeafDigest<C>, + ) -> bool { + root == &self.root_relative_to(parameters, leaf_digest) + } + + /// Returns `true` if `self` is a witness to the fact that `leaf` is stored in a merkle tree + /// with the given `root`. + #[inline] + pub fn verify(&self, parameters: &Parameters<C>, root: &Root<C>, leaf: &Leaf<C>) -> bool { + self.verify_digest(parameters, root, &parameters.digest(leaf)) + } +} + +impl<C> Default for Path<C> +where + C: Configuration + ?Sized, +{ + #[inline] + fn default() -> Self { + let path_length = path_length::<C>(); + let mut inner_path = Vec::with_capacity(path_length); + inner_path.resize_with(path_length, InnerDigest::<C>::default); + Self::new(Default::default(), Default::default(), inner_path) + } +} + +/// Merkle Tree +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "Parameters<C>: Clone, T: Clone"), + Copy(bound = "Parameters<C>: Copy, T: Copy"), + Debug(bound = "Parameters<C>: Debug, T: Debug"), + Default(bound = "Parameters<C>: Default, T: Default"), + Eq(bound = "Parameters<C>: Eq, T: Eq"), + Hash(bound = "Parameters<C>: Hash, T: Hash"), + PartialEq(bound = "Parameters<C>: PartialEq, T: PartialEq") +)] +pub struct MerkleTree<C, T> +where + C: Configuration + ?Sized, + T: Tree<C>, +{ + /// Underlying Tree Structure + pub(super) tree: T, + + /// Merkle Tree Parameters + pub(super) parameters: Parameters<C>, +} + +impl<C, T> MerkleTree<C, T> +where + C: Configuration + ?Sized, + T: Tree<C>, +{ + /// Builds a new [`MerkleTree`]. + #[inline] + pub fn new(parameters: Parameters<C>) -> Self { + Self::from_tree(T::new(&parameters), parameters) + } + + /// Builds a new [`MerkleTree`] with the given `leaves`. + #[inline] + pub fn from_iter<'l, L>(parameters: Parameters<C>, leaves: L) -> Option<Self> + where + Leaf<C>: 'l, + L: IntoIterator<Item = &'l Leaf<C>>, + { + Some(Self::from_tree( + T::from_iter(&parameters, leaves)?, + parameters, + )) + } + + /// Builds a new [`MerkleTree`] with the given `leaves`. + #[inline] + pub fn from_slice(parameters: Parameters<C>, leaves: &[Leaf<C>]) -> Option<Self> + where + Leaf<C>: Sized, + { + Some(Self::from_tree( + T::from_slice(&parameters, leaves)?, + parameters, + )) + } + + /// Builds a new [`MerkleTree`] from a pre-constructed `tree` and `parameters`. + #[inline] + pub fn from_tree(tree: T, parameters: Parameters<C>) -> Self { + Self { tree, parameters } + } + + /// Returns a reference to the parameters used by this merkle tree. + #[inline] + pub fn parameters(&self) -> &Parameters<C> { + &self.parameters + } + + /// Returns the [`Root`] of the merkle tree. + #[inline] + pub fn root(&self) -> Root<C> { + self.tree.root(&self.parameters) + } + + /// Returns the [`Path`] to some element of the merkle tree given by the `query`. + #[inline] + pub fn path(&self, query: T::Query) -> Result<Path<C>, T::Error> { + self.tree.path(&self.parameters, query) + } + + /// Inserts `leaf` at the next avaiable leaf node of the tree, returning `false` if the + /// leaf could not be inserted because the tree has exhausted its capacity. + #[inline] + pub fn push(&mut self, leaf: &Leaf<C>) -> bool { + self.tree.push(&self.parameters, leaf) + } + + /// Appends an iterator of leaves at the end of the tree, returning `false` if the `leaves` + /// could not be inserted because the tree has exhausted its capacity. + #[inline] + pub fn extend<'l, L>(&mut self, leaves: L) -> bool + where + Leaf<C>: 'l, + L: IntoIterator<Item = &'l Leaf<C>>, + { + self.tree.extend(&self.parameters, leaves) + } + + /// Appends a slice of leaves at the end of the tree, returning `false` if the `leaves` could + /// not be inserted because the tree has exhausted its capacity. + #[inline] + pub fn extend_slice(&mut self, leaves: &[Leaf<C>]) -> bool + where + Leaf<C>: Sized, + { + self.tree.extend_slice(&self.parameters, leaves) + } + + /// Forks the merkle tree. + #[inline] + pub fn fork(&self) -> Fork<C, T> { + Fork::new(self) + } + + /// Merges the `fork` into `self`. + #[inline] + pub fn merge(&mut self, fork: Fork<C, T>) { + debug_assert!(self.tree.merge_delta(&self.parameters, fork.delta).is_ok()) + } + + /// Extracts the parameters of the merkle tree, dropping the internal tree. + #[inline] + pub fn into_parameters(self) -> Parameters<C> { + self.parameters + } +} + +impl<C, T> AsRef<T> for MerkleTree<C, T> +where + C: Configuration + ?Sized, + T: Tree<C>, +{ + #[inline] + fn as_ref(&self) -> &T { + &self.tree + } +} + +impl<C, T> AsMut<T> for MerkleTree<C, T> +where + C: Configuration + ?Sized, + T: Tree<C>, +{ + #[inline] + fn as_mut(&mut self) -> &mut T { + &mut self.tree + } +} From 0eb70c03421608f3d6781e99017a4d464f4b5a68 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 26 Sep 2021 01:59:50 -0400 Subject: [PATCH 060/275] WIP: start working on merkle tree forking mechanism --- manta-accounting/src/transfer.rs | 10 +- manta-crypto/Cargo.toml | 3 + manta-crypto/src/lib.rs | 2 +- manta-crypto/src/merkle_tree/fork.rs | 82 +++++-- manta-crypto/src/merkle_tree/full.rs | 59 +++--- manta-crypto/src/merkle_tree/inner_tree.rs | 224 ++++++++++++++------ manta-crypto/src/merkle_tree/node.rs | 13 +- manta-crypto/src/merkle_tree/single_leaf.rs | 19 +- manta-crypto/src/merkle_tree/test.rs | 12 +- manta-crypto/src/merkle_tree/tree.rs | 81 +++++-- manta-pay/src/accounting/keys.rs | 9 +- manta-pay/src/accounting/ledger.rs | 4 +- manta-util/src/lib.rs | 1 + manta-util/src/sealed.rs | 37 ++++ 14 files changed, 386 insertions(+), 170 deletions(-) create mode 100644 manta-util/src/sealed.rs diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 97781872e..46cc026da 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -40,7 +40,7 @@ use manta_crypto::{ ies::{EncryptedMessage, IntegratedEncryptionScheme}, set::{constraint::VerifiedSetVariable, VerifiedSet}, }; -use manta_util::{mixed_chain, Either}; +use manta_util::{create_seal, mixed_chain, seal, Either}; use rand::{ distributions::{Distribution, Standard}, CryptoRng, RngCore, @@ -1134,11 +1134,7 @@ where } } -/// Sealed Trait Module -mod sealed { - /// Sealed Trait - pub trait Sealed {} -} +create_seal! {} /// Transfer Shapes /// @@ -1167,7 +1163,7 @@ pub mod canonical { /// Implements [`Shape`] for a given shape type. macro_rules! impl_shape { ($shape:tt, $sources:expr, $senders:expr, $receivers:expr, $sinks:expr) => { - impl sealed::Sealed for $shape {} + seal!($shape); impl Shape for $shape { const SOURCES: usize = $sources; const SENDERS: usize = $senders; diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index a8ac4fe60..a82d59a2a 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -28,6 +28,9 @@ maintenance = { status = "actively-developed" } # Constraint System Gadgets # TODO: constraints = [] +# Standard Library +std = [] + # Testing Frameworks test = [] diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index e438792de..3ec022eab 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -16,7 +16,7 @@ //! Cryptographic Primitives Library -#![no_std] +#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(doc_cfg, feature(doc_cfg))] #![forbid(rustdoc::broken_intra_doc_links)] #![forbid(missing_docs)] diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs index 7462a6628..672cbbbee 100644 --- a/manta-crypto/src/merkle_tree/fork.rs +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -19,12 +19,34 @@ extern crate alloc; use crate::merkle_tree::{ - capacity, inner_tree::InnerTree, Configuration, InnerDigest, Leaf, LeafDigest, MerkleTree, - Parameters, Path, Root, Tree, + capacity, inner_tree::InnerTree, Configuration, GetPath, GetPathError, InnerDigest, Leaf, + LeafDigest, MerkleTree, Parameters, Path, Root, Tree, }; use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; +/// Fork Path Error +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "GetPathError<C, T>: Clone"), + Copy(bound = "GetPathError<C, T>: Copy"), + Debug(bound = "GetPathError<C, T>: Debug"), + Eq(bound = "GetPathError<C, T>: Eq"), + Hash(bound = "GetPathError<C, T>: Hash"), + PartialEq(bound = "GetPathError<C, T>: PartialEq") +)] +pub enum ForkGetPathError<C, T> +where + C: Configuration + ?Sized, + T: GetPath<C>, +{ + /// Trunk Path Query Error + TrunkError(GetPathError<C, T>), + + /// Unknown Index on Branch Error + UnknownIndexOnBranch, +} + /// Merkle Tree Delta #[derive(derivative::Derivative)] #[derivative( @@ -82,15 +104,29 @@ where /// #[inline] - fn path<T>( + fn current_path<T>(&self, parameters: &Parameters<C>, trunk: &T) -> Path<C> + where + T: Tree<C>, + { + todo!() + } + + /// + #[inline] + fn forward_path<T>( &self, parameters: &Parameters<C>, trunk: &T, - query: ForkQuery<C, T>, - ) -> Result<Path<C>, T::Error> + index: usize, + ) -> Option<Path<C>> where T: Tree<C>, { + let trunk_len = trunk.len(); + let total_len = trunk_len + self.len(); + if !(trunk_len..total_len).contains(&index) { + return None; + } todo!() } @@ -153,6 +189,18 @@ where self.trunk.parameters() } + /// + #[inline] + pub fn len(&self) -> usize { + self.trunk.len() + self.delta.len() + } + + /// + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + /// #[inline] pub fn root(&self) -> Root<C> { @@ -161,9 +209,16 @@ where /// #[inline] - pub fn path(&self, query: ForkQuery<C, T>) -> Result<Path<C>, T::Error> { + pub fn current_path(&self) -> Path<C> { + self.delta + .current_path(&self.trunk.parameters, &self.trunk.tree) + } + + /// + #[inline] + pub fn forward_path(&self, index: usize) -> Option<Path<C>> { self.delta - .path(&self.trunk.parameters, &self.trunk.tree, query) + .forward_path(&self.trunk.parameters, &self.trunk.tree, index) } /// @@ -173,16 +228,3 @@ where .push(&self.trunk.parameters, &self.trunk.tree, leaf) } } - -/// Fork Query Type -pub enum ForkQuery<C, T> -where - C: Configuration + ?Sized, - T: Tree<C>, -{ - /// Trunk Query - Trunk(T::Query), - - /// Branch Query - Branch(usize), -} diff --git a/manta-crypto/src/merkle_tree/full.rs b/manta-crypto/src/merkle_tree/full.rs index 187828307..0a484c8d6 100644 --- a/manta-crypto/src/merkle_tree/full.rs +++ b/manta-crypto/src/merkle_tree/full.rs @@ -19,8 +19,8 @@ extern crate alloc; use crate::merkle_tree::{ - capacity, inner_tree::InnerTree, Configuration, InnerDigest, LeafDigest, MerkleTree, Node, - Parameters, Path, Root, Tree, + capacity, inner_tree::InnerTree, Configuration, GetPath, InnerDigest, LeafDigest, MerkleTree, + Node, Parameters, Path, Root, Tree, }; use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; @@ -28,12 +28,6 @@ use core::{fmt::Debug, hash::Hash}; /// Full Merkle Tree Type pub type FullMerkleTree<C> = MerkleTree<C, Full<C>>; -/// Path Query Error Type -/// -/// Querying for paths beyond the current length of a [`Full`] tree is an error. -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Unknown; - /// Full Merkle Tree Backing Structure #[derive(derivative::Derivative)] #[derivative( @@ -68,7 +62,7 @@ where /// Returns a reference to the root inner digest. #[inline] pub fn root(&self) -> &InnerDigest<C> { - self.inner_digests.previous_root() + self.inner_digests.root() } /// Returns the sibling leaf node to `index`. @@ -106,10 +100,6 @@ where LeafDigest<C>: Clone, InnerDigest<C>: Clone, { - type Query = usize; - - type Error = Unknown; - #[inline] fn new(parameters: &Parameters<C>) -> Self { let _ = parameters; @@ -128,19 +118,8 @@ where } #[inline] - fn path(&self, parameters: &Parameters<C>, query: Self::Query) -> Result<Path<C>, Self::Error> { - let _ = parameters; - if query > capacity::<C>() { - return Err(Unknown); - } - let leaf_index = Node(query); - Ok(Path::new( - leaf_index, - self.get_leaf_sibling(leaf_index) - .map(Clone::clone) - .unwrap_or_default(), - self.inner_digests.inner_path_for_leaf(leaf_index), - )) + fn current_path(&self, parameters: &Parameters<C>) -> Path<C> { + self.path(parameters, 0).unwrap() } #[inline] @@ -156,3 +135,31 @@ where Some(true) } } + +impl<C> GetPath<C> for Full<C> +where + C: Configuration + ?Sized, + LeafDigest<C>: Clone, + InnerDigest<C>: Clone, +{ + type Error = (); + + #[inline] + fn path(&self, parameters: &Parameters<C>, index: usize) -> Result<Path<C>, Self::Error> { + let _ = parameters; + if index > 0 && index >= self.len() { + return Err(()); + } + let leaf_index = Node(index); + Ok(Path::new( + leaf_index, + self.get_leaf_sibling(leaf_index) + .map(Clone::clone) + .unwrap_or_default(), + self.inner_digests + .inner_path_for_leaf(leaf_index) + .cloned() + .collect(), + )) + } +} diff --git a/manta-crypto/src/merkle_tree/inner_tree.rs b/manta-crypto/src/merkle_tree/inner_tree.rs index 46d705a3b..05fb3b481 100644 --- a/manta-crypto/src/merkle_tree/inner_tree.rs +++ b/manta-crypto/src/merkle_tree/inner_tree.rs @@ -19,8 +19,11 @@ extern crate alloc; use crate::merkle_tree::{path_length, Configuration, InnerDigest, Node, Parameters, Parity}; -use alloc::{collections::BTreeMap, vec::Vec}; -use core::{fmt::Debug, hash::Hash, iter::FusedIterator}; +use alloc::collections::btree_map; +use core::{fmt::Debug, hash::Hash, iter::FusedIterator, ops::Index}; + +#[cfg(feature = "std")] +use std::{collections::hash_map, hash::BuildHasher}; /// Inner Tree Node #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] @@ -147,6 +150,12 @@ impl InnerNodeIter { { Self::new(InnerNode::from_leaf::<C>(leaf_index)) } + + /// Returns `true` if the iterator has completed. + #[inline] + pub const fn is_done(&self) -> bool { + self.node.is_none() + } } // TODO: Add all methods which can be optimized. @@ -171,42 +180,93 @@ impl ExactSizeIterator for InnerNodeIter {} impl FusedIterator for InnerNodeIter {} +/// [`InnerTree`] Map Backend +pub trait InnerMap<C>: Default +where + C: Configuration + ?Sized, +{ + /// Returns the inner digest stored at `index`. + fn get(&self, index: usize) -> Option<&InnerDigest<C>>; + + /// Sets the inner digest at `index` to `inner_digest`. + fn set(&mut self, index: usize, inner_digest: InnerDigest<C>); +} + +/// B-Tree Map [`InnerTree`] Backend +pub type BTreeMap<C> = btree_map::BTreeMap<usize, InnerDigest<C>>; + +impl<C> InnerMap<C> for BTreeMap<C> +where + C: Configuration + ?Sized, +{ + #[inline] + fn get(&self, index: usize) -> Option<&InnerDigest<C>> { + self.get(&index) + } + + #[inline] + fn set(&mut self, index: usize, inner_digest: InnerDigest<C>) { + self.insert(index, inner_digest); + } +} + +/// Hash Map [`InnerTree`] Backend +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +pub type HashMap<C, S = hash_map::RandomState> = hash_map::HashMap<usize, InnerDigest<C>, S>; + +#[cfg(feature = "std")] +impl<C, S> InnerMap<C> for HashMap<C, S> +where + C: Configuration + ?Sized, + S: Default + BuildHasher, +{ + #[inline] + fn get(&self, index: usize) -> Option<&InnerDigest<C>> { + self.get(&index) + } + + #[inline] + fn set(&mut self, index: usize, inner_digest: InnerDigest<C>) { + self.insert(index, inner_digest); + } +} + /// Inner Tree /// /// Tree data-structure for storing the inner digests of a merkle tree. -/// -/// # Coordinates -/// -/// Locations in the tree are indexed by two coordinates `depth` and `index`. The `depth` of a -/// node is given by its layer in the tree starting from `depth := -1` at the root increasing -/// downwards towards the leaves. The `index` of a node is its position from left to right along a -/// layer in the tree. #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "InnerDigest<C>: Clone"), - Debug(bound = "InnerDigest<C>: Debug"), - Default(bound = "InnerDigest<C>: Default"), - Eq(bound = "InnerDigest<C>: Eq"), - Hash(bound = "InnerDigest<C>: Hash"), - PartialEq(bound = "InnerDigest<C>: PartialEq") + Clone(bound = "InnerDigest<C>: Clone, M: Clone"), + Debug(bound = "InnerDigest<C>: Debug, M: Debug"), + Default(bound = ""), + Eq(bound = "InnerDigest<C>: Eq, M: Eq"), + Hash(bound = "InnerDigest<C>: Hash, M: Hash"), + PartialEq(bound = "InnerDigest<C>: PartialEq, M: PartialEq") )] -pub struct InnerTree<C> +pub struct InnerTree<C, M = BTreeMap<C>> where C: Configuration + ?Sized, + M: InnerMap<C>, { /// Inner Digest Map /// - /// See [`inner_digest_index`](Self::inner_digest_index) for the definition of the tree - /// coordinate system. - map: BTreeMap<usize, InnerDigest<C>>, + /// # Coordinates + /// + /// Locations in the tree are indexed by two coordinates `depth` and `index`. The `depth` of a + /// node is given by its layer in the tree starting from `depth := -1` at the root increasing + /// downwards towards the leaves. The `index` of a node is its position from left to right + /// along a layer in the tree. See [`InnerNode`] for more details. + map: M, /// Inner Digest Default Value default: InnerDigest<C>, } -impl<C> InnerTree<C> +impl<C, M> InnerTree<C, M> where C: Configuration + ?Sized, + M: InnerMap<C>, { /// Builds a new [`InnerTree`]. #[inline] @@ -214,43 +274,42 @@ where Default::default() } + /// Returns the inner digest at `node` or the default value if the inner digest is missing. + #[inline] + fn map_get(&self, index: usize) -> &InnerDigest<C> { + self.map.get(index).unwrap_or(&self.default) + } + /// Returns a reference to the root inner digest. #[inline] - pub fn previous_root(&self) -> &InnerDigest<C> { - self.map.get(&0).unwrap_or(&self.default) + pub fn root(&self) -> &InnerDigest<C> { + self.map_get(0) } /// Returns the inner digest at `node` or the default value if the inner digest is missing. #[inline] pub fn get(&self, node: InnerNode) -> &InnerDigest<C> { - self.map.get(&node.map_index()).unwrap_or(&self.default) + self.map_get(node.map_index()) } - /// Sets the new `inner_digest` at `(depth, index)` in the tree, and returns back a - /// reference to `inner_digest` and its sibling in the tree in parity order. + /// Inserts the new `inner_digest` at `node` in the tree, and returns a reference to + /// `inner_digest` and its sibling in the tree in parity order. #[inline] - fn set_and_get_inner_pair( + fn insert_and_get_pair( &mut self, node: InnerNode, inner_digest: InnerDigest<C>, ) -> (&InnerDigest<C>, &InnerDigest<C>) { - // TODO: Optimize this so we can remove the extra add and `unwrap_or`. - let depth_starting_index = node.depth_starting_index(); - self.map - .insert(depth_starting_index + node.index.0, inner_digest); - let (lhs_index, rhs_index) = node.index.with_sibling(); - ( - self.map - .get(&(depth_starting_index + lhs_index.0)) - .unwrap_or(&self.default), - self.map - .get(&(depth_starting_index + rhs_index.0)) - .unwrap_or(&self.default), - ) + let index = node.map_index(); + self.map.set(index, inner_digest); + match node.parity() { + Parity::Left => (self.map_get(index), self.map_get(index + 1)), + Parity::Right => (self.map_get(index - 1), self.map_get(index)), + } } /// Inserts `inner_digest` into the tree at `node` and computes the join of `inner_digest` - /// and its sibling in the tree, using the `default` value if it's sibling is not stored in + /// and its sibling in the tree, using the default value if its sibling is not stored in /// the tree. #[inline] fn insert_and_join( @@ -259,7 +318,7 @@ where node: InnerNode, inner_digest: InnerDigest<C>, ) -> InnerDigest<C> { - let (lhs, rhs) = self.set_and_get_inner_pair(node, inner_digest); + let (lhs, rhs) = self.insert_and_get_pair(node, inner_digest); parameters.join(lhs, rhs) } @@ -280,31 +339,33 @@ where /// Inserts the `base` inner digest corresponding to the leaf at `leaf_index` into the tree. #[inline] pub fn insert(&mut self, parameters: &Parameters<C>, leaf_index: Node, base: InnerDigest<C>) { - // TODO: Implement random insertion, not just for leaves. let root = self.compute_root(parameters, leaf_index, base); - self.map.insert(0, root); + self.map.set(0, root); } /// Computes the inner path starting from `node`. #[inline] - pub fn inner_path(&self, node: InnerNode) -> Vec<InnerDigest<C>> - where - InnerDigest<C>: Clone, - { - node.iter() - .map(move |node| self.get(node.sibling()).clone()) - .collect() + pub fn inner_path(&self, node: InnerNode) -> InnerPathIter<C, M> { + InnerPathIter::new(self, node.iter()) } /// Computes the inner path of the leaf given by `leaf_index`. #[inline] - pub fn inner_path_for_leaf(&self, leaf_index: Node) -> Vec<InnerDigest<C>> - where - InnerDigest<C>: Clone, - { - InnerNodeIter::from_leaf::<C>(leaf_index) - .map(move |node| self.get(node.sibling()).clone()) - .collect() + pub fn inner_path_for_leaf(&self, leaf_index: Node) -> InnerPathIter<C, M> { + InnerPathIter::new(self, InnerNodeIter::from_leaf::<C>(leaf_index)) + } +} + +impl<C, M> Index<InnerNode> for InnerTree<C, M> +where + C: Configuration + ?Sized, + M: InnerMap<C>, +{ + type Output = InnerDigest<C>; + + #[inline] + fn index(&self, index: InnerNode) -> &Self::Output { + self.get(index) } } @@ -313,33 +374,46 @@ where #[derivative( Copy, Clone, - Debug(bound = "InnerDigest<C>: Debug"), - Eq(bound = "InnerDigest<C>: Eq"), - Hash(bound = "InnerDigest<C>: Hash"), - PartialEq(bound = "InnerDigest<C>: PartialEq") + Debug(bound = "InnerDigest<C>: Debug, M: Debug"), + Eq(bound = "InnerDigest<C>: Eq, M: Eq"), + Hash(bound = "InnerDigest<C>: Hash, M: Hash"), + PartialEq(bound = "InnerDigest<C>: PartialEq, M: PartialEq") )] -pub struct InnerPathIter<'t, C> +pub struct InnerPathIter<'t, C, M = BTreeMap<C>> where C: Configuration + ?Sized, + M: InnerMap<C>, { + /// Inner Tree + inner_tree: &'t InnerTree<C, M>, + /// Inner Node Iterator iter: InnerNodeIter, +} - /// Inner Tree - inner_tree: &'t InnerTree<C>, +impl<'t, C, M> InnerPathIter<'t, C, M> +where + C: Configuration + ?Sized, + M: InnerMap<C>, +{ + /// Builds a new [`InnerPathIter`] for `inner_tree` using `iter`. + #[inline] + fn new(inner_tree: &'t InnerTree<C, M>, iter: InnerNodeIter) -> Self { + Self { inner_tree, iter } + } } -impl<'t, C> Iterator for InnerPathIter<'t, C> +// TODO: Add all methods which can be optimized. +impl<'t, C, M> Iterator for InnerPathIter<'t, C, M> where C: Configuration + ?Sized, + M: InnerMap<C>, { type Item = &'t InnerDigest<C>; #[inline] fn next(&mut self) -> Option<Self::Item> { - self.iter - .next() - .map(move |n| self.inner_tree.get(n.sibling())) + self.iter.next().map(move |n| &self.inner_tree[n.sibling()]) } #[inline] @@ -347,3 +421,17 @@ where self.iter.size_hint() } } + +impl<'t, C, M> ExactSizeIterator for InnerPathIter<'t, C, M> +where + C: Configuration + ?Sized, + M: InnerMap<C>, +{ +} + +impl<'t, C, M> FusedIterator for InnerPathIter<'t, C, M> +where + C: Configuration + ?Sized, + M: InnerMap<C>, +{ +} diff --git a/manta-crypto/src/merkle_tree/node.rs b/manta-crypto/src/merkle_tree/node.rs index 1ae0f24e0..209cde8aa 100644 --- a/manta-crypto/src/merkle_tree/node.rs +++ b/manta-crypto/src/merkle_tree/node.rs @@ -184,11 +184,16 @@ impl Node { } } - /// Returns `self` with its sibling in parity order. + /// Maps `self` and its sibling over `f`. #[inline] - pub fn with_sibling(self) -> (Self, Self) { - self.parity() - .triple_order(self, move || self - 1, move || self + 1) + pub fn with_sibling<T, F>(self, mut f: F) -> (T, T) + where + F: FnMut(Self) -> T, + { + match self.parity() { + Parity::Left => (f(self), f(self + 1)), + Parity::Right => (f(self - 1), f(self)), + } } /// Returns the left child [`Node`] of this node. diff --git a/manta-crypto/src/merkle_tree/single_leaf.rs b/manta-crypto/src/merkle_tree/single_leaf.rs index b692db759..3b3d9dfe3 100644 --- a/manta-crypto/src/merkle_tree/single_leaf.rs +++ b/manta-crypto/src/merkle_tree/single_leaf.rs @@ -20,18 +20,11 @@ use crate::merkle_tree::{ capacity, Configuration, InnerDigest, LeafDigest, MerkleTree, Node, Parameters, Parity, Path, Root, Tree, }; -use core::{convert::Infallible, fmt::Debug, hash::Hash}; +use core::{fmt::Debug, hash::Hash}; /// Single Leaf Merkle Tree Type pub type SingleLeafMerkleTree<C> = MerkleTree<C, SingleLeaf<C>>; -/// Path Query Type -/// -/// Since the [`SingleLeaf`] tree only stores one leaf and one path, we can only query the -/// tree for the current path. -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Current; - /// Single Leaf Merkle Tree Backing Structure #[derive(derivative::Derivative)] #[derivative( @@ -108,10 +101,6 @@ where LeafDigest<C>: Clone, InnerDigest<C>: Clone, { - type Query = Current; - - type Error = Infallible; - #[inline] fn new(parameters: &Parameters<C>) -> Self { let _ = parameters; @@ -130,9 +119,9 @@ where } #[inline] - fn path(&self, parameters: &Parameters<C>, query: Self::Query) -> Result<Path<C>, Self::Error> { - let _ = (parameters, query); - Ok(self.path.clone()) + fn current_path(&self, parameters: &Parameters<C>) -> Path<C> { + let _ = parameters; + self.path.clone() } #[inline] diff --git a/manta-crypto/src/merkle_tree/test.rs b/manta-crypto/src/merkle_tree/test.rs index e9d7b7f88..f4e59fbf1 100644 --- a/manta-crypto/src/merkle_tree/test.rs +++ b/manta-crypto/src/merkle_tree/test.rs @@ -16,7 +16,9 @@ //! Testing Framework -use crate::merkle_tree::{Configuration, Leaf, MerkleTree, Parameters, Tree}; +use crate::merkle_tree::{ + Configuration, GetPath, GetPathError, Leaf, MerkleTree, Parameters, Tree, +}; use core::fmt::Debug; /// Tests that a tree constructed with `parameters` can accept at least two leaves without @@ -48,14 +50,14 @@ where /// /// [`Path`]: crate::merkle_tree::Path #[inline] -pub fn assert_valid_path<C, T>(tree: &MerkleTree<C, T>, query: T::Query, leaf: &Leaf<C>) +pub fn assert_valid_path<C, T>(tree: &MerkleTree<C, T>, index: usize, leaf: &Leaf<C>) where C: Configuration + ?Sized, - T: Tree<C>, - T::Error: Debug, + T: Tree<C> + GetPath<C>, + GetPathError<C, T>: Debug, { assert!( - tree.path(query) + tree.path(index) .expect("Only valid queries are accepted.") .verify(&tree.parameters, &tree.root(), leaf), "Path returned from tree was not valid." diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index ee01905fe..cf3a2a5a9 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -121,12 +121,6 @@ pub trait Tree<C>: Sized where C: Configuration + ?Sized, { - /// Path Query Type - type Query; - - /// Path Error Type - type Error; - /// Builds a new merkle tree. fn new(parameters: &Parameters<C>) -> Self; @@ -139,7 +133,7 @@ where L: IntoIterator<Item = &'l Leaf<C>>, { let mut tree = Self::new(parameters); - tree.extend(parameters, leaves).then(|| tree) + tree.extend(parameters, leaves).then(move || tree) } /// Builds a new merkle tree with the given `leaves` returning `None` if the slice @@ -164,8 +158,8 @@ where /// Returns the [`Root`] of the merkle tree. fn root(&self, parameters: &Parameters<C>) -> Root<C>; - /// Returns the [`Path`] to some element of the merkle tree given by the `query`. - fn path(&self, parameters: &Parameters<C>, query: Self::Query) -> Result<Path<C>, Self::Error>; + /// Returns the [`Path`] of the current (i.e. right-most) leaf. + fn current_path(&self, parameters: &Parameters<C>) -> Path<C>; /// Checks if a leaf can be inserted into the tree and if it can, it runs `leaf_digest` to /// extract a leaf digest to insert, returning `None` if there was no leaf digest. @@ -180,10 +174,8 @@ where where F: FnOnce() -> LeafDigest<C>, { - match self.maybe_push_digest(parameters, || Some(leaf_digest())) { - Some(result) => result, - _ => unreachable!(), - } + self.maybe_push_digest(parameters, move || Some(leaf_digest())) + .unwrap() } /// Inserts the digest of `leaf` at the next available leaf node of the tree, returning @@ -271,6 +263,42 @@ where } } +/// Merkle Tree Path Query Mixin +pub trait GetPath<C> +where + C: Configuration + ?Sized, +{ + /// Path Query Error Type + type Error; + + /// Returns the [`Path`] of the leaf at the given `index`. + fn path(&self, parameters: &Parameters<C>, index: usize) -> Result<Path<C>, Self::Error>; +} + +/// Tree Path Query Error Type +pub type GetPathError<C, T> = <T as GetPath<C>>::Error; + +/// Digest Type +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), + Copy(bound = "LeafDigest<C>: Copy, InnerDigest<C>: Copy"), + Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), + Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), + Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), + PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") +)] +pub enum Digest<C> +where + C: Configuration + ?Sized, +{ + /// Leaf Digest + Leaf(LeafDigest<C>), + + /// Inner Digest + Inner(InnerDigest<C>), +} + /// Merkle Tree Parameters #[derive(derivative::Derivative)] #[derivative( @@ -518,16 +546,37 @@ where &self.parameters } + /// Returns the length of this merkle tree. + #[inline] + pub fn len(&self) -> usize { + self.tree.len() + } + + /// Returns `true` if this merkle tree is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.tree.is_empty() + } + /// Returns the [`Root`] of the merkle tree. #[inline] pub fn root(&self) -> Root<C> { self.tree.root(&self.parameters) } - /// Returns the [`Path`] to some element of the merkle tree given by the `query`. + /// Returns the [`Path`] of the current (i.e right-most) leaf. #[inline] - pub fn path(&self, query: T::Query) -> Result<Path<C>, T::Error> { - self.tree.path(&self.parameters, query) + pub fn current_path(&self) -> Path<C> { + self.tree.current_path(&self.parameters) + } + + /// Returns the [`Path`] of the leaf at the given `index`. + #[inline] + pub fn path(&self, index: usize) -> Result<Path<C>, GetPathError<C, T>> + where + T: GetPath<C>, + { + self.tree.path(&self.parameters, index) } /// Inserts `leaf` at the next avaiable leaf node of the tree, returning `false` if the diff --git a/manta-pay/src/accounting/keys.rs b/manta-pay/src/accounting/keys.rs index 128f5796e..ba98ae871 100644 --- a/manta-pay/src/accounting/keys.rs +++ b/manta-pay/src/accounting/keys.rs @@ -27,14 +27,11 @@ use alloc::{format, string::String}; use bip32::Seed; use core::{marker::PhantomData, num::ParseIntError, str::FromStr}; use manta_accounting::keys::{DerivedSecretKeyGenerator, DerivedSecretKeyParameter, KeyKind}; +use manta_util::{create_seal, seal}; pub use bip32::{Error, Mnemonic, XPrv as SecretKey}; -/// Sealed Trait Module -mod sealed { - /// Sealed Trait - pub trait Sealed {} -} +create_seal! {} /// Coin Type Id Type pub type CoinTypeId = u128; @@ -57,7 +54,7 @@ pub trait CoinType: sealed::Sealed { /// Implements the [`CoinType`] trait for `$name` with coin type id given by `$id`. macro_rules! impl_coin_type { ($name:ty, $id:ident) => { - impl sealed::Sealed for $name {} + seal!($name); impl CoinType for $name { const COIN_TYPE_ID: CoinTypeId = $id; } diff --git a/manta-pay/src/accounting/ledger.rs b/manta-pay/src/accounting/ledger.rs index 7ba18c08a..ecd21c743 100644 --- a/manta-pay/src/accounting/ledger.rs +++ b/manta-pay/src/accounting/ledger.rs @@ -34,7 +34,7 @@ use blake2::{ use manta_accounting::identity; use manta_crypto::{ constraint::{self, reflection::HasAllocation, Allocation, Constant, Variable}, - merkle_tree::{self, latest_node::LatestNode, Tree}, + merkle_tree::{self, single_leaf::SingleLeaf, Tree}, set::{constraint::VerifiedSetVariable, ContainmentProof, Set, VerifiedSet}, }; use manta_util::{as_bytes, concatenate, into_array_unchecked}; @@ -73,7 +73,7 @@ pub struct UtxoShard { root: Root, /// Unspent Transaction Outputs - utxos: LatestNode<ConfigConverter<Configuration>>, + utxos: SingleLeaf<ConfigConverter<Configuration>>, } /// UTXO Set diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index f03ab78ea..6294cfb2e 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -26,6 +26,7 @@ extern crate alloc; mod array; mod concat; mod mixed_chain; +mod sealed; pub mod num; diff --git a/manta-util/src/sealed.rs b/manta-util/src/sealed.rs new file mode 100644 index 000000000..6b9cf7a90 --- /dev/null +++ b/manta-util/src/sealed.rs @@ -0,0 +1,37 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Sealed Traits + +/// Creates a new `sealed::Sealed` trait in the current module. +#[macro_export] +macro_rules! create_seal { + () => { + /// Sealed Trait Module + mod sealed { + /// Sealed Trait + pub trait Sealed {} + } + }; +} + +/// Adds an `sealed::Sealed` implementation to `$type`. +#[macro_export] +macro_rules! seal { + ($($type:tt),+ $(,)?) => { + $(impl sealed::Sealed for $type {})+ + }; +} From 2ebaee8b33da93f9d7280953067c949ef649bdb2 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 26 Sep 2021 15:45:11 -0400 Subject: [PATCH 061/275] WIP: try using Rc/Weak for forking mechanism --- manta-crypto/src/merkle_tree/fork.rs | 278 +++++++++++++++------------ manta-crypto/src/merkle_tree/tree.rs | 33 +--- 2 files changed, 157 insertions(+), 154 deletions(-) diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs index 672cbbbee..0b2bb49b1 100644 --- a/manta-crypto/src/merkle_tree/fork.rs +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -22,209 +22,233 @@ use crate::merkle_tree::{ capacity, inner_tree::InnerTree, Configuration, GetPath, GetPathError, InnerDigest, Leaf, LeafDigest, MerkleTree, Parameters, Path, Root, Tree, }; -use alloc::vec::Vec; +use alloc::{ + rc::{Rc, Weak}, + vec::Vec, +}; use core::{fmt::Debug, hash::Hash}; -/// Fork Path Error -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "GetPathError<C, T>: Clone"), - Copy(bound = "GetPathError<C, T>: Copy"), - Debug(bound = "GetPathError<C, T>: Debug"), - Eq(bound = "GetPathError<C, T>: Eq"), - Hash(bound = "GetPathError<C, T>: Hash"), - PartialEq(bound = "GetPathError<C, T>: PartialEq") -)] -pub enum ForkGetPathError<C, T> +/// Fork-able Merkle Tree +pub struct Trunk<C, T> where C: Configuration + ?Sized, - T: GetPath<C>, + T: Tree<C>, { - /// Trunk Path Query Error - TrunkError(GetPathError<C, T>), + /// Base Merkle Tree + base: Rc<MerkleTree<C, T>>, +} - /// Unknown Index on Branch Error - UnknownIndexOnBranch, +impl<C, T> Trunk<C, T> +where + C: Configuration + ?Sized, + T: Tree<C>, +{ + /// Builds a new [`Trunk`] from a reference-counted [`MerkleTree`]. + #[inline] + fn new_inner(base: Rc<MerkleTree<C, T>>) -> Self { + Self { base } + } + + /// Builds a new [`Trunk`] from a `base` merkle tree. + #[inline] + pub fn new(base: MerkleTree<C, T>) -> Self { + Self::new_inner(Rc::new(base)) + } + + /// Converts `self` back into its inner [`MerkleTree`]. + /// + /// # Safety + /// + /// This function automatically detaches all of the forks associated to this trunk. To + /// attach them to another trunk use [`Fork::attach`]. + #[inline] + pub fn into_tree(self) -> MerkleTree<C, T> { + Rc::try_unwrap(self.base).ok().unwrap() + } + + /// Creates a new fork of this trunk. + #[inline] + pub fn fork(&self) -> Fork<C, T> { + Fork::new(self) + } } -/// Merkle Tree Delta -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), - Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), - Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default"), - Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), - Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), - PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") -)] -pub struct Delta<C> +/// Merkle Tree Fork +pub struct Fork<C, T> where C: Configuration + ?Sized, + T: Tree<C>, { - /// Leaf Digests - pub(super) leaf_digests: Vec<LeafDigest<C>>, + /// Base Merkle Tree + base: Weak<MerkleTree<C, T>>, - /// Inner Digests - pub(super) inner_digests: InnerTree<C>, + /// Branch Data + branch: Branch<C>, } -impl<C> Delta<C> +impl<C, T> Fork<C, T> where C: Configuration + ?Sized, + T: Tree<C>, { - /// Builds a new [`Delta`] from `leaf_digests` and `inner_digests`. + /// Builds a new [`Fork`] from `trunk`. #[inline] - pub fn new(leaf_digests: Vec<LeafDigest<C>>, inner_digests: InnerTree<C>) -> Self { + pub fn new(trunk: &Trunk<C, T>) -> Self { + Self::with_branch(trunk, Default::default()) + } + + /// Builds a new [`Fork`] from `trunk` with a custom `branch`. + #[inline] + pub fn with_branch(trunk: &Trunk<C, T>, branch: Branch<C>) -> Self { Self { - leaf_digests, - inner_digests, + base: Rc::downgrade(&trunk.base), + branch, } } - /// + /// Attaches this fork to a new `trunk`. #[inline] - pub fn len(&self) -> usize { - self.leaf_digests.len() + pub fn attach(&mut self, trunk: &Trunk<C, T>) { + // FIXME: Do we have to do a re-computation of the `branch` inner data? + self.base = Rc::downgrade(&trunk.base); } - /// + /// Returns `true` if this fork is attached to some `trunk`. #[inline] - pub fn is_empty(&self) -> bool { - self.len() == 0 + pub fn is_attached(&self) -> bool { + self.base.upgrade().is_some() } + /// Computes the length of this fork of the tree. /// + /// Returns `None` if this fork has been detached from its trunk. Use [`attach`](Self::attach) + /// to re-associate a trunk to this fork. #[inline] - fn root<T>(&self, parameters: &Parameters<C>, trunk: &T) -> Root<C> - where - T: Tree<C>, - { - todo!() + pub fn len(&self) -> Option<usize> { + let base = self.base.upgrade()?; + Some(base.len() + self.branch.len()) } + /// Returns `true` if this fork is empty. /// + /// Returns `None` if this fork has been detached from its trunk. Use [`attach`](Self::attach) + /// to re-associate a trunk to this fork. #[inline] - fn current_path<T>(&self, parameters: &Parameters<C>, trunk: &T) -> Path<C> - where - T: Tree<C>, - { - todo!() + pub fn is_empty(&self) -> Option<bool> { + Some(self.len()? == 0) } + /// Computes the current root of this fork. /// + /// Returns `None` if this fork has been detached from its trunk. Use [`attach`](Self::attach) + /// to re-associate a trunk to this fork. #[inline] - fn forward_path<T>( - &self, - parameters: &Parameters<C>, - trunk: &T, - index: usize, - ) -> Option<Path<C>> - where - T: Tree<C>, - { - let trunk_len = trunk.len(); - let total_len = trunk_len + self.len(); - if !(trunk_len..total_len).contains(&index) { - return None; - } - todo!() + pub fn root(&self) -> Option<Root<C>> { + let base = self.base.upgrade()?; + Some(self.branch.root(&base.parameters, &base.tree)) } + /// Appends a new `leaf` onto this fork. /// + /// Returns `None` if this fork has been detached from its trunk. Use [`attach`](Self::attach) + /// to re-associate a trunk to this fork. #[inline] - fn push<T>(&mut self, parameters: &Parameters<C>, trunk: &T, leaf: &Leaf<C>) -> bool - where - T: Tree<C>, - { - if trunk.len() + self.len() >= capacity::<C>() { - return false; - } - - todo!() + pub fn push(&mut self, leaf: &Leaf<C>) -> Option<bool> { + let base = self.base.upgrade()?; + Some(self.branch.push(&base.parameters, &base.tree, leaf)) } } -/// Merkle Tree Fork -#[derive(derivative::Derivative)] /* TODO: +/// Fork Path Error +#[derive(derivative::Derivative)] #[derivative( - Clone(bound = "Parameters<C>: Clone, T: Clone"), - Copy(bound = "Parameters<C>: Copy, T: Copy"), - Debug(bound = "Parameters<C>: Debug, T: Debug"), - Default(bound = "Parameters<C>: Default, T: Default"), - Eq(bound = "Parameters<C>: Eq, T: Eq"), - Hash(bound = "Parameters<C>: Hash, T: Hash"), - PartialEq(bound = "Parameters<C>: PartialEq, T: PartialEq") + Clone(bound = "GetPathError<C, T>: Clone"), + Copy(bound = "GetPathError<C, T>: Copy"), + Debug(bound = "GetPathError<C, T>: Debug"), + Eq(bound = "GetPathError<C, T>: Eq"), + Hash(bound = "GetPathError<C, T>: Hash"), + PartialEq(bound = "GetPathError<C, T>: PartialEq") )] +pub enum ForkGetPathError<C, T> +where + C: Configuration + ?Sized, + T: GetPath<C>, +{ + /// Trunk Path Query Error + TrunkError(GetPathError<C, T>), + + /// Unknown Index on Branch Error + UnknownIndexOnBranch, +} */ -pub struct Fork<'t, C, T> + +/// Merkle Tree Fork Branch +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), + Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), + Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default"), + Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), + Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), + PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") +)] +pub struct Branch<C> where C: Configuration + ?Sized, - T: Tree<C>, { - /// Original Trunk - pub(super) trunk: &'t MerkleTree<C, T>, + /// Leaf Digests + pub leaf_digests: Vec<LeafDigest<C>>, - /// Delta - pub(super) delta: Delta<C>, + /// Inner Digests + pub inner_digests: InnerTree<C>, } -impl<'t, C, T> Fork<'t, C, T> +impl<C> Branch<C> where C: Configuration + ?Sized, - T: Tree<C>, { - /// + /// Builds a new [`Branch`] from `leaf_digests` and `inner_digests`. #[inline] - pub fn new(trunk: &'t MerkleTree<C, T>) -> Self { + pub fn new(leaf_digests: Vec<LeafDigest<C>>, inner_digests: InnerTree<C>) -> Self { Self { - trunk, - delta: Default::default(), + leaf_digests, + inner_digests, } } - /// - #[inline] - pub fn parameters(&self) -> &Parameters<C> { - self.trunk.parameters() - } - - /// + /// Computes the length of this branch. #[inline] pub fn len(&self) -> usize { - self.trunk.len() + self.delta.len() + self.leaf_digests.len() } - /// + /// Returns `true` if this branch is empty. #[inline] pub fn is_empty(&self) -> bool { self.len() == 0 } - /// - #[inline] - pub fn root(&self) -> Root<C> { - self.delta.root(&self.trunk.parameters, &self.trunk.tree) - } - - /// + /// Computes the root of the fork which has `self` as its branch and `tree` as its base tree. #[inline] - pub fn current_path(&self) -> Path<C> { - self.delta - .current_path(&self.trunk.parameters, &self.trunk.tree) + fn root<T>(&self, parameters: &Parameters<C>, tree: &T) -> Root<C> + where + T: Tree<C>, + { + todo!() } - /// + /// Appends a new `leaf` to this branch, recomputing the relevant inner digests relative to + /// the base `tree`. #[inline] - pub fn forward_path(&self, index: usize) -> Option<Path<C>> { - self.delta - .forward_path(&self.trunk.parameters, &self.trunk.tree, index) - } + fn push<T>(&mut self, parameters: &Parameters<C>, tree: &T, leaf: &Leaf<C>) -> bool + where + T: Tree<C>, + { + if tree.len() + self.len() >= capacity::<C>() { + return false; + } - /// - #[inline] - pub fn push(&mut self, leaf: &Leaf<C>) -> bool { - self.delta - .push(&self.trunk.parameters, &self.trunk.tree, leaf) + todo!() } } diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index cf3a2a5a9..e894976b1 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -18,10 +18,7 @@ extern crate alloc; -use crate::merkle_tree::{ - fork::{Delta, Fork}, - Node, -}; +use crate::merkle_tree::{fork::Trunk, Node}; use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; @@ -247,20 +244,6 @@ where { self.extend(parameters, leaves) } - - /// Tries to merge the `delta` into the tree, returning it back if it would exceed the capacity - /// of the tree. - #[inline] - fn merge_delta(&mut self, parameters: &Parameters<C>, delta: Delta<C>) -> Result<(), Delta<C>> { - let Delta { - leaf_digests, - inner_digests, - } = delta; - match self.extend_digests(parameters, leaf_digests) { - Err(leaf_digests) => Err(Delta::new(leaf_digests.collect(), inner_digests)), - _ => Ok(()), - } - } } /// Merkle Tree Path Query Mixin @@ -607,16 +590,12 @@ where self.tree.extend_slice(&self.parameters, leaves) } - /// Forks the merkle tree. - #[inline] - pub fn fork(&self) -> Fork<C, T> { - Fork::new(self) - } - - /// Merges the `fork` into `self`. + /// Converts `self` into a fork-able merkle tree. + /// + /// Use [`Trunk::into_tree`] to convert back. #[inline] - pub fn merge(&mut self, fork: Fork<C, T>) { - debug_assert!(self.tree.merge_delta(&self.parameters, fork.delta).is_ok()) + pub fn into_trunk(self) -> Trunk<C, T> { + Trunk::new(self) } /// Extracts the parameters of the merkle tree, dropping the internal tree. From a663920a46beadf27b5fd437f2353580b02c71b9 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 27 Sep 2021 15:47:12 -0400 Subject: [PATCH 062/275] WIP: fix pointer-family interface for forking mechanism --- manta-crypto/src/merkle_tree/fork.rs | 304 +++++++++++++++++++++++---- manta-crypto/src/merkle_tree/tree.rs | 26 ++- 2 files changed, 283 insertions(+), 47 deletions(-) diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs index 0b2bb49b1..2f888f25f 100644 --- a/manta-crypto/src/merkle_tree/fork.rs +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -20,39 +20,38 @@ extern crate alloc; use crate::merkle_tree::{ capacity, inner_tree::InnerTree, Configuration, GetPath, GetPathError, InnerDigest, Leaf, - LeafDigest, MerkleTree, Parameters, Path, Root, Tree, + LeafDigest, MerkleTree, Path, Root, Tree, }; -use alloc::{ - rc::{Rc, Weak}, - vec::Vec, -}; -use core::{fmt::Debug, hash::Hash}; +use alloc::vec::Vec; +use core::{fmt::Debug, hash::Hash, mem}; /// Fork-able Merkle Tree -pub struct Trunk<C, T> +pub struct Trunk<C, T, P = raw::SingleThreaded> where C: Configuration + ?Sized, T: Tree<C>, + P: raw::MerkleTreePointerFamily<C, T>, { /// Base Merkle Tree - base: Rc<MerkleTree<C, T>>, + base: Option<P::Strong>, } -impl<C, T> Trunk<C, T> +impl<C, T, P> Trunk<C, T, P> where C: Configuration + ?Sized, T: Tree<C>, + P: raw::MerkleTreePointerFamily<C, T>, { /// Builds a new [`Trunk`] from a reference-counted [`MerkleTree`]. #[inline] - fn new_inner(base: Rc<MerkleTree<C, T>>) -> Self { + fn new_inner(base: Option<P::Strong>) -> Self { Self { base } } /// Builds a new [`Trunk`] from a `base` merkle tree. #[inline] pub fn new(base: MerkleTree<C, T>) -> Self { - Self::new_inner(Rc::new(base)) + Self::new_inner(Some(P::new(base))) } /// Converts `self` back into its inner [`MerkleTree`]. @@ -60,63 +59,137 @@ where /// # Safety /// /// This function automatically detaches all of the forks associated to this trunk. To - /// attach them to another trunk use [`Fork::attach`]. + /// attach them to another trunk, use [`Fork::attach`]. #[inline] pub fn into_tree(self) -> MerkleTree<C, T> { - Rc::try_unwrap(self.base).ok().unwrap() + P::claim(self.base.unwrap()) } /// Creates a new fork of this trunk. #[inline] - pub fn fork(&self) -> Fork<C, T> { + pub fn fork(&self) -> Fork<C, T, P> { Fork::new(self) } + + /// Attaches `fork` to `self` as its new trunk. + #[inline] + pub fn attach(&self, fork: &mut Fork<C, T, P>) { + fork.attach(self) + } + + /// Tries to merge `fork` onto `self`, returning `fork` back if it could not be merged. + /// + /// # Safety + /// + /// If the merge succeeds, this function automatically detaches all of the forks associated to + /// this trunk. To attach them to another trunk, use [`Fork::attach`]. To attach them to this + /// trunk, [`attach`](Self::attach) can also be used. + /// + /// Since merging will add leaves to the base tree, forks which were previously associated to + /// this trunk will have to catch up. If [`Fork::attach`] or [`attach`](Self::attach) is used, + /// the leaves which were added in this merge will exist before the first leaf in the fork in + /// the final tree. + #[inline] + pub fn merge(&mut self, fork: Fork<C, T, P>) -> Result<(), Fork<C, T, P>> { + match fork.get_attached_base(self) { + Some(base) => { + self.merge_branch(base, fork.branch); + Ok(()) + } + _ => Err(fork), + } + } + + /// Performs a merge of the `branch` onto `fork_base`, setting `self` equal to the resulting + /// merged tree. + #[inline] + fn merge_branch(&mut self, fork_base: P::Strong, branch: Branch<C>) { + self.base = Some(fork_base); + let mut base = P::claim(mem::take(&mut self.base).unwrap()); + branch.merge(&mut base); + self.base = Some(P::new(base)); + } + + /// Borrows the underlying merkle tree pointer. + #[inline] + fn borrow_base(&self) -> &P::Strong { + self.base.as_ref().unwrap() + } + + /// Returns a new weak pointer to the base tree. + #[inline] + fn downgrade(&self) -> P::Weak { + P::downgrade(self.borrow_base()) + } + + /// Checks if the internal base tree uses the same pointer as `base`. + #[inline] + fn ptr_eq_base(&self, base: &P::Strong) -> bool { + P::strong_ptr_eq(self.borrow_base(), base) + } } /// Merkle Tree Fork -pub struct Fork<C, T> +pub struct Fork<C, T, P = raw::SingleThreaded> where C: Configuration + ?Sized, T: Tree<C>, + P: raw::MerkleTreePointerFamily<C, T>, { /// Base Merkle Tree - base: Weak<MerkleTree<C, T>>, + base: P::Weak, /// Branch Data branch: Branch<C>, } -impl<C, T> Fork<C, T> +impl<C, T, P> Fork<C, T, P> where C: Configuration + ?Sized, T: Tree<C>, + P: raw::MerkleTreePointerFamily<C, T>, { /// Builds a new [`Fork`] from `trunk`. #[inline] - pub fn new(trunk: &Trunk<C, T>) -> Self { - Self::with_branch(trunk, Default::default()) + pub fn new(trunk: &Trunk<C, T, P>) -> Self { + Self::with_leaves(trunk, Default::default()) } - /// Builds a new [`Fork`] from `trunk` with a custom `branch`. + /// Builds a new [`Fork`] from `trunk` extended by `leaf_digests` #[inline] - pub fn with_branch(trunk: &Trunk<C, T>, branch: Branch<C>) -> Self { + pub fn with_leaves(trunk: &Trunk<C, T, P>, leaf_digests: Vec<LeafDigest<C>>) -> Self { Self { - base: Rc::downgrade(&trunk.base), - branch, + base: trunk.downgrade(), + branch: Branch::new(trunk.borrow_base().as_ref(), leaf_digests), } } /// Attaches this fork to a new `trunk`. #[inline] - pub fn attach(&mut self, trunk: &Trunk<C, T>) { - // FIXME: Do we have to do a re-computation of the `branch` inner data? - self.base = Rc::downgrade(&trunk.base); + pub fn attach(&mut self, trunk: &Trunk<C, T, P>) { + self.base = trunk.downgrade(); + self.branch.rebase(trunk.borrow_base().as_ref()); } - /// Returns `true` if this fork is attached to some `trunk`. + /// Returns `true` if this fork is attached to some [`Trunk`]. #[inline] pub fn is_attached(&self) -> bool { - self.base.upgrade().is_some() + P::upgrade(&self.base).is_some() + } + + /// Returns `true` if this fork is attached to `trunk`. + #[inline] + pub fn is_attached_to(&self, trunk: &Trunk<C, T, P>) -> bool { + matches!(P::upgrade(&self.base), Some(base) if trunk.ptr_eq_base(&base)) + } + + /// Returns the attached base tree if `self` is attached to `trunk`. + #[inline] + fn get_attached_base(&self, trunk: &Trunk<C, T, P>) -> Option<P::Strong> { + match P::upgrade(&self.base) { + Some(base) if trunk.ptr_eq_base(&base) => Some(base), + _ => None, + } } /// Computes the length of this fork of the tree. @@ -125,8 +198,7 @@ where /// to re-associate a trunk to this fork. #[inline] pub fn len(&self) -> Option<usize> { - let base = self.base.upgrade()?; - Some(base.len() + self.branch.len()) + Some(P::upgrade(&self.base)?.as_ref().len() + self.branch.len()) } /// Returns `true` if this fork is empty. @@ -144,8 +216,7 @@ where /// to re-associate a trunk to this fork. #[inline] pub fn root(&self) -> Option<Root<C>> { - let base = self.base.upgrade()?; - Some(self.branch.root(&base.parameters, &base.tree)) + Some(self.branch.root(P::upgrade(&self.base)?.as_ref())) } /// Appends a new `leaf` onto this fork. @@ -154,8 +225,7 @@ where /// to re-associate a trunk to this fork. #[inline] pub fn push(&mut self, leaf: &Leaf<C>) -> Option<bool> { - let base = self.base.upgrade()?; - Some(self.branch.push(&base.parameters, &base.tree, leaf)) + Some(self.branch.push(P::upgrade(&self.base)?.as_ref(), leaf)) } } @@ -208,13 +278,27 @@ impl<C> Branch<C> where C: Configuration + ?Sized, { - /// Builds a new [`Branch`] from `leaf_digests` and `inner_digests`. + /// Builds a new [`Branch`] from `base` and `leaf_digests`. #[inline] - pub fn new(leaf_digests: Vec<LeafDigest<C>>, inner_digests: InnerTree<C>) -> Self { - Self { + fn new<T>(base: &MerkleTree<C, T>, leaf_digests: Vec<LeafDigest<C>>) -> Self + where + T: Tree<C>, + { + let mut this = Self { leaf_digests, - inner_digests, - } + inner_digests: Default::default(), + }; + this.rebase(base); + this + } + + /// Restarts `self` at a new base. + #[inline] + fn rebase<T>(&mut self, base: &MerkleTree<C, T>) + where + T: Tree<C>, + { + todo!() } /// Computes the length of this branch. @@ -229,9 +313,9 @@ where self.len() == 0 } - /// Computes the root of the fork which has `self` as its branch and `tree` as its base tree. + /// Computes the root of the fork which has `self` as its branch and `base` as its base tree. #[inline] - fn root<T>(&self, parameters: &Parameters<C>, tree: &T) -> Root<C> + fn root<T>(&self, base: &MerkleTree<C, T>) -> Root<C> where T: Tree<C>, { @@ -239,16 +323,148 @@ where } /// Appends a new `leaf` to this branch, recomputing the relevant inner digests relative to - /// the base `tree`. + /// the `base` tree. #[inline] - fn push<T>(&mut self, parameters: &Parameters<C>, tree: &T, leaf: &Leaf<C>) -> bool + fn push<T>(&mut self, base: &MerkleTree<C, T>, leaf: &Leaf<C>) -> bool where T: Tree<C>, { - if tree.len() + self.len() >= capacity::<C>() { + if base.tree.len() + self.len() >= capacity::<C>() { return false; } todo!() } + + /// Merges `self` onto the `base` merkle tree. + #[inline] + fn merge<T>(self, base: &mut MerkleTree<C, T>) + where + T: Tree<C>, + { + base.tree.merge_branch(&base.parameters, MergeBranch(self)) + } +} + +/// Fork Merge Branch +/// +/// An type which can only be instantiated by the merkle tree forking implementation, which +/// prevents running [`Tree::merge_branch`] on arbitrary user-constructed [`Branch`] values. +pub struct MergeBranch<C>(Branch<C>) +where + C: Configuration + ?Sized; + +impl<C> From<MergeBranch<C>> for Branch<C> +where + C: Configuration + ?Sized, +{ + #[inline] + fn from(branch: MergeBranch<C>) -> Self { + branch.0 + } +} + +/// Raw Forking Primitives +pub mod raw { + use super::*; + use alloc::{ + rc::{Rc, Weak as WeakRc}, + sync::{Arc, Weak as WeakArc}, + }; + use core::borrow::Borrow; + use manta_util::{create_seal, seal}; + + create_seal! {} + + /// Merkle Tree Pointer Family + pub trait MerkleTreePointerFamily<C, T>: sealed::Sealed + where + C: Configuration + ?Sized, + T: Tree<C>, + { + /// Strong Pointer + type Strong: AsRef<MerkleTree<C, T>> + Borrow<MerkleTree<C, T>>; + + /// Weak Pointer + type Weak; + + /// Returns a new strong pointer holding `base`. + fn new(base: MerkleTree<C, T>) -> Self::Strong; + + /// Claims ownership of the underlying merkle tree from `strong`. + /// + /// # Panics + /// + /// This function can only panic if there are other outstanding strong pointers. This + /// function will still succeed if there are other outstanding weak pointers, but they will + /// all be disassociated to `strong`. + fn claim(strong: Self::Strong) -> MerkleTree<C, T>; + + /// Returns a new weak pointer to `strong`. + fn downgrade(strong: &Self::Strong) -> Self::Weak; + + /// Tries to upgrade `weak` to a strong pointer, returning `None` if there is no strong + /// pointer associated to `weak`. + fn upgrade(weak: &Self::Weak) -> Option<Self::Strong>; + + /// Checks if two strong pointers point to the same allocation. + fn strong_ptr_eq(lhs: &Self::Strong, rhs: &Self::Strong) -> bool; + } + + /// Implements [`MerkleTreePointerFamily`] for `$type` with `$strong` and `$weak` pointers. + macro_rules! impl_pointer_family { + ($type:tt, $strong:ident, $weak:ident) => { + seal!($type); + impl<C, T> MerkleTreePointerFamily<C, T> for $type + where + C: Configuration + ?Sized, + T: Tree<C>, + { + type Strong = $strong<MerkleTree<C, T>>; + + type Weak = $weak<MerkleTree<C, T>>; + + #[inline] + fn new(base: MerkleTree<C, T>) -> Self::Strong { + $strong::new(base) + } + + #[inline] + fn claim(strong: Self::Strong) -> MerkleTree<C, T> { + $strong::try_unwrap(strong).ok().unwrap() + } + + #[inline] + fn downgrade(strong: &Self::Strong) -> Self::Weak { + $strong::downgrade(strong) + } + + #[inline] + fn upgrade(weak: &Self::Weak) -> Option<Self::Strong> { + weak.upgrade() + } + + #[inline] + fn strong_ptr_eq(lhs: &Self::Strong, rhs: &Self::Strong) -> bool { + $strong::ptr_eq(lhs, rhs) + } + } + }; + } + + /// Single-Threaded Merkle Tree Pointer Family + /// + /// This is the pointer family for [`Rc`]. + #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct SingleThreaded; + + impl_pointer_family!(SingleThreaded, Rc, WeakRc); + + /// Thread-Safe Merkle Tree Pointer Family + /// + /// This is the pointer family for [`Arc`]. + #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct ThreadSafe; + + impl_pointer_family!(ThreadSafe, Arc, WeakArc); } diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index e894976b1..3d5f43a51 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -18,7 +18,10 @@ extern crate alloc; -use crate::merkle_tree::{fork::Trunk, Node}; +use crate::merkle_tree::{ + fork::{self, Branch, MergeBranch, Trunk}, + Node, +}; use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; @@ -203,7 +206,7 @@ where if matches!(leaf_digests.size_hint().1, Some(max) if max <= capacity::<C>() - self.len()) { loop { match self.maybe_push_digest(parameters, || leaf_digests.next()) { - Some(result) => debug_assert!(result), + Some(result) => assert!(result), _ => return Ok(()), } } @@ -244,6 +247,20 @@ where { self.extend(parameters, leaves) } + + /// Merges the data from `branch` into `self` using `parameters`. + /// + /// # Implementation Note + /// + /// The forking implementation will never input invalid `branch` values, i.e. branches with too + /// many leaves, into this method, so any implementation is allowed to panic on invalid + /// `branch` values as long as it does not panic on any valid `branch` values. + #[inline] + fn merge_branch(&mut self, parameters: &Parameters<C>, branch: MergeBranch<C>) { + assert!(self + .extend_digests(parameters, Branch::from(branch).leaf_digests) + .is_ok()) + } } /// Merkle Tree Path Query Mixin @@ -594,7 +611,10 @@ where /// /// Use [`Trunk::into_tree`] to convert back. #[inline] - pub fn into_trunk(self) -> Trunk<C, T> { + pub fn into_trunk<P>(self) -> Trunk<C, T, P> + where + P: fork::raw::MerkleTreePointerFamily<C, T>, + { Trunk::new(self) } From c5fda1a9ea5159be51da70b4e21dfc336479ae15 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 27 Sep 2021 19:10:47 -0400 Subject: [PATCH 063/275] WIP: simplify implementation of single-leaf MT --- manta-crypto/src/merkle_tree/fork.rs | 2 + manta-crypto/src/merkle_tree/full.rs | 34 +++++--- manta-crypto/src/merkle_tree/inner_tree.rs | 9 ++ manta-crypto/src/merkle_tree/single_leaf.rs | 94 ++++++++++++++++++++- manta-crypto/src/merkle_tree/tree.rs | 50 +++++++++-- manta-pay/src/accounting/config.rs | 9 +- manta-pay/src/crypto/merkle_tree/mod.rs | 8 +- 7 files changed, 179 insertions(+), 27 deletions(-) diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs index 2f888f25f..07871742b 100644 --- a/manta-crypto/src/merkle_tree/fork.rs +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -16,6 +16,8 @@ //! Merkle Tree Forks +// TODO: Think about whether we want to keep the `raw::MerkleTreePointerFamily` API sealed or not. + extern crate alloc; use crate::merkle_tree::{ diff --git a/manta-crypto/src/merkle_tree/full.rs b/manta-crypto/src/merkle_tree/full.rs index 0a484c8d6..5f7a67b88 100644 --- a/manta-crypto/src/merkle_tree/full.rs +++ b/manta-crypto/src/merkle_tree/full.rs @@ -19,39 +19,43 @@ extern crate alloc; use crate::merkle_tree::{ - capacity, inner_tree::InnerTree, Configuration, GetPath, InnerDigest, LeafDigest, MerkleTree, - Node, Parameters, Path, Root, Tree, + capacity, + inner_tree::{BTreeMap, InnerMap, InnerTree}, + Configuration, GetPath, InnerDigest, LeafDigest, MerkleTree, Node, Parameters, Path, Root, + Tree, }; use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; /// Full Merkle Tree Type -pub type FullMerkleTree<C> = MerkleTree<C, Full<C>>; +pub type FullMerkleTree<C, M = BTreeMap<C>> = MerkleTree<C, Full<C, M>>; /// Full Merkle Tree Backing Structure #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), - Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), - Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default"), - Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), - Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), - PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") + Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone, M: Clone"), + Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug, M: Debug"), + Default(bound = ""), + Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq, M: Eq"), + Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash, M: Hash"), + PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq, M: PartialEq") )] -pub struct Full<C> +pub struct Full<C, M = BTreeMap<C>> where C: Configuration + ?Sized, + M: InnerMap<C>, { /// Leaf Digests leaf_digests: Vec<LeafDigest<C>>, /// Inner Digests - inner_digests: InnerTree<C>, + inner_digests: InnerTree<C, M>, } -impl<C> Full<C> +impl<C, M> Full<C, M> where C: Configuration + ?Sized, + M: InnerMap<C>, { /// Returns the leaf digests currently stored in the merkle tree. #[inline] @@ -94,9 +98,10 @@ where } } -impl<C> Tree<C> for Full<C> +impl<C, M> Tree<C> for Full<C, M> where C: Configuration + ?Sized, + M: InnerMap<C>, LeafDigest<C>: Clone, InnerDigest<C>: Clone, { @@ -136,9 +141,10 @@ where } } -impl<C> GetPath<C> for Full<C> +impl<C, M> GetPath<C> for Full<C, M> where C: Configuration + ?Sized, + M: InnerMap<C>, LeafDigest<C>: Clone, InnerDigest<C>: Clone, { diff --git a/manta-crypto/src/merkle_tree/inner_tree.rs b/manta-crypto/src/merkle_tree/inner_tree.rs index 05fb3b481..59ee5bca2 100644 --- a/manta-crypto/src/merkle_tree/inner_tree.rs +++ b/manta-crypto/src/merkle_tree/inner_tree.rs @@ -235,6 +235,15 @@ where /// Inner Tree /// /// Tree data-structure for storing the inner digests of a merkle tree. +/// +/// # Implementation Note +/// +/// This type intentionally lacks an implementation of [`Tree`], especially since it does not store +/// leaf digests. Instead, [`Full`] should be used whenever a tree with the capability to request +/// arbitrary inner nodes is needed. +/// +/// [`Tree`]: crate::merkle_tree::Tree +/// [`Full`]: crate::merkle_tree::full::Full #[derive(derivative::Derivative)] #[derivative( Clone(bound = "InnerDigest<C>: Clone, M: Clone"), diff --git a/manta-crypto/src/merkle_tree/single_leaf.rs b/manta-crypto/src/merkle_tree/single_leaf.rs index 3b3d9dfe3..bc48673e8 100644 --- a/manta-crypto/src/merkle_tree/single_leaf.rs +++ b/manta-crypto/src/merkle_tree/single_leaf.rs @@ -20,7 +20,7 @@ use crate::merkle_tree::{ capacity, Configuration, InnerDigest, LeafDigest, MerkleTree, Node, Parameters, Parity, Path, Root, Tree, }; -use core::{fmt::Debug, hash::Hash}; +use core::{fmt::Debug, hash::Hash, mem}; /// Single Leaf Merkle Tree Type pub type SingleLeafMerkleTree<C> = MerkleTree<C, SingleLeaf<C>>; @@ -93,6 +93,96 @@ where pub fn leaf_digest(&self) -> Option<&LeafDigest<C>> { self.leaf_digest.as_ref() } + + /// Computes the root of the tree under the assumption that `self.leaf_digest.is_some()` + /// evaluates to `true`. + #[inline] + fn compute_root(&self, parameters: &Parameters<C>) -> Root<C> { + self.path + .root_relative_to(parameters, self.leaf_digest.as_ref().unwrap()) + } + + /* TODO: + pub fn maybe_push_digest<F>( + &mut self, + parameters: &Parameters<C>, + leaf_digest: F, + ) -> Option<bool> + where + F: FnOnce() -> Option<LeafDigest<C>>, + InnerDigest<C>: Clone, + { + // TODO[move]: + use crate::merkle_tree::path_length; + + let index = match self.next_index() { + Some(index) => index, + _ => return Some(false), + }; + + let leaf_digest = leaf_digest()?; + + if index == 0 { + self.leaf_digest = Some(leaf_digest); + self.root = self.compute_root(parameters); + } else { + match index.parity() { + Parity::Left => { + let default_leaf_digest = Default::default(); + let default_inner_digest = Default::default(); + + let mut prev_index = index - 1; + let mut prev_accumulator = parameters.join_leaves( + &self.path.sibling_digest, + self.leaf_digest.as_ref().unwrap(), + ); + + let mut next_index = index; + let mut next_accumulator = + parameters.join_leaves(&leaf_digest, &default_leaf_digest); + + let mut i = 0; + loop { + if prev_index.into_parent() == next_index.into_parent() { + next_accumulator = + parameters.join(&prev_accumulator, &next_accumulator); + break; + } else { + self.path.inner_path[i] = prev_accumulator.clone(); + next_accumulator = + parameters.join(&next_accumulator, &default_inner_digest); + prev_accumulator = prev_index.join( + parameters, + &prev_accumulator, + &self.path.inner_path[i], + ); + } + i += 1; + } + + for j in i..path_length::<C>() { + next_accumulator = next_index.into_parent().join( + parameters, + &next_accumulator, + &self.path.inner_path[j], + ); + } + + self.path.leaf_index = index; + self.root = Root(next_accumulator); + } + Parity::Right => { + self.path.leaf_index = index; + self.path.sibling_digest = + mem::replace(self.leaf_digest.as_mut().unwrap(), leaf_digest); + self.root = self.compute_root(parameters); + } + } + } + + Some(true) + } + */ } impl<C> Tree<C> for SingleLeaf<C> @@ -146,6 +236,8 @@ where let current_leaf_digest = self.leaf_digest.as_ref().unwrap(); + // TODO: Get rid of this clone. + let (mut accumulator, sibling_digest) = match next_index.parity() { Parity::Left => ( parameters.join_leaves(&leaf_digest, &default_leaf_digest), diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index 3d5f43a51..00756c08a 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -23,7 +23,7 @@ use crate::merkle_tree::{ Node, }; use alloc::vec::Vec; -use core::{fmt::Debug, hash::Hash}; +use core::{fmt::Debug, hash::Hash, marker::PhantomData}; /// Merkle Tree Leaf Hash pub trait LeafHash { @@ -62,14 +62,17 @@ pub trait InnerHash { ) -> Self::Output; } -/// Merkle Tree Configuration -pub trait Configuration { +/// Merkle Tree Hash Configuration +pub trait HashConfiguration { /// Leaf Hash Type type LeafHash: LeafHash; /// Inner Hash Type type InnerHash: InnerHash<LeafHash = Self::LeafHash>; +} +/// Merkle Tree Configuration +pub trait Configuration: HashConfiguration { /// Height Type type Height: Copy + Into<usize>; @@ -81,20 +84,51 @@ pub trait Configuration { const HEIGHT: Self::Height; } +/// Configuration Structure +/// +/// Use this `struct` to extend any [`C: HashConfiguration`](HashConfiguration) and a given +/// `const HEIGHT: usize` to a full implementation of [`Configuration`]. +/// +/// # Note +/// +/// Since this `struct` is meant to be used as a type parameter, any values of this type have no +/// meaning, just like values of type [`HashConfiguration`] or [`Configuration`]. +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Config<C, const HEIGHT: usize>(PhantomData<C>) +where + C: HashConfiguration + ?Sized; + +impl<C, const HEIGHT: usize> HashConfiguration for Config<C, HEIGHT> +where + C: HashConfiguration + ?Sized, +{ + type LeafHash = C::LeafHash; + type InnerHash = C::InnerHash; +} + +impl<C, const HEIGHT: usize> Configuration for Config<C, HEIGHT> +where + C: HashConfiguration + ?Sized, +{ + type Height = usize; + + const HEIGHT: Self::Height = HEIGHT; +} + /// Leaf Type -pub type Leaf<C> = <<C as Configuration>::LeafHash as LeafHash>::Leaf; +pub type Leaf<C> = <<C as HashConfiguration>::LeafHash as LeafHash>::Leaf; /// Leaf Hash Parameters Type -pub type LeafHashParamters<C> = <<C as Configuration>::LeafHash as LeafHash>::Parameters; +pub type LeafHashParamters<C> = <<C as HashConfiguration>::LeafHash as LeafHash>::Parameters; /// Leaf Hash Digest Type -pub type LeafDigest<C> = <<C as Configuration>::LeafHash as LeafHash>::Output; +pub type LeafDigest<C> = <<C as HashConfiguration>::LeafHash as LeafHash>::Output; /// Inner Hash Parameters Type -pub type InnerHashParameters<C> = <<C as Configuration>::InnerHash as InnerHash>::Parameters; +pub type InnerHashParameters<C> = <<C as HashConfiguration>::InnerHash as InnerHash>::Parameters; /// Inner Hash Digest Type -pub type InnerDigest<C> = <<C as Configuration>::InnerHash as InnerHash>::Output; +pub type InnerDigest<C> = <<C as HashConfiguration>::InnerHash as InnerHash>::Output; /// Returns the capacity of the merkle tree with the given [`C::HEIGHT`](Configuration::HEIGHT) /// parameter. diff --git a/manta-pay/src/accounting/config.rs b/manta-pay/src/accounting/config.rs index 07ab67ac9..856abbe20 100644 --- a/manta-pay/src/accounting/config.rs +++ b/manta-pay/src/accounting/config.rs @@ -97,11 +97,14 @@ impl ArkMerkleTreeConfiguration for Configuration { const HEIGHT: Self::Height = 20; } -impl merkle_tree::Configuration for Configuration { +impl merkle_tree::HashConfiguration for Configuration { type LeafHash = - <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::Configuration>::LeafHash; + <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::HashConfiguration>::LeafHash; type InnerHash = - <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::Configuration>::InnerHash; + <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::HashConfiguration>::InnerHash; +} + +impl merkle_tree::Configuration for Configuration { type Height = <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::Configuration>::Height; diff --git a/manta-pay/src/crypto/merkle_tree/mod.rs b/manta-pay/src/crypto/merkle_tree/mod.rs index f50f1db8b..fce3c08e7 100644 --- a/manta-pay/src/crypto/merkle_tree/mod.rs +++ b/manta-pay/src/crypto/merkle_tree/mod.rs @@ -161,12 +161,18 @@ where const HEIGHT: Self::Height = C::HEIGHT; } -impl<C> merkle_tree::Configuration for ConfigConverter<C> +impl<C> merkle_tree::HashConfiguration for ConfigConverter<C> where C: Configuration, { type LeafHash = LeafHashConverter<C::Leaf, C::LeafHash>; type InnerHash = InnerHashConverter<C::Leaf, C::LeafHash, C::InnerHash>; +} + +impl<C> merkle_tree::Configuration for ConfigConverter<C> +where + C: Configuration, +{ type Height = C::Height; const HEIGHT: Self::Height = C::HEIGHT; From b4084bf1233915e656491478dd2e9bb0703ce712 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 28 Sep 2021 03:52:16 -0400 Subject: [PATCH 064/275] WIP: rewrite single-leaf MT, start CompressedPath --- manta-crypto/src/merkle_tree/node.rs | 48 +---- manta-crypto/src/merkle_tree/single_leaf.rs | 197 +++++------------- manta-crypto/src/merkle_tree/tree.rs | 210 +++++++++++++++++++- 3 files changed, 261 insertions(+), 194 deletions(-) diff --git a/manta-crypto/src/merkle_tree/node.rs b/manta-crypto/src/merkle_tree/node.rs index 209cde8aa..68ac9aa7b 100644 --- a/manta-crypto/src/merkle_tree/node.rs +++ b/manta-crypto/src/merkle_tree/node.rs @@ -107,26 +107,8 @@ impl Parity { C::InnerHash::join(&parameters.inner, lhs, rhs) } - /// Combines two leaf digests into a new inner digest using `parameters`, choosing the right - /// pair `(center, rhs)` if `self` has left parity or choosing the left pair `(lhs, center)` - /// if `self` has right parity. - #[inline] - pub fn join_opposite_pair<C>( - &self, - parameters: &Parameters<C>, - lhs: &InnerDigest<C>, - center: &InnerDigest<C>, - rhs: &InnerDigest<C>, - ) -> InnerDigest<C> - where - C: Configuration + ?Sized, - { - let (lhs, rhs) = self.triple_order(center, move || lhs, move || rhs); - C::InnerHash::join(&parameters.inner, lhs, rhs) - } - /// Combines two leaf digests into a new inner digest using `parameters`, swapping the order - /// of `lhs` and `rhs` depending on the parity of `self` in its subtree. + /// of `lhs` and `rhs` depending on the parity of `self`. #[inline] pub fn join_leaves<C>( &self, @@ -196,6 +178,12 @@ impl Node { } } + /// Returns `true` if `lhs` and `rhs` are siblings. + #[inline] + pub const fn are_siblings(lhs: &Self, rhs: &Self) -> bool { + lhs.sibling().0 == rhs.0 + } + /// Returns the left child [`Node`] of this node. #[inline] pub const fn left_child(&self) -> Self { @@ -228,7 +216,7 @@ impl Node { } /// Combines two inner digests into a new inner digest using `parameters`, swapping the order - /// of `lhs` and `rhs` depending on the location of `self` in its subtree. + /// of `lhs` and `rhs` depending on the location of `self`. #[inline] pub fn join<C>( &self, @@ -242,26 +230,8 @@ impl Node { self.parity().join(parameters, lhs, rhs) } - /// Combines two leaf digests into a new inner digest using `parameters`, choosing the right - /// pair `(center, rhs)` if `self` has left parity or choosing the left pair `(lhs, center)` - /// if `self` has right parity. - #[inline] - pub fn join_opposite_pair<C>( - &self, - parameters: &Parameters<C>, - lhs: &InnerDigest<C>, - center: &InnerDigest<C>, - rhs: &InnerDigest<C>, - ) -> InnerDigest<C> - where - C: Configuration + ?Sized, - { - self.parity() - .join_opposite_pair(parameters, lhs, center, rhs) - } - /// Combines two leaf digests into a new inner digest using `parameters`, swapping the order - /// of `lhs` and `rhs` depending on the location of `self` in its subtree. + /// of `lhs` and `rhs` depending on the location of `self`. #[inline] pub fn join_leaves<C>( &self, diff --git a/manta-crypto/src/merkle_tree/single_leaf.rs b/manta-crypto/src/merkle_tree/single_leaf.rs index bc48673e8..ce881c88c 100644 --- a/manta-crypto/src/merkle_tree/single_leaf.rs +++ b/manta-crypto/src/merkle_tree/single_leaf.rs @@ -16,6 +16,8 @@ //! Single Leaf Merkle Tree Storage +// TODO: Should we be storing the root? Can we have a version where we don't? + use crate::merkle_tree::{ capacity, Configuration, InnerDigest, LeafDigest, MerkleTree, Node, Parameters, Parity, Path, Root, Tree, @@ -94,95 +96,24 @@ where self.leaf_digest.as_ref() } - /// Computes the root of the tree under the assumption that `self.leaf_digest.is_some()` - /// evaluates to `true`. + /// Returns a shared reference to the current leaf digest. #[inline] - fn compute_root(&self, parameters: &Parameters<C>) -> Root<C> { - self.path - .root_relative_to(parameters, self.leaf_digest.as_ref().unwrap()) + fn leaf_digest_ref(&self) -> &LeafDigest<C> { + self.leaf_digest().unwrap() } - /* TODO: - pub fn maybe_push_digest<F>( - &mut self, - parameters: &Parameters<C>, - leaf_digest: F, - ) -> Option<bool> - where - F: FnOnce() -> Option<LeafDigest<C>>, - InnerDigest<C>: Clone, - { - // TODO[move]: - use crate::merkle_tree::path_length; - - let index = match self.next_index() { - Some(index) => index, - _ => return Some(false), - }; - - let leaf_digest = leaf_digest()?; - - if index == 0 { - self.leaf_digest = Some(leaf_digest); - self.root = self.compute_root(parameters); - } else { - match index.parity() { - Parity::Left => { - let default_leaf_digest = Default::default(); - let default_inner_digest = Default::default(); - - let mut prev_index = index - 1; - let mut prev_accumulator = parameters.join_leaves( - &self.path.sibling_digest, - self.leaf_digest.as_ref().unwrap(), - ); - - let mut next_index = index; - let mut next_accumulator = - parameters.join_leaves(&leaf_digest, &default_leaf_digest); - - let mut i = 0; - loop { - if prev_index.into_parent() == next_index.into_parent() { - next_accumulator = - parameters.join(&prev_accumulator, &next_accumulator); - break; - } else { - self.path.inner_path[i] = prev_accumulator.clone(); - next_accumulator = - parameters.join(&next_accumulator, &default_inner_digest); - prev_accumulator = prev_index.join( - parameters, - &prev_accumulator, - &self.path.inner_path[i], - ); - } - i += 1; - } - - for j in i..path_length::<C>() { - next_accumulator = next_index.into_parent().join( - parameters, - &next_accumulator, - &self.path.inner_path[j], - ); - } - - self.path.leaf_index = index; - self.root = Root(next_accumulator); - } - Parity::Right => { - self.path.leaf_index = index; - self.path.sibling_digest = - mem::replace(self.leaf_digest.as_mut().unwrap(), leaf_digest); - self.root = self.compute_root(parameters); - } - } - } + /// Returns a mutable reference to the current leaf digest. + #[inline] + fn leaf_digest_mut_ref(&mut self) -> &mut LeafDigest<C> { + self.leaf_digest.as_mut().unwrap() + } - Some(true) + /// Computes the root of the tree under the assumption that `self.leaf_digest.is_some()` + /// evaluates to `true`. + #[inline] + fn compute_root(&self, parameters: &Parameters<C>) -> Root<C> { + self.path.root(parameters, self.leaf_digest_ref()) } - */ } impl<C> Tree<C> for SingleLeaf<C> @@ -219,7 +150,7 @@ where where F: FnOnce() -> Option<LeafDigest<C>>, { - let index = match self.next_index() { + let mut index = match self.next_index() { Some(index) => index, _ => return Some(false), }; @@ -227,74 +158,52 @@ where let leaf_digest = leaf_digest()?; if index == 0 { - self.root = self.path.root_relative_to(parameters, &leaf_digest); + self.leaf_digest = Some(leaf_digest); + self.root = self.compute_root(parameters); } else { - let mut next_index = index; - - let default_leaf_digest = Default::default(); - let default_inner_digest = Default::default(); - - let current_leaf_digest = self.leaf_digest.as_ref().unwrap(); - - // TODO: Get rid of this clone. - - let (mut accumulator, sibling_digest) = match next_index.parity() { - Parity::Left => ( - parameters.join_leaves(&leaf_digest, &default_leaf_digest), - default_leaf_digest, - ), - Parity::Right => ( - parameters.join_leaves(current_leaf_digest, &leaf_digest), - current_leaf_digest.clone(), - ), - }; - - let mut prev_index = next_index - 1; - let mut prev_digest = - prev_index.join_leaves(parameters, current_leaf_digest, &self.path.sibling_digest); - - // TODO: Mutate the path in place. + self.path.leaf_index = index; + match index.parity() { + Parity::Left => { + let mut last_index = index - 1; + let mut last_accumulator = parameters.join_leaves( + &self.path.sibling_digest, + &mem::replace(self.leaf_digest.as_mut().unwrap(), leaf_digest), + ); - let inner_path = self - .path - .inner_path - .iter() - .map(|digest| { - if prev_index.into_parent() == next_index.into_parent() { - accumulator = next_index.join_opposite_pair( - parameters, - digest, - &accumulator, - &default_inner_digest, - ); - digest.clone() - } else { - let next_index_parity = next_index.parity(); + self.path.sibling_digest = Default::default(); - let next_inner_path_digest = next_index_parity - .map(|| default_inner_digest.clone(), || prev_digest.clone()); + let mut accumulator = + parameters.join_leaves(self.leaf_digest_ref(), &self.path.sibling_digest); - accumulator = next_index_parity.join_opposite_pair( + let mut i = 0; + while !Node::are_siblings(&last_index.into_parent(), &index.into_parent()) { + last_accumulator = last_index.join( parameters, - &prev_digest, - &accumulator, - &default_inner_digest, + &last_accumulator, + &self.path.inner_path[i], ); - - if prev_index.is_right() { - prev_digest = parameters.join(digest, &prev_digest); - } - - next_inner_path_digest + self.path.inner_path[i] = Default::default(); + accumulator = parameters.join(&accumulator, &self.path.inner_path[i]); + i += 1; } - }) - .collect(); - self.path = Path::new(index, sibling_digest, inner_path); - self.root = Root(accumulator); - } + self.path.inner_path[i] = last_accumulator; + accumulator = parameters.join(&self.path.inner_path[i], &accumulator); - self.leaf_digest = Some(leaf_digest); + self.root = Path::fold( + parameters, + index, + accumulator, + &self.path.inner_path[i + 1..], + ); + } + Parity::Right => { + self.path.sibling_digest = + mem::replace(self.leaf_digest_mut_ref(), leaf_digest); + self.root = self.compute_root(parameters); + } + } + } Some(true) } diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index 00756c08a..ec147eecf 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -22,7 +22,7 @@ use crate::merkle_tree::{ fork::{self, Branch, MergeBranch, Trunk}, Node, }; -use alloc::vec::Vec; +use alloc::{vec, vec::Vec}; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; /// Merkle Tree Leaf Hash @@ -51,6 +51,12 @@ pub trait InnerHash { /// Inner Hash Output Type type Output: Default + PartialEq; + /// Returns `true` if `digest` is the default inner hash output value. + #[inline] + fn is_default(digest: &Self::Output) -> bool { + digest == &Default::default() + } + /// Combines two inner digests into a new inner digest using `parameters`. fn join(parameters: &Self::Parameters, lhs: &Self::Output, rhs: &Self::Output) -> Self::Output; @@ -449,6 +455,11 @@ where C: Configuration + ?Sized, { /// Builds a new [`Path`] from `leaf_index`, `sibling_digest`, and `inner_path`. + /// + /// # Safety + /// + /// In order for paths to compute the correct root, they should always have an `inner_path` + /// with length given by [`path_length`]. #[inline] pub fn new( leaf_index: Node, @@ -462,18 +473,52 @@ where } } + /// Compresses [`self.inner_path`](Self::inner_path) by removing all default values and saving + /// only the positions in the path of those default values. + #[inline] + pub fn compress(self) -> CompressedPath<C> { + let default_inner_digest = Default::default(); + let mut started = false; + let mut sentinel_ranges = Vec::new(); + let inner_path = self + .inner_path + .into_iter() + .enumerate() + .filter_map(|(i, d)| { + if d == default_inner_digest { + if !started { + sentinel_ranges.push(i); + started = true; + } + None + } else { + if started { + sentinel_ranges.push(i); + started = false; + } + Some(d) + } + }) + .collect(); + sentinel_ranges.shrink_to_fit(); + CompressedPath::new( + self.leaf_index, + self.sibling_digest, + inner_path, + sentinel_ranges, + ) + } + /// Computes the root of the merkle tree relative to `leaf_digest` using `parameters`. #[inline] - pub fn root_relative_to( - &self, - parameters: &Parameters<C>, - leaf_digest: &LeafDigest<C>, - ) -> Root<C> { - let mut index = self.leaf_index; - Root(self.inner_path.iter().fold( + pub fn root(&self, parameters: &Parameters<C>, leaf_digest: &LeafDigest<C>) -> Root<C> { + let index = self.leaf_index; + Self::fold( + parameters, + index, index.join_leaves(parameters, leaf_digest, &self.sibling_digest), - move |acc, d| index.into_parent().join(parameters, &acc, d), - )) + &self.inner_path, + ) } /// Returns `true` if `self` is a witness to the fact that `leaf_digest` is stored in a @@ -485,7 +530,7 @@ where root: &Root<C>, leaf_digest: &LeafDigest<C>, ) -> bool { - root == &self.root_relative_to(parameters, leaf_digest) + root == &self.root(parameters, leaf_digest) } /// Returns `true` if `self` is a witness to the fact that `leaf` is stored in a merkle tree @@ -494,6 +539,33 @@ where pub fn verify(&self, parameters: &Parameters<C>, root: &Root<C>, leaf: &Leaf<C>) -> bool { self.verify_digest(parameters, root, &parameters.digest(leaf)) } + + /// Returns the folding algorithm for a path with `index` as its starting index. + #[inline] + pub fn fold_fn<'d>( + parameters: &'d Parameters<C>, + mut index: Node, + ) -> impl 'd + FnMut(InnerDigest<C>, &'d InnerDigest<C>) -> InnerDigest<C> { + move |acc, d| index.into_parent().join(parameters, &acc, d) + } + + /// Folds `iter` into a root using [`fold_fn`](Self::fold_fn), the path folding algorithm. + #[inline] + pub fn fold<'i, I>( + parameters: &'i Parameters<C>, + index: Node, + base: InnerDigest<C>, + iter: I, + ) -> Root<C> + where + InnerDigest<C>: 'i, + I: IntoIterator<Item = &'i InnerDigest<C>>, + { + Root( + iter.into_iter() + .fold(base, Self::fold_fn(parameters, index)), + ) + } } impl<C> Default for Path<C> @@ -509,6 +581,122 @@ where } } +impl<C> From<CompressedPath<C>> for Path<C> +where + C: Configuration + ?Sized, +{ + #[inline] + fn from(path: CompressedPath<C>) -> Self { + path.decompress() + } +} + +/// Compressed Path +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), + Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), + Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), + Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), + PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") +)] +pub struct CompressedPath<C> +where + C: Configuration + ?Sized, +{ + /// Leaf Index + pub leaf_index: Node, + + /// Sibling Digest + pub sibling_digest: LeafDigest<C>, + + /// Inner Path + /// + /// Inner digests are stored from leaf to root, not including the root. + pub inner_path: Vec<InnerDigest<C>>, + + /// Sentinel Ranges + pub sentinel_ranges: Vec<usize>, +} + +impl<C> CompressedPath<C> +where + C: Configuration + ?Sized, +{ + /// Builds a new [`CompressedPath`] from `leaf_index`, `sibling_digest`, `inner_path`, and + /// `sentinel_ranges`. + /// + /// # Safety + /// + /// In order for paths to compute the correct root, they should always have an `inner_path` + /// with length given by [`path_length`]. + /// + /// For compressed paths, we need the `sentinel_ranges` to contain all the ranges which include + /// the default inner hash, and then `inner_path` contains the non-default values. The total + /// number of values represented in this way must equal the [`path_length`]. + #[inline] + pub fn new( + leaf_index: Node, + sibling_digest: LeafDigest<C>, + inner_path: Vec<InnerDigest<C>>, + sentinel_ranges: Vec<usize>, + ) -> Self { + Self { + leaf_index, + sibling_digest, + inner_path, + sentinel_ranges, + } + } + + /// Decompresses a path by re-inserting the default values into [`self.inner_path`] at the + /// indices described by [`self.sentinel_ranges`]. + /// + /// [`self.sentinel_ranges`]: Self::sentinel_ranges + /// [`self.inner_path`]: Self::inner_path + #[inline] + pub fn decompress(mut self) -> Path<C> { + let path_length = path_length::<C>(); + self.inner_path.reserve(path_length); + let mut start = 0; + for (i, index) in self.sentinel_ranges.into_iter().enumerate() { + if i % 2 == 0 { + start = index; + } else { + self.inner_path + .splice(start..start, (start..index).map(|_| Default::default())); + } + } + self.inner_path.resize_with(path_length, Default::default); + Path::new(self.leaf_index, self.sibling_digest, self.inner_path) + } +} + +impl<C> Default for CompressedPath<C> +where + C: Configuration + ?Sized, +{ + #[inline] + fn default() -> Self { + Self::new( + Default::default(), + Default::default(), + Default::default(), + vec![0, path_length::<C>()], + ) + } +} + +impl<C> From<Path<C>> for CompressedPath<C> +where + C: Configuration + ?Sized, +{ + #[inline] + fn from(path: Path<C>) -> Self { + path.compress() + } +} + /// Merkle Tree #[derive(derivative::Derivative)] #[derivative( From 498c98b972a1b141f97da5984c9e715904775e43 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 28 Sep 2021 16:16:59 -0400 Subject: [PATCH 065/275] WIP: add more interfaces to InnerTree, working on Fork impl --- manta-codec/Cargo.toml | 2 +- manta-crypto/src/lib.rs | 1 + manta-crypto/src/merkle_tree/fork.rs | 197 ++++++++++++---- manta-crypto/src/merkle_tree/full.rs | 8 +- manta-crypto/src/merkle_tree/inner_tree.rs | 256 +++++++++++++++++---- manta-crypto/src/merkle_tree/mod.rs | 20 +- manta-crypto/src/merkle_tree/node.rs | 10 +- manta-crypto/src/merkle_tree/tree.rs | 32 ++- 8 files changed, 421 insertions(+), 105 deletions(-) diff --git a/manta-codec/Cargo.toml b/manta-codec/Cargo.toml index d09319cae..6e34c7c5c 100644 --- a/manta-codec/Cargo.toml +++ b/manta-codec/Cargo.toml @@ -36,4 +36,4 @@ ark-serialize = { version = "0.3.0", default-features = false } ark-std = { version = "0.3.0", default-features = false } displaydoc = { version = "0.2.3", default-features = false } manta-util = { path = "../manta-util", default-features = false } -scale-codec = { package = "parity-scale-codec", version = "2.3.0", default-features = false } +scale-codec = { package = "parity-scale-codec", version = "2.3.1", default-features = false } diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index 3ec022eab..a298d400c 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -31,5 +31,6 @@ pub mod set; pub use commitment::prelude::*; pub use ies::prelude::*; +pub use merkle_tree::prelude::*; pub use prf::*; pub use set::prelude::*; diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs index 07871742b..e16669909 100644 --- a/manta-crypto/src/merkle_tree/fork.rs +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -17,15 +17,18 @@ //! Merkle Tree Forks // TODO: Think about whether we want to keep the `raw::MerkleTreePointerFamily` API sealed or not. +// TODO: Implement derive-able traits for these types. extern crate alloc; use crate::merkle_tree::{ - capacity, inner_tree::InnerTree, Configuration, GetPath, GetPathError, InnerDigest, Leaf, - LeafDigest, MerkleTree, Path, Root, Tree, + capacity, + inner_tree::{BTreeMap, InnerMap, InnerTree}, + Configuration, GetPath, GetPathError, InnerDigest, Leaf, LeafDigest, MerkleTree, Path, Root, + Tree, }; use alloc::vec::Vec; -use core::{fmt::Debug, hash::Hash, mem}; +use core::{borrow::Borrow, fmt::Debug, hash::Hash, mem, ops::Deref}; /// Fork-able Merkle Tree pub struct Trunk<C, T, P = raw::SingleThreaded> @@ -73,9 +76,10 @@ where Fork::new(self) } - /// Attaches `fork` to `self` as its new trunk. + /// Tries to attach `fork` to `self` as its new trunk, returning `false` if `fork` has + /// too many leaves to fit in `self`. #[inline] - pub fn attach(&self, fork: &mut Fork<C, T, P>) { + pub fn attach(&self, fork: &mut Fork<C, T, P>) -> bool { fork.attach(self) } @@ -131,6 +135,44 @@ where } } +impl<C, T, P> AsRef<MerkleTree<C, T>> for Trunk<C, T, P> +where + C: Configuration + ?Sized, + T: Tree<C>, + P: raw::MerkleTreePointerFamily<C, T>, +{ + #[inline] + fn as_ref(&self) -> &MerkleTree<C, T> { + self.borrow_base().as_ref() + } +} + +impl<C, T, P> Borrow<MerkleTree<C, T>> for Trunk<C, T, P> +where + C: Configuration + ?Sized, + T: Tree<C>, + P: raw::MerkleTreePointerFamily<C, T>, +{ + #[inline] + fn borrow(&self) -> &MerkleTree<C, T> { + self.borrow_base().borrow() + } +} + +impl<C, T, P> Deref for Trunk<C, T, P> +where + C: Configuration + ?Sized, + T: Tree<C>, + P: raw::MerkleTreePointerFamily<C, T>, +{ + type Target = MerkleTree<C, T>; + + #[inline] + fn deref(&self) -> &Self::Target { + self.borrow_base().deref() + } +} + /// Merkle Tree Fork pub struct Fork<C, T, P = raw::SingleThreaded> where @@ -154,23 +196,28 @@ where /// Builds a new [`Fork`] from `trunk`. #[inline] pub fn new(trunk: &Trunk<C, T, P>) -> Self { - Self::with_leaves(trunk, Default::default()) + Self::with_leaves(trunk, Default::default()).unwrap() } - /// Builds a new [`Fork`] from `trunk` extended by `leaf_digests` + /// Builds a new [`Fork`] from `trunk` extended by `leaf_digests`, returning `None` if + /// appending `leaf_digests` would exceed the capacity of the `trunk`. #[inline] - pub fn with_leaves(trunk: &Trunk<C, T, P>, leaf_digests: Vec<LeafDigest<C>>) -> Self { - Self { + pub fn with_leaves(trunk: &Trunk<C, T, P>, leaf_digests: Vec<LeafDigest<C>>) -> Option<Self> { + Some(Self { base: trunk.downgrade(), - branch: Branch::new(trunk.borrow_base().as_ref(), leaf_digests), - } + branch: Branch::new(trunk.borrow_base().as_ref(), leaf_digests)?, + }) } - /// Attaches this fork to a new `trunk`. + /// Tries to attach this fork to a new `trunk`, returning `false` if `self` has too many leaves + /// to fit in `trunk`. #[inline] - pub fn attach(&mut self, trunk: &Trunk<C, T, P>) { + pub fn attach(&mut self, trunk: &Trunk<C, T, P>) -> bool { + if !self.branch.try_rebase(trunk.borrow_base().as_ref()) { + return false; + } self.base = trunk.downgrade(); - self.branch.rebase(trunk.borrow_base().as_ref()); + true } /// Returns `true` if this fork is attached to some [`Trunk`]. @@ -258,31 +305,33 @@ where /// Merkle Tree Fork Branch #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), - Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), - Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default"), - Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), - Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), - PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") + Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone, M: Clone"), + Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug, M: Debug"), + Default(bound = "M: Default"), + Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq, M: Eq"), + Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash, M: Hash"), + PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq, M: PartialEq") )] -pub struct Branch<C> +pub struct Branch<C, M = BTreeMap<C>> where C: Configuration + ?Sized, + M: InnerMap<C> + Default, { /// Leaf Digests pub leaf_digests: Vec<LeafDigest<C>>, /// Inner Digests - pub inner_digests: InnerTree<C>, + pub inner_digests: InnerTree<C, M>, } -impl<C> Branch<C> +impl<C, M> Branch<C, M> where C: Configuration + ?Sized, + M: InnerMap<C> + Default, { /// Builds a new [`Branch`] from `base` and `leaf_digests`. #[inline] - fn new<T>(base: &MerkleTree<C, T>, leaf_digests: Vec<LeafDigest<C>>) -> Self + fn new<T>(base: &MerkleTree<C, T>, leaf_digests: Vec<LeafDigest<C>>) -> Option<Self> where T: Tree<C>, { @@ -290,17 +339,23 @@ where leaf_digests, inner_digests: Default::default(), }; - this.rebase(base); - this + this.try_rebase(base).then(|| this) } - /// Restarts `self` at a new base. + /// Tries to restart `self` at a new `base` if `base` has enough capacity. #[inline] - fn rebase<T>(&mut self, base: &MerkleTree<C, T>) + fn try_rebase<T>(&mut self, base: &MerkleTree<C, T>) -> bool where T: Tree<C>, { - todo!() + if self.len() + base.len() > capacity::<C>() { + return false; + } + let Self { leaf_digests, .. } = mem::take(self); + for digest in leaf_digests { + self.push_digest(base, || digest); + } + true } /// Computes the length of this branch. @@ -324,18 +379,77 @@ where todo!() } - /// Appends a new `leaf` to this branch, recomputing the relevant inner digests relative to - /// the `base` tree. + /// Appends a new `leaf_digest` to this branch, recomputing the relevant inner digests + /// relative to the `base` tree`. #[inline] - fn push<T>(&mut self, base: &MerkleTree<C, T>, leaf: &Leaf<C>) -> bool + fn push_digest<T, F>(&mut self, base: &MerkleTree<C, T>, leaf_digest: F) -> bool where T: Tree<C>, + F: FnOnce() -> LeafDigest<C>, { - if base.tree.len() + self.len() >= capacity::<C>() { + use super::{inner_tree::InnerNodeIter, Node, Parity}; + + let len = self.len(); + let base_tree_len = base.tree.len(); + let total_len = base_tree_len + len; + if total_len >= capacity::<C>() { return false; } - todo!() + let leaf_digest = leaf_digest(); + + let leaf_index = Node(total_len); + + let first_inner = leaf_index.join_leaves( + &base.parameters, + &leaf_digest, + self.leaf_digests + .get(leaf_index.sibling().0) + .unwrap_or(&Default::default()), + ); + + let default_inner_digest = Default::default(); + + let root = InnerNodeIter::from_leaf::<C>(leaf_index).fold(first_inner, |acc, node| { + let index = node.map_index(); + InnerMap::<C>::set(&mut self.inner_digests.map, index, acc); + // FIXME: This should be unwraping into `base` rather than `default_inner_digest`. + let (lhs, rhs) = match node.parity() { + Parity::Left => ( + self.inner_digests + .map_get(index) + .unwrap_or(&default_inner_digest), + self.inner_digests + .map_get(index + 1) + .unwrap_or(&default_inner_digest), + ), + Parity::Right => ( + self.inner_digests + .map_get(index - 1) + .unwrap_or(&default_inner_digest), + self.inner_digests + .map_get(index) + .unwrap_or(&default_inner_digest), + ), + }; + base.parameters.join(lhs, rhs) + }); + + InnerMap::<C>::set(&mut self.inner_digests.map, 0, root); + + self.leaf_digests.push(leaf_digest); + + true + } + + /// Appends a new `leaf` to this branch, recomputing the relevant inner digests relative to + /// the `base` tree. + #[inline] + fn push<T>(&mut self, base: &MerkleTree<C, T>, leaf: &Leaf<C>) -> bool + where + T: Tree<C>, + { + self.push_digest(base, || base.parameters.digest(leaf)) } /// Merges `self` onto the `base` merkle tree. @@ -352,16 +466,18 @@ where /// /// An type which can only be instantiated by the merkle tree forking implementation, which /// prevents running [`Tree::merge_branch`] on arbitrary user-constructed [`Branch`] values. -pub struct MergeBranch<C>(Branch<C>) +pub struct MergeBranch<C, M = BTreeMap<C>>(Branch<C, M>) where - C: Configuration + ?Sized; + C: Configuration + ?Sized, + M: InnerMap<C> + Default; -impl<C> From<MergeBranch<C>> for Branch<C> +impl<C, M> From<MergeBranch<C, M>> for Branch<C, M> where C: Configuration + ?Sized, + M: InnerMap<C> + Default, { #[inline] - fn from(branch: MergeBranch<C>) -> Self { + fn from(branch: MergeBranch<C, M>) -> Self { branch.0 } } @@ -373,7 +489,6 @@ pub mod raw { rc::{Rc, Weak as WeakRc}, sync::{Arc, Weak as WeakArc}, }; - use core::borrow::Borrow; use manta_util::{create_seal, seal}; create_seal! {} @@ -385,7 +500,9 @@ pub mod raw { T: Tree<C>, { /// Strong Pointer - type Strong: AsRef<MerkleTree<C, T>> + Borrow<MerkleTree<C, T>>; + type Strong: AsRef<MerkleTree<C, T>> + + Borrow<MerkleTree<C, T>> + + Deref<Target = MerkleTree<C, T>>; /// Weak Pointer type Weak; diff --git a/manta-crypto/src/merkle_tree/full.rs b/manta-crypto/src/merkle_tree/full.rs index 5f7a67b88..75f8cf3d0 100644 --- a/manta-crypto/src/merkle_tree/full.rs +++ b/manta-crypto/src/merkle_tree/full.rs @@ -16,6 +16,8 @@ //! Full Merkle Tree Storage +// TODO: Do we allow custom sentinel sources for this tree? + extern crate alloc; use crate::merkle_tree::{ @@ -35,7 +37,7 @@ pub type FullMerkleTree<C, M = BTreeMap<C>> = MerkleTree<C, Full<C, M>>; #[derivative( Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone, M: Clone"), Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug, M: Debug"), - Default(bound = ""), + Default(bound = "M: Default"), Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq, M: Eq"), Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash, M: Hash"), PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq, M: PartialEq") @@ -101,7 +103,7 @@ where impl<C, M> Tree<C> for Full<C, M> where C: Configuration + ?Sized, - M: InnerMap<C>, + M: InnerMap<C> + Default, LeafDigest<C>: Clone, InnerDigest<C>: Clone, { @@ -153,7 +155,7 @@ where #[inline] fn path(&self, parameters: &Parameters<C>, index: usize) -> Result<Path<C>, Self::Error> { let _ = parameters; - if index > 0 && index >= self.len() { + if index > 0 && index >= self.leaf_digests.len() { return Err(()); } let leaf_index = Node(index); diff --git a/manta-crypto/src/merkle_tree/inner_tree.rs b/manta-crypto/src/merkle_tree/inner_tree.rs index 59ee5bca2..af50f09da 100644 --- a/manta-crypto/src/merkle_tree/inner_tree.rs +++ b/manta-crypto/src/merkle_tree/inner_tree.rs @@ -16,11 +16,14 @@ //! Inner Digest Tree +// TODO: Figure out how we want to expose the meaning of `InnerNode` coordinates. We should share +// some of it, to reduce potential duplication. + extern crate alloc; use crate::merkle_tree::{path_length, Configuration, InnerDigest, Node, Parameters, Parity}; use alloc::collections::btree_map; -use core::{fmt::Debug, hash::Hash, iter::FusedIterator, ops::Index}; +use core::{fmt::Debug, hash::Hash, iter::FusedIterator, marker::PhantomData, ops::Index}; #[cfg(feature = "std")] use std::{collections::hash_map, hash::BuildHasher}; @@ -109,9 +112,9 @@ impl InnerNode { (1 << (self.depth + 1)) - 1 } - /// Computes the index into the tree map of `self`. + /// Computes an [`InnerMap`] index for the coordinates represented by `self`. #[inline] - const fn map_index(&self) -> usize { + pub const fn map_index(&self) -> usize { self.depth_starting_index() + self.index.0 } } @@ -181,7 +184,7 @@ impl ExactSizeIterator for InnerNodeIter {} impl FusedIterator for InnerNodeIter {} /// [`InnerTree`] Map Backend -pub trait InnerMap<C>: Default +pub trait InnerMap<C> where C: Configuration + ?Sized, { @@ -192,6 +195,22 @@ where fn set(&mut self, index: usize, inner_digest: InnerDigest<C>); } +impl<C, M> InnerMap<C> for &mut M +where + C: Configuration + ?Sized, + M: InnerMap<C>, +{ + #[inline] + fn get(&self, index: usize) -> Option<&InnerDigest<C>> { + (**self).get(index) + } + + #[inline] + fn set(&mut self, index: usize, inner_digest: InnerDigest<C>) { + (**self).set(index, inner_digest) + } +} + /// B-Tree Map [`InnerTree`] Backend pub type BTreeMap<C> = btree_map::BTreeMap<usize, InnerDigest<C>>; @@ -232,6 +251,66 @@ where } } +/// [`InnerTree`] Sentinel Source Tree Backend +pub trait SentinelSource<C> +where + C: Configuration + ?Sized, +{ + /// Returns the sentinel value at the location `index` of the tree. + fn get(&self, index: usize) -> &InnerDigest<C>; +} + +impl<C, S> SentinelSource<C> for &S +where + C: Configuration + ?Sized, + S: SentinelSource<C>, +{ + #[inline] + fn get(&self, index: usize) -> &InnerDigest<C> { + (**self).get(index) + } +} + +impl<C, S> SentinelSource<C> for &mut S +where + C: Configuration + ?Sized, + S: SentinelSource<C>, +{ + #[inline] + fn get(&self, index: usize) -> &InnerDigest<C> { + (**self).get(index) + } +} + +/// Sentinel Source for a Single Sentinel Value +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "InnerDigest<C>: Clone"), + Copy(bound = "InnerDigest<C>: Copy"), + Debug(bound = "InnerDigest<C>: Debug"), + Default(bound = ""), + Eq(bound = "InnerDigest<C>: Eq"), + Hash(bound = "InnerDigest<C>: Hash"), + PartialEq(bound = "InnerDigest<C>: PartialEq") +)] +pub struct Sentinel<C>( + /// Sentinel Value + pub InnerDigest<C>, +) +where + C: Configuration + ?Sized; + +impl<C> SentinelSource<C> for Sentinel<C> +where + C: Configuration + ?Sized, +{ + #[inline] + fn get(&self, index: usize) -> &InnerDigest<C> { + let _ = index; + &self.0 + } +} + /// Inner Tree /// /// Tree data-structure for storing the inner digests of a merkle tree. @@ -246,17 +325,18 @@ where /// [`Full`]: crate::merkle_tree::full::Full #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "InnerDigest<C>: Clone, M: Clone"), - Debug(bound = "InnerDigest<C>: Debug, M: Debug"), - Default(bound = ""), - Eq(bound = "InnerDigest<C>: Eq, M: Eq"), - Hash(bound = "InnerDigest<C>: Hash, M: Hash"), - PartialEq(bound = "InnerDigest<C>: PartialEq, M: PartialEq") + Clone(bound = "M: Clone, S: Clone"), + Debug(bound = "M: Debug, S: Debug"), + Default(bound = "M: Default, S: Default"), + Eq(bound = "M: Eq, S: Eq"), + Hash(bound = "M: Hash, S: Hash"), + PartialEq(bound = "M: PartialEq, S: PartialEq") )] -pub struct InnerTree<C, M = BTreeMap<C>> +pub struct InnerTree<C, M = BTreeMap<C>, S = Sentinel<C>> where C: Configuration + ?Sized, M: InnerMap<C>, + S: SentinelSource<C>, { /// Inner Digest Map /// @@ -266,41 +346,93 @@ where /// node is given by its layer in the tree starting from `depth := -1` at the root increasing /// downwards towards the leaves. The `index` of a node is its position from left to right /// along a layer in the tree. See [`InnerNode`] for more details. - map: M, + pub(super) map: M, - /// Inner Digest Default Value - default: InnerDigest<C>, + /// Sentinel Source + /// + /// The background tree for sentinel values. + pub(super) sentinel_source: S, + + /// Type Parameter Marker + __: PhantomData<C>, } -impl<C, M> InnerTree<C, M> +impl<C, M, S> InnerTree<C, M, S> where C: Configuration + ?Sized, M: InnerMap<C>, + S: SentinelSource<C>, { /// Builds a new [`InnerTree`]. #[inline] - pub fn new() -> Self { + pub fn new() -> Self + where + M: Default, + S: Default, + { Default::default() } - /// Returns the inner digest at `node` or the default value if the inner digest is missing. + /// Builds a new [`InnerTree`] with the given inner `map`. #[inline] - fn map_get(&self, index: usize) -> &InnerDigest<C> { - self.map.get(index).unwrap_or(&self.default) + pub fn with_map(map: M) -> Self + where + S: Default, + { + Self::with_map_and_sentinel(map, Default::default()) } - /// Returns a reference to the root inner digest. + /// Builds a new [`InnerTree`] with the given `sentinel_source`. #[inline] - pub fn root(&self) -> &InnerDigest<C> { - self.map_get(0) + pub fn with_sentinel(sentinel_source: S) -> Self + where + M: Default, + { + Self::with_map_and_sentinel(Default::default(), sentinel_source) } - /// Returns the inner digest at `node` or the default value if the inner digest is missing. + /// Builds a new [`InnerTree`] with the given `map` and `sentinel_source`. #[inline] - pub fn get(&self, node: InnerNode) -> &InnerDigest<C> { + pub fn with_map_and_sentinel(map: M, sentinel_source: S) -> Self { + Self { + map, + sentinel_source, + __: PhantomData, + } + } + + /// Tries to get the inner digest at `node`, returning `None` if the inner digest is missing. + #[inline] + pub fn get(&self, node: InnerNode) -> Option<&InnerDigest<C>> { self.map_get(node.map_index()) } + /// Returns the inner digest at `node` or a sentinel value if the inner digest is missing. + #[inline] + pub fn get_or_sentinel(&self, node: InnerNode) -> &InnerDigest<C> { + self.map_get_or_sentinel(node.map_index()) + } + + /// Tries to return the inner digest at `index`, returning `None` if the inner digest is + /// missing. + #[inline] + pub fn map_get(&self, index: usize) -> Option<&InnerDigest<C>> { + self.map.get(index) + } + + /// Returns the inner digest at `index` or a sentinel value if the inner digest is missing. + #[inline] + pub fn map_get_or_sentinel(&self, index: usize) -> &InnerDigest<C> { + self.map_get(index) + .unwrap_or_else(move || self.sentinel_source.get(index)) + } + + /// Returns a reference to the root inner digest. + #[inline] + pub fn root(&self) -> &InnerDigest<C> { + self.map_get_or_sentinel(0) + } + /// Inserts the new `inner_digest` at `node` in the tree, and returns a reference to /// `inner_digest` and its sibling in the tree in parity order. #[inline] @@ -312,8 +444,14 @@ where let index = node.map_index(); self.map.set(index, inner_digest); match node.parity() { - Parity::Left => (self.map_get(index), self.map_get(index + 1)), - Parity::Right => (self.map_get(index - 1), self.map_get(index)), + Parity::Left => ( + self.map_get_or_sentinel(index), + self.map_get_or_sentinel(index + 1), + ), + Parity::Right => ( + self.map_get_or_sentinel(index - 1), + self.map_get_or_sentinel(index), + ), } } @@ -354,69 +492,99 @@ where /// Computes the inner path starting from `node`. #[inline] - pub fn inner_path(&self, node: InnerNode) -> InnerPathIter<C, M> { + pub fn inner_path(&self, node: InnerNode) -> InnerPathIter<C, M, S> { InnerPathIter::new(self, node.iter()) } /// Computes the inner path of the leaf given by `leaf_index`. #[inline] - pub fn inner_path_for_leaf(&self, leaf_index: Node) -> InnerPathIter<C, M> { + pub fn inner_path_for_leaf(&self, leaf_index: Node) -> InnerPathIter<C, M, S> { InnerPathIter::new(self, InnerNodeIter::from_leaf::<C>(leaf_index)) } } -impl<C, M> Index<InnerNode> for InnerTree<C, M> +impl<C, M, S> Index<InnerNode> for InnerTree<C, M, S> where C: Configuration + ?Sized, M: InnerMap<C>, + S: SentinelSource<C>, { type Output = InnerDigest<C>; #[inline] fn index(&self, index: InnerNode) -> &Self::Output { - self.get(index) + self.get_or_sentinel(index) + } +} + +impl<C, M, S> Index<usize> for InnerTree<C, M, S> +where + C: Configuration + ?Sized, + M: InnerMap<C>, + S: SentinelSource<C>, +{ + type Output = InnerDigest<C>; + + #[inline] + fn index(&self, index: usize) -> &Self::Output { + self.map_get_or_sentinel(index) + } +} + +impl<C, M, S> SentinelSource<C> for InnerTree<C, M, S> +where + C: Configuration + ?Sized, + M: InnerMap<C>, + S: SentinelSource<C>, +{ + #[inline] + fn get(&self, index: usize) -> &InnerDigest<C> { + self.map_get_or_sentinel(index) } } /// Inner Path Iterator #[derive(derivative::Derivative)] #[derivative( - Copy, - Clone, - Debug(bound = "InnerDigest<C>: Debug, M: Debug"), - Eq(bound = "InnerDigest<C>: Eq, M: Eq"), - Hash(bound = "InnerDigest<C>: Hash, M: Hash"), - PartialEq(bound = "InnerDigest<C>: PartialEq, M: PartialEq") + Copy(bound = ""), + Clone(bound = ""), + Debug(bound = "M: Debug, S: Debug"), + Eq(bound = "M: Eq, S: Eq"), + Hash(bound = "M: Hash, S: Hash"), + PartialEq(bound = "M: PartialEq, S: PartialEq") )] -pub struct InnerPathIter<'t, C, M = BTreeMap<C>> +pub struct InnerPathIter<'t, C, M = BTreeMap<C>, S = Sentinel<C>> where C: Configuration + ?Sized, M: InnerMap<C>, + S: SentinelSource<C>, { /// Inner Tree - inner_tree: &'t InnerTree<C, M>, + inner_tree: &'t InnerTree<C, M, S>, /// Inner Node Iterator iter: InnerNodeIter, } -impl<'t, C, M> InnerPathIter<'t, C, M> +impl<'t, C, M, S> InnerPathIter<'t, C, M, S> where C: Configuration + ?Sized, M: InnerMap<C>, + S: SentinelSource<C>, { /// Builds a new [`InnerPathIter`] for `inner_tree` using `iter`. #[inline] - fn new(inner_tree: &'t InnerTree<C, M>, iter: InnerNodeIter) -> Self { + fn new(inner_tree: &'t InnerTree<C, M, S>, iter: InnerNodeIter) -> Self { Self { inner_tree, iter } } } // TODO: Add all methods which can be optimized. -impl<'t, C, M> Iterator for InnerPathIter<'t, C, M> +impl<'t, C, M, S> Iterator for InnerPathIter<'t, C, M, S> where C: Configuration + ?Sized, M: InnerMap<C>, + S: SentinelSource<C>, { type Item = &'t InnerDigest<C>; @@ -431,16 +599,18 @@ where } } -impl<'t, C, M> ExactSizeIterator for InnerPathIter<'t, C, M> +impl<'t, C, M, S> ExactSizeIterator for InnerPathIter<'t, C, M, S> where C: Configuration + ?Sized, M: InnerMap<C>, + S: SentinelSource<C>, { } -impl<'t, C, M> FusedIterator for InnerPathIter<'t, C, M> +impl<'t, C, M, S> FusedIterator for InnerPathIter<'t, C, M, S> where C: Configuration + ?Sized, M: InnerMap<C>, + S: SentinelSource<C>, { } diff --git a/manta-crypto/src/merkle_tree/mod.rs b/manta-crypto/src/merkle_tree/mod.rs index f39deebc9..272da6ca6 100644 --- a/manta-crypto/src/merkle_tree/mod.rs +++ b/manta-crypto/src/merkle_tree/mod.rs @@ -16,13 +16,14 @@ //! Merkle Trees -// TODO: Should `Leaf` move into `Tree`/`Configuration` since we might want the tree to have -// special kinds of leaf input (metadata along with just the digest)? -// TODO: Implement [`crate::VerifiedSet`] for [`MerkleTree`]? -// TODO: Maybe we should require `INNER_HEIGHT` instead of `HEIGHT` so that we don't have to rely -// on the user to check that `HEIGHT >= 2`. -// TODO: Look into optimizations related to default values. Ex: computing the default values once -// and caching them in the tree storage? +// TODO: Should `Leaf` move into `Tree`/`Configuration` since we might want the tree to have +// special kinds of leaf input (metadata along with just the digest)? +// TODO: Implement [`crate::VerifiedSet`] for [`MerkleTree`]? +// TODO: Maybe we should require `INNER_HEIGHT` instead of `HEIGHT` so that we don't have to rely +// on the user to check that `HEIGHT >= 2`. +// TODO: Look into optimizations related to default values. Ex: computing the default values once +// and caching them in the tree storage? +// FIXME: Get rid of as many `pub(super)` declarations as we can. mod node; mod tree; @@ -38,3 +39,8 @@ pub mod test; pub use node::*; pub use tree::*; + +pub(crate) mod prelude { + #[doc(inline)] + pub use super::full::FullMerkleTree; +} diff --git a/manta-crypto/src/merkle_tree/node.rs b/manta-crypto/src/merkle_tree/node.rs index 68ac9aa7b..3309ec358 100644 --- a/manta-crypto/src/merkle_tree/node.rs +++ b/manta-crypto/src/merkle_tree/node.rs @@ -16,7 +16,7 @@ //! Merkle Tree Node Abstractions -use crate::merkle_tree::{Configuration, InnerDigest, InnerHash, LeafDigest, Parameters}; +use crate::merkle_tree::{HashConfiguration, InnerDigest, InnerHash, LeafDigest, Parameters}; use core::{ iter::FusedIterator, ops::{Add, Sub}, @@ -101,7 +101,7 @@ impl Parity { rhs: &InnerDigest<C>, ) -> InnerDigest<C> where - C: Configuration + ?Sized, + C: HashConfiguration + ?Sized, { let (lhs, rhs) = self.order(lhs, rhs); C::InnerHash::join(&parameters.inner, lhs, rhs) @@ -117,7 +117,7 @@ impl Parity { rhs: &LeafDigest<C>, ) -> InnerDigest<C> where - C: Configuration + ?Sized, + C: HashConfiguration + ?Sized, { let (lhs, rhs) = self.order(lhs, rhs); C::InnerHash::join_leaves(&parameters.inner, lhs, rhs) @@ -225,7 +225,7 @@ impl Node { rhs: &InnerDigest<C>, ) -> InnerDigest<C> where - C: Configuration + ?Sized, + C: HashConfiguration + ?Sized, { self.parity().join(parameters, lhs, rhs) } @@ -240,7 +240,7 @@ impl Node { rhs: &LeafDigest<C>, ) -> InnerDigest<C> where - C: Configuration + ?Sized, + C: HashConfiguration + ?Sized, { self.parity().join_leaves(parameters, lhs, rhs) } diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index ec147eecf..c3dbc2a78 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -16,10 +16,14 @@ //! Merkle Tree Abstractions +// TODO: Should we get rid of the `H > 2` requirement, and find a way to give correct +// implementations for the trivial tree sizes? + extern crate alloc; use crate::merkle_tree::{ fork::{self, Branch, MergeBranch, Trunk}, + inner_tree::InnerMap, Node, }; use alloc::{vec, vec::Vec}; @@ -138,6 +142,8 @@ pub type InnerDigest<C> = <<C as HashConfiguration>::InnerHash as InnerHash>::Ou /// Returns the capacity of the merkle tree with the given [`C::HEIGHT`](Configuration::HEIGHT) /// parameter. +/// +/// The capacity of a merkle tree with height `H` is `2^(H-1)`. #[inline] pub fn capacity<C>() -> usize where @@ -148,6 +154,8 @@ where /// Returns the path length of the merkle tree with the given [`C::HEIGHT`](Configuration::HEIGHT) /// parameter. +/// +/// The path length of a merkle tree with height `H` is `H - 2`. #[inline] pub fn path_length<C>() -> usize where @@ -296,7 +304,10 @@ where /// many leaves, into this method, so any implementation is allowed to panic on invalid /// `branch` values as long as it does not panic on any valid `branch` values. #[inline] - fn merge_branch(&mut self, parameters: &Parameters<C>, branch: MergeBranch<C>) { + fn merge_branch<M>(&mut self, parameters: &Parameters<C>, branch: MergeBranch<C, M>) + where + M: InnerMap<C> + Default, + { assert!(self .extend_digests(parameters, Branch::from(branch).leaf_digests) .is_ok()) @@ -352,7 +363,7 @@ where )] pub struct Parameters<C> where - C: Configuration + ?Sized, + C: HashConfiguration + ?Sized, { /// Leaf Hash Parameters pub leaf: LeafHashParamters<C>, @@ -363,7 +374,7 @@ where impl<C> Parameters<C> where - C: Configuration + ?Sized, + C: HashConfiguration + ?Sized, { /// Builds a new [`Parameters`] from `leaf` and `inner` parameters. #[inline] @@ -392,7 +403,10 @@ where /// Verify that `path` witnesses the fact that `leaf` is a member of a merkle tree with the /// given `root`. #[inline] - pub fn verify_path(&self, path: &Path<C>, root: &Root<C>, leaf: &Leaf<C>) -> bool { + pub fn verify_path(&self, path: &Path<C>, root: &Root<C>, leaf: &Leaf<C>) -> bool + where + C: Configuration, + { path.verify(self, root, leaf) } } @@ -413,11 +427,11 @@ pub struct Root<C>( pub InnerDigest<C>, ) where - C: Configuration + ?Sized; + C: HashConfiguration + ?Sized; impl<C> AsRef<InnerDigest<C>> for Root<C> where - C: Configuration + ?Sized, + C: HashConfiguration + ?Sized, { #[inline] fn as_ref(&self) -> &InnerDigest<C> { @@ -780,6 +794,12 @@ where self.tree.is_empty() } + /// Returns the number of leaves that can fit in this merkle tree. + #[inline] + pub fn capacity(&self) -> usize { + capacity::<C>() + } + /// Returns the [`Root`] of the merkle tree. #[inline] pub fn root(&self) -> Root<C> { From b13edca7b95978fab6cb82e588c5249912fdfe5e Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 29 Sep 2021 00:52:45 -0400 Subject: [PATCH 066/275] WIP: start partial MT impl for fork mechanism --- manta-crypto/src/lib.rs | 1 - manta-crypto/src/merkle_tree/fork.rs | 11 +- manta-crypto/src/merkle_tree/inner_tree.rs | 91 +++++++++- manta-crypto/src/merkle_tree/mod.rs | 6 +- manta-crypto/src/merkle_tree/node.rs | 23 +-- manta-crypto/src/merkle_tree/partial.rs | 194 +++++++++++++++++++++ manta-crypto/src/merkle_tree/tree.rs | 8 +- 7 files changed, 304 insertions(+), 30 deletions(-) create mode 100644 manta-crypto/src/merkle_tree/partial.rs diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index a298d400c..3ec022eab 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -31,6 +31,5 @@ pub mod set; pub use commitment::prelude::*; pub use ies::prelude::*; -pub use merkle_tree::prelude::*; pub use prf::*; pub use set::prelude::*; diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs index e16669909..3bb7ee647 100644 --- a/manta-crypto/src/merkle_tree/fork.rs +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -18,12 +18,13 @@ // TODO: Think about whether we want to keep the `raw::MerkleTreePointerFamily` API sealed or not. // TODO: Implement derive-able traits for these types. +// TODO: See if we can get rid of the smart pointer logic. extern crate alloc; use crate::merkle_tree::{ capacity, - inner_tree::{BTreeMap, InnerMap, InnerTree}, + inner_tree::{BTreeMap, InnerMap, PartialInnerTree}, Configuration, GetPath, GetPathError, InnerDigest, Leaf, LeafDigest, MerkleTree, Path, Root, Tree, }; @@ -321,7 +322,7 @@ where pub leaf_digests: Vec<LeafDigest<C>>, /// Inner Digests - pub inner_digests: InnerTree<C, M>, + pub inner_digests: PartialInnerTree<C, M>, } impl<C, M> Branch<C, M> @@ -387,6 +388,7 @@ where T: Tree<C>, F: FnOnce() -> LeafDigest<C>, { + /* TODO: use super::{inner_tree::InnerNodeIter, Node, Parity}; let len = self.len(); @@ -410,6 +412,8 @@ where let default_inner_digest = Default::default(); + let current_base_path = base.current_path(); + let root = InnerNodeIter::from_leaf::<C>(leaf_index).fold(first_inner, |acc, node| { let index = node.map_index(); InnerMap::<C>::set(&mut self.inner_digests.map, index, acc); @@ -438,8 +442,9 @@ where InnerMap::<C>::set(&mut self.inner_digests.map, 0, root); self.leaf_digests.push(leaf_digest); - true + */ + todo!() } /// Appends a new `leaf` to this branch, recomputing the relevant inner digests relative to diff --git a/manta-crypto/src/merkle_tree/inner_tree.rs b/manta-crypto/src/merkle_tree/inner_tree.rs index af50f09da..c4b7e2740 100644 --- a/manta-crypto/src/merkle_tree/inner_tree.rs +++ b/manta-crypto/src/merkle_tree/inner_tree.rs @@ -21,7 +21,7 @@ extern crate alloc; -use crate::merkle_tree::{path_length, Configuration, InnerDigest, Node, Parameters, Parity}; +use crate::merkle_tree::{path_length, Configuration, InnerDigest, Node, Parameters, Parity, Path}; use alloc::collections::btree_map; use core::{fmt::Debug, hash::Hash, iter::FusedIterator, marker::PhantomData, ops::Index}; @@ -346,12 +346,12 @@ where /// node is given by its layer in the tree starting from `depth := -1` at the root increasing /// downwards towards the leaves. The `index` of a node is its position from left to right /// along a layer in the tree. See [`InnerNode`] for more details. - pub(super) map: M, + map: M, /// Sentinel Source /// /// The background tree for sentinel values. - pub(super) sentinel_source: S, + sentinel_source: S, /// Type Parameter Marker __: PhantomData<C>, @@ -543,6 +543,91 @@ where } } +/// Partial Inner Tree +/// +/// Tree data-structure for storing a subset of the inner digests of a merkle tree. +/// +/// # Implementation Note +/// +/// This type intentionally lacks an implementation of [`Tree`], especially since it does not store +/// leaf digests. +/// +/// [`Tree`]: crate::merkle_tree::Tree +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "M: Clone, S: Clone"), + Debug(bound = "M: Debug, S: Debug"), + Default(bound = "M: Default, S: Default"), + Eq(bound = "M: Eq, S: Eq"), + Hash(bound = "M: Hash, S: Hash"), + PartialEq(bound = "M: PartialEq, S: PartialEq") +)] +pub struct PartialInnerTree<C, M = BTreeMap<C>, S = Sentinel<C>> +where + C: Configuration + ?Sized, + M: InnerMap<C>, + S: SentinelSource<C>, +{ + /// Inner Tree + inner_tree: InnerTree<C, M, S>, +} + +impl<C, M, S> PartialInnerTree<C, M, S> +where + C: Configuration + ?Sized, + M: InnerMap<C>, + S: SentinelSource<C>, +{ + /// Builds a [`PartialInnerTree`] from `path` treated as the current path of the tree. + #[inline] + pub fn from_current(path: Path<C>) -> Self { + todo!() + } + + /// Tries to get the inner digest at `node`, returning `None` if the inner digest is missing. + #[inline] + pub fn get(&self, node: InnerNode) -> Option<&InnerDigest<C>> { + self.inner_tree.get(node) + } + + /// Returns the inner digest at `node` or a sentinel value if the inner digest is missing. + #[inline] + fn get_or_sentinel(&self, node: InnerNode) -> &InnerDigest<C> { + self.inner_tree.get_or_sentinel(node) + } + + /// Tries to return the inner digest at `index`, returning `None` if the inner digest is + /// missing. + #[inline] + pub fn map_get(&self, index: usize) -> Option<&InnerDigest<C>> { + self.inner_tree.map_get(index) + } + + /// Returns the inner digest at `index` or a sentinel value if the inner digest is missing. + #[inline] + fn map_get_or_sentinel(&self, index: usize) -> &InnerDigest<C> { + self.inner_tree.map_get_or_sentinel(index) + } + + /// Returns a reference to the root inner digest. + #[inline] + pub fn root(&self) -> &InnerDigest<C> { + self.inner_tree.root() + } +} + +impl<C, M, S> From<InnerTree<C, M, S>> for PartialInnerTree<C, M, S> +where + C: Configuration + ?Sized, + M: InnerMap<C>, + S: SentinelSource<C>, +{ + #[inline] + fn from(inner_tree: InnerTree<C, M, S>) -> Self { + Self { inner_tree } + } +} + /// Inner Path Iterator #[derive(derivative::Derivative)] #[derivative( diff --git a/manta-crypto/src/merkle_tree/mod.rs b/manta-crypto/src/merkle_tree/mod.rs index 272da6ca6..e2169bcd3 100644 --- a/manta-crypto/src/merkle_tree/mod.rs +++ b/manta-crypto/src/merkle_tree/mod.rs @@ -31,6 +31,7 @@ mod tree; pub mod fork; pub mod full; pub mod inner_tree; +pub mod partial; pub mod single_leaf; #[cfg(feature = "test")] @@ -39,8 +40,3 @@ pub mod test; pub use node::*; pub use tree::*; - -pub(crate) mod prelude { - #[doc(inline)] - pub use super::full::FullMerkleTree; -} diff --git a/manta-crypto/src/merkle_tree/node.rs b/manta-crypto/src/merkle_tree/node.rs index 3309ec358..ea9476762 100644 --- a/manta-crypto/src/merkle_tree/node.rs +++ b/manta-crypto/src/merkle_tree/node.rs @@ -77,20 +77,6 @@ impl Parity { } } - /// Returns the `center` placed in the pair at the location given by `self`, placing `lhs` and - /// `rhs` in the left or right empty slot of the pair respectively. - #[inline] - pub fn triple_order<T, L, R>(&self, center: T, lhs: L, rhs: R) -> (T, T) - where - L: FnOnce() -> T, - R: FnOnce() -> T, - { - match self { - Self::Left => (center, rhs()), - Self::Right => (lhs(), center), - } - } - /// Combines two inner digests into a new inner digest using `parameters`, swapping the order /// of `lhs` and `rhs` depending on the parity of `self` in its subtree. #[inline] @@ -190,12 +176,19 @@ impl Node { Self(self.0 << 1) } - /// Returns the right child [`Node`] fo this node. + /// Returns the right child [`Node`] of this node. #[inline] pub const fn right_child(&self) -> Self { Self(self.left_child().0 + 1) } + /// Returns the [`Node`] children of this node. + #[inline] + pub const fn children(&self) -> (Self, Self) { + let left_child = self.left_child(); + (left_child, Self(left_child.0 + 1)) + } + /// Returns the parent [`Node`] of this node. #[inline] pub const fn parent(&self) -> Self { diff --git a/manta-crypto/src/merkle_tree/partial.rs b/manta-crypto/src/merkle_tree/partial.rs new file mode 100644 index 000000000..f8e08283a --- /dev/null +++ b/manta-crypto/src/merkle_tree/partial.rs @@ -0,0 +1,194 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Partial Merkle Tree Storage + +// TODO: Do we allow custom sentinel sources for this tree? + +extern crate alloc; + +use crate::merkle_tree::{ + capacity, + inner_tree::{BTreeMap, InnerMap, PartialInnerTree}, + Configuration, GetPath, InnerDigest, LeafDigest, MerkleTree, Node, Parameters, Path, Root, + Tree, +}; +use alloc::vec::Vec; +use core::{fmt::Debug, hash::Hash}; + +/// Partial Merkle Tree Type +pub type PartialMerkleTree<C, M = BTreeMap<C>> = MerkleTree<C, Partial<C, M>>; + +/// Partial Merkle Tree Backing Structure +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone, M: Clone"), + Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug, M: Debug"), + Default(bound = "M: Default"), + Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq, M: Eq"), + Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash, M: Hash"), + PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq, M: PartialEq") +)] +pub struct Partial<C, M = BTreeMap<C>> +where + C: Configuration + ?Sized, + M: InnerMap<C>, +{ + /// Leaf Digests + leaf_digests: Vec<LeafDigest<C>>, + + /// Inner Digests + inner_digests: PartialInnerTree<C, M>, +} + +impl<C, M> Partial<C, M> +where + C: Configuration + ?Sized, + M: InnerMap<C>, +{ + /// Returns the leaf digests currently stored in the merkle tree. + #[inline] + pub fn leaf_digests(&self) -> &[LeafDigest<C>] { + &self.leaf_digests + } + + /// Returns a reference to the root inner digest. + #[inline] + pub fn root(&self) -> &InnerDigest<C> { + self.inner_digests.root() + } + + /// Returns the sibling leaf node to `index`. + #[inline] + fn get_leaf_sibling(&self, index: Node) -> Option<&LeafDigest<C>> { + /* TODO: + // TODO: Add `Index` methods to accept `Node` as indices. + self.leaf_digests.get(index.sibling().0) + */ + todo!() + } + + /// Appends a `leaf_digest` with index given by `leaf_index` into the tree. + #[inline] + fn push_leaf_digest( + &mut self, + parameters: &Parameters<C>, + leaf_index: Node, + leaf_digest: LeafDigest<C>, + ) { + /* TODO: + self.inner_digests.insert( + parameters, + leaf_index, + leaf_index.join_leaves( + parameters, + &leaf_digest, + self.get_leaf_sibling(leaf_index) + .unwrap_or(&Default::default()), + ), + ); + self.leaf_digests.push(leaf_digest); + */ + todo!() + } +} + +impl<C, M> Tree<C> for Partial<C, M> +where + C: Configuration + ?Sized, + M: InnerMap<C> + Default, + LeafDigest<C>: Clone, + InnerDigest<C>: Clone, +{ + #[inline] + fn new(parameters: &Parameters<C>) -> Self { + let _ = parameters; + Default::default() + } + + #[inline] + fn len(&self) -> usize { + /* TODO: + self.leaf_digests.len() + */ + todo!() + } + + #[inline] + fn root(&self, parameters: &Parameters<C>) -> Root<C> { + /* TODO: + let _ = parameters; + Root(self.root().clone()) + */ + todo!() + } + + #[inline] + fn current_path(&self, parameters: &Parameters<C>) -> Path<C> { + /* TODO: + self.path(parameters, 0).unwrap() + */ + todo!() + } + + #[inline] + fn maybe_push_digest<F>(&mut self, parameters: &Parameters<C>, leaf_digest: F) -> Option<bool> + where + F: FnOnce() -> Option<LeafDigest<C>>, + { + /* TODO: + let len = self.len(); + if len >= capacity::<C>() { + return Some(false); + } + self.push_leaf_digest(parameters, Node(len), leaf_digest()?); + Some(true) + */ + todo!() + } +} + +impl<C, M> GetPath<C> for Partial<C, M> +where + C: Configuration + ?Sized, + M: InnerMap<C>, + LeafDigest<C>: Clone, + InnerDigest<C>: Clone, +{ + type Error = (); + + #[inline] + fn path(&self, parameters: &Parameters<C>, index: usize) -> Result<Path<C>, Self::Error> { + /* TODO: + let _ = parameters; + if index > 0 && index >= self.leaf_digests.len() { + return Err(()); + } + let leaf_index = Node(index); + Ok(Path::new( + leaf_index, + self.get_leaf_sibling(leaf_index) + .map(Clone::clone) + .unwrap_or_default(), + self.inner_digests + .inner_path_for_leaf(leaf_index) + .cloned() + .collect(), + )) + */ + todo!() + } +} diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index c3dbc2a78..c5f034a4c 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -18,6 +18,8 @@ // TODO: Should we get rid of the `H > 2` requirement, and find a way to give correct // implementations for the trivial tree sizes? +// TODO: Add "copy-on-write" adapters for `Root` and `Path`, and see if we can incorporate them +// into `Tree`. extern crate alloc; @@ -601,7 +603,7 @@ where { #[inline] fn from(path: CompressedPath<C>) -> Self { - path.decompress() + path.uncompress() } } @@ -663,13 +665,13 @@ where } } - /// Decompresses a path by re-inserting the default values into [`self.inner_path`] at the + /// Uncompresses a path by re-inserting the default values into [`self.inner_path`] at the /// indices described by [`self.sentinel_ranges`]. /// /// [`self.sentinel_ranges`]: Self::sentinel_ranges /// [`self.inner_path`]: Self::inner_path #[inline] - pub fn decompress(mut self) -> Path<C> { + pub fn uncompress(mut self) -> Path<C> { let path_length = path_length::<C>(); self.inner_path.reserve(path_length); let mut start = 0; From 30eaa0fc9b9167fdc7f33b86b996a094b17a1d65 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 29 Sep 2021 19:34:35 -0400 Subject: [PATCH 067/275] WIP: working on CurrentPath abstraction --- manta-crypto/src/lib.rs | 2 + manta-crypto/src/merkle_tree/fork.rs | 2 - manta-crypto/src/merkle_tree/full.rs | 13 +- manta-crypto/src/merkle_tree/inner_tree.rs | 6 +- manta-crypto/src/merkle_tree/mod.rs | 2 + manta-crypto/src/merkle_tree/partial.rs | 8 +- manta-crypto/src/merkle_tree/path.rs | 743 ++++++++++++++++++++ manta-crypto/src/merkle_tree/single_leaf.rs | 42 +- manta-crypto/src/merkle_tree/tree.rs | 287 +------- 9 files changed, 791 insertions(+), 314 deletions(-) create mode 100644 manta-crypto/src/merkle_tree/path.rs diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index 3ec022eab..67c4b219a 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -21,6 +21,8 @@ #![forbid(rustdoc::broken_intra_doc_links)] #![forbid(missing_docs)] +extern crate alloc; + mod prf; pub mod commitment; diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs index 3bb7ee647..33c792862 100644 --- a/manta-crypto/src/merkle_tree/fork.rs +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -20,8 +20,6 @@ // TODO: Implement derive-able traits for these types. // TODO: See if we can get rid of the smart pointer logic. -extern crate alloc; - use crate::merkle_tree::{ capacity, inner_tree::{BTreeMap, InnerMap, PartialInnerTree}, diff --git a/manta-crypto/src/merkle_tree/full.rs b/manta-crypto/src/merkle_tree/full.rs index 75f8cf3d0..927dc7189 100644 --- a/manta-crypto/src/merkle_tree/full.rs +++ b/manta-crypto/src/merkle_tree/full.rs @@ -18,13 +18,11 @@ // TODO: Do we allow custom sentinel sources for this tree? -extern crate alloc; - use crate::merkle_tree::{ capacity, inner_tree::{BTreeMap, InnerMap, InnerTree}, - Configuration, GetPath, InnerDigest, LeafDigest, MerkleTree, Node, Parameters, Path, Root, - Tree, + Configuration, CurrentPath, GetPath, InnerDigest, LeafDigest, MerkleTree, Node, Parameters, + Path, Root, Tree, }; use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; @@ -125,8 +123,9 @@ where } #[inline] - fn current_path(&self, parameters: &Parameters<C>) -> Path<C> { - self.path(parameters, 0).unwrap() + fn current_path(&self, parameters: &Parameters<C>) -> CurrentPath<C> { + // FIXME: self.path(parameters, 0).unwrap() + todo!() } #[inline] @@ -160,10 +159,10 @@ where } let leaf_index = Node(index); Ok(Path::new( - leaf_index, self.get_leaf_sibling(leaf_index) .map(Clone::clone) .unwrap_or_default(), + leaf_index, self.inner_digests .inner_path_for_leaf(leaf_index) .cloned() diff --git a/manta-crypto/src/merkle_tree/inner_tree.rs b/manta-crypto/src/merkle_tree/inner_tree.rs index c4b7e2740..accb39680 100644 --- a/manta-crypto/src/merkle_tree/inner_tree.rs +++ b/manta-crypto/src/merkle_tree/inner_tree.rs @@ -16,10 +16,8 @@ //! Inner Digest Tree -// TODO: Figure out how we want to expose the meaning of `InnerNode` coordinates. We should share -// some of it, to reduce potential duplication. - -extern crate alloc; +// TODO: Figure out how we want to expose the meaning of `InnerNode` coordinates. Should we share +// some of it, to reduce potential duplication? use crate::merkle_tree::{path_length, Configuration, InnerDigest, Node, Parameters, Parity, Path}; use alloc::collections::btree_map; diff --git a/manta-crypto/src/merkle_tree/mod.rs b/manta-crypto/src/merkle_tree/mod.rs index e2169bcd3..b0c3a41ef 100644 --- a/manta-crypto/src/merkle_tree/mod.rs +++ b/manta-crypto/src/merkle_tree/mod.rs @@ -26,6 +26,7 @@ // FIXME: Get rid of as many `pub(super)` declarations as we can. mod node; +mod path; mod tree; pub mod fork; @@ -39,4 +40,5 @@ pub mod single_leaf; pub mod test; pub use node::*; +pub use path::*; pub use tree::*; diff --git a/manta-crypto/src/merkle_tree/partial.rs b/manta-crypto/src/merkle_tree/partial.rs index f8e08283a..ef1f20df2 100644 --- a/manta-crypto/src/merkle_tree/partial.rs +++ b/manta-crypto/src/merkle_tree/partial.rs @@ -18,13 +18,11 @@ // TODO: Do we allow custom sentinel sources for this tree? -extern crate alloc; - use crate::merkle_tree::{ capacity, inner_tree::{BTreeMap, InnerMap, PartialInnerTree}, - Configuration, GetPath, InnerDigest, LeafDigest, MerkleTree, Node, Parameters, Path, Root, - Tree, + Configuration, CurrentPath, GetPath, InnerDigest, LeafDigest, MerkleTree, Node, Parameters, + Path, Root, Tree, }; use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; @@ -137,7 +135,7 @@ where } #[inline] - fn current_path(&self, parameters: &Parameters<C>) -> Path<C> { + fn current_path(&self, parameters: &Parameters<C>) -> CurrentPath<C> { /* TODO: self.path(parameters, 0).unwrap() */ diff --git a/manta-crypto/src/merkle_tree/path.rs b/manta-crypto/src/merkle_tree/path.rs new file mode 100644 index 000000000..8378dda5a --- /dev/null +++ b/manta-crypto/src/merkle_tree/path.rs @@ -0,0 +1,743 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Merkle Tree Paths + +// TODO: Move some methods to a `raw` module for paths. + +use crate::merkle_tree::{ + inner_tree::InnerNodeIter, node::Parity, path_length, Configuration, InnerDigest, Leaf, + LeafDigest, Node, Parameters, Root, +}; +use alloc::vec::Vec; +use core::{ + fmt::Debug, + hash::Hash, + iter::FusedIterator, + mem, + ops::{Index, IndexMut}, + slice::{self, SliceIndex}, +}; + +/// Merkle Tree Inner Path +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "InnerDigest<C>: Clone"), + Debug(bound = "InnerDigest<C>: Debug"), + Eq(bound = "InnerDigest<C>: Eq"), + Hash(bound = "InnerDigest<C>: Hash"), + PartialEq(bound = "InnerDigest<C>: PartialEq") +)] +pub struct InnerPath<C> +where + C: Configuration + ?Sized, +{ + /// Leaf Index + pub leaf_index: Node, + + /// Inner Digest Path + /// + /// Inner digests are stored from leaf to root, not including the root. + pub path: Vec<InnerDigest<C>>, +} + +impl<C> InnerPath<C> +where + C: Configuration + ?Sized, +{ + /// Builds a new [`InnerPath`] from `leaf_index` and `path`. + /// + /// # Safety + /// + /// In order for paths to compute the correct root, they should always have a `path` with + /// length given by [`path_length`]. + #[inline] + pub fn new(leaf_index: Node, path: Vec<InnerDigest<C>>) -> Self { + Self { leaf_index, path } + } + + /// Computes the root of the merkle tree relative to `base` using `parameters`. + #[inline] + pub fn root_from_base(&self, parameters: &Parameters<C>, base: InnerDigest<C>) -> Root<C> { + Self::fold(parameters, self.leaf_index, base, &self.path) + } + + /// Computes the root of the merkle tree relative to `leaf_digest` and its `sibling_digest` + /// using `parameters`. + #[inline] + pub fn root( + &self, + parameters: &Parameters<C>, + leaf_digest: &LeafDigest<C>, + sibling_digest: &LeafDigest<C>, + ) -> Root<C> { + self.root_from_base( + parameters, + self.leaf_index + .join_leaves(parameters, leaf_digest, sibling_digest), + ) + } + + /// Returns `true` if `self` is a witness to the fact that `leaf_digest` is stored in a + /// merkle tree with the given `root` and `sibling_digest`. + #[inline] + pub fn verify_digest( + &self, + parameters: &Parameters<C>, + root: &Root<C>, + leaf_digest: &LeafDigest<C>, + sibling_digest: &LeafDigest<C>, + ) -> bool { + root == &self.root(parameters, leaf_digest, sibling_digest) + } + + /// Returns the folding algorithm for a path with `index` as its starting index. + #[inline] + fn fold_fn<'d>( + parameters: &'d Parameters<C>, + mut index: Node, + ) -> impl 'd + FnMut(InnerDigest<C>, &'d InnerDigest<C>) -> InnerDigest<C> { + move |acc, d| index.into_parent().join(parameters, &acc, d) + } + + /// Folds `iter` into a root using the path folding algorithm for [`InnerPath`]. + #[inline] + pub(super) fn fold<'i, I>( + parameters: &'i Parameters<C>, + index: Node, + base: InnerDigest<C>, + iter: I, + ) -> Root<C> + where + InnerDigest<C>: 'i, + I: IntoIterator<Item = &'i InnerDigest<C>>, + { + Root( + iter.into_iter() + .fold(base, Self::fold_fn(parameters, index)), + ) + } +} + +impl<C> Default for InnerPath<C> +where + C: Configuration + ?Sized, +{ + #[inline] + fn default() -> Self { + let path_length = path_length::<C>(); + let mut path = Vec::with_capacity(path_length); + path.resize_with(path_length, InnerDigest::<C>::default); + Self::new(Default::default(), path) + } +} + +impl<C> From<Path<C>> for InnerPath<C> +where + C: Configuration + ?Sized, +{ + #[inline] + fn from(path: Path<C>) -> Self { + path.inner_path + } +} + +impl<C> From<CurrentInnerPath<C>> for InnerPath<C> +where + C: Configuration + ?Sized, +{ + #[inline] + fn from(path: CurrentInnerPath<C>) -> Self { + todo!() + } +} + +impl<C, I> Index<I> for InnerPath<C> +where + C: Configuration + ?Sized, + I: SliceIndex<[InnerDigest<C>]>, +{ + type Output = I::Output; + + #[inline] + fn index(&self, index: I) -> &Self::Output { + &self.path[index] + } +} + +impl<C, I> IndexMut<I> for InnerPath<C> +where + C: Configuration + ?Sized, + I: SliceIndex<[InnerDigest<C>]>, +{ + #[inline] + fn index_mut(&mut self, index: I) -> &mut Self::Output { + &mut self.path[index] + } +} + +/// Merkle Tree Current Inner Path +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "InnerDigest<C>: Clone"), + Debug(bound = "InnerDigest<C>: Debug"), + Default(bound = ""), + Eq(bound = "InnerDigest<C>: Eq"), + Hash(bound = "InnerDigest<C>: Hash"), + PartialEq(bound = "InnerDigest<C>: PartialEq") +)] +pub struct CurrentInnerPath<C> +where + C: Configuration + ?Sized, +{ + /// Leaf Index + pub leaf_index: Node, + + /// Inner Digest Path + /// + /// Inner digests are stored from leaf to root, not including the root. + /// + /// For [`CurrentInnerPath`], only non-default inner digests are stored in the `path`. + pub path: Vec<InnerDigest<C>>, +} + +impl<C> CurrentInnerPath<C> +where + C: Configuration + ?Sized, +{ + /// Builds a new [`CurrentInnerPath`] from `leaf_index` and `path`. + /// + /// # Safety + /// + /// In order for paths to compute the correct root, they should always have a `path` with + /// length given by [`path_length`]. For [`CurrentInnerPath`], we also have the invariant + /// that any right-siblings on the path, which can only be a sentinel value, are not stored. + /// This function assumes that this is the case for `path`. + #[inline] + pub fn new(leaf_index: Node, path: Vec<InnerDigest<C>>) -> Self { + Self { leaf_index, path } + } + + /// Computes the root of the merkle tree relative to `base` using `parameters`. + #[inline] + pub fn root_from_base(&self, parameters: &Parameters<C>, base: InnerDigest<C>) -> Root<C> { + Self::fold( + &Default::default(), + parameters, + 0, + self.leaf_index, + base, + &self.path, + ) + } + + /// Computes the root of the merkle tree relative to `leaf_digest` and its `sibling_digest` + /// using `parameters`. + #[inline] + pub fn root( + &self, + parameters: &Parameters<C>, + leaf_digest: &LeafDigest<C>, + sibling_digest: &LeafDigest<C>, + ) -> Root<C> { + self.root_from_base( + parameters, + self.leaf_index + .join_leaves(parameters, leaf_digest, sibling_digest), + ) + } + + /// Returns `true` if `self` is a witness to the fact that `leaf_digest` is stored in a + /// merkle tree with the given `root` and `sibling_digest`. + #[inline] + pub fn verify_digest( + &self, + parameters: &Parameters<C>, + root: &Root<C>, + leaf_digest: &LeafDigest<C>, + sibling_digest: &LeafDigest<C>, + ) -> bool { + root == &self.root(parameters, leaf_digest, sibling_digest) + } + + /// Folds `iter` into a root using the path folding algorithm for [`CurrentInnerPath`]. + #[inline] + fn fold<'i, I>( + default: &'i InnerDigest<C>, + parameters: &'i Parameters<C>, + depth: usize, + mut index: Node, + base: InnerDigest<C>, + iter: I, + ) -> Root<C> + where + InnerDigest<C>: 'i, + I: IntoIterator<Item = &'i InnerDigest<C>>, + { + let mut iter = iter.into_iter().peekable(); + let mut accumulator = base; + for _ in depth..path_length::<C>() { + accumulator = match index.into_parent().parity() { + Parity::Left => parameters.join(&accumulator, default), + Parity::Right => parameters.join(iter.next().unwrap(), &accumulator), + }; + } + Root(accumulator) + } + + /// Updates the path to the next current path with `next_leaf_digest`, updating `leaf_digest` + /// and `sibling_digest` as necessary. + #[inline] + fn update( + &mut self, + parameters: &Parameters<C>, + leaf_digest: &mut LeafDigest<C>, + sibling_digest: &mut LeafDigest<C>, + next_leaf_digest: LeafDigest<C>, + ) -> Root<C> { + let mut last_index = self.leaf_index; + let mut index = self.leaf_index + 1; + self.leaf_index = index; + match index.parity() { + Parity::Left => { + let mut last_accumulator = parameters.join_leaves( + &mem::take(sibling_digest), + &mem::replace(leaf_digest, next_leaf_digest), + ); + + let mut accumulator = parameters.join_leaves(leaf_digest, sibling_digest); + + let default_inner_digest = Default::default(); + + let mut depth = 0; + while !Node::are_siblings(&last_index.into_parent(), &index.into_parent()) { + last_accumulator = match last_index.parity() { + Parity::Left => parameters.join(&last_accumulator, &default_inner_digest), + Parity::Right => parameters.join(&self.path.remove(0), &last_accumulator), + }; + accumulator = parameters.join(&accumulator, &default_inner_digest); + depth += 1; + } + + self.path.insert(0, last_accumulator); + accumulator = parameters.join(&self.path[0], &accumulator); + + Self::fold( + &default_inner_digest, + parameters, + depth + 1, + index, + accumulator, + &self.path[1..], + ) + } + Parity::Right => { + *sibling_digest = mem::replace(leaf_digest, next_leaf_digest); + self.root(parameters, leaf_digest, sibling_digest) + } + } + } +} + +impl<C> From<CurrentPath<C>> for CurrentInnerPath<C> +where + C: Configuration + ?Sized, +{ + #[inline] + fn from(path: CurrentPath<C>) -> Self { + path.inner_path + } +} + +/// Merkle Tree Path +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), + Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), + Default(bound = ""), + Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), + Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), + PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") +)] +pub struct Path<C> +where + C: Configuration + ?Sized, +{ + /// Sibling Digest + pub sibling_digest: LeafDigest<C>, + + /// Inner Path + pub inner_path: InnerPath<C>, +} + +impl<C> Path<C> +where + C: Configuration + ?Sized, +{ + /// Builds a new [`Path`] from `sibling_digest`, `leaf_index`, and `path`. + /// + /// # Safety + /// + /// See [`InnerPath::new`] for the invariants on `path` assumed by this function. + #[inline] + pub fn new(sibling_digest: LeafDigest<C>, leaf_index: Node, path: Vec<InnerDigest<C>>) -> Self { + Self::from_inner(sibling_digest, InnerPath::new(leaf_index, path)) + } + + /// Builds a new [`Path`] from `sibling_digest` and `inner_path`. + #[inline] + pub fn from_inner(sibling_digest: LeafDigest<C>, inner_path: InnerPath<C>) -> Self { + Self { + sibling_digest, + inner_path, + } + } + + /// Returns the leaf index for this [`Path`]. + #[inline] + pub fn leaf_index(&self) -> Node { + self.inner_path.leaf_index + } + + /// Computes the root of the merkle tree relative to `leaf_digest` using `parameters`. + #[inline] + pub fn root(&self, parameters: &Parameters<C>, leaf_digest: &LeafDigest<C>) -> Root<C> { + self.inner_path + .root(parameters, leaf_digest, &self.sibling_digest) + } + + /// Returns `true` if `self` is a witness to the fact that `leaf_digest` is stored in a + /// merkle tree with the given `root`. + #[inline] + pub fn verify_digest( + &self, + parameters: &Parameters<C>, + root: &Root<C>, + leaf_digest: &LeafDigest<C>, + ) -> bool { + self.inner_path + .verify_digest(parameters, root, leaf_digest, &self.sibling_digest) + } + + /// Returns `true` if `self` is a witness to the fact that `leaf` is stored in a merkle tree + /// with the given `root`. + #[inline] + pub fn verify(&self, parameters: &Parameters<C>, root: &Root<C>, leaf: &Leaf<C>) -> bool { + self.verify_digest(parameters, root, &parameters.digest(leaf)) + } + + /* TODO: + /// Folds `iter` into a root using the path folding algorithm for [`Path`]. + #[inline] + pub(super) fn fold<'i, I>( + parameters: &'i Parameters<C>, + index: Node, + base: InnerDigest<C>, + iter: I, + ) -> Root<C> + where + InnerDigest<C>: 'i, + I: IntoIterator<Item = &'i InnerDigest<C>>, + { + InnerPath::fold(parameters, index, base, iter) + } + */ +} + +impl<C> From<CurrentPath<C>> for Path<C> +where + C: Configuration + ?Sized, +{ + #[inline] + fn from(path: CurrentPath<C>) -> Self { + Self::from_inner(path.sibling_digest, path.inner_path.into()) + } +} + +impl<C, I> Index<I> for Path<C> +where + C: Configuration + ?Sized, + I: SliceIndex<[InnerDigest<C>]>, +{ + type Output = I::Output; + + #[inline] + fn index(&self, index: I) -> &Self::Output { + &self.inner_path[index] + } +} + +impl<C, I> IndexMut<I> for Path<C> +where + C: Configuration + ?Sized, + I: SliceIndex<[InnerDigest<C>]>, +{ + #[inline] + fn index_mut(&mut self, index: I) -> &mut Self::Output { + &mut self.inner_path[index] + } +} + +/// Merkle Tree Current Path +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), + Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), + Default(bound = ""), + Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), + Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), + PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") +)] +pub struct CurrentPath<C> +where + C: Configuration + ?Sized, +{ + /// Sibling Digest + pub sibling_digest: LeafDigest<C>, + + /// Current Inner Path + pub inner_path: CurrentInnerPath<C>, +} + +impl<C> CurrentPath<C> +where + C: Configuration + ?Sized, +{ + /// Builds a new [`CurrentPath`] from `sibling_digest`, `leaf_index`, and `path`. + /// + /// # Safety + /// + /// See [`CurrentInnerPath::new`] for the invariants on `path` assumed by this function. + #[inline] + pub fn new(sibling_digest: LeafDigest<C>, leaf_index: Node, path: Vec<InnerDigest<C>>) -> Self { + Self::from_inner(sibling_digest, CurrentInnerPath::new(leaf_index, path)) + } + + /// Builds a new [`CurrentPath`] from `sibling_digest` and `inner_path`. + #[inline] + pub fn from_inner(sibling_digest: LeafDigest<C>, inner_path: CurrentInnerPath<C>) -> Self { + Self { + sibling_digest, + inner_path, + } + } + + /// Returns the leaf index for this [`CurrentPath`]. + #[inline] + pub fn leaf_index(&self) -> Node { + self.inner_path.leaf_index + } + + /// Computes the root of the merkle tree relative to `leaf_digest` using `parameters`. + #[inline] + pub fn root(&self, parameters: &Parameters<C>, leaf_digest: &LeafDigest<C>) -> Root<C> { + self.inner_path + .root(parameters, leaf_digest, &self.sibling_digest) + } + + /// Returns `true` if `self` is a witness to the fact that `leaf_digest` is stored in a + /// merkle tree with the given `root`. + #[inline] + pub fn verify_digest( + &self, + parameters: &Parameters<C>, + root: &Root<C>, + leaf_digest: &LeafDigest<C>, + ) -> bool { + self.inner_path + .verify_digest(parameters, root, leaf_digest, &self.sibling_digest) + } + + /// Returns `true` if `self` is a witness to the fact that `leaf` is stored in a merkle tree + /// with the given `root`. + #[inline] + pub fn verify(&self, parameters: &Parameters<C>, root: &Root<C>, leaf: &Leaf<C>) -> bool { + self.verify_digest(parameters, root, &parameters.digest(leaf)) + } + + /// Updates the path to the next current path with `next`, updating `current`. + #[inline] + pub fn update( + &mut self, + parameters: &Parameters<C>, + current: &mut LeafDigest<C>, + next: LeafDigest<C>, + ) -> Root<C> { + self.inner_path + .update(parameters, current, &mut self.sibling_digest, next) + } +} + +/* TODO: Compressed Path Implementation + +/// Compressed Path +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), + Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), + Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), + Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), + PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") +)] +pub struct CompressedPath<C> +where + C: Configuration + ?Sized, +{ + /// Leaf Index + pub leaf_index: Node, + + /// Sibling Digest + pub sibling_digest: LeafDigest<C>, + + /// Inner Path + /// + /// Inner digests are stored from leaf to root, not including the root. + pub inner_path: Vec<InnerDigest<C>>, + + /// Sentinel Ranges + pub sentinel_ranges: Vec<usize>, +} + +impl<C> CompressedPath<C> +where + C: Configuration + ?Sized, +{ + /// Builds a new [`CompressedPath`] from `leaf_index`, `sibling_digest`, `inner_path`, and + /// `sentinel_ranges`. + /// + /// # Safety + /// + /// In order for paths to compute the correct root, they should always have an `inner_path` + /// with length given by [`path_length`]. + /// + /// For compressed paths, we need the `sentinel_ranges` to contain all the ranges which include + /// the default inner hash, and then `inner_path` contains the non-default values. The total + /// number of values represented in this way must equal the [`path_length`]. + #[inline] + pub fn new( + leaf_index: Node, + sibling_digest: LeafDigest<C>, + inner_path: Vec<InnerDigest<C>>, + sentinel_ranges: Vec<usize>, + ) -> Self { + Self { + leaf_index, + sibling_digest, + inner_path, + sentinel_ranges, + } + } + + /// Uncompresses a path by re-inserting the default values into [`self.inner_path`] at the + /// indices described by [`self.sentinel_ranges`]. + /// + /// [`self.sentinel_ranges`]: Self::sentinel_ranges + /// [`self.inner_path`]: Self::inner_path + #[inline] + pub fn uncompress(mut self) -> Path<C> { + let path_length = path_length::<C>(); + self.inner_path.reserve(path_length); + let mut start = 0; + for (i, index) in self.sentinel_ranges.into_iter().enumerate() { + if i % 2 == 0 { + start = index; + } else { + self.inner_path + .splice(start..start, (start..index).map(|_| Default::default())); + } + } + self.inner_path.resize_with(path_length, Default::default); + Path::new(self.leaf_index, self.sibling_digest, self.inner_path) + } +} + +impl<C> Default for CompressedPath<C> +where + C: Configuration + ?Sized, +{ + #[inline] + fn default() -> Self { + Self::new( + Default::default(), + Default::default(), + Default::default(), + vec![0, path_length::<C>()], + ) + } +} + +impl<C> From<Path<C>> for CompressedPath<C> +where + C: Configuration + ?Sized, +{ + #[inline] + fn from(path: Path<C>) -> Self { + path.compress() + } +} + +impl<C> Path<C> +where + C: Configuration + ?Sized, +{ + /// Compresses [`self.inner_path`](Self::inner_path) by removing all default values and saving + /// only the positions in the path of those default values. + #[inline] + pub fn compress(self) -> CompressedPath<C> { + let default_inner_digest = Default::default(); + let mut started = false; + let mut sentinel_ranges = Vec::new(); + let inner_path = self + .inner_path + .into_iter() + .enumerate() + .filter_map(|(i, d)| { + if d == default_inner_digest { + if !started { + sentinel_ranges.push(i); + started = true; + } + None + } else { + if started { + sentinel_ranges.push(i); + started = false; + } + Some(d) + } + }) + .collect(); + sentinel_ranges.shrink_to_fit(); + CompressedPath::new( + self.leaf_index, + self.sibling_digest, + inner_path, + sentinel_ranges, + ) + } +} + +impl<C> From<CompressedPath<C>> for Path<C> +where + C: Configuration + ?Sized, +{ + #[inline] + fn from(path: CompressedPath<C>) -> Self { + path.uncompress() + } +} + +*/ diff --git a/manta-crypto/src/merkle_tree/single_leaf.rs b/manta-crypto/src/merkle_tree/single_leaf.rs index ce881c88c..1800d117a 100644 --- a/manta-crypto/src/merkle_tree/single_leaf.rs +++ b/manta-crypto/src/merkle_tree/single_leaf.rs @@ -19,8 +19,8 @@ // TODO: Should we be storing the root? Can we have a version where we don't? use crate::merkle_tree::{ - capacity, Configuration, InnerDigest, LeafDigest, MerkleTree, Node, Parameters, Parity, Path, - Root, Tree, + capacity, Configuration, CurrentPath, InnerDigest, InnerPath, LeafDigest, MerkleTree, Node, + Parameters, Parity, Path, Root, Tree, }; use core::{fmt::Debug, hash::Hash, mem}; @@ -44,7 +44,7 @@ where /// Leaf Digest leaf_digest: Option<LeafDigest<C>>, - /// Path + /// Current Path path: Path<C>, /// Root @@ -61,7 +61,7 @@ where if self.leaf_digest.is_none() { 0 } else { - self.path.leaf_index.0 + 1 + self.path.leaf_index().0 + 1 } } @@ -84,11 +84,13 @@ where &self.root } + /* TODO: /// Returns the current merkle tree path for the current leaf. #[inline] - pub fn path(&self) -> &Path<C> { - &self.path + pub fn current_path(&self) -> &CurrentPath<C> { + &self.current_path } + */ /// Returns the currently stored leaf digest, returning `None` if the tree is empty. #[inline] @@ -140,9 +142,12 @@ where } #[inline] - fn current_path(&self, parameters: &Parameters<C>) -> Path<C> { + fn current_path(&self, parameters: &Parameters<C>) -> CurrentPath<C> { + /* TODO: let _ = parameters; - self.path.clone() + self.current_path.clone() + */ + todo!() } #[inline] @@ -161,7 +166,7 @@ where self.leaf_digest = Some(leaf_digest); self.root = self.compute_root(parameters); } else { - self.path.leaf_index = index; + self.path.inner_path.leaf_index = index; match index.parity() { Parity::Left => { let mut last_index = index - 1; @@ -180,21 +185,21 @@ where last_accumulator = last_index.join( parameters, &last_accumulator, - &self.path.inner_path[i], + &self.path.inner_path.path[i], ); - self.path.inner_path[i] = Default::default(); - accumulator = parameters.join(&accumulator, &self.path.inner_path[i]); + self.path.inner_path.path[i] = Default::default(); + accumulator = parameters.join(&accumulator, &self.path.inner_path.path[i]); i += 1; } - self.path.inner_path[i] = last_accumulator; - accumulator = parameters.join(&self.path.inner_path[i], &accumulator); + self.path.inner_path.path[i] = last_accumulator; + accumulator = parameters.join(&self.path.inner_path.path[i], &accumulator); - self.root = Path::fold( + self.root = InnerPath::fold( parameters, index, accumulator, - &self.path.inner_path[i + 1..], + &self.path.inner_path.path[i + 1..], ); } Parity::Right => { @@ -203,6 +208,11 @@ where self.root = self.compute_root(parameters); } } + /* TODO: + self.root = + self.current_path + .update(parameters, self.leaf_digest_mut_ref(), leaf_digest); + */ } Some(true) diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index c5f034a4c..da13fc48f 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -20,15 +20,14 @@ // implementations for the trivial tree sizes? // TODO: Add "copy-on-write" adapters for `Root` and `Path`, and see if we can incorporate them // into `Tree`. - -extern crate alloc; +// TODO: Should we add optimization paths for when `cloning` the default value is cheaper than +// creating a new one from scratch (for inner digests)? use crate::merkle_tree::{ fork::{self, Branch, MergeBranch, Trunk}, inner_tree::InnerMap, - Node, + path::{CurrentPath, Path}, }; -use alloc::{vec, vec::Vec}; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; /// Merkle Tree Leaf Hash @@ -208,8 +207,8 @@ where /// Returns the [`Root`] of the merkle tree. fn root(&self, parameters: &Parameters<C>) -> Root<C>; - /// Returns the [`Path`] of the current (i.e. right-most) leaf. - fn current_path(&self, parameters: &Parameters<C>) -> Path<C>; + /// Returns the [`CurrentPath`] of the current (i.e. right-most) leaf. + fn current_path(&self, parameters: &Parameters<C>) -> CurrentPath<C>; /// Checks if a leaf can be inserted into the tree and if it can, it runs `leaf_digest` to /// extract a leaf digest to insert, returning `None` if there was no leaf digest. @@ -441,278 +440,6 @@ where } } -/// Merkle Tree Path -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), - Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), - Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), - Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), - PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") -)] -pub struct Path<C> -where - C: Configuration + ?Sized, -{ - /// Leaf Index - pub leaf_index: Node, - - /// Sibling Digest - pub sibling_digest: LeafDigest<C>, - - /// Inner Path - /// - /// Inner digests are stored from leaf to root, not including the root. - pub inner_path: Vec<InnerDigest<C>>, -} - -impl<C> Path<C> -where - C: Configuration + ?Sized, -{ - /// Builds a new [`Path`] from `leaf_index`, `sibling_digest`, and `inner_path`. - /// - /// # Safety - /// - /// In order for paths to compute the correct root, they should always have an `inner_path` - /// with length given by [`path_length`]. - #[inline] - pub fn new( - leaf_index: Node, - sibling_digest: LeafDigest<C>, - inner_path: Vec<InnerDigest<C>>, - ) -> Self { - Self { - leaf_index, - sibling_digest, - inner_path, - } - } - - /// Compresses [`self.inner_path`](Self::inner_path) by removing all default values and saving - /// only the positions in the path of those default values. - #[inline] - pub fn compress(self) -> CompressedPath<C> { - let default_inner_digest = Default::default(); - let mut started = false; - let mut sentinel_ranges = Vec::new(); - let inner_path = self - .inner_path - .into_iter() - .enumerate() - .filter_map(|(i, d)| { - if d == default_inner_digest { - if !started { - sentinel_ranges.push(i); - started = true; - } - None - } else { - if started { - sentinel_ranges.push(i); - started = false; - } - Some(d) - } - }) - .collect(); - sentinel_ranges.shrink_to_fit(); - CompressedPath::new( - self.leaf_index, - self.sibling_digest, - inner_path, - sentinel_ranges, - ) - } - - /// Computes the root of the merkle tree relative to `leaf_digest` using `parameters`. - #[inline] - pub fn root(&self, parameters: &Parameters<C>, leaf_digest: &LeafDigest<C>) -> Root<C> { - let index = self.leaf_index; - Self::fold( - parameters, - index, - index.join_leaves(parameters, leaf_digest, &self.sibling_digest), - &self.inner_path, - ) - } - - /// Returns `true` if `self` is a witness to the fact that `leaf_digest` is stored in a - /// merkle tree with the given `root`. - #[inline] - pub fn verify_digest( - &self, - parameters: &Parameters<C>, - root: &Root<C>, - leaf_digest: &LeafDigest<C>, - ) -> bool { - root == &self.root(parameters, leaf_digest) - } - - /// Returns `true` if `self` is a witness to the fact that `leaf` is stored in a merkle tree - /// with the given `root`. - #[inline] - pub fn verify(&self, parameters: &Parameters<C>, root: &Root<C>, leaf: &Leaf<C>) -> bool { - self.verify_digest(parameters, root, &parameters.digest(leaf)) - } - - /// Returns the folding algorithm for a path with `index` as its starting index. - #[inline] - pub fn fold_fn<'d>( - parameters: &'d Parameters<C>, - mut index: Node, - ) -> impl 'd + FnMut(InnerDigest<C>, &'d InnerDigest<C>) -> InnerDigest<C> { - move |acc, d| index.into_parent().join(parameters, &acc, d) - } - - /// Folds `iter` into a root using [`fold_fn`](Self::fold_fn), the path folding algorithm. - #[inline] - pub fn fold<'i, I>( - parameters: &'i Parameters<C>, - index: Node, - base: InnerDigest<C>, - iter: I, - ) -> Root<C> - where - InnerDigest<C>: 'i, - I: IntoIterator<Item = &'i InnerDigest<C>>, - { - Root( - iter.into_iter() - .fold(base, Self::fold_fn(parameters, index)), - ) - } -} - -impl<C> Default for Path<C> -where - C: Configuration + ?Sized, -{ - #[inline] - fn default() -> Self { - let path_length = path_length::<C>(); - let mut inner_path = Vec::with_capacity(path_length); - inner_path.resize_with(path_length, InnerDigest::<C>::default); - Self::new(Default::default(), Default::default(), inner_path) - } -} - -impl<C> From<CompressedPath<C>> for Path<C> -where - C: Configuration + ?Sized, -{ - #[inline] - fn from(path: CompressedPath<C>) -> Self { - path.uncompress() - } -} - -/// Compressed Path -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), - Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), - Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), - Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), - PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") -)] -pub struct CompressedPath<C> -where - C: Configuration + ?Sized, -{ - /// Leaf Index - pub leaf_index: Node, - - /// Sibling Digest - pub sibling_digest: LeafDigest<C>, - - /// Inner Path - /// - /// Inner digests are stored from leaf to root, not including the root. - pub inner_path: Vec<InnerDigest<C>>, - - /// Sentinel Ranges - pub sentinel_ranges: Vec<usize>, -} - -impl<C> CompressedPath<C> -where - C: Configuration + ?Sized, -{ - /// Builds a new [`CompressedPath`] from `leaf_index`, `sibling_digest`, `inner_path`, and - /// `sentinel_ranges`. - /// - /// # Safety - /// - /// In order for paths to compute the correct root, they should always have an `inner_path` - /// with length given by [`path_length`]. - /// - /// For compressed paths, we need the `sentinel_ranges` to contain all the ranges which include - /// the default inner hash, and then `inner_path` contains the non-default values. The total - /// number of values represented in this way must equal the [`path_length`]. - #[inline] - pub fn new( - leaf_index: Node, - sibling_digest: LeafDigest<C>, - inner_path: Vec<InnerDigest<C>>, - sentinel_ranges: Vec<usize>, - ) -> Self { - Self { - leaf_index, - sibling_digest, - inner_path, - sentinel_ranges, - } - } - - /// Uncompresses a path by re-inserting the default values into [`self.inner_path`] at the - /// indices described by [`self.sentinel_ranges`]. - /// - /// [`self.sentinel_ranges`]: Self::sentinel_ranges - /// [`self.inner_path`]: Self::inner_path - #[inline] - pub fn uncompress(mut self) -> Path<C> { - let path_length = path_length::<C>(); - self.inner_path.reserve(path_length); - let mut start = 0; - for (i, index) in self.sentinel_ranges.into_iter().enumerate() { - if i % 2 == 0 { - start = index; - } else { - self.inner_path - .splice(start..start, (start..index).map(|_| Default::default())); - } - } - self.inner_path.resize_with(path_length, Default::default); - Path::new(self.leaf_index, self.sibling_digest, self.inner_path) - } -} - -impl<C> Default for CompressedPath<C> -where - C: Configuration + ?Sized, -{ - #[inline] - fn default() -> Self { - Self::new( - Default::default(), - Default::default(), - Default::default(), - vec![0, path_length::<C>()], - ) - } -} - -impl<C> From<Path<C>> for CompressedPath<C> -where - C: Configuration + ?Sized, -{ - #[inline] - fn from(path: Path<C>) -> Self { - path.compress() - } -} - /// Merkle Tree #[derive(derivative::Derivative)] #[derivative( @@ -808,9 +535,9 @@ where self.tree.root(&self.parameters) } - /// Returns the [`Path`] of the current (i.e right-most) leaf. + /// Returns the [`CurrentPath`] of the current (i.e right-most) leaf. #[inline] - pub fn current_path(&self) -> Path<C> { + pub fn current_path(&self) -> CurrentPath<C> { self.tree.current_path(&self.parameters) } From 2280e49b56ce0b9cd3f8bc03b7d3098b8616d160 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 30 Sep 2021 05:00:41 -0400 Subject: [PATCH 068/275] finish CurrentPath and Fork abstractions --- manta-accounting/src/identity.rs | 2 +- manta-crypto/src/ies.rs | 2 +- manta-crypto/src/merkle_tree/fork.rs | 206 ++++++-------- manta-crypto/src/merkle_tree/full.rs | 27 +- manta-crypto/src/merkle_tree/inner_tree.rs | 147 ++++++++-- manta-crypto/src/merkle_tree/mod.rs | 4 +- manta-crypto/src/merkle_tree/node.rs | 28 ++ manta-crypto/src/merkle_tree/partial.rs | 66 +++-- manta-crypto/src/merkle_tree/path.rs | 286 +++++++++++++++++--- manta-crypto/src/merkle_tree/single_leaf.rs | 138 ++++------ manta-crypto/src/merkle_tree/tree.rs | 9 + manta-crypto/src/set.rs | 2 +- manta-pay/src/crypto/merkle_tree/mod.rs | 4 +- manta-util/src/lib.rs | 14 + 14 files changed, 614 insertions(+), 321 deletions(-) diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index af8508280..175a01858 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -35,7 +35,7 @@ use rand::{ CryptoRng, RngCore, SeedableRng, }; -pub(crate) mod prelude { +pub(super) mod prelude { #[doc(inline)] pub use super::{ Identity, Receiver, Sender, SenderError, ShieldedIdentity, Spend, SpendError, Utxo, diff --git a/manta-crypto/src/ies.rs b/manta-crypto/src/ies.rs index ad0e78ab3..9dbb7d915 100644 --- a/manta-crypto/src/ies.rs +++ b/manta-crypto/src/ies.rs @@ -21,7 +21,7 @@ use core::{fmt::Debug, hash::Hash}; use rand::{CryptoRng, RngCore}; -pub(crate) mod prelude { +pub(super) mod prelude { #[doc(inline)] pub use super::IntegratedEncryptionScheme; } diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs index 33c792862..3c682970e 100644 --- a/manta-crypto/src/merkle_tree/fork.rs +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -19,12 +19,12 @@ // TODO: Think about whether we want to keep the `raw::MerkleTreePointerFamily` API sealed or not. // TODO: Implement derive-able traits for these types. // TODO: See if we can get rid of the smart pointer logic. +// TODO: Reduce duplication in this file, relative to `merkle_tree::partial`. use crate::merkle_tree::{ capacity, inner_tree::{BTreeMap, InnerMap, PartialInnerTree}, - Configuration, GetPath, GetPathError, InnerDigest, Leaf, LeafDigest, MerkleTree, Path, Root, - Tree, + Configuration, InnerDigest, Leaf, LeafDigest, MerkleTree, Node, Parameters, Tree, }; use alloc::vec::Vec; use core::{borrow::Borrow, fmt::Debug, hash::Hash, mem, ops::Deref}; @@ -241,30 +241,21 @@ where } /// Computes the length of this fork of the tree. - /// - /// Returns `None` if this fork has been detached from its trunk. Use [`attach`](Self::attach) - /// to re-associate a trunk to this fork. #[inline] - pub fn len(&self) -> Option<usize> { - Some(P::upgrade(&self.base)?.as_ref().len() + self.branch.len()) + pub fn len(&self) -> usize { + self.branch.len() } /// Returns `true` if this fork is empty. - /// - /// Returns `None` if this fork has been detached from its trunk. Use [`attach`](Self::attach) - /// to re-associate a trunk to this fork. #[inline] - pub fn is_empty(&self) -> Option<bool> { - Some(self.len()? == 0) + pub fn is_empty(&self) -> bool { + self.branch.is_empty() } - /// Computes the current root of this fork. - /// - /// Returns `None` if this fork has been detached from its trunk. Use [`attach`](Self::attach) - /// to re-associate a trunk to this fork. + /// Returns the current root of this fork. #[inline] - pub fn root(&self) -> Option<Root<C>> { - Some(self.branch.root(P::upgrade(&self.base)?.as_ref())) + pub fn root(&self) -> &InnerDigest<C> { + self.branch.root() } /// Appends a new `leaf` onto this fork. @@ -273,34 +264,13 @@ where /// to re-associate a trunk to this fork. #[inline] pub fn push(&mut self, leaf: &Leaf<C>) -> Option<bool> { - Some(self.branch.push(P::upgrade(&self.base)?.as_ref(), leaf)) + Some( + self.branch + .push(&P::upgrade(&self.base)?.as_ref().parameters, leaf), + ) } } -/* TODO: -/// Fork Path Error -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "GetPathError<C, T>: Clone"), - Copy(bound = "GetPathError<C, T>: Copy"), - Debug(bound = "GetPathError<C, T>: Debug"), - Eq(bound = "GetPathError<C, T>: Eq"), - Hash(bound = "GetPathError<C, T>: Hash"), - PartialEq(bound = "GetPathError<C, T>: PartialEq") -)] -pub enum ForkGetPathError<C, T> -where - C: Configuration + ?Sized, - T: GetPath<C>, -{ - /// Trunk Path Query Error - TrunkError(GetPathError<C, T>), - - /// Unknown Index on Branch Error - UnknownIndexOnBranch, -} -*/ - /// Merkle Tree Fork Branch #[derive(derivative::Derivative)] #[derivative( @@ -328,17 +298,50 @@ where C: Configuration + ?Sized, M: InnerMap<C> + Default, { + /// Builds a new [`Branch`] from `leaf_digests` and `inner_digests`. + #[inline] + fn new_inner(leaf_digests: Vec<LeafDigest<C>>, inner_digests: PartialInnerTree<C, M>) -> Self { + Self { + leaf_digests, + inner_digests, + } + } + + /// Builds a new [`Branch`] from `base` and `leaf_digests` without checking capacity limits. + #[inline] + fn new_unchecked<T>(base: &MerkleTree<C, T>, leaf_digests: Vec<LeafDigest<C>>) -> Self + where + T: Tree<C>, + { + let current_path = base.current_path(); + let mut this = Self::new_inner( + Default::default(), + PartialInnerTree::from_current( + &base.parameters, + current_path.leaf_index().join_leaves( + &base.parameters, + &base.current_leaf(), + &current_path.sibling_digest, + ), + current_path.inner_path, + ), + ); + for digest in leaf_digests { + this.push_digest(&base.parameters, || digest); + } + this + } + /// Builds a new [`Branch`] from `base` and `leaf_digests`. #[inline] fn new<T>(base: &MerkleTree<C, T>, leaf_digests: Vec<LeafDigest<C>>) -> Option<Self> where T: Tree<C>, { - let mut this = Self { - leaf_digests, - inner_digests: Default::default(), - }; - this.try_rebase(base).then(|| this) + if leaf_digests.len() + base.len() > capacity::<C>() { + return None; + } + Some(Self::new_unchecked(base, leaf_digests)) } /// Tries to restart `self` at a new `base` if `base` has enough capacity. @@ -347,20 +350,17 @@ where where T: Tree<C>, { - if self.len() + base.len() > capacity::<C>() { + if self.leaf_digests.len() + base.len() > capacity::<C>() { return false; } - let Self { leaf_digests, .. } = mem::take(self); - for digest in leaf_digests { - self.push_digest(base, || digest); - } + *self = Self::new_unchecked(base, mem::take(self).leaf_digests); true } - /// Computes the length of this branch. + /// Computes the total length of this branch. #[inline] pub fn len(&self) -> usize { - self.leaf_digests.len() + self.inner_digests.starting_leaf_index().0 + self.leaf_digests.len() } /// Returns `true` if this branch is empty. @@ -369,90 +369,52 @@ where self.len() == 0 } - /// Computes the root of the fork which has `self` as its branch and `base` as its base tree. + /// Returns the root inner digest of this branch. #[inline] - fn root<T>(&self, base: &MerkleTree<C, T>) -> Root<C> - where - T: Tree<C>, - { - todo!() + pub fn root(&self) -> &InnerDigest<C> { + self.inner_digests.root() } - /// Appends a new `leaf_digest` to this branch, recomputing the relevant inner digests - /// relative to the `base` tree`. + /// Returns the sibling leaf node to `index`. #[inline] - fn push_digest<T, F>(&mut self, base: &MerkleTree<C, T>, leaf_digest: F) -> bool + fn get_leaf_sibling(&self, index: Node) -> Option<&LeafDigest<C>> { + self.leaf_digests.get( + (index - self.inner_digests.starting_leaf_index().0) + .sibling() + .0, + ) + } + + /// Appends a new `leaf_digest` to this branch. + #[inline] + fn push_digest<F>(&mut self, parameters: &Parameters<C>, leaf_digest: F) -> bool where - T: Tree<C>, F: FnOnce() -> LeafDigest<C>, { - /* TODO: - use super::{inner_tree::InnerNodeIter, Node, Parity}; - let len = self.len(); - let base_tree_len = base.tree.len(); - let total_len = base_tree_len + len; - if total_len >= capacity::<C>() { + if len >= capacity::<C>() { return false; } - let leaf_digest = leaf_digest(); - - let leaf_index = Node(total_len); - - let first_inner = leaf_index.join_leaves( - &base.parameters, - &leaf_digest, - self.leaf_digests - .get(leaf_index.sibling().0) - .unwrap_or(&Default::default()), + let leaf_index = Node(len); + self.inner_digests.insert( + parameters, + leaf_index, + leaf_index.join_leaves( + parameters, + &leaf_digest, + self.get_leaf_sibling(leaf_index) + .unwrap_or(&Default::default()), + ), ); - - let default_inner_digest = Default::default(); - - let current_base_path = base.current_path(); - - let root = InnerNodeIter::from_leaf::<C>(leaf_index).fold(first_inner, |acc, node| { - let index = node.map_index(); - InnerMap::<C>::set(&mut self.inner_digests.map, index, acc); - // FIXME: This should be unwraping into `base` rather than `default_inner_digest`. - let (lhs, rhs) = match node.parity() { - Parity::Left => ( - self.inner_digests - .map_get(index) - .unwrap_or(&default_inner_digest), - self.inner_digests - .map_get(index + 1) - .unwrap_or(&default_inner_digest), - ), - Parity::Right => ( - self.inner_digests - .map_get(index - 1) - .unwrap_or(&default_inner_digest), - self.inner_digests - .map_get(index) - .unwrap_or(&default_inner_digest), - ), - }; - base.parameters.join(lhs, rhs) - }); - - InnerMap::<C>::set(&mut self.inner_digests.map, 0, root); - self.leaf_digests.push(leaf_digest); true - */ - todo!() } - /// Appends a new `leaf` to this branch, recomputing the relevant inner digests relative to - /// the `base` tree. + /// Appends a new `leaf` to this branch. #[inline] - fn push<T>(&mut self, base: &MerkleTree<C, T>, leaf: &Leaf<C>) -> bool - where - T: Tree<C>, - { - self.push_digest(base, || base.parameters.digest(leaf)) + fn push(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> bool { + self.push_digest(parameters, || parameters.digest(leaf)) } /// Merges `self` onto the `base` merkle tree. diff --git a/manta-crypto/src/merkle_tree/full.rs b/manta-crypto/src/merkle_tree/full.rs index 927dc7189..470000d8d 100644 --- a/manta-crypto/src/merkle_tree/full.rs +++ b/manta-crypto/src/merkle_tree/full.rs @@ -116,6 +116,11 @@ where self.leaf_digests.len() } + #[inline] + fn current_leaf(&self) -> LeafDigest<C> { + self.leaf_digests.last().cloned().unwrap_or_default() + } + #[inline] fn root(&self, parameters: &Parameters<C>) -> Root<C> { let _ = parameters; @@ -124,8 +129,20 @@ where #[inline] fn current_path(&self, parameters: &Parameters<C>) -> CurrentPath<C> { - // FIXME: self.path(parameters, 0).unwrap() - todo!() + let _ = parameters; + let default = Default::default(); + let leaf_index = Node(self.len() - 1); + CurrentPath::new( + self.get_leaf_sibling(leaf_index) + .map(Clone::clone) + .unwrap_or_default(), + leaf_index, + self.inner_digests + .path_for_leaf(leaf_index) + .filter(move |&d| d != &default) + .cloned() + .collect(), + ) } #[inline] @@ -145,7 +162,7 @@ where impl<C, M> GetPath<C> for Full<C, M> where C: Configuration + ?Sized, - M: InnerMap<C>, + M: InnerMap<C> + Default, LeafDigest<C>: Clone, InnerDigest<C>: Clone, { @@ -154,7 +171,7 @@ where #[inline] fn path(&self, parameters: &Parameters<C>, index: usize) -> Result<Path<C>, Self::Error> { let _ = parameters; - if index > 0 && index >= self.leaf_digests.len() { + if index > 0 && index >= self.len() { return Err(()); } let leaf_index = Node(index); @@ -164,7 +181,7 @@ where .unwrap_or_default(), leaf_index, self.inner_digests - .inner_path_for_leaf(leaf_index) + .path_for_leaf(leaf_index) .cloned() .collect(), )) diff --git a/manta-crypto/src/merkle_tree/inner_tree.rs b/manta-crypto/src/merkle_tree/inner_tree.rs index accb39680..e6d821eb7 100644 --- a/manta-crypto/src/merkle_tree/inner_tree.rs +++ b/manta-crypto/src/merkle_tree/inner_tree.rs @@ -18,8 +18,11 @@ // TODO: Figure out how we want to expose the meaning of `InnerNode` coordinates. Should we share // some of it, to reduce potential duplication? +// TODO: We should probably move `InnerNode` and its related `struct`s to `merkle_tree::node`. -use crate::merkle_tree::{path_length, Configuration, InnerDigest, Node, Parameters, Parity, Path}; +use crate::merkle_tree::{ + path::CurrentInnerPath, path_length, Configuration, InnerDigest, Node, Parameters, Parity, +}; use alloc::collections::btree_map; use core::{fmt::Debug, hash::Hash, iter::FusedIterator, marker::PhantomData, ops::Index}; @@ -27,7 +30,7 @@ use core::{fmt::Debug, hash::Hash, iter::FusedIterator, marker::PhantomData, ops use std::{collections::hash_map, hash::BuildHasher}; /// Inner Tree Node -#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct InnerNode { /// Depth depth: usize, @@ -191,6 +194,31 @@ where /// Sets the inner digest at `index` to `inner_digest`. fn set(&mut self, index: usize, inner_digest: InnerDigest<C>); + + /// Sets the inner digest at `index` to `inner_digest` and returns a reference to the + /// newly stored value. + #[inline] + fn set_get(&mut self, index: usize, inner_digest: InnerDigest<C>) -> &InnerDigest<C> { + self.set(index, inner_digest); + self.get(index).unwrap() + } + + /// Sets the inner digests at `lhs_index` and `rhs_index` to `lhs_digest` and `rhs_digest` + /// respectively, returning their join. + #[inline] + fn set_and_join( + &mut self, + parameters: &Parameters<C>, + lhs_index: usize, + lhs_digest: InnerDigest<C>, + rhs_index: usize, + rhs_digest: InnerDigest<C>, + ) -> InnerDigest<C> { + let digest = parameters.join(&lhs_digest, &rhs_digest); + self.set(lhs_index, lhs_digest); + self.set(rhs_index, rhs_digest); + digest + } } impl<C, M> InnerMap<C> for &mut M @@ -431,6 +459,12 @@ where self.map_get_or_sentinel(0) } + /// Sets the current root to `root`. + #[inline] + fn set_root(&mut self, root: InnerDigest<C>) { + self.map.set(0, root) + } + /// Inserts the new `inner_digest` at `node` in the tree, and returns a reference to /// `inner_digest` and its sibling in the tree in parity order. #[inline] @@ -485,19 +519,19 @@ where #[inline] pub fn insert(&mut self, parameters: &Parameters<C>, leaf_index: Node, base: InnerDigest<C>) { let root = self.compute_root(parameters, leaf_index, base); - self.map.set(0, root); + self.set_root(root); } /// Computes the inner path starting from `node`. #[inline] - pub fn inner_path(&self, node: InnerNode) -> InnerPathIter<C, M, S> { - InnerPathIter::new(self, node.iter()) + pub fn path(&self, node: InnerNode) -> InnerTreePathIter<C, M, S> { + InnerTreePathIter::new(self, node.iter()) } /// Computes the inner path of the leaf given by `leaf_index`. #[inline] - pub fn inner_path_for_leaf(&self, leaf_index: Node) -> InnerPathIter<C, M, S> { - InnerPathIter::new(self, InnerNodeIter::from_leaf::<C>(leaf_index)) + pub fn path_for_leaf(&self, leaf_index: Node) -> InnerTreePathIter<C, M, S> { + InnerTreePathIter::new(self, InnerNodeIter::from_leaf::<C>(leaf_index)) } } @@ -568,6 +602,9 @@ where { /// Inner Tree inner_tree: InnerTree<C, M, S>, + + /// Starting Leaf Index + starting_leaf_index: Node, } impl<C, M, S> PartialInnerTree<C, M, S> @@ -576,22 +613,65 @@ where M: InnerMap<C>, S: SentinelSource<C>, { - /// Builds a [`PartialInnerTree`] from `path` treated as the current path of the tree. + /// Builds a new [`PartialInnerTree`] from `inner_tree` and `starting_leaf_index`. #[inline] - pub fn from_current(path: Path<C>) -> Self { - todo!() + fn new(inner_tree: InnerTree<C, M, S>, starting_leaf_index: Node) -> Self { + Self { + inner_tree, + starting_leaf_index, + } } - /// Tries to get the inner digest at `node`, returning `None` if the inner digest is missing. + /// Builds a new [`PartialInnerTree`] from `base` and `path`. #[inline] - pub fn get(&self, node: InnerNode) -> Option<&InnerDigest<C>> { - self.inner_tree.get(node) + pub fn from_current( + parameters: &Parameters<C>, + base: InnerDigest<C>, + path: CurrentInnerPath<C>, + ) -> Self + where + M: Default, + S: Default, + { + // TODO: Remove duplicated function calls. + + let mut inner_tree = InnerTree::<C, M, S>::default(); + + let leaf_index = path.leaf_index; + let node_iter = path.into_nodes(); + + if node_iter.len() == 0 { + return inner_tree.into(); + } + + let default = Default::default(); + let root = node_iter.fold(base, |acc, (node, digest)| { + let index = node.map_index(); + match digest { + Some(digest) => { + inner_tree + .map + .set_and_join(parameters, index - 1, digest, index, acc) + } + _ => parameters.join(inner_tree.map.set_get(index, acc), &default), + } + }); + + inner_tree.set_root(root); + + Self::new(inner_tree, leaf_index) } - /// Returns the inner digest at `node` or a sentinel value if the inner digest is missing. + /// Returns the starting leaf index where the tree was constructed from. + #[inline] + pub fn starting_leaf_index(&self) -> Node { + self.starting_leaf_index + } + + /// Tries to get the inner digest at `node`, returning `None` if the inner digest is missing. #[inline] - fn get_or_sentinel(&self, node: InnerNode) -> &InnerDigest<C> { - self.inner_tree.get_or_sentinel(node) + pub fn get(&self, node: InnerNode) -> Option<&InnerDigest<C>> { + self.inner_tree.get(node) } /// Tries to return the inner digest at `index`, returning `None` if the inner digest is @@ -601,17 +681,24 @@ where self.inner_tree.map_get(index) } - /// Returns the inner digest at `index` or a sentinel value if the inner digest is missing. - #[inline] - fn map_get_or_sentinel(&self, index: usize) -> &InnerDigest<C> { - self.inner_tree.map_get_or_sentinel(index) - } - /// Returns a reference to the root inner digest. #[inline] pub fn root(&self) -> &InnerDigest<C> { self.inner_tree.root() } + + /// Inserts the `base` inner digest corresponding to the leaf at `leaf_index` into the tree. + #[inline] + pub fn insert(&mut self, parameters: &Parameters<C>, leaf_index: Node, base: InnerDigest<C>) { + self.inner_tree.insert(parameters, leaf_index, base) + } + + /// Computes the inner path of the leaf given by `leaf_index` without checking if + /// `leaf_index` is later than the starting index of this tree. + #[inline] + pub fn path_for_leaf_unchecked(&self, leaf_index: Node) -> InnerTreePathIter<C, M, S> { + self.inner_tree.path_for_leaf(leaf_index) + } } impl<C, M, S> From<InnerTree<C, M, S>> for PartialInnerTree<C, M, S> @@ -622,11 +709,11 @@ where { #[inline] fn from(inner_tree: InnerTree<C, M, S>) -> Self { - Self { inner_tree } + Self::new(inner_tree, Default::default()) } } -/// Inner Path Iterator +/// [`InnerTree`] Path Iterator #[derive(derivative::Derivative)] #[derivative( Copy(bound = ""), @@ -636,7 +723,7 @@ where Hash(bound = "M: Hash, S: Hash"), PartialEq(bound = "M: PartialEq, S: PartialEq") )] -pub struct InnerPathIter<'t, C, M = BTreeMap<C>, S = Sentinel<C>> +pub struct InnerTreePathIter<'t, C, M = BTreeMap<C>, S = Sentinel<C>> where C: Configuration + ?Sized, M: InnerMap<C>, @@ -649,13 +736,13 @@ where iter: InnerNodeIter, } -impl<'t, C, M, S> InnerPathIter<'t, C, M, S> +impl<'t, C, M, S> InnerTreePathIter<'t, C, M, S> where C: Configuration + ?Sized, M: InnerMap<C>, S: SentinelSource<C>, { - /// Builds a new [`InnerPathIter`] for `inner_tree` using `iter`. + /// Builds a new [`InnerTreePathIter`] for `inner_tree` using `iter`. #[inline] fn new(inner_tree: &'t InnerTree<C, M, S>, iter: InnerNodeIter) -> Self { Self { inner_tree, iter } @@ -663,7 +750,7 @@ where } // TODO: Add all methods which can be optimized. -impl<'t, C, M, S> Iterator for InnerPathIter<'t, C, M, S> +impl<'t, C, M, S> Iterator for InnerTreePathIter<'t, C, M, S> where C: Configuration + ?Sized, M: InnerMap<C>, @@ -682,7 +769,7 @@ where } } -impl<'t, C, M, S> ExactSizeIterator for InnerPathIter<'t, C, M, S> +impl<'t, C, M, S> ExactSizeIterator for InnerTreePathIter<'t, C, M, S> where C: Configuration + ?Sized, M: InnerMap<C>, @@ -690,7 +777,7 @@ where { } -impl<'t, C, M, S> FusedIterator for InnerPathIter<'t, C, M, S> +impl<'t, C, M, S> FusedIterator for InnerTreePathIter<'t, C, M, S> where C: Configuration + ?Sized, M: InnerMap<C>, diff --git a/manta-crypto/src/merkle_tree/mod.rs b/manta-crypto/src/merkle_tree/mod.rs index b0c3a41ef..103a2b77e 100644 --- a/manta-crypto/src/merkle_tree/mod.rs +++ b/manta-crypto/src/merkle_tree/mod.rs @@ -26,13 +26,13 @@ // FIXME: Get rid of as many `pub(super)` declarations as we can. mod node; -mod path; mod tree; pub mod fork; pub mod full; pub mod inner_tree; pub mod partial; +pub mod path; pub mod single_leaf; #[cfg(feature = "test")] @@ -40,5 +40,5 @@ pub mod single_leaf; pub mod test; pub use node::*; -pub use path::*; +pub use path::prelude::*; pub use tree::*; diff --git a/manta-crypto/src/merkle_tree/node.rs b/manta-crypto/src/merkle_tree/node.rs index ea9476762..60e056002 100644 --- a/manta-crypto/src/merkle_tree/node.rs +++ b/manta-crypto/src/merkle_tree/node.rs @@ -55,6 +55,34 @@ impl Parity { matches!(self, Self::Right) } + /// Returns the output of `f` if `self` is [`Left`](Self::Left), or returns a default value + /// otherwise. + #[inline] + pub fn left_or_default<T, F>(&self, f: F) -> T + where + T: Default, + F: FnOnce() -> T, + { + match self { + Self::Left => f(), + Self::Right => Default::default(), + } + } + + /// Returns the output of `f` if `self` is [`Right`](Self::Right), or returns a default value + /// otherwise. + #[inline] + pub fn right_or_default<T, F>(&self, f: F) -> T + where + T: Default, + F: FnOnce() -> T, + { + match self { + Self::Left => Default::default(), + Self::Right => f(), + } + } + /// Maps `self` to the output of `lhs` and `rhs` depending on its parity. #[inline] pub fn map<T, L, R>(self, lhs: L, rhs: R) -> T diff --git a/manta-crypto/src/merkle_tree/partial.rs b/manta-crypto/src/merkle_tree/partial.rs index ef1f20df2..2bd646ae6 100644 --- a/manta-crypto/src/merkle_tree/partial.rs +++ b/manta-crypto/src/merkle_tree/partial.rs @@ -21,8 +21,7 @@ use crate::merkle_tree::{ capacity, inner_tree::{BTreeMap, InnerMap, PartialInnerTree}, - Configuration, CurrentPath, GetPath, InnerDigest, LeafDigest, MerkleTree, Node, Parameters, - Path, Root, Tree, + Configuration, CurrentPath, InnerDigest, LeafDigest, MerkleTree, Node, Parameters, Root, Tree, }; use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; @@ -58,11 +57,23 @@ where M: InnerMap<C>, { /// Returns the leaf digests currently stored in the merkle tree. + /// + /// # Note + /// + /// Since this tree does not start its leaf nodes from the first possible index, indexing into + /// this slice will not be the same as indexing into a slice from a full tree. For all other + /// indexing, use the full indexing scheme. #[inline] pub fn leaf_digests(&self) -> &[LeafDigest<C>] { &self.leaf_digests } + /// Returns the starting leaf index for this tree. + #[inline] + pub fn starting_leaf_index(&self) -> Node { + self.inner_digests.starting_leaf_index() + } + /// Returns a reference to the root inner digest. #[inline] pub fn root(&self) -> &InnerDigest<C> { @@ -72,11 +83,8 @@ where /// Returns the sibling leaf node to `index`. #[inline] fn get_leaf_sibling(&self, index: Node) -> Option<&LeafDigest<C>> { - /* TODO: - // TODO: Add `Index` methods to accept `Node` as indices. - self.leaf_digests.get(index.sibling().0) - */ - todo!() + self.leaf_digests + .get((index - self.starting_leaf_index().0).sibling().0) } /// Appends a `leaf_digest` with index given by `leaf_index` into the tree. @@ -87,7 +95,6 @@ where leaf_index: Node, leaf_digest: LeafDigest<C>, ) { - /* TODO: self.inner_digests.insert( parameters, leaf_index, @@ -99,8 +106,6 @@ where ), ); self.leaf_digests.push(leaf_digest); - */ - todo!() } } @@ -119,27 +124,36 @@ where #[inline] fn len(&self) -> usize { - /* TODO: - self.leaf_digests.len() - */ - todo!() + self.starting_leaf_index().0 + self.leaf_digests.len() + } + + #[inline] + fn current_leaf(&self) -> LeafDigest<C> { + self.leaf_digests.last().cloned().unwrap_or_default() } #[inline] fn root(&self, parameters: &Parameters<C>) -> Root<C> { - /* TODO: let _ = parameters; Root(self.root().clone()) - */ - todo!() } #[inline] fn current_path(&self, parameters: &Parameters<C>) -> CurrentPath<C> { - /* TODO: - self.path(parameters, 0).unwrap() - */ - todo!() + let _ = parameters; + let default = Default::default(); + let leaf_index = Node(self.len() - 1); + CurrentPath::new( + self.get_leaf_sibling(leaf_index) + .map(Clone::clone) + .unwrap_or_default(), + leaf_index, + self.inner_digests + .path_for_leaf_unchecked(leaf_index) + .filter(move |&d| d != &default) + .cloned() + .collect(), + ) } #[inline] @@ -147,18 +161,17 @@ where where F: FnOnce() -> Option<LeafDigest<C>>, { - /* TODO: let len = self.len(); if len >= capacity::<C>() { return Some(false); } self.push_leaf_digest(parameters, Node(len), leaf_digest()?); Some(true) - */ - todo!() } } +/* TODO: Implement `GetPath` for `Partial` + impl<C, M> GetPath<C> for Partial<C, M> where C: Configuration + ?Sized, @@ -170,6 +183,7 @@ where #[inline] fn path(&self, parameters: &Parameters<C>, index: usize) -> Result<Path<C>, Self::Error> { + // TODO: Make sure we don't query paths too far to the left. /* TODO: let _ = parameters; if index > 0 && index >= self.leaf_digests.len() { @@ -182,7 +196,7 @@ where .map(Clone::clone) .unwrap_or_default(), self.inner_digests - .inner_path_for_leaf(leaf_index) + .path_for_leaf_unchecked(leaf_index) .cloned() .collect(), )) @@ -190,3 +204,5 @@ where todo!() } } + +*/ diff --git a/manta-crypto/src/merkle_tree/path.rs b/manta-crypto/src/merkle_tree/path.rs index 8378dda5a..8caf30372 100644 --- a/manta-crypto/src/merkle_tree/path.rs +++ b/manta-crypto/src/merkle_tree/path.rs @@ -19,19 +19,25 @@ // TODO: Move some methods to a `raw` module for paths. use crate::merkle_tree::{ - inner_tree::InnerNodeIter, node::Parity, path_length, Configuration, InnerDigest, Leaf, - LeafDigest, Node, Parameters, Root, + inner_tree::{InnerNode, InnerNodeIter}, + path_length, Configuration, InnerDigest, Leaf, LeafDigest, Node, Parameters, Parity, Root, }; -use alloc::vec::Vec; +use alloc::vec::{self, Vec}; use core::{ + convert::{TryFrom, TryInto}, fmt::Debug, hash::Hash, iter::FusedIterator, mem, ops::{Index, IndexMut}, - slice::{self, SliceIndex}, + slice::SliceIndex, }; +pub(super) mod prelude { + #[doc(inline)] + pub use super::{CurrentPath, Path}; +} + /// Merkle Tree Inner Path #[derive(derivative::Derivative)] #[derivative( @@ -69,6 +75,18 @@ where Self { leaf_index, path } } + /// Checks if `self` could represent the [`CurrentInnerPath`] of some tree. + #[inline] + pub fn is_current(&self) -> bool { + let default = Default::default(); + InnerNodeIter::from_leaf::<C>(self.leaf_index) + .zip(self.path.iter()) + .all(move |(node, d)| match node.parity() { + Parity::Left => d == &default, + Parity::Right => true, + }) + } + /// Computes the root of the merkle tree relative to `base` using `parameters`. #[inline] pub fn root_from_base(&self, parameters: &Parameters<C>, base: InnerDigest<C>) -> Root<C> { @@ -115,7 +133,7 @@ where /// Folds `iter` into a root using the path folding algorithm for [`InnerPath`]. #[inline] - pub(super) fn fold<'i, I>( + fn fold<'i, I>( parameters: &'i Parameters<C>, index: Node, base: InnerDigest<C>, @@ -161,7 +179,7 @@ where { #[inline] fn from(path: CurrentInnerPath<C>) -> Self { - todo!() + InnerPath::new(path.leaf_index, path.into_iter().collect()) } } @@ -231,15 +249,24 @@ where Self { leaf_index, path } } + /// Builds a new [`CurrentInnerPath`] from an [`InnerPath`] without checking that `path` + /// satisfies [`InnerPath::is_current`]. + #[inline] + pub fn from_path_unchecked(mut path: InnerPath<C>) -> Self { + let default = Default::default(); + path.path.retain(|d| d != &default); + Self::new(path.leaf_index, path.path) + } + /// Computes the root of the merkle tree relative to `base` using `parameters`. #[inline] pub fn root_from_base(&self, parameters: &Parameters<C>, base: InnerDigest<C>) -> Root<C> { Self::fold( - &Default::default(), parameters, 0, self.leaf_index, base, + &Default::default(), &self.path, ) } @@ -273,14 +300,39 @@ where root == &self.root(parameters, leaf_digest, sibling_digest) } + /// Returns an iterator over the elements of [`self.path`](Self::path) as [`InnerNode`] + /// objects, yielding elements of the path if they are not default. + #[inline] + pub fn into_nodes(self) -> CurrentInnerPathNodeIter<C> { + CurrentInnerPathNodeIter::new(self) + } + + /// Computes the folding algorithm for a path with `index` as its starting index. + #[inline] + fn fold_fn<'d, D>( + parameters: &'d Parameters<C>, + index: Node, + accumulator: &'d InnerDigest<C>, + default: &'d InnerDigest<C>, + digest: D, + ) -> InnerDigest<C> + where + D: FnOnce() -> &'d InnerDigest<C>, + { + match index.parity() { + Parity::Left => parameters.join(accumulator, default), + Parity::Right => parameters.join(digest(), accumulator), + } + } + /// Folds `iter` into a root using the path folding algorithm for [`CurrentInnerPath`]. #[inline] fn fold<'i, I>( - default: &'i InnerDigest<C>, parameters: &'i Parameters<C>, depth: usize, mut index: Node, base: InnerDigest<C>, + default: &'i InnerDigest<C>, iter: I, ) -> Root<C> where @@ -288,18 +340,15 @@ where I: IntoIterator<Item = &'i InnerDigest<C>>, { let mut iter = iter.into_iter().peekable(); - let mut accumulator = base; - for _ in depth..path_length::<C>() { - accumulator = match index.into_parent().parity() { - Parity::Left => parameters.join(&accumulator, default), - Parity::Right => parameters.join(iter.next().unwrap(), &accumulator), - }; - } - Root(accumulator) + Root((depth..path_length::<C>()).fold(base, |acc, _| { + Self::fold_fn(parameters, index.into_parent(), &acc, default, || { + iter.next().unwrap() + }) + })) } - /// Updates the path to the next current path with `next_leaf_digest`, updating `leaf_digest` - /// and `sibling_digest` as necessary. + /// Updates `self` to the next current path with `next_leaf_digest`, updating `leaf_digest` + /// and `sibling_digest` as needed. #[inline] fn update( &mut self, @@ -322,25 +371,35 @@ where let default_inner_digest = Default::default(); + let mut i = 0; let mut depth = 0; while !Node::are_siblings(&last_index.into_parent(), &index.into_parent()) { - last_accumulator = match last_index.parity() { - Parity::Left => parameters.join(&last_accumulator, &default_inner_digest), - Parity::Right => parameters.join(&self.path.remove(0), &last_accumulator), - }; + last_accumulator = Self::fold_fn( + parameters, + last_index, + &last_accumulator, + &default_inner_digest, + || { + let next = &self.path[i]; + i += 1; + next + }, + ); accumulator = parameters.join(&accumulator, &default_inner_digest); depth += 1; } - self.path.insert(0, last_accumulator); + mem::drop(self.path.drain(1..i)); + + self.path[0] = last_accumulator; accumulator = parameters.join(&self.path[0], &accumulator); Self::fold( - &default_inner_digest, parameters, depth + 1, index, accumulator, + &default_inner_digest, &self.path[1..], ) } @@ -362,6 +421,129 @@ where } } +impl<C> TryFrom<InnerPath<C>> for CurrentInnerPath<C> +where + C: Configuration + ?Sized, +{ + type Error = InnerPath<C>; + + #[inline] + fn try_from(path: InnerPath<C>) -> Result<Self, Self::Error> { + if path.is_current() { + Ok(CurrentInnerPath::from_path_unchecked(path)) + } else { + Err(path) + } + } +} + +impl<C> IntoIterator for CurrentInnerPath<C> +where + C: Configuration + ?Sized, +{ + type Item = InnerDigest<C>; + + type IntoIter = CurrentInnerPathIntoIter<C>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + Self::IntoIter { + current_path_iter: self.path.into_iter(), + node_iter: InnerNodeIter::from_leaf::<C>(self.leaf_index), + } + } +} + +/// Owning Iterator for [`CurrentInnerPath`] +pub struct CurrentInnerPathIntoIter<C> +where + C: Configuration + ?Sized, +{ + /// Current Path Iterator + current_path_iter: vec::IntoIter<InnerDigest<C>>, + + /// Inner Node Iterator + node_iter: InnerNodeIter, +} + +impl<C> Iterator for CurrentInnerPathIntoIter<C> +where + C: Configuration + ?Sized, +{ + type Item = InnerDigest<C>; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + Some( + self.node_iter + .next()? + .parity() + .right_or_default(|| self.current_path_iter.next().unwrap()), + ) + } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + self.node_iter.size_hint() + } +} + +impl<C> ExactSizeIterator for CurrentInnerPathIntoIter<C> where C: Configuration + ?Sized {} + +impl<C> FusedIterator for CurrentInnerPathIntoIter<C> where C: Configuration + ?Sized {} + +/// [`InnerNode`] Iterator for [`CurrentInnerPath`] +pub struct CurrentInnerPathNodeIter<C> +where + C: Configuration + ?Sized, +{ + /// Current Path Iterator + current_path_iter: vec::IntoIter<InnerDigest<C>>, + + /// Inner Node Iterator + node_iter: InnerNodeIter, +} + +impl<C> CurrentInnerPathNodeIter<C> +where + C: Configuration + ?Sized, +{ + /// Builds a new [`CurrentInnerPathNodeIter`] from a [`CurrentInnerPath`]. + #[inline] + pub fn new(path: CurrentInnerPath<C>) -> Self { + Self { + current_path_iter: path.path.into_iter(), + node_iter: InnerNodeIter::from_leaf::<C>(path.leaf_index), + } + } +} + +impl<C> Iterator for CurrentInnerPathNodeIter<C> +where + C: Configuration + ?Sized, +{ + type Item = (InnerNode, Option<InnerDigest<C>>); + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + let node = self.node_iter.next()?; + Some(( + node, + node.parity() + .right_or_default(|| Some(self.current_path_iter.next().unwrap())), + )) + } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + self.node_iter.size_hint() + } +} + +impl<C> ExactSizeIterator for CurrentInnerPathNodeIter<C> where C: Configuration + ?Sized {} + +impl<C> FusedIterator for CurrentInnerPathNodeIter<C> where C: Configuration + ?Sized {} + /// Merkle Tree Path #[derive(derivative::Derivative)] #[derivative( @@ -412,6 +594,12 @@ where self.inner_path.leaf_index } + /// Checks if `self` could represent the [`CurrentPath`] of some tree. + #[inline] + pub fn is_current(&self) -> bool { + self.inner_path.is_current() + } + /// Computes the root of the merkle tree relative to `leaf_digest` using `parameters`. #[inline] pub fn root(&self, parameters: &Parameters<C>, leaf_digest: &LeafDigest<C>) -> Root<C> { @@ -438,23 +626,6 @@ where pub fn verify(&self, parameters: &Parameters<C>, root: &Root<C>, leaf: &Leaf<C>) -> bool { self.verify_digest(parameters, root, &parameters.digest(leaf)) } - - /* TODO: - /// Folds `iter` into a root using the path folding algorithm for [`Path`]. - #[inline] - pub(super) fn fold<'i, I>( - parameters: &'i Parameters<C>, - index: Node, - base: InnerDigest<C>, - iter: I, - ) -> Root<C> - where - InnerDigest<C>: 'i, - I: IntoIterator<Item = &'i InnerDigest<C>>, - { - InnerPath::fold(parameters, index, base, iter) - } - */ } impl<C> From<CurrentPath<C>> for Path<C> @@ -535,6 +706,16 @@ where } } + /// Builds a new [`CurrentPath`] from a [`Path`] without checking that `path` satisfies + /// [`Path::is_current`]. + #[inline] + pub fn from_path_unchecked(path: Path<C>) -> Self { + Self::from_inner( + path.sibling_digest, + CurrentInnerPath::from_path_unchecked(path.inner_path), + ) + } + /// Returns the leaf index for this [`CurrentPath`]. #[inline] pub fn leaf_index(&self) -> Node { @@ -581,6 +762,25 @@ where } } +impl<C> TryFrom<Path<C>> for CurrentPath<C> +where + C: Configuration + ?Sized, +{ + type Error = Path<C>; + + #[inline] + fn try_from(path: Path<C>) -> Result<Self, Self::Error> { + let Path { + sibling_digest, + inner_path, + } = path; + match inner_path.try_into() { + Ok(inner_path) => Ok(Self::from_inner(sibling_digest, inner_path)), + Err(inner_path) => Err(Path::from_inner(sibling_digest, inner_path)), + } + } +} + /* TODO: Compressed Path Implementation /// Compressed Path @@ -697,7 +897,7 @@ where /// only the positions in the path of those default values. #[inline] pub fn compress(self) -> CompressedPath<C> { - let default_inner_digest = Default::default(); + let default = Default::default(); let mut started = false; let mut sentinel_ranges = Vec::new(); let inner_path = self @@ -705,7 +905,7 @@ where .into_iter() .enumerate() .filter_map(|(i, d)| { - if d == default_inner_digest { + if d == default { if !started { sentinel_ranges.push(i); started = true; diff --git a/manta-crypto/src/merkle_tree/single_leaf.rs b/manta-crypto/src/merkle_tree/single_leaf.rs index 1800d117a..0bcbd2a71 100644 --- a/manta-crypto/src/merkle_tree/single_leaf.rs +++ b/manta-crypto/src/merkle_tree/single_leaf.rs @@ -19,10 +19,23 @@ // TODO: Should we be storing the root? Can we have a version where we don't? use crate::merkle_tree::{ - capacity, Configuration, CurrentPath, InnerDigest, InnerPath, LeafDigest, MerkleTree, Node, - Parameters, Parity, Path, Root, Tree, + capacity, Configuration, CurrentPath, InnerDigest, LeafDigest, MerkleTree, Parameters, Root, + Tree, }; -use core::{fmt::Debug, hash::Hash, mem}; +use core::{fmt::Debug, hash::Hash}; + +/// Tree Length State +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum Length { + /// Empty Tree + Empty, + + /// Can Accept Leaves + CanAccept, + + /// Full Tree + Full, +} /// Single Leaf Merkle Tree Type pub type SingleLeafMerkleTree<C> = MerkleTree<C, SingleLeaf<C>>; @@ -45,7 +58,7 @@ where leaf_digest: Option<LeafDigest<C>>, /// Current Path - path: Path<C>, + current_path: CurrentPath<C>, /// Root root: Root<C>, @@ -61,20 +74,19 @@ where if self.leaf_digest.is_none() { 0 } else { - self.path.leaf_index().0 + 1 + self.current_path.leaf_index().0 + 1 } } - /// Returns the next avaiable index or `None` if the merkle tree is full. + /// Returns the state of the length of this tree. #[inline] - fn next_index(&self) -> Option<Node> { - let len = self.len(); - if len == 0 { - Some(Node(0)) - } else if len < capacity::<C>() - 1 { - Some(Node(len + 1)) + pub fn length_state(&self) -> Length { + if self.leaf_digest.is_none() { + Length::Empty + } else if self.current_path.leaf_index().0 < capacity::<C>() - 2 { + Length::CanAccept } else { - None + Length::Full } } @@ -84,13 +96,11 @@ where &self.root } - /* TODO: /// Returns the current merkle tree path for the current leaf. #[inline] pub fn current_path(&self) -> &CurrentPath<C> { &self.current_path } - */ /// Returns the currently stored leaf digest, returning `None` if the tree is empty. #[inline] @@ -98,23 +108,12 @@ where self.leaf_digest.as_ref() } - /// Returns a shared reference to the current leaf digest. - #[inline] - fn leaf_digest_ref(&self) -> &LeafDigest<C> { - self.leaf_digest().unwrap() - } - - /// Returns a mutable reference to the current leaf digest. - #[inline] - fn leaf_digest_mut_ref(&mut self) -> &mut LeafDigest<C> { - self.leaf_digest.as_mut().unwrap() - } - /// Computes the root of the tree under the assumption that `self.leaf_digest.is_some()` /// evaluates to `true`. #[inline] fn compute_root(&self, parameters: &Parameters<C>) -> Root<C> { - self.path.root(parameters, self.leaf_digest_ref()) + self.current_path + .root(parameters, self.leaf_digest().unwrap()) } } @@ -135,6 +134,16 @@ where self.len() } + #[inline] + fn is_empty(&self) -> bool { + self.leaf_digest.is_none() + } + + #[inline] + fn current_leaf(&self) -> LeafDigest<C> { + self.leaf_digest.as_ref().cloned().unwrap_or_default() + } + #[inline] fn root(&self, parameters: &Parameters<C>) -> Root<C> { let _ = parameters; @@ -143,11 +152,8 @@ where #[inline] fn current_path(&self, parameters: &Parameters<C>) -> CurrentPath<C> { - /* TODO: let _ = parameters; self.current_path.clone() - */ - todo!() } #[inline] @@ -155,66 +161,20 @@ where where F: FnOnce() -> Option<LeafDigest<C>>, { - let mut index = match self.next_index() { - Some(index) => index, - _ => return Some(false), - }; - - let leaf_digest = leaf_digest()?; - - if index == 0 { - self.leaf_digest = Some(leaf_digest); - self.root = self.compute_root(parameters); - } else { - self.path.inner_path.leaf_index = index; - match index.parity() { - Parity::Left => { - let mut last_index = index - 1; - let mut last_accumulator = parameters.join_leaves( - &self.path.sibling_digest, - &mem::replace(self.leaf_digest.as_mut().unwrap(), leaf_digest), - ); - - self.path.sibling_digest = Default::default(); - - let mut accumulator = - parameters.join_leaves(self.leaf_digest_ref(), &self.path.sibling_digest); - - let mut i = 0; - while !Node::are_siblings(&last_index.into_parent(), &index.into_parent()) { - last_accumulator = last_index.join( - parameters, - &last_accumulator, - &self.path.inner_path.path[i], - ); - self.path.inner_path.path[i] = Default::default(); - accumulator = parameters.join(&accumulator, &self.path.inner_path.path[i]); - i += 1; - } - - self.path.inner_path.path[i] = last_accumulator; - accumulator = parameters.join(&self.path.inner_path.path[i], &accumulator); - - self.root = InnerPath::fold( - parameters, - index, - accumulator, - &self.path.inner_path.path[i + 1..], - ); - } - Parity::Right => { - self.path.sibling_digest = - mem::replace(self.leaf_digest_mut_ref(), leaf_digest); - self.root = self.compute_root(parameters); - } + match self.length_state() { + Length::Full => return Some(false), + Length::Empty => { + self.leaf_digest = Some(leaf_digest()?); + self.root = self.compute_root(parameters); + } + Length::CanAccept => { + self.root = self.current_path.update( + parameters, + self.leaf_digest.as_mut().unwrap(), + leaf_digest()?, + ); } - /* TODO: - self.root = - self.current_path - .update(parameters, self.leaf_digest_mut_ref(), leaf_digest); - */ } - Some(true) } } diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index da13fc48f..2e1ba2026 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -204,6 +204,9 @@ where self.len() == 0 } + /// Returns the current (i.e. right-most) leaf. + fn current_leaf(&self) -> LeafDigest<C>; + /// Returns the [`Root`] of the merkle tree. fn root(&self, parameters: &Parameters<C>) -> Root<C>; @@ -529,6 +532,12 @@ where capacity::<C>() } + /// Returns the current (i.e right-most) leaf. + #[inline] + pub fn current_leaf(&self) -> LeafDigest<C> { + self.tree.current_leaf() + } + /// Returns the [`Root`] of the merkle tree. #[inline] pub fn root(&self) -> Root<C> { diff --git a/manta-crypto/src/set.rs b/manta-crypto/src/set.rs index 7a82c1722..366249226 100644 --- a/manta-crypto/src/set.rs +++ b/manta-crypto/src/set.rs @@ -23,7 +23,7 @@ // FIXME: The `Set::contains` method is not really something we can always implement properly. // FIXME: Should we just get rid of `Set` and just ensure we can get proofs working? -pub(crate) mod prelude { +pub(super) mod prelude { #[doc(inline)] pub use super::{Set, VerifiedSet}; } diff --git a/manta-pay/src/crypto/merkle_tree/mod.rs b/manta-pay/src/crypto/merkle_tree/mod.rs index fce3c08e7..fa0c2785f 100644 --- a/manta-pay/src/crypto/merkle_tree/mod.rs +++ b/manta-pay/src/crypto/merkle_tree/mod.rs @@ -402,8 +402,8 @@ pub mod constraint { fn convert_path(path: &Path<ConfigConverter<C>>) -> PathInnerType<C> { PathInnerType { leaf_sibling_hash: path.sibling_digest.clone(), - auth_path: path.inner_path.iter().rev().cloned().collect(), - leaf_index: path.leaf_index.0, + auth_path: path.inner_path.path.iter().rev().cloned().collect(), + leaf_index: path.inner_path.leaf_index.0, } } diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index 6294cfb2e..ac9ea5b00 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -61,3 +61,17 @@ pub enum Either<L, R> { /// Right Variant Right(R), } + +/// Folds every element into an accumulator by applying an operation, returning the final result. +/// +/// This function differs from [`Iterator::fold`] because its initial state is borrowed instead +/// of moved. This means that we have to return `Option<B>` in case the iterator is empty. +#[inline] +pub fn fold_ref<I, B, F>(mut iter: I, init: &B, mut f: F) -> Option<B> +where + I: Iterator, + F: FnMut(&B, I::Item) -> B, +{ + iter.next() + .map(move |first| iter.fold(f(init, first), move |acc, n| f(&acc, n))) +} From c4f2587068fd362ff9bd020d08a75e1d76fd2f5b Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 30 Sep 2021 16:47:20 -0400 Subject: [PATCH 069/275] fixes and cleanup for fork impl --- manta-accounting/src/transfer.rs | 24 +- manta-crypto/src/merkle_tree/fork.rs | 360 +++++++-------- manta-crypto/src/merkle_tree/full.rs | 68 ++- manta-crypto/src/merkle_tree/inner_tree.rs | 79 +++- manta-crypto/src/merkle_tree/node.rs | 20 + manta-crypto/src/merkle_tree/partial.rs | 88 +++- manta-crypto/src/merkle_tree/path.rs | 45 +- manta-crypto/src/merkle_tree/test.rs | 4 +- manta-crypto/src/merkle_tree/tree.rs | 20 +- .../{merkle_tree/mod.rs => merkle_tree.rs} | 7 - .../src/crypto/merkle_tree/incremental.rs | 422 ------------------ 11 files changed, 423 insertions(+), 714 deletions(-) rename manta-pay/src/crypto/{merkle_tree/mod.rs => merkle_tree.rs} (97%) delete mode 100644 manta-pay/src/crypto/merkle_tree/incremental.rs diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 46cc026da..0c990ee5a 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -499,17 +499,13 @@ where /// Checks that the sender side is not empty. #[inline] fn check_sender_side() { - if SENDERS == 0 { - panic!("Not enough senders.") - } + assert_ne!(SENDERS, 0, "Not enough senders.") } /// Checks that the receiver side is not empty. #[inline] fn check_receiver_side() { - if RECEIVERS == 0 { - panic!("Not enough receivers.") - } + assert_ne!(RECEIVERS, 0, "Not enough receivers.") } /// Checks that the number of senders and/or receivers does not exceed the allocation limit. @@ -645,17 +641,21 @@ where /// Checks that the sender side is not empty. #[inline] fn check_sender_side() { - if SOURCES + SENDERS == 0 { - panic!("Not enough participants on the sender side."); - } + assert_ne!( + SOURCES + SENDERS, + 0, + "Not enough participants on the sender side." + ) } /// Checks that the receiver side is not empty. #[inline] fn check_receiver_side() { - if RECEIVERS + SINKS == 0 { - panic!("Not enough participants on the receiver side."); - } + assert_ne!( + RECEIVERS + SINKS, + 0, + "Not enough participants on the receiver side." + ) } /// Builds a new [`Transfer`] without checking the number of participants on the sender and diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs index 3c682970e..f1ced2cb7 100644 --- a/manta-crypto/src/merkle_tree/fork.rs +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -19,14 +19,15 @@ // TODO: Think about whether we want to keep the `raw::MerkleTreePointerFamily` API sealed or not. // TODO: Implement derive-able traits for these types. // TODO: See if we can get rid of the smart pointer logic. -// TODO: Reduce duplication in this file, relative to `merkle_tree::partial`. use crate::merkle_tree::{ capacity, inner_tree::{BTreeMap, InnerMap, PartialInnerTree}, - Configuration, InnerDigest, Leaf, LeafDigest, MerkleTree, Node, Parameters, Tree, + partial::Partial, + path::CurrentInnerPath, + Configuration, InnerDigest, Leaf, LeafDigest, MerkleTree, Node, Parity, Tree, }; -use alloc::vec::Vec; +use alloc::{vec, vec::Vec}; use core::{borrow::Borrow, fmt::Debug, hash::Hash, mem, ops::Deref}; /// Fork-able Merkle Tree @@ -71,14 +72,20 @@ where /// Creates a new fork of this trunk. #[inline] - pub fn fork(&self) -> Fork<C, T, P> { + pub fn fork<M>(&self) -> Fork<C, T, P, M> + where + M: InnerMap<C> + Default, + { Fork::new(self) } /// Tries to attach `fork` to `self` as its new trunk, returning `false` if `fork` has /// too many leaves to fit in `self`. #[inline] - pub fn attach(&self, fork: &mut Fork<C, T, P>) -> bool { + pub fn attach<M>(&self, fork: &mut Fork<C, T, P, M>) -> bool + where + M: InnerMap<C> + Default, + { fork.attach(self) } @@ -95,10 +102,13 @@ where /// the leaves which were added in this merge will exist before the first leaf in the fork in /// the final tree. #[inline] - pub fn merge(&mut self, fork: Fork<C, T, P>) -> Result<(), Fork<C, T, P>> { + pub fn merge<M>(&mut self, fork: Fork<C, T, P, M>) -> Result<(), Fork<C, T, P, M>> + where + M: InnerMap<C> + Default, + { match fork.get_attached_base(self) { Some(base) => { - self.merge_branch(base, fork.branch); + self.merge_branch(base, fork.base_contribution, fork.branch); Ok(()) } _ => Err(fork), @@ -108,10 +118,23 @@ where /// Performs a merge of the `branch` onto `fork_base`, setting `self` equal to the resulting /// merged tree. #[inline] - fn merge_branch(&mut self, fork_base: P::Strong, branch: Branch<C>) { + fn merge_branch<M>( + &mut self, + fork_base: P::Strong, + base_contribution: BaseContribution, + branch: Partial<C, M>, + ) where + M: InnerMap<C> + Default, + { self.base = Some(fork_base); let mut base = P::claim(mem::take(&mut self.base).unwrap()); - branch.merge(&mut base); + assert!(base + .tree + .extend_digests( + &base.parameters, + Fork::<C, T, P, M>::extract_leaves(base_contribution, branch), + ) + .is_ok()); self.base = Some(P::new(base)); } @@ -172,25 +195,50 @@ where } } +/// Base Tree Leaf Contribution +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +enum BaseContribution { + /// No Leaves Contributed + Empty, + + /// Left Leaf Contributed + LeftLeaf, + + /// Both Leaves Contributed + BothLeaves, +} + +impl Default for BaseContribution { + #[inline] + fn default() -> Self { + Self::Empty + } +} + /// Merkle Tree Fork -pub struct Fork<C, T, P = raw::SingleThreaded> +pub struct Fork<C, T, P = raw::SingleThreaded, M = BTreeMap<C>> where C: Configuration + ?Sized, T: Tree<C>, P: raw::MerkleTreePointerFamily<C, T>, + M: InnerMap<C> + Default, { /// Base Merkle Tree base: P::Weak, + /// Base Tree Contribution + base_contribution: BaseContribution, + /// Branch Data - branch: Branch<C>, + branch: Partial<C, M>, } -impl<C, T, P> Fork<C, T, P> +impl<C, T, P, M> Fork<C, T, P, M> where C: Configuration + ?Sized, T: Tree<C>, P: raw::MerkleTreePointerFamily<C, T>, + M: InnerMap<C> + Default, { /// Builds a new [`Fork`] from `trunk`. #[inline] @@ -202,17 +250,125 @@ where /// appending `leaf_digests` would exceed the capacity of the `trunk`. #[inline] pub fn with_leaves(trunk: &Trunk<C, T, P>, leaf_digests: Vec<LeafDigest<C>>) -> Option<Self> { + let (base_contribution, branch) = + Self::new_branch(trunk.borrow_base().as_ref(), leaf_digests)?; Some(Self { base: trunk.downgrade(), - branch: Branch::new(trunk.borrow_base().as_ref(), leaf_digests)?, + base_contribution, + branch, }) } + /// Builds a new branch off of `base`, extending by `leaf_digests`. + #[inline] + fn new_branch( + base: &MerkleTree<C, T>, + leaf_digests: Vec<LeafDigest<C>>, + ) -> Option<(BaseContribution, Partial<C, M>)> { + if leaf_digests.len() + base.len() >= capacity::<C>() { + return None; + } + Some(Self::new_branch_unchecked(base, leaf_digests)) + } + + /// Builds a new branch off of `base`, extending by `leaf_digests` without checking that + /// `base` can accept new leaves. + #[inline] + fn new_branch_unchecked( + base: &MerkleTree<C, T>, + leaf_digests: Vec<LeafDigest<C>>, + ) -> (BaseContribution, Partial<C, M>) { + let (base_contribution, base_inner_digest, base_leaf_digests, inner_path) = + Self::generate_branch_setup(base); + let mut partial = Partial::new_unchecked( + base_leaf_digests, + PartialInnerTree::from_current(&base.parameters, base_inner_digest, inner_path), + ); + let partial_tree_len = partial.len(); + for (i, digest) in leaf_digests.into_iter().enumerate() { + partial.push_leaf_digest(&base.parameters, Node(partial_tree_len + i), digest); + } + (base_contribution, partial) + } + + /// Generates the setup data to compute [`new_branch_unchecked`](Self::new_branch_unchecked). + #[inline] + fn generate_branch_setup( + base: &MerkleTree<C, T>, + ) -> ( + BaseContribution, + InnerDigest<C>, + Vec<LeafDigest<C>>, + CurrentInnerPath<C>, + ) { + if base.is_empty() { + ( + BaseContribution::Empty, + Default::default(), + Default::default(), + base.current_path().inner_path, + ) + } else { + let current_leaf = base.current_leaf(); + let current_path = base.current_path(); + match current_path.leaf_index().parity() { + Parity::Left => ( + BaseContribution::LeftLeaf, + base.parameters + .join_leaves(&current_leaf, &current_path.sibling_digest), + vec![current_leaf], + current_path.inner_path, + ), + Parity::Right => ( + BaseContribution::BothLeaves, + base.parameters + .join_leaves(&current_path.sibling_digest, &current_leaf), + vec![current_path.sibling_digest, current_leaf], + current_path.inner_path, + ), + } + } + } + + /// Extracts the non-base leaves from `branch`. + #[inline] + fn extract_leaves( + base_contribution: BaseContribution, + branch: Partial<C, M>, + ) -> Vec<LeafDigest<C>> { + let mut leaf_digests = branch.into_leaves(); + mem::drop(leaf_digests.drain(0..base_contribution as usize)); + leaf_digests + } + + /// Tries to rebase `branch` at `base`. + #[inline] + fn try_rebase( + base: &MerkleTree<C, T>, + base_contribution: &mut BaseContribution, + branch: &mut Partial<C, M>, + ) -> bool { + if branch.len() + base.len() - (*base_contribution as usize) >= capacity::<C>() { + return false; + } + let (new_base_contribution, new_branch) = Self::new_branch_unchecked( + base, + Self::extract_leaves(*base_contribution, mem::take(branch)), + ); + *base_contribution = new_base_contribution; + *branch = new_branch; + true + } + /// Tries to attach this fork to a new `trunk`, returning `false` if `self` has too many leaves /// to fit in `trunk`. #[inline] pub fn attach(&mut self, trunk: &Trunk<C, T, P>) -> bool { - if !self.branch.try_rebase(trunk.borrow_base().as_ref()) { + if !Self::try_rebase( + trunk.borrow_base().as_ref(), + &mut self.base_contribution, + &mut self.branch, + ) { return false; } self.base = trunk.downgrade(); @@ -271,182 +427,6 @@ where } } -/// Merkle Tree Fork Branch -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone, M: Clone"), - Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug, M: Debug"), - Default(bound = "M: Default"), - Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq, M: Eq"), - Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash, M: Hash"), - PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq, M: PartialEq") -)] -pub struct Branch<C, M = BTreeMap<C>> -where - C: Configuration + ?Sized, - M: InnerMap<C> + Default, -{ - /// Leaf Digests - pub leaf_digests: Vec<LeafDigest<C>>, - - /// Inner Digests - pub inner_digests: PartialInnerTree<C, M>, -} - -impl<C, M> Branch<C, M> -where - C: Configuration + ?Sized, - M: InnerMap<C> + Default, -{ - /// Builds a new [`Branch`] from `leaf_digests` and `inner_digests`. - #[inline] - fn new_inner(leaf_digests: Vec<LeafDigest<C>>, inner_digests: PartialInnerTree<C, M>) -> Self { - Self { - leaf_digests, - inner_digests, - } - } - - /// Builds a new [`Branch`] from `base` and `leaf_digests` without checking capacity limits. - #[inline] - fn new_unchecked<T>(base: &MerkleTree<C, T>, leaf_digests: Vec<LeafDigest<C>>) -> Self - where - T: Tree<C>, - { - let current_path = base.current_path(); - let mut this = Self::new_inner( - Default::default(), - PartialInnerTree::from_current( - &base.parameters, - current_path.leaf_index().join_leaves( - &base.parameters, - &base.current_leaf(), - &current_path.sibling_digest, - ), - current_path.inner_path, - ), - ); - for digest in leaf_digests { - this.push_digest(&base.parameters, || digest); - } - this - } - - /// Builds a new [`Branch`] from `base` and `leaf_digests`. - #[inline] - fn new<T>(base: &MerkleTree<C, T>, leaf_digests: Vec<LeafDigest<C>>) -> Option<Self> - where - T: Tree<C>, - { - if leaf_digests.len() + base.len() > capacity::<C>() { - return None; - } - Some(Self::new_unchecked(base, leaf_digests)) - } - - /// Tries to restart `self` at a new `base` if `base` has enough capacity. - #[inline] - fn try_rebase<T>(&mut self, base: &MerkleTree<C, T>) -> bool - where - T: Tree<C>, - { - if self.leaf_digests.len() + base.len() > capacity::<C>() { - return false; - } - *self = Self::new_unchecked(base, mem::take(self).leaf_digests); - true - } - - /// Computes the total length of this branch. - #[inline] - pub fn len(&self) -> usize { - self.inner_digests.starting_leaf_index().0 + self.leaf_digests.len() - } - - /// Returns `true` if this branch is empty. - #[inline] - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Returns the root inner digest of this branch. - #[inline] - pub fn root(&self) -> &InnerDigest<C> { - self.inner_digests.root() - } - - /// Returns the sibling leaf node to `index`. - #[inline] - fn get_leaf_sibling(&self, index: Node) -> Option<&LeafDigest<C>> { - self.leaf_digests.get( - (index - self.inner_digests.starting_leaf_index().0) - .sibling() - .0, - ) - } - - /// Appends a new `leaf_digest` to this branch. - #[inline] - fn push_digest<F>(&mut self, parameters: &Parameters<C>, leaf_digest: F) -> bool - where - F: FnOnce() -> LeafDigest<C>, - { - let len = self.len(); - if len >= capacity::<C>() { - return false; - } - let leaf_digest = leaf_digest(); - let leaf_index = Node(len); - self.inner_digests.insert( - parameters, - leaf_index, - leaf_index.join_leaves( - parameters, - &leaf_digest, - self.get_leaf_sibling(leaf_index) - .unwrap_or(&Default::default()), - ), - ); - self.leaf_digests.push(leaf_digest); - true - } - - /// Appends a new `leaf` to this branch. - #[inline] - fn push(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> bool { - self.push_digest(parameters, || parameters.digest(leaf)) - } - - /// Merges `self` onto the `base` merkle tree. - #[inline] - fn merge<T>(self, base: &mut MerkleTree<C, T>) - where - T: Tree<C>, - { - base.tree.merge_branch(&base.parameters, MergeBranch(self)) - } -} - -/// Fork Merge Branch -/// -/// An type which can only be instantiated by the merkle tree forking implementation, which -/// prevents running [`Tree::merge_branch`] on arbitrary user-constructed [`Branch`] values. -pub struct MergeBranch<C, M = BTreeMap<C>>(Branch<C, M>) -where - C: Configuration + ?Sized, - M: InnerMap<C> + Default; - -impl<C, M> From<MergeBranch<C, M>> for Branch<C, M> -where - C: Configuration + ?Sized, - M: InnerMap<C> + Default, -{ - #[inline] - fn from(branch: MergeBranch<C, M>) -> Self { - branch.0 - } -} - /// Raw Forking Primitives pub mod raw { use super::*; diff --git a/manta-crypto/src/merkle_tree/full.rs b/manta-crypto/src/merkle_tree/full.rs index 470000d8d..af277cb6d 100644 --- a/manta-crypto/src/merkle_tree/full.rs +++ b/manta-crypto/src/merkle_tree/full.rs @@ -57,12 +57,40 @@ where C: Configuration + ?Sized, M: InnerMap<C>, { + /// Builds a new [`Full`] without checking that `leaf_digests` and `inner_digests` form a + /// consistent merkle tree. + #[inline] + pub fn new_unchecked(leaf_digests: Vec<LeafDigest<C>>, inner_digests: InnerTree<C, M>) -> Self { + Self { + leaf_digests, + inner_digests, + } + } + /// Returns the leaf digests currently stored in the merkle tree. #[inline] pub fn leaf_digests(&self) -> &[LeafDigest<C>] { &self.leaf_digests } + /// Returns the leaf digests stored in the tree, dropping the rest of the tree data. + #[inline] + pub fn into_leaves(self) -> Vec<LeafDigest<C>> { + self.leaf_digests + } + + /// Returns the number of leaves in this tree. + #[inline] + pub fn len(&self) -> usize { + self.leaf_digests.len() + } + + /// Returns `true` if this tree is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + /// Returns a reference to the root inner digest. #[inline] pub fn root(&self) -> &InnerDigest<C> { @@ -72,10 +100,18 @@ where /// Returns the sibling leaf node to `index`. #[inline] fn get_leaf_sibling(&self, index: Node) -> Option<&LeafDigest<C>> { - // TODO: Add `Index` methods to accept `Node` as indices. self.leaf_digests.get(index.sibling().0) } + /// Returns an owned sibling leaf node to `index`. + #[inline] + fn get_owned_leaf_sibling(&self, index: Node) -> LeafDigest<C> + where + LeafDigest<C>: Clone, + { + self.get_leaf_sibling(index).cloned().unwrap_or_default() + } + /// Appends a `leaf_digest` with index given by `leaf_index` into the tree. #[inline] fn push_leaf_digest( @@ -113,7 +149,7 @@ where #[inline] fn len(&self) -> usize { - self.leaf_digests.len() + self.len() } #[inline] @@ -130,18 +166,10 @@ where #[inline] fn current_path(&self, parameters: &Parameters<C>) -> CurrentPath<C> { let _ = parameters; - let default = Default::default(); let leaf_index = Node(self.len() - 1); - CurrentPath::new( - self.get_leaf_sibling(leaf_index) - .map(Clone::clone) - .unwrap_or_default(), - leaf_index, - self.inner_digests - .path_for_leaf(leaf_index) - .filter(move |&d| d != &default) - .cloned() - .collect(), + CurrentPath::from_inner( + self.get_owned_leaf_sibling(leaf_index), + self.inner_digests.current_path_unchecked(leaf_index), ) } @@ -162,7 +190,7 @@ where impl<C, M> GetPath<C> for Full<C, M> where C: Configuration + ?Sized, - M: InnerMap<C> + Default, + M: InnerMap<C>, LeafDigest<C>: Clone, InnerDigest<C>: Clone, { @@ -175,15 +203,9 @@ where return Err(()); } let leaf_index = Node(index); - Ok(Path::new( - self.get_leaf_sibling(leaf_index) - .map(Clone::clone) - .unwrap_or_default(), - leaf_index, - self.inner_digests - .path_for_leaf(leaf_index) - .cloned() - .collect(), + Ok(Path::from_inner( + self.get_owned_leaf_sibling(leaf_index), + self.inner_digests.path(leaf_index), )) } } diff --git a/manta-crypto/src/merkle_tree/inner_tree.rs b/manta-crypto/src/merkle_tree/inner_tree.rs index e6d821eb7..8a33ff1f0 100644 --- a/manta-crypto/src/merkle_tree/inner_tree.rs +++ b/manta-crypto/src/merkle_tree/inner_tree.rs @@ -21,7 +21,8 @@ // TODO: We should probably move `InnerNode` and its related `struct`s to `merkle_tree::node`. use crate::merkle_tree::{ - path::CurrentInnerPath, path_length, Configuration, InnerDigest, Node, Parameters, Parity, + path::{CurrentInnerPath, InnerPath}, + path_length, Configuration, InnerDigest, Node, Parameters, Parity, }; use alloc::collections::btree_map; use core::{fmt::Debug, hash::Hash, iter::FusedIterator, marker::PhantomData, ops::Index}; @@ -524,15 +525,49 @@ where /// Computes the inner path starting from `node`. #[inline] - pub fn path(&self, node: InnerNode) -> InnerTreePathIter<C, M, S> { + pub fn path_iter(&self, node: InnerNode) -> InnerTreePathIter<C, M, S> { InnerTreePathIter::new(self, node.iter()) } /// Computes the inner path of the leaf given by `leaf_index`. #[inline] - pub fn path_for_leaf(&self, leaf_index: Node) -> InnerTreePathIter<C, M, S> { + pub fn path_iter_for_leaf(&self, leaf_index: Node) -> InnerTreePathIter<C, M, S> { InnerTreePathIter::new(self, InnerNodeIter::from_leaf::<C>(leaf_index)) } + + /// Returns the path at `leaf_index`. + #[inline] + pub fn path(&self, leaf_index: Node) -> InnerPath<C> + where + InnerDigest<C>: Clone, + { + InnerPath::new( + leaf_index, + self.path_iter_for_leaf(leaf_index).cloned().collect(), + ) + } +} + +impl<C, M> InnerTree<C, M, Sentinel<C>> +where + C: Configuration + ?Sized, + M: InnerMap<C>, +{ + /// Returns the path at `leaf_index`, assuming that `leaf_index` is the right-most index, + /// so that the return value is a valid [`CurrentInnerPath`]. + #[inline] + pub fn current_path_unchecked(&self, leaf_index: Node) -> CurrentInnerPath<C> + where + InnerDigest<C>: Clone, + { + CurrentInnerPath::new( + leaf_index, + self.path_iter_for_leaf(leaf_index) + .filter(move |&d| d != &self.sentinel_source.0) + .cloned() + .collect(), + ) + } } impl<C, M, S> Index<InnerNode> for InnerTree<C, M, S> @@ -637,14 +672,13 @@ where let mut inner_tree = InnerTree::<C, M, S>::default(); - let leaf_index = path.leaf_index; + let leaf_index = path.leaf_index.as_left(); let node_iter = path.into_nodes(); if node_iter.len() == 0 { return inner_tree.into(); } - let default = Default::default(); let root = node_iter.fold(base, |acc, (node, digest)| { let index = node.map_index(); match digest { @@ -653,7 +687,10 @@ where .map .set_and_join(parameters, index - 1, digest, index, acc) } - _ => parameters.join(inner_tree.map.set_get(index, acc), &default), + _ => parameters.join( + inner_tree.map.set_get(index, acc), + inner_tree.sentinel_source.get(index + 1), + ), } }); @@ -696,8 +733,34 @@ where /// Computes the inner path of the leaf given by `leaf_index` without checking if /// `leaf_index` is later than the starting index of this tree. #[inline] - pub fn path_for_leaf_unchecked(&self, leaf_index: Node) -> InnerTreePathIter<C, M, S> { - self.inner_tree.path_for_leaf(leaf_index) + pub fn path_iter_for_leaf_unchecked(&self, leaf_index: Node) -> InnerTreePathIter<C, M, S> { + self.inner_tree.path_iter_for_leaf(leaf_index) + } + + /// Returns the path at `leaf_index` without checking if `leaf_index` is later than the + /// starting index of this tree. + #[inline] + pub fn path_unchecked(&self, leaf_index: Node) -> InnerPath<C> + where + InnerDigest<C>: Clone, + { + self.inner_tree.path(leaf_index) + } +} + +impl<C, M> PartialInnerTree<C, M, Sentinel<C>> +where + C: Configuration + ?Sized, + M: InnerMap<C>, +{ + /// Returns the path at `leaf_index`, assuming that `leaf_index` is the right-most index, + /// so that the return value is a valid [`CurrentInnerPath`]. + #[inline] + pub fn current_path_unchecked(&self, leaf_index: Node) -> CurrentInnerPath<C> + where + InnerDigest<C>: Clone, + { + self.inner_tree.current_path_unchecked(leaf_index) } } diff --git a/manta-crypto/src/merkle_tree/node.rs b/manta-crypto/src/merkle_tree/node.rs index 60e056002..db9c98030 100644 --- a/manta-crypto/src/merkle_tree/node.rs +++ b/manta-crypto/src/merkle_tree/node.rs @@ -198,6 +198,26 @@ impl Node { lhs.sibling().0 == rhs.0 } + /// Returns `self` if `self` has left parity or returns the sibling of `self` if `self` has + /// right parity. + #[inline] + pub const fn as_left(&self) -> Self { + match self.parity() { + Parity::Left => *self, + Parity::Right => Self(self.0 - 1), + } + } + + /// Returns `self` if `self` has right parity or returns the sibling of `self` if `self` has + /// left parity. + #[inline] + pub const fn as_right(&self) -> Self { + match self.parity() { + Parity::Left => Self(self.0 + 1), + Parity::Right => *self, + } + } + /// Returns the left child [`Node`] of this node. #[inline] pub const fn left_child(&self) -> Self { diff --git a/manta-crypto/src/merkle_tree/partial.rs b/manta-crypto/src/merkle_tree/partial.rs index 2bd646ae6..a554a0a40 100644 --- a/manta-crypto/src/merkle_tree/partial.rs +++ b/manta-crypto/src/merkle_tree/partial.rs @@ -21,7 +21,8 @@ use crate::merkle_tree::{ capacity, inner_tree::{BTreeMap, InnerMap, PartialInnerTree}, - Configuration, CurrentPath, InnerDigest, LeafDigest, MerkleTree, Node, Parameters, Root, Tree, + Configuration, CurrentPath, InnerDigest, Leaf, LeafDigest, MerkleTree, Node, Parameters, Root, + Tree, }; use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; @@ -56,6 +57,19 @@ where C: Configuration + ?Sized, M: InnerMap<C>, { + /// Builds a new [`Partial`] without checking that `leaf_digests` and `inner_digests` form a + /// consistent merkle tree. + #[inline] + pub fn new_unchecked( + leaf_digests: Vec<LeafDigest<C>>, + inner_digests: PartialInnerTree<C, M>, + ) -> Self { + Self { + leaf_digests, + inner_digests, + } + } + /// Returns the leaf digests currently stored in the merkle tree. /// /// # Note @@ -68,12 +82,35 @@ where &self.leaf_digests } + /// Returns the leaf digests stored in the tree, dropping the rest of the tree data. + /// + /// # Note + /// + /// See the note at [`leaf_digests`](Self::leaf_digests) for more information on indexing this + /// vector. + #[inline] + pub fn into_leaves(self) -> Vec<LeafDigest<C>> { + self.leaf_digests + } + /// Returns the starting leaf index for this tree. #[inline] pub fn starting_leaf_index(&self) -> Node { self.inner_digests.starting_leaf_index() } + /// Returns the number of leaves in this tree. + #[inline] + pub fn len(&self) -> usize { + self.starting_leaf_index().0 + self.leaf_digests.len() + } + + /// Returns `true` if this tree is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + /// Returns a reference to the root inner digest. #[inline] pub fn root(&self) -> &InnerDigest<C> { @@ -87,9 +124,19 @@ where .get((index - self.starting_leaf_index().0).sibling().0) } + /// Returns an owned sibling leaf node to `index`. + #[inline] + fn get_owned_leaf_sibling(&self, index: Node) -> LeafDigest<C> + where + LeafDigest<C>: Clone, + { + self.get_leaf_sibling(index).cloned().unwrap_or_default() + } + /// Appends a `leaf_digest` with index given by `leaf_index` into the tree. + // FIXME: Remove pub(super) on this once we have a better interface for `fork`. #[inline] - fn push_leaf_digest( + pub(super) fn push_leaf_digest( &mut self, parameters: &Parameters<C>, leaf_index: Node, @@ -107,6 +154,18 @@ where ); self.leaf_digests.push(leaf_digest); } + + /// Appends a `leaf` to the tree using `parameters`. + // FIXME: Remove pub(super) on this once we have a better interface for `fork`. + #[inline] + pub(super) fn push(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> bool { + let len = self.len(); + if len >= capacity::<C>() { + return false; + } + self.push_leaf_digest(parameters, Node(len), parameters.digest(leaf)); + true + } } impl<C, M> Tree<C> for Partial<C, M> @@ -124,7 +183,7 @@ where #[inline] fn len(&self) -> usize { - self.starting_leaf_index().0 + self.leaf_digests.len() + self.len() } #[inline] @@ -141,18 +200,10 @@ where #[inline] fn current_path(&self, parameters: &Parameters<C>) -> CurrentPath<C> { let _ = parameters; - let default = Default::default(); let leaf_index = Node(self.len() - 1); - CurrentPath::new( - self.get_leaf_sibling(leaf_index) - .map(Clone::clone) - .unwrap_or_default(), - leaf_index, - self.inner_digests - .path_for_leaf_unchecked(leaf_index) - .filter(move |&d| d != &default) - .cloned() - .collect(), + CurrentPath::from_inner( + self.get_owned_leaf_sibling(leaf_index), + self.inner_digests.current_path_unchecked(leaf_index), ) } @@ -192,13 +243,8 @@ where let leaf_index = Node(index); Ok(Path::new( leaf_index, - self.get_leaf_sibling(leaf_index) - .map(Clone::clone) - .unwrap_or_default(), - self.inner_digests - .path_for_leaf_unchecked(leaf_index) - .cloned() - .collect(), + self.get_owned_leaf_sibling(leaf_index), + self.inner_digests.path_unchecked(leaf_index), )) */ todo!() diff --git a/manta-crypto/src/merkle_tree/path.rs b/manta-crypto/src/merkle_tree/path.rs index 8caf30372..1eeebd14b 100644 --- a/manta-crypto/src/merkle_tree/path.rs +++ b/manta-crypto/src/merkle_tree/path.rs @@ -78,11 +78,17 @@ where /// Checks if `self` could represent the [`CurrentInnerPath`] of some tree. #[inline] pub fn is_current(&self) -> bool { - let default = Default::default(); + self.is_current_with(&Default::default()) + } + + /// Checks if `self` could represent the [`CurrentInnerPath`] of some tree, using `default` + /// as the sentinel value. + #[inline] + pub fn is_current_with(&self, default: &InnerDigest<C>) -> bool { InnerNodeIter::from_leaf::<C>(self.leaf_index) .zip(self.path.iter()) .all(move |(node, d)| match node.parity() { - Parity::Left => d == &default, + Parity::Left => d == default, Parity::Right => true, }) } @@ -252,9 +258,15 @@ where /// Builds a new [`CurrentInnerPath`] from an [`InnerPath`] without checking that `path` /// satisfies [`InnerPath::is_current`]. #[inline] - pub fn from_path_unchecked(mut path: InnerPath<C>) -> Self { - let default = Default::default(); - path.path.retain(|d| d != &default); + pub fn from_path_unchecked(path: InnerPath<C>) -> Self { + Self::from_path_unchecked_with(path, &Default::default()) + } + + /// Builds a new [`CurrentInnerPath`] from an [`InnerPath`] without checking that `path` + /// satisfies [`InnerPath::is_current_with`] against `default`. + #[inline] + pub fn from_path_unchecked_with(mut path: InnerPath<C>, default: &InnerDigest<C>) -> Self { + path.path.retain(|d| d != default); Self::new(path.leaf_index, path.path) } @@ -429,8 +441,9 @@ where #[inline] fn try_from(path: InnerPath<C>) -> Result<Self, Self::Error> { - if path.is_current() { - Ok(CurrentInnerPath::from_path_unchecked(path)) + let default = Default::default(); + if path.is_current_with(&default) { + Ok(CurrentInnerPath::from_path_unchecked_with(path, &default)) } else { Err(path) } @@ -597,7 +610,14 @@ where /// Checks if `self` could represent the [`CurrentPath`] of some tree. #[inline] pub fn is_current(&self) -> bool { - self.inner_path.is_current() + self.is_current_with(&Default::default()) + } + + /// Checks if `self` could represent the [`CurrentPath`] of some tree, using `default` as the + /// sentinel value. + #[inline] + pub fn is_current_with(&self, default: &InnerDigest<C>) -> bool { + self.inner_path.is_current_with(default) } /// Computes the root of the merkle tree relative to `leaf_digest` using `parameters`. @@ -710,9 +730,16 @@ where /// [`Path::is_current`]. #[inline] pub fn from_path_unchecked(path: Path<C>) -> Self { + Self::from_path_unchecked_with(path, &Default::default()) + } + + /// Builds a new [`CurrentPath`] from a [`Path`] without checking that `path` satisfies + /// [`Path::is_current_with`] against `default`. + #[inline] + pub fn from_path_unchecked_with(path: Path<C>, default: &InnerDigest<C>) -> Self { Self::from_inner( path.sibling_digest, - CurrentInnerPath::from_path_unchecked(path.inner_path), + CurrentInnerPath::from_path_unchecked_with(path.inner_path, default), ) } diff --git a/manta-crypto/src/merkle_tree/test.rs b/manta-crypto/src/merkle_tree/test.rs index f4e59fbf1..398eba756 100644 --- a/manta-crypto/src/merkle_tree/test.rs +++ b/manta-crypto/src/merkle_tree/test.rs @@ -46,9 +46,7 @@ where } /// Tests path construction by checking that the path generated by `query` on `tree` is a valid -/// [`Path`] for `leaf`. -/// -/// [`Path`]: crate::merkle_tree::Path +/// [`Path`](super::Path) for `leaf`. #[inline] pub fn assert_valid_path<C, T>(tree: &MerkleTree<C, T>, index: usize, leaf: &Leaf<C>) where diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index 2e1ba2026..f95d587ce 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -24,8 +24,7 @@ // creating a new one from scratch (for inner digests)? use crate::merkle_tree::{ - fork::{self, Branch, MergeBranch, Trunk}, - inner_tree::InnerMap, + fork::{self, Trunk}, path::{CurrentPath, Path}, }; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; @@ -299,23 +298,6 @@ where { self.extend(parameters, leaves) } - - /// Merges the data from `branch` into `self` using `parameters`. - /// - /// # Implementation Note - /// - /// The forking implementation will never input invalid `branch` values, i.e. branches with too - /// many leaves, into this method, so any implementation is allowed to panic on invalid - /// `branch` values as long as it does not panic on any valid `branch` values. - #[inline] - fn merge_branch<M>(&mut self, parameters: &Parameters<C>, branch: MergeBranch<C, M>) - where - M: InnerMap<C> + Default, - { - assert!(self - .extend_digests(parameters, Branch::from(branch).leaf_digests) - .is_ok()) - } } /// Merkle Tree Path Query Mixin diff --git a/manta-pay/src/crypto/merkle_tree/mod.rs b/manta-pay/src/crypto/merkle_tree.rs similarity index 97% rename from manta-pay/src/crypto/merkle_tree/mod.rs rename to manta-pay/src/crypto/merkle_tree.rs index fa0c2785f..f72e1a3d7 100644 --- a/manta-pay/src/crypto/merkle_tree/mod.rs +++ b/manta-pay/src/crypto/merkle_tree.rs @@ -16,13 +16,6 @@ //! Arkworks Merkle Tree Wrappers -// FIXME: Remove this old implementation: -// NOTE: This is meant to be a full implementation of the incremental merkle tree type suitable -// for merging into arkworks itself. Therefore, even if we don't use all of the -// functionality available in this module, we want to preserve the code anyway. -#[allow(dead_code)] -mod incremental; - use alloc::{vec, vec::Vec}; use ark_crypto_primitives::{ crh::{TwoToOneCRH, CRH}, diff --git a/manta-pay/src/crypto/merkle_tree/incremental.rs b/manta-pay/src/crypto/merkle_tree/incremental.rs deleted file mode 100644 index 535951bf3..000000000 --- a/manta-pay/src/crypto/merkle_tree/incremental.rs +++ /dev/null @@ -1,422 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Arkworks Incremental Merkle Tree Implementation - -use alloc::vec::Vec; -use ark_crypto_primitives::{ - crh::TwoToOneCRH, - merkle_tree::{Config, LeafDigest, LeafParam, TwoToOneDigest, TwoToOneParam}, - Error, Path, CRH, -}; -use ark_ff::{to_bytes, ToBytes}; - -/// Incremental Merkle Tree -/// -/// This merkle tree implementation has a fixed runtime height `h`, and stores `2^h` leaves. -#[derive(Clone)] -pub struct IncrementalMerkleTree<P> -where - P: Config, -{ - /// Hashes of leaf nodes from left to right - leaf_nodes: Vec<LeafDigest<P>>, - - /// Inner Hash Parameters - two_to_one_hash_param: TwoToOneParam<P>, - - /// Leaf Hash Parameters - leaf_hash_param: LeafParam<P>, - - /// Fixed Merkle Tree Height - height: usize, - - /// Path of the Current Leaf - current_path: Path<P>, - - /// Root of the Merkle Tree - root: TwoToOneDigest<P>, - - /// Emptiness Flag - empty: bool, -} - -impl<P> IncrementalMerkleTree<P> -where - P: Config, -{ - /// Checks if `self` is an empty tree. - #[inline] - pub fn is_empty(&self) -> bool { - self.empty - } - - /// Returns the index of the current (right-most) leaf. - #[inline] - pub fn current_index(&self) -> Option<usize> { - if self.is_empty() { - None - } else { - Some(self.current_path.leaf_index) - } - } - - /// Returns the next available index for a leaf node. - #[inline] - pub fn next_available(&self) -> Option<usize> { - let current_index = self.current_path.leaf_index; - if self.is_empty() { - Some(0) - } else if current_index < (1 << (self.height - 1)) - 1 { - Some(current_index + 1) - } else { - None - } - } - - /// Returns the proof for the current (right-most) leaf. - #[inline] - pub fn current_proof(&self) -> &Path<P> { - &self.current_path - } - - /// Returns the root of the tree. - #[inline] - pub fn root(&self) -> &TwoToOneDigest<P> { - &self.root - } - - /// Creates an empty merkle tree with leaves filled with the sentinel value. - /// - /// # Panics - /// - /// This function panics if the given `height` is less than `2`. - pub fn blank( - leaf_hash_param: &LeafParam<P>, - two_to_one_hash_param: &TwoToOneParam<P>, - height: usize, - ) -> Self { - assert!( - height > 1, - "the height of incremental merkle tree should be at least 2" - ); - IncrementalMerkleTree { - current_path: Path { - leaf_sibling_hash: Default::default(), - auth_path: Default::default(), - leaf_index: Default::default(), - }, - leaf_nodes: Default::default(), - two_to_one_hash_param: two_to_one_hash_param.clone(), - leaf_hash_param: leaf_hash_param.clone(), - root: Default::default(), - height, - empty: true, - } - } - - /// Asserts that we are inserting into a valid index. - #[inline] - fn assert_valid_index(&self) { - assert!(self.next_available() != None, "index out of range"); - } - - /// Appends `leaf` to the tree at the next available index. - /// - /// # Example - /// - /// Given the following tree: - /// ```tree_diagram - /// [A] - /// / \ - /// [B] () - /// / \ / \ - /// D [E] () () - /// .. / \ .... - /// [I]{leaf} - /// ``` - /// running `append({leaf})` would insert `leaf` after `[I]` and would trigger a recompute of - /// `[E]`, `[B]`, and `[A]`. - pub fn append<L>(&mut self, leaf: L) -> Result<(), Error> - where - L: ToBytes, - { - self.assert_valid_index(); - let leaf_digest = P::LeafHash::evaluate(&self.leaf_hash_param, &to_bytes!(leaf)?)?; - let (path, root) = self.next_path(&leaf_digest)?; - self.leaf_nodes.push(leaf_digest); - self.current_path = path; - self.root = root; - self.empty = false; - Ok(()) - } - - /// Generates the new path and root of the tree given `new_leaf_digest` as the next inserted - /// leaf in the tree. - fn next_path( - &self, - new_leaf_digest: &LeafDigest<P>, - ) -> Result<(Path<P>, TwoToOneDigest<P>), Error> { - self.assert_valid_index(); - - // Calculate tree height and empty hash. - let tree_height = self.height; - let hash_of_empty_node = TwoToOneDigest::<P>::default(); - let hash_of_empty_leaf = LeafDigest::<P>::default(); - - // Create a new auth path with length two less than the tree height. - let mut new_auth_path = Vec::with_capacity(tree_height - 2); - - if self.is_empty() { - // generate auth path and calculate the root - let mut current_node = P::TwoToOneHash::evaluate( - &self.two_to_one_hash_param, - &to_bytes!(new_leaf_digest)?, - &to_bytes!(LeafDigest::<P>::default())?, - )?; - // all the auth path node are empty nodes - for _ in 0..tree_height - 2 { - new_auth_path.push(hash_of_empty_node.clone()); - current_node = P::TwoToOneHash::evaluate( - &self.two_to_one_hash_param, - &to_bytes!(current_node)?, - &to_bytes!(hash_of_empty_node.clone())?, - )?; - } - - let path = Path { - leaf_index: 0, - auth_path: new_auth_path, - leaf_sibling_hash: hash_of_empty_leaf, - }; - Ok((path, current_node)) - } else { - // compute next path of a non-empty tree - // Get the indices of the previous and propsed (new) leaf node - let mut new_index = self.next_available().unwrap(); - let mut old_index = self.current_index().unwrap(); - let old_leaf = &self.leaf_nodes[old_index]; - - // generate two mutable node: old_current_node, new_current_node to iterate on - let (old_left_leaf, old_right_leaf) = if is_left_child(old_index) { - ( - self.leaf_nodes[old_index].clone(), - self.current_path.leaf_sibling_hash.clone(), - ) - } else { - ( - self.current_path.leaf_sibling_hash.clone(), - self.leaf_nodes[old_index].clone(), - ) - }; - - let (new_left_leaf, new_right_leaf, leaf_sibling) = if is_left_child(new_index) { - (new_leaf_digest, &hash_of_empty_leaf, &hash_of_empty_leaf) - } else { - (old_leaf, new_leaf_digest, old_leaf) - }; - - let mut old_current_node = P::TwoToOneHash::evaluate( - &self.two_to_one_hash_param, - &to_bytes!(old_left_leaf)?, - &to_bytes!(old_right_leaf)?, - )?; - let mut new_current_node = P::TwoToOneHash::evaluate( - &self.two_to_one_hash_param, - &to_bytes!(new_left_leaf)?, - &to_bytes!(new_right_leaf)?, - )?; - - // reverse the old_auth_path to make it bottom up - let mut old_auth_path = self.current_path.auth_path.clone(); - old_auth_path.reverse(); - - // build new_auth_path and root recursively - for old_auth_path_point in old_auth_path.iter().take(tree_height - 2) { - new_index = parent_index_on_level(new_index); - old_index = parent_index_on_level(old_index); - if new_index == old_index { - // this means the old path and new path are merged, - // as a result, no need to update the old_current_node any more - - // add the auth path node - new_auth_path.push(old_auth_path_point.clone()); - - // update the new current node (this is needed to compute the root) - let (new_left, new_right) = if is_left_child(new_index) { - (new_current_node, hash_of_empty_node.clone()) - } else { - (old_auth_path_point.clone(), new_current_node) - }; - new_current_node = P::TwoToOneHash::evaluate( - &self.two_to_one_hash_param, - &to_bytes!(new_left)?, - &to_bytes!(new_right)?, - )?; - } else { - // this means old path and new path haven't been merged, - // as a reulst, need to update both the new_current_node and new_current_node - let auth_node = if is_left_child(new_index) { - hash_of_empty_node.clone() - } else { - old_current_node.clone() - }; - new_auth_path.push(auth_node); - - // update both old_current_node and new_current_node - // update new_current_node - let (new_left, new_right) = if is_left_child(new_index) { - (new_current_node.clone(), hash_of_empty_node.clone()) - } else { - (old_current_node.clone(), new_current_node) - }; - new_current_node = P::TwoToOneHash::evaluate( - &self.two_to_one_hash_param, - &to_bytes!(new_left)?, - &to_bytes!(new_right)?, - )?; - - // We only need to update the old_current_node bottom up when it is right child - if !is_left_child(old_index) { - old_current_node = P::TwoToOneHash::evaluate( - &self.two_to_one_hash_param, - &to_bytes!(old_auth_path_point.clone())?, - &to_bytes!(old_current_node)?, - )?; - } - } - } - - // reverse new_auth_path to top down - new_auth_path.reverse(); - let path = Path { - leaf_index: self.next_available().unwrap(), - auth_path: new_auth_path, - leaf_sibling_hash: leaf_sibling.clone(), - }; - Ok((path, new_current_node)) - } - } -} - -/// Returns `true` if and only if the given index on the current level represents a left child. -#[inline] -fn is_left_child(index_on_level: usize) -> bool { - index_on_level % 2 == 0 -} - -/// Returns the parent index for the index on the current level. -#[inline] -fn parent_index_on_level(index_on_level: usize) -> usize { - index_on_level >> 1 -} - -#[cfg(test)] -mod test { - use super::*; - use alloc::vec::Vec; - use ark_crypto_primitives::crh::{pedersen, TwoToOneCRH as TwoToOneCRHTrait, CRH as CRHTrait}; - use ark_ed_on_bls12_381::EdwardsProjective as JubJub; - use ark_ff::{BigInteger256, UniformRand}; - use rand::{rngs::ThreadRng, thread_rng}; - - /// Pedersen Window Parameters - #[derive(Clone)] - struct Window4x256; - - impl pedersen::Window for Window4x256 { - const WINDOW_SIZE: usize = 4; - const NUM_WINDOWS: usize = 256; - } - - /// Leaf Hash - type LeafH = pedersen::CRH<JubJub, Window4x256>; - - /// Two-to-One Pedersen Hash - type CompressH = pedersen::CRH<JubJub, Window4x256>; - - /// JubJub Merkle Tree Parameters - struct JubJubMerkleTreeParams; - - impl Config for JubJubMerkleTreeParams { - type LeafHash = LeafH; - type TwoToOneHash = CompressH; - } - - /// Jub Jub Incremental Merkle Tree - type JubJubIncrementalMerkleTree = IncrementalMerkleTree<JubJubMerkleTreeParams>; - - /// Builds an incremental merkle tree element by element and tests that each generated proof is - /// valid. - fn incremental_merkle_tree_test<L: ToBytes>(tree_height: usize, update_query: &[L]) { - let mut rng = thread_rng(); - let leaf_crh_params = <LeafH as CRHTrait>::setup(&mut rng).unwrap(); - let two_to_one_params = <CompressH as TwoToOneCRHTrait>::setup(&mut rng).unwrap(); - let mut tree = - JubJubIncrementalMerkleTree::blank(&leaf_crh_params, &two_to_one_params, tree_height); - for v in update_query { - let v = to_bytes!(v).unwrap(); - tree.append(v.clone()).unwrap(); - println!("{:?}", tree.next_available()); - println!("{:?}", tree.is_empty()); - let proof = tree.current_proof(); - assert!(proof - .verify(&leaf_crh_params, &two_to_one_params, tree.root(), &v) - .unwrap()); - } - } - - /// Tests the emptiness criterion for an incremental merkle tree. - #[test] - fn test_emptyness_for_imt() { - let mut rng = thread_rng(); - let leaf_crh_params = <LeafH as CRHTrait>::setup(&mut rng).unwrap(); - let two_to_one_params = <CompressH as TwoToOneCRHTrait>::setup(&mut rng).unwrap(); - let mut tree = JubJubIncrementalMerkleTree::blank(&leaf_crh_params, &two_to_one_params, 5); - assert!(tree.is_empty()); - let v = BigInteger256::rand(&mut rng); - tree.append(to_bytes!(v).unwrap()).unwrap(); - assert!(!tree.is_empty()); - } - - /// Samples merkle tree updates from `rng` and tests them with the - /// [`incremental_merkle_tree_test`] function. - #[inline] - fn sample_updates_and_test_imt(rng: &mut ThreadRng, update_count: usize, tree_height: usize) { - let mut updates = Vec::new(); - for _ in 0..update_count { - updates.push(BigInteger256::rand(rng)); - } - incremental_merkle_tree_test(tree_height, &updates); - } - - /// Runs tests for well-formed trees. - #[test] - fn good_root_test_for_imt() { - let mut rng = thread_rng(); - sample_updates_and_test_imt(&mut rng, 2, 2); - sample_updates_and_test_imt(&mut rng, 7, 4); - sample_updates_and_test_imt(&mut rng, 128, 8); - } - - /// Runs test for a tree which has exceeded capacity. - #[test] - #[should_panic] - fn out_of_capacity_test_for_imt() { - let mut rng = thread_rng(); - sample_updates_and_test_imt(&mut rng, 3, 2); - } -} From a89f92892c1e5f2ef0e2112301a5da31916d8e87 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 30 Sep 2021 19:36:01 -0400 Subject: [PATCH 070/275] WIP: coin selection abstractions for batched transfers --- manta-accounting/src/asset.rs | 90 ++++++++++++++++++- manta-accounting/src/keys.rs | 19 ++++ manta-accounting/src/wallet.rs | 159 +++++++++++++++++++++++++++++---- 3 files changed, 248 insertions(+), 20 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index dbcd3340f..201285ed1 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -26,7 +26,7 @@ use core::{ convert::TryFrom, fmt::Debug, hash::Hash, - iter::Sum, + iter::{FusedIterator, Sum}, ops::{Add, AddAssign, Mul, Sub, SubAssign}, }; use derive_more::{ @@ -156,6 +156,12 @@ impl AssetBalance { _ => None, } } + + /// Returns an iterator over change amounts in `n` parts. + #[inline] + pub const fn make_change(self, n: usize) -> Option<Change> { + Change::new(self.0, n) + } } impl Distribution<AssetBalance> for Standard { @@ -194,6 +200,7 @@ impl<'a> Sum<&'a AssetBalance> for AssetBalance { /// [`AssetBalance`] Array Type pub type AssetBalances<const N: usize> = [AssetBalance; N]; +/// Samples asset balances from `rng`. #[inline] pub(crate) fn sample_asset_balances<R, const N: usize>(rng: &mut R) -> AssetBalances<N> where @@ -204,6 +211,68 @@ where into_array_unchecked(rng.sample_iter(Standard).take(N).collect::<Vec<_>>()) } +/// Change Iterator +/// +/// An iterator over [`AssetBalance`] change amounts. +/// +/// This `struct` is created by the [`make_change`](AssetBalance::make_change) method on +/// [`AssetBalance`]. See its documentation for more. +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +pub struct Change { + /// Base Amount + base: AssetBalanceType, + + /// Remainder to be Divided + remainder: usize, + + /// Total Number of Units + units: usize, + + /// Current Index + index: usize, +} + +impl Change { + /// Builds a new [`Change`] iterator for `amount` into `n` pieces. + #[inline] + const fn new(amount: AssetBalanceType, n: usize) -> Option<Self> { + let n_div = n as AssetBalanceType; + match amount.checked_div(n_div) { + Some(base) if base < AssetBalanceType::MAX => Some(Self { + base, + remainder: (amount % n_div) as usize, + units: n, + index: 0, + }), + _ => None, + } + } +} + +impl Iterator for Change { + type Item = AssetBalance; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + if self.index >= self.units { + return None; + } + let amount = self.base + (self.index < self.remainder) as u128; + self.index += 1; + Some(AssetBalance(amount)) + } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + let len = self.units - self.index; + (len, Some(len)) + } +} + +impl ExactSizeIterator for Change {} + +impl FusedIterator for Change {} + /// Asset #[derive(Clone, Copy, Debug, Default, Display, Eq, From, Hash, PartialEq)] #[display(fmt = "{{id: {}, value: {}}}", id, value)] @@ -228,6 +297,12 @@ impl Asset { Self { id, value } } + /// Builds a new zero [`Asset`] with the given `id`. + #[inline] + pub const fn zero(id: AssetId) -> Self { + Self::new(id, AssetBalance(0)) + } + /// Builds a new [`Asset`] from an existing one with a new `value`. #[inline] pub const fn with_value(&self, value: AssetBalance) -> Self { @@ -517,4 +592,17 @@ mod test { let _ = asset - value; asset -= value; } + + /// Tests that the [`Change`] iterator makes the correct change. + #[test] + fn test_change_iterator() { + let mut rng = thread_rng(); + for _ in 0..0xFFF { + let amount = AssetBalance(rng.gen_range(0..0xFFFFFF)); + let n = rng.gen_range(1..0xFFFF); + let change = amount.make_change(n).unwrap().collect::<Vec<_>>(); + assert_eq!(n, change.len()); + assert_eq!(amount, change.into_iter().sum()); + } + } } diff --git a/manta-accounting/src/keys.rs b/manta-accounting/src/keys.rs index 2fd33c06b..40b538e6e 100644 --- a/manta-accounting/src/keys.rs +++ b/manta-accounting/src/keys.rs @@ -398,6 +398,15 @@ where self } + /// Generates a new key of the given `kind` for this account. + #[inline] + pub fn key(&self, source: &D, kind: KeyKind) -> Result<D::SecretKey, D::Error> { + match kind { + KeyKind::External => self.external_key(source), + KeyKind::Internal => self.internal_key(source), + } + } + /// Generates a new external key for this account. #[inline] pub fn external_key(&self, source: &D) -> Result<D::SecretKey, D::Error> { @@ -410,6 +419,16 @@ where source.generate_internal_key(&self.account, &self.internal_index) } + /// Generates the next key of the given `kind` for this account, incrementing the + /// appropriate index. + #[inline] + pub fn next_key(&mut self, source: &D, kind: KeyKind) -> Result<D::SecretKey, D::Error> { + match kind { + KeyKind::External => self.next_external_key(source), + KeyKind::Internal => self.next_internal_key(source), + } + } + /// Generates the next external key for this account, incrementing the `external_index`. #[inline] pub fn next_external_key(&mut self, source: &D) -> Result<D::SecretKey, D::Error> { diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index 6d5e1044b..aaef2b37a 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -29,7 +29,7 @@ use crate::{ fs::{Load, LoadWith, Save, SaveWith}, identity::{ self, AssetParameters, Identity, InternalReceiver, InternalReceiverError, OpenSpend, - ShieldedIdentity, Utxo, VoidNumber, + PreSender, ShieldedIdentity, Utxo, VoidNumber, }, keys::{Account, DerivedSecretKeyGenerator, InternalKeys, KeyKind}, transfer::{ @@ -92,40 +92,54 @@ where ) } - /// Generates the next external key for this signer. + /// Returns the identity for a key of the given `kind` and `index`. #[inline] - fn next_external_key(&mut self) -> Result<D::SecretKey, D::Error> { - self.account.next_external_key(&self.secret_key_source) + pub fn get<C>(&self, kind: KeyKind, index: D::Index) -> Result<Identity<C>, D::Error> + where + C: identity::Configuration<SecretKey = D::SecretKey>, + { + self.secret_key_source + .generate_key(kind, self.account.as_ref(), &index) + .map(Identity::new) } - /// Generates the next internal key for this signer. + /// Generates the next identity of the given `kind` for this signer. #[inline] - fn next_internal_key(&mut self) -> Result<D::SecretKey, D::Error> { - self.account.next_internal_key(&self.secret_key_source) + pub fn next<C>(&mut self, kind: KeyKind) -> Result<Identity<C>, D::Error> + where + C: identity::Configuration<SecretKey = D::SecretKey>, + { + self.account + .next_key(&self.secret_key_source, kind) + .map(Identity::new) } /// Generates the next external identity for this signer. #[inline] - pub fn next_external_identity<C>(&mut self) -> Result<Identity<C>, D::Error> + pub fn next_external<C>(&mut self) -> Result<Identity<C>, D::Error> where C: identity::Configuration<SecretKey = D::SecretKey>, { - self.next_external_key().map(Identity::new) + self.account + .next_external_key(&self.secret_key_source) + .map(Identity::new) } /// Generates the next internal identity for this signer. #[inline] - pub fn next_internal_identity<C>(&mut self) -> Result<Identity<C>, D::Error> + pub fn next_internal<C>(&mut self) -> Result<Identity<C>, D::Error> where C: identity::Configuration<SecretKey = D::SecretKey>, { - self.next_internal_key().map(Identity::new) + self.account + .next_internal_key(&self.secret_key_source) + .map(Identity::new) } /// Generates a new [`ShieldedIdentity`] to receive assets to this account via an external /// transaction. #[inline] - pub fn generate_shielded_identity<C, I>( + pub fn next_shielded_identity<C, I>( &mut self, commitment_scheme: &C::CommitmentScheme, ) -> Result<ShieldedIdentity<C, I>, D::Error> @@ -134,14 +148,14 @@ where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { - self.next_external_identity() + self.next_external() .map(move |identity| identity.into_shielded(commitment_scheme)) } /// Generates a new [`InternalReceiver`] to receive `asset` to this account via an /// internal transaction. #[inline] - pub fn generate_internal_receiver<C, I, R>( + pub fn next_internal_receiver<C, I, R>( &mut self, commitment_scheme: &C::CommitmentScheme, asset: Asset, @@ -153,12 +167,30 @@ where R: CryptoRng + RngCore + ?Sized, Standard: Distribution<AssetParameters<C>>, { - self.next_internal_identity() + self.next_internal() .map_err(InternalReceiverError::SecretKeyError)? .into_internal_receiver(commitment_scheme, asset, rng) .map_err(InternalReceiverError::EncryptionError) } + /// Generates a new [`InternalReceiver`] to receive an asset, with the given `asset_id` and + /// no value, to this account via an internal transaction. + #[inline] + pub fn next_empty_internal_receiver<C, I, R>( + &mut self, + commitment_scheme: &C::CommitmentScheme, + asset_id: AssetId, + rng: &mut R, + ) -> Result<InternalReceiver<C, I>, InternalReceiverError<InternalKeys<D>, I>> + where + C: identity::Configuration<SecretKey = D::SecretKey>, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + R: CryptoRng + RngCore + ?Sized, + Standard: Distribution<AssetParameters<C>>, + { + self.next_internal_receiver(commitment_scheme, Asset::zero(asset_id), rng) + } + /* TODO: Revisit how this is designed (this is part of recovery/ledger-sync): /// Returns an [`ExternalKeys`] generator starting from the given `index`. @@ -472,6 +504,95 @@ pub trait AssetMap { } } +/// Asset Key +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = ""), + Copy(bound = "D::Index: Copy"), + Debug(bound = "D::Index: Debug"), + Eq(bound = "D::Index: Eq"), + Hash(bound = "D::Index: Hash"), + PartialEq(bound = "D::Index: PartialEq") +)] +pub struct AssetKey<D> +where + D: DerivedSecretKeyGenerator, +{ + /// Key Kind + pub kind: KeyKind, + + /// Key Index + pub index: D::Index, + + /// Value stored at this key + pub value: AssetBalance, +} + +impl<D> AssetKey<D> +where + D: DerivedSecretKeyGenerator, +{ + /// Builds a [`PreSender`] for `self`, using `signer` to generate the secret key. + #[inline] + pub fn into_pre_sender<C>( + self, + signer: &Signer<D>, + commitment_scheme: &C::CommitmentScheme, + asset_id: AssetId, + ) -> Result<PreSender<C>, D::Error> + where + C: identity::Configuration<SecretKey = D::SecretKey>, + Standard: Distribution<AssetParameters<C>>, + { + Ok(signer + .get(self.kind, self.index)? + .into_pre_sender(commitment_scheme, Asset::new(asset_id, self.value))) + } +} + +/// Asset Selection +pub struct AssetSelection<D, S> +where + D: DerivedSecretKeyGenerator, + S: AssetSelector<D> + ?Sized, +{ + /// Change Amount + pub change: AssetBalance, + + /// Asset Keys + pub keys: S::Keys, +} + +impl<D, S> AssetSelection<D, S> +where + D: DerivedSecretKeyGenerator, + S: AssetSelector<D> + ?Sized, +{ + /* TODO: + /// + #[inline] + pub fn make_change(&self, signer: &Signer<D>, n: usize) -> Option<Vec<()>> { + let amounts = self.change.make_change(n)?; + todo!() + } + */ +} + +/// Asset Selector +pub trait AssetSelector<D> +where + D: DerivedSecretKeyGenerator, +{ + /// Keys Type + type Keys: Iterator<Item = AssetKey<D>>; + + /// Error Type + type Error; + + /// Selects assets which total up to at least `asset` in value. + fn select(&self, asset: Asset) -> Result<AssetSelection<D, Self>, Self::Error>; +} + /// Ledger Ownership Marker #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum Ownership { @@ -537,7 +658,7 @@ where fn set_checkpoint(&mut self, checkpoint: Self::Checkpoint); /// Saves `data` into the local ledger. - fn append(&mut self, data: LedgerData<C>) -> Result<(), Self::Error>; + fn push(&mut self, data: LedgerData<C>) -> Result<(), Self::Error>; /// Prepares a `post` in the local ledger, returning a key to decide if the post should /// be preserved by the ledger or not. @@ -601,7 +722,7 @@ where // communicate with it when we try to send transactions to a ledger source. // Do we care about keeping void numbers? Maybe only for recovery? // - let _ = self.ledger.append(LedgerData::Sender(void_number)); + let _ = self.ledger.push(LedgerData::Sender(void_number)); } for (utxo, encrypted_asset) in utxos.into_iter().zip(encrypted_assets) { @@ -615,7 +736,7 @@ where // let _ = self .ledger - .append(LedgerData::Receiver(utxo, encrypted_asset)); + .push(LedgerData::Receiver(utxo, encrypted_asset)); } self.ledger.set_checkpoint(checkpoint); @@ -735,7 +856,7 @@ where { Mint::from_identity( self.signer - .next_internal_identity() + .next_internal() .map_err(MintError::SecretKeyError)?, commitment_scheme, asset, From 7398ec11855832f928c7fd75c0cb58e90de1074e Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 1 Oct 2021 01:17:17 -0400 Subject: [PATCH 071/275] WIP: redesign asset map abstractions --- manta-accounting/src/asset.rs | 2 +- manta-accounting/src/identity.rs | 273 +-------------------- manta-accounting/src/keys.rs | 67 +++++- manta-accounting/src/wallet.rs | 396 +++++++++++++++++-------------- manta-pay/src/accounting/keys.rs | 2 + 5 files changed, 285 insertions(+), 455 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 201285ed1..eec86ddbb 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -238,7 +238,7 @@ impl Change { const fn new(amount: AssetBalanceType, n: usize) -> Option<Self> { let n_div = n as AssetBalanceType; match amount.checked_div(n_div) { - Some(base) if base < AssetBalanceType::MAX => Some(Self { + Some(base) => Some(Self { base, remainder: (amount % n_div) as usize, units: n, diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 175a01858..d49784722 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -19,10 +19,7 @@ // FIXME: Check the secret key APIs. // TODO: Since `Configuration::SecretKey: Clone`, should `Identity: Clone`? -use crate::{ - asset::{Asset, AssetBalance, AssetId, AssetVar}, - keys::SecretKeyGenerator, -}; +use crate::asset::{Asset, AssetBalance, AssetId, AssetVar}; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use manta_crypto::{ commitment::{CommitmentScheme, Input as CommitmentInput}, @@ -38,8 +35,7 @@ use rand::{ pub(super) mod prelude { #[doc(inline)] pub use super::{ - Identity, Receiver, Sender, SenderError, ShieldedIdentity, Spend, SpendError, Utxo, - VoidNumber, + Identity, Receiver, Sender, ShieldedIdentity, Spend, SpendError, Utxo, VoidNumber, }; } @@ -260,15 +256,6 @@ where Self { secret_key } } - /// Generates a new [`Identity`] from a secret key generation source. - #[inline] - pub fn generate<G>(source: &mut G) -> Result<Self, G::Error> - where - G: SecretKeyGenerator<SecretKey = SecretKey<C>>, - { - source.generate_key().map(Self::new) - } - /// Generates the associated `C::Rng` and a `AssetParameters<C>` for this identity. /// /// # API Note @@ -409,21 +396,6 @@ where } } - /// Generates a new [`Identity`] from a secret key generation source and builds a new - /// [`PreSender`] from it. - #[inline] - pub fn generate_pre_sender<G>( - source: &mut G, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - ) -> Result<PreSender<C>, G::Error> - where - G: SecretKeyGenerator<SecretKey = SecretKey<C>>, - Standard: Distribution<AssetParameters<C>>, - { - Ok(Self::generate(source)?.into_pre_sender(commitment_scheme, asset)) - } - /// Builds a new [`Sender`] for the given `asset`. #[inline] pub fn into_sender<S>( @@ -451,26 +423,6 @@ where }) } - /// Generates a new [`Identity`] from a secret key generation source and builds a new - /// [`Sender`] from it. - #[inline] - pub fn generate_sender<G, S>( - source: &mut G, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - utxo_set: &S, - ) -> Result<Sender<C, S>, SenderError<G, S>> - where - G: SecretKeyGenerator<SecretKey = SecretKey<C>>, - S: VerifiedSet<Item = Utxo<C>>, - Standard: Distribution<AssetParameters<C>>, - { - Self::generate(source) - .map_err(SenderError::SecretKeyError)? - .into_sender(commitment_scheme, asset, utxo_set) - .map_err(SenderError::MissingUtxo) - } - /// Builds a new [`ShieldedIdentity`] from `commitment_scheme`, `parameters`, and /// `asset_keypair`. #[inline] @@ -502,21 +454,6 @@ where self.build_shielded_identity(commitment_scheme, parameters, asset_public_key) } - /// Generates a new [`Identity`] from a secret key generation source and builds a new - /// [`ShieldedIdentity`] from it. - #[inline] - pub fn generate_shielded<G, I>( - source: &mut G, - commitment_scheme: &C::CommitmentScheme, - ) -> Result<ShieldedIdentity<C, I>, G::Error> - where - G: SecretKeyGenerator<SecretKey = SecretKey<C>>, - I: IntegratedEncryptionScheme<Plaintext = Asset>, - Standard: Distribution<AssetParameters<C>>, - { - Ok(Self::generate(source)?.into_shielded(commitment_scheme)) - } - /// Builds a new [`Spend`]. #[inline] pub fn into_spend<I>(self) -> Spend<C, I> @@ -528,18 +465,6 @@ where Spend::new(self, asset_secret_key) } - /// Generates a new [`Identity`] from a secret key generation source and builds a new - /// [`Spend`] from it. - #[inline] - pub fn generate_spend<G, I>(source: &mut G) -> Result<Spend<C, I>, G::Error> - where - G: SecretKeyGenerator<SecretKey = SecretKey<C>>, - I: IntegratedEncryptionScheme<Plaintext = Asset>, - Standard: Distribution<AssetParameters<C>>, - { - Ok(Self::generate(source)?.into_spend()) - } - /// Tries to open an `encrypted_asset`, returning an [`OpenSpend`] if successful. #[inline] pub fn try_open<I>( @@ -575,21 +500,6 @@ where } } - /// Generates a new [`Identity`] from a secret key generation source and builds a new - /// [`ExternalReceiver`] from it. - #[inline] - pub fn generate_external_receiver<G, I>( - source: &mut G, - commitment_scheme: &C::CommitmentScheme, - ) -> Result<ExternalReceiver<C, I>, G::Error> - where - G: SecretKeyGenerator<SecretKey = SecretKey<C>>, - I: IntegratedEncryptionScheme<Plaintext = Asset>, - Standard: Distribution<AssetParameters<C>>, - { - Ok(Self::generate(source)?.into_external_receiver(commitment_scheme)) - } - /// Builds a new [`InternalReceiver`]. #[inline] pub fn into_internal_receiver<I, R>( @@ -611,27 +521,6 @@ where open_spend: OpenSpend::new(self, asset), }) } - - /// Generates a new [`Identity`] from a secret key generation source and builds a new - /// [`InternalReceiver`] from it. - #[inline] - pub fn generate_internal_receiver<G, I, R>( - source: &mut G, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - rng: &mut R, - ) -> Result<InternalReceiver<C, I>, InternalReceiverError<G, I>> - where - G: SecretKeyGenerator<SecretKey = SecretKey<C>>, - I: IntegratedEncryptionScheme<Plaintext = Asset>, - R: CryptoRng + RngCore + ?Sized, - Standard: Distribution<AssetParameters<C>>, - { - Self::generate(source) - .map_err(InternalReceiverError::SecretKeyError)? - .into_internal_receiver(commitment_scheme, asset, rng) - .map_err(InternalReceiverError::EncryptionError) - } } impl<C> Distribution<Identity<C>> for Standard @@ -676,21 +565,6 @@ where identity.into_shielded(commitment_scheme) } - /// Generates a new [`Identity`] from a secret key generation source and builds a - /// [`ShieldedIdentity`] from it. - #[inline] - pub fn generate<G>( - source: &mut G, - commitment_scheme: &C::CommitmentScheme, - ) -> Result<Self, G::Error> - where - G: SecretKeyGenerator<SecretKey = SecretKey<C>>, - I: IntegratedEncryptionScheme<Plaintext = Asset>, - Standard: Distribution<AssetParameters<C>>, - { - Identity::generate_shielded(source, commitment_scheme) - } - /// Generates a [`Receiver`] from a [`ShieldedIdentity`]. #[inline] pub fn into_receiver<R>( @@ -790,17 +664,6 @@ where identity.into_spend() } - /// Generates a new [`Identity`] from a secret key generation source and builds a - /// [`Spend`] from it. - #[inline] - pub fn generate<G>(source: &mut G) -> Result<Self, G::Error> - where - G: SecretKeyGenerator<SecretKey = SecretKey<C>>, - Standard: Distribution<AssetParameters<C>>, - { - Identity::generate_spend(source) - } - /// Tries to open an `encrypted_asset`, returning an [`OpenSpend`] if successful. #[inline] pub fn try_open(self, encrypted_asset: &EncryptedMessage<I>) -> Result<OpenSpend<C>, I::Error> { @@ -854,26 +717,6 @@ where pub spend: Spend<C, I>, } -impl<C, I> ExternalReceiver<C, I> -where - C: Configuration, - I: IntegratedEncryptionScheme<Plaintext = Asset>, -{ - /// Generates a new [`Identity`] from a secret key generation source and builds a new - /// [`ExternalReceiver`] from it. - #[inline] - pub fn generate<G>( - source: &mut G, - commitment_scheme: &C::CommitmentScheme, - ) -> Result<Self, G::Error> - where - G: SecretKeyGenerator<SecretKey = SecretKey<C>>, - Standard: Distribution<AssetParameters<C>>, - { - Identity::generate_external_receiver(source, commitment_scheme) - } -} - impl<C, I> From<ExternalReceiver<C, I>> for (ShieldedIdentity<C, I>, Spend<C, I>) where C: Configuration, @@ -928,33 +771,6 @@ where } } -/// Internal Receiver Error -/// -/// This `enum` is the error state for the [`generate`] method on [`InternalReceiver`]. -/// See its documentation for more. -/// -/// [`generate`]: InternalReceiver::generate -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "G::Error: Clone, I::Error: Clone"), - Copy(bound = "G::Error: Copy, I::Error: Copy"), - Debug(bound = "G::Error: Debug, I::Error: Debug"), - Eq(bound = "G::Error: Eq, I::Error: Eq"), - Hash(bound = "G::Error: Hash, I::Error: Hash"), - PartialEq(bound = "G::Error: PartialEq, I::Error: PartialEq") -)] -pub enum InternalReceiverError<G, I> -where - G: SecretKeyGenerator, - I: IntegratedEncryptionScheme<Plaintext = Asset>, -{ - /// Secret Key Generator Error - SecretKeyError(G::Error), - - /// Encryption Error - EncryptionError(I::Error), -} - /// Internal Receiver pub struct InternalReceiver<C, I> where @@ -968,29 +784,6 @@ where pub open_spend: OpenSpend<C>, } -impl<C, I> InternalReceiver<C, I> -where - C: Configuration, - I: IntegratedEncryptionScheme<Plaintext = Asset>, -{ - /// Generates a new [`Identity`] from a secret key generation source and builds a new - /// [`InternalReceiver`] from it. - #[inline] - pub fn generate<G, R>( - source: &mut G, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - rng: &mut R, - ) -> Result<Self, InternalReceiverError<G, I>> - where - G: SecretKeyGenerator<SecretKey = SecretKey<C>>, - R: CryptoRng + RngCore + ?Sized, - Standard: Distribution<AssetParameters<C>>, - { - Identity::generate_internal_receiver(source, commitment_scheme, asset, rng) - } -} - impl<C, I> From<InternalReceiver<C, I>> for (Receiver<C, I>, OpenSpend<C>) where C: Configuration, @@ -1086,21 +879,6 @@ where identity.into_pre_sender(commitment_scheme, asset) } - /// Generates a new [`Identity`] from a secret key generation source and builds a new - /// [`PreSender`] from it. - #[inline] - pub fn generate<G>( - source: &mut G, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - ) -> Result<Self, G::Error> - where - G: SecretKeyGenerator<SecretKey = SecretKey<C>>, - Standard: Distribution<AssetParameters<C>>, - { - Identity::generate_pre_sender(source, commitment_scheme, asset) - } - /// Requests the containment proof of `self.utxo` from `utxo_set` so that we can turn `self` /// into a [`Sender`]. #[inline] @@ -1139,33 +917,6 @@ where } } -/// Sender Error -/// -/// This `enum` is the error state for the [`generate`] method on [`Sender`]. -/// See its documentation for more. -/// -/// [`generate`]: Sender::generate -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "G::Error: Clone, S::ContainmentError: Clone"), - Copy(bound = "G::Error: Copy, S::ContainmentError: Copy"), - Debug(bound = "G::Error: Debug, S::ContainmentError: Debug"), - Eq(bound = "G::Error: Eq, S::ContainmentError: Eq"), - Hash(bound = "G::Error: Hash, S::ContainmentError: Hash"), - PartialEq(bound = "G::Error: PartialEq, S::ContainmentError: PartialEq") -)] -pub enum SenderError<G, S> -where - G: SecretKeyGenerator, - S: VerifiedSet, -{ - /// Secret Key Generator Error - SecretKeyError(G::Error), - - /// Containment Error - MissingUtxo(S::ContainmentError), -} - /// Sender pub struct Sender<C, S> where @@ -1216,31 +967,15 @@ where identity.into_sender(commitment_scheme, asset, utxo_set) } - /// Generates a new [`Identity`] from a secret key generation source and builds a new - /// [`Sender`] from it. - #[inline] - pub fn generate<G>( - source: &mut G, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - utxo_set: &S, - ) -> Result<Self, SenderError<G, S>> - where - G: SecretKeyGenerator<SecretKey = SecretKey<C>>, - Standard: Distribution<AssetParameters<C>>, - { - Identity::generate_sender(source, commitment_scheme, asset, utxo_set) - } - /// Returns the asset id for this sender. #[inline] - pub(crate) fn asset_id(&self) -> AssetId { + pub(super) fn asset_id(&self) -> AssetId { self.asset.id } /// Returns the asset value for this sender. #[inline] - pub(crate) fn asset_value(&self) -> AssetBalance { + pub(super) fn asset_value(&self) -> AssetBalance { self.asset.value } diff --git a/manta-accounting/src/keys.rs b/manta-accounting/src/keys.rs index 40b538e6e..cb729a2ae 100644 --- a/manta-accounting/src/keys.rs +++ b/manta-accounting/src/keys.rs @@ -36,6 +36,20 @@ pub trait SecretKeyGenerator { fn generate_key(&mut self) -> Result<Self::SecretKey, Self::Error>; } +impl<S> SecretKeyGenerator for &mut S +where + S: SecretKeyGenerator, +{ + type SecretKey = S::SecretKey; + + type Error = S::Error; + + #[inline] + fn generate_key(&mut self) -> Result<Self::SecretKey, Self::Error> { + (*self).generate_key() + } +} + /// Derived Secret Key Parameter pub trait DerivedSecretKeyParameter: Clone + Default { /// Increments the key parameter by one unit. @@ -120,6 +134,47 @@ pub trait DerivedSecretKeyGenerator { } } +impl<D> DerivedSecretKeyGenerator for &D +where + D: DerivedSecretKeyGenerator, +{ + type SecretKey = D::SecretKey; + + type Account = D::Account; + + type Index = D::Index; + + type Error = D::Error; + + #[inline] + fn generate_key( + &self, + kind: KeyKind, + account: &Self::Account, + index: &Self::Index, + ) -> Result<Self::SecretKey, Self::Error> { + (*self).generate_key(kind, account, index) + } + + #[inline] + fn generate_external_key( + &self, + account: &Self::Account, + index: &Self::Index, + ) -> Result<Self::SecretKey, Self::Error> { + (*self).generate_external_key(account, index) + } + + #[inline] + fn generate_internal_key( + &self, + account: &Self::Account, + index: &Self::Index, + ) -> Result<Self::SecretKey, Self::Error> { + (*self).generate_internal_key(account, index) + } +} + /// Key Kind #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum KeyKind { @@ -343,10 +398,10 @@ where /// Account Index Manager #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "D::Account: Clone, D::Index: Clone"), + Clone(bound = ""), Copy(bound = "D::Account: Copy, D::Index: Copy"), Debug(bound = "D::Account: Debug, D::Index: Debug"), - Default(bound = "D::Account: Default, D::Index: Default"), + Default(bound = ""), Eq(bound = "D::Account: Eq, D::Index: Eq"), Hash(bound = "D::Account: Hash, D::Index: Hash"), PartialEq(bound = "D::Account: PartialEq, D::Index: PartialEq") @@ -390,6 +445,14 @@ where } } + /// Returns the next [`Account`] after `this`. + #[inline] + pub fn next(this: &Self) -> Self { + let mut next_account = this.account.clone(); + next_account.increment(); + Self::new(next_account) + } + /// Resets the external and internal running indices to their default values. #[inline] pub fn reset(&mut self) -> &mut Self { diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index aaef2b37a..f831ade09 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -16,7 +16,6 @@ //! Wallet Abstractions -// TODO: How to manage accounts? Wallet should have a fixed account or not? // TODO: Is recovery different than just building a fresh `Wallet` instance? // TODO: Add query builder for encrypted asset search (internal/external, gap_limit, start_index) // TODO: Merge `AssetMap` and `LocalLedger` into one container and have it query `LedgerSource` @@ -28,10 +27,10 @@ use crate::{ asset::{Asset, AssetBalance, AssetId}, fs::{Load, LoadWith, Save, SaveWith}, identity::{ - self, AssetParameters, Identity, InternalReceiver, InternalReceiverError, OpenSpend, - PreSender, ShieldedIdentity, Utxo, VoidNumber, + self, AssetParameters, Identity, InternalReceiver, OpenSpend, PreSender, ShieldedIdentity, + Utxo, VoidNumber, }, - keys::{Account, DerivedSecretKeyGenerator, InternalKeys, KeyKind}, + keys::{Account, DerivedSecretKeyGenerator, KeyKind}, transfer::{ self, canonical::{Mint, PrivateTransfer, Reclaim}, @@ -39,13 +38,34 @@ use crate::{ }, }; use alloc::{vec, vec::Vec}; -use core::{convert::Infallible, fmt::Debug, future::Future, hash::Hash, marker::PhantomData}; +use core::{fmt::Debug, future::Future, hash::Hash, marker::PhantomData}; use manta_crypto::ies::IntegratedEncryptionScheme; use rand::{ distributions::{Distribution, Standard}, CryptoRng, RngCore, }; +/// Secret Key Generation Error +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "D::Error: Clone, E: Clone"), + Copy(bound = "D::Error: Copy, E: Copy"), + Debug(bound = "D::Error: Debug, E: Debug"), + Eq(bound = "D::Error: Eq, E: Eq"), + Hash(bound = "D::Error: Hash, E: Hash"), + PartialEq(bound = "D::Error: PartialEq, E: PartialEq") +)] +pub enum SecretKeyGenerationError<D, E> +where + D: DerivedSecretKeyGenerator, +{ + /// Secret Key Generator Error + SecretKeyError(D::Error), + + /// Other Error + Error(E), +} + /// Signer pub struct Signer<D> where @@ -92,6 +112,15 @@ where ) } + /// Returns the next [`Signer`] after `this`, incrementing the account number. + #[inline] + pub fn next(this: &Self) -> Self + where + D: Clone, + { + Self::with_account(this.secret_key_source.clone(), Account::next(&this.account)) + } + /// Returns the identity for a key of the given `kind` and `index`. #[inline] pub fn get<C>(&self, kind: KeyKind, index: D::Index) -> Result<Identity<C>, D::Error> @@ -105,7 +134,7 @@ where /// Generates the next identity of the given `kind` for this signer. #[inline] - pub fn next<C>(&mut self, kind: KeyKind) -> Result<Identity<C>, D::Error> + pub fn next_identity<C>(&mut self, kind: KeyKind) -> Result<Identity<C>, D::Error> where C: identity::Configuration<SecretKey = D::SecretKey>, { @@ -116,7 +145,7 @@ where /// Generates the next external identity for this signer. #[inline] - pub fn next_external<C>(&mut self) -> Result<Identity<C>, D::Error> + pub fn next_external_identity<C>(&mut self) -> Result<Identity<C>, D::Error> where C: identity::Configuration<SecretKey = D::SecretKey>, { @@ -127,7 +156,7 @@ where /// Generates the next internal identity for this signer. #[inline] - pub fn next_internal<C>(&mut self) -> Result<Identity<C>, D::Error> + pub fn next_internal_identity<C>(&mut self) -> Result<Identity<C>, D::Error> where C: identity::Configuration<SecretKey = D::SecretKey>, { @@ -148,7 +177,7 @@ where I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { - self.next_external() + self.next_external_identity() .map(move |identity| identity.into_shielded(commitment_scheme)) } @@ -160,17 +189,17 @@ where commitment_scheme: &C::CommitmentScheme, asset: Asset, rng: &mut R, - ) -> Result<InternalReceiver<C, I>, InternalReceiverError<InternalKeys<D>, I>> + ) -> Result<InternalReceiver<C, I>, SecretKeyGenerationError<D, I::Error>> where C: identity::Configuration<SecretKey = D::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, R: CryptoRng + RngCore + ?Sized, Standard: Distribution<AssetParameters<C>>, { - self.next_internal() - .map_err(InternalReceiverError::SecretKeyError)? + self.next_internal_identity() + .map_err(SecretKeyGenerationError::SecretKeyError)? .into_internal_receiver(commitment_scheme, asset, rng) - .map_err(InternalReceiverError::EncryptionError) + .map_err(SecretKeyGenerationError::Error) } /// Generates a new [`InternalReceiver`] to receive an asset, with the given `asset_id` and @@ -181,7 +210,7 @@ where commitment_scheme: &C::CommitmentScheme, asset_id: AssetId, rng: &mut R, - ) -> Result<InternalReceiver<C, I>, InternalReceiverError<InternalKeys<D>, I>> + ) -> Result<InternalReceiver<C, I>, SecretKeyGenerationError<D, I::Error>> where C: identity::Configuration<SecretKey = D::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, @@ -415,132 +444,48 @@ where /// Current Ledger Checkpoint pub checkpoint: LS::Checkpoint, - /// Ledger Responses - // FIXME: Design responses. - pub responses: Vec<bool>, -} - -/// Asset Map -pub trait AssetMap { - /// Returns the current balance associated with this `id`. - fn balance(&self, id: AssetId) -> AssetBalance; - - /// Returns `true` if `self` contains at least `asset.value` of the asset of kind `asset.id`. - #[inline] - fn contains(&self, asset: Asset) -> bool { - self.balance(asset.id) >= asset.value - } - - /// Sets the asset balance for `id` to `value`. - fn set_balance(&mut self, id: AssetId, value: AssetBalance); - - /// Sets the asset balance for `asset.id` to `asset.value`. - #[inline] - fn set_asset(&mut self, asset: Asset) { - self.set_balance(asset.id, asset.value) - } - - /// Mutates the asset balance for `id` to the result of `f` if it succeeds, returning the - /// new balance. - #[inline] - #[must_use = "this only modifies the stored value if the function call succeeded"] - fn mutate<F, E>(&mut self, id: AssetId, f: F) -> Result<AssetBalance, E> - where - F: FnOnce(AssetBalance) -> Result<AssetBalance, E>, - { - // TODO: use `try_trait_v2` when it comes out - f(self.balance(id)).map(move |v| { - self.set_balance(id, v); - v - }) - } - - /// Performs a deposit of value `asset.value` into the balance of `asset.id`, - /// returning the new balance for this `asset.id` if it did not overflow. - /// - /// To skip the overflow check, use [`deposit_unchecked`](Self::deposit_unchecked) instead. - #[inline] - #[must_use = "this only modifies the stored value if the addition did not overflow"] - fn deposit(&mut self, asset: Asset) -> Option<AssetBalance> { - self.mutate(asset.id, move |v| v.checked_add(asset.value).ok_or(())) - .ok() - } - - /// Performs a deposit of value `asset.value` into the balance of `asset.id`, - /// without checking for overflow, returning the new balance for this `asset.id`. - /// - /// # Panics - /// - /// This function panics on overflow. To explicitly check for overflow, use - /// [`deposit`](Self::deposit) instead. - #[inline] - fn deposit_unchecked(&mut self, asset: Asset) -> AssetBalance { - self.mutate::<_, Infallible>(asset.id, move |v| Ok(v + asset.value)) - .unwrap() - } - - /// Performs a withdrawl of value `asset.value` from the balance of `asset.id`, - /// returning the new balance for this `asset.id` if it did not overflow. - /// - /// To skip the overflow check, use [`withdraw_unchecked`](Self::withdraw_unchecked) instead. - #[inline] - #[must_use = "this only modifies the stored value if the subtraction did not overflow"] - fn withdraw(&mut self, asset: Asset) -> Option<AssetBalance> { - self.mutate(asset.id, move |v| v.checked_sub(asset.value).ok_or(())) - .ok() - } - - /// Performs a withdrawl of value `asset.value` from the balance of `asset.id`, - /// without checking for overflow, returning the new balance for this `asset.id`. - /// - /// # Panics - /// - /// This function panics on overflow. To explicitly check for overflow, use - /// [`withdraw`](Self::withdraw) instead. - #[inline] - fn withdraw_unchecked(&mut self, asset: Asset) -> AssetBalance { - self.mutate::<_, Infallible>(asset.id, move |v| Ok(v - asset.value)) - .unwrap() - } + /// Transaction Failed at the Given Index + pub failure_index: Option<usize>, } /// Asset Key #[derive(derivative::Derivative)] #[derivative( - Clone(bound = ""), - Copy(bound = "D::Index: Copy"), - Debug(bound = "D::Index: Debug"), - Eq(bound = "D::Index: Eq"), - Hash(bound = "D::Index: Hash"), - PartialEq(bound = "D::Index: PartialEq") + Clone(bound = "S::Index: Clone"), + Copy(bound = "S::Index: Copy"), + Debug(bound = "S::Index: Debug"), + Eq(bound = "S::Index: Eq"), + Hash(bound = "S::Index: Hash"), + PartialEq(bound = "S::Index: PartialEq") )] -pub struct AssetKey<D> +pub struct AssetKey<S> where - D: DerivedSecretKeyGenerator, + S: AssetMap + ?Sized, { /// Key Kind pub kind: KeyKind, /// Key Index - pub index: D::Index, + pub index: S::Index, /// Value stored at this key pub value: AssetBalance, } -impl<D> AssetKey<D> +impl<S> AssetKey<S> where - D: DerivedSecretKeyGenerator, + S: AssetMap + ?Sized, { /// Builds a [`PreSender`] for `self`, using `signer` to generate the secret key. #[inline] - pub fn into_pre_sender<C>( + pub fn into_pre_sender<D, C>( self, signer: &Signer<D>, commitment_scheme: &C::CommitmentScheme, asset_id: AssetId, ) -> Result<PreSender<C>, D::Error> where + D: DerivedSecretKeyGenerator<Index = S::Index>, C: identity::Configuration<SecretKey = D::SecretKey>, Standard: Distribution<AssetParameters<C>>, { @@ -551,46 +496,105 @@ where } /// Asset Selection -pub struct AssetSelection<D, S> +pub struct AssetSelection<S> where - D: DerivedSecretKeyGenerator, - S: AssetSelector<D> + ?Sized, + S: AssetMap + ?Sized, { /// Change Amount pub change: AssetBalance, - /// Asset Keys - pub keys: S::Keys, + /// Asset Sender Keys + pub sender_keys: S::Keys, } -impl<D, S> AssetSelection<D, S> +impl<S> AssetSelection<S> where - D: DerivedSecretKeyGenerator, - S: AssetSelector<D> + ?Sized, + S: AssetMap + ?Sized, { - /* TODO: + /// Builds an [`InternalReceiver`] to capture the `change` from the given [`AssetSelection`]. + #[inline] + pub fn change_receiver<D, C, I, R>( + &self, + signer: &mut Signer<D>, + asset_id: AssetId, + commitment_scheme: &C::CommitmentScheme, + rng: &mut R, + ) -> Result<InternalReceiver<C, I>, SecretKeyGenerationError<D, I::Error>> + where + D: DerivedSecretKeyGenerator<Index = S::Index>, + C: identity::Configuration<SecretKey = D::SecretKey>, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + R: CryptoRng + RngCore + ?Sized, + Standard: Distribution<AssetParameters<C>>, + { + signer.next_internal_receiver(commitment_scheme, Asset::new(asset_id, self.change), rng) + } + + /// Builds a vector of `n` internal receivers to capture the `change` from the given + /// [`AssetSelection`]. /// + /// # Panics + /// + /// This method panics if `n == 0`. #[inline] - pub fn make_change(&self, signer: &Signer<D>, n: usize) -> Option<Vec<()>> { - let amounts = self.change.make_change(n)?; - todo!() + pub fn change_receivers<D, C, I, R>( + &self, + signer: &mut Signer<D>, + asset_id: AssetId, + n: usize, + commitment_scheme: &C::CommitmentScheme, + rng: &mut R, + ) -> Result<Vec<InternalReceiver<C, I>>, SecretKeyGenerationError<D, I::Error>> + where + D: DerivedSecretKeyGenerator<Index = S::Index>, + C: identity::Configuration<SecretKey = D::SecretKey>, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + R: CryptoRng + RngCore + ?Sized, + Standard: Distribution<AssetParameters<C>>, + { + self.change + .make_change(n) + .unwrap() + .map(move |value| { + signer.next_internal_receiver(commitment_scheme, Asset::new(asset_id, value), rng) + }) + .collect() } - */ } -/// Asset Selector -pub trait AssetSelector<D> -where - D: DerivedSecretKeyGenerator, -{ - /// Keys Type - type Keys: Iterator<Item = AssetKey<D>>; +/// Asset Map +pub trait AssetMap { + /// Key Index Type + type Index; + + /// Keys Iterator Type + type Keys: Iterator<Item = AssetKey<Self>>; /// Error Type type Error; /// Selects assets which total up to at least `asset` in value. - fn select(&self, asset: Asset) -> Result<AssetSelection<D, Self>, Self::Error>; + fn select(&self, asset: Asset) -> Result<AssetSelection<Self>, Self::Error>; + + /// Withdraws the asset stored at `kind` and `index`. + fn withdraw(&mut self, kind: KeyKind, index: Self::Index) -> Result<(), Self::Error>; + + /// Deposits `asset` at the key stored at `kind` and `index`. + fn deposit( + &mut self, + kind: KeyKind, + index: Self::Index, + asset: Asset, + ) -> Result<(), Self::Error>; + + /// Returns the current balance associated with this `id`. + fn balance(&self, id: AssetId) -> AssetBalance; + + /// Returns true if `self` contains at least `asset.value` of the asset of kind `asset.id`. + #[inline] + fn contains(&self, asset: Asset) -> bool { + self.balance(asset.id) >= asset.value + } } /// Ledger Ownership Marker @@ -672,22 +676,28 @@ where } /// Wallet Balance State +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "M: Clone, L: Clone"), + Copy(bound = "M: Copy, L: Copy"), + Debug(bound = "M: Debug, L: Debug"), + Default(bound = "M: Default, L: Default"), + Eq(bound = "M: Eq, L: Eq"), + Hash(bound = "M: Hash, L: Hash"), + PartialEq(bound = "M: PartialEq, L: PartialEq") +)] pub struct BalanceState<C, L, M> where C: transfer::Configuration, L: LocalLedger<C>, M: AssetMap, { - /// Secret Asset Map - secret_assets: M, - - /// Public Asset Map - // TODO: Find a way to connect this. - _public_assets: M, - /// Local Ledger ledger: L, + /// Asset Map + asset_map: M, + /// Type Parameter Marker __: PhantomData<C>, } @@ -741,7 +751,7 @@ where self.ledger.set_checkpoint(checkpoint); - // FIXME: Deposit into `secret_assets` and `public_assets`. + // FIXME: Deposit into `asset_map`. Ok(()) } @@ -789,74 +799,77 @@ where let SendResponse { checkpoint, - responses, + failure_index, } = ledger.send(transfers).await?; // TODO: Revoke keys if the transfer failed, otherwise recover from the failure. - let _ = responses; + let _ = failure_index; self.ledger.set_checkpoint(checkpoint); - // FIXME: Withdraw from `secret_assets` and `public_assets`. + // FIXME: Withdraw from `asset_map` todo!() } } -/* TODO: /// Wallet -pub struct Wallet<C, D, LL, SM, PM = SM> +pub struct Wallet<D, C, L, M> where - C: transfer::Configuration, - D: DerivedSecretKeyGenerator<SecretKey = C::SecretKey>, - LL: LocalLedger<C>, - SM: AssetMap, - PM: AssetMap, + D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, + L: LocalLedger<C>, + M: AssetMap<Index = D::Index>, { /// Wallet Signer signer: Signer<D>, /// Wallet Balance State - balance_state: BalanceState<C, LL, SM, PM>, + balance_state: BalanceState<C, L, M>, } -*/ -/// Wallet -pub struct Wallet<D> -where - D: DerivedSecretKeyGenerator, -{ - /// Wallet Signer - signer: Signer<D>, -} - -impl<D> Wallet<D> +impl<D, C, L, M> Wallet<D, C, L, M> where D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, + L: LocalLedger<C>, + M: AssetMap<Index = D::Index>, { /// Builds a new [`Wallet`] for `signer`. #[inline] - pub fn new(signer: Signer<D>) -> Self { - Self { signer } + pub fn new(signer: Signer<D>) -> Self + where + L: Default, + M: Default, + { + Self::with_balances(signer, Default::default()) + } + + /// Builds a new [`Wallet`] for `signer` with the given `balance_state`. + #[inline] + pub fn with_balances(signer: Signer<D>, balance_state: BalanceState<C, L, M>) -> Self { + Self { + signer, + balance_state, + } } /// Builds a [`Mint`] transaction to mint `asset` and returns the [`OpenSpend`] for that asset. #[inline] - pub fn mint<C, R>( + pub fn mint<R>( &mut self, commitment_scheme: &C::CommitmentScheme, asset: Asset, rng: &mut R, ) -> Result<(Mint<C>, OpenSpend<C>), MintError<D, C>> where - C: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, Standard: Distribution<AssetParameters<C>>, { Mint::from_identity( self.signer - .next_internal() + .next_internal_identity() .map_err(MintError::SecretKeyError)?, commitment_scheme, asset, @@ -865,39 +878,52 @@ where .map_err(MintError::EncryptionError) } - /// TODO: Builds [`PrivateTransfer`] transactions to send `asset` to an `external_receiver`. + /// Builds [`PrivateTransfer`] transactions to send `asset` to an `external_receiver`. #[inline] - pub fn batch_private_transfer_external<C, R>( - &self, + pub fn private_transfer_external<R>( + &mut self, commitment_scheme: &C::CommitmentScheme, asset: Asset, - external_receiver: ShieldedIdentity<C, C::IntegratedEncryptionScheme>, + external_receiver: transfer::ShieldedIdentity<C>, rng: &mut R, ) -> Option<Vec<PrivateTransfer<C>>> where - C: transfer::Configuration, R: CryptoRng + RngCore + ?Sized, + Standard: Distribution<AssetParameters<C>>, { // TODO: spec: - // 1. check that we have enough `asset` in the secret_assets map + // 1. check that we have enough `asset` in the `asset_map` // 2. find out which keys have control over `asset` // 3. build two senders and build a receiver and a change receiver for the extra change + let selection = self.balance_state.asset_map.select(asset).ok()?; + + // let senders = selection.sender_keys; + + let change_receiver = selection + .change_receiver::<_, _, C::IntegratedEncryptionScheme, _>( + &mut self.signer, + asset.id, + commitment_scheme, + rng, + ) + .ok()?; + let _ = external_receiver.into_receiver(commitment_scheme, asset, rng); + // TODO: PrivateTransfer::build([fst, snd], [external_receiver, change]) todo!() } - /// TODO: Builds a [`Reclaim`] transaction. + /// Builds a [`Reclaim`] transaction. #[inline] - pub fn reclaim<C, R>( + pub fn reclaim<R>( &self, commitment_scheme: &C::CommitmentScheme, asset: Asset, rng: &mut R, ) -> Option<Reclaim<C>> where - C: transfer::Configuration, R: CryptoRng + RngCore + ?Sized, { let _ = (commitment_scheme, asset, rng); @@ -906,9 +932,12 @@ where } } -impl<D> Load for Wallet<D> +impl<D, C, L, M> Load for Wallet<D, C, L, M> where D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, + L: LocalLedger<C> + Default, + M: AssetMap<Index = D::Index> + Default, Signer<D>: Load, { type Path = <Signer<D> as Load>::Path; @@ -922,14 +951,16 @@ where where P: AsRef<Self::Path>, { - // TODO: Also add a way to save `BalanceState`. - Ok(Self::new(Signer::<D>::load(path, loading_key)?)) + Ok(Self::new(Signer::load(path, loading_key)?)) } } -impl<D> Save for Wallet<D> +impl<D, C, L, M> Save for Wallet<D, C, L, M> where D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, + L: LocalLedger<C>, + M: AssetMap<Index = D::Index>, Signer<D>: Save, { type Path = <Signer<D> as Save>::Path; @@ -943,7 +974,6 @@ where where P: AsRef<Self::Path>, { - // TODO: Also add a way to save `BalanceState`. self.signer.save(path, saving_key) } } diff --git a/manta-pay/src/accounting/keys.rs b/manta-pay/src/accounting/keys.rs index ba98ae871..b73d9fa49 100644 --- a/manta-pay/src/accounting/keys.rs +++ b/manta-pay/src/accounting/keys.rs @@ -150,6 +150,8 @@ pub type MantaDerivedKeySecret = DerivedKeySecret<Manta>; pub type CalamariDerivedKeySecret = DerivedKeySecret<Calamari>; /// Derived Key Secret +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy)] pub struct DerivedKeySecret<C> where C: CoinType, From 81f59af8c26f2cf709e5a1d7d27e80a11d5f715f Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 1 Oct 2021 04:16:54 -0400 Subject: [PATCH 072/275] WIP: refactoring keys interfaces to save the key index for later use --- manta-accounting/src/identity.rs | 24 ++++++ manta-accounting/src/keys.rs | 81 ++++++++++++++++++++ manta-accounting/src/wallet.rs | 127 ++++++++++++++++++++++++++----- 3 files changed, 211 insertions(+), 21 deletions(-) diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index d49784722..282f5c200 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -673,6 +673,21 @@ where )) } + /// Builds a new [`PreSender`] for the given `encrypted_asset`. + #[inline] + pub fn into_pre_sender( + self, + commitment_scheme: &C::CommitmentScheme, + encrypted_asset: EncryptedMessage<I>, + ) -> Result<PreSender<C>, I::Error> + where + Standard: Distribution<AssetParameters<C>>, + { + Ok(self + .try_open(&encrypted_asset)? + .into_pre_sender(commitment_scheme)) + } + /// Builds a new [`Sender`] for the given `encrypted_asset`. #[inline] pub fn into_sender<S>( @@ -755,6 +770,15 @@ where Self { identity, asset } } + /// Builds a new [`PreSender`] for `self`. + #[inline] + pub fn into_pre_sender(self, commitment_scheme: &C::CommitmentScheme) -> PreSender<C> + where + Standard: Distribution<AssetParameters<C>>, + { + self.identity.into_pre_sender(commitment_scheme, self.asset) + } + /// Builds a new [`Sender`] for `self`. #[inline] pub fn into_sender<S>( diff --git a/manta-accounting/src/keys.rs b/manta-accounting/src/keys.rs index cb729a2ae..b05adf7c4 100644 --- a/manta-accounting/src/keys.rs +++ b/manta-accounting/src/keys.rs @@ -21,6 +21,8 @@ // TODO: Check to make sure we conform to the specification and then make a note about it in the // module documentation, and add a link to the specification. +// TODO: How many of these interfaces should actually return `SecretKey<D>` instead of +// `D::SecretKey`? use core::{fmt::Debug, hash::Hash}; @@ -199,6 +201,85 @@ impl KeyKind { } } +/// Key Index +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = ""), + Copy(bound = "D::Index: Copy"), + Debug(bound = "D::Index: Debug"), + Eq(bound = "D::Index: Eq"), + Hash(bound = "D::Index: Hash"), + PartialEq(bound = "D::Index: PartialEq") +)] +pub struct Index<D> +where + D: DerivedSecretKeyGenerator, +{ + /// Key Kind + pub kind: KeyKind, + + /// Key Index + pub index: D::Index, +} + +impl<D> Index<D> +where + D: DerivedSecretKeyGenerator, +{ + /// Returns `true` if `self` represents an external key. + #[inline] + pub fn is_external(&self) -> bool { + self.kind.is_external() + } + + /// Returns `true` if `self` represents an internal key. + #[inline] + pub fn is_internal(&self) -> bool { + self.kind.is_internal() + } +} + +/// Labelled Secret Key Type +pub type SecretKey<D> = KeyOwned<D, <D as DerivedSecretKeyGenerator>::SecretKey>; + +/// Key-Owned Value +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "T: Clone"), + Copy(bound = "D::Index: Copy, T: Copy"), + Debug(bound = "D::Index: Debug, T: Debug"), + Eq(bound = "D::Index: Eq, T: Eq"), + Hash(bound = "D::Index: Hash, T: Hash"), + PartialEq(bound = "D::Index: PartialEq, T: PartialEq") +)] +pub struct KeyOwned<D, T> +where + D: DerivedSecretKeyGenerator, +{ + /// Key Index + pub index: Index<D>, + + /// Value Owned by the Key + pub value: T, +} + +impl<D, T> KeyOwned<D, T> +where + D: DerivedSecretKeyGenerator, +{ + /// Returns `true` if `self` represents a value owned by an external key. + #[inline] + pub fn is_external(&self) -> bool { + self.index.is_external() + } + + /// Returns `true` if `self` represents a value owned by an internal key. + #[inline] + pub fn is_internal(&self) -> bool { + self.index.is_internal() + } +} + /// Generates an external or internal secret key according to the [`DerivedSecretKeyGenerator`] /// protocol and increments the running `index`. #[inline] diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index f831ade09..0cf767c74 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -22,6 +22,7 @@ // instead of `Wallet`. Then `Wallet` just has access to local ledger (also async). // Have then a "light wallet" which is just `Wallet` and a "heavy wallet" where the // local ledger and asset map are built-in to it. +// TODO: Have a good way to asynchronously connect `Signer` and `BalanceState`. use crate::{ asset::{Asset, AssetBalance, AssetId}, @@ -448,10 +449,46 @@ where pub failure_index: Option<usize>, } +/// Key Index +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "Idx: Clone"), + Copy(bound = "Idx: Copy"), + Debug(bound = "Idx: Debug"), + Eq(bound = "Idx: Eq"), + Hash(bound = "Idx: Hash"), + PartialEq(bound = "Idx: PartialEq") +)] +pub struct Key<Idx> { + /// Key Kind + pub kind: KeyKind, + + /// Key Index + pub index: Idx, +} + +/// Key-Owned Value +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "Idx: Clone, T: Clone"), + Copy(bound = "Idx: Copy, T: Copy"), + Debug(bound = "Idx: Debug, T: Debug"), + Eq(bound = "Idx: Eq, T: Eq"), + Hash(bound = "Idx: Hash, T: Hash"), + PartialEq(bound = "Idx: PartialEq, T: PartialEq") +)] +pub struct KeyOwned<Idx, T> { + /// Key + pub key: Key<Idx>, + + /// Value Owned by the Key + pub value: T, +} + /// Asset Key #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "S::Index: Clone"), + Clone(bound = ""), Copy(bound = "S::Index: Copy"), Debug(bound = "S::Index: Debug"), Eq(bound = "S::Index: Eq"), @@ -462,11 +499,8 @@ pub struct AssetKey<S> where S: AssetMap + ?Sized, { - /// Key Kind - pub kind: KeyKind, - - /// Key Index - pub index: S::Index, + /// Key + pub key: Key<S::Index>, /// Value stored at this key pub value: AssetBalance, @@ -489,8 +523,9 @@ where C: identity::Configuration<SecretKey = D::SecretKey>, Standard: Distribution<AssetParameters<C>>, { + let key = self.key; // FIXME: We need to attach these to types. Ok(signer - .get(self.kind, self.index)? + .get(key.kind, key.index)? .into_pre_sender(commitment_scheme, Asset::new(asset_id, self.value))) } } @@ -565,28 +600,60 @@ where /// Asset Map pub trait AssetMap { /// Key Index Type - type Index; + type Index: Clone + Default; /// Keys Iterator Type - type Keys: Iterator<Item = AssetKey<Self>>; + /// + /// # Implementation Note + /// + /// See [`select`](Self::select) for properties of this iterator. + type Keys: IntoIterator<Item = AssetKey<Self>>; /// Error Type type Error; /// Selects assets which total up to at least `asset` in value. + /// + /// # Contract + /// + /// Implementations should always return an error whenever the `asset` cannot be covered + /// by a set of keys, i.e. when the call `self.contains(asset)` returns `false`. In any other + /// case, the iterator returned by the `Ok` branch of this method must always contain at least + /// one element, i.e. the following + /// + /// ```text + /// match self.select(asset) { + /// Ok(selection) => selection.keys.into_iter().next().is_some(), + /// _ => true, + /// } + /// ``` + /// + /// should always return `true`. fn select(&self, asset: Asset) -> Result<AssetSelection<Self>, Self::Error>; /// Withdraws the asset stored at `kind` and `index`. - fn withdraw(&mut self, kind: KeyKind, index: Self::Index) -> Result<(), Self::Error>; + fn withdraw_from(&mut self, kind: KeyKind, index: Self::Index) -> Result<(), Self::Error>; + + /// Withdraws the asset stored at `key`. + #[inline] + fn withdraw(&mut self, key: Key<Self::Index>) -> Result<(), Self::Error> { + self.withdraw_from(key.kind, key.index) + } /// Deposits `asset` at the key stored at `kind` and `index`. - fn deposit( + fn deposit_to( &mut self, kind: KeyKind, index: Self::Index, asset: Asset, ) -> Result<(), Self::Error>; + /// Deposits `asset` at the key stored at `key`. + #[inline] + fn deposit(&mut self, key: Key<Self::Index>, asset: Asset) -> Result<(), Self::Error> { + self.deposit_to(key.kind, key.index, asset) + } + /// Returns the current balance associated with this `id`. fn balance(&self, id: AssetId) -> AssetBalance; @@ -880,7 +947,7 @@ where /// Builds [`PrivateTransfer`] transactions to send `asset` to an `external_receiver`. #[inline] - pub fn private_transfer_external<R>( + pub fn private_transfer<R>( &mut self, commitment_scheme: &C::CommitmentScheme, asset: Asset, @@ -891,15 +958,8 @@ where R: CryptoRng + RngCore + ?Sized, Standard: Distribution<AssetParameters<C>>, { - // TODO: spec: - // 1. check that we have enough `asset` in the `asset_map` - // 2. find out which keys have control over `asset` - // 3. build two senders and build a receiver and a change receiver for the extra change - let selection = self.balance_state.asset_map.select(asset).ok()?; - // let senders = selection.sender_keys; - let change_receiver = selection .change_receiver::<_, _, C::IntegratedEncryptionScheme, _>( &mut self.signer, @@ -909,9 +969,34 @@ where ) .ok()?; - let _ = external_receiver.into_receiver(commitment_scheme, asset, rng); + let mut pre_senders = selection + .sender_keys + .into_iter() + .map(|k| k.into_pre_sender(&self.signer, commitment_scheme, asset.id)) + .collect::<Result<Vec<_>, _>>() + .ok()?; + + let external_receiver = external_receiver.into_receiver(commitment_scheme, asset, rng); + + let mint = if pre_senders.len() % 2 == 1 { + let (mint, open_spend) = self + .mint(commitment_scheme, Asset::zero(asset.id), rng) + .ok()?; + pre_senders.push(open_spend.into_pre_sender(commitment_scheme)); + Some(mint) + } else { + None + }; + + /* TODO: + for i in 0..(pre_senders.len() / 2) { + PrivateTransfer::build( + [pre_senders[2 * i], pre_senders[2 * i + 1]], + [?, ?] + ) + } + */ - // TODO: PrivateTransfer::build([fst, snd], [external_receiver, change]) todo!() } From 28ae7669fff69dc56e1def886c54835c86dadfd5 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 1 Oct 2021 15:11:01 -0400 Subject: [PATCH 073/275] WIP: add KeyOwned wrapper type --- manta-accounting/src/keys.rs | 260 ++++++++++++++++++++++++++++----- manta-accounting/src/wallet.rs | 259 +++++++++++++------------------- 2 files changed, 322 insertions(+), 197 deletions(-) diff --git a/manta-accounting/src/keys.rs b/manta-accounting/src/keys.rs index b05adf7c4..ae44e2cec 100644 --- a/manta-accounting/src/keys.rs +++ b/manta-accounting/src/keys.rs @@ -21,10 +21,8 @@ // TODO: Check to make sure we conform to the specification and then make a note about it in the // module documentation, and add a link to the specification. -// TODO: How many of these interfaces should actually return `SecretKey<D>` instead of -// `D::SecretKey`? -use core::{fmt::Debug, hash::Hash}; +use core::{convert::TryFrom, fmt::Debug, hash::Hash}; /// Secret Key Generator Trait pub trait SecretKeyGenerator { @@ -35,7 +33,7 @@ pub trait SecretKeyGenerator { type Error; /// Generates a new secret key. - fn generate_key(&mut self) -> Result<Self::SecretKey, Self::Error>; + fn next_key(&mut self) -> Result<Self::SecretKey, Self::Error>; } impl<S> SecretKeyGenerator for &mut S @@ -47,8 +45,8 @@ where type Error = S::Error; #[inline] - fn generate_key(&mut self) -> Result<Self::SecretKey, Self::Error> { - (*self).generate_key() + fn next_key(&mut self) -> Result<Self::SecretKey, Self::Error> { + (*self).next_key() } } @@ -213,7 +211,7 @@ impl KeyKind { )] pub struct Index<D> where - D: DerivedSecretKeyGenerator, + D: DerivedSecretKeyGenerator + ?Sized, { /// Key Kind pub kind: KeyKind, @@ -224,8 +222,26 @@ where impl<D> Index<D> where - D: DerivedSecretKeyGenerator, + D: DerivedSecretKeyGenerator + ?Sized, { + /// Builds a new [`Index`] for `kind` and `index`. + #[inline] + pub fn new(kind: KeyKind, index: D::Index) -> Self { + Self { kind, index } + } + + /// Builds a new [`Index`] for an external key with the given `index`. + #[inline] + pub fn new_external(index: D::Index) -> Self { + Self::new(KeyKind::External, index) + } + + /// Builds a new [`Index`] for an internal key with the given `index`. + #[inline] + pub fn new_internal(index: D::Index) -> Self { + Self::new(KeyKind::Internal, index) + } + /// Returns `true` if `self` represents an external key. #[inline] pub fn is_external(&self) -> bool { @@ -237,6 +253,12 @@ where pub fn is_internal(&self) -> bool { self.kind.is_internal() } + + /// Gets the underlying key for this `index` at the `account`. + #[inline] + pub fn key(&self, source: &D, account: &D::Account) -> Result<D::SecretKey, D::Error> { + source.generate_key(self.kind, account, &self.index) + } } /// Labelled Secret Key Type @@ -254,19 +276,43 @@ pub type SecretKey<D> = KeyOwned<D, <D as DerivedSecretKeyGenerator>::SecretKey> )] pub struct KeyOwned<D, T> where - D: DerivedSecretKeyGenerator, + D: DerivedSecretKeyGenerator + ?Sized, { - /// Key Index - pub index: Index<D>, - /// Value Owned by the Key pub value: T, + + /// Key Index + pub index: Index<D>, } impl<D, T> KeyOwned<D, T> where - D: DerivedSecretKeyGenerator, + D: DerivedSecretKeyGenerator + ?Sized, { + /// Builds a new [`KeyOwned`] for `value` with `index` as the [`Index`]. + #[inline] + pub fn new(value: T, index: Index<D>) -> Self { + Self { value, index } + } + + /// Builds a new [`KeyOwned`] for `value` with `kind` and `index` as the [`Index`]. + #[inline] + pub fn with_kind(value: T, kind: KeyKind, index: D::Index) -> Self { + Self::new(value, Index::new(kind, index)) + } + + /// Builds a new [`KeyOwned`] for `value` for an external key with `index`. + #[inline] + pub fn new_external(value: T, index: D::Index) -> Self { + Self::new(value, Index::new_external(index)) + } + + /// Builds a new [`KeyOwned`] for `value` for an internal key with `index`. + #[inline] + pub fn new_internal(value: T, index: D::Index) -> Self { + Self::new(value, Index::new_internal(index)) + } + /// Returns `true` if `self` represents a value owned by an external key. #[inline] pub fn is_external(&self) -> bool { @@ -278,6 +324,134 @@ where pub fn is_internal(&self) -> bool { self.index.is_internal() } + + /// Maps the underlying value using `f`. + #[inline] + pub fn map<U, F>(self, f: F) -> KeyOwned<D, U> + where + F: FnOnce(T) -> U, + { + KeyOwned::new(f(self.value), self.index) + } + + /// Maps the underlying value using `f` and then factors over the `Some` branch. + #[inline] + pub fn map_some<U, F>(self, f: F) -> Option<KeyOwned<D, U>> + where + F: FnOnce(T) -> Option<U>, + { + self.map(f).collect() + } + + /// Maps the underlying value using `f` and then factors over the `Ok` branch. + #[inline] + pub fn map_ok<U, E, F>(self, f: F) -> Result<KeyOwned<D, U>, E> + where + F: FnOnce(T) -> Result<U, E>, + { + self.map(f).collect() + } +} + +impl<D, T> AsRef<T> for KeyOwned<D, T> +where + D: DerivedSecretKeyGenerator + ?Sized, +{ + #[inline] + fn as_ref(&self) -> &T { + &self.value + } +} + +impl<D, T> AsMut<T> for KeyOwned<D, T> +where + D: DerivedSecretKeyGenerator + ?Sized, +{ + #[inline] + fn as_mut(&mut self) -> &mut T { + &mut self.value + } +} + +impl<D, T> From<KeyOwned<D, T>> for (T, Index<D>) +where + D: DerivedSecretKeyGenerator + ?Sized, +{ + #[inline] + fn from(key_owned: KeyOwned<D, T>) -> Self { + (key_owned.value, key_owned.index) + } +} + +impl<D, L, R> KeyOwned<D, (L, R)> +where + D: DerivedSecretKeyGenerator + ?Sized, +{ + /// Factors the key index over the left value in the pair. + #[inline] + pub fn left(self) -> (KeyOwned<D, L>, R) { + (KeyOwned::new(self.value.0, self.index), self.value.1) + } + + /// Factors the key index over the right value in the pair. + #[inline] + pub fn right(self) -> (L, KeyOwned<D, R>) { + (self.value.0, KeyOwned::new(self.value.1, self.index)) + } +} + +impl<D, T> KeyOwned<D, Option<T>> +where + D: DerivedSecretKeyGenerator + ?Sized, +{ + /// Converts a `KeyOwned<D, Option<T>>` into an `Option<KeyOwned<D, T>>`. + #[inline] + pub fn collect(self) -> Option<KeyOwned<D, T>> { + Some(KeyOwned::new(self.value?, self.index)) + } +} + +impl<D, T> From<KeyOwned<D, Option<T>>> for Option<KeyOwned<D, T>> +where + D: DerivedSecretKeyGenerator + ?Sized, +{ + #[inline] + fn from(key_owned: KeyOwned<D, Option<T>>) -> Self { + key_owned.collect() + } +} + +impl<D, T, E> KeyOwned<D, Result<T, E>> +where + D: DerivedSecretKeyGenerator + ?Sized, +{ + /// Converts a `KeyOwned<D, Result<T, E>>` into an `Result<KeyOwned<D, T>, E>`. + #[inline] + pub fn collect(self) -> Result<KeyOwned<D, T>, E> { + Ok(KeyOwned::new(self.value?, self.index)) + } +} + +impl<D, T, E> From<KeyOwned<D, Result<T, E>>> for Result<KeyOwned<D, T>, E> +where + D: DerivedSecretKeyGenerator + ?Sized, +{ + #[inline] + fn from(key_owned: KeyOwned<D, Result<T, E>>) -> Self { + key_owned.collect() + } +} + +impl<D, T, E> TryFrom<KeyOwned<D, Result<T, E>>> for KeyOwned<D, T> +where + D: DerivedSecretKeyGenerator + ?Sized, +{ + type Error = E; + + #[inline] + fn try_from(key_owned: KeyOwned<D, Result<T, E>>) -> Result<Self, Self::Error> { + key_owned.collect() + } } /// Generates an external or internal secret key according to the [`DerivedSecretKeyGenerator`] @@ -288,11 +462,15 @@ pub fn next_key<D>( kind: KeyKind, account: &D::Account, index: &mut D::Index, -) -> Result<D::SecretKey, D::Error> +) -> Result<SecretKey<D>, D::Error> where D: DerivedSecretKeyGenerator + ?Sized, { - let secret_key = source.generate_key(kind, account, index)?; + let secret_key = SecretKey::with_kind( + source.generate_key(kind, account, index)?, + kind, + index.clone(), + ); index.increment(); Ok(secret_key) } @@ -304,11 +482,12 @@ pub fn next_external<D>( source: &D, account: &D::Account, index: &mut D::Index, -) -> Result<D::SecretKey, D::Error> +) -> Result<SecretKey<D>, D::Error> where D: DerivedSecretKeyGenerator + ?Sized, { - let secret_key = source.generate_external_key(account, index)?; + let secret_key = + SecretKey::new_external(source.generate_external_key(account, index)?, index.clone()); index.increment(); Ok(secret_key) } @@ -320,11 +499,12 @@ pub fn next_internal<D>( source: &D, account: &D::Account, index: &mut D::Index, -) -> Result<D::SecretKey, D::Error> +) -> Result<SecretKey<D>, D::Error> where D: DerivedSecretKeyGenerator + ?Sized, { - let secret_key = source.generate_internal_key(account, index)?; + let secret_key = + SecretKey::new_internal(source.generate_internal_key(account, index)?, index.clone()); index.increment(); Ok(secret_key) } @@ -368,14 +548,14 @@ where /// Generates an external secret key according to the [`DerivedSecretKeyGenerator`] protocol /// and increments the running `self.index`. #[inline] - fn generate_external_key(&mut self) -> Result<D::SecretKey, D::Error> { + fn generate_external_key(&mut self) -> Result<SecretKey<D>, D::Error> { next_external(self.derived_key_generator, self.account, &mut self.index) } /// Generates an internal secret key according to the [`DerivedSecretKeyGenerator`] protocol /// and increments the running `self.index`. #[inline] - fn generate_internal_key(&mut self) -> Result<D::SecretKey, D::Error> { + fn generate_internal_key(&mut self) -> Result<SecretKey<D>, D::Error> { next_internal(self.derived_key_generator, self.account, &mut self.index) } } @@ -406,12 +586,12 @@ impl<'d, D> SecretKeyGenerator for ExternalKeys<'d, D> where D: DerivedSecretKeyGenerator + ?Sized, { - type SecretKey = D::SecretKey; + type SecretKey = SecretKey<D>; type Error = D::Error; #[inline] - fn generate_key(&mut self) -> Result<Self::SecretKey, Self::Error> { + fn next_key(&mut self) -> Result<Self::SecretKey, Self::Error> { self.0.generate_external_key() } } @@ -420,11 +600,11 @@ impl<'d, D> Iterator for ExternalKeys<'d, D> where D: DerivedSecretKeyGenerator + ?Sized, { - type Item = D::SecretKey; + type Item = Result<SecretKey<D>, D::Error>; #[inline] fn next(&mut self) -> Option<Self::Item> { - self.generate_key().ok() + Some(self.next_key()) } } @@ -454,12 +634,12 @@ impl<'d, D> SecretKeyGenerator for InternalKeys<'d, D> where D: DerivedSecretKeyGenerator + ?Sized, { - type SecretKey = D::SecretKey; + type SecretKey = SecretKey<D>; type Error = D::Error; #[inline] - fn generate_key(&mut self) -> Result<Self::SecretKey, Self::Error> { + fn next_key(&mut self) -> Result<Self::SecretKey, Self::Error> { self.0.generate_internal_key() } } @@ -468,11 +648,11 @@ impl<'d, D> Iterator for InternalKeys<'d, D> where D: DerivedSecretKeyGenerator + ?Sized, { - type Item = D::SecretKey; + type Item = Result<SecretKey<D>, D::Error>; #[inline] fn next(&mut self) -> Option<Self::Item> { - self.generate_key().ok() + Some(self.next_key()) } } @@ -544,7 +724,7 @@ where /// Generates a new key of the given `kind` for this account. #[inline] - pub fn key(&self, source: &D, kind: KeyKind) -> Result<D::SecretKey, D::Error> { + pub fn key(&self, source: &D, kind: KeyKind) -> Result<SecretKey<D>, D::Error> { match kind { KeyKind::External => self.external_key(source), KeyKind::Internal => self.internal_key(source), @@ -553,20 +733,26 @@ where /// Generates a new external key for this account. #[inline] - pub fn external_key(&self, source: &D) -> Result<D::SecretKey, D::Error> { - source.generate_external_key(&self.account, &self.external_index) + pub fn external_key(&self, source: &D) -> Result<SecretKey<D>, D::Error> { + Ok(SecretKey::new_external( + source.generate_external_key(&self.account, &self.external_index)?, + self.external_index.clone(), + )) } /// Generates a new internal key for this account. #[inline] - pub fn internal_key(&self, source: &D) -> Result<D::SecretKey, D::Error> { - source.generate_internal_key(&self.account, &self.internal_index) + pub fn internal_key(&self, source: &D) -> Result<SecretKey<D>, D::Error> { + Ok(SecretKey::new_internal( + source.generate_internal_key(&self.account, &self.internal_index)?, + self.internal_index.clone(), + )) } /// Generates the next key of the given `kind` for this account, incrementing the /// appropriate index. #[inline] - pub fn next_key(&mut self, source: &D, kind: KeyKind) -> Result<D::SecretKey, D::Error> { + pub fn next_key(&mut self, source: &D, kind: KeyKind) -> Result<SecretKey<D>, D::Error> { match kind { KeyKind::External => self.next_external_key(source), KeyKind::Internal => self.next_internal_key(source), @@ -575,13 +761,13 @@ where /// Generates the next external key for this account, incrementing the `external_index`. #[inline] - pub fn next_external_key(&mut self, source: &D) -> Result<D::SecretKey, D::Error> { + pub fn next_external_key(&mut self, source: &D) -> Result<SecretKey<D>, D::Error> { next_external(source, &self.account, &mut self.external_index) } /// Generates the next internal key for this account, incrementing the `internal_index`. #[inline] - pub fn next_internal_key(&mut self, source: &D) -> Result<D::SecretKey, D::Error> { + pub fn next_internal_key(&mut self, source: &D) -> Result<SecretKey<D>, D::Error> { next_internal(source, &self.account, &mut self.internal_index) } diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet.rs index 0cf767c74..f6643b0db 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet.rs @@ -27,11 +27,8 @@ use crate::{ asset::{Asset, AssetBalance, AssetId}, fs::{Load, LoadWith, Save, SaveWith}, - identity::{ - self, AssetParameters, Identity, InternalReceiver, OpenSpend, PreSender, ShieldedIdentity, - Utxo, VoidNumber, - }, - keys::{Account, DerivedSecretKeyGenerator, KeyKind}, + identity::{self, AssetParameters, Identity, Utxo, VoidNumber}, + keys::{Account, DerivedSecretKeyGenerator, Index, KeyKind, KeyOwned}, transfer::{ self, canonical::{Mint, PrivateTransfer, Reclaim}, @@ -46,6 +43,18 @@ use rand::{ CryptoRng, RngCore, }; +/// Key-Owned Pre-Sender Type +pub type PreSender<D, C> = KeyOwned<D, identity::PreSender<C>>; + +/// Key-Owned Shielded Identity Type +pub type ShieldedIdentity<D, C, I> = KeyOwned<D, identity::ShieldedIdentity<C, I>>; + +/// Key-Owned Internal Receiver Type +pub type InternalReceiver<D, C, I> = KeyOwned<D, identity::InternalReceiver<C, I>>; + +/// Key-Owned Open Spend Type +pub type OpenSpend<D, C> = KeyOwned<D, identity::OpenSpend<C>>; + /// Secret Key Generation Error #[derive(derivative::Derivative)] #[derivative( @@ -122,48 +131,69 @@ where Self::with_account(this.secret_key_source.clone(), Account::next(&this.account)) } - /// Returns the identity for a key of the given `kind` and `index`. + /// Returns the identity for a key of the given `index`. #[inline] - pub fn get<C>(&self, kind: KeyKind, index: D::Index) -> Result<Identity<C>, D::Error> + pub fn get<C>(&self, index: &Index<D>) -> Result<Identity<C>, D::Error> where C: identity::Configuration<SecretKey = D::SecretKey>, { - self.secret_key_source - .generate_key(kind, self.account.as_ref(), &index) + index + .key(&self.secret_key_source, self.account.as_ref()) .map(Identity::new) } + /// Returns a [`PreSender`] for the key at the given `index`. + #[inline] + pub fn get_pre_sender<C>( + &self, + index: Index<D>, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + ) -> Result<PreSender<D, C>, D::Error> + where + C: identity::Configuration<SecretKey = D::SecretKey>, + Standard: Distribution<AssetParameters<C>>, + { + Ok(KeyOwned::new( + self.get(&index)?.into_pre_sender(commitment_scheme, asset), + index, + )) + } + /// Generates the next identity of the given `kind` for this signer. #[inline] - pub fn next_identity<C>(&mut self, kind: KeyKind) -> Result<Identity<C>, D::Error> + pub fn next_identity<C>(&mut self, kind: KeyKind) -> Result<KeyOwned<D, Identity<C>>, D::Error> where C: identity::Configuration<SecretKey = D::SecretKey>, { - self.account - .next_key(&self.secret_key_source, kind) - .map(Identity::new) + Ok(self + .account + .next_key(&self.secret_key_source, kind)? + .map(Identity::new)) } /// Generates the next external identity for this signer. #[inline] - pub fn next_external_identity<C>(&mut self) -> Result<Identity<C>, D::Error> + pub fn next_external_identity<C>(&mut self) -> Result<KeyOwned<D, Identity<C>>, D::Error> where C: identity::Configuration<SecretKey = D::SecretKey>, { - self.account - .next_external_key(&self.secret_key_source) - .map(Identity::new) + Ok(self + .account + .next_external_key(&self.secret_key_source)? + .map(Identity::new)) } /// Generates the next internal identity for this signer. #[inline] - pub fn next_internal_identity<C>(&mut self) -> Result<Identity<C>, D::Error> + pub fn next_internal_identity<C>(&mut self) -> Result<KeyOwned<D, Identity<C>>, D::Error> where C: identity::Configuration<SecretKey = D::SecretKey>, { - self.account - .next_internal_key(&self.secret_key_source) - .map(Identity::new) + Ok(self + .account + .next_internal_key(&self.secret_key_source)? + .map(Identity::new)) } /// Generates a new [`ShieldedIdentity`] to receive assets to this account via an external @@ -172,14 +202,15 @@ where pub fn next_shielded_identity<C, I>( &mut self, commitment_scheme: &C::CommitmentScheme, - ) -> Result<ShieldedIdentity<C, I>, D::Error> + ) -> Result<ShieldedIdentity<D, C, I>, D::Error> where C: identity::Configuration<SecretKey = D::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, Standard: Distribution<AssetParameters<C>>, { - self.next_external_identity() - .map(move |identity| identity.into_shielded(commitment_scheme)) + Ok(self + .next_external_identity()? + .map(move |identity| identity.into_shielded(commitment_scheme))) } /// Generates a new [`InternalReceiver`] to receive `asset` to this account via an @@ -190,7 +221,7 @@ where commitment_scheme: &C::CommitmentScheme, asset: Asset, rng: &mut R, - ) -> Result<InternalReceiver<C, I>, SecretKeyGenerationError<D, I::Error>> + ) -> Result<InternalReceiver<D, C, I>, SecretKeyGenerationError<D, I::Error>> where C: identity::Configuration<SecretKey = D::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, @@ -199,7 +230,7 @@ where { self.next_internal_identity() .map_err(SecretKeyGenerationError::SecretKeyError)? - .into_internal_receiver(commitment_scheme, asset, rng) + .map_ok(move |identity| identity.into_internal_receiver(commitment_scheme, asset, rng)) .map_err(SecretKeyGenerationError::Error) } @@ -211,7 +242,7 @@ where commitment_scheme: &C::CommitmentScheme, asset_id: AssetId, rng: &mut R, - ) -> Result<InternalReceiver<C, I>, SecretKeyGenerationError<D, I::Error>> + ) -> Result<InternalReceiver<D, C, I>, SecretKeyGenerationError<D, I::Error>> where C: identity::Configuration<SecretKey = D::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, @@ -449,87 +480,6 @@ where pub failure_index: Option<usize>, } -/// Key Index -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "Idx: Clone"), - Copy(bound = "Idx: Copy"), - Debug(bound = "Idx: Debug"), - Eq(bound = "Idx: Eq"), - Hash(bound = "Idx: Hash"), - PartialEq(bound = "Idx: PartialEq") -)] -pub struct Key<Idx> { - /// Key Kind - pub kind: KeyKind, - - /// Key Index - pub index: Idx, -} - -/// Key-Owned Value -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "Idx: Clone, T: Clone"), - Copy(bound = "Idx: Copy, T: Copy"), - Debug(bound = "Idx: Debug, T: Debug"), - Eq(bound = "Idx: Eq, T: Eq"), - Hash(bound = "Idx: Hash, T: Hash"), - PartialEq(bound = "Idx: PartialEq, T: PartialEq") -)] -pub struct KeyOwned<Idx, T> { - /// Key - pub key: Key<Idx>, - - /// Value Owned by the Key - pub value: T, -} - -/// Asset Key -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = "S::Index: Copy"), - Debug(bound = "S::Index: Debug"), - Eq(bound = "S::Index: Eq"), - Hash(bound = "S::Index: Hash"), - PartialEq(bound = "S::Index: PartialEq") -)] -pub struct AssetKey<S> -where - S: AssetMap + ?Sized, -{ - /// Key - pub key: Key<S::Index>, - - /// Value stored at this key - pub value: AssetBalance, -} - -impl<S> AssetKey<S> -where - S: AssetMap + ?Sized, -{ - /// Builds a [`PreSender`] for `self`, using `signer` to generate the secret key. - #[inline] - pub fn into_pre_sender<D, C>( - self, - signer: &Signer<D>, - commitment_scheme: &C::CommitmentScheme, - asset_id: AssetId, - ) -> Result<PreSender<C>, D::Error> - where - D: DerivedSecretKeyGenerator<Index = S::Index>, - C: identity::Configuration<SecretKey = D::SecretKey>, - Standard: Distribution<AssetParameters<C>>, - { - let key = self.key; // FIXME: We need to attach these to types. - Ok(signer - .get(key.kind, key.index)? - .into_pre_sender(commitment_scheme, Asset::new(asset_id, self.value))) - } -} - /// Asset Selection pub struct AssetSelection<S> where @@ -538,8 +488,8 @@ where /// Change Amount pub change: AssetBalance, - /// Asset Sender Keys - pub sender_keys: S::Keys, + /// Sender Assets + pub assets: S::Assets, } impl<S> AssetSelection<S> @@ -554,9 +504,10 @@ where asset_id: AssetId, commitment_scheme: &C::CommitmentScheme, rng: &mut R, - ) -> Result<InternalReceiver<C, I>, SecretKeyGenerationError<D, I::Error>> + ) -> Result<InternalReceiver<D, C, I>, SecretKeyGenerationError<D, I::Error>> where - D: DerivedSecretKeyGenerator<Index = S::Index>, + S: AssetMap<Key = Index<D>>, + D: DerivedSecretKeyGenerator, C: identity::Configuration<SecretKey = D::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, R: CryptoRng + RngCore + ?Sized, @@ -579,9 +530,10 @@ where n: usize, commitment_scheme: &C::CommitmentScheme, rng: &mut R, - ) -> Result<Vec<InternalReceiver<C, I>>, SecretKeyGenerationError<D, I::Error>> + ) -> Result<Vec<InternalReceiver<D, C, I>>, SecretKeyGenerationError<D, I::Error>> where - D: DerivedSecretKeyGenerator<Index = S::Index>, + S: AssetMap<Key = Index<D>>, + D: DerivedSecretKeyGenerator, C: identity::Configuration<SecretKey = D::SecretKey>, I: IntegratedEncryptionScheme<Plaintext = Asset>, R: CryptoRng + RngCore + ?Sized, @@ -599,27 +551,30 @@ where /// Asset Map pub trait AssetMap { - /// Key Index Type - type Index: Clone + Default; + /// Key Type + /// + /// Keys are used to access the underlying asset balances. See [`withdraw`](Self::withdraw) + /// and [`deposit`](Self::deposit) for uses of the [`Key`](Self::Key) type. + type Key; - /// Keys Iterator Type + /// Assets Iterator Type /// /// # Implementation Note /// /// See [`select`](Self::select) for properties of this iterator. - type Keys: IntoIterator<Item = AssetKey<Self>>; + type Assets: IntoIterator<Item = (Self::Key, AssetBalance)>; /// Error Type type Error; - /// Selects assets which total up to at least `asset` in value. + /// Selects asset keys which total up to at least `asset` in value. /// /// # Contract /// /// Implementations should always return an error whenever the `asset` cannot be covered - /// by a set of keys, i.e. when the call `self.contains(asset)` returns `false`. In any other - /// case, the iterator returned by the `Ok` branch of this method must always contain at least - /// one element, i.e. the following + /// by a set of keys, i.e. when the call [`self.contains(asset)`](Self::contains) returns + /// `false`. In any other case, the iterator returned by the `Ok` branch of this method must + /// always contain at least one element, i.e. the following /// /// ```text /// match self.select(asset) { @@ -631,28 +586,11 @@ pub trait AssetMap { /// should always return `true`. fn select(&self, asset: Asset) -> Result<AssetSelection<Self>, Self::Error>; - /// Withdraws the asset stored at `kind` and `index`. - fn withdraw_from(&mut self, kind: KeyKind, index: Self::Index) -> Result<(), Self::Error>; - /// Withdraws the asset stored at `key`. - #[inline] - fn withdraw(&mut self, key: Key<Self::Index>) -> Result<(), Self::Error> { - self.withdraw_from(key.kind, key.index) - } + fn withdraw(&mut self, key: Self::Key) -> Result<(), Self::Error>; /// Deposits `asset` at the key stored at `kind` and `index`. - fn deposit_to( - &mut self, - kind: KeyKind, - index: Self::Index, - asset: Asset, - ) -> Result<(), Self::Error>; - - /// Deposits `asset` at the key stored at `key`. - #[inline] - fn deposit(&mut self, key: Key<Self::Index>, asset: Asset) -> Result<(), Self::Error> { - self.deposit_to(key.kind, key.index, asset) - } + fn deposit(&mut self, key: Self::Key, asset: Asset) -> Result<(), Self::Error>; /// Returns the current balance associated with this `id`. fn balance(&self, id: AssetId) -> AssetBalance; @@ -887,7 +825,7 @@ where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, L: LocalLedger<C>, - M: AssetMap<Index = D::Index>, + M: AssetMap<Key = Index<D>>, { /// Wallet Signer signer: Signer<D>, @@ -901,7 +839,7 @@ where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, L: LocalLedger<C>, - M: AssetMap<Index = D::Index>, + M: AssetMap<Key = Index<D>>, { /// Builds a new [`Wallet`] for `signer`. #[inline] @@ -929,20 +867,18 @@ where commitment_scheme: &C::CommitmentScheme, asset: Asset, rng: &mut R, - ) -> Result<(Mint<C>, OpenSpend<C>), MintError<D, C>> + ) -> Result<(Mint<C>, OpenSpend<D, C>), MintError<D, C>> where R: CryptoRng + RngCore + ?Sized, Standard: Distribution<AssetParameters<C>>, { - Mint::from_identity( - self.signer - .next_internal_identity() - .map_err(MintError::SecretKeyError)?, - commitment_scheme, - asset, - rng, - ) - .map_err(MintError::EncryptionError) + Ok(self + .signer + .next_internal_identity() + .map_err(MintError::SecretKeyError)? + .map_ok(move |identity| Mint::from_identity(identity, commitment_scheme, asset, rng)) + .map_err(MintError::EncryptionError)? + .right()) } /// Builds [`PrivateTransfer`] transactions to send `asset` to an `external_receiver`. @@ -970,24 +906,27 @@ where .ok()?; let mut pre_senders = selection - .sender_keys + .assets .into_iter() - .map(|k| k.into_pre_sender(&self.signer, commitment_scheme, asset.id)) + .map(|(index, value)| { + self.signer + .get_pre_sender(index, commitment_scheme, Asset::new(asset.id, value)) + }) .collect::<Result<Vec<_>, _>>() .ok()?; - let external_receiver = external_receiver.into_receiver(commitment_scheme, asset, rng); - let mint = if pre_senders.len() % 2 == 1 { let (mint, open_spend) = self .mint(commitment_scheme, Asset::zero(asset.id), rng) .ok()?; - pre_senders.push(open_spend.into_pre_sender(commitment_scheme)); + pre_senders.push(open_spend.map(move |os| os.into_pre_sender(commitment_scheme))); Some(mint) } else { None }; + let external_receiver = external_receiver.into_receiver(commitment_scheme, asset, rng); + /* TODO: for i in 0..(pre_senders.len() / 2) { PrivateTransfer::build( @@ -1022,7 +961,7 @@ where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, L: LocalLedger<C> + Default, - M: AssetMap<Index = D::Index> + Default, + M: AssetMap<Key = Index<D>> + Default, Signer<D>: Load, { type Path = <Signer<D> as Load>::Path; @@ -1045,7 +984,7 @@ where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, L: LocalLedger<C>, - M: AssetMap<Index = D::Index>, + M: AssetMap<Key = Index<D>>, Signer<D>: Save, { type Path = <Signer<D> as Save>::Path; From bba5b5bea3a862ac3b1ade034a7bf8aa8c158030 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sat, 2 Oct 2021 15:34:47 -0400 Subject: [PATCH 074/275] WIP: work on async interfaces between signer/state/ledger --- manta-accounting/src/keys.rs | 71 +- manta-accounting/src/wallet/ledger.rs | 106 +++ manta-accounting/src/wallet/mod.rs | 24 + .../src/{wallet.rs => wallet/signer.rs} | 794 ++++-------------- manta-accounting/src/wallet/state.rs | 421 ++++++++++ manta-pay/src/accounting/keys.rs | 2 - 6 files changed, 777 insertions(+), 641 deletions(-) create mode 100644 manta-accounting/src/wallet/ledger.rs create mode 100644 manta-accounting/src/wallet/mod.rs rename manta-accounting/src/{wallet.rs => wallet/signer.rs} (51%) create mode 100644 manta-accounting/src/wallet/state.rs diff --git a/manta-accounting/src/keys.rs b/manta-accounting/src/keys.rs index ae44e2cec..e974f050b 100644 --- a/manta-accounting/src/keys.rs +++ b/manta-accounting/src/keys.rs @@ -99,6 +99,7 @@ pub trait DerivedSecretKeyGenerator { self.generate_key(KeyKind::Internal, account, index) } + /* TODO[remove]: /// Builds a [`SecretKeyGenerator`] for external keys associated to `account`. #[inline] fn external_keys<'s>(&'s self, account: &'s Self::Account) -> ExternalKeys<'s, Self> { @@ -132,6 +133,7 @@ pub trait DerivedSecretKeyGenerator { ) -> InternalKeys<'s, Self> { InternalKeys::from_index(self, account, index) } + */ } impl<D> DerivedSecretKeyGenerator for &D @@ -211,7 +213,7 @@ impl KeyKind { )] pub struct Index<D> where - D: DerivedSecretKeyGenerator + ?Sized, + D: DerivedSecretKeyGenerator, { /// Key Kind pub kind: KeyKind, @@ -222,7 +224,7 @@ where impl<D> Index<D> where - D: DerivedSecretKeyGenerator + ?Sized, + D: DerivedSecretKeyGenerator, { /// Builds a new [`Index`] for `kind` and `index`. #[inline] @@ -254,6 +256,12 @@ where self.kind.is_internal() } + /// Increments the internal `index`. + #[inline] + pub fn increment(&mut self) { + self.index.increment() + } + /// Gets the underlying key for this `index` at the `account`. #[inline] pub fn key(&self, source: &D, account: &D::Account) -> Result<D::SecretKey, D::Error> { @@ -276,7 +284,7 @@ pub type SecretKey<D> = KeyOwned<D, <D as DerivedSecretKeyGenerator>::SecretKey> )] pub struct KeyOwned<D, T> where - D: DerivedSecretKeyGenerator + ?Sized, + D: DerivedSecretKeyGenerator, { /// Value Owned by the Key pub value: T, @@ -287,7 +295,7 @@ where impl<D, T> KeyOwned<D, T> where - D: DerivedSecretKeyGenerator + ?Sized, + D: DerivedSecretKeyGenerator, { /// Builds a new [`KeyOwned`] for `value` with `index` as the [`Index`]. #[inline] @@ -355,7 +363,7 @@ where impl<D, T> AsRef<T> for KeyOwned<D, T> where - D: DerivedSecretKeyGenerator + ?Sized, + D: DerivedSecretKeyGenerator, { #[inline] fn as_ref(&self) -> &T { @@ -365,7 +373,7 @@ where impl<D, T> AsMut<T> for KeyOwned<D, T> where - D: DerivedSecretKeyGenerator + ?Sized, + D: DerivedSecretKeyGenerator, { #[inline] fn as_mut(&mut self) -> &mut T { @@ -375,7 +383,7 @@ where impl<D, T> From<KeyOwned<D, T>> for (T, Index<D>) where - D: DerivedSecretKeyGenerator + ?Sized, + D: DerivedSecretKeyGenerator, { #[inline] fn from(key_owned: KeyOwned<D, T>) -> Self { @@ -385,7 +393,7 @@ where impl<D, L, R> KeyOwned<D, (L, R)> where - D: DerivedSecretKeyGenerator + ?Sized, + D: DerivedSecretKeyGenerator, { /// Factors the key index over the left value in the pair. #[inline] @@ -402,7 +410,7 @@ where impl<D, T> KeyOwned<D, Option<T>> where - D: DerivedSecretKeyGenerator + ?Sized, + D: DerivedSecretKeyGenerator, { /// Converts a `KeyOwned<D, Option<T>>` into an `Option<KeyOwned<D, T>>`. #[inline] @@ -413,7 +421,7 @@ where impl<D, T> From<KeyOwned<D, Option<T>>> for Option<KeyOwned<D, T>> where - D: DerivedSecretKeyGenerator + ?Sized, + D: DerivedSecretKeyGenerator, { #[inline] fn from(key_owned: KeyOwned<D, Option<T>>) -> Self { @@ -423,7 +431,7 @@ where impl<D, T, E> KeyOwned<D, Result<T, E>> where - D: DerivedSecretKeyGenerator + ?Sized, + D: DerivedSecretKeyGenerator, { /// Converts a `KeyOwned<D, Result<T, E>>` into an `Result<KeyOwned<D, T>, E>`. #[inline] @@ -434,7 +442,7 @@ where impl<D, T, E> From<KeyOwned<D, Result<T, E>>> for Result<KeyOwned<D, T>, E> where - D: DerivedSecretKeyGenerator + ?Sized, + D: DerivedSecretKeyGenerator, { #[inline] fn from(key_owned: KeyOwned<D, Result<T, E>>) -> Self { @@ -444,7 +452,7 @@ where impl<D, T, E> TryFrom<KeyOwned<D, Result<T, E>>> for KeyOwned<D, T> where - D: DerivedSecretKeyGenerator + ?Sized, + D: DerivedSecretKeyGenerator, { type Error = E; @@ -464,7 +472,7 @@ pub fn next_key<D>( index: &mut D::Index, ) -> Result<SecretKey<D>, D::Error> where - D: DerivedSecretKeyGenerator + ?Sized, + D: DerivedSecretKeyGenerator, { let secret_key = SecretKey::with_kind( source.generate_key(kind, account, index)?, @@ -484,7 +492,7 @@ pub fn next_external<D>( index: &mut D::Index, ) -> Result<SecretKey<D>, D::Error> where - D: DerivedSecretKeyGenerator + ?Sized, + D: DerivedSecretKeyGenerator, { let secret_key = SecretKey::new_external(source.generate_external_key(account, index)?, index.clone()); @@ -501,7 +509,7 @@ pub fn next_internal<D>( index: &mut D::Index, ) -> Result<SecretKey<D>, D::Error> where - D: DerivedSecretKeyGenerator + ?Sized, + D: DerivedSecretKeyGenerator, { let secret_key = SecretKey::new_internal(source.generate_internal_key(account, index)?, index.clone()); @@ -512,7 +520,7 @@ where /// Keys struct Keys<'d, D> where - D: DerivedSecretKeyGenerator + ?Sized, + D: DerivedSecretKeyGenerator, { /// Derived Key Generator derived_key_generator: &'d D, @@ -526,7 +534,7 @@ where impl<'d, D> Keys<'d, D> where - D: DerivedSecretKeyGenerator + ?Sized, + D: DerivedSecretKeyGenerator, { /// Builds a new [`Keys`] generator from a [`DerivedSecretKeyGenerator`] and an `account`. #[inline] @@ -563,11 +571,11 @@ where /// External Keys pub struct ExternalKeys<'d, D>(Keys<'d, D>) where - D: DerivedSecretKeyGenerator + ?Sized; + D: DerivedSecretKeyGenerator; impl<'d, D> ExternalKeys<'d, D> where - D: DerivedSecretKeyGenerator + ?Sized, + D: DerivedSecretKeyGenerator, { /// Builds a new [`ExternalKeys`] generator for `account` from a `source`. #[inline] @@ -584,7 +592,7 @@ where impl<'d, D> SecretKeyGenerator for ExternalKeys<'d, D> where - D: DerivedSecretKeyGenerator + ?Sized, + D: DerivedSecretKeyGenerator, { type SecretKey = SecretKey<D>; @@ -598,7 +606,7 @@ where impl<'d, D> Iterator for ExternalKeys<'d, D> where - D: DerivedSecretKeyGenerator + ?Sized, + D: DerivedSecretKeyGenerator, { type Item = Result<SecretKey<D>, D::Error>; @@ -611,11 +619,11 @@ where /// Internal Keys pub struct InternalKeys<'d, D>(Keys<'d, D>) where - D: DerivedSecretKeyGenerator + ?Sized; + D: DerivedSecretKeyGenerator; impl<'d, D> InternalKeys<'d, D> where - D: DerivedSecretKeyGenerator + ?Sized, + D: DerivedSecretKeyGenerator, { /// Builds a new [`InternalKeys`] generator for `account` from a `source`. #[inline] @@ -632,7 +640,7 @@ where impl<'d, D> SecretKeyGenerator for InternalKeys<'d, D> where - D: DerivedSecretKeyGenerator + ?Sized, + D: DerivedSecretKeyGenerator, { type SecretKey = SecretKey<D>; @@ -646,7 +654,7 @@ where impl<'d, D> Iterator for InternalKeys<'d, D> where - D: DerivedSecretKeyGenerator + ?Sized, + D: DerivedSecretKeyGenerator, { type Item = Result<SecretKey<D>, D::Error>; @@ -706,12 +714,11 @@ where } } - /// Returns the next [`Account`] after `this`. + /// Returns the next [`Account`] after `self`. #[inline] - pub fn next(this: &Self) -> Self { - let mut next_account = this.account.clone(); - next_account.increment(); - Self::new(next_account) + pub fn next(mut self) -> Self { + self.account.increment(); + Self::new(self.account) } /// Resets the external and internal running indices to their default values. @@ -771,6 +778,7 @@ where next_internal(source, &self.account, &mut self.internal_index) } + /* TODO[remove]: /// Returns an [`ExternalKeys`] generator starting from the current external index. #[inline] pub fn external_keys<'s>(&'s self, source: &'s D) -> ExternalKeys<'s, D> { @@ -802,6 +810,7 @@ where ) -> InternalKeys<'s, D> { source.internal_keys_from_index(&self.account, index) } + */ } impl<D> AsRef<D::Account> for Account<D> diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs new file mode 100644 index 000000000..6b35fe150 --- /dev/null +++ b/manta-accounting/src/wallet/ledger.rs @@ -0,0 +1,106 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Ledger Source + +use crate::{ + identity::{Utxo, VoidNumber}, + transfer::{self, EncryptedAsset, TransferPost}, +}; +use alloc::{vec, vec::Vec}; +use core::future::Future; + +/// Ledger Source Connection +pub trait Connection<C> +where + C: transfer::Configuration, +{ + /// Sync Future Type + /// + /// Future for the [`sync`](Self::sync) method. + type SyncFuture: Future<Output = Result<SyncResponse<C, Self>, Self::Error>>; + + /// Send Future Type + /// + /// Future for the [`send`](Self::send) method. + type SendFuture: Future<Output = Result<SendResponse<C, Self>, Self::Error>>; + + /// Ledger State Checkpoint Type + type Checkpoint: Default + Ord; + + /// Error Type + type Error; + + /// Pulls data from the ledger starting from `checkpoint`, returning the current + /// [`Checkpoint`](Self::Checkpoint). + fn sync(&self, checkpoint: &Self::Checkpoint) -> Self::SyncFuture; + + /// Pulls all of the data from the entire history of the ledger, returning the current + /// [`Checkpoint`](Self::Checkpoint). + #[inline] + fn sync_all(&self) -> Self::SyncFuture { + self.sync(&Default::default()) + } + + /// Sends `transfers` to the ledger, returning the current [`Checkpoint`](Self::Checkpoint) + /// and the status of the transfers. + fn send(&self, transfers: Vec<TransferPost<C>>) -> Self::SendFuture; + + /// Sends `transfer` to the ledger, returning the current [`Checkpoint`](Self::Checkpoint) + /// and the status of the transfer. + #[inline] + fn send_one(&self, transfer: TransferPost<C>) -> Self::SendFuture { + self.send(vec![transfer]) + } +} + +/// Ledger Source Sync Response +/// +/// This `struct` is created by the [`sync`](Connection::sync) method on [`Connection`]. +/// See its documentation for more. +pub struct SyncResponse<C, LC> +where + C: transfer::Configuration, + LC: Connection<C> + ?Sized, +{ + /// Current Ledger Checkpoint + pub checkpoint: LC::Checkpoint, + + /// New Void Numbers + pub void_numbers: Vec<VoidNumber<C>>, + + /// New UTXOS + pub utxos: Vec<Utxo<C>>, + + /// New Encrypted Assets + pub encrypted_assets: Vec<EncryptedAsset<C>>, +} + +/// Ledger Source Send Response +/// +/// This `struct` is created by the [`send`](Connection::send) method on [`Connection`]. +/// See its documentation for more. +pub struct SendResponse<C, LC> +where + C: transfer::Configuration, + LC: Connection<C> + ?Sized, +{ + /// Current Ledger Checkpoint + pub checkpoint: LC::Checkpoint, + + /// Transaction Failed at the Given Index + pub failure_index: Option<usize>, +} diff --git a/manta-accounting/src/wallet/mod.rs b/manta-accounting/src/wallet/mod.rs new file mode 100644 index 000000000..71db8716d --- /dev/null +++ b/manta-accounting/src/wallet/mod.rs @@ -0,0 +1,24 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Wallet Abstractions + +mod state; + +pub mod ledger; +pub mod signer; + +pub use state::*; diff --git a/manta-accounting/src/wallet.rs b/manta-accounting/src/wallet/signer.rs similarity index 51% rename from manta-accounting/src/wallet.rs rename to manta-accounting/src/wallet/signer.rs index f6643b0db..20aa72e73 100644 --- a/manta-accounting/src/wallet.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -14,29 +14,25 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Wallet Abstractions - -// TODO: Is recovery different than just building a fresh `Wallet` instance? -// TODO: Add query builder for encrypted asset search (internal/external, gap_limit, start_index) -// TODO: Merge `AssetMap` and `LocalLedger` into one container and have it query `LedgerSource` -// instead of `Wallet`. Then `Wallet` just has access to local ledger (also async). -// Have then a "light wallet" which is just `Wallet` and a "heavy wallet" where the -// local ledger and asset map are built-in to it. -// TODO: Have a good way to asynchronously connect `Signer` and `BalanceState`. +//! Wallet Signer use crate::{ - asset::{Asset, AssetBalance, AssetId}, + asset::{Asset, AssetId}, fs::{Load, LoadWith, Save, SaveWith}, - identity::{self, AssetParameters, Identity, Utxo, VoidNumber}, + identity::{self, AssetParameters, Identity}, keys::{Account, DerivedSecretKeyGenerator, Index, KeyKind, KeyOwned}, transfer::{ self, canonical::{Mint, PrivateTransfer, Reclaim}, - EncryptedAsset, IntegratedEncryptionSchemeError, ReceiverPost, SenderPost, TransferPost, + IntegratedEncryptionSchemeError, }, }; -use alloc::{vec, vec::Vec}; -use core::{fmt::Debug, future::Future, hash::Hash, marker::PhantomData}; +use alloc::vec::Vec; +use core::{ + fmt::Debug, + future::{self, Future, Ready}, + hash::Hash, +}; use manta_crypto::ies::IntegratedEncryptionScheme; use rand::{ distributions::{Distribution, Standard}, @@ -76,6 +72,51 @@ where Error(E), } +/// Signer Server +pub trait Server {} + +/// Signer Connection +pub trait Connection<D> +where + D: DerivedSecretKeyGenerator, +{ + /// + type SignFuture: Future<Output = Result<Response<D>, Self::Error>>; + + /// + type Error; + + /// + fn sign(&mut self, request: Request<D>) -> Self::SignFuture; +} + +/// +pub enum Request<D> +where + D: DerivedSecretKeyGenerator, +{ + /// + Mint(Asset), + + /// + PrivateTransfer(Asset, Vec<Index<D>>, ()), + + /// + Reclaim(Asset, Vec<Index<D>>), +} + +/// +pub struct Response<D> +where + D: DerivedSecretKeyGenerator, +{ + /// + pub owner: Index<D>, + + /// + pub transfers: Vec<()>, +} + /// Signer pub struct Signer<D> where @@ -122,13 +163,10 @@ where ) } - /// Returns the next [`Signer`] after `this`, incrementing the account number. + /// Returns the next [`Signer`] after `self`, incrementing the account number. #[inline] - pub fn next(this: &Self) -> Self - where - D: Clone, - { - Self::with_account(this.secret_key_source.clone(), Account::next(&this.account)) + pub fn next(self) -> Self { + Self::with_account(self.secret_key_source, self.account.next()) } /// Returns the identity for a key of the given `index`. @@ -252,6 +290,106 @@ where self.next_internal_receiver(commitment_scheme, Asset::zero(asset_id), rng) } + /// Builds a [`Mint`] transaction to mint `asset` and returns the [`OpenSpend`] for that asset. + #[inline] + pub fn mint<C, R>( + &mut self, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + rng: &mut R, + ) -> Result<(Mint<C>, OpenSpend<D, C>), MintError<D, C>> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + R: CryptoRng + RngCore + ?Sized, + Standard: Distribution<AssetParameters<C>>, + { + Ok(self + .next_internal_identity() + .map_err(MintError::SecretKeyError)? + .map_ok(move |identity| Mint::from_identity(identity, commitment_scheme, asset, rng)) + .map_err(MintError::EncryptionError)? + .right()) + } + + /// Builds [`PrivateTransfer`] transactions to send `asset` to an `external_receiver`. + #[inline] + pub fn private_transfer<C, R>( + &mut self, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + external_receiver: transfer::ShieldedIdentity<C>, + rng: &mut R, + ) -> Option<Vec<PrivateTransfer<C>>> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + R: CryptoRng + RngCore + ?Sized, + Standard: Distribution<AssetParameters<C>>, + { + /* TODO: + let selection = self.balance_state.asset_map.select(asset).ok()?; + + let change_receiver = selection + .change_receiver::<_, _, C::IntegratedEncryptionScheme, _>( + &mut self.signer, + asset.id, + commitment_scheme, + rng, + ) + .ok()?; + + let mut pre_senders = selection + .assets + .into_iter() + .map(|(index, value)| { + self.signer + .get_pre_sender(index, commitment_scheme, Asset::new(asset.id, value)) + }) + .collect::<Result<Vec<_>, _>>() + .ok()?; + + let mint = if pre_senders.len() % 2 == 1 { + let (mint, open_spend) = self + .signer + .mint(commitment_scheme, Asset::zero(asset.id), rng) + .ok()?; + pre_senders.push(open_spend.map(move |os| os.into_pre_sender(commitment_scheme))); + Some(mint) + } else { + None + }; + */ + + let external_receiver = external_receiver.into_receiver(commitment_scheme, asset, rng); + + /* TODO: + for i in 0..(pre_senders.len() / 2) { + PrivateTransfer::build( + [pre_senders[2 * i], pre_senders[2 * i + 1]], + [?, ?] + ) + } + */ + + todo!() + } + + /// Builds a [`Reclaim`] transaction. + #[inline] + pub fn reclaim<C, R>( + &self, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + rng: &mut R, + ) -> Option<Reclaim<C>> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + R: CryptoRng + RngCore + ?Sized, + { + let _ = (commitment_scheme, asset, rng); + // TODO: Reclaim::build(senders, receiver, reclaim); + todo!() + } + /* TODO: Revisit how this is designed (this is part of recovery/ledger-sync): /// Returns an [`ExternalKeys`] generator starting from the given `index`. @@ -398,616 +536,56 @@ where } } -/// Ledger Source -pub trait LedgerSource<C> -where - C: transfer::Configuration, -{ - /// Sync Future Type - /// - /// Future for the [`sync`](Self::sync) method. - type SyncFuture: Future<Output = Result<SyncResponse<C, Self>, Self::Error>>; - - /// Send Future Type - /// - /// Future for the [`send`](Self::send) method. - type SendFuture: Future<Output = Result<SendResponse<C, Self>, Self::Error>>; - - /// Ledger State Checkpoint Type - type Checkpoint: Default + Ord; - - /// Error Type - type Error; - - /// Pulls data from the ledger starting from `checkpoint`, returning the current - /// [`Checkpoint`](Self::Checkpoint). - fn sync(&self, checkpoint: &Self::Checkpoint) -> Self::SyncFuture; - - /// Pulls all of the data from the entire history of the ledger, returning the current - /// [`Checkpoint`](Self::Checkpoint). - #[inline] - fn sync_all(&self) -> Self::SyncFuture { - self.sync(&Default::default()) - } - - /// Sends `transfers` to the ledger, returning the current [`Checkpoint`](Self::Checkpoint) - /// and the status of the transfers. - fn send(&self, transfers: Vec<TransferPost<C>>) -> Self::SendFuture; - - /// Sends `transfer` to the ledger, returning the current [`Checkpoint`](Self::Checkpoint) - /// and the status of the transfer. - #[inline] - fn send_one(&self, transfer: TransferPost<C>) -> Self::SendFuture { - self.send(vec![transfer]) - } -} - -/// Ledger Source Sync Response -/// -/// This `struct` is created by the [`sync`](LedgerSource::sync) method on [`LedgerSource`]. -/// See its documentation for more. -pub struct SyncResponse<C, LS> -where - C: transfer::Configuration, - LS: LedgerSource<C> + ?Sized, -{ - /// Current Ledger Checkpoint - pub checkpoint: LS::Checkpoint, - - /// New Void Numbers - pub void_numbers: Vec<VoidNumber<C>>, - - /// New UTXOS - pub utxos: Vec<Utxo<C>>, - - /// New Encrypted Assets - pub encrypted_assets: Vec<EncryptedAsset<C>>, -} - -/// Ledger Source Send Response -/// -/// This `struct` is created by the [`send`](LedgerSource::send) method on [`LedgerSource`]. -/// See its documentation for more. -pub struct SendResponse<C, LS> -where - C: transfer::Configuration, - LS: LedgerSource<C> + ?Sized, -{ - /// Current Ledger Checkpoint - pub checkpoint: LS::Checkpoint, - - /// Transaction Failed at the Given Index - pub failure_index: Option<usize>, -} - -/// Asset Selection -pub struct AssetSelection<S> -where - S: AssetMap + ?Sized, -{ - /// Change Amount - pub change: AssetBalance, - - /// Sender Assets - pub assets: S::Assets, -} - -impl<S> AssetSelection<S> -where - S: AssetMap + ?Sized, -{ - /// Builds an [`InternalReceiver`] to capture the `change` from the given [`AssetSelection`]. - #[inline] - pub fn change_receiver<D, C, I, R>( - &self, - signer: &mut Signer<D>, - asset_id: AssetId, - commitment_scheme: &C::CommitmentScheme, - rng: &mut R, - ) -> Result<InternalReceiver<D, C, I>, SecretKeyGenerationError<D, I::Error>> - where - S: AssetMap<Key = Index<D>>, - D: DerivedSecretKeyGenerator, - C: identity::Configuration<SecretKey = D::SecretKey>, - I: IntegratedEncryptionScheme<Plaintext = Asset>, - R: CryptoRng + RngCore + ?Sized, - Standard: Distribution<AssetParameters<C>>, - { - signer.next_internal_receiver(commitment_scheme, Asset::new(asset_id, self.change), rng) - } - - /// Builds a vector of `n` internal receivers to capture the `change` from the given - /// [`AssetSelection`]. - /// - /// # Panics - /// - /// This method panics if `n == 0`. - #[inline] - pub fn change_receivers<D, C, I, R>( - &self, - signer: &mut Signer<D>, - asset_id: AssetId, - n: usize, - commitment_scheme: &C::CommitmentScheme, - rng: &mut R, - ) -> Result<Vec<InternalReceiver<D, C, I>>, SecretKeyGenerationError<D, I::Error>> - where - S: AssetMap<Key = Index<D>>, - D: DerivedSecretKeyGenerator, - C: identity::Configuration<SecretKey = D::SecretKey>, - I: IntegratedEncryptionScheme<Plaintext = Asset>, - R: CryptoRng + RngCore + ?Sized, - Standard: Distribution<AssetParameters<C>>, - { - self.change - .make_change(n) - .unwrap() - .map(move |value| { - signer.next_internal_receiver(commitment_scheme, Asset::new(asset_id, value), rng) - }) - .collect() - } -} - -/// Asset Map -pub trait AssetMap { - /// Key Type - /// - /// Keys are used to access the underlying asset balances. See [`withdraw`](Self::withdraw) - /// and [`deposit`](Self::deposit) for uses of the [`Key`](Self::Key) type. - type Key; - - /// Assets Iterator Type - /// - /// # Implementation Note - /// - /// See [`select`](Self::select) for properties of this iterator. - type Assets: IntoIterator<Item = (Self::Key, AssetBalance)>; - - /// Error Type - type Error; - - /// Selects asset keys which total up to at least `asset` in value. - /// - /// # Contract - /// - /// Implementations should always return an error whenever the `asset` cannot be covered - /// by a set of keys, i.e. when the call [`self.contains(asset)`](Self::contains) returns - /// `false`. In any other case, the iterator returned by the `Ok` branch of this method must - /// always contain at least one element, i.e. the following - /// - /// ```text - /// match self.select(asset) { - /// Ok(selection) => selection.keys.into_iter().next().is_some(), - /// _ => true, - /// } - /// ``` - /// - /// should always return `true`. - fn select(&self, asset: Asset) -> Result<AssetSelection<Self>, Self::Error>; - - /// Withdraws the asset stored at `key`. - fn withdraw(&mut self, key: Self::Key) -> Result<(), Self::Error>; - - /// Deposits `asset` at the key stored at `kind` and `index`. - fn deposit(&mut self, key: Self::Key, asset: Asset) -> Result<(), Self::Error>; - - /// Returns the current balance associated with this `id`. - fn balance(&self, id: AssetId) -> AssetBalance; - - /// Returns true if `self` contains at least `asset.value` of the asset of kind `asset.id`. - #[inline] - fn contains(&self, asset: Asset) -> bool { - self.balance(asset.id) >= asset.value - } -} - -/// Ledger Ownership Marker -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum Ownership { - /// Key Ownership - Key(KeyKind), - - /// Unknown Self-Ownership - Unknown, - - /// Ownership by Others - Other, -} - -impl Default for Ownership { - #[inline] - fn default() -> Self { - Self::Unknown - } -} - -/// Ledger Data -pub enum LedgerData<C> -where - C: transfer::Configuration, -{ - /// Sender Data - Sender(VoidNumber<C>), - - /// Receiver Data - Receiver(Utxo<C>, EncryptedAsset<C>), -} - -/// Ledger Post Entry -pub enum PostEntry<'t, C> -where - C: transfer::Configuration, -{ - /// Sender Entry - Sender(&'t SenderPost<C>), - - /// Receiver Entry - Receiver(&'t ReceiverPost<C>), -} - -/// Local Ledger -pub trait LocalLedger<C> -where - C: transfer::Configuration, -{ - /// Ledger State Checkpoint Type - type Checkpoint: Default + Ord; - - /// Data Preservation Key - type Key; - - /// Data Access Error - type Error; - - /// Returns the checkpoint of the local ledger. - fn checkpoint(&self) -> &Self::Checkpoint; - - /// Sets the checkpoint of the local ledger to `checkpoint`. - fn set_checkpoint(&mut self, checkpoint: Self::Checkpoint); - - /// Saves `data` into the local ledger. - fn push(&mut self, data: LedgerData<C>) -> Result<(), Self::Error>; - - /// Prepares a `post` in the local ledger, returning a key to decide if the post should - /// be preserved by the ledger or not. - fn prepare(&mut self, post: PostEntry<C>, ownership: Ownership) -> Self::Key; - - /// Preserves the data associated to the `key`. - fn keep(&mut self, key: Self::Key); - - /// Drops the data associated to the `key`. - fn drop(&mut self, key: Self::Key); -} - -/// Wallet Balance State -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "M: Clone, L: Clone"), - Copy(bound = "M: Copy, L: Copy"), - Debug(bound = "M: Debug, L: Debug"), - Default(bound = "M: Default, L: Default"), - Eq(bound = "M: Eq, L: Eq"), - Hash(bound = "M: Hash, L: Hash"), - PartialEq(bound = "M: PartialEq, L: PartialEq") -)] -pub struct BalanceState<C, L, M> -where - C: transfer::Configuration, - L: LocalLedger<C>, - M: AssetMap, -{ - /// Local Ledger - ledger: L, - - /// Asset Map - asset_map: M, - - /// Type Parameter Marker - __: PhantomData<C>, -} - -impl<C, L, M> BalanceState<C, L, M> -where - C: transfer::Configuration, - L: LocalLedger<C>, - M: AssetMap, -{ - /// Returns the [`Checkpoint`](LocalLedger::Checkpoint) associated with the local ledger. - #[inline] - pub fn checkpoint(&self) -> &L::Checkpoint { - self.ledger.checkpoint() - } - - /// Synchronizes `self` with the `ledger`. - #[inline] - pub async fn sync<LS>(&mut self, ledger: &LS) -> Result<(), LS::Error> - where - LS: LedgerSource<C, Checkpoint = L::Checkpoint> + ?Sized, - { - let SyncResponse { - checkpoint, - void_numbers, - utxos, - encrypted_assets, - } = ledger.sync(self.checkpoint()).await?; - - for void_number in void_numbers { - // TODO: Only keep the ones we care about. How do we alert the local ledger? We have to - // communicate with it when we try to send transactions to a ledger source. - // Do we care about keeping void numbers? Maybe only for recovery? - // - let _ = self.ledger.push(LedgerData::Sender(void_number)); - } - - for (utxo, encrypted_asset) in utxos.into_iter().zip(encrypted_assets) { - // TODO: Only keep the ones we care about. For internal transactions we know the `utxo` - // ahead of time. For external ones, we have to get the `asset` first. - // - // TODO: Decrypt them on the way in ... threads? For internal transactions we know the - // `encrypted_asset` ahead of time, and we know it decrypted too. For external - // transactions we have to keep all of them since we have to try and decrypt all - // of them. - // - let _ = self - .ledger - .push(LedgerData::Receiver(utxo, encrypted_asset)); - } - - self.ledger.set_checkpoint(checkpoint); - - // FIXME: Deposit into `asset_map`. - - Ok(()) - } - - /// Sends the `transfers` to the `ledger`. - #[inline] - pub async fn send<LS>( - &mut self, - transfers: Vec<TransferPost<C>>, - ledger: &LS, - ) -> Result<(), LS::Error> - where - LS: LedgerSource<C, Checkpoint = L::Checkpoint> + ?Sized, - { - // FIXME: How do to batched transfers? Those above are not necessarily "batched-atomic". - // - // TODO: When sending to the ledger, we really have to set up a backup state in case we - // missed the "send window", and we need to recover. We can also hint to the local - // ledger that we are about to send some transactions and so it should know which - // `utxo`s and such are important to keep when it sees them appear later in the real - // ledger state. - // - // Sender Posts: `void_number` - // Receiver Posts: `utxo`, `encrypted_asset` - // - - let mut keys = Vec::new(); - - for transfer in &transfers { - for sender in &transfer.sender_posts { - keys.push( - self.ledger - .prepare(PostEntry::Sender(sender), Default::default()), - ); - } - for receiver in &transfer.receiver_posts { - keys.push( - self.ledger - .prepare(PostEntry::Receiver(receiver), Default::default()), - ); - } - } - - keys.shrink_to_fit(); - - let SendResponse { - checkpoint, - failure_index, - } = ledger.send(transfers).await?; - - // TODO: Revoke keys if the transfer failed, otherwise recover from the failure. - - let _ = failure_index; - - self.ledger.set_checkpoint(checkpoint); - - // FIXME: Withdraw from `asset_map` - - todo!() - } -} - -/// Wallet -pub struct Wallet<D, C, L, M> +/// Full Signer +pub struct FullSigner<D, C, R> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, - L: LocalLedger<C>, - M: AssetMap<Key = Index<D>>, + R: CryptoRng + RngCore + ?Sized, { - /// Wallet Signer + /// Signer signer: Signer<D>, - /// Wallet Balance State - balance_state: BalanceState<C, L, M>, -} + /// Commitment Scheme + commitment_scheme: C::CommitmentScheme, -impl<D, C, L, M> Wallet<D, C, L, M> -where - D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, - L: LocalLedger<C>, - M: AssetMap<Key = Index<D>>, -{ - /// Builds a new [`Wallet`] for `signer`. - #[inline] - pub fn new(signer: Signer<D>) -> Self - where - L: Default, - M: Default, - { - Self::with_balances(signer, Default::default()) - } - - /// Builds a new [`Wallet`] for `signer` with the given `balance_state`. - #[inline] - pub fn with_balances(signer: Signer<D>, balance_state: BalanceState<C, L, M>) -> Self { - Self { - signer, - balance_state, - } - } - - /// Builds a [`Mint`] transaction to mint `asset` and returns the [`OpenSpend`] for that asset. - #[inline] - pub fn mint<R>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - rng: &mut R, - ) -> Result<(Mint<C>, OpenSpend<D, C>), MintError<D, C>> - where - R: CryptoRng + RngCore + ?Sized, - Standard: Distribution<AssetParameters<C>>, - { - Ok(self - .signer - .next_internal_identity() - .map_err(MintError::SecretKeyError)? - .map_ok(move |identity| Mint::from_identity(identity, commitment_scheme, asset, rng)) - .map_err(MintError::EncryptionError)? - .right()) - } - - /// Builds [`PrivateTransfer`] transactions to send `asset` to an `external_receiver`. - #[inline] - pub fn private_transfer<R>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - external_receiver: transfer::ShieldedIdentity<C>, - rng: &mut R, - ) -> Option<Vec<PrivateTransfer<C>>> - where - R: CryptoRng + RngCore + ?Sized, - Standard: Distribution<AssetParameters<C>>, - { - let selection = self.balance_state.asset_map.select(asset).ok()?; - - let change_receiver = selection - .change_receiver::<_, _, C::IntegratedEncryptionScheme, _>( - &mut self.signer, - asset.id, - commitment_scheme, - rng, - ) - .ok()?; - - let mut pre_senders = selection - .assets - .into_iter() - .map(|(index, value)| { - self.signer - .get_pre_sender(index, commitment_scheme, Asset::new(asset.id, value)) - }) - .collect::<Result<Vec<_>, _>>() - .ok()?; - - let mint = if pre_senders.len() % 2 == 1 { - let (mint, open_spend) = self - .mint(commitment_scheme, Asset::zero(asset.id), rng) - .ok()?; - pre_senders.push(open_spend.map(move |os| os.into_pre_sender(commitment_scheme))); - Some(mint) - } else { - None - }; - - let external_receiver = external_receiver.into_receiver(commitment_scheme, asset, rng); - - /* TODO: - for i in 0..(pre_senders.len() / 2) { - PrivateTransfer::build( - [pre_senders[2 * i], pre_senders[2 * i + 1]], - [?, ?] - ) - } - */ - - todo!() - } - - /// Builds a [`Reclaim`] transaction. - #[inline] - pub fn reclaim<R>( - &self, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - rng: &mut R, - ) -> Option<Reclaim<C>> - where - R: CryptoRng + RngCore + ?Sized, - { - let _ = (commitment_scheme, asset, rng); - // TODO: Reclaim::build(senders, receiver, reclaim); - todo!() - } -} - -impl<D, C, L, M> Load for Wallet<D, C, L, M> -where - D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, - L: LocalLedger<C> + Default, - M: AssetMap<Key = Index<D>> + Default, - Signer<D>: Load, -{ - type Path = <Signer<D> as Load>::Path; - - type LoadingKey = <Signer<D> as Load>::LoadingKey; - - type Error = <Signer<D> as Load>::Error; - - #[inline] - fn load<P>(path: P, loading_key: &Self::LoadingKey) -> Result<Self, Self::Error> - where - P: AsRef<Self::Path>, - { - Ok(Self::new(Signer::load(path, loading_key)?)) - } + /// Random Number Generator + rng: R, } -impl<D, C, L, M> Save for Wallet<D, C, L, M> +impl<D, C, R> Connection<D> for FullSigner<D, C, R> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, - L: LocalLedger<C>, - M: AssetMap<Key = Index<D>>, - Signer<D>: Save, + R: CryptoRng + RngCore + ?Sized, { - type Path = <Signer<D> as Save>::Path; + type SignFuture = Ready<Result<Response<D>, Self::Error>>; - type SavingKey = <Signer<D> as Save>::SavingKey; - - type Error = <Signer<D> as Save>::Error; + type Error = (); #[inline] - fn save<P>(self, path: P, saving_key: &Self::SavingKey) -> Result<(), Self::Error> - where - P: AsRef<Self::Path>, - { - self.signer.save(path, saving_key) + fn sign(&mut self, request: Request<D>) -> Self::SignFuture { + future::ready(match request { + Request::Mint(asset) => { + // + todo!() + } + Request::PrivateTransfer(asset, senders, receiver) => { + // + todo!() + } + Request::Reclaim(asset, senders) => { + // + todo!() + } + }) } } /// Mint Error /// -/// This `enum` is the error state for the [`mint`] method on [`Wallet`]. +/// This `enum` is the error state for the [`mint`](Signer::mint) method on [`Signer`]. /// See its documentation for more. -/// -/// [`mint`]: Wallet::mint #[derive(derivative::Derivative)] #[derivative( Clone(bound = "D::Error: Clone, IntegratedEncryptionSchemeError<C>: Clone"), diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs new file mode 100644 index 000000000..d2594d658 --- /dev/null +++ b/manta-accounting/src/wallet/state.rs @@ -0,0 +1,421 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Wallet State + +use crate::{ + asset::{Asset, AssetBalance, AssetId}, + identity::{self, AssetParameters, Utxo, VoidNumber}, + keys::{DerivedSecretKeyGenerator, Index, KeyKind}, + transfer::{self, EncryptedAsset, ReceiverPost, SenderPost, TransferPost}, + wallet::{ + ledger::{self, SendResponse, SyncResponse}, + signer::{self, InternalReceiver, SecretKeyGenerationError, Signer}, + }, +}; +use alloc::vec::Vec; +use core::{fmt::Debug, hash::Hash, marker::PhantomData}; +use manta_crypto::ies::IntegratedEncryptionScheme; +use rand::{ + distributions::{Distribution, Standard}, + CryptoRng, RngCore, +}; + +/// Asset Map +pub trait AssetMap { + /// Key Type + /// + /// Keys are used to access the underlying asset balances. See [`withdraw`](Self::withdraw) + /// and [`deposit`](Self::deposit) for uses of the [`Key`](Self::Key) type. + type Key; + + /// Assets Iterator Type + /// + /// This type is returned by [`select`](Self::select) when looking for assets in the map. + type Assets: IntoIterator<Item = (Self::Key, AssetBalance)>; + + /// Error Type + type Error; + + /// Selects asset keys which total up to at least `asset` in value. + fn select(&self, asset: Asset) -> AssetSelection<Self>; + + /// Withdraws the asset stored at `key`. + fn withdraw(&mut self, key: Self::Key) -> Result<(), Self::Error>; + + /// Deposits `asset` at the key stored at `kind` and `index`. + fn deposit(&mut self, key: Self::Key, asset: Asset) -> Result<(), Self::Error>; + + /// Returns the current balance associated with this `id`. + fn balance(&self, id: AssetId) -> AssetBalance; + + /// Returns true if `self` contains at least `asset.value` of the asset of kind `asset.id`. + #[inline] + fn contains(&self, asset: Asset) -> bool { + self.balance(asset.id) >= asset.value + } +} + +/// Asset Selection +pub struct AssetSelection<S> +where + S: AssetMap + ?Sized, +{ + /// Change Amount + pub change: AssetBalance, + + /// Sender Assets + pub assets: S::Assets, +} + +impl<S> AssetSelection<S> +where + S: AssetMap + ?Sized, +{ + /// Builds an [`InternalReceiver`] to capture the `change` from the given [`AssetSelection`]. + #[inline] + pub fn change_receiver<D, C, I, R>( + &self, + signer: &mut Signer<D>, + asset_id: AssetId, + commitment_scheme: &C::CommitmentScheme, + rng: &mut R, + ) -> Result<InternalReceiver<D, C, I>, SecretKeyGenerationError<D, I::Error>> + where + S: AssetMap<Key = Index<D>>, + D: DerivedSecretKeyGenerator, + C: identity::Configuration<SecretKey = D::SecretKey>, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + R: CryptoRng + RngCore + ?Sized, + Standard: Distribution<AssetParameters<C>>, + { + signer.next_internal_receiver(commitment_scheme, Asset::new(asset_id, self.change), rng) + } + + /// Builds a vector of `n` internal receivers to capture the `change` from the given + /// [`AssetSelection`]. + /// + /// # Panics + /// + /// This method panics if `n == 0`. + #[inline] + pub fn change_receivers<D, C, I, R>( + &self, + signer: &mut Signer<D>, + asset_id: AssetId, + n: usize, + commitment_scheme: &C::CommitmentScheme, + rng: &mut R, + ) -> Result<Vec<InternalReceiver<D, C, I>>, SecretKeyGenerationError<D, I::Error>> + where + S: AssetMap<Key = Index<D>>, + D: DerivedSecretKeyGenerator, + C: identity::Configuration<SecretKey = D::SecretKey>, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + R: CryptoRng + RngCore + ?Sized, + Standard: Distribution<AssetParameters<C>>, + { + self.change + .make_change(n) + .unwrap() + .map(move |value| { + signer.next_internal_receiver(commitment_scheme, Asset::new(asset_id, value), rng) + }) + .collect() + } +} + +/// Ledger Ownership Marker +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum Ownership { + /// Key Ownership + Key(KeyKind), + + /// Unknown Self-Ownership + Unknown, + + /// Ownership by Others + Other, +} + +impl Default for Ownership { + #[inline] + fn default() -> Self { + Self::Unknown + } +} + +/// Ledger Data +pub enum LedgerData<C> +where + C: transfer::Configuration, +{ + /// Sender Data + Sender(VoidNumber<C>), + + /// Receiver Data + Receiver(Utxo<C>, EncryptedAsset<C>), +} + +/// Ledger Post Entry +pub enum PostEntry<'t, C> +where + C: transfer::Configuration, +{ + /// Sender Entry + Sender(&'t SenderPost<C>), + + /// Receiver Entry + Receiver(&'t ReceiverPost<C>), +} + +/// Local Ledger State +pub trait LocalLedger<C> +where + C: transfer::Configuration, +{ + /// Ledger State Checkpoint Type + type Checkpoint: Default + Ord; + + /// Data Preservation Key + type Key; + + /// Data Access Error + type Error; + + /// Returns the checkpoint of the local ledger. + fn checkpoint(&self) -> &Self::Checkpoint; + + /// Sets the checkpoint of the local ledger to `checkpoint`. + fn set_checkpoint(&mut self, checkpoint: Self::Checkpoint); + + /// Saves `data` into the local ledger. + fn push(&mut self, data: LedgerData<C>) -> Result<(), Self::Error>; + + /// Prepares a `post` in the local ledger, returning a key to decide if the post should + /// be preserved by the ledger or not. + fn prepare(&mut self, post: PostEntry<C>, ownership: Ownership) -> Self::Key; + + /// Preserves the data associated to the `key`. + fn keep(&mut self, key: Self::Key); + + /// Drops the data associated to the `key`. + fn drop(&mut self, key: Self::Key); +} + +/// Wallet Balance State +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "M: Clone, L: Clone"), + Copy(bound = "M: Copy, L: Copy"), + Debug(bound = "M: Debug, L: Debug"), + Default(bound = "M: Default, L: Default"), + Eq(bound = "M: Eq, L: Eq"), + Hash(bound = "M: Hash, L: Hash"), + PartialEq(bound = "M: PartialEq, L: PartialEq") +)] +pub struct BalanceState<C, L, M> +where + C: transfer::Configuration, + L: LocalLedger<C>, + M: AssetMap, +{ + /// Local Ledger + ledger: L, + + /// Asset Map + asset_map: M, + + /// Type Parameter Marker + __: PhantomData<C>, +} + +impl<C, L, M> BalanceState<C, L, M> +where + C: transfer::Configuration, + L: LocalLedger<C>, + M: AssetMap, +{ + /// Returns the current balance associated with this `id`. + #[inline] + pub fn balance(&self, id: AssetId) -> AssetBalance { + self.asset_map.balance(id) + } + + /// Returns true if `self` contains at least `asset.value` of the asset of kind `asset.id`. + #[inline] + pub fn contains(&self, asset: Asset) -> bool { + self.asset_map.contains(asset) + } + + /// Returns the [`Checkpoint`](LocalLedger::Checkpoint) associated with the local ledger. + #[inline] + pub fn checkpoint(&self) -> &L::Checkpoint { + self.ledger.checkpoint() + } + + /// + #[inline] + pub async fn sign<D, SC>( + &mut self, + signer: &mut SC, + request: signer::Request<D>, + ) -> Result<(), SC::Error> + where + D: DerivedSecretKeyGenerator<SecretKey = C::SecretKey>, + SC: signer::Connection<D>, + { + let signer::Response { owner, transfers } = signer.sign(request).await?; + + let _ = owner; + let _ = transfers; + + todo!() + } + + /// Synchronizes `self` with the `ledger`. + #[inline] + pub async fn sync<LC>(&mut self, ledger: &LC) -> Result<(), LC::Error> + where + LC: ledger::Connection<C, Checkpoint = L::Checkpoint>, + { + let SyncResponse { + checkpoint, + void_numbers, + utxos, + encrypted_assets, + } = ledger.sync(self.checkpoint()).await?; + + for void_number in void_numbers { + // TODO: Only keep the ones we care about. How do we alert the local ledger? We have to + // communicate with it when we try to send transactions to a ledger source. + // Do we care about keeping void numbers? Maybe only for recovery? + // + let _ = self.ledger.push(LedgerData::Sender(void_number)); + } + + for (utxo, encrypted_asset) in utxos.into_iter().zip(encrypted_assets) { + // TODO: Only keep the ones we care about. For internal transactions we know the `utxo` + // ahead of time. For external ones, we have to get the `asset` first. + // + // TODO: Decrypt them on the way in ... threads? For internal transactions we know the + // `encrypted_asset` ahead of time, and we know it decrypted too. For external + // transactions we have to keep all of them since we have to try and decrypt all + // of them. + // + let _ = self + .ledger + .push(LedgerData::Receiver(utxo, encrypted_asset)); + } + + self.ledger.set_checkpoint(checkpoint); + + // FIXME: Deposit into `asset_map`. + + Ok(()) + } + + /// Sends the `transfers` to the `ledger`. + #[inline] + pub async fn send_transfers<LC>( + &mut self, + transfers: Vec<TransferPost<C>>, + ledger: &LC, + ) -> Result<(), LC::Error> + where + LC: ledger::Connection<C, Checkpoint = L::Checkpoint>, + { + // FIXME: How do to batched transfers? Those above are not necessarily "batched-atomic". + // + // TODO: When sending to the ledger, we really have to set up a backup state in case we + // missed the "send window", and we need to recover. We can also hint to the local + // ledger that we are about to send some transactions and so it should know which + // `utxo`s and such are important to keep when it sees them appear later in the real + // ledger state. + // + // Sender Posts: `void_number` + // Receiver Posts: `utxo`, `encrypted_asset` + // + + let mut keys = Vec::new(); + + for transfer in &transfers { + for sender in &transfer.sender_posts { + keys.push( + self.ledger + .prepare(PostEntry::Sender(sender), Default::default()), + ); + } + for receiver in &transfer.receiver_posts { + keys.push( + self.ledger + .prepare(PostEntry::Receiver(receiver), Default::default()), + ); + } + } + + keys.shrink_to_fit(); + + let SendResponse { + checkpoint, + failure_index, + } = ledger.send(transfers).await?; + + // TODO: Revoke keys if the transfer failed, otherwise recover from the failure. + + let _ = failure_index; + + self.ledger.set_checkpoint(checkpoint); + + // FIXME: Withdraw from `asset_map` + + todo!() + } +} + +/// Full Wallet +pub struct Wallet<D, C, L, M, SC, LC> +where + D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, + L: LocalLedger<C>, + M: AssetMap, + SC: signer::Connection<D>, + LC: ledger::Connection<C>, +{ + /// Wallet Balance State + _state: BalanceState<C, L, M>, + + /// Signer Connection + _signer: SC, + + /// Ledger Connection + _ledger: LC, + + /// Type Parameter Marker + __: PhantomData<D>, +} + +impl<D, C, L, M, SC, LC> Wallet<D, C, L, M, SC, LC> +where + D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, + L: LocalLedger<C>, + M: AssetMap, + SC: signer::Connection<D>, + LC: ledger::Connection<C>, +{ +} diff --git a/manta-pay/src/accounting/keys.rs b/manta-pay/src/accounting/keys.rs index b73d9fa49..ba98ae871 100644 --- a/manta-pay/src/accounting/keys.rs +++ b/manta-pay/src/accounting/keys.rs @@ -150,8 +150,6 @@ pub type MantaDerivedKeySecret = DerivedKeySecret<Manta>; pub type CalamariDerivedKeySecret = DerivedKeySecret<Calamari>; /// Derived Key Secret -#[derive(derivative::Derivative)] -#[derivative(Clone, Copy)] pub struct DerivedKeySecret<C> where C: CoinType, From 773bea0910e9abf88f80f17844738f48d30414ef Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 3 Oct 2021 03:39:55 -0400 Subject: [PATCH 075/275] WIP: work on signer<->state APIs --- manta-accounting/src/wallet/signer.rs | 109 +++++++++++++++++++------- manta-accounting/src/wallet/state.rs | 10 +-- 2 files changed, 85 insertions(+), 34 deletions(-) diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 20aa72e73..45af70f25 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -24,7 +24,7 @@ use crate::{ transfer::{ self, canonical::{Mint, PrivateTransfer, Reclaim}, - IntegratedEncryptionSchemeError, + IntegratedEncryptionSchemeError, ProofSystemError, ProvingContext, TransferPost, }, }; use alloc::vec::Vec; @@ -72,49 +72,82 @@ where Error(E), } -/// Signer Server -pub trait Server {} - /// Signer Connection -pub trait Connection<D> +pub trait Connection<D, C> where D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, { + /// Sign Future Type /// - type SignFuture: Future<Output = Result<Response<D>, Self::Error>>; + /// Future for the [`sign`](Self::sign) method. + type SignFuture: Future<Output = Result<Response<D, C>, Error<D, C>>>; - /// - type Error; - - /// - fn sign(&mut self, request: Request<D>) -> Self::SignFuture; + /// Signs a transfer request and returns the ledger transfer posts if successful. + fn sign(&mut self, request: Request<D, C>) -> Self::SignFuture; } +/// Signer Connection Request /// -pub enum Request<D> +/// This `struct` is used by the [`sign`](Connection::sign) method on [`Connection`]. +/// See its documentation for more. +pub enum Request<D, C> where D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, { - /// + /// Mint Transaction Mint(Asset), - /// - PrivateTransfer(Asset, Vec<Index<D>>, ()), + /// Private Transfer Transaction + PrivateTransfer(Asset, Vec<Index<D>>, transfer::ShieldedIdentity<C>), - /// + /// Reclaim Transaction Reclaim(Asset, Vec<Index<D>>), } +/// Signer Connection Response /// -pub struct Response<D> +/// This `struct` is created by the [`sign`](Connection::sign) method on [`Connection`]. +/// See its documentation for more. +pub struct Response<D, C> where D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, { - /// + /// Final Owner Index pub owner: Index<D>, - /// - pub transfers: Vec<()>, + /// Transfer Posts + pub transfers: Vec<TransferPost<C>>, +} + +impl<D, C> Response<D, C> +where + D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, +{ + /// Builds a new [`Response`] from `owner` and `transfers`. + #[inline] + pub fn new(owner: Index<D>, transfers: Vec<TransferPost<C>>) -> Self { + Self { owner, transfers } + } +} + +/// Signer Connection Error +pub enum Error<D, C> +where + D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, +{ + /// Secret Key Generation Error + SecretKeyError(D::Error), + + /// Encryption Error + EncryptionError(IntegratedEncryptionSchemeError<C>), + + /// Proof System Error + ProofSystemError(ProofSystemError<C>), } /// Signer @@ -317,6 +350,7 @@ where &mut self, commitment_scheme: &C::CommitmentScheme, asset: Asset, + senders: Vec<Index<D>>, external_receiver: transfer::ShieldedIdentity<C>, rng: &mut R, ) -> Option<Vec<PrivateTransfer<C>>> @@ -549,27 +583,44 @@ where /// Commitment Scheme commitment_scheme: C::CommitmentScheme, + /// Proving Context + proving_context: ProvingContext<C>, + /// Random Number Generator rng: R, } -impl<D, C, R> Connection<D> for FullSigner<D, C, R> +impl<D, C, R> Connection<D, C> for FullSigner<D, C, R> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, + Standard: Distribution<AssetParameters<C>>, { - type SignFuture = Ready<Result<Response<D>, Self::Error>>; - - type Error = (); + type SignFuture = Ready<Result<Response<D, C>, Error<D, C>>>; #[inline] - fn sign(&mut self, request: Request<D>) -> Self::SignFuture { + fn sign(&mut self, request: Request<D, C>) -> Self::SignFuture { future::ready(match request { - Request::Mint(asset) => { - // - todo!() - } + Request::Mint(asset) => self + .signer + .mint::<C, _>(&self.commitment_scheme, asset, &mut self.rng) + .map_err(|e| match e { + MintError::SecretKeyError(err) => Error::SecretKeyError(err), + MintError::EncryptionError(err) => Error::EncryptionError(err), + }) + .and_then(|(mint, open_spend)| { + /* TODO: + mint.into_post( + &self.commitment_scheme, + &self.proving_context, + &mut self.rng, + ) + .map_err(Error::ProofSystemError) + .map(|p| Response::new(open_spend.index, vec![p])) + */ + todo!() + }), Request::PrivateTransfer(asset, senders, receiver) => { // todo!() diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index d2594d658..d88ed4204 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -272,11 +272,11 @@ where pub async fn sign<D, SC>( &mut self, signer: &mut SC, - request: signer::Request<D>, - ) -> Result<(), SC::Error> + request: signer::Request<D, C>, + ) -> Result<(), signer::Error<D, C>> where D: DerivedSecretKeyGenerator<SecretKey = C::SecretKey>, - SC: signer::Connection<D>, + SC: signer::Connection<D, C>, { let signer::Response { owner, transfers } = signer.sign(request).await?; @@ -393,7 +393,7 @@ where C: transfer::Configuration<SecretKey = D::SecretKey>, L: LocalLedger<C>, M: AssetMap, - SC: signer::Connection<D>, + SC: signer::Connection<D, C>, LC: ledger::Connection<C>, { /// Wallet Balance State @@ -415,7 +415,7 @@ where C: transfer::Configuration<SecretKey = D::SecretKey>, L: LocalLedger<C>, M: AssetMap, - SC: signer::Connection<D>, + SC: signer::Connection<D, C>, LC: ledger::Connection<C>, { } From 2f9b99bb0a46375083bfd7f5bfaf23e121fc1e69 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 5 Oct 2021 16:53:01 -0400 Subject: [PATCH 076/275] WIP: wallet <-> ledger APIs --- manta-accounting/src/asset.rs | 132 +++++++- manta-accounting/src/identity.rs | 5 +- manta-accounting/src/wallet/ledger.rs | 40 +-- manta-accounting/src/wallet/mod.rs | 5 +- manta-accounting/src/wallet/signer.rs | 431 +++++++++++++++++++------- manta-accounting/src/wallet/state.rs | 146 +-------- manta-crypto/src/merkle_tree/path.rs | 11 +- 7 files changed, 464 insertions(+), 306 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index eec86ddbb..48f0c4de4 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -20,6 +20,7 @@ // TODO: Implement all `rand` sampling traits. // TODO: Should we rename `AssetBalance` to `AssetValue` to be more consistent? // TODO: Implement `Concat` for `AssetId` and `AssetBalance`. +// TODO: Add implementations for `AssetMap` using key-value maps like `BTreeMap` and `HashMap` use alloc::vec::Vec; use core::{ @@ -60,10 +61,11 @@ impl AssetId { /// The size of this type in bytes. pub const SIZE: usize = (Self::BITS / 8) as usize; - /// Converts `self` into a byte array. + /// Constructs a new [`Asset`] with `self` as the [`AssetId`] and `value` as the + /// [`AssetBalance`]. #[inline] - pub const fn into_bytes(self) -> [u8; Self::SIZE] { - self.0.to_le_bytes() + pub const fn with(self, value: AssetBalance) -> Asset { + Asset::new(self, value) } /// Converts a byte array into `self`. @@ -71,6 +73,12 @@ impl AssetId { pub const fn from_bytes(bytes: [u8; Self::SIZE]) -> Self { Self(AssetIdType::from_le_bytes(bytes)) } + + /// Converts `self` into a byte array. + #[inline] + pub const fn into_bytes(self) -> [u8; Self::SIZE] { + self.0.to_le_bytes() + } } impl Distribution<AssetId> for Standard { @@ -127,10 +135,10 @@ impl AssetBalance { /// The size of this type in bytes. pub const SIZE: usize = (Self::BITS / 8) as usize; - /// Converts `self` into a byte array. + /// Constructs a new [`Asset`] with `self` as the [`AssetBalance`] and `id` as the [`AssetId`]. #[inline] - pub const fn into_bytes(self) -> [u8; Self::SIZE] { - self.0.to_le_bytes() + pub const fn with(self, id: AssetId) -> Asset { + Asset::new(id, self) } /// Converts a byte array into `self`. @@ -139,6 +147,12 @@ impl AssetBalance { Self(AssetBalanceType::from_le_bytes(bytes)) } + /// Converts `self` into a byte array. + #[inline] + pub const fn into_bytes(self) -> [u8; Self::SIZE] { + self.0.to_le_bytes() + } + /// Checked integer addition. Computes `self + rhs`, returning `None` if overflow occurred. #[inline] pub const fn checked_add(self, rhs: Self) -> Option<Self> { @@ -315,12 +329,6 @@ impl Asset { self.id.0 == rhs.id.0 } - /// Converts `self` into a byte array. - #[inline] - pub fn into_bytes(self) -> [u8; Self::SIZE] { - into_array_unchecked(self.accumulated::<Vec<_>>()) - } - /// Converts a byte array into `self`. #[inline] pub fn from_bytes(bytes: [u8; Self::SIZE]) -> Self { @@ -330,6 +338,12 @@ impl Asset { AssetBalance::from_bytes(into_array_unchecked(&bytes[split..])), ) } + + /// Converts `self` into a byte array. + #[inline] + pub fn into_bytes(self) -> [u8; Self::SIZE] { + into_array_unchecked(self.accumulated::<Vec<_>>()) + } } impl Add<AssetBalance> for Asset { @@ -529,7 +543,7 @@ impl<const N: usize> Default for AssetCollection<N> { impl<const N: usize> From<AssetCollection<N>> for [Asset; N] { #[inline] fn from(collection: AssetCollection<N>) -> Self { - array_map(collection.values, move |v| Asset::new(collection.id, v)) + array_map(collection.values, move |v| collection.id.with(v)) } } @@ -564,6 +578,96 @@ impl<const N: usize> TryFrom<[Asset; N]> for AssetCollection<N> { } } +/// Asset Map +/// +/// This trait represents an asset distribution over some [`Key`](Self::Key) type. +pub trait AssetMap { + /// Key Type + /// + /// Keys are used to access the underlying asset balances. See [`withdraw`](Self::withdraw) + /// and [`deposit`](Self::deposit) for uses of the [`Key`](Self::Key) type. + type Key; + + /// Asset Selection Iterator Type + /// + /// This type is returned by [`select`](Self::select) when looking for assets in the map. + type Selection: Iterator<Item = (Self::Key, AssetBalance)>; + + /// Asset Iterator Type + /// + /// This type is returned by [`iter`](Self::iter) when iterating over all assets. + type Iter: Iterator<Item = (Self::Key, Asset)>; + + /// Selects asset keys which total up to at least `asset` in value. + /// + /// See [`iter`](Self::iter) for iterating over all the assets in the map instead of a specific + /// subset summing to the `asset` total. + fn select(&self, asset: Asset) -> AssetSelection<Self>; + + /// Returns an iterator over all the assets stored in the map. + /// + /// See [`select`](Self::select) for selecting an asset distribution that sums to some known + /// `asset` total. + fn iter(&self) -> Self::Iter; + + /// Withdraws the asset stored at `key`. + fn withdraw(&mut self, key: Self::Key); + + /// Deposits `asset` at the key stored at `kind` and `index`, returning `false` if the `key` + /// was already assigned to some other [`Asset`]. + fn deposit(&mut self, key: Self::Key, asset: Asset) -> bool; + + /// Returns the current balance associated with this `id`. + #[inline] + fn balance(&self, id: AssetId) -> AssetBalance { + self.iter() + .filter_map(move |(_, asset)| (asset.id == id).then(move || asset.value)) + .sum() + } + + /// Returns true if `self` contains at least `asset.value` of the asset of kind `asset.id`. + #[inline] + fn contains(&self, asset: Asset) -> bool { + self.balance(asset.id) >= asset.value + } +} + +/// Asset Selection +/// +/// This `struct` is generated by the [`select`](AssetMap::select) method of [`AssetMap`]. See its +/// documentation for more. +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "M::Selection: Clone"), + Copy(bound = "M::Selection: Copy"), + Debug(bound = "M::Selection: Debug"), + Default(bound = "M::Selection: Default"), + Eq(bound = "M::Selection: Eq"), + Hash(bound = "M::Selection: Hash"), + PartialEq(bound = "M::Selection: PartialEq") +)] +pub struct AssetSelection<M> +where + M: AssetMap + ?Sized, +{ + /// Change Amount + pub change: AssetBalance, + + /// Asset Distribution + pub assets: M::Selection, +} + +impl<M> AssetSelection<M> +where + M: AssetMap + ?Sized, +{ + /// Splits [`self.change`] into `n` change components. + #[inline] + pub fn split_change(&self, n: usize) -> Option<Change> { + self.change.make_change(n) + } +} + /// Testing Suite #[cfg(test)] mod test { @@ -585,7 +689,7 @@ mod test { #[test] fn asset_arithmetic() { let mut rng = thread_rng(); - let mut asset = Asset::new(rng.gen(), AssetBalance(0)); + let mut asset = Asset::zero(rng.gen()); let value = rng.gen::<AssetBalance>(); let _ = asset + value; asset += value; diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 282f5c200..0c76dd57f 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -17,7 +17,6 @@ //! Identities, Senders, and Receivers // FIXME: Check the secret key APIs. -// TODO: Since `Configuration::SecretKey: Clone`, should `Identity: Clone`? use crate::asset::{Asset, AssetBalance, AssetId, AssetVar}; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; @@ -34,9 +33,7 @@ use rand::{ pub(super) mod prelude { #[doc(inline)] - pub use super::{ - Identity, Receiver, Sender, ShieldedIdentity, Spend, SpendError, Utxo, VoidNumber, - }; + pub use super::{Identity, Receiver, Sender, ShieldedIdentity, Spend, Utxo, VoidNumber}; } /// [`Identity`] Configuration diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs index 6b35fe150..cd9af9de8 100644 --- a/manta-accounting/src/wallet/ledger.rs +++ b/manta-accounting/src/wallet/ledger.rs @@ -28,15 +28,15 @@ pub trait Connection<C> where C: transfer::Configuration, { - /// Sync Future Type + /// Pull Future Type /// - /// Future for the [`sync`](Self::sync) method. - type SyncFuture: Future<Output = Result<SyncResponse<C, Self>, Self::Error>>; + /// Future for the [`pull`](Self::pull) method. + type PullFuture: Future<Output = Result<PullResponse<C, Self>, Self::Error>>; - /// Send Future Type + /// Push Future Type /// - /// Future for the [`send`](Self::send) method. - type SendFuture: Future<Output = Result<SendResponse<C, Self>, Self::Error>>; + /// Future for the [`push`](Self::push) method. + type PushFuture: Future<Output = Result<PushResponse<C, Self>, Self::Error>>; /// Ledger State Checkpoint Type type Checkpoint: Default + Ord; @@ -46,32 +46,32 @@ where /// Pulls data from the ledger starting from `checkpoint`, returning the current /// [`Checkpoint`](Self::Checkpoint). - fn sync(&self, checkpoint: &Self::Checkpoint) -> Self::SyncFuture; + fn pull(&self, checkpoint: &Self::Checkpoint) -> Self::PullFuture; /// Pulls all of the data from the entire history of the ledger, returning the current /// [`Checkpoint`](Self::Checkpoint). #[inline] - fn sync_all(&self) -> Self::SyncFuture { - self.sync(&Default::default()) + fn pull_all(&self) -> Self::PullFuture { + self.pull(&Default::default()) } /// Sends `transfers` to the ledger, returning the current [`Checkpoint`](Self::Checkpoint) - /// and the status of the transfers. - fn send(&self, transfers: Vec<TransferPost<C>>) -> Self::SendFuture; + /// and the status of the transfers if successful. + fn push(&self, transfers: Vec<TransferPost<C>>) -> Self::PushFuture; /// Sends `transfer` to the ledger, returning the current [`Checkpoint`](Self::Checkpoint) - /// and the status of the transfer. + /// and the status of the transfer if successful. #[inline] - fn send_one(&self, transfer: TransferPost<C>) -> Self::SendFuture { - self.send(vec![transfer]) + fn push_one(&self, transfer: TransferPost<C>) -> Self::PushFuture { + self.push(vec![transfer]) } } -/// Ledger Source Sync Response +/// Ledger Source Pull Response /// -/// This `struct` is created by the [`sync`](Connection::sync) method on [`Connection`]. +/// This `struct` is created by the [`pull`](Connection::pull) method on [`Connection`]. /// See its documentation for more. -pub struct SyncResponse<C, LC> +pub struct PullResponse<C, LC> where C: transfer::Configuration, LC: Connection<C> + ?Sized, @@ -89,11 +89,11 @@ where pub encrypted_assets: Vec<EncryptedAsset<C>>, } -/// Ledger Source Send Response +/// Ledger Source Push Response /// -/// This `struct` is created by the [`send`](Connection::send) method on [`Connection`]. +/// This `struct` is created by the [`push`](Connection::push) method on [`Connection`]. /// See its documentation for more. -pub struct SendResponse<C, LC> +pub struct PushResponse<C, LC> where C: transfer::Configuration, LC: Connection<C> + ?Sized, diff --git a/manta-accounting/src/wallet/mod.rs b/manta-accounting/src/wallet/mod.rs index 71db8716d..964077e53 100644 --- a/manta-accounting/src/wallet/mod.rs +++ b/manta-accounting/src/wallet/mod.rs @@ -16,9 +16,6 @@ //! Wallet Abstractions -mod state; - pub mod ledger; pub mod signer; - -pub use state::*; +// TODO: pub mod state; diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 45af70f25..95f2340b2 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -17,7 +17,7 @@ //! Wallet Signer use crate::{ - asset::{Asset, AssetId}, + asset::{Asset, AssetBalance, AssetId, AssetMap}, fs::{Load, LoadWith, Save, SaveWith}, identity::{self, AssetParameters, Identity}, keys::{Account, DerivedSecretKeyGenerator, Index, KeyKind, KeyOwned}, @@ -29,11 +29,11 @@ use crate::{ }; use alloc::vec::Vec; use core::{ + convert::Infallible, fmt::Debug, future::{self, Future, Ready}, hash::Hash, }; -use manta_crypto::ies::IntegratedEncryptionScheme; use rand::{ distributions::{Distribution, Standard}, CryptoRng, RngCore, @@ -43,55 +43,96 @@ use rand::{ pub type PreSender<D, C> = KeyOwned<D, identity::PreSender<C>>; /// Key-Owned Shielded Identity Type -pub type ShieldedIdentity<D, C, I> = KeyOwned<D, identity::ShieldedIdentity<C, I>>; +pub type ShieldedIdentity<D, C> = KeyOwned<D, transfer::ShieldedIdentity<C>>; /// Key-Owned Internal Receiver Type -pub type InternalReceiver<D, C, I> = KeyOwned<D, identity::InternalReceiver<C, I>>; +pub type InternalReceiver<D, C> = KeyOwned< + D, + identity::InternalReceiver<C, <C as transfer::Configuration>::IntegratedEncryptionScheme>, +>; /// Key-Owned Open Spend Type pub type OpenSpend<D, C> = KeyOwned<D, identity::OpenSpend<C>>; -/// Secret Key Generation Error -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "D::Error: Clone, E: Clone"), - Copy(bound = "D::Error: Copy, E: Copy"), - Debug(bound = "D::Error: Debug, E: Debug"), - Eq(bound = "D::Error: Eq, E: Eq"), - Hash(bound = "D::Error: Hash, E: Hash"), - PartialEq(bound = "D::Error: PartialEq, E: PartialEq") -)] -pub enum SecretKeyGenerationError<D, E> +/// Wallet Connection +pub trait Connection<D, C> where D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, { - /// Secret Key Generator Error - SecretKeyError(D::Error), + /// Sync Future Type + /// + /// Future for the [`sync`](Self::sync) method. + type SyncFuture: Future<Output = SyncResult<D, C, Self::Error>>; - /// Other Error - Error(E), + /// Sign Future Type + /// + /// Future for the [`sign`](Self::sign) method. + type SignFuture: Future<Output = SignResult<D, C, Self::Error>>; + + /// New Receiver Future Type + /// + /// Future for the [`new_receiver`](Self::new_receiver) method. + type NewReceiverFuture: Future<Output = NewReceiverResult<D, C, Self::Error>>; + + /// Error Type + type Error; + + /// Pushes updates from the ledger to the wallet, synchronizing it with the ledger state and + /// returning an updated asset distribution. + fn sync(&mut self, msg: ()) -> Self::SyncFuture; + + /// Signs a transfer `request` and returns the ledger transfer posts if successful. + fn sign(&mut self, request: SignRequest<D, C>) -> Self::SignFuture; + + /// Generates a new [`ShieldedIdentity`] for `self` to receive assets. + fn new_receiver(&mut self) -> Self::NewReceiverFuture; } -/// Signer Connection -pub trait Connection<D, C> +/// Synchronization Result +/// +/// See the [`sync`](Connection::sync) method on [`Connection`] for more information. +pub type SyncResult<D, C, CE> = Result<(), Error<D, C, CE>>; + +/// Signing Result +/// +/// See the [`sign`](Connection::sign) method on [`Connection`] for more information. +pub type SignResult<D, C, CE> = Result<SignResponse<D, C>, Error<D, C, CE>>; + +/// New Receiver Generation Result +/// +/// See the [`new_receiver`](Connection::new_receiver) method on [`Connection`] for more +/// information. +pub type NewReceiverResult<D, C, CE> = Result<ShieldedIdentity<D, C>, Error<D, C, CE>>; + +/* TODO: +pub struct SignRequest<D, C> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, { - /// Sign Future Type /// - /// Future for the [`sign`](Self::sign) method. - type SignFuture: Future<Output = Result<Response<D, C>, Error<D, C>>>; + pub asset: Asset, - /// Signs a transfer request and returns the ledger transfer posts if successful. - fn sign(&mut self, request: Request<D, C>) -> Self::SignFuture; + /// + pub sources: Vec<AssetBalance>, + + /// + pub senders: Vec<(AssetBalance, Index<D>)>, + + /// + pub receivers: Vec<(AssetBalance, transfer::ShieldedIdentity<C>)>, + + /// + pub sinks: Vec<AssetBalance>, } +*/ -/// Signer Connection Request +/// Wallet Signing Request /// /// This `struct` is used by the [`sign`](Connection::sign) method on [`Connection`]. /// See its documentation for more. -pub enum Request<D, C> +pub enum SignRequest<D, C> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, @@ -106,36 +147,47 @@ where Reclaim(Asset, Vec<Index<D>>), } -/// Signer Connection Response +/// Wallet Signing Response /// /// This `struct` is created by the [`sign`](Connection::sign) method on [`Connection`]. /// See its documentation for more. -pub struct Response<D, C> +pub struct SignResponse<D, C> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, { - /// Final Owner Index - pub owner: Index<D>, + /// Asset Distribution Deposit Asset Id + pub asset_id: AssetId, + + /// Asset Distribution Deposit Balance Updates + pub balances: Vec<(Index<D>, AssetBalance)>, /// Transfer Posts pub transfers: Vec<TransferPost<C>>, } -impl<D, C> Response<D, C> +impl<D, C> SignResponse<D, C> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, { - /// Builds a new [`Response`] from `owner` and `transfers`. + /// Builds a new [`SignResponse`] from `asset_id`, `balances` and `transfers`. #[inline] - pub fn new(owner: Index<D>, transfers: Vec<TransferPost<C>>) -> Self { - Self { owner, transfers } + pub fn new( + asset_id: AssetId, + balances: Vec<(Index<D>, AssetBalance)>, + transfers: Vec<TransferPost<C>>, + ) -> Self { + Self { + asset_id, + balances, + transfers, + } } } -/// Signer Connection Error -pub enum Error<D, C> +/// Wallet Error +pub enum Error<D, C, CE> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, @@ -148,6 +200,23 @@ where /// Proof System Error ProofSystemError(ProofSystemError<C>), + + /// Wallet Connection Error + ConnectionError(CE), +} + +impl<D, C, CE> From<InternalReceiverError<D, C>> for Error<D, C, CE> +where + D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, +{ + #[inline] + fn from(err: InternalReceiverError<D, C>) -> Self { + match err { + InternalReceiverError::SecretKeyError(err) => Self::SecretKeyError(err), + InternalReceiverError::EncryptionError(err) => Self::EncryptionError(err), + } + } } /// Signer @@ -270,13 +339,12 @@ where /// Generates a new [`ShieldedIdentity`] to receive assets to this account via an external /// transaction. #[inline] - pub fn next_shielded_identity<C, I>( + pub fn next_shielded<C>( &mut self, commitment_scheme: &C::CommitmentScheme, - ) -> Result<ShieldedIdentity<D, C, I>, D::Error> + ) -> Result<ShieldedIdentity<D, C>, D::Error> where - C: identity::Configuration<SecretKey = D::SecretKey>, - I: IntegratedEncryptionScheme<Plaintext = Asset>, + C: transfer::Configuration<SecretKey = D::SecretKey>, Standard: Distribution<AssetParameters<C>>, { Ok(self @@ -287,40 +355,98 @@ where /// Generates a new [`InternalReceiver`] to receive `asset` to this account via an /// internal transaction. #[inline] - pub fn next_internal_receiver<C, I, R>( + pub fn next_change_receiver<C, R>( &mut self, commitment_scheme: &C::CommitmentScheme, asset: Asset, rng: &mut R, - ) -> Result<InternalReceiver<D, C, I>, SecretKeyGenerationError<D, I::Error>> + ) -> Result<InternalReceiver<D, C>, InternalReceiverError<D, C>> where - C: identity::Configuration<SecretKey = D::SecretKey>, - I: IntegratedEncryptionScheme<Plaintext = Asset>, + C: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, Standard: Distribution<AssetParameters<C>>, { self.next_internal_identity() - .map_err(SecretKeyGenerationError::SecretKeyError)? + .map_err(InternalReceiverError::SecretKeyError)? .map_ok(move |identity| identity.into_internal_receiver(commitment_scheme, asset, rng)) - .map_err(SecretKeyGenerationError::Error) + .map_err(InternalReceiverError::EncryptionError) + } + + /// Builds a vector of `n` internal receivers to capture the given `asset`. + /// + /// # Panics + /// + /// This method panics if `n == 0`. + #[inline] + pub fn next_change_receivers<C, R>( + &mut self, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + n: usize, + rng: &mut R, + ) -> InternalReceiverResult<D, C, Vec<InternalReceiver<D, C>>> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + R: CryptoRng + RngCore + ?Sized, + Standard: Distribution<AssetParameters<C>>, + { + asset + .value + .make_change(n) + .unwrap() + .map(move |value| { + self.next_change_receiver(commitment_scheme, asset.id.with(value), rng) + }) + .collect() } /// Generates a new [`InternalReceiver`] to receive an asset, with the given `asset_id` and /// no value, to this account via an internal transaction. #[inline] - pub fn next_empty_internal_receiver<C, I, R>( + pub fn next_empty_receiver<C, R>( &mut self, commitment_scheme: &C::CommitmentScheme, asset_id: AssetId, rng: &mut R, - ) -> Result<InternalReceiver<D, C, I>, SecretKeyGenerationError<D, I::Error>> + ) -> Result<InternalReceiver<D, C>, InternalReceiverError<D, C>> where - C: identity::Configuration<SecretKey = D::SecretKey>, - I: IntegratedEncryptionScheme<Plaintext = Asset>, + C: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, Standard: Distribution<AssetParameters<C>>, { - self.next_internal_receiver(commitment_scheme, Asset::zero(asset_id), rng) + self.next_change_receiver(commitment_scheme, Asset::zero(asset_id), rng) + } + + /// Generates a new [`PreSender`] to send `asset` from this account via an internal + /// transaction. + #[inline] + pub fn next_pre_sender<C>( + &mut self, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + ) -> Result<PreSender<D, C>, D::Error> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + Standard: Distribution<AssetParameters<C>>, + { + Ok(self + .next_internal_identity()? + .map(move |identity| identity.into_pre_sender(commitment_scheme, asset))) + } + + /// Generates a new [`PreSender`] to send an asset with the given `asset_id` and no value, + /// from this account via an internal transaction. + #[inline] + pub fn next_empty_pre_sender<C>( + &mut self, + commitment_scheme: &C::CommitmentScheme, + asset_id: AssetId, + ) -> Result<PreSender<D, C>, D::Error> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + Standard: Distribution<AssetParameters<C>>, + { + self.next_pre_sender(commitment_scheme, Asset::zero(asset_id)) } /// Builds a [`Mint`] transaction to mint `asset` and returns the [`OpenSpend`] for that asset. @@ -330,7 +456,7 @@ where commitment_scheme: &C::CommitmentScheme, asset: Asset, rng: &mut R, - ) -> Result<(Mint<C>, OpenSpend<D, C>), MintError<D, C>> + ) -> InternalReceiverResult<D, C, (Mint<C>, OpenSpend<D, C>)> where C: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, @@ -338,19 +464,36 @@ where { Ok(self .next_internal_identity() - .map_err(MintError::SecretKeyError)? + .map_err(InternalReceiverError::SecretKeyError)? .map_ok(move |identity| Mint::from_identity(identity, commitment_scheme, asset, rng)) - .map_err(MintError::EncryptionError)? + .map_err(InternalReceiverError::EncryptionError)? .right()) } + /// Builds a [`Mint`] transaction to mint an asset with the given `asset_id` and no value, + /// returning the [`OpenSpend`] for that asset. + #[inline] + pub fn mint_zero<C, R>( + &mut self, + commitment_scheme: &C::CommitmentScheme, + asset_id: AssetId, + rng: &mut R, + ) -> InternalReceiverResult<D, C, (Mint<C>, OpenSpend<D, C>)> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + R: CryptoRng + RngCore + ?Sized, + Standard: Distribution<AssetParameters<C>>, + { + self.mint(commitment_scheme, Asset::zero(asset_id), rng) + } + /// Builds [`PrivateTransfer`] transactions to send `asset` to an `external_receiver`. #[inline] pub fn private_transfer<C, R>( &mut self, commitment_scheme: &C::CommitmentScheme, asset: Asset, - senders: Vec<Index<D>>, + senders: Vec<(Index<D>, AssetBalance)>, external_receiver: transfer::ShieldedIdentity<C>, rng: &mut R, ) -> Option<Vec<PrivateTransfer<C>>> @@ -360,48 +503,47 @@ where Standard: Distribution<AssetParameters<C>>, { /* TODO: - let selection = self.balance_state.asset_map.select(asset).ok()?; - - let change_receiver = selection - .change_receiver::<_, _, C::IntegratedEncryptionScheme, _>( - &mut self.signer, - asset.id, - commitment_scheme, - rng, - ) - .ok()?; - - let mut pre_senders = selection - .assets + let mut sender_total = AssetBalance(0); + let mut pre_senders = senders .into_iter() .map(|(index, value)| { - self.signer - .get_pre_sender(index, commitment_scheme, Asset::new(asset.id, value)) + sender_total += value; + self.get_pre_sender(index, commitment_scheme, asset.id.with(value)) }) .collect::<Result<Vec<_>, _>>() .ok()?; let mint = if pre_senders.len() % 2 == 1 { - let (mint, open_spend) = self - .signer - .mint(commitment_scheme, Asset::zero(asset.id), rng) - .ok()?; + let (mint, open_spend) = self.mint_zero(commitment_scheme, asset.id, rng).ok()?; pre_senders.push(open_spend.map(move |os| os.into_pre_sender(commitment_scheme))); Some(mint) } else { None }; - */ + + let mut transfers = Vec::new(); + let mut accumulator = self + .next_internal_identity()? + .into_pre_sender(commitment_scheme, Asset::zero(asset.id)); + for pre_sender in pre_senders { + let (next_receiver, next_open_spend) = + self.next_change_receiver(commitment_scheme)?.value.into(); + transfers.push(PrivateTransfer::build( + [accumulator.into_sender(), pre_sender.value], + [ + next_receiver, + self.next_empty_receiver(commitment_scheme, asset.id, rng)?, + ], + )); + accumulator = next_open_spend.into_pre_sender(commitment_scheme); + } let external_receiver = external_receiver.into_receiver(commitment_scheme, asset, rng); - /* TODO: - for i in 0..(pre_senders.len() / 2) { - PrivateTransfer::build( - [pre_senders[2 * i], pre_senders[2 * i + 1]], - [?, ?] - ) - } + transfers.push(PrivateTransfer::build( + [accumulator.into_sender(), self.next_empty_sender()], + [external_receiver, change], + )); */ todo!() @@ -570,11 +712,12 @@ where } } -/// Full Signer -pub struct FullSigner<D, C, R> +/// Wallet +pub struct Wallet<D, C, M, R> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, + M: AssetMap<Key = Index<D>>, R: CryptoRng + RngCore + ?Sized, { /// Signer @@ -586,57 +729,110 @@ where /// Proving Context proving_context: ProvingContext<C>, + /// UTXO Set + utxo_set: C::UtxoSet, + + /// Asset Distribution + assets: M, + /// Random Number Generator rng: R, } -impl<D, C, R> Connection<D, C> for FullSigner<D, C, R> +impl<D, C, M, R> Wallet<D, C, M, R> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, + M: AssetMap<Key = Index<D>>, R: CryptoRng + RngCore + ?Sized, - Standard: Distribution<AssetParameters<C>>, { - type SignFuture = Ready<Result<Response<D, C>, Error<D, C>>>; - - #[inline] - fn sign(&mut self, request: Request<D, C>) -> Self::SignFuture { - future::ready(match request { - Request::Mint(asset) => self - .signer - .mint::<C, _>(&self.commitment_scheme, asset, &mut self.rng) - .map_err(|e| match e { - MintError::SecretKeyError(err) => Error::SecretKeyError(err), - MintError::EncryptionError(err) => Error::EncryptionError(err), - }) - .and_then(|(mint, open_spend)| { - /* TODO: - mint.into_post( + /// Updates the internal ledger state, returing the new asset distribution. + #[inline] + fn sync(&mut self, msg: ()) -> SyncResult<D, C, Infallible> { + // TODO: + // 1. Update the utxo_set + // 2. Update the asset distribution + // 3. Return asset distribution changes + todo!() + } + + /// Signs the `request`, generating transfer posts. + #[inline] + fn sign(&mut self, request: SignRequest<D, C>) -> SignResult<D, C, Infallible> + where + Standard: Distribution<AssetParameters<C>>, + { + match request { + SignRequest::Mint(asset) => { + let (mint, open_spend) = + self.signer + .mint(&self.commitment_scheme, asset, &mut self.rng)?; + let mint_post = mint + .into_post( &self.commitment_scheme, + &self.utxo_set, &self.proving_context, &mut self.rng, ) - .map_err(Error::ProofSystemError) - .map(|p| Response::new(open_spend.index, vec![p])) - */ - todo!() - }), - Request::PrivateTransfer(asset, senders, receiver) => { + .map_err(Error::ProofSystemError)?; + Ok(SignResponse::new( + asset.id, + vec![(open_spend.index, asset.value)], + vec![mint_post], + )) + } + SignRequest::PrivateTransfer(asset, senders, receiver) => { // todo!() } - Request::Reclaim(asset, senders) => { + SignRequest::Reclaim(asset, senders) => { // todo!() } - }) + } + } +} + +impl<D, C, M, R> Connection<D, C> for Wallet<D, C, M, R> +where + D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, + M: AssetMap<Key = Index<D>>, + R: CryptoRng + RngCore + ?Sized, + Standard: Distribution<AssetParameters<C>>, +{ + type SyncFuture = Ready<SyncResult<D, C, Self::Error>>; + + type SignFuture = Ready<SignResult<D, C, Self::Error>>; + + type NewReceiverFuture = Ready<NewReceiverResult<D, C, Self::Error>>; + + type Error = Infallible; + + #[inline] + fn sync(&mut self, msg: ()) -> Self::SyncFuture { + future::ready(self.sync(msg)) + } + + #[inline] + fn sign(&mut self, request: SignRequest<D, C>) -> Self::SignFuture { + future::ready(self.sign(request)) + } + + #[inline] + fn new_receiver(&mut self) -> Self::NewReceiverFuture { + future::ready( + self.signer + .next_shielded(&self.commitment_scheme) + .map_err(Error::SecretKeyError), + ) } } -/// Mint Error +/// Internal Receiver Error /// -/// This `enum` is the error state for the [`mint`](Signer::mint) method on [`Signer`]. -/// See its documentation for more. +/// This `enum` is the error state for any construction of an [`InternalReceiver`] from a derived +/// secret key generator. #[derive(derivative::Derivative)] #[derivative( Clone(bound = "D::Error: Clone, IntegratedEncryptionSchemeError<C>: Clone"), @@ -646,10 +842,10 @@ where Hash(bound = "D::Error: Hash, IntegratedEncryptionSchemeError<C>: Hash"), PartialEq(bound = "D::Error: PartialEq, IntegratedEncryptionSchemeError<C>: PartialEq") )] -pub enum MintError<D, C> +pub enum InternalReceiverError<D, C> where D: DerivedSecretKeyGenerator, - C: transfer::Configuration, + C: transfer::Configuration<SecretKey = D::SecretKey>, { /// Secret Key Generator Error SecretKeyError(D::Error), @@ -657,3 +853,6 @@ where /// Encryption Error EncryptionError(IntegratedEncryptionSchemeError<C>), } + +/// Internal Receiver Result Type +pub type InternalReceiverResult<D, C, T> = Result<T, InternalReceiverError<D, C>>; diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index d88ed4204..d9487ad94 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -18,9 +18,12 @@ use crate::{ asset::{Asset, AssetBalance, AssetId}, - identity::{self, AssetParameters, Utxo, VoidNumber}, + identity::{AssetParameters, Utxo, VoidNumber}, keys::{DerivedSecretKeyGenerator, Index, KeyKind}, - transfer::{self, EncryptedAsset, ReceiverPost, SenderPost, TransferPost}, + transfer::{ + self, EncryptedAsset, IntegratedEncryptionSchemeError, ReceiverPost, SenderPost, + TransferPost, + }, wallet::{ ledger::{self, SendResponse, SyncResponse}, signer::{self, InternalReceiver, SecretKeyGenerationError, Signer}, @@ -28,116 +31,11 @@ use crate::{ }; use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; -use manta_crypto::ies::IntegratedEncryptionScheme; use rand::{ distributions::{Distribution, Standard}, CryptoRng, RngCore, }; -/// Asset Map -pub trait AssetMap { - /// Key Type - /// - /// Keys are used to access the underlying asset balances. See [`withdraw`](Self::withdraw) - /// and [`deposit`](Self::deposit) for uses of the [`Key`](Self::Key) type. - type Key; - - /// Assets Iterator Type - /// - /// This type is returned by [`select`](Self::select) when looking for assets in the map. - type Assets: IntoIterator<Item = (Self::Key, AssetBalance)>; - - /// Error Type - type Error; - - /// Selects asset keys which total up to at least `asset` in value. - fn select(&self, asset: Asset) -> AssetSelection<Self>; - - /// Withdraws the asset stored at `key`. - fn withdraw(&mut self, key: Self::Key) -> Result<(), Self::Error>; - - /// Deposits `asset` at the key stored at `kind` and `index`. - fn deposit(&mut self, key: Self::Key, asset: Asset) -> Result<(), Self::Error>; - - /// Returns the current balance associated with this `id`. - fn balance(&self, id: AssetId) -> AssetBalance; - - /// Returns true if `self` contains at least `asset.value` of the asset of kind `asset.id`. - #[inline] - fn contains(&self, asset: Asset) -> bool { - self.balance(asset.id) >= asset.value - } -} - -/// Asset Selection -pub struct AssetSelection<S> -where - S: AssetMap + ?Sized, -{ - /// Change Amount - pub change: AssetBalance, - - /// Sender Assets - pub assets: S::Assets, -} - -impl<S> AssetSelection<S> -where - S: AssetMap + ?Sized, -{ - /// Builds an [`InternalReceiver`] to capture the `change` from the given [`AssetSelection`]. - #[inline] - pub fn change_receiver<D, C, I, R>( - &self, - signer: &mut Signer<D>, - asset_id: AssetId, - commitment_scheme: &C::CommitmentScheme, - rng: &mut R, - ) -> Result<InternalReceiver<D, C, I>, SecretKeyGenerationError<D, I::Error>> - where - S: AssetMap<Key = Index<D>>, - D: DerivedSecretKeyGenerator, - C: identity::Configuration<SecretKey = D::SecretKey>, - I: IntegratedEncryptionScheme<Plaintext = Asset>, - R: CryptoRng + RngCore + ?Sized, - Standard: Distribution<AssetParameters<C>>, - { - signer.next_internal_receiver(commitment_scheme, Asset::new(asset_id, self.change), rng) - } - - /// Builds a vector of `n` internal receivers to capture the `change` from the given - /// [`AssetSelection`]. - /// - /// # Panics - /// - /// This method panics if `n == 0`. - #[inline] - pub fn change_receivers<D, C, I, R>( - &self, - signer: &mut Signer<D>, - asset_id: AssetId, - n: usize, - commitment_scheme: &C::CommitmentScheme, - rng: &mut R, - ) -> Result<Vec<InternalReceiver<D, C, I>>, SecretKeyGenerationError<D, I::Error>> - where - S: AssetMap<Key = Index<D>>, - D: DerivedSecretKeyGenerator, - C: identity::Configuration<SecretKey = D::SecretKey>, - I: IntegratedEncryptionScheme<Plaintext = Asset>, - R: CryptoRng + RngCore + ?Sized, - Standard: Distribution<AssetParameters<C>>, - { - self.change - .make_change(n) - .unwrap() - .map(move |value| { - signer.next_internal_receiver(commitment_scheme, Asset::new(asset_id, value), rng) - }) - .collect() - } -} - /// Ledger Ownership Marker #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum Ownership { @@ -385,37 +283,3 @@ where todo!() } } - -/// Full Wallet -pub struct Wallet<D, C, L, M, SC, LC> -where - D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, - L: LocalLedger<C>, - M: AssetMap, - SC: signer::Connection<D, C>, - LC: ledger::Connection<C>, -{ - /// Wallet Balance State - _state: BalanceState<C, L, M>, - - /// Signer Connection - _signer: SC, - - /// Ledger Connection - _ledger: LC, - - /// Type Parameter Marker - __: PhantomData<D>, -} - -impl<D, C, L, M, SC, LC> Wallet<D, C, L, M, SC, LC> -where - D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, - L: LocalLedger<C>, - M: AssetMap, - SC: signer::Connection<D, C>, - LC: ledger::Connection<C>, -{ -} diff --git a/manta-crypto/src/merkle_tree/path.rs b/manta-crypto/src/merkle_tree/path.rs index 1eeebd14b..7e63ab5be 100644 --- a/manta-crypto/src/merkle_tree/path.rs +++ b/manta-crypto/src/merkle_tree/path.rs @@ -312,8 +312,8 @@ where root == &self.root(parameters, leaf_digest, sibling_digest) } - /// Returns an iterator over the elements of [`self.path`](Self::path) as [`InnerNode`] - /// objects, yielding elements of the path if they are not default. + /// Returns an iterator over the elements of [`self.path`] as [`InnerNode`] objects, yielding + /// elements of the path if they are not default. #[inline] pub fn into_nodes(self) -> CurrentInnerPathNodeIter<C> { CurrentInnerPathNodeIter::new(self) @@ -870,9 +870,6 @@ where /// Uncompresses a path by re-inserting the default values into [`self.inner_path`] at the /// indices described by [`self.sentinel_ranges`]. - /// - /// [`self.sentinel_ranges`]: Self::sentinel_ranges - /// [`self.inner_path`]: Self::inner_path #[inline] pub fn uncompress(mut self) -> Path<C> { let path_length = path_length::<C>(); @@ -920,8 +917,8 @@ impl<C> Path<C> where C: Configuration + ?Sized, { - /// Compresses [`self.inner_path`](Self::inner_path) by removing all default values and saving - /// only the positions in the path of those default values. + /// Compresses [`self.inner_path`] by removing all default values and saving only the positions + /// in the path of those default values. #[inline] pub fn compress(self) -> CompressedPath<C> { let default = Default::default(); From 938304fdc61fe8deb799dfc62895677209cc65c7 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 6 Oct 2021 03:28:54 -0400 Subject: [PATCH 077/275] WIP: simplified signer/ledger; finalizing wallet design --- manta-accounting/src/keys.rs | 6 + manta-accounting/src/wallet/ledger.rs | 75 ++++-- manta-accounting/src/wallet/mod.rs | 5 +- manta-accounting/src/wallet/signer.rs | 103 +++++++-- manta-accounting/src/wallet/state.rs | 321 ++++++++------------------ 5 files changed, 242 insertions(+), 268 deletions(-) diff --git a/manta-accounting/src/keys.rs b/manta-accounting/src/keys.rs index e974f050b..882148d8f 100644 --- a/manta-accounting/src/keys.rs +++ b/manta-accounting/src/keys.rs @@ -333,6 +333,12 @@ where self.index.is_internal() } + /// Returns the inner [`self.value`] dropping the [`self.index`]. + #[inline] + pub fn unwrap(self) -> T { + self.value + } + /// Maps the underlying value using `f`. #[inline] pub fn map<U, F>(self, f: F) -> KeyOwned<D, U> diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs index cd9af9de8..810d05b65 100644 --- a/manta-accounting/src/wallet/ledger.rs +++ b/manta-accounting/src/wallet/ledger.rs @@ -20,7 +20,7 @@ use crate::{ identity::{Utxo, VoidNumber}, transfer::{self, EncryptedAsset, TransferPost}, }; -use alloc::{vec, vec::Vec}; +use alloc::vec::Vec; use core::future::Future; /// Ledger Source Connection @@ -28,19 +28,22 @@ pub trait Connection<C> where C: transfer::Configuration, { + /// Ledger State Checkpoint Type + type Checkpoint: Default + Ord; + /// Pull Future Type /// /// Future for the [`pull`](Self::pull) method. type PullFuture: Future<Output = Result<PullResponse<C, Self>, Self::Error>>; + /// Pull Data Iterator Type + type PullData: IntoIterator<Item = LedgerData<C>>; + /// Push Future Type /// /// Future for the [`push`](Self::push) method. type PushFuture: Future<Output = Result<PushResponse<C, Self>, Self::Error>>; - /// Ledger State Checkpoint Type - type Checkpoint: Default + Ord; - /// Error Type type Error; @@ -58,49 +61,71 @@ where /// Sends `transfers` to the ledger, returning the current [`Checkpoint`](Self::Checkpoint) /// and the status of the transfers if successful. fn push(&self, transfers: Vec<TransferPost<C>>) -> Self::PushFuture; - - /// Sends `transfer` to the ledger, returning the current [`Checkpoint`](Self::Checkpoint) - /// and the status of the transfer if successful. - #[inline] - fn push_one(&self, transfer: TransferPost<C>) -> Self::PushFuture { - self.push(vec![transfer]) - } } /// Ledger Source Pull Response /// /// This `struct` is created by the [`pull`](Connection::pull) method on [`Connection`]. /// See its documentation for more. -pub struct PullResponse<C, LC> +pub struct PullResponse<C, L> where C: transfer::Configuration, - LC: Connection<C> + ?Sized, + L: Connection<C> + ?Sized, { /// Current Ledger Checkpoint - pub checkpoint: LC::Checkpoint, - - /// New Void Numbers - pub void_numbers: Vec<VoidNumber<C>>, + pub checkpoint: L::Checkpoint, - /// New UTXOS - pub utxos: Vec<Utxo<C>>, - - /// New Encrypted Assets - pub encrypted_assets: Vec<EncryptedAsset<C>>, + /// Ledger Data + pub data: L::PullData, } /// Ledger Source Push Response /// /// This `struct` is created by the [`push`](Connection::push) method on [`Connection`]. /// See its documentation for more. -pub struct PushResponse<C, LC> +pub struct PushResponse<C, L> where C: transfer::Configuration, - LC: Connection<C> + ?Sized, + L: Connection<C> + ?Sized, { /// Current Ledger Checkpoint - pub checkpoint: LC::Checkpoint, + pub checkpoint: L::Checkpoint, /// Transaction Failed at the Given Index pub failure_index: Option<usize>, } + +/// Ledger Data +pub enum LedgerData<C> +where + C: transfer::Configuration, +{ + /// Sender Data + Sender(VoidNumber<C>), + + /// Receiver Data + Receiver(Utxo<C>, EncryptedAsset<C>), +} + +impl<C> LedgerData<C> +where + C: transfer::Configuration, +{ + /// Extracts the sender data, if `self` matches [`Self::Sender`]. + #[inline] + pub fn sender(self) -> Option<VoidNumber<C>> { + match self { + Self::Sender(void_number) => Some(void_number), + _ => None, + } + } + + /// Extracts the receiver data, if `self` matches [`Self::Receiver`]. + #[inline] + pub fn receiver(self) -> Option<(Utxo<C>, EncryptedAsset<C>)> { + match self { + Self::Receiver(utxo, encryped_asset) => Some((utxo, encryped_asset)), + _ => None, + } + } +} diff --git a/manta-accounting/src/wallet/mod.rs b/manta-accounting/src/wallet/mod.rs index 964077e53..71db8716d 100644 --- a/manta-accounting/src/wallet/mod.rs +++ b/manta-accounting/src/wallet/mod.rs @@ -16,6 +16,9 @@ //! Wallet Abstractions +mod state; + pub mod ledger; pub mod signer; -// TODO: pub mod state; + +pub use state::*; diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 95f2340b2..8ec95d118 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -19,15 +19,16 @@ use crate::{ asset::{Asset, AssetBalance, AssetId, AssetMap}, fs::{Load, LoadWith, Save, SaveWith}, - identity::{self, AssetParameters, Identity}, + identity::{self, AssetParameters, Identity, Utxo}, keys::{Account, DerivedSecretKeyGenerator, Index, KeyKind, KeyOwned}, transfer::{ self, canonical::{Mint, PrivateTransfer, Reclaim}, - IntegratedEncryptionSchemeError, ProofSystemError, ProvingContext, TransferPost, + EncryptedAsset, IntegratedEncryptionSchemeError, ProofSystemError, ProvingContext, + TransferPost, }, }; -use alloc::vec::Vec; +use alloc::{vec, vec::Vec}; use core::{ convert::Infallible, fmt::Debug, @@ -54,7 +55,7 @@ pub type InternalReceiver<D, C> = KeyOwned< /// Key-Owned Open Spend Type pub type OpenSpend<D, C> = KeyOwned<D, identity::OpenSpend<C>>; -/// Wallet Connection +/// Signer Connection pub trait Connection<D, C> where D: DerivedSecretKeyGenerator, @@ -80,7 +81,7 @@ where /// Pushes updates from the ledger to the wallet, synchronizing it with the ledger state and /// returning an updated asset distribution. - fn sync(&mut self, msg: ()) -> Self::SyncFuture; + fn sync(&mut self, updates: Vec<(Utxo<C>, EncryptedAsset<C>)>) -> Self::SyncFuture; /// Signs a transfer `request` and returns the ledger transfer posts if successful. fn sign(&mut self, request: SignRequest<D, C>) -> Self::SignFuture; @@ -92,7 +93,7 @@ where /// Synchronization Result /// /// See the [`sync`](Connection::sync) method on [`Connection`] for more information. -pub type SyncResult<D, C, CE> = Result<(), Error<D, C, CE>>; +pub type SyncResult<D, C, CE> = Result<SyncResponse<D>, Error<D, C, CE>>; /// Signing Result /// @@ -103,7 +104,22 @@ pub type SignResult<D, C, CE> = Result<SignResponse<D, C>, Error<D, C, CE>>; /// /// See the [`new_receiver`](Connection::new_receiver) method on [`Connection`] for more /// information. -pub type NewReceiverResult<D, C, CE> = Result<ShieldedIdentity<D, C>, Error<D, C, CE>>; +pub type NewReceiverResult<D, C, CE> = Result<transfer::ShieldedIdentity<C>, Error<D, C, CE>>; + +/// Signer Synchronization Response +/// +/// This `struct` is created by the [`sync`](Connection::sync) methon on [`Connection`]. +/// See its documentation for more. +pub struct SyncResponse<D> +where + D: DerivedSecretKeyGenerator, +{ + /// + pub deposit: Vec<(Index<D>, Asset)>, + + /// + pub withdraw: Vec<Index<D>>, +} /* TODO: pub struct SignRequest<D, C> @@ -128,7 +144,7 @@ where } */ -/// Wallet Signing Request +/// Signer Signing Request /// /// This `struct` is used by the [`sign`](Connection::sign) method on [`Connection`]. /// See its documentation for more. @@ -147,7 +163,7 @@ where Reclaim(Asset, Vec<Index<D>>), } -/// Wallet Signing Response +/// Signer Signing Response /// /// This `struct` is created by the [`sign`](Connection::sign) method on [`Connection`]. /// See its documentation for more. @@ -186,7 +202,7 @@ where } } -/// Wallet Error +/// Signer Error pub enum Error<D, C, CE> where D: DerivedSecretKeyGenerator, @@ -201,7 +217,7 @@ where /// Proof System Error ProofSystemError(ProofSystemError<C>), - /// Wallet Connection Error + /// Signer Connection Error ConnectionError(CE), } @@ -712,13 +728,13 @@ where } } -/// Wallet -pub struct Wallet<D, C, M, R> +/// Full Signer +pub struct FullSigner<D, C, M, R> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, M: AssetMap<Key = Index<D>>, - R: CryptoRng + RngCore + ?Sized, + R: CryptoRng + RngCore, { /// Signer signer: Signer<D>, @@ -739,16 +755,60 @@ where rng: R, } -impl<D, C, M, R> Wallet<D, C, M, R> +impl<D, C, M, R> FullSigner<D, C, M, R> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, M: AssetMap<Key = Index<D>>, - R: CryptoRng + RngCore + ?Sized, + R: CryptoRng + RngCore, { + /// Builds a new [`FullSigner`]. + #[inline] + fn new_inner( + signer: Signer<D>, + commitment_scheme: C::CommitmentScheme, + proving_context: ProvingContext<C>, + utxo_set: C::UtxoSet, + assets: M, + rng: R, + ) -> Self { + Self { + signer, + commitment_scheme, + proving_context, + utxo_set, + assets, + rng, + } + } + + /// Builds a new [`FullSigner`] from `secret_key_source`, `account`, `commitment_scheme`, + /// `proving_context`, and `rng`, using a default [`Utxo`] set and empty asset distribution. + #[inline] + pub fn new( + secret_key_source: D, + account: D::Account, + commitment_scheme: C::CommitmentScheme, + proving_context: ProvingContext<C>, + rng: R, + ) -> Self + where + C::UtxoSet: Default, + M: Default, + { + Self::new_inner( + Signer::new(secret_key_source, account), + commitment_scheme, + proving_context, + Default::default(), + Default::default(), + rng, + ) + } + /// Updates the internal ledger state, returing the new asset distribution. #[inline] - fn sync(&mut self, msg: ()) -> SyncResult<D, C, Infallible> { + fn sync(&mut self, updates: Vec<(Utxo<C>, EncryptedAsset<C>)>) -> SyncResult<D, C, Infallible> { // TODO: // 1. Update the utxo_set // 2. Update the asset distribution @@ -793,12 +853,12 @@ where } } -impl<D, C, M, R> Connection<D, C> for Wallet<D, C, M, R> +impl<D, C, M, R> Connection<D, C> for FullSigner<D, C, M, R> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, M: AssetMap<Key = Index<D>>, - R: CryptoRng + RngCore + ?Sized, + R: CryptoRng + RngCore, Standard: Distribution<AssetParameters<C>>, { type SyncFuture = Ready<SyncResult<D, C, Self::Error>>; @@ -810,8 +870,8 @@ where type Error = Infallible; #[inline] - fn sync(&mut self, msg: ()) -> Self::SyncFuture { - future::ready(self.sync(msg)) + fn sync(&mut self, updates: Vec<(Utxo<C>, EncryptedAsset<C>)>) -> Self::SyncFuture { + future::ready(self.sync(updates)) } #[inline] @@ -824,6 +884,7 @@ where future::ready( self.signer .next_shielded(&self.commitment_scheme) + .map(KeyOwned::unwrap) .map_err(Error::SecretKeyError), ) } diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index d9487ad94..e7679cc98 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -14,272 +14,151 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Wallet State +//! Wallet Full State Implementation use crate::{ - asset::{Asset, AssetBalance, AssetId}, - identity::{AssetParameters, Utxo, VoidNumber}, - keys::{DerivedSecretKeyGenerator, Index, KeyKind}, - transfer::{ - self, EncryptedAsset, IntegratedEncryptionSchemeError, ReceiverPost, SenderPost, - TransferPost, - }, + asset::{Asset, AssetBalance, AssetId, AssetMap}, + keys::{DerivedSecretKeyGenerator, Index}, + transfer::{self, ShieldedIdentity}, wallet::{ - ledger::{self, SendResponse, SyncResponse}, - signer::{self, InternalReceiver, SecretKeyGenerationError, Signer}, + ledger::{self, LedgerData, PullResponse}, + signer::{self, SyncResponse}, }, }; -use alloc::vec::Vec; -use core::{fmt::Debug, hash::Hash, marker::PhantomData}; -use rand::{ - distributions::{Distribution, Standard}, - CryptoRng, RngCore, -}; - -/// Ledger Ownership Marker -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum Ownership { - /// Key Ownership - Key(KeyKind), - - /// Unknown Self-Ownership - Unknown, - - /// Ownership by Others - Other, -} - -impl Default for Ownership { - #[inline] - fn default() -> Self { - Self::Unknown - } -} - -/// Ledger Data -pub enum LedgerData<C> -where - C: transfer::Configuration, -{ - /// Sender Data - Sender(VoidNumber<C>), - - /// Receiver Data - Receiver(Utxo<C>, EncryptedAsset<C>), -} +use core::marker::PhantomData; -/// Ledger Post Entry -pub enum PostEntry<'t, C> +/// Wallet +pub struct Wallet<D, C, M, S, L> where - C: transfer::Configuration, + D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, + M: AssetMap<Key = Index<D>>, + S: signer::Connection<D, C>, + L: ledger::Connection<C>, { - /// Sender Entry - Sender(&'t SenderPost<C>), + /// Asset Distribution + assets: M, - /// Receiver Entry - Receiver(&'t ReceiverPost<C>), -} + /// Signer Connection + signer: S, -/// Local Ledger State -pub trait LocalLedger<C> -where - C: transfer::Configuration, -{ - /// Ledger State Checkpoint Type - type Checkpoint: Default + Ord; - - /// Data Preservation Key - type Key; - - /// Data Access Error - type Error; - - /// Returns the checkpoint of the local ledger. - fn checkpoint(&self) -> &Self::Checkpoint; - - /// Sets the checkpoint of the local ledger to `checkpoint`. - fn set_checkpoint(&mut self, checkpoint: Self::Checkpoint); - - /// Saves `data` into the local ledger. - fn push(&mut self, data: LedgerData<C>) -> Result<(), Self::Error>; - - /// Prepares a `post` in the local ledger, returning a key to decide if the post should - /// be preserved by the ledger or not. - fn prepare(&mut self, post: PostEntry<C>, ownership: Ownership) -> Self::Key; - - /// Preserves the data associated to the `key`. - fn keep(&mut self, key: Self::Key); - - /// Drops the data associated to the `key`. - fn drop(&mut self, key: Self::Key); -} - -/// Wallet Balance State -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "M: Clone, L: Clone"), - Copy(bound = "M: Copy, L: Copy"), - Debug(bound = "M: Debug, L: Debug"), - Default(bound = "M: Default, L: Default"), - Eq(bound = "M: Eq, L: Eq"), - Hash(bound = "M: Hash, L: Hash"), - PartialEq(bound = "M: PartialEq, L: PartialEq") -)] -pub struct BalanceState<C, L, M> -where - C: transfer::Configuration, - L: LocalLedger<C>, - M: AssetMap, -{ - /// Local Ledger + /// Ledger Connection ledger: L, - /// Asset Map - asset_map: M, + /// Ledger Checkpoint + checkpoint: L::Checkpoint, /// Type Parameter Marker - __: PhantomData<C>, + __: PhantomData<(D, C)>, } -impl<C, L, M> BalanceState<C, L, M> +impl<D, C, M, S, L> Wallet<D, C, M, S, L> where - C: transfer::Configuration, - L: LocalLedger<C>, - M: AssetMap, + D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, + M: AssetMap<Key = Index<D>>, + S: signer::Connection<D, C>, + L: ledger::Connection<C>, { /// Returns the current balance associated with this `id`. #[inline] pub fn balance(&self, id: AssetId) -> AssetBalance { - self.asset_map.balance(id) + self.assets.balance(id) } /// Returns true if `self` contains at least `asset.value` of the asset of kind `asset.id`. #[inline] pub fn contains(&self, asset: Asset) -> bool { - self.asset_map.contains(asset) + self.assets.contains(asset) } - /// Returns the [`Checkpoint`](LocalLedger::Checkpoint) associated with the local ledger. + /// Returns the [`Checkpoint`](ledger::Connection::Checkpoint) representing the current state + /// of this wallet. #[inline] pub fn checkpoint(&self) -> &L::Checkpoint { - self.ledger.checkpoint() - } - - /// - #[inline] - pub async fn sign<D, SC>( - &mut self, - signer: &mut SC, - request: signer::Request<D, C>, - ) -> Result<(), signer::Error<D, C>> - where - D: DerivedSecretKeyGenerator<SecretKey = C::SecretKey>, - SC: signer::Connection<D, C>, - { - let signer::Response { owner, transfers } = signer.sign(request).await?; - - let _ = owner; - let _ = transfers; - - todo!() + &self.checkpoint } - /// Synchronizes `self` with the `ledger`. + /// Pulls data from the `ledger`, synchronizing the asset distribution. #[inline] - pub async fn sync<LC>(&mut self, ledger: &LC) -> Result<(), LC::Error> - where - LC: ledger::Connection<C, Checkpoint = L::Checkpoint>, - { - let SyncResponse { - checkpoint, - void_numbers, - utxos, - encrypted_assets, - } = ledger.sync(self.checkpoint()).await?; - - for void_number in void_numbers { - // TODO: Only keep the ones we care about. How do we alert the local ledger? We have to - // communicate with it when we try to send transactions to a ledger source. - // Do we care about keeping void numbers? Maybe only for recovery? - // - let _ = self.ledger.push(LedgerData::Sender(void_number)); + pub async fn sync(&mut self) -> Result<(), Error<D, C, S, L>> { + let PullResponse { checkpoint, data } = self + .ledger + .pull(&self.checkpoint) + .await + .map_err(Error::LedgerError)?; + + // NOTE: We only care about void numbers when we are doing recovery. + // TODO: Add an optimization path here, so we can decide if we want void + // numbers or not when doing a `LedgerConnection::pull`. + let updates = data.into_iter().filter_map(LedgerData::receiver).collect(); + + let SyncResponse { deposit, .. } = self.signer.sync(updates).await?; + + for (key, asset) in deposit { + self.assets.deposit(key, asset); } - for (utxo, encrypted_asset) in utxos.into_iter().zip(encrypted_assets) { - // TODO: Only keep the ones we care about. For internal transactions we know the `utxo` - // ahead of time. For external ones, we have to get the `asset` first. - // - // TODO: Decrypt them on the way in ... threads? For internal transactions we know the - // `encrypted_asset` ahead of time, and we know it decrypted too. For external - // transactions we have to keep all of them since we have to try and decrypt all - // of them. - // - let _ = self - .ledger - .push(LedgerData::Receiver(utxo, encrypted_asset)); - } + // TODO: ... - self.ledger.set_checkpoint(checkpoint); + self.checkpoint = checkpoint; - // FIXME: Deposit into `asset_map`. + // TODO: ... - Ok(()) + todo!() } - /// Sends the `transfers` to the `ledger`. + /// Posts data to the ledger. #[inline] - pub async fn send_transfers<LC>( - &mut self, - transfers: Vec<TransferPost<C>>, - ledger: &LC, - ) -> Result<(), LC::Error> - where - LC: ledger::Connection<C, Checkpoint = L::Checkpoint>, - { - // FIXME: How do to batched transfers? Those above are not necessarily "batched-atomic". - // - // TODO: When sending to the ledger, we really have to set up a backup state in case we - // missed the "send window", and we need to recover. We can also hint to the local - // ledger that we are about to send some transactions and so it should know which - // `utxo`s and such are important to keep when it sees them appear later in the real - // ledger state. - // - // Sender Posts: `void_number` - // Receiver Posts: `utxo`, `encrypted_asset` + pub async fn post(&mut self) -> Result<(), Error<D, C, S, L>> { + // TODO: Should we do a `sync` first or let the user do that? // + // TODO: + // 1. Send transfer request to signer to build transfer posts, setting rollback-checkpoint + // to current state in case the transaction fails. If error, rollback state. + // 2. Send transfer posts to ledger and wait for result. + // 3. If error, rollback signer and return. + // 4. If success, send ok to signer to use new state and update checkpoint / asset + // distribution accordingly + todo!() + } - let mut keys = Vec::new(); - - for transfer in &transfers { - for sender in &transfer.sender_posts { - keys.push( - self.ledger - .prepare(PostEntry::Sender(sender), Default::default()), - ); - } - for receiver in &transfer.receiver_posts { - keys.push( - self.ledger - .prepare(PostEntry::Receiver(receiver), Default::default()), - ); - } - } - - keys.shrink_to_fit(); - - let SendResponse { - checkpoint, - failure_index, - } = ledger.send(transfers).await?; - - // TODO: Revoke keys if the transfer failed, otherwise recover from the failure. - - let _ = failure_index; + /// Returns a new shielded identity to receive external assets at this wallet. + #[inline] + pub async fn new_receiver( + &mut self, + ) -> Result<ShieldedIdentity<C>, signer::Error<D, C, S::Error>> { + self.signer.new_receiver().await + } +} - self.ledger.set_checkpoint(checkpoint); +/// Wallet Error +/// +/// This `enum` is the error state for [`Wallet`] methods. See [`sync`](Wallet::sync) and +/// [`post`](Wallet::post) for more. +pub enum Error<D, C, S, L> +where + D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, + S: signer::Connection<D, C>, + L: ledger::Connection<C> + ?Sized, +{ + /// Signer Error + SignerError(signer::Error<D, C, S::Error>), - // FIXME: Withdraw from `asset_map` + /// Ledger Error + LedgerError(L::Error), +} - todo!() +impl<D, C, S, L> From<signer::Error<D, C, S::Error>> for Error<D, C, S, L> +where + D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, + S: signer::Connection<D, C>, + L: ledger::Connection<C> + ?Sized, +{ + #[inline] + fn from(err: signer::Error<D, C, S::Error>) -> Self { + Self::SignerError(err) } } From c6052201e828b623f61e3d178c44fc79d06a2da0 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 6 Oct 2021 19:40:47 -0400 Subject: [PATCH 078/275] WIP: start implementing sync/pull methods --- manta-accounting/src/identity.rs | 6 ++ manta-accounting/src/keys.rs | 48 ++--------- manta-accounting/src/wallet/ledger.rs | 99 ++++++++++++----------- manta-accounting/src/wallet/signer.rs | 111 ++++++++++++++------------ manta-accounting/src/wallet/state.rs | 19 ++--- 5 files changed, 138 insertions(+), 145 deletions(-) diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 0c76dd57f..f26ec1bd4 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -767,6 +767,12 @@ where Self { identity, asset } } + /// Extracts decrypted asset from `self`. + #[inline] + pub fn into_asset(self) -> Asset { + self.asset + } + /// Builds a new [`PreSender`] for `self`. #[inline] pub fn into_pre_sender(self, commitment_scheme: &C::CommitmentScheme) -> PreSender<C> diff --git a/manta-accounting/src/keys.rs b/manta-accounting/src/keys.rs index 882148d8f..15f32c4be 100644 --- a/manta-accounting/src/keys.rs +++ b/manta-accounting/src/keys.rs @@ -98,42 +98,6 @@ pub trait DerivedSecretKeyGenerator { ) -> Result<Self::SecretKey, Self::Error> { self.generate_key(KeyKind::Internal, account, index) } - - /* TODO[remove]: - /// Builds a [`SecretKeyGenerator`] for external keys associated to `account`. - #[inline] - fn external_keys<'s>(&'s self, account: &'s Self::Account) -> ExternalKeys<'s, Self> { - ExternalKeys::new(self, account) - } - - /// Builds a [`SecretKeyGenerator`] for internal keys associated to `account`. - #[inline] - fn internal_keys<'s>(&'s self, account: &'s Self::Account) -> InternalKeys<'s, Self> { - InternalKeys::new(self, account) - } - - /// Builds a [`SecretKeyGenerator`] for external keys associated to `account`, starting - /// from `index`. - #[inline] - fn external_keys_from_index<'s>( - &'s self, - account: &'s Self::Account, - index: Self::Index, - ) -> ExternalKeys<'s, Self> { - ExternalKeys::from_index(self, account, index) - } - - /// Builds a [`SecretKeyGenerator`] for internal keys associated to `account`, starting - /// from `index`. - #[inline] - fn internal_keys_from_index<'s>( - &'s self, - account: &'s Self::Account, - index: Self::Index, - ) -> InternalKeys<'s, Self> { - InternalKeys::from_index(self, account, index) - } - */ } impl<D> DerivedSecretKeyGenerator for &D @@ -444,6 +408,12 @@ where pub fn collect(self) -> Result<KeyOwned<D, T>, E> { Ok(KeyOwned::new(self.value?, self.index)) } + + /// Converts a `KeyOwned<D, Result<T, E>>` into an `Option<KeyOwned<D, T>>`. + #[inline] + pub fn ok(self) -> Option<KeyOwned<D, T>> { + self.collect().ok() + } } impl<D, T, E> From<KeyOwned<D, Result<T, E>>> for Result<KeyOwned<D, T>, E> @@ -784,7 +754,6 @@ where next_internal(source, &self.account, &mut self.internal_index) } - /* TODO[remove]: /// Returns an [`ExternalKeys`] generator starting from the current external index. #[inline] pub fn external_keys<'s>(&'s self, source: &'s D) -> ExternalKeys<'s, D> { @@ -804,7 +773,7 @@ where source: &'s D, index: D::Index, ) -> ExternalKeys<'s, D> { - source.external_keys_from_index(&self.account, index) + ExternalKeys::from_index(source, &self.account, index) } /// Returns an [`InternalKeys`] generator starting from the given `index`. @@ -814,9 +783,8 @@ where source: &'s D, index: D::Index, ) -> InternalKeys<'s, D> { - source.internal_keys_from_index(&self.account, index) + InternalKeys::from_index(source, &self.account, index) } - */ } impl<D> AsRef<D::Account> for Account<D> diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs index 810d05b65..28021a29b 100644 --- a/manta-accounting/src/wallet/ledger.rs +++ b/manta-accounting/src/wallet/ledger.rs @@ -16,6 +16,8 @@ //! Ledger Source +// TODO: Move to streams so we can process some of the data as it's incoming. + use crate::{ identity::{Utxo, VoidNumber}, transfer::{self, EncryptedAsset, TransferPost}, @@ -31,38 +33,57 @@ where /// Ledger State Checkpoint Type type Checkpoint: Default + Ord; + /// Receiver Data Iterator Type + type ReceiverData: IntoIterator<Item = (Utxo<C>, EncryptedAsset<C>)>; + + /// Sender Data Iterator Type + type SenderData: IntoIterator<Item = VoidNumber<C>>; + /// Pull Future Type /// /// Future for the [`pull`](Self::pull) method. - type PullFuture: Future<Output = Result<PullResponse<C, Self>, Self::Error>>; + type PullFuture: Future<Output = PullResult<C, Self>>; - /// Pull Data Iterator Type - type PullData: IntoIterator<Item = LedgerData<C>>; + /// Pull All Future Type + /// + /// Future for the [`pull_all`](Self::pull_all) method. + type PullAllFuture: Future<Output = PullAllResult<C, Self>>; /// Push Future Type /// /// Future for the [`push`](Self::push) method. - type PushFuture: Future<Output = Result<PushResponse<C, Self>, Self::Error>>; + type PushFuture: Future<Output = PushResult<C, Self>>; /// Error Type type Error; - /// Pulls data from the ledger starting from `checkpoint`, returning the current + /// Pulls receiver data from the ledger starting from `checkpoint`, returning the current /// [`Checkpoint`](Self::Checkpoint). fn pull(&self, checkpoint: &Self::Checkpoint) -> Self::PullFuture; - /// Pulls all of the data from the entire history of the ledger, returning the current - /// [`Checkpoint`](Self::Checkpoint). - #[inline] - fn pull_all(&self) -> Self::PullFuture { - self.pull(&Default::default()) - } + /// Pulls all data from the ledger, returning the current [`Checkpoint`](Self::Checkpoint). + fn pull_all(&self) -> Self::PullAllFuture; /// Sends `transfers` to the ledger, returning the current [`Checkpoint`](Self::Checkpoint) /// and the status of the transfers if successful. fn push(&self, transfers: Vec<TransferPost<C>>) -> Self::PushFuture; } +/// Ledger Source Pull Result +/// +/// See the [`pull`](Connection::pull) method on [`Connection`] for more information. +pub type PullResult<C, L> = Result<PullResponse<C, L>, <L as Connection<C>>::Error>; + +/// Ledger Source Pull All Result +/// +/// See the [`pull_all`](Connection::pull_all) method on [`Connection`] for more information. +pub type PullAllResult<C, L> = Result<PullAllResponse<C, L>, <L as Connection<C>>::Error>; + +/// Ledger Source Push Result +/// +/// See the [`push`](Connection::push) method on [`Connection`] for more information. +pub type PushResult<C, L> = Result<PushResponse<C, L>, <L as Connection<C>>::Error>; + /// Ledger Source Pull Response /// /// This `struct` is created by the [`pull`](Connection::pull) method on [`Connection`]. @@ -75,15 +96,15 @@ where /// Current Ledger Checkpoint pub checkpoint: L::Checkpoint, - /// Ledger Data - pub data: L::PullData, + /// Ledger Receiver Data + pub receiver_data: L::ReceiverData, } -/// Ledger Source Push Response +/// Ledger Source Pull All Response /// -/// This `struct` is created by the [`push`](Connection::push) method on [`Connection`]. +/// This `struct` is created by the [`pull_all`](Connection::pull_all) method on [`Connection`]. /// See its documentation for more. -pub struct PushResponse<C, L> +pub struct PullAllResponse<C, L> where C: transfer::Configuration, L: Connection<C> + ?Sized, @@ -91,41 +112,25 @@ where /// Current Ledger Checkpoint pub checkpoint: L::Checkpoint, - /// Transaction Failed at the Given Index - pub failure_index: Option<usize>, -} - -/// Ledger Data -pub enum LedgerData<C> -where - C: transfer::Configuration, -{ - /// Sender Data - Sender(VoidNumber<C>), + /// Ledger Receiver Data + pub receiver_data: L::ReceiverData, - /// Receiver Data - Receiver(Utxo<C>, EncryptedAsset<C>), + /// Ledger Sender Data + pub sender_data: L::SenderData, } -impl<C> LedgerData<C> +/// Ledger Source Push Response +/// +/// This `struct` is created by the [`push`](Connection::push) method on [`Connection`]. +/// See its documentation for more. +pub struct PushResponse<C, L> where C: transfer::Configuration, + L: Connection<C> + ?Sized, { - /// Extracts the sender data, if `self` matches [`Self::Sender`]. - #[inline] - pub fn sender(self) -> Option<VoidNumber<C>> { - match self { - Self::Sender(void_number) => Some(void_number), - _ => None, - } - } - - /// Extracts the receiver data, if `self` matches [`Self::Receiver`]. - #[inline] - pub fn receiver(self) -> Option<(Utxo<C>, EncryptedAsset<C>)> { - match self { - Self::Receiver(utxo, encryped_asset) => Some((utxo, encryped_asset)), - _ => None, - } - } + /// Current Ledger Checkpoint + pub checkpoint: L::Checkpoint, + + /// Transaction Failure Index + pub failure_index: Option<usize>, } diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 8ec95d118..63736dd1a 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -20,7 +20,9 @@ use crate::{ asset::{Asset, AssetBalance, AssetId, AssetMap}, fs::{Load, LoadWith, Save, SaveWith}, identity::{self, AssetParameters, Identity, Utxo}, - keys::{Account, DerivedSecretKeyGenerator, Index, KeyKind, KeyOwned}, + keys::{ + Account, DerivedSecretKeyGenerator, ExternalKeys, Index, InternalKeys, KeyKind, KeyOwned, + }, transfer::{ self, canonical::{Mint, PrivateTransfer, Reclaim}, @@ -64,17 +66,17 @@ where /// Sync Future Type /// /// Future for the [`sync`](Self::sync) method. - type SyncFuture: Future<Output = SyncResult<D, C, Self::Error>>; + type SyncFuture: Future<Output = SyncResult<D, C, Self>>; /// Sign Future Type /// /// Future for the [`sign`](Self::sign) method. - type SignFuture: Future<Output = SignResult<D, C, Self::Error>>; + type SignFuture: Future<Output = SignResult<D, C, Self>>; /// New Receiver Future Type /// /// Future for the [`new_receiver`](Self::new_receiver) method. - type NewReceiverFuture: Future<Output = NewReceiverResult<D, C, Self::Error>>; + type NewReceiverFuture: Future<Output = NewReceiverResult<D, C, Self>>; /// Error Type type Error; @@ -93,18 +95,20 @@ where /// Synchronization Result /// /// See the [`sync`](Connection::sync) method on [`Connection`] for more information. -pub type SyncResult<D, C, CE> = Result<SyncResponse<D>, Error<D, C, CE>>; +pub type SyncResult<D, C, S> = Result<SyncResponse<D>, Error<D, C, <S as Connection<D, C>>::Error>>; /// Signing Result /// /// See the [`sign`](Connection::sign) method on [`Connection`] for more information. -pub type SignResult<D, C, CE> = Result<SignResponse<D, C>, Error<D, C, CE>>; +pub type SignResult<D, C, S> = + Result<SignResponse<D, C>, Error<D, C, <S as Connection<D, C>>::Error>>; /// New Receiver Generation Result /// /// See the [`new_receiver`](Connection::new_receiver) method on [`Connection`] for more /// information. -pub type NewReceiverResult<D, C, CE> = Result<transfer::ShieldedIdentity<C>, Error<D, C, CE>>; +pub type NewReceiverResult<D, C, S> = + Result<transfer::ShieldedIdentity<C>, Error<D, C, <S as Connection<D, C>>::Error>>; /// Signer Synchronization Response /// @@ -115,10 +119,7 @@ where D: DerivedSecretKeyGenerator, { /// - pub deposit: Vec<(Index<D>, Asset)>, - - /// - pub withdraw: Vec<Index<D>>, + pub deposits: Vec<(Index<D>, Asset)>, } /* TODO: @@ -582,52 +583,51 @@ where todo!() } - /* TODO: Revisit how this is designed (this is part of recovery/ledger-sync): - + /* TODO: /// Returns an [`ExternalKeys`] generator starting from the given `index`. #[inline] fn external_keys_from_index(&self, index: D::Index) -> ExternalKeys<D> { - self.secret_key_source - .external_keys_from_index(self.account.as_ref(), index) + self.account + .external_keys_from_index(&self.secret_key_source, index) } /// Returns an [`InternalKeys`] generator starting from the given `index`. #[inline] fn internal_keys_from_index(&self, index: D::Index) -> InternalKeys<D> { - self.secret_key_source - .internal_keys_from_index(self.account.as_ref(), index) + self.account + .internal_keys_from_index(&self.secret_key_source, index) } /// Looks for an [`OpenSpend`] for this `encrypted_asset` by checking every secret key /// in the iterator. #[inline] - fn find_open_spend_from_iter<C, I, Iter>( + fn find_open_spend_from_iter<C, I>( &self, - encrypted_asset: &EncryptedMessage<I>, - iter: Iter, - ) -> Option<OpenSpend<C>> + encrypted_asset: &EncryptedAsset<C>, + iter: I, + ) -> Option<OpenSpend<D, C>> where - C: identity::Configuration<SecretKey = D::SecretKey>, - I: IntegratedEncryptionScheme<Plaintext = Asset>, - Iter: IntoIterator<Item = D::SecretKey>, + C: transfer::Configuration<SecretKey = D::SecretKey>, + I: IntoIterator<Item = SecretKey<D>>, Standard: Distribution<AssetParameters<C>>, { - iter.into_iter() - .find_map(move |k| Identity::new(k).try_open(encrypted_asset).ok()) + iter.into_iter().find_map(move |k| { + k.map(move |k| Identity::new(k).try_open(encrypted_asset)) + .ok() + }) } /// Looks for an [`OpenSpend`] for this `encrypted_asset`, only trying `gap_limit`-many /// external keys starting from `index`. #[inline] - pub fn find_external_open_spend<C, I>( + pub fn find_external_open_spend<C>( &self, - encrypted_asset: &EncryptedMessage<I>, + encrypted_asset: &EncryptedAsset<C>, index: D::Index, gap_limit: usize, - ) -> Option<OpenSpend<C>> + ) -> Option<OpenSpend<D, C>> where - C: identity::Configuration<SecretKey = D::SecretKey>, - I: IntegratedEncryptionScheme<Plaintext = Asset>, + C: transfer::Configuration<SecretKey = D::SecretKey>, Standard: Distribution<AssetParameters<C>>, { self.find_open_spend_from_iter( @@ -639,15 +639,14 @@ where /// Looks for an [`OpenSpend`] for this `encrypted_asset`, only trying `gap_limit`-many /// internal keys starting from `index`. #[inline] - pub fn find_internal_open_spend<C, I>( + pub fn find_internal_open_spend<C>( &self, - encrypted_asset: &EncryptedMessage<I>, + encrypted_asset: &EncryptedAsset<C>, index: D::Index, gap_limit: usize, - ) -> Option<OpenSpend<C>> + ) -> Option<OpenSpend<D, C>> where - C: identity::Configuration<SecretKey = D::SecretKey>, - I: IntegratedEncryptionScheme<Plaintext = Asset>, + C: transfer::Configuration<SecretKey = D::SecretKey>, Standard: Distribution<AssetParameters<C>>, { self.find_open_spend_from_iter( @@ -659,32 +658,30 @@ where /// Looks for an [`OpenSpend`] for this `encrypted_asset`, only trying `gap_limit`-many /// external and internal keys starting from `external_index` and `internal_index`. #[inline] - pub fn find_open_spend<C, I>( + pub fn find_open_spend<C>( &self, - encrypted_asset: &EncryptedMessage<I>, + encrypted_asset: &EncryptedAsset<C>, external_index: D::Index, internal_index: D::Index, gap_limit: usize, - ) -> Option<(OpenSpend<C>, KeyKind)> + ) -> Option<OpenSpend<D, C>> where - C: identity::Configuration<SecretKey = D::SecretKey>, - I: IntegratedEncryptionScheme<Plaintext = Asset>, + C: transfer::Configuration<SecretKey = D::SecretKey>, Standard: Distribution<AssetParameters<C>>, { // TODO: Find a way to either interleave these or parallelize these. if let Some(spend) = self.find_external_open_spend(encrypted_asset, external_index, gap_limit) { - return Some((spend, KeyKind::External)); + return Some(spend); } if let Some(spend) = self.find_internal_open_spend(encrypted_asset, internal_index, gap_limit) { - return Some((spend, KeyKind::Internal)); + return Some(spend); } None } - */ } @@ -808,7 +805,23 @@ where /// Updates the internal ledger state, returing the new asset distribution. #[inline] - fn sync(&mut self, updates: Vec<(Utxo<C>, EncryptedAsset<C>)>) -> SyncResult<D, C, Infallible> { + fn sync(&mut self, updates: Vec<(Utxo<C>, EncryptedAsset<C>)>) -> SyncResult<D, C, Self> + where + Standard: Distribution<AssetParameters<C>>, + { + /* TODO: + use manta_crypto::Set; + + let mut asset_distribution = Vec::new(); + + for (utxo, encrypted_asset) in updates { + if let Some(open_spend) = self.signer.find_open_spend(encrypted_asset) { + asset_distribution.push((open_spend.index, open_spend.value.into_asset())); + } + let _ = self.utxo_set.try_insert(utxo); + } + */ + // TODO: // 1. Update the utxo_set // 2. Update the asset distribution @@ -818,7 +831,7 @@ where /// Signs the `request`, generating transfer posts. #[inline] - fn sign(&mut self, request: SignRequest<D, C>) -> SignResult<D, C, Infallible> + fn sign(&mut self, request: SignRequest<D, C>) -> SignResult<D, C, Self> where Standard: Distribution<AssetParameters<C>>, { @@ -861,11 +874,11 @@ where R: CryptoRng + RngCore, Standard: Distribution<AssetParameters<C>>, { - type SyncFuture = Ready<SyncResult<D, C, Self::Error>>; + type SyncFuture = Ready<SyncResult<D, C, Self>>; - type SignFuture = Ready<SignResult<D, C, Self::Error>>; + type SignFuture = Ready<SignResult<D, C, Self>>; - type NewReceiverFuture = Ready<NewReceiverResult<D, C, Self::Error>>; + type NewReceiverFuture = Ready<NewReceiverResult<D, C, Self>>; type Error = Infallible; diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index e7679cc98..b7ae91d9d 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -21,7 +21,7 @@ use crate::{ keys::{DerivedSecretKeyGenerator, Index}, transfer::{self, ShieldedIdentity}, wallet::{ - ledger::{self, LedgerData, PullResponse}, + ledger::{self, PullResponse}, signer::{self, SyncResponse}, }, }; @@ -82,20 +82,21 @@ where /// Pulls data from the `ledger`, synchronizing the asset distribution. #[inline] pub async fn sync(&mut self) -> Result<(), Error<D, C, S, L>> { - let PullResponse { checkpoint, data } = self + let PullResponse { + checkpoint, + receiver_data, + } = self .ledger .pull(&self.checkpoint) .await .map_err(Error::LedgerError)?; - // NOTE: We only care about void numbers when we are doing recovery. - // TODO: Add an optimization path here, so we can decide if we want void - // numbers or not when doing a `LedgerConnection::pull`. - let updates = data.into_iter().filter_map(LedgerData::receiver).collect(); + let SyncResponse { deposits } = self + .signer + .sync(receiver_data.into_iter().collect()) + .await?; - let SyncResponse { deposit, .. } = self.signer.sync(updates).await?; - - for (key, asset) in deposit { + for (key, asset) in deposits { self.assets.deposit(key, asset); } From 4fd52eb4bc695f4bea8b4a9da054e54d97b6d4ab Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 7 Oct 2021 02:45:20 -0400 Subject: [PATCH 079/275] add wallet/ledger sync implementation --- manta-accounting/src/asset.rs | 2 +- manta-accounting/src/keys.rs | 465 +++++++++++++++----------- manta-accounting/src/wallet/signer.rs | 241 ++++++------- manta-accounting/src/wallet/state.rs | 22 +- manta-crypto/src/merkle_tree/path.rs | 9 +- manta-pay/src/accounting/keys.rs | 4 +- 6 files changed, 386 insertions(+), 357 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 48f0c4de4..5374e570a 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -661,7 +661,7 @@ impl<M> AssetSelection<M> where M: AssetMap + ?Sized, { - /// Splits [`self.change`] into `n` change components. + /// Splits [`self.change`](Self::change) into `n` change components. #[inline] pub fn split_change(&self, n: usize) -> Option<Change> { self.change.make_change(n) diff --git a/manta-accounting/src/keys.rs b/manta-accounting/src/keys.rs index 15f32c4be..ed52bfd86 100644 --- a/manta-accounting/src/keys.rs +++ b/manta-accounting/src/keys.rs @@ -22,36 +22,10 @@ // TODO: Check to make sure we conform to the specification and then make a note about it in the // module documentation, and add a link to the specification. -use core::{convert::TryFrom, fmt::Debug, hash::Hash}; - -/// Secret Key Generator Trait -pub trait SecretKeyGenerator { - /// Secret Key Type - type SecretKey; - - /// Key Generation Error - type Error; - - /// Generates a new secret key. - fn next_key(&mut self) -> Result<Self::SecretKey, Self::Error>; -} - -impl<S> SecretKeyGenerator for &mut S -where - S: SecretKeyGenerator, -{ - type SecretKey = S::SecretKey; - - type Error = S::Error; - - #[inline] - fn next_key(&mut self) -> Result<Self::SecretKey, Self::Error> { - (*self).next_key() - } -} +use core::{convert::TryFrom, fmt::Debug, hash::Hash, ops::Range}; /// Derived Secret Key Parameter -pub trait DerivedSecretKeyParameter: Clone + Default { +pub trait DerivedSecretKeyParameter: Clone + Default + PartialOrd { /// Increments the key parameter by one unit. fn increment(&mut self); } @@ -165,37 +139,86 @@ impl KeyKind { } } -/// Key Index +/// External Key Kind +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct External; + +impl From<External> for KeyKind { + #[inline] + fn from(kind: External) -> Self { + let _ = kind; + Self::External + } +} + +/// Internal Key Kind +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Internal; + +impl From<Internal> for KeyKind { + #[inline] + fn from(kind: Internal) -> Self { + let _ = kind; + Self::Internal + } +} + +/// External Secret Key Index Type +pub type ExternalIndex<D> = Index<D, External>; + +/// Internal Secret Key Index Type +pub type InternalIndex<D> = Index<D, Internal>; + +/// Secret Key Index #[derive(derivative::Derivative)] #[derivative( - Clone(bound = ""), - Copy(bound = "D::Index: Copy"), - Debug(bound = "D::Index: Debug"), - Eq(bound = "D::Index: Eq"), - Hash(bound = "D::Index: Hash"), - PartialEq(bound = "D::Index: PartialEq") + Clone(bound = "K: Clone"), + Copy(bound = "K: Copy, D::Index: Copy"), + Debug(bound = "K: Debug, D::Index: Debug"), + Eq(bound = "K: Eq, D::Index: Eq"), + Hash(bound = "K: Hash, D::Index: Hash"), + PartialEq(bound = "K: PartialEq") )] -pub struct Index<D> +pub struct Index<D, K = KeyKind> where D: DerivedSecretKeyGenerator, + K: Into<KeyKind>, { /// Key Kind - pub kind: KeyKind, + pub kind: K, /// Key Index pub index: D::Index, } -impl<D> Index<D> +impl<D, K> Index<D, K> where D: DerivedSecretKeyGenerator, + K: Into<KeyKind>, { /// Builds a new [`Index`] for `kind` and `index`. #[inline] - pub fn new(kind: KeyKind, index: D::Index) -> Self { + pub fn new(kind: K, index: D::Index) -> Self { Self { kind, index } } + /// Increments the internal `index`. + #[inline] + pub fn increment(&mut self) { + self.index.increment() + } + + /// Reduces `self` into an [`Index`] with [`KeyKind`] as the key kind. + #[inline] + pub fn reduce(self) -> Index<D> { + Index::new(self.kind.into(), self.index) + } +} + +impl<D> Index<D> +where + D: DerivedSecretKeyGenerator, +{ /// Builds a new [`Index`] for an external key with the given `index`. #[inline] pub fn new_external(index: D::Index) -> Self { @@ -220,92 +243,116 @@ where self.kind.is_internal() } - /// Increments the internal `index`. + /// Gets the underlying key for this `index` at the `account`. #[inline] - pub fn increment(&mut self) { - self.index.increment() + pub fn key(&self, source: &D, account: &D::Account) -> Result<D::SecretKey, D::Error> { + source.generate_key(self.kind, account, &self.index) + } +} + +impl<D> ExternalIndex<D> +where + D: DerivedSecretKeyGenerator, +{ + /// Builds a new [`Index`] for an external key with the given `index`. + #[inline] + pub fn from(index: D::Index) -> Self { + Self::new(External, index) } /// Gets the underlying key for this `index` at the `account`. #[inline] pub fn key(&self, source: &D, account: &D::Account) -> Result<D::SecretKey, D::Error> { - source.generate_key(self.kind, account, &self.index) + source.generate_external_key(account, &self.index) + } +} + +impl<D> InternalIndex<D> +where + D: DerivedSecretKeyGenerator, +{ + /// Builds a new [`Index`] for an internal key with the given `index`. + #[inline] + pub fn from(index: D::Index) -> Self { + Self::new(Internal, index) + } + + /// Gets the underlying key for this `index` at the `account`. + #[inline] + pub fn key(&self, source: &D, account: &D::Account) -> Result<D::SecretKey, D::Error> { + source.generate_internal_key(account, &self.index) } } /// Labelled Secret Key Type -pub type SecretKey<D> = KeyOwned<D, <D as DerivedSecretKeyGenerator>::SecretKey>; +pub type SecretKey<D, K = KeyKind> = KeyOwned<D, <D as DerivedSecretKeyGenerator>::SecretKey, K>; + +/// Labelled External Secret Key Type +pub type ExternalSecretKey<D> = SecretKey<D, External>; + +/// Labelled Internal Secret Key Type +pub type InternalSecretKey<D> = SecretKey<D, Internal>; + +/// External Key-Owned Value Type +pub type ExternalKeyOwned<D, T> = KeyOwned<D, T, External>; + +/// Internal Key-Owned Value Type +pub type InternalKeyOwned<D, T> = KeyOwned<D, T, Internal>; /// Key-Owned Value #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "T: Clone"), - Copy(bound = "D::Index: Copy, T: Copy"), - Debug(bound = "D::Index: Debug, T: Debug"), - Eq(bound = "D::Index: Eq, T: Eq"), - Hash(bound = "D::Index: Hash, T: Hash"), - PartialEq(bound = "D::Index: PartialEq, T: PartialEq") + Clone(bound = "T: Clone, K: Clone"), + Copy(bound = "D::Index: Copy, T: Copy, K: Copy"), + Debug(bound = "D::Index: Debug, T: Debug, K: Debug"), + Eq(bound = "D::Index: Eq, T: Eq, K: Eq"), + Hash(bound = "D::Index: Hash, T: Hash, K: Hash"), + PartialEq(bound = "T: PartialEq, K: PartialEq") )] -pub struct KeyOwned<D, T> +pub struct KeyOwned<D, T, K = KeyKind> where D: DerivedSecretKeyGenerator, + K: Into<KeyKind>, { /// Value Owned by the Key pub value: T, /// Key Index - pub index: Index<D>, + pub index: Index<D, K>, } -impl<D, T> KeyOwned<D, T> +impl<D, T, K> KeyOwned<D, T, K> where D: DerivedSecretKeyGenerator, + K: Into<KeyKind>, { /// Builds a new [`KeyOwned`] for `value` with `index` as the [`Index`]. #[inline] - pub fn new(value: T, index: Index<D>) -> Self { + pub fn new(value: T, index: Index<D, K>) -> Self { Self { value, index } } /// Builds a new [`KeyOwned`] for `value` with `kind` and `index` as the [`Index`]. #[inline] - pub fn with_kind(value: T, kind: KeyKind, index: D::Index) -> Self { + pub fn with_kind(value: T, kind: K, index: D::Index) -> Self { Self::new(value, Index::new(kind, index)) } - /// Builds a new [`KeyOwned`] for `value` for an external key with `index`. - #[inline] - pub fn new_external(value: T, index: D::Index) -> Self { - Self::new(value, Index::new_external(index)) - } - - /// Builds a new [`KeyOwned`] for `value` for an internal key with `index`. + /// Returns the inner [`self.value`](Self::value) dropping the [`self.index`](Self::index). #[inline] - pub fn new_internal(value: T, index: D::Index) -> Self { - Self::new(value, Index::new_internal(index)) - } - - /// Returns `true` if `self` represents a value owned by an external key. - #[inline] - pub fn is_external(&self) -> bool { - self.index.is_external() - } - - /// Returns `true` if `self` represents a value owned by an internal key. - #[inline] - pub fn is_internal(&self) -> bool { - self.index.is_internal() + pub fn unwrap(self) -> T { + self.value } - /// Returns the inner [`self.value`] dropping the [`self.index`]. + /// Reduces `self` into a [`KeyOwned`] with [`KeyKind`] as the key kind. #[inline] - pub fn unwrap(self) -> T { - self.value + pub fn reduce(self) -> KeyOwned<D, T> { + KeyOwned::new(self.value, self.index.reduce()) } /// Maps the underlying value using `f`. #[inline] - pub fn map<U, F>(self, f: F) -> KeyOwned<D, U> + pub fn map<U, F>(self, f: F) -> KeyOwned<D, U, K> where F: FnOnce(T) -> U, { @@ -314,7 +361,7 @@ where /// Maps the underlying value using `f` and then factors over the `Some` branch. #[inline] - pub fn map_some<U, F>(self, f: F) -> Option<KeyOwned<D, U>> + pub fn map_some<U, F>(self, f: F) -> Option<KeyOwned<D, U, K>> where F: FnOnce(T) -> Option<U>, { @@ -323,7 +370,7 @@ where /// Maps the underlying value using `f` and then factors over the `Ok` branch. #[inline] - pub fn map_ok<U, E, F>(self, f: F) -> Result<KeyOwned<D, U>, E> + pub fn map_ok<U, E, F>(self, f: F) -> Result<KeyOwned<D, U, K>, E> where F: FnOnce(T) -> Result<U, E>, { @@ -331,9 +378,39 @@ where } } -impl<D, T> AsRef<T> for KeyOwned<D, T> +impl<D, T> KeyOwned<D, T> where D: DerivedSecretKeyGenerator, +{ + /// Builds a new [`KeyOwned`] for `value` for an external key with `index`. + #[inline] + pub fn new_external(value: T, index: D::Index) -> Self { + Self::new(value, Index::new_external(index)) + } + + /// Builds a new [`KeyOwned`] for `value` for an internal key with `index`. + #[inline] + pub fn new_internal(value: T, index: D::Index) -> Self { + Self::new(value, Index::new_internal(index)) + } + + /// Returns `true` if `self` represents a value owned by an external key. + #[inline] + pub fn is_external(&self) -> bool { + self.index.is_external() + } + + /// Returns `true` if `self` represents a value owned by an internal key. + #[inline] + pub fn is_internal(&self) -> bool { + self.index.is_internal() + } +} + +impl<D, T, K> AsRef<T> for KeyOwned<D, T, K> +where + D: DerivedSecretKeyGenerator, + K: Into<KeyKind>, { #[inline] fn as_ref(&self) -> &T { @@ -341,9 +418,10 @@ where } } -impl<D, T> AsMut<T> for KeyOwned<D, T> +impl<D, T, K> AsMut<T> for KeyOwned<D, T, K> where D: DerivedSecretKeyGenerator, + K: Into<KeyKind>, { #[inline] fn as_mut(&mut self) -> &mut T { @@ -351,89 +429,96 @@ where } } -impl<D, T> From<KeyOwned<D, T>> for (T, Index<D>) +impl<D, T, K> From<KeyOwned<D, T, K>> for (T, Index<D, K>) where D: DerivedSecretKeyGenerator, + K: Into<KeyKind>, { #[inline] - fn from(key_owned: KeyOwned<D, T>) -> Self { + fn from(key_owned: KeyOwned<D, T, K>) -> Self { (key_owned.value, key_owned.index) } } -impl<D, L, R> KeyOwned<D, (L, R)> +impl<D, L, R, K> KeyOwned<D, (L, R), K> where D: DerivedSecretKeyGenerator, + K: Into<KeyKind>, { /// Factors the key index over the left value in the pair. #[inline] - pub fn left(self) -> (KeyOwned<D, L>, R) { + pub fn left(self) -> (KeyOwned<D, L, K>, R) { (KeyOwned::new(self.value.0, self.index), self.value.1) } /// Factors the key index over the right value in the pair. #[inline] - pub fn right(self) -> (L, KeyOwned<D, R>) { + pub fn right(self) -> (L, KeyOwned<D, R, K>) { (self.value.0, KeyOwned::new(self.value.1, self.index)) } } -impl<D, T> KeyOwned<D, Option<T>> +impl<D, T, K> KeyOwned<D, Option<T>, K> where D: DerivedSecretKeyGenerator, + K: Into<KeyKind>, { - /// Converts a `KeyOwned<D, Option<T>>` into an `Option<KeyOwned<D, T>>`. + /// Converts a `KeyOwned<D, Option<T>, K>` into an `Option<KeyOwned<D, T, K>>`. #[inline] - pub fn collect(self) -> Option<KeyOwned<D, T>> { + pub fn collect(self) -> Option<KeyOwned<D, T, K>> { Some(KeyOwned::new(self.value?, self.index)) } } -impl<D, T> From<KeyOwned<D, Option<T>>> for Option<KeyOwned<D, T>> +impl<D, T, K> From<KeyOwned<D, Option<T>, K>> for Option<KeyOwned<D, T, K>> where D: DerivedSecretKeyGenerator, + K: Into<KeyKind>, { #[inline] - fn from(key_owned: KeyOwned<D, Option<T>>) -> Self { + fn from(key_owned: KeyOwned<D, Option<T>, K>) -> Self { key_owned.collect() } } -impl<D, T, E> KeyOwned<D, Result<T, E>> +impl<D, T, E, K> KeyOwned<D, Result<T, E>, K> where D: DerivedSecretKeyGenerator, + K: Into<KeyKind>, { - /// Converts a `KeyOwned<D, Result<T, E>>` into an `Result<KeyOwned<D, T>, E>`. + /// Converts a `KeyOwned<D, Result<T, E>, K>` into an `Result<KeyOwned<D, T, K>, E>`. #[inline] - pub fn collect(self) -> Result<KeyOwned<D, T>, E> { + pub fn collect(self) -> Result<KeyOwned<D, T, K>, E> { Ok(KeyOwned::new(self.value?, self.index)) } - /// Converts a `KeyOwned<D, Result<T, E>>` into an `Option<KeyOwned<D, T>>`. + /// Converts a `KeyOwned<D, Result<T, E>, K>` into an `Option<KeyOwned<D, T, K>>`. #[inline] - pub fn ok(self) -> Option<KeyOwned<D, T>> { + pub fn ok(self) -> Option<KeyOwned<D, T, K>> { self.collect().ok() } } -impl<D, T, E> From<KeyOwned<D, Result<T, E>>> for Result<KeyOwned<D, T>, E> +impl<D, T, E, K> From<KeyOwned<D, Result<T, E>, K>> for Result<KeyOwned<D, T, K>, E> where D: DerivedSecretKeyGenerator, + K: Into<KeyKind>, { #[inline] - fn from(key_owned: KeyOwned<D, Result<T, E>>) -> Self { + fn from(key_owned: KeyOwned<D, Result<T, E>, K>) -> Self { key_owned.collect() } } -impl<D, T, E> TryFrom<KeyOwned<D, Result<T, E>>> for KeyOwned<D, T> +impl<D, T, E, K> TryFrom<KeyOwned<D, Result<T, E>, K>> for KeyOwned<D, T, K> where D: DerivedSecretKeyGenerator, + K: Into<KeyKind>, { type Error = E; #[inline] - fn try_from(key_owned: KeyOwned<D, Result<T, E>>) -> Result<Self, Self::Error> { + fn try_from(key_owned: KeyOwned<D, Result<T, E>, K>) -> Result<Self, Self::Error> { key_owned.collect() } } @@ -466,12 +551,14 @@ pub fn next_external<D>( source: &D, account: &D::Account, index: &mut D::Index, -) -> Result<SecretKey<D>, D::Error> +) -> Result<ExternalSecretKey<D>, D::Error> where D: DerivedSecretKeyGenerator, { - let secret_key = - SecretKey::new_external(source.generate_external_key(account, index)?, index.clone()); + let secret_key = ExternalSecretKey::new( + source.generate_external_key(account, index)?, + Index::new(External, index.clone()), + ); index.increment(); Ok(secret_key) } @@ -483,12 +570,14 @@ pub fn next_internal<D>( source: &D, account: &D::Account, index: &mut D::Index, -) -> Result<SecretKey<D>, D::Error> +) -> Result<InternalSecretKey<D>, D::Error> where D: DerivedSecretKeyGenerator, { - let secret_key = - SecretKey::new_internal(source.generate_internal_key(account, index)?, index.clone()); + let secret_key = InternalSecretKey::new( + source.generate_internal_key(account, index)?, + Index::new(Internal, index.clone()), + ); index.increment(); Ok(secret_key) } @@ -532,14 +621,14 @@ where /// Generates an external secret key according to the [`DerivedSecretKeyGenerator`] protocol /// and increments the running `self.index`. #[inline] - fn generate_external_key(&mut self) -> Result<SecretKey<D>, D::Error> { + fn generate_external_key(&mut self) -> Result<ExternalSecretKey<D>, D::Error> { next_external(self.derived_key_generator, self.account, &mut self.index) } /// Generates an internal secret key according to the [`DerivedSecretKeyGenerator`] protocol /// and increments the running `self.index`. #[inline] - fn generate_internal_key(&mut self) -> Result<SecretKey<D>, D::Error> { + fn generate_internal_key(&mut self) -> Result<InternalSecretKey<D>, D::Error> { next_internal(self.derived_key_generator, self.account, &mut self.index) } } @@ -566,29 +655,15 @@ where } } -impl<'d, D> SecretKeyGenerator for ExternalKeys<'d, D> -where - D: DerivedSecretKeyGenerator, -{ - type SecretKey = SecretKey<D>; - - type Error = D::Error; - - #[inline] - fn next_key(&mut self) -> Result<Self::SecretKey, Self::Error> { - self.0.generate_external_key() - } -} - impl<'d, D> Iterator for ExternalKeys<'d, D> where D: DerivedSecretKeyGenerator, { - type Item = Result<SecretKey<D>, D::Error>; + type Item = Result<ExternalSecretKey<D>, D::Error>; #[inline] fn next(&mut self) -> Option<Self::Item> { - Some(self.next_key()) + Some(self.0.generate_external_key()) } } @@ -614,29 +689,15 @@ where } } -impl<'d, D> SecretKeyGenerator for InternalKeys<'d, D> -where - D: DerivedSecretKeyGenerator, -{ - type SecretKey = SecretKey<D>; - - type Error = D::Error; - - #[inline] - fn next_key(&mut self) -> Result<Self::SecretKey, Self::Error> { - self.0.generate_internal_key() - } -} - impl<'d, D> Iterator for InternalKeys<'d, D> where D: DerivedSecretKeyGenerator, { - type Item = Result<SecretKey<D>, D::Error>; + type Item = Result<InternalSecretKey<D>, D::Error>; #[inline] fn next(&mut self) -> Option<Self::Item> { - Some(self.next_key()) + Some(self.0.generate_internal_key()) } } @@ -644,12 +705,11 @@ where #[derive(derivative::Derivative)] #[derivative( Clone(bound = ""), - Copy(bound = "D::Account: Copy, D::Index: Copy"), Debug(bound = "D::Account: Debug, D::Index: Debug"), Default(bound = ""), Eq(bound = "D::Account: Eq, D::Index: Eq"), Hash(bound = "D::Account: Hash, D::Index: Hash"), - PartialEq(bound = "D::Account: PartialEq, D::Index: PartialEq") + PartialEq(bound = "") )] pub struct Account<D> where @@ -658,8 +718,8 @@ where /// Account Identifier pub account: D::Account, - /// External Transaction Running Index - pub external_index: D::Index, + /// External Transaction Index Range + pub external_index_range: Range<D::Index>, /// Internal Transaction Running Index pub internal_index: D::Index, @@ -676,16 +736,16 @@ where } /// Builds a new [`Account`] for the given `account` identifier with starting indices - /// `external_index` and `internal_index`. + /// `external_index_range` and `internal_index`. #[inline] pub fn with_indices( account: D::Account, - external_index: D::Index, + external_index_range: Range<D::Index>, internal_index: D::Index, ) -> Self { Self { account, - external_index, + external_index_range, internal_index, } } @@ -700,7 +760,7 @@ where /// Resets the external and internal running indices to their default values. #[inline] pub fn reset(&mut self) -> &mut Self { - self.external_index = Default::default(); + self.external_index_range = Default::default(); self.internal_index = Default::default(); self } @@ -709,26 +769,26 @@ where #[inline] pub fn key(&self, source: &D, kind: KeyKind) -> Result<SecretKey<D>, D::Error> { match kind { - KeyKind::External => self.external_key(source), - KeyKind::Internal => self.internal_key(source), + KeyKind::External => Ok(self.external_key(source)?.reduce()), + KeyKind::Internal => Ok(self.internal_key(source)?.reduce()), } } /// Generates a new external key for this account. #[inline] - pub fn external_key(&self, source: &D) -> Result<SecretKey<D>, D::Error> { - Ok(SecretKey::new_external( - source.generate_external_key(&self.account, &self.external_index)?, - self.external_index.clone(), + pub fn external_key(&self, source: &D) -> Result<ExternalSecretKey<D>, D::Error> { + Ok(SecretKey::new( + source.generate_external_key(&self.account, &self.external_index_range.end)?, + Index::new(External, self.external_index_range.end.clone()), )) } /// Generates a new internal key for this account. #[inline] - pub fn internal_key(&self, source: &D) -> Result<SecretKey<D>, D::Error> { - Ok(SecretKey::new_internal( + pub fn internal_key(&self, source: &D) -> Result<InternalSecretKey<D>, D::Error> { + Ok(SecretKey::new( source.generate_internal_key(&self.account, &self.internal_index)?, - self.internal_index.clone(), + Index::new(Internal, self.internal_index.clone()), )) } @@ -737,62 +797,87 @@ where #[inline] pub fn next_key(&mut self, source: &D, kind: KeyKind) -> Result<SecretKey<D>, D::Error> { match kind { - KeyKind::External => self.next_external_key(source), - KeyKind::Internal => self.next_internal_key(source), + KeyKind::External => Ok(self.next_external_key(source)?.reduce()), + KeyKind::Internal => Ok(self.next_internal_key(source)?.reduce()), } } /// Generates the next external key for this account, incrementing the `external_index`. #[inline] - pub fn next_external_key(&mut self, source: &D) -> Result<SecretKey<D>, D::Error> { - next_external(source, &self.account, &mut self.external_index) + pub fn next_external_key(&mut self, source: &D) -> Result<ExternalSecretKey<D>, D::Error> { + next_external(source, &self.account, &mut self.external_index_range.end) } /// Generates the next internal key for this account, incrementing the `internal_index`. #[inline] - pub fn next_internal_key(&mut self, source: &D) -> Result<SecretKey<D>, D::Error> { + pub fn next_internal_key(&mut self, source: &D) -> Result<InternalSecretKey<D>, D::Error> { next_internal(source, &self.account, &mut self.internal_index) } - /// Returns an [`ExternalKeys`] generator starting from the current external index. + /// Returns an iterator over the external keys generated by the indices in + /// [`self.external_index_range`](Self::external_index_range). #[inline] - pub fn external_keys<'s>(&'s self, source: &'s D) -> ExternalKeys<'s, D> { - self.external_keys_from_index(source, self.external_index.clone()) + pub fn external_keys<'s>(&'s self, source: &'s D) -> ExternalKeysRange<'s, D> { + ExternalKeysRange { + source, + account: &self.account, + range: self.external_index_range.clone(), + } } - /// Returns an [`InternalKeys`] generator starting from the current internal index. + /// Increments the start of the [`self.external_index_range`](Self::external_index_range) if + /// `index` is equal to the start of the range. #[inline] - pub fn internal_keys<'s>(&'s self, source: &'s D) -> InternalKeys<'s, D> { - self.internal_keys_from_index(source, self.internal_index.clone()) + pub fn conditional_update_external_key_range(&mut self, index: &D::Index) { + if &self.external_index_range.start == index { + self.external_index_range.start.increment() + } } +} - /// Returns an [`ExternalKeys`] generator starting from the given `index`. +impl<D> AsRef<D::Account> for Account<D> +where + D: DerivedSecretKeyGenerator, +{ #[inline] - pub fn external_keys_from_index<'s>( - &'s self, - source: &'s D, - index: D::Index, - ) -> ExternalKeys<'s, D> { - ExternalKeys::from_index(source, &self.account, index) + fn as_ref(&self) -> &D::Account { + &self.account } +} - /// Returns an [`InternalKeys`] generator starting from the given `index`. - #[inline] - pub fn internal_keys_from_index<'s>( - &'s self, - source: &'s D, - index: D::Index, - ) -> InternalKeys<'s, D> { - InternalKeys::from_index(source, &self.account, index) - } +/// External Keys Range Iterator +/// +/// This `struct` is created by the [`external_keys`](Account::external_keys) method on [`Account`]. +/// See its documentation for more. +pub struct ExternalKeysRange<'d, D> +where + D: DerivedSecretKeyGenerator, +{ + /// Derived Secret Key Generator + source: &'d D, + + /// Key Account + account: &'d D::Account, + + /// Index Range + range: Range<D::Index>, } -impl<D> AsRef<D::Account> for Account<D> +impl<'d, D> Iterator for ExternalKeysRange<'d, D> where D: DerivedSecretKeyGenerator, { + type Item = Result<ExternalSecretKey<D>, D::Error>; + #[inline] - fn as_ref(&self) -> &D::Account { - &self.account + fn next(&mut self) -> Option<Self::Item> { + if self.range.is_empty() { + return None; + } + Some(next_external( + self.source, + self.account, + &mut self.range.end, + )) } } diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 63736dd1a..770c0a78c 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -17,11 +17,12 @@ //! Wallet Signer use crate::{ - asset::{Asset, AssetBalance, AssetId, AssetMap}, + asset::{Asset, AssetBalance, AssetId}, fs::{Load, LoadWith, Save, SaveWith}, identity::{self, AssetParameters, Identity, Utxo}, keys::{ - Account, DerivedSecretKeyGenerator, ExternalKeys, Index, InternalKeys, KeyKind, KeyOwned, + Account, DerivedSecretKeyGenerator, External, ExternalIndex, ExternalKeyOwned, Index, + Internal, InternalIndex, InternalKeyOwned, KeyKind, KeyOwned, }, transfer::{ self, @@ -36,6 +37,7 @@ use core::{ fmt::Debug, future::{self, Future, Ready}, hash::Hash, + ops::Range, }; use rand::{ distributions::{Distribution, Standard}, @@ -43,19 +45,25 @@ use rand::{ }; /// Key-Owned Pre-Sender Type -pub type PreSender<D, C> = KeyOwned<D, identity::PreSender<C>>; +pub type PreSender<D, C> = InternalKeyOwned<D, identity::PreSender<C>>; /// Key-Owned Shielded Identity Type -pub type ShieldedIdentity<D, C> = KeyOwned<D, transfer::ShieldedIdentity<C>>; +pub type ShieldedIdentity<D, C> = ExternalKeyOwned<D, transfer::ShieldedIdentity<C>>; /// Key-Owned Internal Receiver Type -pub type InternalReceiver<D, C> = KeyOwned< +pub type InternalReceiver<D, C> = InternalKeyOwned< D, identity::InternalReceiver<C, <C as transfer::Configuration>::IntegratedEncryptionScheme>, >; /// Key-Owned Open Spend Type -pub type OpenSpend<D, C> = KeyOwned<D, identity::OpenSpend<C>>; +pub type OpenSpend<D, C, K = KeyKind> = KeyOwned<D, identity::OpenSpend<C>, K>; + +/// External Key-Owned Open Spend Type +pub type ExternalOpenSpend<D, C> = OpenSpend<D, C, External>; + +/// Internal Key-Owned Open Spend Type +pub type InternalOpenSpend<D, C> = OpenSpend<D, C, Internal>; /// Signer Connection pub trait Connection<D, C> @@ -83,7 +91,9 @@ where /// Pushes updates from the ledger to the wallet, synchronizing it with the ledger state and /// returning an updated asset distribution. - fn sync(&mut self, updates: Vec<(Utxo<C>, EncryptedAsset<C>)>) -> Self::SyncFuture; + fn sync<I>(&mut self, updates: I) -> Self::SyncFuture + where + I: IntoIterator<Item = (Utxo<C>, EncryptedAsset<C>)>; /// Signs a transfer `request` and returns the ledger transfer posts if successful. fn sign(&mut self, request: SignRequest<D, C>) -> Self::SignFuture; @@ -112,14 +122,14 @@ pub type NewReceiverResult<D, C, S> = /// Signer Synchronization Response /// -/// This `struct` is created by the [`sync`](Connection::sync) methon on [`Connection`]. +/// This `struct` is created by the [`sync`](Connection::sync) method on [`Connection`]. /// See its documentation for more. pub struct SyncResponse<D> where D: DerivedSecretKeyGenerator, { - /// - pub deposits: Vec<(Index<D>, Asset)>, + /// Deposit Asset Distribution + pub deposits: Vec<(ExternalIndex<D>, Asset)>, } /* TODO: @@ -268,17 +278,17 @@ where } /// Builds a new [`Signer`] for `account` from a `secret_key_source` with starting indices - /// `external_index` and `internal_index`. + /// `external_index_range` and `internal_index`. #[inline] pub fn with_indices( secret_key_source: D, account: D::Account, - external_index: D::Index, + external_index_range: Range<D::Index>, internal_index: D::Index, ) -> Self { Self::with_account( secret_key_source, - Account::with_indices(account, external_index, internal_index), + Account::with_indices(account, external_index_range, internal_index), ) } @@ -299,11 +309,33 @@ where .map(Identity::new) } + /// Returns the identity for an external key of the given `index`. + #[inline] + pub fn get_external<C>(&self, index: &ExternalIndex<D>) -> Result<Identity<C>, D::Error> + where + C: identity::Configuration<SecretKey = D::SecretKey>, + { + index + .key(&self.secret_key_source, self.account.as_ref()) + .map(Identity::new) + } + + /// Returns the identity for an internal key of the given `index`. + #[inline] + pub fn get_internal<C>(&self, index: &InternalIndex<D>) -> Result<Identity<C>, D::Error> + where + C: identity::Configuration<SecretKey = D::SecretKey>, + { + index + .key(&self.secret_key_source, self.account.as_ref()) + .map(Identity::new) + } + /// Returns a [`PreSender`] for the key at the given `index`. #[inline] pub fn get_pre_sender<C>( &self, - index: Index<D>, + index: InternalIndex<D>, commitment_scheme: &C::CommitmentScheme, asset: Asset, ) -> Result<PreSender<D, C>, D::Error> @@ -312,7 +344,8 @@ where Standard: Distribution<AssetParameters<C>>, { Ok(KeyOwned::new( - self.get(&index)?.into_pre_sender(commitment_scheme, asset), + self.get_internal(&index)? + .into_pre_sender(commitment_scheme, asset), index, )) } @@ -331,7 +364,9 @@ where /// Generates the next external identity for this signer. #[inline] - pub fn next_external_identity<C>(&mut self) -> Result<KeyOwned<D, Identity<C>>, D::Error> + pub fn next_external_identity<C>( + &mut self, + ) -> Result<ExternalKeyOwned<D, Identity<C>>, D::Error> where C: identity::Configuration<SecretKey = D::SecretKey>, { @@ -343,7 +378,9 @@ where /// Generates the next internal identity for this signer. #[inline] - pub fn next_internal_identity<C>(&mut self) -> Result<KeyOwned<D, Identity<C>>, D::Error> + pub fn next_internal_identity<C>( + &mut self, + ) -> Result<InternalKeyOwned<D, Identity<C>>, D::Error> where C: identity::Configuration<SecretKey = D::SecretKey>, { @@ -473,7 +510,7 @@ where commitment_scheme: &C::CommitmentScheme, asset: Asset, rng: &mut R, - ) -> InternalReceiverResult<D, C, (Mint<C>, OpenSpend<D, C>)> + ) -> InternalReceiverResult<D, C, (Mint<C>, InternalOpenSpend<D, C>)> where C: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, @@ -495,7 +532,7 @@ where commitment_scheme: &C::CommitmentScheme, asset_id: AssetId, rng: &mut R, - ) -> InternalReceiverResult<D, C, (Mint<C>, OpenSpend<D, C>)> + ) -> InternalReceiverResult<D, C, (Mint<C>, InternalOpenSpend<D, C>)> where C: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, @@ -579,110 +616,34 @@ where R: CryptoRng + RngCore + ?Sized, { let _ = (commitment_scheme, asset, rng); - // TODO: Reclaim::build(senders, receiver, reclaim); todo!() } - /* TODO: - /// Returns an [`ExternalKeys`] generator starting from the given `index`. - #[inline] - fn external_keys_from_index(&self, index: D::Index) -> ExternalKeys<D> { - self.account - .external_keys_from_index(&self.secret_key_source, index) - } - - /// Returns an [`InternalKeys`] generator starting from the given `index`. + /// Looks for an [`OpenSpend`] for this `encrypted_asset`. #[inline] - fn internal_keys_from_index(&self, index: D::Index) -> InternalKeys<D> { - self.account - .internal_keys_from_index(&self.secret_key_source, index) - } - - /// Looks for an [`OpenSpend`] for this `encrypted_asset` by checking every secret key - /// in the iterator. - #[inline] - fn find_open_spend_from_iter<C, I>( - &self, + pub fn find_next_external_open_spend<C>( + &mut self, encrypted_asset: &EncryptedAsset<C>, - iter: I, - ) -> Option<OpenSpend<D, C>> + ) -> Option<ExternalOpenSpend<D, C>> where C: transfer::Configuration<SecretKey = D::SecretKey>, - I: IntoIterator<Item = SecretKey<D>>, Standard: Distribution<AssetParameters<C>>, { - iter.into_iter().find_map(move |k| { - k.map(move |k| Identity::new(k).try_open(encrypted_asset)) + let open_spend = self + .account + .external_keys(&self.secret_key_source) + .find_map(move |ek| { + ek.map(move |ek| { + ek.map(move |k| Identity::new(k).try_open(encrypted_asset)) + .ok() + }) .ok() - }) - } - - /// Looks for an [`OpenSpend`] for this `encrypted_asset`, only trying `gap_limit`-many - /// external keys starting from `index`. - #[inline] - pub fn find_external_open_spend<C>( - &self, - encrypted_asset: &EncryptedAsset<C>, - index: D::Index, - gap_limit: usize, - ) -> Option<OpenSpend<D, C>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - Standard: Distribution<AssetParameters<C>>, - { - self.find_open_spend_from_iter( - encrypted_asset, - self.external_keys_from_index(index).take(gap_limit), - ) - } - - /// Looks for an [`OpenSpend`] for this `encrypted_asset`, only trying `gap_limit`-many - /// internal keys starting from `index`. - #[inline] - pub fn find_internal_open_spend<C>( - &self, - encrypted_asset: &EncryptedAsset<C>, - index: D::Index, - gap_limit: usize, - ) -> Option<OpenSpend<D, C>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - Standard: Distribution<AssetParameters<C>>, - { - self.find_open_spend_from_iter( - encrypted_asset, - self.internal_keys_from_index(index).take(gap_limit), - ) - } - - /// Looks for an [`OpenSpend`] for this `encrypted_asset`, only trying `gap_limit`-many - /// external and internal keys starting from `external_index` and `internal_index`. - #[inline] - pub fn find_open_spend<C>( - &self, - encrypted_asset: &EncryptedAsset<C>, - external_index: D::Index, - internal_index: D::Index, - gap_limit: usize, - ) -> Option<OpenSpend<D, C>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - Standard: Distribution<AssetParameters<C>>, - { - // TODO: Find a way to either interleave these or parallelize these. - if let Some(spend) = - self.find_external_open_spend(encrypted_asset, external_index, gap_limit) - { - return Some(spend); - } - if let Some(spend) = - self.find_internal_open_spend(encrypted_asset, internal_index, gap_limit) - { - return Some(spend); - } - None + .flatten() + })?; + self.account + .conditional_update_external_key_range(&open_spend.index.index); + Some(open_spend) } - */ } impl<D> Load for Signer<D> @@ -726,11 +687,10 @@ where } /// Full Signer -pub struct FullSigner<D, C, M, R> +pub struct FullSigner<D, C, R> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, - M: AssetMap<Key = Index<D>>, R: CryptoRng + RngCore, { /// Signer @@ -745,18 +705,14 @@ where /// UTXO Set utxo_set: C::UtxoSet, - /// Asset Distribution - assets: M, - /// Random Number Generator rng: R, } -impl<D, C, M, R> FullSigner<D, C, M, R> +impl<D, C, R> FullSigner<D, C, R> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, - M: AssetMap<Key = Index<D>>, R: CryptoRng + RngCore, { /// Builds a new [`FullSigner`]. @@ -766,7 +722,6 @@ where commitment_scheme: C::CommitmentScheme, proving_context: ProvingContext<C>, utxo_set: C::UtxoSet, - assets: M, rng: R, ) -> Self { Self { @@ -774,13 +729,12 @@ where commitment_scheme, proving_context, utxo_set, - assets, rng, } } /// Builds a new [`FullSigner`] from `secret_key_source`, `account`, `commitment_scheme`, - /// `proving_context`, and `rng`, using a default [`Utxo`] set and empty asset distribution. + /// `proving_context`, and `rng`, using a default [`Utxo`] set. #[inline] pub fn new( secret_key_source: D, @@ -791,42 +745,37 @@ where ) -> Self where C::UtxoSet: Default, - M: Default, { Self::new_inner( Signer::new(secret_key_source, account), commitment_scheme, proving_context, Default::default(), - Default::default(), rng, ) } /// Updates the internal ledger state, returing the new asset distribution. #[inline] - fn sync(&mut self, updates: Vec<(Utxo<C>, EncryptedAsset<C>)>) -> SyncResult<D, C, Self> + fn sync<I>(&mut self, updates: I) -> SyncResult<D, C, Self> where + I: IntoIterator<Item = (Utxo<C>, EncryptedAsset<C>)>, Standard: Distribution<AssetParameters<C>>, { - /* TODO: - use manta_crypto::Set; - - let mut asset_distribution = Vec::new(); + use manta_crypto::Set; // FIXME: move up to top of file + let mut deposits = Vec::new(); for (utxo, encrypted_asset) in updates { - if let Some(open_spend) = self.signer.find_open_spend(encrypted_asset) { - asset_distribution.push((open_spend.index, open_spend.value.into_asset())); + // TODO: Add optimization path where we have "strong" and "weak" insertions into the + // `utxo_set`. If the `utxo` is accompanied by an `encrypted_asset` then we + // "strong insert", if not we "weak insert". + // + if let Some(open_spend) = self.signer.find_next_external_open_spend(&encrypted_asset) { + deposits.push((open_spend.index, open_spend.value.into_asset())); } - let _ = self.utxo_set.try_insert(utxo); + let _ = self.utxo_set.try_insert(utxo); // FIXME: Should this ever error? } - */ - - // TODO: - // 1. Update the utxo_set - // 2. Update the asset distribution - // 3. Return asset distribution changes - todo!() + Ok(SyncResponse { deposits }) } /// Signs the `request`, generating transfer posts. @@ -850,27 +799,26 @@ where .map_err(Error::ProofSystemError)?; Ok(SignResponse::new( asset.id, - vec![(open_spend.index, asset.value)], + vec![(open_spend.index.reduce(), asset.value)], vec![mint_post], )) } SignRequest::PrivateTransfer(asset, senders, receiver) => { - // + // TODO: implement todo!() } SignRequest::Reclaim(asset, senders) => { - // + // TODO: implement todo!() } } } } -impl<D, C, M, R> Connection<D, C> for FullSigner<D, C, M, R> +impl<D, C, R> Connection<D, C> for FullSigner<D, C, R> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, - M: AssetMap<Key = Index<D>>, R: CryptoRng + RngCore, Standard: Distribution<AssetParameters<C>>, { @@ -883,7 +831,10 @@ where type Error = Infallible; #[inline] - fn sync(&mut self, updates: Vec<(Utxo<C>, EncryptedAsset<C>)>) -> Self::SyncFuture { + fn sync<I>(&mut self, updates: I) -> Self::SyncFuture + where + I: IntoIterator<Item = (Utxo<C>, EncryptedAsset<C>)>, + { future::ready(self.sync(updates)) } diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index b7ae91d9d..c0576bf9a 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -22,7 +22,7 @@ use crate::{ transfer::{self, ShieldedIdentity}, wallet::{ ledger::{self, PullResponse}, - signer::{self, SyncResponse}, + signer, }, }; use core::marker::PhantomData; @@ -79,7 +79,7 @@ where &self.checkpoint } - /// Pulls data from the `ledger`, synchronizing the asset distribution. + /// Pulls data from the `ledger`, synchronizing the wallet and asset distribution. #[inline] pub async fn sync(&mut self) -> Result<(), Error<D, C, S, L>> { let PullResponse { @@ -90,23 +90,11 @@ where .pull(&self.checkpoint) .await .map_err(Error::LedgerError)?; - - let SyncResponse { deposits } = self - .signer - .sync(receiver_data.into_iter().collect()) - .await?; - - for (key, asset) in deposits { - self.assets.deposit(key, asset); + for (key, asset) in self.signer.sync(receiver_data).await?.deposits { + self.assets.deposit(key.reduce(), asset); } - - // TODO: ... - self.checkpoint = checkpoint; - - // TODO: ... - - todo!() + Ok(()) } /// Posts data to the ledger. diff --git a/manta-crypto/src/merkle_tree/path.rs b/manta-crypto/src/merkle_tree/path.rs index 7e63ab5be..3384a705f 100644 --- a/manta-crypto/src/merkle_tree/path.rs +++ b/manta-crypto/src/merkle_tree/path.rs @@ -312,8 +312,8 @@ where root == &self.root(parameters, leaf_digest, sibling_digest) } - /// Returns an iterator over the elements of [`self.path`] as [`InnerNode`] objects, yielding - /// elements of the path if they are not default. + /// Returns an iterator over the elements of [`self.path`](Self::path) as [`InnerNode`] + /// objects, yielding elements of the path if they are not default. #[inline] pub fn into_nodes(self) -> CurrentInnerPathNodeIter<C> { CurrentInnerPathNodeIter::new(self) @@ -870,6 +870,9 @@ where /// Uncompresses a path by re-inserting the default values into [`self.inner_path`] at the /// indices described by [`self.sentinel_ranges`]. + /// + /// [`self.inner_path`]: Self::inner_path + /// [`self.sentinel_ranges`]: Self::sentinel_ranges #[inline] pub fn uncompress(mut self) -> Path<C> { let path_length = path_length::<C>(); @@ -919,6 +922,8 @@ where { /// Compresses [`self.inner_path`] by removing all default values and saving only the positions /// in the path of those default values. + /// + /// [`self.inner_path`]: Self::inner_path #[inline] pub fn compress(self) -> CompressedPath<C> { let default = Default::default(); diff --git a/manta-pay/src/accounting/keys.rs b/manta-pay/src/accounting/keys.rs index ba98ae871..693b73b28 100644 --- a/manta-pay/src/accounting/keys.rs +++ b/manta-pay/src/accounting/keys.rs @@ -129,13 +129,13 @@ macro_rules! impl_parameter { type ParameterType = u128; /// Account Parameter -#[derive(Clone, Copy, Default)] +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct AccountParameter(ParameterType); impl_parameter!(AccountParameter); /// Index Parameter -#[derive(Clone, Copy, Default)] +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct IndexParameter(ParameterType); impl_parameter!(IndexParameter); From 23df88e2d51d761eecbebbfbcd09a8aee69815a5 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 7 Oct 2021 16:17:49 -0400 Subject: [PATCH 080/275] implement ledger posting --- manta-accounting/src/asset.rs | 54 ++++--- manta-accounting/src/wallet/ledger.rs | 25 ++-- manta-accounting/src/wallet/signer.rs | 200 ++++++++++++++++--------- manta-accounting/src/wallet/state.rs | 204 ++++++++++++++++++++------ manta-pay/src/lib.rs | 2 + 5 files changed, 339 insertions(+), 146 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 5374e570a..4ec5c3751 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -584,25 +584,19 @@ impl<const N: usize> TryFrom<[Asset; N]> for AssetCollection<N> { pub trait AssetMap { /// Key Type /// - /// Keys are used to access the underlying asset balances. See [`withdraw`](Self::withdraw) - /// and [`deposit`](Self::deposit) for uses of the [`Key`](Self::Key) type. + /// Keys are used to access the underlying asset balances. See [`select`](Self::select) + /// and [`insert`](Self::insert) for uses of the [`Key`](Self::Key) type. type Key; - /// Asset Selection Iterator Type - /// - /// This type is returned by [`select`](Self::select) when looking for assets in the map. - type Selection: Iterator<Item = (Self::Key, AssetBalance)>; - /// Asset Iterator Type /// /// This type is returned by [`iter`](Self::iter) when iterating over all assets. type Iter: Iterator<Item = (Self::Key, Asset)>; - /// Selects asset keys which total up to at least `asset` in value. + /// Asset Selection Iterator Type /// - /// See [`iter`](Self::iter) for iterating over all the assets in the map instead of a specific - /// subset summing to the `asset` total. - fn select(&self, asset: Asset) -> AssetSelection<Self>; + /// This type is returned by [`select`](Self::select) when looking for assets in the map. + type Selection: Iterator<Item = (Self::Key, AssetBalance)>; /// Returns an iterator over all the assets stored in the map. /// @@ -610,12 +604,36 @@ pub trait AssetMap { /// `asset` total. fn iter(&self) -> Self::Iter; - /// Withdraws the asset stored at `key`. - fn withdraw(&mut self, key: Self::Key); + /// Selects asset keys which total up to at least `asset` in value, removing them from + /// the asset distribution. If there are not enough keys to total `asset`, then this method + /// returns `None`. + /// + /// See [`iter`](Self::iter) for iterating over all the assets in the map instead of a specific + /// subset summing to the `asset` total. + fn select(&mut self, asset: Asset) -> Option<AssetSelection<Self>>; + + /// Inserts `asset` at the key stored at `key`. + fn insert(&mut self, key: Self::Key, asset: Asset); + + /// Inserts all of the assets in `iter`. + #[inline] + fn insert_all<I>(&mut self, iter: I) + where + I: IntoIterator<Item = (Self::Key, Asset)>, + { + for (key, asset) in iter { + self.insert(key, asset) + } + } - /// Deposits `asset` at the key stored at `kind` and `index`, returning `false` if the `key` - /// was already assigned to some other [`Asset`]. - fn deposit(&mut self, key: Self::Key, asset: Asset) -> bool; + /// Inserts all of the balances in `iter` using the same `id`. + #[inline] + fn insert_all_same<I>(&mut self, id: AssetId, iter: I) + where + I: IntoIterator<Item = (Self::Key, AssetBalance)>, + { + self.insert_all(iter.into_iter().map(move |(k, b)| (k, b.with(id)))) + } /// Returns the current balance associated with this `id`. #[inline] @@ -653,8 +671,8 @@ where /// Change Amount pub change: AssetBalance, - /// Asset Distribution - pub assets: M::Selection, + /// Asset Balance Distribution + pub balances: M::Selection, } impl<M> AssetSelection<M> diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs index 28021a29b..f16c51626 100644 --- a/manta-accounting/src/wallet/ledger.rs +++ b/manta-accounting/src/wallet/ledger.rs @@ -64,9 +64,9 @@ where /// Pulls all data from the ledger, returning the current [`Checkpoint`](Self::Checkpoint). fn pull_all(&self) -> Self::PullAllFuture; - /// Sends `transfers` to the ledger, returning the current [`Checkpoint`](Self::Checkpoint) - /// and the status of the transfers if successful. - fn push(&self, transfers: Vec<TransferPost<C>>) -> Self::PushFuture; + /// Sends `posts` to the ledger returning the post of the transfer which failed and all + /// following transfer posts. + fn push(&self, posts: Vec<TransferPost<C>>) -> Self::PushFuture; } /// Ledger Source Pull Result @@ -82,7 +82,7 @@ pub type PullAllResult<C, L> = Result<PullAllResponse<C, L>, <L as Connection<C> /// Ledger Source Push Result /// /// See the [`push`](Connection::push) method on [`Connection`] for more information. -pub type PushResult<C, L> = Result<PushResponse<C, L>, <L as Connection<C>>::Error>; +pub type PushResult<C, L> = Result<PushResponse, <L as Connection<C>>::Error>; /// Ledger Source Pull Response /// @@ -123,14 +123,11 @@ where /// /// This `struct` is created by the [`push`](Connection::push) method on [`Connection`]. /// See its documentation for more. -pub struct PushResponse<C, L> -where - C: transfer::Configuration, - L: Connection<C> + ?Sized, -{ - /// Current Ledger Checkpoint - pub checkpoint: L::Checkpoint, - - /// Transaction Failure Index - pub failure_index: Option<usize>, +pub struct PushResponse { + /* TODO: + /// Failed Transfer Posts + pub failed_posts: Vec<TransferPost<C>>, + */ + /// Successful Push + pub success: bool, } diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 770c0a78c..f782dcea1 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -16,6 +16,8 @@ //! Wallet Signer +// TODO: Use universal transfers instead of just the canonical ones. + use crate::{ asset::{Asset, AssetBalance, AssetId}, fs::{Load, LoadWith, Save, SaveWith}, @@ -81,25 +83,62 @@ where /// Future for the [`sign`](Self::sign) method. type SignFuture: Future<Output = SignResult<D, C, Self>>; - /// New Receiver Future Type + /// Sign Commit Future Type + /// + /// Future for the [`commit`](Self::commit) method. + type CommitFuture: Future<Output = Result<(), Self::Error>>; + + /// Sign Rollback Future Type + /// + /// Future for the [`rollback`](Self::rollback) method. + type RollbackFuture: Future<Output = Result<(), Self::Error>>; + + /// External Receiver Future Type /// - /// Future for the [`new_receiver`](Self::new_receiver) method. - type NewReceiverFuture: Future<Output = NewReceiverResult<D, C, Self>>; + /// Future for the [`external_receiver`](Self::external_receiver) method. + type ExternalReceiverFuture: Future<Output = ExternalReceiverResult<D, C, Self>>; /// Error Type type Error; /// Pushes updates from the ledger to the wallet, synchronizing it with the ledger state and - /// returning an updated asset distribution. - fn sync<I>(&mut self, updates: I) -> Self::SyncFuture + /// returning an updated asset distribution. Depending on the `sync_state`, the signer will + /// either commit to the current state before synchronizing or rollback to the previous state. + fn sync<I>(&mut self, updates: I, sync_state: SyncState) -> Self::SyncFuture where I: IntoIterator<Item = (Utxo<C>, EncryptedAsset<C>)>; /// Signs a transfer `request` and returns the ledger transfer posts if successful. + /// + /// # Safety + /// + /// To preserve consistency, calls to [`sign`](Self::sign) should be followed by a call to + /// either [`commit`](Self::commit), [`rollback`](Self::rollback), or [`sync`](Self::sync) with + /// the appropriate [`SyncState`]. Repeated calls to [`sign`](Self::sign) should automatically + /// commit the current state before signing. fn sign(&mut self, request: SignRequest<D, C>) -> Self::SignFuture; + /// Commits to the state after the last call to [`sign`](Self::sign). + fn commit(&mut self) -> Self::CommitFuture; + + /// Rolls back to the state before the last call to [`sign`](Self::sign). + fn rollback(&mut self) -> Self::RollbackFuture; + /// Generates a new [`ShieldedIdentity`] for `self` to receive assets. - fn new_receiver(&mut self) -> Self::NewReceiverFuture; + fn external_receiver(&mut self) -> Self::ExternalReceiverFuture; +} + +/// Synchronization State +/// +/// This `enum` is used by the [`sync`](Connection::sync) method on [`Connection`]. See its +/// documentation for more. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum SyncState { + /// Should commit the current state before synchronizing + Commit, + + /// Should rollback to the previous state before synchronizing + Rollback, } /// Synchronization Result @@ -113,11 +152,11 @@ pub type SyncResult<D, C, S> = Result<SyncResponse<D>, Error<D, C, <S as Connect pub type SignResult<D, C, S> = Result<SignResponse<D, C>, Error<D, C, <S as Connection<D, C>>::Error>>; -/// New Receiver Generation Result +/// External Receiver Generation Result /// -/// See the [`new_receiver`](Connection::new_receiver) method on [`Connection`] for more +/// See the [`external_receiver`](Connection::external_receiver) method on [`Connection`] for more /// information. -pub type NewReceiverResult<D, C, S> = +pub type ExternalReceiverResult<D, C, S> = Result<transfer::ShieldedIdentity<C>, Error<D, C, <S as Connection<D, C>>::Error>>; /// Signer Synchronization Response @@ -128,32 +167,9 @@ pub struct SyncResponse<D> where D: DerivedSecretKeyGenerator, { - /// Deposit Asset Distribution - pub deposits: Vec<(ExternalIndex<D>, Asset)>, -} - -/* TODO: -pub struct SignRequest<D, C> -where - D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, -{ - /// - pub asset: Asset, - - /// - pub sources: Vec<AssetBalance>, - - /// - pub senders: Vec<(AssetBalance, Index<D>)>, - - /// - pub receivers: Vec<(AssetBalance, transfer::ShieldedIdentity<C>)>, - - /// - pub sinks: Vec<AssetBalance>, + /// Updates to the Asset Distribution + pub assets: Vec<(ExternalIndex<D>, Asset)>, } -*/ /// Signer Signing Request /// @@ -168,10 +184,31 @@ where Mint(Asset), /// Private Transfer Transaction - PrivateTransfer(Asset, Vec<Index<D>>, transfer::ShieldedIdentity<C>), + PrivateTransfer { + /// Total Asset to Transfer + total: Asset, + + /// Change Remaining from Asset Selection + change: AssetBalance, + + /// Asset Selection + balances: Vec<(Index<D>, AssetBalance)>, + + /// Receiver Shielded Identity + receiver: transfer::ShieldedIdentity<C>, + }, /// Reclaim Transaction - Reclaim(Asset, Vec<Index<D>>), + Reclaim { + /// Total Asset to Transfer + total: Asset, + + /// Change Remaining from Asset Selection + change: AssetBalance, + + /// Asset Selection + balances: Vec<(Index<D>, AssetBalance)>, + }, } /// Signer Signing Response @@ -183,14 +220,11 @@ where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, { - /// Asset Distribution Deposit Asset Id - pub asset_id: AssetId, - /// Asset Distribution Deposit Balance Updates - pub balances: Vec<(Index<D>, AssetBalance)>, + pub balances: Vec<(InternalIndex<D>, AssetBalance)>, /// Transfer Posts - pub transfers: Vec<TransferPost<C>>, + pub posts: Vec<TransferPost<C>>, } impl<D, C> SignResponse<D, C> @@ -198,18 +232,13 @@ where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, { - /// Builds a new [`SignResponse`] from `asset_id`, `balances` and `transfers`. + /// Builds a new [`SignResponse`] from `balances` and `posts`. #[inline] pub fn new( - asset_id: AssetId, - balances: Vec<(Index<D>, AssetBalance)>, - transfers: Vec<TransferPost<C>>, + balances: Vec<(InternalIndex<D>, AssetBalance)>, + posts: Vec<TransferPost<C>>, ) -> Self { - Self { - asset_id, - balances, - transfers, - } + Self { balances, posts } } } @@ -621,7 +650,7 @@ where /// Looks for an [`OpenSpend`] for this `encrypted_asset`. #[inline] - pub fn find_next_external_open_spend<C>( + pub fn find_external_open_spend<C>( &mut self, encrypted_asset: &EncryptedAsset<C>, ) -> Option<ExternalOpenSpend<D, C>> @@ -757,25 +786,30 @@ where /// Updates the internal ledger state, returing the new asset distribution. #[inline] - fn sync<I>(&mut self, updates: I) -> SyncResult<D, C, Self> + fn sync<I>(&mut self, updates: I, sync_state: SyncState) -> SyncResult<D, C, Self> where I: IntoIterator<Item = (Utxo<C>, EncryptedAsset<C>)>, Standard: Distribution<AssetParameters<C>>, { use manta_crypto::Set; // FIXME: move up to top of file - let mut deposits = Vec::new(); + match sync_state { + SyncState::Commit => self.commit(), + SyncState::Rollback => self.rollback(), + } + + let mut assets = Vec::new(); for (utxo, encrypted_asset) in updates { // TODO: Add optimization path where we have "strong" and "weak" insertions into the // `utxo_set`. If the `utxo` is accompanied by an `encrypted_asset` then we // "strong insert", if not we "weak insert". // - if let Some(open_spend) = self.signer.find_next_external_open_spend(&encrypted_asset) { - deposits.push((open_spend.index, open_spend.value.into_asset())); + if let Some(open_spend) = self.signer.find_external_open_spend(&encrypted_asset) { + assets.push((open_spend.index, open_spend.value.into_asset())); } let _ = self.utxo_set.try_insert(utxo); // FIXME: Should this ever error? } - Ok(SyncResponse { deposits }) + Ok(SyncResponse { assets }) } /// Signs the `request`, generating transfer posts. @@ -784,6 +818,7 @@ where where Standard: Distribution<AssetParameters<C>>, { + // FIXME: Repeated calls to sign should automatically commit. match request { SignRequest::Mint(asset) => { let (mint, open_spend) = @@ -798,21 +833,34 @@ where ) .map_err(Error::ProofSystemError)?; Ok(SignResponse::new( - asset.id, - vec![(open_spend.index.reduce(), asset.value)], + vec![(open_spend.index, asset.value)], vec![mint_post], )) } - SignRequest::PrivateTransfer(asset, senders, receiver) => { - // TODO: implement + SignRequest::PrivateTransfer { .. } => { + // FIXME: implement todo!() } - SignRequest::Reclaim(asset, senders) => { - // TODO: implement + SignRequest::Reclaim { .. } => { + // FIXME: implement todo!() } } } + + /// Commits to the state after the last call to [`sign`](Self::sign). + #[inline] + fn commit(&mut self) { + // FIXME: Implement. + todo!() + } + + /// Rolls back to the state before the last call to [`sign`](Self::sign). + #[inline] + fn rollback(&mut self) { + // FIXME: Implement. + todo!() + } } impl<D, C, R> Connection<D, C> for FullSigner<D, C, R> @@ -826,16 +874,20 @@ where type SignFuture = Ready<SignResult<D, C, Self>>; - type NewReceiverFuture = Ready<NewReceiverResult<D, C, Self>>; + type CommitFuture = Ready<Result<(), Self::Error>>; + + type RollbackFuture = Ready<Result<(), Self::Error>>; + + type ExternalReceiverFuture = Ready<ExternalReceiverResult<D, C, Self>>; type Error = Infallible; #[inline] - fn sync<I>(&mut self, updates: I) -> Self::SyncFuture + fn sync<I>(&mut self, updates: I, sync_state: SyncState) -> Self::SyncFuture where I: IntoIterator<Item = (Utxo<C>, EncryptedAsset<C>)>, { - future::ready(self.sync(updates)) + future::ready(self.sync(updates, sync_state)) } #[inline] @@ -844,7 +896,23 @@ where } #[inline] - fn new_receiver(&mut self) -> Self::NewReceiverFuture { + fn commit(&mut self) -> Self::CommitFuture { + future::ready({ + self.commit(); + Ok(()) + }) + } + + #[inline] + fn rollback(&mut self) -> Self::RollbackFuture { + future::ready({ + self.rollback(); + Ok(()) + }) + } + + #[inline] + fn external_receiver(&mut self) -> Self::ExternalReceiverFuture { future::ready( self.signer .next_shielded(&self.commitment_scheme) diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index c0576bf9a..4f42b9074 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -17,16 +17,64 @@ //! Wallet Full State Implementation use crate::{ - asset::{Asset, AssetBalance, AssetId, AssetMap}, + asset::{Asset, AssetBalance, AssetId, AssetMap, AssetSelection}, keys::{DerivedSecretKeyGenerator, Index}, transfer::{self, ShieldedIdentity}, wallet::{ - ledger::{self, PullResponse}, - signer, + ledger::{self, PullResponse, PushResponse}, + signer::{self, SignRequest, SignResponse, SyncState}, }, }; use core::marker::PhantomData; +/// Wallet Transaction +pub enum Transaction<C> +where + C: transfer::Configuration, +{ + /// Mint a Private Asset + Mint(Asset), + + /// Transfer a Private Asset + PrivateTransfer(Asset, ShieldedIdentity<C>), + + /// Reclaim a Private Asset + Reclaim(Asset), +} + +/// Wallet Error +/// +/// This `enum` is the error state for [`Wallet`] methods. See [`sync`](Wallet::sync) and +/// [`post`](Wallet::post) for more. +pub enum Error<D, C, S, L> +where + D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, + S: signer::Connection<D, C>, + L: ledger::Connection<C> + ?Sized, +{ + /// Signer Error + SignerError(signer::Error<D, C, S::Error>), + + /// Insufficient Balance Error + InsufficientBalance(Asset), + + /// Ledger Error + LedgerError(L::Error), +} + +impl<D, C, S, L> From<signer::Error<D, C, S::Error>> for Error<D, C, S, L> +where + D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, + S: signer::Connection<D, C>, + L: ledger::Connection<C> + ?Sized, +{ + #[inline] + fn from(err: signer::Error<D, C, S::Error>) -> Self { + Self::SignerError(err) + } +} /// Wallet pub struct Wallet<D, C, M, S, L> where @@ -42,6 +90,9 @@ where /// Signer Connection signer: S, + /// Signer Synchronization State + sync_state: SyncState, + /// Ledger Connection ledger: L, @@ -90,64 +141,121 @@ where .pull(&self.checkpoint) .await .map_err(Error::LedgerError)?; - for (key, asset) in self.signer.sync(receiver_data).await?.deposits { - self.assets.deposit(key.reduce(), asset); - } + self.assets.insert_all( + self.signer + .sync(receiver_data, self.sync_state) + .await? + .assets + .into_iter() + .map(move |(k, a)| (k.reduce(), a)), + ); self.checkpoint = checkpoint; Ok(()) } - /// Posts data to the ledger. + /// Selects `asset` from the asset distribution, returning it back if there was an insufficient + /// balance. #[inline] - pub async fn post(&mut self) -> Result<(), Error<D, C, S, L>> { - // TODO: Should we do a `sync` first or let the user do that? - // - // TODO: - // 1. Send transfer request to signer to build transfer posts, setting rollback-checkpoint - // to current state in case the transaction fails. If error, rollback state. - // 2. Send transfer posts to ledger and wait for result. - // 3. If error, rollback signer and return. - // 4. If success, send ok to signer to use new state and update checkpoint / asset - // distribution accordingly - todo!() + fn select(&mut self, asset: Asset) -> Result<AssetSelection<M>, Asset> { + self.assets.select(asset).ok_or(asset) } - /// Returns a new shielded identity to receive external assets at this wallet. + /// Prepares a `transaction` for signing. #[inline] - pub async fn new_receiver( + fn prepare( &mut self, - ) -> Result<ShieldedIdentity<C>, signer::Error<D, C, S::Error>> { - self.signer.new_receiver().await + transaction: Transaction<C>, + ) -> Result<(AssetId, SignRequest<D, C>), Asset> { + match transaction { + Transaction::Mint(asset) => Ok((asset.id, SignRequest::Mint(asset))), + Transaction::PrivateTransfer(asset, receiver) => { + let AssetSelection { change, balances } = self.select(asset)?; + Ok(( + asset.id, + SignRequest::PrivateTransfer { + total: asset, + change, + balances: balances.collect(), + receiver, + }, + )) + } + Transaction::Reclaim(asset) => { + let AssetSelection { change, balances } = self.select(asset)?; + Ok(( + asset.id, + SignRequest::Reclaim { + total: asset, + change, + balances: balances.collect(), + }, + )) + } + } } -} -/// Wallet Error -/// -/// This `enum` is the error state for [`Wallet`] methods. See [`sync`](Wallet::sync) and -/// [`post`](Wallet::post) for more. -pub enum Error<D, C, S, L> -where - D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, - S: signer::Connection<D, C>, - L: ledger::Connection<C> + ?Sized, -{ - /// Signer Error - SignerError(signer::Error<D, C, S::Error>), + /// Tries to commit to the current signer state. + #[inline] + async fn try_commit(&mut self) { + if self.signer.commit().await.is_err() { + self.sync_state = SyncState::Commit; + } + } - /// Ledger Error - LedgerError(L::Error), -} + /// Tries to rollback to the previous signer state. + #[inline] + async fn try_rollback(&mut self) { + if self.signer.rollback().await.is_err() { + self.sync_state = SyncState::Rollback; + } + } -impl<D, C, S, L> From<signer::Error<D, C, S::Error>> for Error<D, C, S, L> -where - D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, - S: signer::Connection<D, C>, - L: ledger::Connection<C> + ?Sized, -{ + /// Posts a transaction to the ledger, returning `true` if the `transaction` was successfully + /// saved onto the ledger. + /// + /// # Failure Conditions + /// + /// This method returns `false` when there were no errors in producing transfer data and + /// sending and receiving from the ledger, but instead the ledger just did not accept the + /// transaction as is. This could be caused by an external update to the ledger while the + /// signer was building the transaction that caused the wallet and the ledger to get out of + /// sync. In this case, [`post`](Self::post) can safely be called again, to retry the + /// transaction. + /// + /// This method returns an error in any other case. The internal state of the wallet is kept + /// consistent between calls and recoverable errors are returned for the caller to handle. #[inline] - fn from(err: signer::Error<D, C, S::Error>) -> Self { - Self::SignerError(err) + pub async fn post(&mut self, transaction: Transaction<C>) -> Result<bool, Error<D, C, S, L>> { + self.sync().await?; + let (asset_id, request) = self + .prepare(transaction) + .map_err(Error::InsufficientBalance)?; + let SignResponse { balances, posts } = self.signer.sign(request).await?; + match self.ledger.push(posts).await { + Ok(PushResponse { success: true }) => { + self.try_commit().await; + self.assets.insert_all_same( + asset_id, + balances.into_iter().map(move |(k, b)| (k.reduce(), b)), + ); + Ok(true) + } + Ok(PushResponse { success: false }) => { + self.try_rollback().await; + Ok(false) + } + Err(err) => { + self.try_rollback().await; + Err(Error::LedgerError(err)) + } + } + } + + /// Returns a new shielded identity to receive external assets at this wallet. + #[inline] + pub async fn external_receiver( + &mut self, + ) -> Result<ShieldedIdentity<C>, signer::Error<D, C, S::Error>> { + self.signer.external_receiver().await } } diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index 0b11d5fc4..36d63544f 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -16,6 +16,8 @@ //! Manta Pay Implementation +// FIXME: Ensure that `no-default-features` is a valid build. + #![cfg_attr(not(any(feature = "std", test)), no_std)] #![cfg_attr(doc_cfg, feature(doc_cfg))] #![forbid(rustdoc::broken_intra_doc_links)] From 19e26bf9e8a369e5a8b29b8a3885df491d43329c Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 7 Oct 2021 18:17:17 -0400 Subject: [PATCH 081/275] WIP: add commit/rollback interfaces --- manta-accounting/src/keys.rs | 64 ++++++++++++++++----------- manta-accounting/src/wallet/signer.rs | 20 +++++---- manta-accounting/src/wallet/state.rs | 11 +++-- 3 files changed, 56 insertions(+), 39 deletions(-) diff --git a/manta-accounting/src/keys.rs b/manta-accounting/src/keys.rs index ed52bfd86..b7555f200 100644 --- a/manta-accounting/src/keys.rs +++ b/manta-accounting/src/keys.rs @@ -719,10 +719,10 @@ where pub account: D::Account, /// External Transaction Index Range - pub external_index_range: Range<D::Index>, + pub external_indices: Range<D::Index>, - /// Internal Transaction Running Index - pub internal_index: D::Index, + /// Internal Transaction Index Range + pub internal_indices: Range<D::Index>, } impl<D> Account<D> @@ -732,21 +732,21 @@ where /// Builds a new [`Account`] for the given `account` identifier. #[inline] pub fn new(account: D::Account) -> Self { - Self::with_indices(account, Default::default(), Default::default()) + Self::with_ranges(account, Default::default(), Default::default()) } - /// Builds a new [`Account`] for the given `account` identifier with starting indices - /// `external_index_range` and `internal_index`. + /// Builds a new [`Account`] for the given `account` identifier with starting ranges + /// `external_indices` and `internal_indices`. #[inline] - pub fn with_indices( + pub fn with_ranges( account: D::Account, - external_index_range: Range<D::Index>, - internal_index: D::Index, + external_indices: Range<D::Index>, + internal_indices: Range<D::Index>, ) -> Self { Self { account, - external_index_range, - internal_index, + external_indices, + internal_indices, } } @@ -760,8 +760,8 @@ where /// Resets the external and internal running indices to their default values. #[inline] pub fn reset(&mut self) -> &mut Self { - self.external_index_range = Default::default(); - self.internal_index = Default::default(); + self.external_indices = Default::default(); + self.internal_indices = Default::default(); self } @@ -778,8 +778,8 @@ where #[inline] pub fn external_key(&self, source: &D) -> Result<ExternalSecretKey<D>, D::Error> { Ok(SecretKey::new( - source.generate_external_key(&self.account, &self.external_index_range.end)?, - Index::new(External, self.external_index_range.end.clone()), + source.generate_external_key(&self.account, &self.external_indices.end)?, + Index::new(External, self.external_indices.end.clone()), )) } @@ -787,8 +787,8 @@ where #[inline] pub fn internal_key(&self, source: &D) -> Result<InternalSecretKey<D>, D::Error> { Ok(SecretKey::new( - source.generate_internal_key(&self.account, &self.internal_index)?, - Index::new(Internal, self.internal_index.clone()), + source.generate_internal_key(&self.account, &self.internal_indices.end)?, + Index::new(Internal, self.internal_indices.end.clone()), )) } @@ -805,34 +805,46 @@ where /// Generates the next external key for this account, incrementing the `external_index`. #[inline] pub fn next_external_key(&mut self, source: &D) -> Result<ExternalSecretKey<D>, D::Error> { - next_external(source, &self.account, &mut self.external_index_range.end) + next_external(source, &self.account, &mut self.external_indices.end) } /// Generates the next internal key for this account, incrementing the `internal_index`. #[inline] pub fn next_internal_key(&mut self, source: &D) -> Result<InternalSecretKey<D>, D::Error> { - next_internal(source, &self.account, &mut self.internal_index) + next_internal(source, &self.account, &mut self.internal_indices.end) } /// Returns an iterator over the external keys generated by the indices in - /// [`self.external_index_range`](Self::external_index_range). + /// [`self.external_indices`](Self::external_indices). #[inline] pub fn external_keys<'s>(&'s self, source: &'s D) -> ExternalKeysRange<'s, D> { ExternalKeysRange { source, account: &self.account, - range: self.external_index_range.clone(), + range: self.external_indices.clone(), } } - /// Increments the start of the [`self.external_index_range`](Self::external_index_range) if - /// `index` is equal to the start of the range. + /// Increments the start of the external index range if `index` is equal to the start of the + /// range. #[inline] - pub fn conditional_update_external_key_range(&mut self, index: &D::Index) { - if &self.external_index_range.start == index { - self.external_index_range.start.increment() + pub fn conditional_increment_external_range(&mut self, index: &D::Index) { + if &self.external_indices.start == index { + self.external_indices.start.increment() } } + + /// Shifts the end of the internal key range to the start. + #[inline] + pub fn internal_range_shift_to_start(&mut self) { + self.internal_indices.end = self.internal_indices.start.clone(); + } + + /// Shifts the start of the internal key range to the end. + #[inline] + pub fn internal_range_shift_to_end(&mut self) { + self.internal_indices.start = self.internal_indices.end.clone(); + } } impl<D> AsRef<D::Account> for Account<D> diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index f782dcea1..c62786e4a 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -306,18 +306,18 @@ where } } - /// Builds a new [`Signer`] for `account` from a `secret_key_source` with starting indices - /// `external_index_range` and `internal_index`. + /// Builds a new [`Signer`] for `account` from a `secret_key_source` with starting ranges + /// `external_indices` and `internal_indices`. #[inline] - pub fn with_indices( + pub fn with_ranges( secret_key_source: D, account: D::Account, - external_index_range: Range<D::Index>, - internal_index: D::Index, + external_indices: Range<D::Index>, + internal_indices: Range<D::Index>, ) -> Self { Self::with_account( secret_key_source, - Account::with_indices(account, external_index_range, internal_index), + Account::with_ranges(account, external_indices, internal_indices), ) } @@ -670,7 +670,7 @@ where .flatten() })?; self.account - .conditional_update_external_key_range(&open_spend.index.index); + .conditional_increment_external_range(&open_spend.index.index); Some(open_spend) } } @@ -851,14 +851,16 @@ where /// Commits to the state after the last call to [`sign`](Self::sign). #[inline] fn commit(&mut self) { - // FIXME: Implement. + // FIXME: Implement commit for UTXO set. + self.signer.account.internal_range_shift_to_end(); todo!() } /// Rolls back to the state before the last call to [`sign`](Self::sign). #[inline] fn rollback(&mut self) { - // FIXME: Implement. + // FIXME: Implement rollback for UTXO set. + self.signer.account.internal_range_shift_to_start(); todo!() } } diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 4f42b9074..27f7c94d3 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -53,12 +53,12 @@ where S: signer::Connection<D, C>, L: ledger::Connection<C> + ?Sized, { + /// Insufficient Balance + InsufficientBalance(Asset), + /// Signer Error SignerError(signer::Error<D, C, S::Error>), - /// Insufficient Balance Error - InsufficientBalance(Asset), - /// Ledger Error LedgerError(L::Error), } @@ -149,6 +149,7 @@ where .into_iter() .map(move |(k, a)| (k.reduce(), a)), ); + self.sync_state = SyncState::Commit; self.checkpoint = checkpoint; Ok(()) } @@ -211,7 +212,9 @@ where } /// Posts a transaction to the ledger, returning `true` if the `transaction` was successfully - /// saved onto the ledger. + /// saved onto the ledger. This method automatically synchronizes with the ledger before + /// posting. To amortize the cost of future calls to [`post`](Self::post), the + /// [`sync`](Self::sync) method can be used to synchronize with the ledger. /// /// # Failure Conditions /// From dc2e6ce9247485534345cb8b89a0e9834d29ba0d Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 8 Oct 2021 02:53:50 -0400 Subject: [PATCH 082/275] WIP: implement transaction building algorithm --- manta-accounting/src/identity.rs | 37 ++- manta-accounting/src/transfer.rs | 18 +- manta-accounting/src/wallet/signer.rs | 263 ++++++++++++++------- manta-accounting/src/wallet/state.rs | 11 +- manta-crypto/src/merkle_tree/fork.rs | 12 +- manta-crypto/src/merkle_tree/inner_tree.rs | 2 +- manta-crypto/src/merkle_tree/path.rs | 6 +- 7 files changed, 224 insertions(+), 125 deletions(-) diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index f26ec1bd4..ab8d63323 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -257,14 +257,13 @@ where /// /// # API Note /// - /// This function is intentionally private so that internal random number generator is - /// not part of the public interface. See [`Self::parameters`] for access to the associated - /// `parameters`. + /// This method is intentionally private so that internal random number generator is not part + /// of the public interface. See [`Self::parameters`] for access to the associated `parameters`. /// /// # Implementation Note /// - /// Contributors should always use this function when generating an `rng` or a - /// `parameters` in the folowing ways: + /// Contributors should always use this method when generating an `rng` or a `parameters` in the + /// folowing ways: /// /// ```text /// 1. [BOTH] let (mut rng, parameters) = self.rng_and_parameters(); @@ -273,8 +272,8 @@ where /// ``` /// /// This is important because we need to preserve the order in which objects are randomly - /// generated across different functions. The `parameters` is always generated immediately - /// after creation of the random number generator. + /// generated across different methods. The `parameters` is always generated immediately after + /// creation of the random number generator. #[inline] fn rng_and_parameters(&self) -> (C::Rng, AssetParameters<C>) where @@ -642,8 +641,8 @@ where /// /// # API Note /// - /// This function is intentionally private so that `identity` and `asset_secret_key` are - /// not part of the public interface. + /// This method is intentionally private so that `identity` and `asset_secret_key` are not part + /// of the public interface. #[inline] fn new(identity: Identity<C>, asset_secret_key: ies::SecretKey<I>) -> Self { Self { @@ -760,8 +759,8 @@ where /// /// # API Note /// - /// This function is intentionally private so that `identity` and `asset` are - /// not part of the public interface. + /// This method is intentionally private so that `identity` and `asset` are not part of the + /// public interface. #[inline] fn new(identity: Identity<C>, asset: Asset) -> Self { Self { identity, asset } @@ -853,7 +852,7 @@ where /// /// # Note /// - /// When using this function, be sure to check that [`can_upgrade`](Self::can_upgrade) returns + /// When using this method, be sure to check that [`can_upgrade`](Self::can_upgrade) returns /// `true`. Otherwise, using the sender returned here will most likely return an error when /// posting to the ledger. #[inline] @@ -923,9 +922,9 @@ where /// /// # Note /// - /// When using this function, be sure to check that [`SenderProof::can_upgrade`] returns - /// `true`. Otherwise, using the sender returned here will most likely return an error when - /// posting to the ledger. + /// When using this method, be sure to check that [`SenderProof::can_upgrade`] returns `true`. + /// Otherwise, using the sender returned here will most likely return an error when posting to + /// the ledger. #[inline] pub fn upgrade<S>(self, proof: SenderProof<C, S>) -> Sender<C, S> where @@ -1089,8 +1088,8 @@ where /// /// # Safety /// - /// This function can only be called once we check that `void_number` is not already stored - /// on the ledger. See [`is_unspent`](Self::is_unspent). + /// This method can only be called once we check that `void_number` is not already stored on + /// the ledger. See [`is_unspent`](Self::is_unspent). fn spend( &mut self, void_number: Self::ValidVoidNumber, @@ -1312,8 +1311,8 @@ where /// /// # Safety /// - /// This function can only be called once we check that `utxo` is not already stored - /// on the ledger. See [`is_not_registered`](Self::is_not_registered). + /// This method can only be called once we check that `utxo` is not already stored on the + /// ledger. See [`is_not_registered`](Self::is_not_registered). fn register( &mut self, utxo: Self::ValidUtxo, diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 0c990ee5a..58e8a2dbf 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -21,7 +21,7 @@ // TODO: See if we can get rid of the `Copy` restriction on `ValidProof` and `SuperPostingKey`. // TODO: Add `generate_context`/`generate_proof` logic to `SecretTransfer`. // TODO: Have a compile-time way to check if proof generation is used for a certain shape, -// so that the `generate_context`/`generate_proof` functions can only exist on the right +// so that the `generate_context`/`generate_proof` method can only exist on the right // shape implementations, instead of failing at runtime with `None`. use crate::{ @@ -1229,16 +1229,20 @@ pub mod canonical { commitment_scheme: &C::CommitmentScheme, asset: Asset, rng: &mut R, - ) -> Result<(Mint<C>, OpenSpend<C>), IntegratedEncryptionSchemeError<C>> + ) -> Result<Mint<C>, IntegratedEncryptionSchemeError<C>> where R: CryptoRng + RngCore + ?Sized, Standard: Distribution<AssetParameters<C>>, { - let InternalReceiver { - receiver, - open_spend, - } = identity.into_internal_receiver(commitment_scheme, asset, rng)?; - Ok((Mint::build(asset, receiver), open_spend)) + // TODO: Add convenience method for `Identity::into_receiver`. + Ok(Mint::build( + asset, + identity.into_shielded(commitment_scheme).into_receiver( + commitment_scheme, + asset, + rng, + )?, + )) } } diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index c62786e4a..0ba1f68b9 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -29,8 +29,8 @@ use crate::{ transfer::{ self, canonical::{Mint, PrivateTransfer, Reclaim}, - EncryptedAsset, IntegratedEncryptionSchemeError, ProofSystemError, ProvingContext, - TransferPost, + EncryptedAsset, IntegratedEncryptionSchemeError, ProofSystemError, ProvingContext, Sender, + Transfer, TransferPost, }, }; use alloc::{vec, vec::Vec}; @@ -41,6 +41,7 @@ use core::{ hash::Hash, ops::Range, }; +use manta_crypto::{Set, VerifiedSet}; use rand::{ distributions::{Distribution, Standard}, CryptoRng, RngCore, @@ -67,6 +68,27 @@ pub type ExternalOpenSpend<D, C> = OpenSpend<D, C, External>; /// Internal Key-Owned Open Spend Type pub type InternalOpenSpend<D, C> = OpenSpend<D, C, Internal>; +/// Rollback Trait +pub trait Rollback { + /// Commits `self` to the current state. + /// + /// # Implementation Note + /// + /// Commiting to the current state must be idempotent. Calling [`rollback`](Self::rollback) + /// after [`commit`](Self::commit) must not change the state after the call to + /// [`commit`](Self::commit). + fn commit(&mut self); + + /// Rolls back `self` to the previous state. + /// + /// # Implementation Note + /// + /// Rolling back to the previous state must be idempotent. Calling [`commit`](Self::commit) + /// after [`rollback`](Self::rollback) must not change the state after the call to + /// [`rollback`](Self::rollback). + fn rollback(&mut self); +} + /// Signer Connection pub trait Connection<D, C> where @@ -116,15 +138,28 @@ where /// either [`commit`](Self::commit), [`rollback`](Self::rollback), or [`sync`](Self::sync) with /// the appropriate [`SyncState`]. Repeated calls to [`sign`](Self::sign) should automatically /// commit the current state before signing. + /// + /// See the [`Rollback`] trait for expectations on the behavior of [`commit`](Self::commit) + /// and [`rollback`](Self::rollback). fn sign(&mut self, request: SignRequest<D, C>) -> Self::SignFuture; /// Commits to the state after the last call to [`sign`](Self::sign). + /// + /// See the [`Rollback`] trait for expectations on the behavior of [`commit`](Self::commit). fn commit(&mut self) -> Self::CommitFuture; /// Rolls back to the state before the last call to [`sign`](Self::sign). + /// + /// See the [`Rollback`] trait for expectations on the behavior of [`rollback`](Self::rollback). fn rollback(&mut self) -> Self::RollbackFuture; /// Generates a new [`ShieldedIdentity`] for `self` to receive assets. + /// + /// # Note + /// + /// This method does not interact with the other methods on [`Connection`] so it can be called + /// at any point in between calls to [`sync`](Self::sync), [`sign`](Self::sign), and other + /// rollback related methods. fn external_receiver(&mut self) -> Self::ExternalReceiverFuture; } @@ -220,8 +255,11 @@ where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, { - /// Asset Distribution Deposit Balance Updates - pub balances: Vec<(InternalIndex<D>, AssetBalance)>, + /// Owner Index of the Resulting Balance + pub owner: InternalIndex<D>, + + /// Resulting Balance + pub balance: AssetBalance, /// Transfer Posts pub posts: Vec<TransferPost<C>>, @@ -232,13 +270,18 @@ where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, { - /// Builds a new [`SignResponse`] from `balances` and `posts`. + /// Builds a new [`SignResponse`] from `owner`, `balance`, and `posts`. #[inline] pub fn new( - balances: Vec<(InternalIndex<D>, AssetBalance)>, + owner: InternalIndex<D>, + balance: AssetBalance, posts: Vec<TransferPost<C>>, ) -> Self { - Self { balances, posts } + Self { + owner, + balance, + posts, + } } } @@ -379,6 +422,25 @@ where )) } + /// Returns a [`Sender`] for the key at the given `index`. + #[inline] + pub fn get_sender<C>( + &self, + index: Index<D>, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + utxo_set: &C::UtxoSet, + ) -> Result<Sender<C>, SenderError<D, C>> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + Standard: Distribution<AssetParameters<C>>, + { + self.get(&index) + .map_err(SenderError::SecretKeyError)? + .into_sender(commitment_scheme, asset, utxo_set) + .map_err(SenderError::ContainmentError) + } + /// Generates the next identity of the given `kind` for this signer. #[inline] pub fn next_identity<C>(&mut self, kind: KeyKind) -> Result<KeyOwned<D, Identity<C>>, D::Error> @@ -455,34 +517,6 @@ where .map_err(InternalReceiverError::EncryptionError) } - /// Builds a vector of `n` internal receivers to capture the given `asset`. - /// - /// # Panics - /// - /// This method panics if `n == 0`. - #[inline] - pub fn next_change_receivers<C, R>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - n: usize, - rng: &mut R, - ) -> InternalReceiverResult<D, C, Vec<InternalReceiver<D, C>>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - R: CryptoRng + RngCore + ?Sized, - Standard: Distribution<AssetParameters<C>>, - { - asset - .value - .make_change(n) - .unwrap() - .map(move |value| { - self.next_change_receiver(commitment_scheme, asset.id.with(value), rng) - }) - .collect() - } - /// Generates a new [`InternalReceiver`] to receive an asset, with the given `asset_id` and /// no value, to this account via an internal transaction. #[inline] @@ -539,35 +573,21 @@ where commitment_scheme: &C::CommitmentScheme, asset: Asset, rng: &mut R, - ) -> InternalReceiverResult<D, C, (Mint<C>, InternalOpenSpend<D, C>)> + ) -> Result<(Mint<C>, InternalIndex<D>), InternalReceiverError<D, C>> where C: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, Standard: Distribution<AssetParameters<C>>, { - Ok(self + let (identity, index) = self .next_internal_identity() .map_err(InternalReceiverError::SecretKeyError)? - .map_ok(move |identity| Mint::from_identity(identity, commitment_scheme, asset, rng)) - .map_err(InternalReceiverError::EncryptionError)? - .right()) - } - - /// Builds a [`Mint`] transaction to mint an asset with the given `asset_id` and no value, - /// returning the [`OpenSpend`] for that asset. - #[inline] - pub fn mint_zero<C, R>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - asset_id: AssetId, - rng: &mut R, - ) -> InternalReceiverResult<D, C, (Mint<C>, InternalOpenSpend<D, C>)> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - R: CryptoRng + RngCore + ?Sized, - Standard: Distribution<AssetParameters<C>>, - { - self.mint(commitment_scheme, Asset::zero(asset_id), rng) + .into(); + Ok(( + Mint::from_identity(identity, commitment_scheme, asset, rng) + .map_err(InternalReceiverError::EncryptionError)?, + index, + )) } /// Builds [`PrivateTransfer`] transactions to send `asset` to an `external_receiver`. @@ -585,7 +605,7 @@ where R: CryptoRng + RngCore + ?Sized, Standard: Distribution<AssetParameters<C>>, { - /* TODO: + /* FIXME: let mut sender_total = AssetBalance(0); let mut pre_senders = senders .into_iter() @@ -720,6 +740,7 @@ pub struct FullSigner<D, C, R> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, + C::UtxoSet: Rollback, R: CryptoRng + RngCore, { /// Signer @@ -742,6 +763,7 @@ impl<D, C, R> FullSigner<D, C, R> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, + C::UtxoSet: Rollback, R: CryptoRng + RngCore, { /// Builds a new [`FullSigner`]. @@ -791,12 +813,7 @@ where I: IntoIterator<Item = (Utxo<C>, EncryptedAsset<C>)>, Standard: Distribution<AssetParameters<C>>, { - use manta_crypto::Set; // FIXME: move up to top of file - - match sync_state { - SyncState::Commit => self.commit(), - SyncState::Rollback => self.rollback(), - } + self.start_sync(sync_state); let mut assets = Vec::new(); for (utxo, encrypted_asset) in updates { @@ -812,37 +829,94 @@ where Ok(SyncResponse { assets }) } + /// Returns a [`Sender`] for the key at the given `index`. + #[inline] + fn get_sender(&self, index: Index<D>, asset: Asset) -> Result<Sender<C>, SenderError<D, C>> + where + Standard: Distribution<AssetParameters<C>>, + { + self.signer + .get_sender(index, &self.commitment_scheme, asset, &self.utxo_set) + } + + /// Builds a [`TransferPost`] for the given `transfer`. + #[inline] + fn build_post< + const SOURCES: usize, + const SENDERS: usize, + const RECEIVERS: usize, + const SINKS: usize, + >( + &mut self, + transfer: Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>, + ) -> Result<TransferPost<C>, Error<D, C, Infallible>> { + transfer + .into_post( + &self.commitment_scheme, + &self.utxo_set, + &self.proving_context, + &mut self.rng, + ) + .map_err(Error::ProofSystemError) + } + /// Signs the `request`, generating transfer posts. #[inline] fn sign(&mut self, request: SignRequest<D, C>) -> SignResult<D, C, Self> where Standard: Distribution<AssetParameters<C>>, { - // FIXME: Repeated calls to sign should automatically commit. + self.commit(); match request { SignRequest::Mint(asset) => { - let (mint, open_spend) = + let (mint, owner) = self.signer .mint(&self.commitment_scheme, asset, &mut self.rng)?; - let mint_post = mint - .into_post( - &self.commitment_scheme, - &self.utxo_set, - &self.proving_context, - &mut self.rng, - ) - .map_err(Error::ProofSystemError)?; - Ok(SignResponse::new( - vec![(open_spend.index, asset.value)], - vec![mint_post], - )) + let mint_post = self.build_post(mint)?; + Ok(SignResponse::new(owner, asset.value, vec![mint_post])) } SignRequest::PrivateTransfer { .. } => { // FIXME: implement todo!() } - SignRequest::Reclaim { .. } => { - // FIXME: implement + SignRequest::Reclaim { + total, + change, + balances, + } => { + /* FIXME: + let mut posts = Vec::new(); + + let mut balances = balances.chunks_exact(2); + + let mut accumulator = 0; + for pair in balances { + if let [(lhs_key, lhs_value), (rhs_key, rhs_value)] = pair { + let lhs = self.get_sender(lhs_key, total.id.with(lhs_value))?; + let rhs = self.get_sender(rhs_key, total.id.with(rhs_value))?; + let (next_receiver, next_open_spend) = self + .signer + .next_change_receiver(&self.commitment_scheme)? + .unwrap() + .into(); + posts.push(PrivateTransfer::build([lhs, rhs], [accumulator, zero])); + } + } + + let reclaim = match balances.remainder() { + [last] => Reclaim::build([accumulator, last], [change]), + _ => { + let (mint, zero) = self.signer.mint_zero( + &self.commitment_scheme, + total.id, + &mut self.rng, + )?; + posts.push(mint); + Reclaim::build([accumulator, zero.into_sender()], [change]) + } + }; + */ + todo!() } } @@ -851,17 +925,24 @@ where /// Commits to the state after the last call to [`sign`](Self::sign). #[inline] fn commit(&mut self) { - // FIXME: Implement commit for UTXO set. self.signer.account.internal_range_shift_to_end(); - todo!() + self.utxo_set.commit(); } /// Rolls back to the state before the last call to [`sign`](Self::sign). #[inline] fn rollback(&mut self) { - // FIXME: Implement rollback for UTXO set. self.signer.account.internal_range_shift_to_start(); - todo!() + self.utxo_set.rollback(); + } + + /// Commits or rolls back the state depending on the value of `sync_state`. + #[inline] + fn start_sync(&mut self, sync_state: SyncState) { + match sync_state { + SyncState::Commit => self.commit(), + SyncState::Rollback => self.rollback(), + } } } @@ -869,6 +950,7 @@ impl<D, C, R> Connection<D, C> for FullSigner<D, C, R> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, + C::UtxoSet: Rollback, R: CryptoRng + RngCore, Standard: Distribution<AssetParameters<C>>, { @@ -951,3 +1033,16 @@ where /// Internal Receiver Result Type pub type InternalReceiverResult<D, C, T> = Result<T, InternalReceiverError<D, C>>; + +/// Sender Error +pub enum SenderError<D, C> +where + D: DerivedSecretKeyGenerator, + C: transfer::Configuration, +{ + /// Secret Key Generator Error + SecretKeyError(D::Error), + + /// Containment Error + ContainmentError(<C::UtxoSet as VerifiedSet>::ContainmentError), +} diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 27f7c94d3..05ad5c642 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -233,14 +233,15 @@ where let (asset_id, request) = self .prepare(transaction) .map_err(Error::InsufficientBalance)?; - let SignResponse { balances, posts } = self.signer.sign(request).await?; + let SignResponse { + owner, + balance, + posts, + } = self.signer.sign(request).await?; match self.ledger.push(posts).await { Ok(PushResponse { success: true }) => { self.try_commit().await; - self.assets.insert_all_same( - asset_id, - balances.into_iter().map(move |(k, b)| (k.reduce(), b)), - ); + self.assets.insert(owner.reduce(), asset_id.with(balance)); Ok(true) } Ok(PushResponse { success: false }) => { diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs index f1ced2cb7..8fa9892a9 100644 --- a/manta-crypto/src/merkle_tree/fork.rs +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -63,8 +63,8 @@ where /// /// # Safety /// - /// This function automatically detaches all of the forks associated to this trunk. To - /// attach them to another trunk, use [`Fork::attach`]. + /// This method automatically detaches all of the forks associated to this trunk. To attach them + /// to another trunk, use [`Fork::attach`]. #[inline] pub fn into_tree(self) -> MerkleTree<C, T> { P::claim(self.base.unwrap()) @@ -93,7 +93,7 @@ where /// /// # Safety /// - /// If the merge succeeds, this function automatically detaches all of the forks associated to + /// If the merge succeeds, this method automatically detaches all of the forks associated to /// this trunk. To attach them to another trunk, use [`Fork::attach`]. To attach them to this /// trunk, [`attach`](Self::attach) can also be used. /// @@ -459,9 +459,9 @@ pub mod raw { /// /// # Panics /// - /// This function can only panic if there are other outstanding strong pointers. This - /// function will still succeed if there are other outstanding weak pointers, but they will - /// all be disassociated to `strong`. + /// This method can only panic if there are other outstanding strong pointers. This method + /// will still succeed if there are other outstanding weak pointers, but they will all be + /// disassociated to `strong`. fn claim(strong: Self::Strong) -> MerkleTree<C, T>; /// Returns a new weak pointer to `strong`. diff --git a/manta-crypto/src/merkle_tree/inner_tree.rs b/manta-crypto/src/merkle_tree/inner_tree.rs index 8a33ff1f0..35fca7ddf 100644 --- a/manta-crypto/src/merkle_tree/inner_tree.rs +++ b/manta-crypto/src/merkle_tree/inner_tree.rs @@ -668,7 +668,7 @@ where M: Default, S: Default, { - // TODO: Remove duplicated function calls. + // TODO: Remove duplicated method calls. let mut inner_tree = InnerTree::<C, M, S>::default(); diff --git a/manta-crypto/src/merkle_tree/path.rs b/manta-crypto/src/merkle_tree/path.rs index 3384a705f..1bfc2d832 100644 --- a/manta-crypto/src/merkle_tree/path.rs +++ b/manta-crypto/src/merkle_tree/path.rs @@ -249,7 +249,7 @@ where /// In order for paths to compute the correct root, they should always have a `path` with /// length given by [`path_length`]. For [`CurrentInnerPath`], we also have the invariant /// that any right-siblings on the path, which can only be a sentinel value, are not stored. - /// This function assumes that this is the case for `path`. + /// This method assumes that this is the case for `path`. #[inline] pub fn new(leaf_index: Node, path: Vec<InnerDigest<C>>) -> Self { Self { leaf_index, path } @@ -586,7 +586,7 @@ where /// /// # Safety /// - /// See [`InnerPath::new`] for the invariants on `path` assumed by this function. + /// See [`InnerPath::new`] for the invariants on `path` assumed by this method. #[inline] pub fn new(sibling_digest: LeafDigest<C>, leaf_index: Node, path: Vec<InnerDigest<C>>) -> Self { Self::from_inner(sibling_digest, InnerPath::new(leaf_index, path)) @@ -711,7 +711,7 @@ where /// /// # Safety /// - /// See [`CurrentInnerPath::new`] for the invariants on `path` assumed by this function. + /// See [`CurrentInnerPath::new`] for the invariants on `path` assumed by this method. #[inline] pub fn new(sibling_digest: LeafDigest<C>, leaf_index: Node, path: Vec<InnerDigest<C>>) -> Self { Self::from_inner(sibling_digest, CurrentInnerPath::new(leaf_index, path)) From 46961993b2ec55e6ae77ed5c88661b1dceedc7bf Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 8 Oct 2021 14:53:01 -0400 Subject: [PATCH 083/275] WIP: moving asset distribution to the signer --- manta-accounting/src/asset.rs | 121 ++++--- manta-accounting/src/lib.rs | 5 +- manta-accounting/src/transfer.rs | 4 +- manta-accounting/src/wallet/ledger.rs | 5 +- manta-accounting/src/wallet/signer.rs | 466 +++++++++++++------------- manta-accounting/src/wallet/state.rs | 180 +++++----- 6 files changed, 403 insertions(+), 378 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 4ec5c3751..4685505c1 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -43,6 +43,11 @@ use rand::{ Rng, RngCore, }; +pub(super) mod prelude { + #[doc(inline)] + pub use super::{Asset, AssetBalance, AssetBalances, AssetId}; +} + /// [`AssetId`] Base Type type AssetIdType = u32; @@ -317,12 +322,6 @@ impl Asset { Self::new(id, AssetBalance(0)) } - /// Builds a new [`Asset`] from an existing one with a new `value`. - #[inline] - pub const fn with_value(&self, value: AssetBalance) -> Self { - Self::new(self.id, value) - } - /// Checks if the `rhs` asset has the same [`AssetId`]. #[inline] pub const fn same_id(&self, rhs: &Self) -> bool { @@ -578,10 +577,35 @@ impl<const N: usize> TryFrom<[Asset; N]> for AssetCollection<N> { } } +/// Asset Selection +/// +/// This `struct` is created by the [`select`](AssetMap::select) method of [`AssetMap`]. See its +/// documentation for more. +pub struct Selection<K, Balances, Zeroes> +where + Balances: IntoIterator<Item = (K, AssetBalance)>, + Balances::IntoIter: ExactSizeIterator, + Zeroes: IntoIterator<Item = K>, + Zeroes::IntoIter: ExactSizeIterator, +{ + /// Change Amount + pub change: AssetBalance, + + /// Asset Balance Distribution + pub balances: Balances, + + /// Zero Asset Balances + pub zeroes: Zeroes, +} + +/// [`Selection`] for [`AssetMap`] Type Alias +pub type AssetMapSelection<M> = + Selection<<M as AssetMap>::Key, <M as AssetMap>::Balances, <M as AssetMap>::Zeroes>; + /// Asset Map /// /// This trait represents an asset distribution over some [`Key`](Self::Key) type. -pub trait AssetMap { +pub trait AssetMap: Default { /// Key Type /// /// Keys are used to access the underlying asset balances. See [`select`](Self::select) @@ -593,24 +617,31 @@ pub trait AssetMap { /// This type is returned by [`iter`](Self::iter) when iterating over all assets. type Iter: Iterator<Item = (Self::Key, Asset)>; + /// Asset Selection Strategy Type + /// + /// This type is used by [`select`](Self::select) when looking for assets in the map. + type Strategy: SelectionStrategy<Self>; + /// Asset Selection Iterator Type /// /// This type is returned by [`select`](Self::select) when looking for assets in the map. - type Selection: Iterator<Item = (Self::Key, AssetBalance)>; + type Balances: ExactSizeIterator<Item = (Self::Key, AssetBalance)>; - /// Returns an iterator over all the assets stored in the map. + /// Asset Selection Zeroes Iterator Type /// - /// See [`select`](Self::select) for selecting an asset distribution that sums to some known - /// `asset` total. + /// This type is returned by [`select`](Self::select) when looking for assets in the map. + type Zeroes: ExactSizeIterator<Item = Self::Key>; + + /// Returns an iterator over all the assets stored in the map. fn iter(&self) -> Self::Iter; /// Selects asset keys which total up to at least `asset` in value, removing them from - /// the asset distribution. If there are not enough keys to total `asset`, then this method - /// returns `None`. - /// - /// See [`iter`](Self::iter) for iterating over all the assets in the map instead of a specific - /// subset summing to the `asset` total. - fn select(&mut self, asset: Asset) -> Option<AssetSelection<Self>>; + /// the asset distribution. + fn select( + &mut self, + asset: Asset, + strategy: &Self::Strategy, + ) -> Option<AssetMapSelection<Self>>; /// Inserts `asset` at the key stored at `key`. fn insert(&mut self, key: Self::Key, asset: Asset); @@ -621,18 +652,28 @@ pub trait AssetMap { where I: IntoIterator<Item = (Self::Key, Asset)>, { - for (key, asset) in iter { - self.insert(key, asset) - } + iter.into_iter() + .for_each(move |(key, asset)| self.insert(key, asset)) } - /// Inserts all of the balances in `iter` using the same `id`. + /// Inserts all of the assets in `iter` using a fixed `id`. #[inline] fn insert_all_same<I>(&mut self, id: AssetId, iter: I) where I: IntoIterator<Item = (Self::Key, AssetBalance)>, { - self.insert_all(iter.into_iter().map(move |(k, b)| (k, b.with(id)))) + iter.into_iter() + .for_each(move |(key, value)| self.insert(key, id.with(value))) + } + + /// Inserts all of the assets in `iter` using a fixed `id` and zero value. + #[inline] + fn insert_all_zeroes<I>(&mut self, id: AssetId, iter: I) + where + I: IntoIterator<Item = Self::Key>, + { + iter.into_iter() + .for_each(move |key| self.insert(key, Asset::zero(id))) } /// Returns the current balance associated with this `id`. @@ -650,40 +691,14 @@ pub trait AssetMap { } } -/// Asset Selection -/// -/// This `struct` is generated by the [`select`](AssetMap::select) method of [`AssetMap`]. See its -/// documentation for more. -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "M::Selection: Clone"), - Copy(bound = "M::Selection: Copy"), - Debug(bound = "M::Selection: Debug"), - Default(bound = "M::Selection: Default"), - Eq(bound = "M::Selection: Eq"), - Hash(bound = "M::Selection: Hash"), - PartialEq(bound = "M::Selection: PartialEq") -)] -pub struct AssetSelection<M> +/// Asset Selection Strategy +pub trait SelectionStrategy<M>: Default where M: AssetMap + ?Sized, { - /// Change Amount - pub change: AssetBalance, - - /// Asset Balance Distribution - pub balances: M::Selection, -} - -impl<M> AssetSelection<M> -where - M: AssetMap + ?Sized, -{ - /// Splits [`self.change`](Self::change) into `n` change components. - #[inline] - pub fn split_change(&self, n: usize) -> Option<Change> { - self.change.make_change(n) - } + /// Returns `true` if `selection` could have been generated by a call to [`AssetMap::select`] + /// with `self` as the strategy. + fn is_valid(&self, selection: &AssetMapSelection<M>) -> bool; } /// Testing Suite diff --git a/manta-accounting/src/lib.rs b/manta-accounting/src/lib.rs index 5f3431edc..005b01d85 100644 --- a/manta-accounting/src/lib.rs +++ b/manta-accounting/src/lib.rs @@ -27,13 +27,12 @@ extern crate derive_more; #[cfg(feature = "cocoon")] extern crate cocoon as cocoon_crate; -mod asset; - +pub mod asset; pub mod fs; pub mod identity; pub mod keys; pub mod transfer; pub mod wallet; -pub use asset::*; +pub use asset::prelude::*; pub use identity::prelude::*; diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 58e8a2dbf..bad7b04da 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -1158,7 +1158,7 @@ pub trait Shape: sealed::Sealed { /// Canonical Transaction Types pub mod canonical { use super::*; - use crate::identity::{AssetParameters, Identity, InternalReceiver, OpenSpend}; + use crate::identity::{AssetParameters, Identity}; /// Implements [`Shape`] for a given shape type. macro_rules! impl_shape { @@ -1222,7 +1222,7 @@ pub mod canonical { ) } - /// Builds a [`Mint`]-[`OpenSpend`] pair from an `identity` and an `asset`. + /// Builds a [`Mint`] from an `identity` and an `asset`. #[inline] pub fn from_identity<R>( identity: Identity<C>, diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs index f16c51626..334cb7835 100644 --- a/manta-accounting/src/wallet/ledger.rs +++ b/manta-accounting/src/wallet/ledger.rs @@ -17,6 +17,7 @@ //! Ledger Source // TODO: Move to streams so we can process some of the data as it's incoming. +// TODO: Add non-atomic transactions. use crate::{ identity::{Utxo, VoidNumber}, @@ -124,10 +125,6 @@ where /// This `struct` is created by the [`push`](Connection::push) method on [`Connection`]. /// See its documentation for more. pub struct PushResponse { - /* TODO: - /// Failed Transfer Posts - pub failed_posts: Vec<TransferPost<C>>, - */ /// Successful Push pub success: bool, } diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 0ba1f68b9..363543d8a 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -19,18 +19,18 @@ // TODO: Use universal transfers instead of just the canonical ones. use crate::{ - asset::{Asset, AssetBalance, AssetId}, + asset::{self, Asset, AssetBalance, AssetId, AssetMap, AssetMapSelection}, fs::{Load, LoadWith, Save, SaveWith}, - identity::{self, AssetParameters, Identity, Utxo}, + identity::{self, AssetParameters, Identity, PreSender, Utxo}, keys::{ - Account, DerivedSecretKeyGenerator, External, ExternalIndex, ExternalKeyOwned, Index, - Internal, InternalIndex, InternalKeyOwned, KeyKind, KeyOwned, + Account, DerivedSecretKeyGenerator, External, ExternalIndex, Index, Internal, + InternalIndex, InternalKeyOwned, KeyKind, KeyOwned, }, transfer::{ self, canonical::{Mint, PrivateTransfer, Reclaim}, - EncryptedAsset, IntegratedEncryptionSchemeError, ProofSystemError, ProvingContext, Sender, - Transfer, TransferPost, + EncryptedAsset, IntegratedEncryptionSchemeError, ProofSystemError, ProvingContext, + Receiver, Sender, ShieldedIdentity, Transfer, TransferPost, }, }; use alloc::{vec, vec::Vec}; @@ -47,26 +47,8 @@ use rand::{ CryptoRng, RngCore, }; -/// Key-Owned Pre-Sender Type -pub type PreSender<D, C> = InternalKeyOwned<D, identity::PreSender<C>>; - -/// Key-Owned Shielded Identity Type -pub type ShieldedIdentity<D, C> = ExternalKeyOwned<D, transfer::ShieldedIdentity<C>>; - -/// Key-Owned Internal Receiver Type -pub type InternalReceiver<D, C> = InternalKeyOwned< - D, - identity::InternalReceiver<C, <C as transfer::Configuration>::IntegratedEncryptionScheme>, ->; - -/// Key-Owned Open Spend Type -pub type OpenSpend<D, C, K = KeyKind> = KeyOwned<D, identity::OpenSpend<C>, K>; - -/// External Key-Owned Open Spend Type -pub type ExternalOpenSpend<D, C> = OpenSpend<D, C, External>; - -/// Internal Key-Owned Open Spend Type -pub type InternalOpenSpend<D, C> = OpenSpend<D, C, Internal>; +/// +pub type Selection<D> = asset::Selection<Index<D>, Vec<(Index<D>, AssetBalance)>, Vec<Index<D>>>; /// Rollback Trait pub trait Rollback { @@ -141,7 +123,7 @@ where /// /// See the [`Rollback`] trait for expectations on the behavior of [`commit`](Self::commit) /// and [`rollback`](Self::rollback). - fn sign(&mut self, request: SignRequest<D, C>) -> Self::SignFuture; + fn sign(&mut self, request: SignRequest<C>) -> Self::SignFuture; /// Commits to the state after the last call to [`sign`](Self::sign). /// @@ -179,109 +161,97 @@ pub enum SyncState { /// Synchronization Result /// /// See the [`sync`](Connection::sync) method on [`Connection`] for more information. -pub type SyncResult<D, C, S> = Result<SyncResponse<D>, Error<D, C, <S as Connection<D, C>>::Error>>; +pub type SyncResult<D, C, S> = Result<SyncResponse, Error<D, C, <S as Connection<D, C>>::Error>>; /// Signing Result /// /// See the [`sign`](Connection::sign) method on [`Connection`] for more information. -pub type SignResult<D, C, S> = - Result<SignResponse<D, C>, Error<D, C, <S as Connection<D, C>>::Error>>; +pub type SignResult<D, C, S> = Result<SignResponse<C>, Error<D, C, <S as Connection<D, C>>::Error>>; /// External Receiver Generation Result /// /// See the [`external_receiver`](Connection::external_receiver) method on [`Connection`] for more /// information. pub type ExternalReceiverResult<D, C, S> = - Result<transfer::ShieldedIdentity<C>, Error<D, C, <S as Connection<D, C>>::Error>>; + Result<ShieldedIdentity<C>, Error<D, C, <S as Connection<D, C>>::Error>>; /// Signer Synchronization Response /// /// This `struct` is created by the [`sync`](Connection::sync) method on [`Connection`]. /// See its documentation for more. -pub struct SyncResponse<D> -where - D: DerivedSecretKeyGenerator, -{ +pub struct SyncResponse { /// Updates to the Asset Distribution - pub assets: Vec<(ExternalIndex<D>, Asset)>, + pub assets: Vec<Asset>, } /// Signer Signing Request /// /// This `struct` is used by the [`sign`](Connection::sign) method on [`Connection`]. /// See its documentation for more. -pub enum SignRequest<D, C> +pub enum SignRequest<C> where - D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, + C: transfer::Configuration, { /// Mint Transaction Mint(Asset), /// Private Transfer Transaction - PrivateTransfer { - /// Total Asset to Transfer - total: Asset, - - /// Change Remaining from Asset Selection - change: AssetBalance, - - /// Asset Selection - balances: Vec<(Index<D>, AssetBalance)>, - - /// Receiver Shielded Identity - receiver: transfer::ShieldedIdentity<C>, - }, + PrivateTransfer(Asset, ShieldedIdentity<C>), /// Reclaim Transaction - Reclaim { - /// Total Asset to Transfer - total: Asset, + Reclaim(Asset), +} - /// Change Remaining from Asset Selection - change: AssetBalance, +impl<C> SignRequest<C> +where + C: transfer::Configuration, +{ + /// Returns the underlying asset of the transaction. + #[inline] + pub fn asset(&self) -> Asset { + match self { + Self::Mint(asset) => *asset, + Self::PrivateTransfer(asset, _) => *asset, + Self::Reclaim(asset) => *asset, + } + } - /// Asset Selection - balances: Vec<(Index<D>, AssetBalance)>, - }, + /// Returns `true` if the transaction is a deposit relative to the poster. + #[inline] + pub fn is_deposit(&self) -> bool { + matches!(self, Self::Mint(_)) + } + + /// Returns `true` if the transaction is a withdraw relative to the poster. + #[inline] + pub fn is_withdraw(&self) -> bool { + !self.is_deposit() + } } /// Signer Signing Response /// /// This `struct` is created by the [`sign`](Connection::sign) method on [`Connection`]. /// See its documentation for more. -pub struct SignResponse<D, C> +pub struct SignResponse<C> where - D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, + C: transfer::Configuration, { - /// Owner Index of the Resulting Balance - pub owner: InternalIndex<D>, - - /// Resulting Balance - pub balance: AssetBalance, + /// Resulting Balance to Deposit + pub deposit: AssetBalance, /// Transfer Posts pub posts: Vec<TransferPost<C>>, } -impl<D, C> SignResponse<D, C> +impl<C> SignResponse<C> where - D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, + C: transfer::Configuration, { - /// Builds a new [`SignResponse`] from `owner`, `balance`, and `posts`. + /// Builds a new [`SignResponse`] from `deposit` and `posts`. #[inline] - pub fn new( - owner: InternalIndex<D>, - balance: AssetBalance, - posts: Vec<TransferPost<C>>, - ) -> Self { - Self { - owner, - balance, - posts, - } + pub fn new(deposit: AssetBalance, posts: Vec<TransferPost<C>>) -> Self { + Self { deposit, posts } } } @@ -403,25 +373,6 @@ where .map(Identity::new) } - /// Returns a [`PreSender`] for the key at the given `index`. - #[inline] - pub fn get_pre_sender<C>( - &self, - index: InternalIndex<D>, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - ) -> Result<PreSender<D, C>, D::Error> - where - C: identity::Configuration<SecretKey = D::SecretKey>, - Standard: Distribution<AssetParameters<C>>, - { - Ok(KeyOwned::new( - self.get_internal(&index)? - .into_pre_sender(commitment_scheme, asset), - index, - )) - } - /// Returns a [`Sender`] for the key at the given `index`. #[inline] pub fn get_sender<C>( @@ -441,37 +392,22 @@ where .map_err(SenderError::ContainmentError) } - /// Generates the next identity of the given `kind` for this signer. - #[inline] - pub fn next_identity<C>(&mut self, kind: KeyKind) -> Result<KeyOwned<D, Identity<C>>, D::Error> - where - C: identity::Configuration<SecretKey = D::SecretKey>, - { - Ok(self - .account - .next_key(&self.secret_key_source, kind)? - .map(Identity::new)) - } - /// Generates the next external identity for this signer. #[inline] - pub fn next_external_identity<C>( - &mut self, - ) -> Result<ExternalKeyOwned<D, Identity<C>>, D::Error> + fn next_external_identity<C>(&mut self) -> Result<Identity<C>, D::Error> where C: identity::Configuration<SecretKey = D::SecretKey>, { Ok(self .account .next_external_key(&self.secret_key_source)? - .map(Identity::new)) + .map(Identity::new) + .unwrap()) } /// Generates the next internal identity for this signer. #[inline] - pub fn next_internal_identity<C>( - &mut self, - ) -> Result<InternalKeyOwned<D, Identity<C>>, D::Error> + fn next_internal_identity<C>(&mut self) -> Result<InternalKeyOwned<D, Identity<C>>, D::Error> where C: identity::Configuration<SecretKey = D::SecretKey>, { @@ -487,86 +423,73 @@ where pub fn next_shielded<C>( &mut self, commitment_scheme: &C::CommitmentScheme, - ) -> Result<ShieldedIdentity<D, C>, D::Error> + ) -> Result<ShieldedIdentity<C>, D::Error> where C: transfer::Configuration<SecretKey = D::SecretKey>, Standard: Distribution<AssetParameters<C>>, { Ok(self .next_external_identity()? - .map(move |identity| identity.into_shielded(commitment_scheme))) + .into_shielded(commitment_scheme)) } - /// Generates a new [`InternalReceiver`] to receive `asset` to this account via an - /// internal transaction. + /// Builds the next inner change receiver and pre-sender. #[inline] - pub fn next_change_receiver<C, R>( + pub fn next_inner_change<C, R>( &mut self, commitment_scheme: &C::CommitmentScheme, asset: Asset, rng: &mut R, - ) -> Result<InternalReceiver<D, C>, InternalReceiverError<D, C>> + ) -> Result<(Receiver<C>, PreSender<C>), InternalReceiverError<D, C>> where C: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, Standard: Distribution<AssetParameters<C>>, { - self.next_internal_identity() + // TODO: Simplify this so that `into_internal_receiver` automatically produces a + // `PreSender` instead of an `OpenSpend`. + let internal_receiver = self + .next_internal_identity() .map_err(InternalReceiverError::SecretKeyError)? - .map_ok(move |identity| identity.into_internal_receiver(commitment_scheme, asset, rng)) - .map_err(InternalReceiverError::EncryptionError) + .unwrap() + .into_internal_receiver(commitment_scheme, asset, rng) + .map_err(InternalReceiverError::EncryptionError)?; + Ok(( + internal_receiver.receiver, + internal_receiver + .open_spend + .into_pre_sender(commitment_scheme), + )) } - /// Generates a new [`InternalReceiver`] to receive an asset, with the given `asset_id` and - /// no value, to this account via an internal transaction. + /// Builds the final change receiver for the end of a transaction. #[inline] - pub fn next_empty_receiver<C, R>( + pub fn next_change<C, R>( &mut self, commitment_scheme: &C::CommitmentScheme, - asset_id: AssetId, + asset: Asset, rng: &mut R, - ) -> Result<InternalReceiver<D, C>, InternalReceiverError<D, C>> + ) -> Result<InternalKeyOwned<D, Receiver<C>>, InternalReceiverError<D, C>> where C: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, Standard: Distribution<AssetParameters<C>>, { - self.next_change_receiver(commitment_scheme, Asset::zero(asset_id), rng) - } - - /// Generates a new [`PreSender`] to send `asset` from this account via an internal - /// transaction. - #[inline] - pub fn next_pre_sender<C>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - ) -> Result<PreSender<D, C>, D::Error> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - Standard: Distribution<AssetParameters<C>>, - { - Ok(self - .next_internal_identity()? - .map(move |identity| identity.into_pre_sender(commitment_scheme, asset))) - } - - /// Generates a new [`PreSender`] to send an asset with the given `asset_id` and no value, - /// from this account via an internal transaction. - #[inline] - pub fn next_empty_pre_sender<C>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - asset_id: AssetId, - ) -> Result<PreSender<D, C>, D::Error> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - Standard: Distribution<AssetParameters<C>>, - { - self.next_pre_sender(commitment_scheme, Asset::zero(asset_id)) + // TODO: Simplify this so that `into_shielded` and `into_receiver` can be replaced by a + // one-step `into_receiver` call on `Identity`. + self.next_internal_identity() + .map_err(InternalReceiverError::SecretKeyError)? + .map_ok(move |identity| { + identity.into_shielded(commitment_scheme).into_receiver( + commitment_scheme, + asset, + rng, + ) + }) + .map_err(InternalReceiverError::EncryptionError) } - /// Builds a [`Mint`] transaction to mint `asset` and returns the [`OpenSpend`] for that asset. + /// Builds a [`Mint`] transaction to mint `asset` and returns the index for that asset. #[inline] pub fn mint<C, R>( &mut self, @@ -597,7 +520,7 @@ where commitment_scheme: &C::CommitmentScheme, asset: Asset, senders: Vec<(Index<D>, AssetBalance)>, - external_receiver: transfer::ShieldedIdentity<C>, + external_receiver: ShieldedIdentity<C>, rng: &mut R, ) -> Option<Vec<PrivateTransfer<C>>> where @@ -652,28 +575,12 @@ where todo!() } - /// Builds a [`Reclaim`] transaction. - #[inline] - pub fn reclaim<C, R>( - &self, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - rng: &mut R, - ) -> Option<Reclaim<C>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - R: CryptoRng + RngCore + ?Sized, - { - let _ = (commitment_scheme, asset, rng); - todo!() - } - - /// Looks for an [`OpenSpend`] for this `encrypted_asset`. + /// Looks for an index that can decrypt the given `encrypted_asset`. #[inline] - pub fn find_external_open_spend<C>( + pub fn find_external_asset<C>( &mut self, encrypted_asset: &EncryptedAsset<C>, - ) -> Option<ExternalOpenSpend<D, C>> + ) -> Option<(Index<D>, Asset)> where C: transfer::Configuration<SecretKey = D::SecretKey>, Standard: Distribution<AssetParameters<C>>, @@ -691,7 +598,7 @@ where })?; self.account .conditional_increment_external_range(&open_spend.index.index); - Some(open_spend) + Some((open_spend.index.reduce(), open_spend.value.into_asset())) } } @@ -735,12 +642,51 @@ where } } +/// Pending Asset Map +#[derive(derivative::Derivative)] +#[derivative(Default(bound = ""))] +struct PendingAssetMap<M> +where + M: AssetMap + ?Sized, +{ + /// Pending Deposit Data + deposit: Option<(M::Key, Asset)>, + + /// Pending Withdraw Data + withdraw: Option<(AssetId, AssetMapSelection<M>)>, +} + +impl<M> PendingAssetMap<M> +where + M: AssetMap + ?Sized, +{ + /// Commits the pending asset map data to `assets`. + #[inline] + fn commit(&mut self, assets: &mut M) { + self.withdraw = None; + if let Some((key, asset)) = self.deposit.take() { + assets.insert(key, asset); + } + } + + /// Rolls back the pending asset map data updating `assets`. + #[inline] + fn rollback(&mut self, assets: &mut M) { + self.deposit = None; + if let Some((asset_id, selection)) = self.withdraw.take() { + assets.insert_all_same(asset_id, selection.balances); + assets.insert_all_zeroes(asset_id, selection.zeroes); + } + } +} + /// Full Signer -pub struct FullSigner<D, C, R> +pub struct FullSigner<D, C, M, R> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, C::UtxoSet: Rollback, + M: AssetMap<Key = Index<D>>, R: CryptoRng + RngCore, { /// Signer @@ -755,15 +701,22 @@ where /// UTXO Set utxo_set: C::UtxoSet, + /// Asset Distribution + assets: M, + + /// Pending Asset Distribution + pending_assets: PendingAssetMap<M>, + /// Random Number Generator rng: R, } -impl<D, C, R> FullSigner<D, C, R> +impl<D, C, M, R> FullSigner<D, C, M, R> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, C::UtxoSet: Rollback, + M: AssetMap<Key = Index<D>>, R: CryptoRng + RngCore, { /// Builds a new [`FullSigner`]. @@ -773,6 +726,8 @@ where commitment_scheme: C::CommitmentScheme, proving_context: ProvingContext<C>, utxo_set: C::UtxoSet, + assets: M, + pending_assets: PendingAssetMap<M>, rng: R, ) -> Self { Self { @@ -780,12 +735,14 @@ where commitment_scheme, proving_context, utxo_set, + assets, + pending_assets, rng, } } /// Builds a new [`FullSigner`] from `secret_key_source`, `account`, `commitment_scheme`, - /// `proving_context`, and `rng`, using a default [`Utxo`] set. + /// `proving_context`, and `rng`, using a default [`Utxo`] set and asset distribution. #[inline] pub fn new( secret_key_source: D, @@ -802,6 +759,8 @@ where commitment_scheme, proving_context, Default::default(), + Default::default(), + Default::default(), rng, ) } @@ -821,14 +780,74 @@ where // `utxo_set`. If the `utxo` is accompanied by an `encrypted_asset` then we // "strong insert", if not we "weak insert". // - if let Some(open_spend) = self.signer.find_external_open_spend(&encrypted_asset) { - assets.push((open_spend.index, open_spend.value.into_asset())); + if let Some((key, asset)) = self.signer.find_external_asset(&encrypted_asset) { + assets.push(asset); + self.assets.insert(key, asset); } - let _ = self.utxo_set.try_insert(utxo); // FIXME: Should this ever error? + + // FIXME: Should this ever error? We should check the capacity at `updates`, then + // insert should always work here. + let _ = self.utxo_set.try_insert(utxo); } Ok(SyncResponse { assets }) } + /// Selects `asset` from the asset distribution, returning it back if there was an insufficient + /// balance. + #[inline] + fn select(&mut self, asset: Asset) -> Result<AssetMapSelection<M>, Asset> { + /* + self.assets + .select(asset, &self.selection_strategy) + .ok_or(asset) + */ + todo!() + } + + /// Prepares a `request` for signing. + #[inline] + fn prepare(&mut self, request: SignRequest<C>) { + /* TODO: + match transaction { + Transaction::Mint(asset) => Ok((asset.id, SignRequest::Mint(asset))), + Transaction::PrivateTransfer(asset, receiver) => { + let Selection { + change, + balances, + zeroes, + } = self.select(asset)?; + Ok(( + asset.id, + SignRequest::PrivateTransfer { + total: asset, + change, + balances: balances.collect(), + zeroes: zeroes.collect(), + receiver, + }, + )) + } + Transaction::Reclaim(asset) => { + let Selection { + change, + balances, + zeroes, + } = self.select(asset)?; + Ok(( + asset.id, + SignRequest::Reclaim { + total: asset, + change, + balances: balances.collect(), + zeroes: zeroes.collect(), + }, + )) + } + } + */ + todo!() + } + /// Returns a [`Sender`] for the key at the given `index`. #[inline] fn get_sender(&self, index: Index<D>, asset: Asset) -> Result<Sender<C>, SenderError<D, C>> @@ -862,7 +881,7 @@ where /// Signs the `request`, generating transfer posts. #[inline] - fn sign(&mut self, request: SignRequest<D, C>) -> SignResult<D, C, Self> + fn sign(&mut self, request: SignRequest<C>) -> SignResult<D, C, Self> where Standard: Distribution<AssetParameters<C>>, { @@ -873,50 +892,15 @@ where self.signer .mint(&self.commitment_scheme, asset, &mut self.rng)?; let mint_post = self.build_post(mint)?; - Ok(SignResponse::new(owner, asset.value, vec![mint_post])) + self.pending_assets.deposit = Some((owner.reduce(), asset)); + Ok(SignResponse::new(asset.value, vec![mint_post])) } - SignRequest::PrivateTransfer { .. } => { + SignRequest::PrivateTransfer(..) => { // FIXME: implement todo!() } - SignRequest::Reclaim { - total, - change, - balances, - } => { - /* FIXME: - let mut posts = Vec::new(); - - let mut balances = balances.chunks_exact(2); - - let mut accumulator = 0; - for pair in balances { - if let [(lhs_key, lhs_value), (rhs_key, rhs_value)] = pair { - let lhs = self.get_sender(lhs_key, total.id.with(lhs_value))?; - let rhs = self.get_sender(rhs_key, total.id.with(rhs_value))?; - let (next_receiver, next_open_spend) = self - .signer - .next_change_receiver(&self.commitment_scheme)? - .unwrap() - .into(); - posts.push(PrivateTransfer::build([lhs, rhs], [accumulator, zero])); - } - } - - let reclaim = match balances.remainder() { - [last] => Reclaim::build([accumulator, last], [change]), - _ => { - let (mint, zero) = self.signer.mint_zero( - &self.commitment_scheme, - total.id, - &mut self.rng, - )?; - posts.push(mint); - Reclaim::build([accumulator, zero.into_sender()], [change]) - } - }; - */ - + SignRequest::Reclaim(..) => { + // FIXME: implement todo!() } } @@ -927,6 +911,7 @@ where fn commit(&mut self) { self.signer.account.internal_range_shift_to_end(); self.utxo_set.commit(); + self.pending_assets.commit(&mut self.assets); } /// Rolls back to the state before the last call to [`sign`](Self::sign). @@ -934,6 +919,7 @@ where fn rollback(&mut self) { self.signer.account.internal_range_shift_to_start(); self.utxo_set.rollback(); + self.pending_assets.rollback(&mut self.assets); } /// Commits or rolls back the state depending on the value of `sync_state`. @@ -946,11 +932,12 @@ where } } -impl<D, C, R> Connection<D, C> for FullSigner<D, C, R> +impl<D, C, M, R> Connection<D, C> for FullSigner<D, C, M, R> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, C::UtxoSet: Rollback, + M: AssetMap<Key = Index<D>>, R: CryptoRng + RngCore, Standard: Distribution<AssetParameters<C>>, { @@ -975,7 +962,7 @@ where } #[inline] - fn sign(&mut self, request: SignRequest<D, C>) -> Self::SignFuture { + fn sign(&mut self, request: SignRequest<C>) -> Self::SignFuture { future::ready(self.sign(request)) } @@ -1000,7 +987,6 @@ where future::ready( self.signer .next_shielded(&self.commitment_scheme) - .map(KeyOwned::unwrap) .map_err(Error::SecretKeyError), ) } @@ -1008,7 +994,7 @@ where /// Internal Receiver Error /// -/// This `enum` is the error state for any construction of an [`InternalReceiver`] from a derived +/// This `enum` is the error state for any construction of an internal receiver from a derived /// secret key generator. #[derive(derivative::Derivative)] #[derivative( diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 05ad5c642..ac2347f8e 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -17,9 +17,9 @@ //! Wallet Full State Implementation use crate::{ - asset::{Asset, AssetBalance, AssetId, AssetMap, AssetSelection}, - keys::{DerivedSecretKeyGenerator, Index}, - transfer::{self, ShieldedIdentity}, + asset::{Asset, AssetBalance, AssetId}, + keys::DerivedSecretKeyGenerator, + transfer::{Configuration, ShieldedIdentity}, wallet::{ ledger::{self, PullResponse, PushResponse}, signer::{self, SignRequest, SignResponse, SyncState}, @@ -27,21 +27,47 @@ use crate::{ }; use core::marker::PhantomData; -/// Wallet Transaction -pub enum Transaction<C> -where - C: transfer::Configuration, -{ - /// Mint a Private Asset - Mint(Asset), +/// Balance State +pub trait BalanceState { + /// Returns the current balance associated with this `id`. + fn balance(&self, id: AssetId) -> AssetBalance; + + /// Returns true if `self` contains at least `asset.value` of the asset of kind `asset.id`. + #[inline] + fn contains(&self, asset: Asset) -> bool { + self.balance(asset.id) >= asset.value + } - /// Transfer a Private Asset - PrivateTransfer(Asset, ShieldedIdentity<C>), + /// Deposits `asset` into the balance state, increasing the balance of the asset stored at + /// `asset.id` by an amount equal to `asset.value`. + fn deposit(&mut self, asset: Asset); - /// Reclaim a Private Asset - Reclaim(Asset), + /// Deposits every asset in `assets` into the balance state. + #[inline] + fn deposit_all<I>(&mut self, assets: I) + where + I: IntoIterator<Item = Asset>, + { + for asset in assets { + self.deposit(asset) + } + } + + /// Withdraws `asset` from the balance state without checking if it would overdraw. + /// + /// # Panics + /// + /// This method does not check if withdrawing `asset` from the balance state would cause an + /// overdraw, but if it were to overdraw, this method must panic. + fn withdraw_unchecked(&mut self, asset: Asset); } +/* TODO: Implement these: +impl BalanceState for Vec<Asset> {} +impl BalanceState for BTreeMap<Asset> {} +impl BalanceState for HashMap<Asset> {} +*/ + /// Wallet Error /// /// This `enum` is the error state for [`Wallet`] methods. See [`sync`](Wallet::sync) and @@ -49,7 +75,7 @@ where pub enum Error<D, C, S, L> where D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, + C: Configuration<SecretKey = D::SecretKey>, S: signer::Connection<D, C>, L: ledger::Connection<C> + ?Sized, { @@ -66,7 +92,7 @@ where impl<D, C, S, L> From<signer::Error<D, C, S::Error>> for Error<D, C, S, L> where D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, + C: Configuration<SecretKey = D::SecretKey>, S: signer::Connection<D, C>, L: ledger::Connection<C> + ?Sized, { @@ -76,17 +102,14 @@ where } } /// Wallet -pub struct Wallet<D, C, M, S, L> +pub struct Wallet<D, C, S, L, B> where D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, - M: AssetMap<Key = Index<D>>, + C: Configuration<SecretKey = D::SecretKey>, S: signer::Connection<D, C>, L: ledger::Connection<C>, + B: BalanceState, { - /// Asset Distribution - assets: M, - /// Signer Connection signer: S, @@ -99,18 +122,40 @@ where /// Ledger Checkpoint checkpoint: L::Checkpoint, + /// Balance State + assets: B, + /// Type Parameter Marker __: PhantomData<(D, C)>, } -impl<D, C, M, S, L> Wallet<D, C, M, S, L> +impl<D, C, S, L, B> Wallet<D, C, S, L, B> where D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, - M: AssetMap<Key = Index<D>>, + C: Configuration<SecretKey = D::SecretKey>, S: signer::Connection<D, C>, L: ledger::Connection<C>, + B: BalanceState, { + /// Builds a new [`Wallet`]. + #[inline] + pub fn new( + signer: S, + sync_state: SyncState, + ledger: L, + checkpoint: L::Checkpoint, + assets: B, + ) -> Self { + Self { + signer, + sync_state, + ledger, + checkpoint, + assets, + __: PhantomData, + } + } + /// Returns the current balance associated with this `id`. #[inline] pub fn balance(&self, id: AssetId) -> AssetBalance { @@ -130,7 +175,7 @@ where &self.checkpoint } - /// Pulls data from the `ledger`, synchronizing the wallet and asset distribution. + /// Pulls data from the `ledger`, synchronizing the wallet and balance state. #[inline] pub async fn sync(&mut self) -> Result<(), Error<D, C, S, L>> { let PullResponse { @@ -141,57 +186,27 @@ where .pull(&self.checkpoint) .await .map_err(Error::LedgerError)?; - self.assets.insert_all( + self.assets.deposit_all( self.signer .sync(receiver_data, self.sync_state) .await? - .assets - .into_iter() - .map(move |(k, a)| (k.reduce(), a)), + .assets, ); self.sync_state = SyncState::Commit; self.checkpoint = checkpoint; Ok(()) } - /// Selects `asset` from the asset distribution, returning it back if there was an insufficient - /// balance. + /// Checks if there is enough balance in the balance state to perform the `transaction`. #[inline] - fn select(&mut self, asset: Asset) -> Result<AssetSelection<M>, Asset> { - self.assets.select(asset).ok_or(asset) - } - - /// Prepares a `transaction` for signing. - #[inline] - fn prepare( - &mut self, - transaction: Transaction<C>, - ) -> Result<(AssetId, SignRequest<D, C>), Asset> { - match transaction { - Transaction::Mint(asset) => Ok((asset.id, SignRequest::Mint(asset))), - Transaction::PrivateTransfer(asset, receiver) => { - let AssetSelection { change, balances } = self.select(asset)?; - Ok(( - asset.id, - SignRequest::PrivateTransfer { - total: asset, - change, - balances: balances.collect(), - receiver, - }, - )) - } - Transaction::Reclaim(asset) => { - let AssetSelection { change, balances } = self.select(asset)?; - Ok(( - asset.id, - SignRequest::Reclaim { - total: asset, - change, - balances: balances.collect(), - }, - )) - } + fn prepare(&self, transaction: &SignRequest<C>) -> Result<TransactionKind, Asset> { + let asset = transaction.asset(); + if transaction.is_deposit() { + Ok(TransactionKind::Deposit(asset.id)) + } else if self.assets.contains(asset) { + Ok(TransactionKind::Withdraw(asset)) + } else { + Err(asset) } } @@ -228,20 +243,24 @@ where /// This method returns an error in any other case. The internal state of the wallet is kept /// consistent between calls and recoverable errors are returned for the caller to handle. #[inline] - pub async fn post(&mut self, transaction: Transaction<C>) -> Result<bool, Error<D, C, S, L>> { + pub async fn post(&mut self, transaction: SignRequest<C>) -> Result<bool, Error<D, C, S, L>> { self.sync().await?; - let (asset_id, request) = self - .prepare(transaction) + let transaction_kind = self + .prepare(&transaction) .map_err(Error::InsufficientBalance)?; - let SignResponse { - owner, - balance, - posts, - } = self.signer.sign(request).await?; + let SignResponse { deposit, posts } = self.signer.sign(transaction).await?; match self.ledger.push(posts).await { Ok(PushResponse { success: true }) => { self.try_commit().await; - self.assets.insert(owner.reduce(), asset_id.with(balance)); + match transaction_kind { + TransactionKind::Deposit(asset_id) => { + self.assets.deposit(asset_id.with(deposit)); + } + TransactionKind::Withdraw(asset) => { + self.assets.withdraw_unchecked(asset); + self.assets.deposit(asset.id.with(deposit)); + } + } Ok(true) } Ok(PushResponse { success: false }) => { @@ -263,3 +282,12 @@ where self.signer.external_receiver().await } } + +/// Transaction Kind +enum TransactionKind { + /// Deposit Transaction + Deposit(AssetId), + + /// Withdraw Transaction + Withdraw(Asset), +} From d5250be5269cf331db1a562de9744b492b2b27c2 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sat, 9 Oct 2021 16:14:36 -0400 Subject: [PATCH 084/275] WIP: implementing signing algorithm --- manta-accounting/src/asset.rs | 108 ++++++++-------------- manta-accounting/src/wallet/signer.rs | 124 ++++++++++---------------- 2 files changed, 83 insertions(+), 149 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 4685505c1..3d8d0e40a 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -577,73 +577,24 @@ impl<const N: usize> TryFrom<[Asset; N]> for AssetCollection<N> { } } -/// Asset Selection -/// -/// This `struct` is created by the [`select`](AssetMap::select) method of [`AssetMap`]. See its -/// documentation for more. -pub struct Selection<K, Balances, Zeroes> -where - Balances: IntoIterator<Item = (K, AssetBalance)>, - Balances::IntoIter: ExactSizeIterator, - Zeroes: IntoIterator<Item = K>, - Zeroes::IntoIter: ExactSizeIterator, -{ - /// Change Amount - pub change: AssetBalance, - - /// Asset Balance Distribution - pub balances: Balances, - - /// Zero Asset Balances - pub zeroes: Zeroes, -} - -/// [`Selection`] for [`AssetMap`] Type Alias -pub type AssetMapSelection<M> = - Selection<<M as AssetMap>::Key, <M as AssetMap>::Balances, <M as AssetMap>::Zeroes>; - /// Asset Map /// /// This trait represents an asset distribution over some [`Key`](Self::Key) type. pub trait AssetMap: Default { /// Key Type /// - /// Keys are used to access the underlying asset balances. See [`select`](Self::select) - /// and [`insert`](Self::insert) for uses of the [`Key`](Self::Key) type. + /// Keys are used to access the underlying asset balances. type Key; - /// Asset Iterator Type - /// - /// This type is returned by [`iter`](Self::iter) when iterating over all assets. - type Iter: Iterator<Item = (Self::Key, Asset)>; - - /// Asset Selection Strategy Type - /// - /// This type is used by [`select`](Self::select) when looking for assets in the map. - type Strategy: SelectionStrategy<Self>; - - /// Asset Selection Iterator Type - /// - /// This type is returned by [`select`](Self::select) when looking for assets in the map. - type Balances: ExactSizeIterator<Item = (Self::Key, AssetBalance)>; - - /// Asset Selection Zeroes Iterator Type - /// - /// This type is returned by [`select`](Self::select) when looking for assets in the map. - type Zeroes: ExactSizeIterator<Item = Self::Key>; + // TODO: Turn `select` and `zeroes` back into iterator returning functions. - /// Returns an iterator over all the assets stored in the map. - fn iter(&self) -> Self::Iter; + /// Selects asset keys which total up to at least `asset` in value. + fn select(&self, asset: Asset) -> Selection<Self>; - /// Selects asset keys which total up to at least `asset` in value, removing them from - /// the asset distribution. - fn select( - &mut self, - asset: Asset, - strategy: &Self::Strategy, - ) -> Option<AssetMapSelection<Self>>; + /// Returns all of the keys which have zero balance for the given `id`. + fn zeroes(&self, id: AssetId) -> Vec<Self::Key>; - /// Inserts `asset` at the key stored at `key`. + /// Inserts `asset` at the `key` in the map. fn insert(&mut self, key: Self::Key, asset: Asset); /// Inserts all of the assets in `iter`. @@ -676,29 +627,42 @@ pub trait AssetMap: Default { .for_each(move |key| self.insert(key, Asset::zero(id))) } - /// Returns the current balance associated with this `id`. - #[inline] - fn balance(&self, id: AssetId) -> AssetBalance { - self.iter() - .filter_map(move |(_, asset)| (asset.id == id).then(move || asset.value)) - .sum() - } + /// Removes the `key` from the map. + fn remove(&mut self, key: Self::Key); - /// Returns true if `self` contains at least `asset.value` of the asset of kind `asset.id`. - #[inline] - fn contains(&self, asset: Asset) -> bool { - self.balance(asset.id) >= asset.value + /// Removes all the keys in `iter` from the map. + fn remove_all<I>(&mut self, iter: I) + where + I: IntoIterator<Item = Self::Key>, + { + iter.into_iter().for_each(move |key| self.remove(key)) } } -/// Asset Selection Strategy -pub trait SelectionStrategy<M>: Default +/// Asset Selection +/// +/// This `struct` is created by the [`select`](AssetMap::select) method of [`AssetMap`]. See its +/// documentation for more. +pub struct Selection<M> where M: AssetMap + ?Sized, { - /// Returns `true` if `selection` could have been generated by a call to [`AssetMap::select`] - /// with `self` as the strategy. - fn is_valid(&self, selection: &AssetMapSelection<M>) -> bool; + /// Change Amount + pub change: AssetBalance, + + /// Asset Balance Distribution + pub balances: Vec<(M::Key, AssetBalance)>, +} + +impl<M> Selection<M> +where + M: AssetMap + ?Sized, +{ + /// Returns `true` if `self` is an empty [`Selection`]. + #[inline] + pub fn is_empty(&self) -> bool { + self.balances.is_empty() + } } /// Testing Suite diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 363543d8a..62bccd02a 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -19,7 +19,7 @@ // TODO: Use universal transfers instead of just the canonical ones. use crate::{ - asset::{self, Asset, AssetBalance, AssetId, AssetMap, AssetMapSelection}, + asset::{Asset, AssetBalance, AssetId, AssetMap, Selection}, fs::{Load, LoadWith, Save, SaveWith}, identity::{self, AssetParameters, Identity, PreSender, Utxo}, keys::{ @@ -39,6 +39,7 @@ use core::{ fmt::Debug, future::{self, Future, Ready}, hash::Hash, + mem, ops::Range, }; use manta_crypto::{Set, VerifiedSet}; @@ -47,9 +48,6 @@ use rand::{ CryptoRng, RngCore, }; -/// -pub type Selection<D> = asset::Selection<Index<D>, Vec<(Index<D>, AssetBalance)>, Vec<Index<D>>>; - /// Rollback Trait pub trait Rollback { /// Commits `self` to the current state. @@ -267,6 +265,9 @@ where /// Encryption Error EncryptionError(IntegratedEncryptionSchemeError<C>), + /// Insufficient Balance + InsufficientBalance(Asset), + /// Proof System Error ProofSystemError(ProofSystemError<C>), @@ -513,6 +514,7 @@ where )) } + /* FIXME[remove]: /// Builds [`PrivateTransfer`] transactions to send `asset` to an `external_receiver`. #[inline] pub fn private_transfer<C, R>( @@ -574,6 +576,7 @@ where todo!() } + */ /// Looks for an index that can decrypt the given `encrypted_asset`. #[inline] @@ -653,7 +656,7 @@ where deposit: Option<(M::Key, Asset)>, /// Pending Withdraw Data - withdraw: Option<(AssetId, AssetMapSelection<M>)>, + withdraw: Vec<M::Key>, } impl<M> PendingAssetMap<M> @@ -663,20 +666,16 @@ where /// Commits the pending asset map data to `assets`. #[inline] fn commit(&mut self, assets: &mut M) { - self.withdraw = None; if let Some((key, asset)) = self.deposit.take() { assets.insert(key, asset); } + assets.remove_all(mem::take(&mut self.withdraw)) } - /// Rolls back the pending asset map data updating `assets`. + /// Clears the pending asset map. #[inline] - fn rollback(&mut self, assets: &mut M) { - self.deposit = None; - if let Some((asset_id, selection)) = self.withdraw.take() { - assets.insert_all_same(asset_id, selection.balances); - assets.insert_all_zeroes(asset_id, selection.zeroes); - } + fn rollback(&mut self) { + *self = Default::default() } } @@ -792,62 +791,6 @@ where Ok(SyncResponse { assets }) } - /// Selects `asset` from the asset distribution, returning it back if there was an insufficient - /// balance. - #[inline] - fn select(&mut self, asset: Asset) -> Result<AssetMapSelection<M>, Asset> { - /* - self.assets - .select(asset, &self.selection_strategy) - .ok_or(asset) - */ - todo!() - } - - /// Prepares a `request` for signing. - #[inline] - fn prepare(&mut self, request: SignRequest<C>) { - /* TODO: - match transaction { - Transaction::Mint(asset) => Ok((asset.id, SignRequest::Mint(asset))), - Transaction::PrivateTransfer(asset, receiver) => { - let Selection { - change, - balances, - zeroes, - } = self.select(asset)?; - Ok(( - asset.id, - SignRequest::PrivateTransfer { - total: asset, - change, - balances: balances.collect(), - zeroes: zeroes.collect(), - receiver, - }, - )) - } - Transaction::Reclaim(asset) => { - let Selection { - change, - balances, - zeroes, - } = self.select(asset)?; - Ok(( - asset.id, - SignRequest::Reclaim { - total: asset, - change, - balances: balances.collect(), - zeroes: zeroes.collect(), - }, - )) - } - } - */ - todo!() - } - /// Returns a [`Sender`] for the key at the given `index`. #[inline] fn get_sender(&self, index: Index<D>, asset: Asset) -> Result<Sender<C>, SenderError<D, C>> @@ -879,6 +822,37 @@ where .map_err(Error::ProofSystemError) } + /// Selects `asset` from the asset distribution, returning it back if there was an insufficient + /// balance. + #[inline] + fn select(&self, asset: Asset) -> Result<Selection<M>, Asset> { + let selection = self.assets.select(asset); + if selection.is_empty() { + Err(asset) + } else { + Ok(selection) + } + } + + /// Signs a withdraw request. + #[inline] + fn sign_withdraw( + &mut self, + asset: Asset, + receiver: Option<ShieldedIdentity<C>>, + ) -> SignResult<D, C, Self> + where + Standard: Distribution<AssetParameters<C>>, + { + let selection = self.select(asset).map_err(Error::InsufficientBalance)?; + // let zeroes = self.assets.zeroes(asset.id); + + // FIXME: Implement the signing. + + self.pending_assets.withdraw = vec![]; // FIXME: Push changes here as we go along + todo!() + } + /// Signs the `request`, generating transfer posts. #[inline] fn sign(&mut self, request: SignRequest<C>) -> SignResult<D, C, Self> @@ -895,14 +869,10 @@ where self.pending_assets.deposit = Some((owner.reduce(), asset)); Ok(SignResponse::new(asset.value, vec![mint_post])) } - SignRequest::PrivateTransfer(..) => { - // FIXME: implement - todo!() - } - SignRequest::Reclaim(..) => { - // FIXME: implement - todo!() + SignRequest::PrivateTransfer(asset, receiver) => { + self.sign_withdraw(asset, Some(receiver)) } + SignRequest::Reclaim(asset) => self.sign_withdraw(asset, None), } } @@ -919,7 +889,7 @@ where fn rollback(&mut self) { self.signer.account.internal_range_shift_to_start(); self.utxo_set.rollback(); - self.pending_assets.rollback(&mut self.assets); + self.pending_assets.rollback(); } /// Commits or rolls back the state depending on the value of `sync_state`. From 81df23f958d0596d3978e5105a6e08c6519c5da9 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 10 Oct 2021 16:55:42 -0400 Subject: [PATCH 085/275] WIP: add balance state impls, improve interfaces/docs --- manta-accounting/src/asset.rs | 35 ++++- manta-accounting/src/keys.rs | 1 + manta-accounting/src/transfer.rs | 54 +++++++ manta-accounting/src/wallet/signer.rs | 201 +++++++------------------ manta-accounting/src/wallet/state.rs | 206 +++++++++++++++++--------- 5 files changed, 282 insertions(+), 215 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 3d8d0e40a..424682a30 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -322,6 +322,12 @@ impl Asset { Self::new(id, AssetBalance(0)) } + /// Returns `true` if `self` is a zero [`Asset`] of some [`AssetId`]. + #[inline] + pub const fn is_zero(&self) -> bool { + self.value.0 == 0 + } + /// Checks if the `rhs` asset has the same [`AssetId`]. #[inline] pub const fn same_id(&self, rhs: &Self) -> bool { @@ -343,6 +349,27 @@ impl Asset { pub fn into_bytes(self) -> [u8; Self::SIZE] { into_array_unchecked(self.accumulated::<Vec<_>>()) } + + /// Returns [`self.value`](Self::value) if the given `id` matches [`self.id`](Self::id). + #[inline] + pub const fn value_of(&self, id: AssetId) -> Option<AssetBalance> { + if self.id.0 == id.0 { + Some(self.value) + } else { + None + } + } + + /// Returns a mutable reference to [`self.value`](Self::value) if the given `id` matches + /// [`self.id`](Self::id). + #[inline] + pub fn value_of_mut(&mut self, id: AssetId) -> Option<&mut AssetBalance> { + if self.id.0 == id.0 { + Some(&mut self.value) + } else { + None + } + } } impl Add<AssetBalance> for Asset { @@ -586,13 +613,13 @@ pub trait AssetMap: Default { /// Keys are used to access the underlying asset balances. type Key; - // TODO: Turn `select` and `zeroes` back into iterator returning functions. + // TODO: Turn `select` and `zeroes` back into iterator returning methods. /// Selects asset keys which total up to at least `asset` in value. fn select(&self, asset: Asset) -> Selection<Self>; - /// Returns all of the keys which have zero balance for the given `id`. - fn zeroes(&self, id: AssetId) -> Vec<Self::Key>; + /// Returns at most `n` zero assets with the given `id`. + fn zeroes(&self, n: usize, id: AssetId) -> Vec<Self::Key>; /// Inserts `asset` at the `key` in the map. fn insert(&mut self, key: Self::Key, asset: Asset); @@ -619,7 +646,7 @@ pub trait AssetMap: Default { /// Inserts all of the assets in `iter` using a fixed `id` and zero value. #[inline] - fn insert_all_zeroes<I>(&mut self, id: AssetId, iter: I) + fn insert_zeroes<I>(&mut self, id: AssetId, iter: I) where I: IntoIterator<Item = Self::Key>, { diff --git a/manta-accounting/src/keys.rs b/manta-accounting/src/keys.rs index b7555f200..6e5fe83f0 100644 --- a/manta-accounting/src/keys.rs +++ b/manta-accounting/src/keys.rs @@ -21,6 +21,7 @@ // TODO: Check to make sure we conform to the specification and then make a note about it in the // module documentation, and add a link to the specification. +// TODO: Try to get rid of `KeyOwned` if possible, or at least minimize its use. use core::{convert::TryFrom, fmt::Debug, hash::Hash, ops::Range}; diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index bad7b04da..67b549d9d 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -1312,6 +1312,60 @@ pub mod canonical { ) } } + + /// Canonical Transaction Type + pub enum Transaction<C> + where + C: Configuration, + { + /// Mint Private Asset + Mint(Asset), + + /// Private Transfer Asset to Receiver + PrivateTransfer(Asset, ShieldedIdentity<C>), + + /// Reclaim Private Asset + Reclaim(Asset), + } + + impl<C> Transaction<C> + where + C: Configuration, + { + /// Checks that `self` can be executed for a given `balance` state, returning the + /// transaction kind if successful, and returning the asset back if the balance was + /// insufficient. + #[inline] + pub fn check<F>(&self, balance: F) -> Result<TransactionKind, Asset> + where + F: FnOnce(Asset) -> bool, + { + match self { + Self::Mint(asset) => Ok(TransactionKind::Deposit(*asset)), + Self::PrivateTransfer(asset, _) | Self::Reclaim(asset) => { + if balance(*asset) { + Ok(TransactionKind::Withdraw(*asset)) + } else { + Err(*asset) + } + } + } + } + } + + /// Transaction Kind + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] + pub enum TransactionKind { + /// Deposit Transaction + /// + /// A transaction of this kind will result in a deposit of `asset`. + Deposit(Asset), + + /// Withdraw Transaction + /// + /// A transaction of this kind will result in a withdraw of `asset`. + Withdraw(Asset), + } } /* TODO: diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 62bccd02a..55a98d89a 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -19,7 +19,7 @@ // TODO: Use universal transfers instead of just the canonical ones. use crate::{ - asset::{Asset, AssetBalance, AssetId, AssetMap, Selection}, + asset::{Asset, AssetId, AssetMap, Selection}, fs::{Load, LoadWith, Save, SaveWith}, identity::{self, AssetParameters, Identity, PreSender, Utxo}, keys::{ @@ -28,7 +28,7 @@ use crate::{ }, transfer::{ self, - canonical::{Mint, PrivateTransfer, Reclaim}, + canonical::{Mint, PrivateTransfer, Reclaim, Transaction}, EncryptedAsset, IntegratedEncryptionSchemeError, ProofSystemError, ProvingContext, Receiver, Sender, ShieldedIdentity, Transfer, TransferPost, }, @@ -110,7 +110,7 @@ where where I: IntoIterator<Item = (Utxo<C>, EncryptedAsset<C>)>; - /// Signs a transfer `request` and returns the ledger transfer posts if successful. + /// Signs a `transaction` and returns the ledger transfer posts if successful. /// /// # Safety /// @@ -121,7 +121,7 @@ where /// /// See the [`Rollback`] trait for expectations on the behavior of [`commit`](Self::commit) /// and [`rollback`](Self::rollback). - fn sign(&mut self, request: SignRequest<C>) -> Self::SignFuture; + fn sign(&mut self, transaction: Transaction<C>) -> Self::SignFuture; /// Commits to the state after the last call to [`sign`](Self::sign). /// @@ -182,48 +182,11 @@ pub struct SyncResponse { pub assets: Vec<Asset>, } -/// Signer Signing Request -/// -/// This `struct` is used by the [`sign`](Connection::sign) method on [`Connection`]. -/// See its documentation for more. -pub enum SignRequest<C> -where - C: transfer::Configuration, -{ - /// Mint Transaction - Mint(Asset), - - /// Private Transfer Transaction - PrivateTransfer(Asset, ShieldedIdentity<C>), - - /// Reclaim Transaction - Reclaim(Asset), -} - -impl<C> SignRequest<C> -where - C: transfer::Configuration, -{ - /// Returns the underlying asset of the transaction. +impl SyncResponse { + /// Builds a new [`SyncResponse`] from `assets`. #[inline] - pub fn asset(&self) -> Asset { - match self { - Self::Mint(asset) => *asset, - Self::PrivateTransfer(asset, _) => *asset, - Self::Reclaim(asset) => *asset, - } - } - - /// Returns `true` if the transaction is a deposit relative to the poster. - #[inline] - pub fn is_deposit(&self) -> bool { - matches!(self, Self::Mint(_)) - } - - /// Returns `true` if the transaction is a withdraw relative to the poster. - #[inline] - pub fn is_withdraw(&self) -> bool { - !self.is_deposit() + pub fn new(assets: Vec<Asset>) -> Self { + Self { assets } } } @@ -235,9 +198,6 @@ pub struct SignResponse<C> where C: transfer::Configuration, { - /// Resulting Balance to Deposit - pub deposit: AssetBalance, - /// Transfer Posts pub posts: Vec<TransferPost<C>>, } @@ -246,10 +206,10 @@ impl<C> SignResponse<C> where C: transfer::Configuration, { - /// Builds a new [`SignResponse`] from `deposit` and `posts`. + /// Builds a new [`SignResponse`] from `posts`. #[inline] - pub fn new(deposit: AssetBalance, posts: Vec<TransferPost<C>>) -> Self { - Self { deposit, posts } + pub fn new(posts: Vec<TransferPost<C>>) -> Self { + Self { posts } } } @@ -514,70 +474,6 @@ where )) } - /* FIXME[remove]: - /// Builds [`PrivateTransfer`] transactions to send `asset` to an `external_receiver`. - #[inline] - pub fn private_transfer<C, R>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - senders: Vec<(Index<D>, AssetBalance)>, - external_receiver: ShieldedIdentity<C>, - rng: &mut R, - ) -> Option<Vec<PrivateTransfer<C>>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - R: CryptoRng + RngCore + ?Sized, - Standard: Distribution<AssetParameters<C>>, - { - /* FIXME: - let mut sender_total = AssetBalance(0); - let mut pre_senders = senders - .into_iter() - .map(|(index, value)| { - sender_total += value; - self.get_pre_sender(index, commitment_scheme, asset.id.with(value)) - }) - .collect::<Result<Vec<_>, _>>() - .ok()?; - - let mint = if pre_senders.len() % 2 == 1 { - let (mint, open_spend) = self.mint_zero(commitment_scheme, asset.id, rng).ok()?; - pre_senders.push(open_spend.map(move |os| os.into_pre_sender(commitment_scheme))); - Some(mint) - } else { - None - }; - - let mut transfers = Vec::new(); - let mut accumulator = self - .next_internal_identity()? - .into_pre_sender(commitment_scheme, Asset::zero(asset.id)); - for pre_sender in pre_senders { - let (next_receiver, next_open_spend) = - self.next_change_receiver(commitment_scheme)?.value.into(); - transfers.push(PrivateTransfer::build( - [accumulator.into_sender(), pre_sender.value], - [ - next_receiver, - self.next_empty_receiver(commitment_scheme, asset.id, rng)?, - ], - )); - accumulator = next_open_spend.into_pre_sender(commitment_scheme); - } - - let external_receiver = external_receiver.into_receiver(commitment_scheme, asset, rng); - - transfers.push(PrivateTransfer::build( - [accumulator.into_sender(), self.next_empty_sender()], - [external_receiver, change], - )); - */ - - todo!() - } - */ - /// Looks for an index that can decrypt the given `encrypted_asset`. #[inline] pub fn find_external_asset<C>( @@ -588,6 +484,7 @@ where C: transfer::Configuration<SecretKey = D::SecretKey>, Standard: Distribution<AssetParameters<C>>, { + // FIXME: Simplify this implementation. let open_spend = self .account .external_keys(&self.secret_key_source) @@ -648,28 +545,37 @@ where /// Pending Asset Map #[derive(derivative::Derivative)] #[derivative(Default(bound = ""))] -struct PendingAssetMap<M> +struct PendingAssetMap<D> where - M: AssetMap + ?Sized, + D: DerivedSecretKeyGenerator, { - /// Pending Deposit Data - deposit: Option<(M::Key, Asset)>, + /// Pending Insert Data + insert: Option<(InternalIndex<D>, Asset)>, - /// Pending Withdraw Data - withdraw: Vec<M::Key>, + /// Pending Insert Zeroes Data + insert_zeroes: Option<(AssetId, Vec<InternalIndex<D>>)>, + + /// Pending Remove Data + remove: Vec<InternalIndex<D>>, } -impl<M> PendingAssetMap<M> +impl<D> PendingAssetMap<D> where - M: AssetMap + ?Sized, + D: DerivedSecretKeyGenerator, { /// Commits the pending asset map data to `assets`. #[inline] - fn commit(&mut self, assets: &mut M) { - if let Some((key, asset)) = self.deposit.take() { - assets.insert(key, asset); + fn commit<M>(&mut self, assets: &mut M) + where + M: AssetMap<Key = Index<D>> + ?Sized, + { + if let Some((key, asset)) = self.insert.take() { + assets.insert(key.reduce(), asset); + } + if let Some((asset_id, zeroes)) = self.insert_zeroes.take() { + assets.insert_zeroes(asset_id, zeroes.into_iter().map(Index::reduce)); } - assets.remove_all(mem::take(&mut self.withdraw)) + assets.remove_all(mem::take(&mut self.remove).into_iter().map(Index::reduce)) } /// Clears the pending asset map. @@ -704,7 +610,7 @@ where assets: M, /// Pending Asset Distribution - pending_assets: PendingAssetMap<M>, + pending_assets: PendingAssetMap<D>, /// Random Number Generator rng: R, @@ -726,7 +632,7 @@ where proving_context: ProvingContext<C>, utxo_set: C::UtxoSet, assets: M, - pending_assets: PendingAssetMap<M>, + pending_assets: PendingAssetMap<D>, rng: R, ) -> Self { Self { @@ -788,7 +694,7 @@ where // insert should always work here. let _ = self.utxo_set.try_insert(utxo); } - Ok(SyncResponse { assets }) + Ok(SyncResponse::new(assets)) } /// Returns a [`Sender`] for the key at the given `index`. @@ -834,7 +740,7 @@ where } } - /// Signs a withdraw request. + /// Signs a withdraw transaction. #[inline] fn sign_withdraw( &mut self, @@ -844,35 +750,40 @@ where where Standard: Distribution<AssetParameters<C>>, { - let selection = self.select(asset).map_err(Error::InsufficientBalance)?; - // let zeroes = self.assets.zeroes(asset.id); + let Selection { change, balances } = + self.select(asset).map_err(Error::InsufficientBalance)?; + let zeroes = self + .assets + .zeroes(2usize.saturating_sub(balances.len()), asset.id); - // FIXME: Implement the signing. + // FIXME: Implement signing. - self.pending_assets.withdraw = vec![]; // FIXME: Push changes here as we go along + // FIXME: Push changes here as we go along + self.pending_assets.insert_zeroes = Some((asset.id, vec![])); + self.pending_assets.remove = vec![]; todo!() } - /// Signs the `request`, generating transfer posts. + /// Signs the `transaction`, generating transfer posts. #[inline] - fn sign(&mut self, request: SignRequest<C>) -> SignResult<D, C, Self> + fn sign(&mut self, transaction: Transaction<C>) -> SignResult<D, C, Self> where Standard: Distribution<AssetParameters<C>>, { self.commit(); - match request { - SignRequest::Mint(asset) => { + match transaction { + Transaction::Mint(asset) => { let (mint, owner) = self.signer .mint(&self.commitment_scheme, asset, &mut self.rng)?; let mint_post = self.build_post(mint)?; - self.pending_assets.deposit = Some((owner.reduce(), asset)); - Ok(SignResponse::new(asset.value, vec![mint_post])) + self.pending_assets.insert = Some((owner, asset)); + Ok(SignResponse::new(vec![mint_post])) } - SignRequest::PrivateTransfer(asset, receiver) => { + Transaction::PrivateTransfer(asset, receiver) => { self.sign_withdraw(asset, Some(receiver)) } - SignRequest::Reclaim(asset) => self.sign_withdraw(asset, None), + Transaction::Reclaim(asset) => self.sign_withdraw(asset, None), } } @@ -932,8 +843,8 @@ where } #[inline] - fn sign(&mut self, request: SignRequest<C>) -> Self::SignFuture { - future::ready(self.sign(request)) + fn sign(&mut self, transaction: Transaction<C>) -> Self::SignFuture { + future::ready(self.sign(transaction)) } #[inline] diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index ac2347f8e..1f70b132a 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -14,19 +14,32 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Wallet Full State Implementation +//! Full Wallet Implementation use crate::{ asset::{Asset, AssetBalance, AssetId}, keys::DerivedSecretKeyGenerator, - transfer::{Configuration, ShieldedIdentity}, + transfer::{ + canonical::{Transaction, TransactionKind}, + Configuration, ShieldedIdentity, + }, wallet::{ ledger::{self, PullResponse, PushResponse}, - signer::{self, SignRequest, SignResponse, SyncState}, + signer::{self, SignResponse, SyncState}, }, }; +use alloc::{ + collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}, + vec::Vec, +}; use core::marker::PhantomData; +#[cfg(feature = "std")] +use std::{ + collections::hash_map::{Entry as HashMapEntry, HashMap, RandomState}, + hash::BuildHasher, +}; + /// Balance State pub trait BalanceState { /// Returns the current balance associated with this `id`. @@ -48,9 +61,7 @@ pub trait BalanceState { where I: IntoIterator<Item = Asset>, { - for asset in assets { - self.deposit(asset) - } + assets.into_iter().for_each(move |a| self.deposit(a)) } /// Withdraws `asset` from the balance state without checking if it would overdraw. @@ -62,47 +73,90 @@ pub trait BalanceState { fn withdraw_unchecked(&mut self, asset: Asset); } -/* TODO: Implement these: -impl BalanceState for Vec<Asset> {} -impl BalanceState for BTreeMap<Asset> {} -impl BalanceState for HashMap<Asset> {} -*/ +/// Performs an unchecked withdraw on `balance`, panicking on overflow. +#[inline] +fn withdraw_unchecked(balance: Option<&mut AssetBalance>, withdraw: AssetBalance) { + let balance = balance.expect("Trying to withdraw from a zero balance."); + *balance = balance + .checked_sub(withdraw) + .expect("Overdrawn balance state."); +} -/// Wallet Error -/// -/// This `enum` is the error state for [`Wallet`] methods. See [`sync`](Wallet::sync) and -/// [`post`](Wallet::post) for more. -pub enum Error<D, C, S, L> -where - D: DerivedSecretKeyGenerator, - C: Configuration<SecretKey = D::SecretKey>, - S: signer::Connection<D, C>, - L: ledger::Connection<C> + ?Sized, -{ - /// Insufficient Balance - InsufficientBalance(Asset), +impl BalanceState for Vec<Asset> { + #[inline] + fn balance(&self, id: AssetId) -> AssetBalance { + self.iter() + .find_map(move |a| a.value_of(id)) + .unwrap_or_default() + } - /// Signer Error - SignerError(signer::Error<D, C, S::Error>), + #[inline] + fn deposit(&mut self, asset: Asset) { + self.push(asset) + } - /// Ledger Error - LedgerError(L::Error), + #[inline] + fn withdraw_unchecked(&mut self, asset: Asset) { + if !asset.is_zero() { + withdraw_unchecked( + self.iter_mut().find_map(move |a| a.value_of_mut(asset.id)), + asset.value, + ) + } + } } -impl<D, C, S, L> From<signer::Error<D, C, S::Error>> for Error<D, C, S, L> +/// Adds implementation of [`BalanceState`] for a map type with the given `$entry` type. +macro_rules! impl_balance_state_map_body { + ($entry:tt) => { + #[inline] + fn balance(&self, id: AssetId) -> AssetBalance { + self.get(&id).copied().unwrap_or_default() + } + + #[inline] + fn deposit(&mut self, asset: Asset) { + match self.entry(asset.id) { + $entry::Vacant(entry) => { + entry.insert(asset.value); + } + $entry::Occupied(entry) => { + *entry.into_mut() += asset.value; + } + } + } + + #[inline] + fn withdraw_unchecked(&mut self, asset: Asset) { + if !asset.is_zero() { + withdraw_unchecked(self.get_mut(&asset.id), asset.value); + } + } + }; +} + +/// B-Tree Map [`BalanceState`] Implementation +pub type BTreeMapBalanceState = BTreeMap<AssetId, AssetBalance>; + +impl BalanceState for BTreeMapBalanceState { + impl_balance_state_map_body! { BTreeMapEntry } +} + +/// Hash Map [`BalanceState`] Implementation +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +pub type HashMapBalanceState<S = RandomState> = HashMap<AssetId, AssetBalance, S>; + +#[cfg(feature = "std")] +impl<S> BalanceState for HashMapBalanceState<S> where - D: DerivedSecretKeyGenerator, - C: Configuration<SecretKey = D::SecretKey>, - S: signer::Connection<D, C>, - L: ledger::Connection<C> + ?Sized, + S: BuildHasher, { - #[inline] - fn from(err: signer::Error<D, C, S::Error>) -> Self { - Self::SignerError(err) - } + impl_balance_state_map_body! { HashMapEntry } } + /// Wallet -pub struct Wallet<D, C, S, L, B> +pub struct Wallet<D, C, S, L, B = BTreeMapBalanceState> where D: DerivedSecretKeyGenerator, C: Configuration<SecretKey = D::SecretKey>, @@ -197,17 +251,17 @@ where Ok(()) } - /// Checks if there is enough balance in the balance state to perform the `transaction`. + /// Checks if `transaction` can be executed on the balance state of `self`, returning the + /// kind of update that should be performed on the balance state if the transaction is + /// successfully posted to the ledger. + /// + /// # Safety + /// + /// This method is already called by [`post`](Self::post), but can be used by custom + /// implementations to perform checks elsewhere. #[inline] - fn prepare(&self, transaction: &SignRequest<C>) -> Result<TransactionKind, Asset> { - let asset = transaction.asset(); - if transaction.is_deposit() { - Ok(TransactionKind::Deposit(asset.id)) - } else if self.assets.contains(asset) { - Ok(TransactionKind::Withdraw(asset)) - } else { - Err(asset) - } + pub fn check(&self, transaction: &Transaction<C>) -> Result<TransactionKind, Asset> { + transaction.check(move |a| self.contains(a)) } /// Tries to commit to the current signer state. @@ -243,23 +297,18 @@ where /// This method returns an error in any other case. The internal state of the wallet is kept /// consistent between calls and recoverable errors are returned for the caller to handle. #[inline] - pub async fn post(&mut self, transaction: SignRequest<C>) -> Result<bool, Error<D, C, S, L>> { + pub async fn post(&mut self, transaction: Transaction<C>) -> Result<bool, Error<D, C, S, L>> { self.sync().await?; - let transaction_kind = self - .prepare(&transaction) + let balance_update = self + .check(&transaction) .map_err(Error::InsufficientBalance)?; - let SignResponse { deposit, posts } = self.signer.sign(transaction).await?; + let SignResponse { posts } = self.signer.sign(transaction).await?; match self.ledger.push(posts).await { Ok(PushResponse { success: true }) => { self.try_commit().await; - match transaction_kind { - TransactionKind::Deposit(asset_id) => { - self.assets.deposit(asset_id.with(deposit)); - } - TransactionKind::Withdraw(asset) => { - self.assets.withdraw_unchecked(asset); - self.assets.deposit(asset.id.with(deposit)); - } + match balance_update { + TransactionKind::Deposit(asset) => self.assets.deposit(asset), + TransactionKind::Withdraw(asset) => self.assets.withdraw_unchecked(asset), } Ok(true) } @@ -283,11 +332,36 @@ where } } -/// Transaction Kind -enum TransactionKind { - /// Deposit Transaction - Deposit(AssetId), +/// Wallet Error +/// +/// This `enum` is the error state for [`Wallet`] methods. See [`sync`](Wallet::sync) and +/// [`post`](Wallet::post) for more. +pub enum Error<D, C, S, L> +where + D: DerivedSecretKeyGenerator, + C: Configuration<SecretKey = D::SecretKey>, + S: signer::Connection<D, C>, + L: ledger::Connection<C> + ?Sized, +{ + /// Insufficient Balance + InsufficientBalance(Asset), + + /// Signer Error + SignerError(signer::Error<D, C, S::Error>), + + /// Ledger Error + LedgerError(L::Error), +} - /// Withdraw Transaction - Withdraw(Asset), +impl<D, C, S, L> From<signer::Error<D, C, S::Error>> for Error<D, C, S, L> +where + D: DerivedSecretKeyGenerator, + C: Configuration<SecretKey = D::SecretKey>, + S: signer::Connection<D, C>, + L: ledger::Connection<C> + ?Sized, +{ + #[inline] + fn from(err: signer::Error<D, C, S::Error>) -> Self { + Self::SignerError(err) + } } From f44de4161cf3cd07292b2421d8a4d39380d6390e Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 11 Oct 2021 03:08:07 -0400 Subject: [PATCH 086/275] WIP: work on signing implementation --- manta-accounting/src/wallet/ledger.rs | 4 +- manta-accounting/src/wallet/signer.rs | 84 +++++++++++++++++++++++---- manta-accounting/src/wallet/state.rs | 2 + 3 files changed, 77 insertions(+), 13 deletions(-) diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs index 334cb7835..b2b98ab33 100644 --- a/manta-accounting/src/wallet/ledger.rs +++ b/manta-accounting/src/wallet/ledger.rs @@ -17,7 +17,7 @@ //! Ledger Source // TODO: Move to streams so we can process some of the data as it's incoming. -// TODO: Add non-atomic transactions. +// TODO: Add non-atomic transactions? use crate::{ identity::{Utxo, VoidNumber}, @@ -32,7 +32,7 @@ where C: transfer::Configuration, { /// Ledger State Checkpoint Type - type Checkpoint: Default + Ord; + type Checkpoint: Default + PartialOrd; /// Receiver Data Iterator Type type ReceiverData: IntoIterator<Item = (Utxo<C>, EncryptedAsset<C>)>; diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 55a98d89a..707aa3420 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -75,6 +75,11 @@ where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, { + /* FIXME: + /// Ledger Checkpoint Type + type Checkpoint; + */ + /// Sync Future Type /// /// Future for the [`sync`](Self::sync) method. @@ -553,10 +558,10 @@ where insert: Option<(InternalIndex<D>, Asset)>, /// Pending Insert Zeroes Data - insert_zeroes: Option<(AssetId, Vec<InternalIndex<D>>)>, + insert_zeroes: Option<(AssetId, Vec<Index<D>>)>, /// Pending Remove Data - remove: Vec<InternalIndex<D>>, + remove: Vec<Index<D>>, } impl<D> PendingAssetMap<D> @@ -573,9 +578,9 @@ where assets.insert(key.reduce(), asset); } if let Some((asset_id, zeroes)) = self.insert_zeroes.take() { - assets.insert_zeroes(asset_id, zeroes.into_iter().map(Index::reduce)); + assets.insert_zeroes(asset_id, zeroes); } - assets.remove_all(mem::take(&mut self.remove).into_iter().map(Index::reduce)) + assets.remove_all(mem::take(&mut self.remove)) } /// Clears the pending asset map. @@ -677,6 +682,8 @@ where I: IntoIterator<Item = (Utxo<C>, EncryptedAsset<C>)>, Standard: Distribution<AssetParameters<C>>, { + // FIXME: Add checkpoint to sync so that we can make sure we are synchronizing correctly. + self.start_sync(sync_state); let mut assets = Vec::new(); @@ -742,7 +749,7 @@ where /// Signs a withdraw transaction. #[inline] - fn sign_withdraw( + fn sign_withdraw_inner( &mut self, asset: Asset, receiver: Option<ShieldedIdentity<C>>, @@ -752,16 +759,71 @@ where { let Selection { change, balances } = self.select(asset).map_err(Error::InsufficientBalance)?; + let zeroes = self .assets - .zeroes(2usize.saturating_sub(balances.len()), asset.id); + .zeroes(2_usize.saturating_sub(balances.len()), asset.id); + + let mut new_zeroes = vec![]; + self.pending_assets.remove = balances.iter().map(move |(k, _)| k.clone()).collect(); + + // FIXME: Implement signing: + /* + fn pad<T: Default, const N: usize>(mut v: Vec<T>) -> [T; N] { + v.extend(repeat_with(Default::default).take(N - v.len())); + into_array_unchecked(v) + } + + fn generate<const M: usize, const N: usize>( + total: u128, + change: u128, + mut balances: Vec<u128>, + ) -> Vec<([u128; M], [u128; N])> { + assert!(M > 1, "M must be > 1!"); + assert!(N > 1, "N must be > 1!"); + assert!(!balances.is_empty(), "Balances cannot be empty!"); + + let mut posts = Vec::new(); + + while balances.len() > M { + let iter = balances.chunks_exact(M); + let mut accumulators = iter.remainder().to_vec(); + for senders in iter { + let accumulator = senders.iter().sum(); + posts.push((into_array_unchecked(senders), pad(vec![accumulator]))); + accumulators.push(accumulator); + } + balances = accumulators; + } - // FIXME: Implement signing. + posts.push((pad(balances), pad(vec![total, change]))); + posts + } + */ + + let mut posts = Vec::new(); - // FIXME: Push changes here as we go along - self.pending_assets.insert_zeroes = Some((asset.id, vec![])); - self.pending_assets.remove = vec![]; - todo!() + self.pending_assets.insert_zeroes = Some((asset.id, new_zeroes)); + Ok(SignResponse::new(posts)) + } + + /// Signs a withdraw transaction, resetting the internal state on an error. + #[inline] + fn sign_withdraw( + &mut self, + asset: Asset, + receiver: Option<ShieldedIdentity<C>>, + ) -> SignResult<D, C, Self> + where + Standard: Distribution<AssetParameters<C>>, + { + let result = self.sign_withdraw_inner(asset, receiver); + if result.is_err() { + // FIXME: Add recovery from error. + // TODO: Is this right: `self.rollback();` + todo!() + } + result } /// Signs the `transaction`, generating transfer posts. diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 1f70b132a..53aa529ef 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -232,6 +232,8 @@ where /// Pulls data from the `ledger`, synchronizing the wallet and balance state. #[inline] pub async fn sync(&mut self) -> Result<(), Error<D, C, S, L>> { + // FIXME: Add a checkpoint to the signer so that we can make sure that if `signer.sync` + // fails, we can catch up properly. let PullResponse { checkpoint, receiver_data, From 3d7d30b25cfdc9d8001059d25c4f2c4f2c969387 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 11 Oct 2021 12:14:24 -0400 Subject: [PATCH 087/275] WIP: fixing some spec issues with signer/ledger --- manta-accounting/src/identity.rs | 87 ++++----------------- manta-accounting/src/transfer.rs | 107 +++++++------------------- manta-accounting/src/wallet/ledger.rs | 28 +++++-- manta-accounting/src/wallet/signer.rs | 80 +++++++++++++------ manta-accounting/src/wallet/state.rs | 50 ++++++------ manta-crypto/src/set.rs | 9 +++ manta-pay/src/accounting/ledger.rs | 6 ++ 7 files changed, 161 insertions(+), 206 deletions(-) diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index ab8d63323..9d458eb6e 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -1063,26 +1063,17 @@ where /// Type that allows super-traits of [`SenderLedger`] to customize posting key behavior. type SuperPostingKey: Copy; - /// Ledger Error - type Error; - /// Checks if the ledger already contains the `void_number` in its set of void numbers. /// /// Existence of such a void number could indicate a possible double-spend. - fn is_unspent( - &self, - void_number: VoidNumber<C>, - ) -> Result<Option<Self::ValidVoidNumber>, Self::Error>; + fn is_unspent(&self, void_number: VoidNumber<C>) -> Option<Self::ValidVoidNumber>; /// Checks if the `public_input` is up-to-date with the current state of the UTXO set that is /// stored on the ledger. /// /// Failure to match the ledger state means that the sender was constructed under an invalid or /// older state of the ledger. - fn is_valid_utxo_state( - &self, - public_input: S::Public, - ) -> Result<Option<Self::ValidUtxoState>, Self::Error>; + fn is_valid_utxo_state(&self, public_input: S::Public) -> Option<Self::ValidUtxoState>; /// Posts the `void_number` to the ledger, spending the asset. /// @@ -1095,25 +1086,12 @@ where void_number: Self::ValidVoidNumber, utxo_state: Self::ValidUtxoState, super_key: &Self::SuperPostingKey, - ) -> Result<(), Self::Error>; + ) -> bool; } /// Sender Post Error -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "L::Error: Clone"), - Copy(bound = "L::Error: Copy"), - Debug(bound = "L::Error: Debug"), - Eq(bound = "L::Error: Eq"), - Hash(bound = "L::Error: Hash"), - PartialEq(bound = "L::Error: PartialEq") -)] -pub enum SenderPostError<C, S, L> -where - C: Configuration, - S: VerifiedSet<Item = Utxo<C>>, - L: SenderLedger<C, S>, -{ +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum SenderPostError { /// Asset Spent Error /// /// The asset has already been spent. @@ -1123,9 +1101,6 @@ where /// /// The sender was not constructed under the current state of the UTXO set. InvalidUtxoState, - - /// Ledger Error - LedgerError(L::Error), } /// Sender Post @@ -1148,24 +1123,17 @@ where { /// Validates `self` on the sender `ledger`. #[inline] - pub fn validate<L>( - self, - ledger: &L, - ) -> Result<SenderPostingKey<C, S, L>, SenderPostError<C, S, L>> + pub fn validate<L>(self, ledger: &L) -> Result<SenderPostingKey<C, S, L>, SenderPostError> where L: SenderLedger<C, S>, { Ok(SenderPostingKey { - void_number: match ledger - .is_unspent(self.void_number) - .map_err(SenderPostError::LedgerError)? - { + void_number: match ledger.is_unspent(self.void_number) { Some(key) => key, _ => return Err(SenderPostError::AssetSpent), }, utxo_containment_proof_public_input: match ledger .is_valid_utxo_state(self.utxo_containment_proof_public_input) - .map_err(SenderPostError::LedgerError)? { Some(key) => key, _ => return Err(SenderPostError::InvalidUtxoState), @@ -1207,7 +1175,7 @@ where { /// Posts `self` to the sender `ledger`. #[inline] - pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) -> Result<(), L::Error> { + pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) -> bool { ledger.spend( self.void_number, self.utxo_containment_proof_public_input, @@ -1299,13 +1267,10 @@ where /// Type that allows super-traits of [`ReceiverLedger`] to customize posting key behavior. type SuperPostingKey: Copy; - /// Ledger Error - type Error; - /// Checks if the ledger already contains the `utxo` in its set of UTXOs. /// /// Existence of such a UTXO could indicate a possible double-spend. - fn is_not_registered(&self, utxo: Utxo<C>) -> Result<Option<Self::ValidUtxo>, Self::Error>; + fn is_not_registered(&self, utxo: Utxo<C>) -> Option<Self::ValidUtxo>; /// Posts the `utxo` and `encrypted_asset` to the ledger, registering the asset. /// @@ -1318,32 +1283,16 @@ where utxo: Self::ValidUtxo, encrypted_asset: EncryptedMessage<I>, super_key: &Self::SuperPostingKey, - ) -> Result<(), Self::Error>; + ) -> bool; } /// Receiver Post Error -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "L::Error: Clone"), - Copy(bound = "L::Error: Copy"), - Debug(bound = "L::Error: Debug"), - Eq(bound = "L::Error: Eq"), - Hash(bound = "L::Error: Hash"), - PartialEq(bound = "L::Error: PartialEq") -)] -pub enum ReceiverPostError<C, I, L> -where - C: Configuration, - I: IntegratedEncryptionScheme<Plaintext = Asset>, - L: ReceiverLedger<C, I>, -{ +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum ReceiverPostError { /// Asset Registered Error /// /// The asset has already been registered with the ledger. AssetRegistered, - - /// Ledger Error - LedgerError(L::Error), } /// Receiver Post @@ -1366,18 +1315,12 @@ where { /// Validates `self` on the receiver `ledger`. #[inline] - pub fn validate<L>( - self, - ledger: &L, - ) -> Result<ReceiverPostingKey<C, I, L>, ReceiverPostError<C, I, L>> + pub fn validate<L>(self, ledger: &L) -> Result<ReceiverPostingKey<C, I, L>, ReceiverPostError> where L: ReceiverLedger<C, I>, { Ok(ReceiverPostingKey { - utxo: match ledger - .is_not_registered(self.utxo) - .map_err(ReceiverPostError::LedgerError)? - { + utxo: match ledger.is_not_registered(self.utxo) { Some(key) => key, _ => return Err(ReceiverPostError::AssetRegistered), }, @@ -1419,7 +1362,7 @@ where { /// Posts `self` to the receiver `ledger`. #[inline] - pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) -> Result<(), L::Error> { + pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) -> bool { ledger.register(self.utxo, self.encrypted_asset, super_key) } } diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 67b549d9d..da6125353 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -26,7 +26,10 @@ use crate::{ asset::{sample_asset_balances, Asset, AssetBalance, AssetBalances, AssetId}, - identity::{self, constraint::UtxoVar, ReceiverLedger, SenderLedger, Utxo}, + identity::{ + self, constraint::UtxoVar, ReceiverLedger, ReceiverPostError, SenderLedger, + SenderPostError, Utxo, + }, }; use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash, ops::AddAssign}; @@ -40,7 +43,7 @@ use manta_crypto::{ ies::{EncryptedMessage, IntegratedEncryptionScheme}, set::{constraint::VerifiedSetVariable, VerifiedSet}, }; -use manta_util::{create_seal, mixed_chain, seal, Either}; +use manta_util::{create_seal, from_variant_impl, mixed_chain, seal, Either}; use rand::{ distributions::{Distribution, Standard}, CryptoRng, RngCore, @@ -117,9 +120,6 @@ pub type Sender<C> = identity::Sender<C, <C as Configuration>::UtxoSet>; /// Sender Post Type pub type SenderPost<C> = identity::SenderPost<C, <C as Configuration>::UtxoSet>; -/// Sender Post Error Type -pub type SenderPostError<C, L> = identity::SenderPostError<C, <C as Configuration>::UtxoSet, L>; - /// Sender Posting Key Type pub type SenderPostingKey<C, L> = identity::SenderPostingKey<C, <C as Configuration>::UtxoSet, L>; @@ -130,10 +130,6 @@ pub type Receiver<C> = identity::Receiver<C, <C as Configuration>::IntegratedEnc pub type ReceiverPost<C> = identity::ReceiverPost<C, <C as Configuration>::IntegratedEncryptionScheme>; -/// Receiver Post Error Type -pub type ReceiverPostError<C, L> = - identity::ReceiverPostError<C, <C as Configuration>::IntegratedEncryptionScheme, L>; - /// Receiver Posting Key Type pub type ReceiverPostingKey<C, L> = identity::ReceiverPostingKey<C, <C as Configuration>::IntegratedEncryptionScheme, L>; @@ -170,21 +166,16 @@ pub type ProofSystemError<C> = <<C as Configuration>::ProofSystem as ProofSystem /// Transfer Ledger Super Posting Key Type pub type TransferLedgerSuperPostingKey<C, L> = <L as TransferLedger<C>>::SuperPostingKey; -/// Transfer Ledger Error Type -pub type TransferLedgerError<C, L> = <L as TransferLedger<C>>::Error; - /// Transfer Ledger pub trait TransferLedger<C>: SenderLedger< C, C::UtxoSet, SuperPostingKey = (Self::ValidProof, TransferLedgerSuperPostingKey<C, Self>), - Error = TransferLedgerError<C, Self>, > + ReceiverLedger< C, C::IntegratedEncryptionScheme, SuperPostingKey = (Self::ValidProof, TransferLedgerSuperPostingKey<C, Self>), - Error = TransferLedgerError<C, Self>, > where C: Configuration, @@ -204,19 +195,13 @@ where /// Type that allows super-traits of [`TransferLedger`] to customize posting key behavior. type SuperPostingKey: Copy; - /// Ledger Error - type Error; - /// Checks that the transfer `proof` is valid. /// /// # Implementation Note /// /// This should always succeed on inputs that demonstrate that they do not require a /// proof, by revealing their transaction shape. - fn is_valid( - &self, - proof: ShapedProof<C>, - ) -> Result<Option<Self::ValidProof>, TransferLedgerError<C, Self>>; + fn is_valid(&self, proof: ShapedProof<C>) -> Option<Self::ValidProof>; } /// Dynamic Transfer Shape @@ -987,56 +972,22 @@ where } /// Transfer Post Error -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "TransferLedgerError<C, L>: Clone"), - Copy(bound = "TransferLedgerError<C, L>: Copy"), - Debug(bound = "TransferLedgerError<C, L>: Debug"), - Eq(bound = "TransferLedgerError<C, L>: Eq"), - Hash(bound = "TransferLedgerError<C, L>: Hash"), - PartialEq(bound = "TransferLedgerError<C, L>: PartialEq") -)] -pub enum TransferPostError<C, L> -where - C: Configuration, - L: TransferLedger<C>, -{ +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum TransferPostError { /// Sender Post Error - Sender(SenderPostError<C, L>), + Sender(SenderPostError), /// Receiver Post Error - Receiver(ReceiverPostError<C, L>), + Receiver(ReceiverPostError), /// Invalid Transfer Proof Error /// /// Validity of the transfer could not be proved by the ledger. InvalidProof, - - /// Ledger Error - LedgerError(TransferLedgerError<C, L>), } -impl<C, L> From<SenderPostError<C, L>> for TransferPostError<C, L> -where - C: Configuration, - L: TransferLedger<C>, -{ - #[inline] - fn from(err: SenderPostError<C, L>) -> Self { - Self::Sender(err) - } -} - -impl<C, L> From<ReceiverPostError<C, L>> for TransferPostError<C, L> -where - C: Configuration, - L: TransferLedger<C>, -{ - #[inline] - fn from(err: ReceiverPostError<C, L>) -> Self { - Self::Receiver(err) - } -} +from_variant_impl!(TransferPostError, Sender, SenderPostError); +from_variant_impl!(TransferPostError, Receiver, ReceiverPostError); /// Transfer Post pub struct TransferPost<C> @@ -1067,10 +1018,7 @@ where /// Validates `self` on the transfer `ledger`. #[inline] - pub fn validate<L>( - self, - ledger: &L, - ) -> Result<TransferPostingKey<C, L>, TransferPostError<C, L>> + pub fn validate<L>(self, ledger: &L) -> Result<TransferPostingKey<C, L>, TransferPostError> where L: TransferLedger<C>, { @@ -1085,10 +1033,7 @@ where .into_iter() .map(move |r| r.validate(ledger)) .collect::<Result<_, _>>()?, - validity_proof: match ledger - .is_valid(self.validity_proof) - .map_err(TransferPostError::LedgerError)? - { + validity_proof: match ledger.is_valid(self.validity_proof) { Some(key) => key, _ => return Err(TransferPostError::InvalidProof), }, @@ -1119,18 +1064,18 @@ where { /// Posts `self` to the transfer `ledger`. #[inline] - pub fn post( - self, - super_key: &TransferLedgerSuperPostingKey<C, L>, - ledger: &mut L, - ) -> Result<(), TransferLedgerError<C, L>> { - for key in self.sender_posting_keys { - key.post(&(self.validity_proof, *super_key), ledger)?; - } - for key in self.receiver_posting_keys { - key.post(&(self.validity_proof, *super_key), ledger)?; - } - Ok(()) + pub fn post(self, super_key: &TransferLedgerSuperPostingKey<C, L>, ledger: &mut L) -> bool { + // FIXME: This needs to be atomic! Add a `commit/rollback` method somewhere. + let proof = self.validity_proof; + let all_senders_posted = self + .sender_posting_keys + .into_iter() + .all(|k| k.post(&(proof, *super_key), ledger)); + let all_receivers_posted = self + .receiver_posting_keys + .into_iter() + .all(|k| k.post(&(proof, *super_key), ledger)); + all_senders_posted && all_receivers_posted } } diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs index b2b98ab33..a8f230025 100644 --- a/manta-accounting/src/wallet/ledger.rs +++ b/manta-accounting/src/wallet/ledger.rs @@ -21,18 +21,27 @@ use crate::{ identity::{Utxo, VoidNumber}, - transfer::{self, EncryptedAsset, TransferPost}, + transfer::{Configuration, EncryptedAsset, TransferPost}, }; use alloc::vec::Vec; use core::future::Future; +/// Ledger Checkpoint +pub trait Checkpoint: Default + PartialOrd { + /// Returns the number of receivers that have participated in transactions on the ledger so far. + fn receiver_index(&self) -> usize; + + /// Returns the number of senders that have participated in transactions on the ledger so far. + fn sender_index(&self) -> usize; +} + /// Ledger Source Connection pub trait Connection<C> where - C: transfer::Configuration, + C: Configuration, { /// Ledger State Checkpoint Type - type Checkpoint: Default + PartialOrd; + type Checkpoint: Checkpoint; /// Receiver Data Iterator Type type ReceiverData: IntoIterator<Item = (Utxo<C>, EncryptedAsset<C>)>; @@ -91,7 +100,7 @@ pub type PushResult<C, L> = Result<PushResponse, <L as Connection<C>>::Error>; /// See its documentation for more. pub struct PullResponse<C, L> where - C: transfer::Configuration, + C: Configuration, L: Connection<C> + ?Sized, { /// Current Ledger Checkpoint @@ -107,7 +116,7 @@ where /// See its documentation for more. pub struct PullAllResponse<C, L> where - C: transfer::Configuration, + C: Configuration, L: Connection<C> + ?Sized, { /// Current Ledger Checkpoint @@ -128,3 +137,12 @@ pub struct PushResponse { /// Successful Push pub success: bool, } + +/* TODO: +/// +pub struct Ledger<C> +where + C: Configuration, {} + +impl<C> TransferLedger<C> for Ledger<C> where C: Configuration {} +*/ diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 707aa3420..df976dada 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -75,11 +75,6 @@ where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, { - /* FIXME: - /// Ledger Checkpoint Type - type Checkpoint; - */ - /// Sync Future Type /// /// Future for the [`sync`](Self::sync) method. @@ -111,7 +106,12 @@ where /// Pushes updates from the ledger to the wallet, synchronizing it with the ledger state and /// returning an updated asset distribution. Depending on the `sync_state`, the signer will /// either commit to the current state before synchronizing or rollback to the previous state. - fn sync<I>(&mut self, updates: I, sync_state: SyncState) -> Self::SyncFuture + fn sync<I>( + &mut self, + sync_state: SyncState, + starting_index: usize, + updates: I, + ) -> Self::SyncFuture where I: IntoIterator<Item = (Utxo<C>, EncryptedAsset<C>)>; @@ -236,6 +236,9 @@ where /// Proof System Error ProofSystemError(ProofSystemError<C>), + /// Inconsistent Synchronization State + InconsistentSynchronization, + /// Signer Connection Error ConnectionError(CE), } @@ -675,17 +678,13 @@ where ) } - /// Updates the internal ledger state, returing the new asset distribution. + /// Updates the internal ledger state, returning the new asset distribution. #[inline] - fn sync<I>(&mut self, updates: I, sync_state: SyncState) -> SyncResult<D, C, Self> + fn sync_inner<I>(&mut self, updates: I) -> SyncResult<D, C, Self> where - I: IntoIterator<Item = (Utxo<C>, EncryptedAsset<C>)>, + I: Iterator<Item = (Utxo<C>, EncryptedAsset<C>)>, Standard: Distribution<AssetParameters<C>>, { - // FIXME: Add checkpoint to sync so that we can make sure we are synchronizing correctly. - - self.start_sync(sync_state); - let mut assets = Vec::new(); for (utxo, encrypted_asset) in updates { // TODO: Add optimization path where we have "strong" and "weak" insertions into the @@ -699,11 +698,30 @@ where // FIXME: Should this ever error? We should check the capacity at `updates`, then // insert should always work here. - let _ = self.utxo_set.try_insert(utxo); + let _ = self.utxo_set.insert(utxo); } Ok(SyncResponse::new(assets)) } + /// Updates the internal ledger state, returning the new asset distribution. + #[inline] + pub fn sync<I>( + &mut self, + sync_state: SyncState, + starting_index: usize, + updates: I, + ) -> SyncResult<D, C, Self> + where + I: IntoIterator<Item = (Utxo<C>, EncryptedAsset<C>)>, + Standard: Distribution<AssetParameters<C>>, + { + self.start_sync(sync_state); + match self.utxo_set.len().checked_sub(starting_index) { + Some(diff) => self.sync_inner(updates.into_iter().skip(diff)), + _ => Err(Error::InconsistentSynchronization), + } + } + /// Returns a [`Sender`] for the key at the given `index`. #[inline] fn get_sender(&self, index: Index<D>, asset: Asset) -> Result<Sender<C>, SenderError<D, C>> @@ -828,7 +846,7 @@ where /// Signs the `transaction`, generating transfer posts. #[inline] - fn sign(&mut self, transaction: Transaction<C>) -> SignResult<D, C, Self> + pub fn sign(&mut self, transaction: Transaction<C>) -> SignResult<D, C, Self> where Standard: Distribution<AssetParameters<C>>, { @@ -851,7 +869,7 @@ where /// Commits to the state after the last call to [`sign`](Self::sign). #[inline] - fn commit(&mut self) { + pub fn commit(&mut self) { self.signer.account.internal_range_shift_to_end(); self.utxo_set.commit(); self.pending_assets.commit(&mut self.assets); @@ -859,7 +877,7 @@ where /// Rolls back to the state before the last call to [`sign`](Self::sign). #[inline] - fn rollback(&mut self) { + pub fn rollback(&mut self) { self.signer.account.internal_range_shift_to_start(); self.utxo_set.rollback(); self.pending_assets.rollback(); @@ -867,12 +885,23 @@ where /// Commits or rolls back the state depending on the value of `sync_state`. #[inline] - fn start_sync(&mut self, sync_state: SyncState) { + pub fn start_sync(&mut self, sync_state: SyncState) { match sync_state { SyncState::Commit => self.commit(), SyncState::Rollback => self.rollback(), } } + + /// Generates a new [`ShieldedIdentity`] for `self` to receive assets. + #[inline] + pub fn external_receiver(&mut self) -> ExternalReceiverResult<D, C, Self> + where + Standard: Distribution<AssetParameters<C>>, + { + self.signer + .next_shielded(&self.commitment_scheme) + .map_err(Error::SecretKeyError) + } } impl<D, C, M, R> Connection<D, C> for FullSigner<D, C, M, R> @@ -897,11 +926,16 @@ where type Error = Infallible; #[inline] - fn sync<I>(&mut self, updates: I, sync_state: SyncState) -> Self::SyncFuture + fn sync<I>( + &mut self, + sync_state: SyncState, + starting_index: usize, + updates: I, + ) -> Self::SyncFuture where I: IntoIterator<Item = (Utxo<C>, EncryptedAsset<C>)>, { - future::ready(self.sync(updates, sync_state)) + future::ready(self.sync(sync_state, starting_index, updates)) } #[inline] @@ -927,11 +961,7 @@ where #[inline] fn external_receiver(&mut self) -> Self::ExternalReceiverFuture { - future::ready( - self.signer - .next_shielded(&self.commitment_scheme) - .map_err(Error::SecretKeyError), - ) + future::ready(self.external_receiver()) } } diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 53aa529ef..b442fe800 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -24,7 +24,7 @@ use crate::{ Configuration, ShieldedIdentity, }, wallet::{ - ledger::{self, PullResponse, PushResponse}, + ledger::{self, Checkpoint, PullResponse, PushResponse}, signer::{self, SignResponse, SyncState}, }, }; @@ -156,26 +156,26 @@ where } /// Wallet -pub struct Wallet<D, C, S, L, B = BTreeMapBalanceState> +pub struct Wallet<D, C, L, S, B = BTreeMapBalanceState> where D: DerivedSecretKeyGenerator, C: Configuration<SecretKey = D::SecretKey>, - S: signer::Connection<D, C>, L: ledger::Connection<C>, + S: signer::Connection<D, C>, B: BalanceState, { - /// Signer Connection - signer: S, - - /// Signer Synchronization State - sync_state: SyncState, - /// Ledger Connection ledger: L, /// Ledger Checkpoint checkpoint: L::Checkpoint, + /// Signer Connection + signer: S, + + /// Signer Synchronization State + sync_state: SyncState, + /// Balance State assets: B, @@ -183,12 +183,12 @@ where __: PhantomData<(D, C)>, } -impl<D, C, S, L, B> Wallet<D, C, S, L, B> +impl<D, C, L, S, B> Wallet<D, C, L, S, B> where D: DerivedSecretKeyGenerator, C: Configuration<SecretKey = D::SecretKey>, - S: signer::Connection<D, C>, L: ledger::Connection<C>, + S: signer::Connection<D, C>, B: BalanceState, { /// Builds a new [`Wallet`]. @@ -231,9 +231,9 @@ where /// Pulls data from the `ledger`, synchronizing the wallet and balance state. #[inline] - pub async fn sync(&mut self) -> Result<(), Error<D, C, S, L>> { - // FIXME: Add a checkpoint to the signer so that we can make sure that if `signer.sync` - // fails, we can catch up properly. + pub async fn sync(&mut self) -> Result<(), Error<D, C, L, S>> { + // TODO: How to recover from an `InconsistentSynchronization` error? Need some sort of + // recovery mode, like starting from the beginning of the state? let PullResponse { checkpoint, receiver_data, @@ -244,7 +244,11 @@ where .map_err(Error::LedgerError)?; self.assets.deposit_all( self.signer - .sync(receiver_data, self.sync_state) + .sync( + self.sync_state, + self.checkpoint.receiver_index(), + receiver_data, + ) .await? .assets, ); @@ -299,7 +303,7 @@ where /// This method returns an error in any other case. The internal state of the wallet is kept /// consistent between calls and recoverable errors are returned for the caller to handle. #[inline] - pub async fn post(&mut self, transaction: Transaction<C>) -> Result<bool, Error<D, C, S, L>> { + pub async fn post(&mut self, transaction: Transaction<C>) -> Result<bool, Error<D, C, L, S>> { self.sync().await?; let balance_update = self .check(&transaction) @@ -338,29 +342,29 @@ where /// /// This `enum` is the error state for [`Wallet`] methods. See [`sync`](Wallet::sync) and /// [`post`](Wallet::post) for more. -pub enum Error<D, C, S, L> +pub enum Error<D, C, L, S> where D: DerivedSecretKeyGenerator, C: Configuration<SecretKey = D::SecretKey>, + L: ledger::Connection<C>, S: signer::Connection<D, C>, - L: ledger::Connection<C> + ?Sized, { /// Insufficient Balance InsufficientBalance(Asset), - /// Signer Error - SignerError(signer::Error<D, C, S::Error>), - /// Ledger Error LedgerError(L::Error), + + /// Signer Error + SignerError(signer::Error<D, C, S::Error>), } -impl<D, C, S, L> From<signer::Error<D, C, S::Error>> for Error<D, C, S, L> +impl<D, C, L, S> From<signer::Error<D, C, S::Error>> for Error<D, C, L, S> where D: DerivedSecretKeyGenerator, C: Configuration<SecretKey = D::SecretKey>, + L: ledger::Connection<C>, S: signer::Connection<D, C>, - L: ledger::Connection<C> + ?Sized, { #[inline] fn from(err: signer::Error<D, C, S::Error>) -> Self { diff --git a/manta-crypto/src/set.rs b/manta-crypto/src/set.rs index 366249226..36e8b4b35 100644 --- a/manta-crypto/src/set.rs +++ b/manta-crypto/src/set.rs @@ -33,6 +33,15 @@ pub trait Set { /// Item Stored in the [`Set`] type Item; + /// Returns the number of elements that are contained in `self`. + fn len(&self) -> usize; + + /// Returns `true` if `self` contains no elements. + #[inline] + fn is_empty(&self) -> bool { + self.len() == 0 + } + /// Returns `true` if `item` is stored in `self`. fn contains(&self, item: &Self::Item) -> bool; diff --git a/manta-pay/src/accounting/ledger.rs b/manta-pay/src/accounting/ledger.rs index ecd21c743..d79f1f682 100644 --- a/manta-pay/src/accounting/ledger.rs +++ b/manta-pay/src/accounting/ledger.rs @@ -136,6 +136,12 @@ impl UtxoSet { impl Set for UtxoSet { type Item = Utxo; + #[inline] + fn len(&self) -> usize { + // FIXME: Implement. + todo!() + } + #[inline] fn contains(&self, item: &Self::Item) -> bool { self.utxo_exists(item) From 701e3b46309cc0d836dac2eab2820ff050ff4bbd Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 11 Oct 2021 17:02:41 -0400 Subject: [PATCH 088/275] WIP: finish main signing algorithm --- manta-accounting/src/identity.rs | 20 ++ manta-accounting/src/transfer.rs | 43 +++- manta-accounting/src/wallet/signer.rs | 253 ++++++++++++++++++----- manta-util/src/iter/chunk_by.rs | 95 +++++++++ manta-util/src/{ => iter}/mixed_chain.rs | 0 manta-util/src/iter/mod.rs | 58 ++++++ manta-util/src/lib.rs | 17 +- 7 files changed, 410 insertions(+), 76 deletions(-) create mode 100644 manta-util/src/iter/chunk_by.rs rename manta-util/src/{ => iter}/mixed_chain.rs (100%) create mode 100644 manta-util/src/iter/mod.rs diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 9d458eb6e..dabec1e5d 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -905,6 +905,16 @@ where identity.into_pre_sender(commitment_scheme, asset) } + /// + #[inline] + pub fn insert_utxo<S>(&self, utxo_set: &mut S) + where + S: VerifiedSet<Item = Utxo<C>>, + { + // FIXME: Need to improve the [`VerifiedSet`] trait before we can implement this. + todo!() + } + /// Requests the containment proof of `self.utxo` from `utxo_set` so that we can turn `self` /// into a [`Sender`]. #[inline] @@ -941,6 +951,16 @@ where utxo_containment_proof: proof.utxo_containment_proof, } } + + /// Tries to convert `self` into a [`Sender`] by getting a proof from `utxo_set`. + #[inline] + pub fn try_upgrade<S>(self, utxo_set: &S) -> Result<Sender<C, S>, S::ContainmentError> + where + S: VerifiedSet<Item = Utxo<C>>, + { + let proof = self.get_proof(utxo_set)?; + Ok(self.upgrade(proof)) + } } /// Sender diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index da6125353..a076a887b 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -43,7 +43,7 @@ use manta_crypto::{ ies::{EncryptedMessage, IntegratedEncryptionScheme}, set::{constraint::VerifiedSetVariable, VerifiedSet}, }; -use manta_util::{create_seal, from_variant_impl, mixed_chain, seal, Either}; +use manta_util::{create_seal, from_variant_impl, iter::mixed_chain, seal, Either}; use rand::{ distributions::{Distribution, Standard}, CryptoRng, RngCore, @@ -1062,20 +1062,41 @@ where C: Configuration, L: TransferLedger<C>, { + /// Posts `senders` to the transfer `ledger`. + #[inline] + fn post_senders( + senders: Vec<SenderPostingKey<C, L>>, + proof: &L::ValidProof, + super_key: &TransferLedgerSuperPostingKey<C, L>, + ledger: &mut L, + ) -> bool { + senders + .into_iter() + .all(|k| k.post(&(*proof, *super_key), ledger)) + } + + /// Posts `receivers` to the transfer `ledger`. + #[inline] + fn post_receivers( + receivers: Vec<ReceiverPostingKey<C, L>>, + proof: &L::ValidProof, + super_key: &TransferLedgerSuperPostingKey<C, L>, + ledger: &mut L, + ) -> bool { + receivers + .into_iter() + .all(|k| k.post(&(*proof, *super_key), ledger)) + } + /// Posts `self` to the transfer `ledger`. #[inline] pub fn post(self, super_key: &TransferLedgerSuperPostingKey<C, L>, ledger: &mut L) -> bool { - // FIXME: This needs to be atomic! Add a `commit/rollback` method somewhere. + // FIXME: This needs to be atomic! Add a `commit/rollback` method somewhere? Or can the + // ledger keep track of its own atomicity, so we have an "atomic-until-next-error" + // kind of behavior. let proof = self.validity_proof; - let all_senders_posted = self - .sender_posting_keys - .into_iter() - .all(|k| k.post(&(proof, *super_key), ledger)); - let all_receivers_posted = self - .receiver_posting_keys - .into_iter() - .all(|k| k.post(&(proof, *super_key), ledger)); - all_senders_posted && all_receivers_posted + Self::post_senders(self.sender_posting_keys, &proof, super_key, ledger) + && Self::post_receivers(self.receiver_posting_keys, &proof, super_key, ledger) } } diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index df976dada..c62439262 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -23,14 +23,13 @@ use crate::{ fs::{Load, LoadWith, Save, SaveWith}, identity::{self, AssetParameters, Identity, PreSender, Utxo}, keys::{ - Account, DerivedSecretKeyGenerator, External, ExternalIndex, Index, Internal, - InternalIndex, InternalKeyOwned, KeyKind, KeyOwned, + Account, DerivedSecretKeyGenerator, ExternalIndex, Index, InternalIndex, InternalKeyOwned, }, transfer::{ self, canonical::{Mint, PrivateTransfer, Reclaim, Transaction}, EncryptedAsset, IntegratedEncryptionSchemeError, ProofSystemError, ProvingContext, - Receiver, Sender, ShieldedIdentity, Transfer, TransferPost, + Receiver, SecretTransfer, Sender, ShieldedIdentity, Transfer, TransferPost, }, }; use alloc::{vec, vec::Vec}; @@ -43,6 +42,7 @@ use core::{ ops::Range, }; use manta_crypto::{Set, VerifiedSet}; +use manta_util::{fallible_array_map, into_array_unchecked, iter::IteratorExt}; use rand::{ distributions::{Distribution, Standard}, CryptoRng, RngCore, @@ -230,6 +230,9 @@ where /// Encryption Error EncryptionError(IntegratedEncryptionSchemeError<C>), + /// Containment Error + ContainmentError(<C::UtxoSet as VerifiedSet>::ContainmentError), + /// Insufficient Balance InsufficientBalance(Asset), @@ -257,6 +260,20 @@ where } } +impl<D, C, CE> From<SenderError<D, C>> for Error<D, C, CE> +where + D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, +{ + #[inline] + fn from(err: SenderError<D, C>) -> Self { + match err { + SenderError::SecretKeyError(err) => Self::SecretKeyError(err), + SenderError::ContainmentError(err) => Self::ContainmentError(err), + } + } +} + /// Signer pub struct Signer<D> where @@ -342,6 +359,22 @@ where .map(Identity::new) } + /// + #[inline] + pub fn get_pre_sender<C>( + &self, + index: Index<D>, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + ) -> Result<PreSender<C>, D::Error> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + Standard: Distribution<AssetParameters<C>>, + { + Ok(self.get(&index)?.into_pre_sender(commitment_scheme, asset)) + } + + /* TODO[remove]: /// Returns a [`Sender`] for the key at the given `index`. #[inline] pub fn get_sender<C>( @@ -360,6 +393,7 @@ where .into_sender(commitment_scheme, asset, utxo_set) .map_err(SenderError::ContainmentError) } + */ /// Generates the next external identity for this signer. #[inline] @@ -402,9 +436,9 @@ where .into_shielded(commitment_scheme)) } - /// Builds the next inner change receiver and pre-sender. + /// Builds the next accumulator receiver and pre-sender. #[inline] - pub fn next_inner_change<C, R>( + pub fn next_accumulator<C, R>( &mut self, commitment_scheme: &C::CommitmentScheme, asset: Asset, @@ -431,7 +465,24 @@ where )) } - /// Builds the final change receiver for the end of a transaction. + /// + #[inline] + pub fn next_zeroes<C, R>( + &mut self, + n: usize, + commitment_scheme: &C::CommitmentScheme, + asset_id: AssetId, + rng: &mut R, + ) -> Result<(Vec<InternalIndex<D>>, Vec<Receiver<C>>), InternalReceiverError<D, C>> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + R: CryptoRng + RngCore + ?Sized, + Standard: Distribution<AssetParameters<C>>, + { + todo!() + } + + /// Builds the change receiver for the end of a transaction. #[inline] pub fn next_change<C, R>( &mut self, @@ -722,6 +773,7 @@ where } } + /* TODO[remove]: /// Returns a [`Sender`] for the key at the given `index`. #[inline] fn get_sender(&self, index: Index<D>, asset: Asset) -> Result<Sender<C>, SenderError<D, C>> @@ -731,6 +783,7 @@ where self.signer .get_sender(index, &self.commitment_scheme, asset, &self.utxo_set) } + */ /// Builds a [`TransferPost`] for the given `transfer`. #[inline] @@ -765,7 +818,97 @@ where } } - /// Signs a withdraw transaction. + /// + #[inline] + fn accumulate_transfers<const SENDERS: usize, const RECEIVERS: usize>( + &mut self, + asset_id: AssetId, + mut pre_senders: Vec<PreSender<C>>, + ) -> Result< + ( + Vec<InternalIndex<D>>, + Vec<PreSender<C>>, + Vec<TransferPost<C>>, + ), + Error<D, C, Infallible>, + > + where + Standard: Distribution<AssetParameters<C>>, + { + assert!(SENDERS > 1, "There must be at least two senders."); + assert!(RECEIVERS > 1, "There must be at least two receivers."); + assert!( + !pre_senders.is_empty(), + "The set of initial senders cannot be empty." + ); + + let mut new_zeroes = Vec::new(); + let mut posts = Vec::new(); + + while pre_senders.len() > SENDERS { + let mut accumulators = Vec::new(); + let mut iter = pre_senders.into_iter().chunk_by::<SENDERS>(); + for pre_sender_side in &mut iter { + let sender_side = + fallible_array_map(pre_sender_side, |ps| ps.try_upgrade(&self.utxo_set)) + .map_err(Error::ContainmentError)?; + + let (receiver, pre_sender) = self.signer.next_accumulator( + &self.commitment_scheme, + asset_id.with(sender_side.iter().map(Sender::asset_value).sum()), + &mut self.rng, + )?; + + let (mut zeroes, mut receiver_side) = self.signer.next_zeroes( + RECEIVERS - 1, + &self.commitment_scheme, + asset_id, + &mut self.rng, + )?; + + receiver_side.push(receiver); + + posts.push( + self.build_post( + SecretTransfer::<_, SENDERS, RECEIVERS>::new( + sender_side, + into_array_unchecked(receiver_side), + ) + .into(), + )?, + ); + + new_zeroes.append(&mut zeroes); + accumulators.push(pre_sender); + } + + for pre_sender in accumulators.iter() { + pre_sender.insert_utxo(&mut self.utxo_set); + } + + accumulators.append(&mut iter.remainder()); + pre_senders = accumulators; + } + + Ok((new_zeroes, pre_senders, posts)) + } + + /// + #[inline] + fn resolve_sender_zeroes<const SENDERS: usize>( + &self, + sender_count: usize, + asset_id: AssetId, + new_zeroes: &mut Vec<InternalIndex<D>>, + ) -> Result<(Vec<Mint<C>>, Vec<PreSender<C>>), InternalReceiverError<D, C>> { + let zeroes = self + .assets + .zeroes(SENDERS.saturating_sub(sender_count), asset_id); + + todo!() + } + + /// Signs a withdraw transaction without resetting on error. #[inline] fn sign_withdraw_inner( &mut self, @@ -778,50 +921,67 @@ where let Selection { change, balances } = self.select(asset).map_err(Error::InsufficientBalance)?; - let zeroes = self - .assets - .zeroes(2_usize.saturating_sub(balances.len()), asset.id); - - let mut new_zeroes = vec![]; self.pending_assets.remove = balances.iter().map(move |(k, _)| k.clone()).collect(); - // FIXME: Implement signing: - /* - fn pad<T: Default, const N: usize>(mut v: Vec<T>) -> [T; N] { - v.extend(repeat_with(Default::default).take(N - v.len())); - into_array_unchecked(v) + let pre_senders = balances + .into_iter() + .map(|(k, v)| { + self.signer + .get_pre_sender(k, &self.commitment_scheme, asset.id.with(v)) + }) + .collect::<Result<_, _>>() + .map_err(Error::SecretKeyError)?; + + let (mut new_zeroes, mut pre_senders, mut posts) = + self.accumulate_transfers::<2, 2>(asset.id, pre_senders)?; + + let (mints, mut pre_sender_side) = + self.resolve_sender_zeroes::<2>(pre_senders.len(), asset.id, &mut new_zeroes)?; + + for mint in mints { + posts.insert(0, self.build_post(mint)?); } - fn generate<const M: usize, const N: usize>( - total: u128, - change: u128, - mut balances: Vec<u128>, - ) -> Vec<([u128; M], [u128; N])> { - assert!(M > 1, "M must be > 1!"); - assert!(N > 1, "N must be > 1!"); - assert!(!balances.is_empty(), "Balances cannot be empty!"); - - let mut posts = Vec::new(); - - while balances.len() > M { - let iter = balances.chunks_exact(M); - let mut accumulators = iter.remainder().to_vec(); - for senders in iter { - let accumulator = senders.iter().sum(); - posts.push((into_array_unchecked(senders), pad(vec![accumulator]))); - accumulators.push(accumulator); - } - balances = accumulators; + pre_sender_side.append(&mut pre_senders); + + let sender_side = into_array_unchecked( + pre_sender_side + .into_iter() + .map(|ps| ps.try_upgrade(&self.utxo_set)) + .collect::<Result<Vec<_>, _>>() + .map_err(Error::ContainmentError)?, + ); + + let (change_receiver, change_index) = self + .signer + .next_change( + &self.commitment_scheme, + asset.id.with(change), + &mut self.rng, + )? + .into(); + + let final_post = match receiver { + Some(receiver) => { + let receiver = receiver + .into_receiver(&self.commitment_scheme, asset, &mut self.rng) + .map_err(Error::EncryptionError)?; + self.build_post(PrivateTransfer::build( + sender_side, + [change_receiver, receiver], + ))? } + _ => self.build_post(Reclaim::build(sender_side, change_receiver, asset))?, + }; - posts.push((pad(balances), pad(vec![total, change]))); - posts - } - */ + posts.push(final_post); - let mut posts = Vec::new(); + self.pending_assets.insert = Some((change_index, asset.id.with(change))); + self.pending_assets.insert_zeroes = Some(( + asset.id, + new_zeroes.into_iter().map(Index::reduce).collect(), + )); - self.pending_assets.insert_zeroes = Some((asset.id, new_zeroes)); Ok(SignResponse::new(posts)) } @@ -837,9 +997,7 @@ where { let result = self.sign_withdraw_inner(asset, receiver); if result.is_err() { - // FIXME: Add recovery from error. - // TODO: Is this right: `self.rollback();` - todo!() + self.rollback(); } result } @@ -990,9 +1148,6 @@ where EncryptionError(IntegratedEncryptionSchemeError<C>), } -/// Internal Receiver Result Type -pub type InternalReceiverResult<D, C, T> = Result<T, InternalReceiverError<D, C>>; - /// Sender Error pub enum SenderError<D, C> where diff --git a/manta-util/src/iter/chunk_by.rs b/manta-util/src/iter/chunk_by.rs new file mode 100644 index 000000000..b0fc8babc --- /dev/null +++ b/manta-util/src/iter/chunk_by.rs @@ -0,0 +1,95 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Chunking Iterator + +// TODO: Add optimized implementations for other methods/iterators. + +use crate::into_array_unchecked; +use alloc::vec::Vec; +use core::iter::FusedIterator; + +/// Chunking Iterator +/// +/// This `struct` is created by the [`chunk_by`] method on [`IteratorExt`]. +/// See its documentation for more. +/// +/// [`chunk_by`]: crate::iter::IteratorExt::chunk_by +/// [`IteratorExt`]: crate::iter::IteratorExt +#[derive(Clone)] +#[must_use = "iterators are lazy and do nothing unless consumed"] +pub struct ChunkBy<I, const N: usize> +where + I: Iterator, +{ + /// Base Iterator + iter: I, + + /// Remainder + remainder: Option<Vec<I::Item>>, +} + +impl<I, const N: usize> ChunkBy<I, N> +where + I: Iterator, +{ + /// Builds a new [`ChunkBy`] iterator. + #[inline] + pub(super) fn new(iter: I) -> Self { + Self { + iter, + remainder: None, + } + } + + /// Returns the remainder of the iterator after consuming all of the chunks. + /// + /// # Panics + /// + /// This method panics if all of the chunks have not been consumed yet. To check if the chunks + /// have been consumed, call [`Iterator::next`] on `self` to see if it returns `None`. + #[inline] + pub fn remainder(self) -> Vec<I::Item> { + self.remainder.unwrap() + } +} + +impl<I, const N: usize> Iterator for ChunkBy<I, N> +where + I: Iterator, +{ + type Item = [I::Item; N]; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + if self.remainder.is_some() { + return None; + } + let mut vec = Vec::with_capacity(N); + for _ in 0..N { + match self.iter.next() { + Some(next) => vec.push(next), + _ => { + self.remainder = Some(vec); + return None; + } + } + } + Some(into_array_unchecked(vec)) + } +} + +impl<I, const N: usize> FusedIterator for ChunkBy<I, N> where I: Iterator {} diff --git a/manta-util/src/mixed_chain.rs b/manta-util/src/iter/mixed_chain.rs similarity index 100% rename from manta-util/src/mixed_chain.rs rename to manta-util/src/iter/mixed_chain.rs diff --git a/manta-util/src/iter/mod.rs b/manta-util/src/iter/mod.rs new file mode 100644 index 000000000..281e7a214 --- /dev/null +++ b/manta-util/src/iter/mod.rs @@ -0,0 +1,58 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Iteration Utilities + +mod chunk_by; +mod mixed_chain; + +pub use chunk_by::*; +pub use mixed_chain::*; + +/// Iterator Extensions +pub trait IteratorExt: Iterator { + /// Returns an iterator over chunks of size `N` from `iter`. + /// + /// # Note + /// + /// This is an alternative to [`ChunksExact`] but it works for any iterator and the + /// chunk size must be known at compile time. + /// + /// [`ChunksExact`]: core::slice::ChunksExact + #[inline] + fn chunk_by<const N: usize>(self) -> ChunkBy<Self, N> + where + Self: Sized, + { + ChunkBy::new(self) + } + + /// Folds every element into an accumulator by applying an operation, returning the final result. + /// + /// This function differs from [`Iterator::fold`] because its initial state is borrowed instead + /// of moved. This means that we have to return `Option<B>` in case the iterator is empty. + #[inline] + fn fold_ref<B, F>(mut self, init: &B, mut f: F) -> Option<B> + where + Self: Sized, + F: FnMut(&B, Self::Item) -> B, + { + self.next() + .map(move |first| self.fold(f(init, first), move |acc, n| f(&acc, n))) + } +} + +impl<I> IteratorExt for I where I: Iterator {} diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index ac9ea5b00..637c16226 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -25,9 +25,9 @@ extern crate alloc; mod array; mod concat; -mod mixed_chain; mod sealed; +pub mod iter; pub mod num; #[cfg(feature = "rand_core")] @@ -36,7 +36,6 @@ pub mod rand; pub use array::*; pub use concat::*; -pub use mixed_chain::*; /// Implements [`From`]`<$from>` for an enum `$to`, choosing the `$kind` variant. // TODO: add `where` clauses @@ -61,17 +60,3 @@ pub enum Either<L, R> { /// Right Variant Right(R), } - -/// Folds every element into an accumulator by applying an operation, returning the final result. -/// -/// This function differs from [`Iterator::fold`] because its initial state is borrowed instead -/// of moved. This means that we have to return `Option<B>` in case the iterator is empty. -#[inline] -pub fn fold_ref<I, B, F>(mut iter: I, init: &B, mut f: F) -> Option<B> -where - I: Iterator, - F: FnMut(&B, I::Item) -> B, -{ - iter.next() - .map(move |first| iter.fold(f(init, first), move |acc, n| f(&acc, n))) -} From 661a25352575287bd932acd387153a8783fca4c2 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 11 Oct 2021 19:47:22 -0400 Subject: [PATCH 089/275] WIP: cleaning up signing algorithm impl --- manta-accounting/src/asset.rs | 110 +++++++ manta-accounting/src/identity.rs | 2 +- manta-accounting/src/transfer.rs | 26 +- manta-accounting/src/wallet/signer.rs | 395 +++++++++++++++----------- 4 files changed, 370 insertions(+), 163 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 424682a30..9e3b1fb6e 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -27,8 +27,10 @@ use core::{ convert::TryFrom, fmt::Debug, hash::Hash, + iter, iter::{FusedIterator, Sum}, ops::{Add, AddAssign, Mul, Sub, SubAssign}, + slice, }; use derive_more::{ Add, AddAssign, Display, Div, DivAssign, From, Mul, MulAssign, Product, Sub, SubAssign, Sum, @@ -690,6 +692,114 @@ where pub fn is_empty(&self) -> bool { self.balances.is_empty() } + + /// Returns an iterator over [`self.balances`](Self::balances) by reference. + #[inline] + pub fn iter(&self) -> SelectionIter<M> { + SelectionIter::new(self.balances.iter()) + } + + /// Returns an iterator over the keys in [`self.balances`](Self::balances) by reference. + #[inline] + pub fn keys(&self) -> SelectionKeys<M> { + SelectionKeys::new(self.balances.iter().map(move |(key, _)| key)) + } +} + +/// [`SelectionIter`] Iterator Type +type SelectionIterType<'s, M> = slice::Iter<'s, (<M as AssetMap>::Key, AssetBalance)>; + +/// Selection Iterator +/// +/// This `struct` is created by the [`iter`](Selection::iter) method on [`Selection`]. +/// See its documentation for more. +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""), Debug(bound = "M::Key: Debug"))] +pub struct SelectionIter<'s, M> +where + M: AssetMap + ?Sized, +{ + /// Base Iterator + iter: SelectionIterType<'s, M>, +} + +impl<'s, M> SelectionIter<'s, M> +where + M: AssetMap + ?Sized, +{ + /// Builds a new [`SelectionIter`] from `iter`. + #[inline] + fn new(iter: SelectionIterType<'s, M>) -> Self { + Self { iter } + } +} + +// TODO: Implement all optimized methods/traits. +impl<'s, M> Iterator for SelectionIter<'s, M> +where + M: AssetMap + ?Sized, +{ + type Item = &'s (M::Key, AssetBalance); + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + self.iter.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + self.iter.size_hint() + } +} + +/// [`SelectionKeys`] Map Function Type +type SelectionKeysMapFnType<'s, M> = + fn(&'s (<M as AssetMap>::Key, AssetBalance)) -> &'s <M as AssetMap>::Key; + +/// [`SelectionKeys`] Iterator Type +type SelectionKeysType<'s, M> = iter::Map<SelectionIterType<'s, M>, SelectionKeysMapFnType<'s, M>>; + +/// Selection Keys Iterator +/// +/// This `struct` is created by the [`keys`](Selection::keys) method on [`Selection`]. +/// See its documentation for more. +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""), Debug(bound = "M::Key: Debug"))] +pub struct SelectionKeys<'s, M> +where + M: AssetMap + ?Sized, +{ + /// Base Iterator + iter: SelectionKeysType<'s, M>, +} + +impl<'s, M> SelectionKeys<'s, M> +where + M: AssetMap + ?Sized, +{ + /// Builds a new [`SelectionKeys`] from `iter`. + #[inline] + fn new(iter: SelectionKeysType<'s, M>) -> Self { + Self { iter } + } +} + +// TODO: Implement all optimized methods/traits. +impl<'s, M> Iterator for SelectionKeys<'s, M> +where + M: AssetMap + ?Sized, +{ + type Item = &'s M::Key; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + self.iter.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + self.iter.size_hint() + } } /// Testing Suite diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index dabec1e5d..21c66c5da 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -905,7 +905,7 @@ where identity.into_pre_sender(commitment_scheme, asset) } - /// + /// Inserts the [`Utxo`] corresponding to `self` into the `utxo_set`. #[inline] pub fn insert_utxo<S>(&self, utxo_set: &mut S) where diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index a076a887b..68c8743c3 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -1124,7 +1124,7 @@ pub trait Shape: sealed::Sealed { /// Canonical Transaction Types pub mod canonical { use super::*; - use crate::identity::{AssetParameters, Identity}; + use crate::identity::{AssetParameters, Identity, PreSender}; /// Implements [`Shape`] for a given shape type. macro_rules! impl_shape { @@ -1210,6 +1210,30 @@ pub mod canonical { )?, )) } + + /// Builds a [`Mint`] from an `identity` for an [`Asset`] with the given `asset_id` but + /// zero value. + #[inline] + pub fn zero<R>( + identity: Identity<C>, + commitment_scheme: &C::CommitmentScheme, + asset_id: AssetId, + rng: &mut R, + ) -> Result<(Mint<C>, PreSender<C>), IntegratedEncryptionSchemeError<C>> + where + R: CryptoRng + RngCore + ?Sized, + Standard: Distribution<AssetParameters<C>>, + { + // TODO: Make this more convenient. + let asset = Asset::zero(asset_id); + let (receiver, open_spend) = identity + .into_internal_receiver(commitment_scheme, asset, rng)? + .into(); + Ok(( + Mint::build(asset, receiver), + open_spend.into_pre_sender(commitment_scheme), + )) + } } /// Private Transfer Transaction Shape diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index c62439262..8059ba665 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -19,11 +19,12 @@ // TODO: Use universal transfers instead of just the canonical ones. use crate::{ - asset::{Asset, AssetId, AssetMap, Selection}, + asset::{Asset, AssetBalance, AssetId, AssetMap}, fs::{Load, LoadWith, Save, SaveWith}, identity::{self, AssetParameters, Identity, PreSender, Utxo}, keys::{ Account, DerivedSecretKeyGenerator, ExternalIndex, Index, InternalIndex, InternalKeyOwned, + KeyOwned, }, transfer::{ self, @@ -219,7 +220,7 @@ where } /// Signer Error -pub enum Error<D, C, CE> +pub enum Error<D, C, CE = Infallible> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, @@ -260,20 +261,6 @@ where } } -impl<D, C, CE> From<SenderError<D, C>> for Error<D, C, CE> -where - D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, -{ - #[inline] - fn from(err: SenderError<D, C>) -> Self { - match err { - SenderError::SecretKeyError(err) => Self::SecretKeyError(err), - SenderError::ContainmentError(err) => Self::ContainmentError(err), - } - } -} - /// Signer pub struct Signer<D> where @@ -359,7 +346,7 @@ where .map(Identity::new) } - /// + /// Returns a [`PreSender`] for the key at the given `index`. #[inline] pub fn get_pre_sender<C>( &self, @@ -374,27 +361,6 @@ where Ok(self.get(&index)?.into_pre_sender(commitment_scheme, asset)) } - /* TODO[remove]: - /// Returns a [`Sender`] for the key at the given `index`. - #[inline] - pub fn get_sender<C>( - &self, - index: Index<D>, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - utxo_set: &C::UtxoSet, - ) -> Result<Sender<C>, SenderError<D, C>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - Standard: Distribution<AssetParameters<C>>, - { - self.get(&index) - .map_err(SenderError::SecretKeyError)? - .into_sender(commitment_scheme, asset, utxo_set) - .map_err(SenderError::ContainmentError) - } - */ - /// Generates the next external identity for this signer. #[inline] fn next_external_identity<C>(&mut self) -> Result<Identity<C>, D::Error> @@ -436,9 +402,46 @@ where .into_shielded(commitment_scheme)) } + /// Builds the next zero accumulator receivers and pre-senders. + #[inline] + fn next_zeroes<C, R, const RECEIVERS: usize>( + &mut self, + commitment_scheme: &C::CommitmentScheme, + asset_id: AssetId, + rng: &mut R, + ) -> Result< + (Vec<Receiver<C>>, Vec<InternalKeyOwned<D, PreSender<C>>>), + InternalReceiverError<D, C>, + > + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + R: CryptoRng + RngCore + ?Sized, + Standard: Distribution<AssetParameters<C>>, + { + let mut receivers = Vec::with_capacity(RECEIVERS); + let mut pre_senders = Vec::with_capacity(RECEIVERS - 1); + for _ in 0..RECEIVERS - 1 { + let (identity, index) = self + .next_internal_identity() + .map_err(InternalReceiverError::SecretKeyError)? + .into(); + let internal_receiver = identity + .into_internal_receiver(commitment_scheme, Asset::zero(asset_id), rng) + .map_err(InternalReceiverError::EncryptionError)?; + receivers.push(internal_receiver.receiver); + pre_senders.push(KeyOwned::new( + internal_receiver + .open_spend + .into_pre_sender(commitment_scheme), + index, + )); + } + Ok((receivers, pre_senders)) + } + /// Builds the next accumulator receiver and pre-sender. #[inline] - pub fn next_accumulator<C, R>( + fn next_accumulator<C, R>( &mut self, commitment_scheme: &C::CommitmentScheme, asset: Asset, @@ -465,21 +468,37 @@ where )) } - /// + /// Builds the next accumulated receiver. #[inline] - pub fn next_zeroes<C, R>( + pub fn next_accumulated_receiver<C, R, const RECEIVERS: usize>( &mut self, - n: usize, commitment_scheme: &C::CommitmentScheme, asset_id: AssetId, + sender_sum: AssetBalance, rng: &mut R, - ) -> Result<(Vec<InternalIndex<D>>, Vec<Receiver<C>>), InternalReceiverError<D, C>> + ) -> Result< + ( + [Receiver<C>; RECEIVERS], + PreSender<C>, + Vec<InternalKeyOwned<D, PreSender<C>>>, + ), + InternalReceiverError<D, C>, + > where C: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, Standard: Distribution<AssetParameters<C>>, { - todo!() + let (mut receivers, zero_pre_senders) = + self.next_zeroes::<_, _, RECEIVERS>(commitment_scheme, asset_id, rng)?; + let (receiver, pre_sender) = + self.next_accumulator(commitment_scheme, asset_id.with(sender_sum), rng)?; + receivers.push(receiver); + Ok(( + into_array_unchecked(receivers), + pre_sender, + zero_pre_senders, + )) } /// Builds the change receiver for the end of a transaction. @@ -533,6 +552,31 @@ where )) } + /// Builds a [`Mint`] transaction to mint a zero asset with the given `asset_id`, returning a + /// [`PreSender`] for that asset. + #[inline] + pub fn mint_zero<C, R>( + &mut self, + commitment_scheme: &C::CommitmentScheme, + asset_id: AssetId, + rng: &mut R, + ) -> Result<(Mint<C>, PreSender<C>), InternalReceiverError<D, C>> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + R: CryptoRng + RngCore + ?Sized, + Standard: Distribution<AssetParameters<C>>, + { + Mint::zero( + self.next_internal_identity() + .map_err(InternalReceiverError::SecretKeyError)? + .unwrap(), + commitment_scheme, + asset_id, + rng, + ) + .map_err(InternalReceiverError::EncryptionError) + } + /// Looks for an index that can decrypt the given `encrypted_asset`. #[inline] pub fn find_external_asset<C>( @@ -773,17 +817,38 @@ where } } - /* TODO[remove]: - /// Returns a [`Sender`] for the key at the given `index`. + /// Returns a [`PreSender`] for the key at the given `index`. #[inline] - fn get_sender(&self, index: Index<D>, asset: Asset) -> Result<Sender<C>, SenderError<D, C>> + fn get_pre_sender(&self, index: Index<D>, asset: Asset) -> Result<PreSender<C>, Error<D, C>> where Standard: Distribution<AssetParameters<C>>, { self.signer - .get_sender(index, &self.commitment_scheme, asset, &self.utxo_set) + .get_pre_sender(index, &self.commitment_scheme, asset) + .map_err(Error::SecretKeyError) + } + + /// Selects the pre-senders which collectively own at least `asset`, returning any change. + #[inline] + fn select(&mut self, asset: Asset) -> Result<(AssetBalance, Vec<PreSender<C>>), Error<D, C>> + where + Standard: Distribution<AssetParameters<C>>, + { + let selection = self.assets.select(asset); + if selection.is_empty() { + return Err(Error::InsufficientBalance(asset)); + } + + self.pending_assets.remove = selection.keys().cloned().collect(); + + let pre_senders = selection + .balances + .into_iter() + .map(move |(k, v)| self.get_pre_sender(k, asset.id.with(v))) + .collect::<Result<_, _>>()?; + + Ok((selection.change, pre_senders)) } - */ /// Builds a [`TransferPost`] for the given `transfer`. #[inline] @@ -794,9 +859,10 @@ where const SINKS: usize, >( &mut self, - transfer: Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>, - ) -> Result<TransferPost<C>, Error<D, C, Infallible>> { + transfer: impl Into<Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>>, + ) -> Result<TransferPost<C>, Error<D, C>> { transfer + .into() .into_post( &self.commitment_scheme, &self.utxo_set, @@ -806,19 +872,7 @@ where .map_err(Error::ProofSystemError) } - /// Selects `asset` from the asset distribution, returning it back if there was an insufficient - /// balance. - #[inline] - fn select(&self, asset: Asset) -> Result<Selection<M>, Asset> { - let selection = self.assets.select(asset); - if selection.is_empty() { - Err(asset) - } else { - Ok(selection) - } - } - - /// + /// Accumulate transfers using the `SENDERS -> RECEIVERS` shape. #[inline] fn accumulate_transfers<const SENDERS: usize, const RECEIVERS: usize>( &mut self, @@ -826,17 +880,23 @@ where mut pre_senders: Vec<PreSender<C>>, ) -> Result< ( - Vec<InternalIndex<D>>, + Vec<InternalKeyOwned<D, PreSender<C>>>, Vec<PreSender<C>>, Vec<TransferPost<C>>, ), - Error<D, C, Infallible>, + Error<D, C>, > where Standard: Distribution<AssetParameters<C>>, { - assert!(SENDERS > 1, "There must be at least two senders."); - assert!(RECEIVERS > 1, "There must be at least two receivers."); + assert!( + SENDERS > 1, + "The transfer shape must include at least two senders." + ); + assert!( + RECEIVERS > 1, + "The transfer shape must include at least two receivers." + ); assert!( !pre_senders.is_empty(), "The set of initial senders cannot be empty." @@ -848,38 +908,22 @@ where while pre_senders.len() > SENDERS { let mut accumulators = Vec::new(); let mut iter = pre_senders.into_iter().chunk_by::<SENDERS>(); - for pre_sender_side in &mut iter { - let sender_side = - fallible_array_map(pre_sender_side, |ps| ps.try_upgrade(&self.utxo_set)) - .map_err(Error::ContainmentError)?; - - let (receiver, pre_sender) = self.signer.next_accumulator( - &self.commitment_scheme, - asset_id.with(sender_side.iter().map(Sender::asset_value).sum()), - &mut self.rng, - )?; - - let (mut zeroes, mut receiver_side) = self.signer.next_zeroes( - RECEIVERS - 1, - &self.commitment_scheme, - asset_id, - &mut self.rng, - )?; - - receiver_side.push(receiver); - - posts.push( - self.build_post( - SecretTransfer::<_, SENDERS, RECEIVERS>::new( - sender_side, - into_array_unchecked(receiver_side), - ) - .into(), - )?, - ); + for chunk in &mut iter { + let senders = fallible_array_map(chunk, |ps| ps.try_upgrade(&self.utxo_set)) + .map_err(Error::ContainmentError)?; + + let (receivers, accumulator, mut zeroes) = + self.signer.next_accumulated_receiver::<_, _, RECEIVERS>( + &self.commitment_scheme, + asset_id, + senders.iter().map(Sender::asset_value).sum(), + &mut self.rng, + )?; + + posts.push(self.build_post(SecretTransfer::new(senders, receivers))?); new_zeroes.append(&mut zeroes); - accumulators.push(pre_sender); + accumulators.push(accumulator); } for pre_sender in accumulators.iter() { @@ -893,19 +937,95 @@ where Ok((new_zeroes, pre_senders, posts)) } - /// + /// Prepare final pre-senders for the transaction. #[inline] - fn resolve_sender_zeroes<const SENDERS: usize>( - &self, - sender_count: usize, + fn prepare_final_pre_senders<const SENDERS: usize>( + &mut self, + asset_id: AssetId, + mut new_zeroes: Vec<InternalKeyOwned<D, PreSender<C>>>, + pre_senders: &mut Vec<PreSender<C>>, + posts: &mut Vec<TransferPost<C>>, + ) -> Result<(), Error<D, C>> + where + Standard: Distribution<AssetParameters<C>>, + { + let mut needed_zeroes = SENDERS - pre_senders.len(); + if needed_zeroes == 0 { + return Ok(()); + } + + let zeroes = self.assets.zeroes(needed_zeroes, asset_id); + needed_zeroes -= zeroes.len(); + + for zero in zeroes { + pre_senders.push(self.get_pre_sender(zero, Asset::zero(asset_id))?); + } + + if needed_zeroes == 0 { + return Ok(()); + } + + let needed_mints = needed_zeroes.saturating_sub(new_zeroes.len()); + + for _ in 0..needed_zeroes { + match new_zeroes.pop() { + Some(zero) => pre_senders.push(zero.unwrap()), + _ => break, + } + } + + self.pending_assets.insert_zeroes = Some(( + asset_id, + new_zeroes + .into_iter() + .map(move |z| z.index.reduce()) + .collect(), + )); + + if needed_mints == 0 { + return Ok(()); + } + + for _ in 0..needed_mints { + let (mint, pre_sender) = + self.signer + .mint_zero(&self.commitment_scheme, asset_id, &mut self.rng)?; + pre_senders.push(pre_sender); + posts.push(self.build_post(mint)?); + } + + Ok(()) + } + + /// Returns the next change receiver for `asset`. + #[inline] + fn next_change( + &mut self, asset_id: AssetId, - new_zeroes: &mut Vec<InternalIndex<D>>, - ) -> Result<(Vec<Mint<C>>, Vec<PreSender<C>>), InternalReceiverError<D, C>> { - let zeroes = self - .assets - .zeroes(SENDERS.saturating_sub(sender_count), asset_id); + change: AssetBalance, + ) -> Result<Receiver<C>, Error<D, C>> + where + Standard: Distribution<AssetParameters<C>>, + { + let asset = asset_id.with(change); + let (receiver, index) = self + .signer + .next_change(&self.commitment_scheme, asset, &mut self.rng)? + .into(); + self.pending_assets.insert = Some((index, asset)); + Ok(receiver) + } - todo!() + /// Prepares a given [`ShieldedIdentity`] for receiving `asset`. + #[inline] + pub fn prepare_receiver( + &mut self, + asset: Asset, + receiver: ShieldedIdentity<C>, + ) -> Result<Receiver<C>, Error<D, C>> { + receiver + .into_receiver(&self.commitment_scheme, asset, &mut self.rng) + .map_err(Error::EncryptionError) } /// Signs a withdraw transaction without resetting on error. @@ -918,54 +1038,26 @@ where where Standard: Distribution<AssetParameters<C>>, { - let Selection { change, balances } = - self.select(asset).map_err(Error::InsufficientBalance)?; - - self.pending_assets.remove = balances.iter().map(move |(k, _)| k.clone()).collect(); + let (change, pre_senders) = self.select(asset)?; - let pre_senders = balances - .into_iter() - .map(|(k, v)| { - self.signer - .get_pre_sender(k, &self.commitment_scheme, asset.id.with(v)) - }) - .collect::<Result<_, _>>() - .map_err(Error::SecretKeyError)?; - - let (mut new_zeroes, mut pre_senders, mut posts) = + let (new_zeroes, mut pre_senders, mut posts) = self.accumulate_transfers::<2, 2>(asset.id, pre_senders)?; - let (mints, mut pre_sender_side) = - self.resolve_sender_zeroes::<2>(pre_senders.len(), asset.id, &mut new_zeroes)?; - - for mint in mints { - posts.insert(0, self.build_post(mint)?); - } - - pre_sender_side.append(&mut pre_senders); + self.prepare_final_pre_senders::<2>(asset.id, new_zeroes, &mut pre_senders, &mut posts)?; let sender_side = into_array_unchecked( - pre_sender_side + pre_senders .into_iter() .map(|ps| ps.try_upgrade(&self.utxo_set)) .collect::<Result<Vec<_>, _>>() .map_err(Error::ContainmentError)?, ); - let (change_receiver, change_index) = self - .signer - .next_change( - &self.commitment_scheme, - asset.id.with(change), - &mut self.rng, - )? - .into(); + let change_receiver = self.next_change(asset.id, change)?; let final_post = match receiver { Some(receiver) => { - let receiver = receiver - .into_receiver(&self.commitment_scheme, asset, &mut self.rng) - .map_err(Error::EncryptionError)?; + let receiver = self.prepare_receiver(asset, receiver)?; self.build_post(PrivateTransfer::build( sender_side, [change_receiver, receiver], @@ -976,12 +1068,6 @@ where posts.push(final_post); - self.pending_assets.insert = Some((change_index, asset.id.with(change))); - self.pending_assets.insert_zeroes = Some(( - asset.id, - new_zeroes.into_iter().map(Index::reduce).collect(), - )); - Ok(SignResponse::new(posts)) } @@ -1147,16 +1233,3 @@ where /// Encryption Error EncryptionError(IntegratedEncryptionSchemeError<C>), } - -/// Sender Error -pub enum SenderError<D, C> -where - D: DerivedSecretKeyGenerator, - C: transfer::Configuration, -{ - /// Secret Key Generator Error - SecretKeyError(D::Error), - - /// Containment Error - ContainmentError(<C::UtxoSet as VerifiedSet>::ContainmentError), -} From 6d95e7f46237dc4a632eff63fb90b128f14d5ccb Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 12 Oct 2021 02:12:24 -0400 Subject: [PATCH 090/275] fix VerifiedSet interfaces --- manta-accounting/src/identity.rs | 113 ++++---- manta-accounting/src/transfer.rs | 66 +++-- manta-accounting/src/wallet/signer.rs | 28 +- manta-crypto/Cargo.toml | 2 +- manta-crypto/src/constraint.rs | 11 +- manta-crypto/src/ies.rs | 2 +- manta-crypto/src/lib.rs | 1 - manta-crypto/src/set.rs | 252 +++++++++--------- manta-pay/src/accounting/config.rs | 7 +- manta-pay/src/accounting/ledger.rs | 113 ++++---- .../arkworks/proof_systems/groth16.rs | 4 +- 11 files changed, 305 insertions(+), 294 deletions(-) diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 21c66c5da..528fc8a06 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -23,7 +23,7 @@ use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use manta_crypto::{ commitment::{CommitmentScheme, Input as CommitmentInput}, ies::{self, EncryptedMessage, IntegratedEncryptionScheme}, - set::{ContainmentProof, VerifiedSet}, + set::{MembershipProof, VerifiedSet}, PseudorandomFunctionFamily, }; use rand::{ @@ -399,7 +399,7 @@ where commitment_scheme: &C::CommitmentScheme, asset: Asset, utxo_set: &S, - ) -> Result<Sender<C, S>, S::ContainmentError> + ) -> Option<Sender<C, S>> where S: VerifiedSet<Item = Utxo<C>>, Standard: Distribution<AssetParameters<C>>, @@ -407,8 +407,8 @@ where let parameters = self.parameters(); let (public_key, void_number_commitment, utxo) = self.construct_utxo(commitment_scheme, &asset, &parameters); - Ok(Sender { - utxo_containment_proof: utxo_set.get_containment_proof(&utxo)?, + Some(Sender { + utxo_membership_proof: utxo_set.get_membership_proof(&utxo)?, void_number: self.void_number(&parameters.void_number_generator), secret_key: self.secret_key, public_key, @@ -600,23 +600,22 @@ where /// [`into_sender`]: Spend::into_sender #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "I::Error: Clone, S::ContainmentError: Clone"), - Copy(bound = "I::Error: Copy, S::ContainmentError: Copy"), - Debug(bound = "I::Error: Debug, S::ContainmentError: Debug"), - Eq(bound = "I::Error: Eq, S::ContainmentError: Eq"), - Hash(bound = "I::Error: Hash, S::ContainmentError: Hash"), - PartialEq(bound = "I::Error: PartialEq, S::ContainmentError: PartialEq") + Clone(bound = "I::Error: Clone"), + Copy(bound = "I::Error: Copy"), + Debug(bound = "I::Error: Debug"), + Eq(bound = "I::Error: Eq"), + Hash(bound = "I::Error: Hash"), + PartialEq(bound = "I::Error: PartialEq") )] -pub enum SpendError<I, S> +pub enum SpendError<I> where I: IntegratedEncryptionScheme<Plaintext = Asset>, - S: VerifiedSet, { /// Encryption Error EncryptionError(I::Error), - /// Missing UTXO Containment Proof - MissingUtxo(S::ContainmentError), + /// Missing UTXO Membership Proof + MissingUtxo, } /// Spending Information @@ -691,7 +690,7 @@ where commitment_scheme: &C::CommitmentScheme, encrypted_asset: EncryptedMessage<I>, utxo_set: &S, - ) -> Result<Sender<C, S>, SpendError<I, S>> + ) -> Result<Sender<C, S>, SpendError<I>> where S: VerifiedSet<Item = Utxo<C>>, Standard: Distribution<AssetParameters<C>>, @@ -699,7 +698,7 @@ where self.try_open(&encrypted_asset) .map_err(SpendError::EncryptionError)? .into_sender(commitment_scheme, utxo_set) - .map_err(SpendError::MissingUtxo) + .ok_or(SpendError::MissingUtxo) } } @@ -787,7 +786,7 @@ where self, commitment_scheme: &C::CommitmentScheme, utxo_set: &S, - ) -> Result<Sender<C, S>, S::ContainmentError> + ) -> Option<Sender<C, S>> where S: VerifiedSet<Item = Utxo<C>>, Standard: Distribution<AssetParameters<C>>, @@ -830,8 +829,8 @@ where C: Configuration, S: VerifiedSet<Item = Utxo<C>>, { - /// UTXO Containment Proof - utxo_containment_proof: ContainmentProof<S>, + /// UTXO Membership Proof + utxo_membership_proof: MembershipProof<S>, /// Type Parameter Marker __: PhantomData<C>, @@ -845,7 +844,7 @@ where /// Returns `true` if a [`PreSender`] could be upgraded using `self` given the `utxo_set`. #[inline] pub fn can_upgrade(&self, utxo_set: &S) -> bool { - self.utxo_containment_proof.check_public_input(utxo_set) + self.utxo_membership_proof.verify_public(utxo_set) } /// Upgrades the `pre_sender` to a [`Sender`] by attaching `self` to it. @@ -907,23 +906,22 @@ where /// Inserts the [`Utxo`] corresponding to `self` into the `utxo_set`. #[inline] - pub fn insert_utxo<S>(&self, utxo_set: &mut S) + pub fn insert_utxo<S>(&self, utxo_set: &mut S) -> bool where S: VerifiedSet<Item = Utxo<C>>, { - // FIXME: Need to improve the [`VerifiedSet`] trait before we can implement this. - todo!() + utxo_set.insert(&self.utxo) } - /// Requests the containment proof of `self.utxo` from `utxo_set` so that we can turn `self` + /// Requests the membership proof of `self.utxo` from `utxo_set` so that we can turn `self` /// into a [`Sender`]. #[inline] - pub fn get_proof<S>(&self, utxo_set: &S) -> Result<SenderProof<C, S>, S::ContainmentError> + pub fn get_proof<S>(&self, utxo_set: &S) -> Option<SenderProof<C, S>> where S: VerifiedSet<Item = Utxo<C>>, { - Ok(SenderProof { - utxo_containment_proof: utxo_set.get_containment_proof(&self.utxo)?, + Some(SenderProof { + utxo_membership_proof: utxo_set.get_membership_proof(&self.utxo)?, __: PhantomData, }) } @@ -948,18 +946,18 @@ where void_number: self.void_number, void_number_commitment: self.void_number_commitment, utxo: self.utxo, - utxo_containment_proof: proof.utxo_containment_proof, + utxo_membership_proof: proof.utxo_membership_proof, } } /// Tries to convert `self` into a [`Sender`] by getting a proof from `utxo_set`. #[inline] - pub fn try_upgrade<S>(self, utxo_set: &S) -> Result<Sender<C, S>, S::ContainmentError> + pub fn try_upgrade<S>(self, utxo_set: &S) -> Option<Sender<C, S>> where S: VerifiedSet<Item = Utxo<C>>, { let proof = self.get_proof(utxo_set)?; - Ok(self.upgrade(proof)) + Some(self.upgrade(proof)) } } @@ -990,8 +988,8 @@ where /// Unspent Transaction Output utxo: Utxo<C>, - /// UTXO Containment Proof - utxo_containment_proof: ContainmentProof<S>, + /// UTXO Membership Proof + utxo_membership_proof: MembershipProof<S>, } impl<C, S> Sender<C, S> @@ -1006,7 +1004,7 @@ where commitment_scheme: &C::CommitmentScheme, asset: Asset, utxo_set: &S, - ) -> Result<Self, S::ContainmentError> + ) -> Option<Self> where Standard: Distribution<AssetParameters<C>>, { @@ -1025,7 +1023,9 @@ where self.asset.value } - /// Reverts `self` back into a [`PreSender`] if its [`Utxo`] containment proof was deemed + /// Reverts `self` back into a [`PreSender`]. + /// + /// This method should be called if the [`Utxo`] membership proof attached to `self` was deemed /// invalid or had expired. #[inline] pub fn downgrade(self) -> PreSender<C> { @@ -1045,7 +1045,7 @@ where pub fn into_post(self) -> SenderPost<C, S> { SenderPost { void_number: self.void_number, - utxo_containment_proof_public_input: self.utxo_containment_proof.into_public_input(), + utxo_membership_proof_public: self.utxo_membership_proof.into_public(), } } } @@ -1132,8 +1132,8 @@ where /// Void Number void_number: VoidNumber<C>, - /// UTXO Containment Proof Public Input - utxo_containment_proof_public_input: S::Public, + /// UTXO Membership Proof Public Part + utxo_membership_proof_public: S::Public, } impl<C, S> SenderPost<C, S> @@ -1152,8 +1152,8 @@ where Some(key) => key, _ => return Err(SenderPostError::AssetSpent), }, - utxo_containment_proof_public_input: match ledger - .is_valid_utxo_state(self.utxo_containment_proof_public_input) + utxo_membership_proof_public: match ledger + .is_valid_utxo_state(self.utxo_membership_proof_public) { Some(key) => key, _ => return Err(SenderPostError::InvalidUtxoState), @@ -1183,8 +1183,8 @@ where /// Void Number Posting Key void_number: L::ValidVoidNumber, - /// UTXO Containment Proof Public Input Posting Key - utxo_containment_proof_public_input: L::ValidUtxoState, + /// UTXO Membership Proof Public Part Posting Key + utxo_membership_proof_public: L::ValidUtxoState, } impl<C, S, L> SenderPostingKey<C, S, L> @@ -1198,7 +1198,7 @@ where pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) -> bool { ledger.spend( self.void_number, - self.utxo_containment_proof_public_input, + self.utxo_membership_proof_public, super_key, ) } @@ -1396,7 +1396,7 @@ pub mod constraint { Allocation, Constant, ConstraintSystem, Derived, Equal, Public, PublicOrSecret, Secret, Variable, }, - set::constraint::{ContainmentProofVar, VerifiedSetVariable}, + set::constraint::{MembershipProofVar, VerifierVariable}, }; /// [`Identity`] Constraint System Configuration @@ -1497,9 +1497,9 @@ pub mod constraint { /// UTXO Variable Type pub type UtxoVar<C> = CommitmentSchemeOutputVar<C>; - /// UTXO Containment Proof Variable Type - pub type UtxoContainmentProofVar<C, S> = - ContainmentProofVar<S, <C as Configuration>::ConstraintSystem>; + /// UTXO Membership Proof Variable Type + pub type UtxoMembershipProofVar<C, S> = + MembershipProofVar<S, <C as Configuration>::ConstraintSystem>; /// Asset Parameters Variable pub struct AssetParametersVar<C> @@ -1603,8 +1603,8 @@ pub mod constraint { /// Unspent Transaction Output utxo: UtxoVar<C>, - /// UTXO Containment Proof - utxo_containment_proof: UtxoContainmentProofVar<C, S>, + /// UTXO Membership Proof + utxo_membership_proof: UtxoMembershipProofVar<C, S>, } impl<C, S> SenderVar<C, S> @@ -1619,11 +1619,12 @@ pub mod constraint { self, cs: &mut C::ConstraintSystem, commitment_scheme: &C::CommitmentSchemeVar, - utxo_set: &Var<S, C::ConstraintSystem>, + utxo_set_verifier: &Var<S::Verifier, C::ConstraintSystem>, ) -> AssetVar<C::ConstraintSystem> where - S: HasAllocation<C::ConstraintSystem>, - S::Variable: VerifiedSetVariable<C::ConstraintSystem, ItemVar = UtxoVar<C>>, + S::Verifier: HasAllocation<C::ConstraintSystem>, + <S::Verifier as HasAllocation<C::ConstraintSystem>>::Variable: + VerifierVariable<C::ConstraintSystem, ItemVar = UtxoVar<C>>, { // Well-formed check: // @@ -1688,13 +1689,13 @@ pub mod constraint { ), ); - // 5. Check UTXO containment proof: + // 5. Check UTXO membership proof: // ``` // is_path(cm, path, root) == true // ``` // where public: {root}, secret: {cm, path}. - self.utxo_containment_proof - .assert_validity(utxo_set, &self.utxo, cs); + self.utxo_membership_proof + .assert_validity(utxo_set_verifier, &self.utxo, cs); self.asset } @@ -1729,7 +1730,7 @@ pub mod constraint { Public, ), utxo: UtxoVar::<C>::new_known(cs, &this.utxo, Secret), - utxo_containment_proof: this.utxo_containment_proof.known(cs, mode), + utxo_membership_proof: this.utxo_membership_proof.known(cs, mode), }, Allocation::Unknown(mode) => Self { secret_key: SecretKeyVar::<C>::new_unknown(cs, mode), @@ -1739,7 +1740,7 @@ pub mod constraint { void_number: VoidNumberVar::<C>::new_unknown(cs, Public), void_number_commitment: VoidNumberCommitmentVar::<C>::new_unknown(cs, Public), utxo: UtxoVar::<C>::new_unknown(cs, Secret), - utxo_containment_proof: ContainmentProof::<S>::unknown(cs, mode), + utxo_membership_proof: MembershipProof::<S>::unknown(cs, mode), }, } } diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 68c8743c3..03869de83 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -41,7 +41,7 @@ use manta_crypto::{ PublicOrSecret, Secret, Variable, VariableSource, }, ies::{EncryptedMessage, IntegratedEncryptionScheme}, - set::{constraint::VerifiedSetVariable, VerifiedSet}, + set::{constraint::VerifierVariable, VerifiedSet, Verifier}, }; use manta_util::{create_seal, from_variant_impl, iter::mixed_chain, seal, Either}; use rand::{ @@ -95,14 +95,20 @@ pub trait Configuration: type IntegratedEncryptionScheme: IntegratedEncryptionScheme<Plaintext = Asset>; /// Verified Set for [`Utxo`] - type UtxoSet: VerifiedSet<Item = Utxo<Self>> - + HasAllocation<ConstraintSystem<Self>, Variable = Self::UtxoSetVar, Mode = Constant>; + type UtxoSet: VerifiedSet<Item = Utxo<Self>, Verifier = Self::UtxoSetVerifier>; - /// Verified Set Variable for [`Utxo`] - type UtxoSetVar: VerifiedSetVariable< + /// Verified Set Verifier for [`Utxo`] + type UtxoSetVerifier: Verifier< + Item = Utxo<Self>, + Public = <Self::UtxoSet as VerifiedSet>::Public, + Secret = <Self::UtxoSet as VerifiedSet>::Secret, + > + HasAllocation<ConstraintSystem<Self>, Variable = Self::UtxoSetVerifierVar, Mode = Constant>; + + /// Verified Set Verifier Variable for [`Utxo`] + type UtxoSetVerifierVar: VerifierVariable< ConstraintSystem<Self>, ItemVar = UtxoVar<Self>, - Type = Self::UtxoSet, + Type = Self::UtxoSetVerifier, Mode = Constant, >; } @@ -561,14 +567,14 @@ where pub fn into_post<R>( self, commitment_scheme: &C::CommitmentScheme, - utxo_set: &C::UtxoSet, + utxo_set_verifier: &C::UtxoSetVerifier, context: &ProvingContext<C>, rng: &mut R, ) -> Result<TransferPost<C>, ProofSystemError<C>> where R: CryptoRng + RngCore + ?Sized, { - Transfer::from(self).into_post(commitment_scheme, utxo_set, context, rng) + Transfer::from(self).into_post(commitment_scheme, utxo_set_verifier, context, rng) } } @@ -709,13 +715,13 @@ where #[inline] fn unknown_variables( commitment_scheme: &C::CommitmentScheme, - utxo_set: &C::UtxoSet, + utxo_set_verifier: &C::UtxoSetVerifier, cs: &mut ConstraintSystem<C>, ) -> ( Option<C::AssetIdVar>, TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS>, C::CommitmentSchemeVar, - C::UtxoSetVar, + C::UtxoSetVerifierVar, ) { let base_asset_id = if has_no_public_side(SOURCES, SENDERS, RECEIVERS, SINKS) { None @@ -726,7 +732,7 @@ where base_asset_id, TransferParticipantsVar::new_unknown(cs, Derived), commitment_scheme.as_known(cs, Public), - utxo_set.as_known(cs, Public), + utxo_set_verifier.as_known(cs, Public), ) } @@ -735,19 +741,19 @@ where fn known_variables( &self, commitment_scheme: &C::CommitmentScheme, - utxo_set: &C::UtxoSet, + utxo_set_verifier: &C::UtxoSetVerifier, cs: &mut ConstraintSystem<C>, ) -> ( Option<C::AssetIdVar>, TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS>, C::CommitmentSchemeVar, - C::UtxoSetVar, + C::UtxoSetVerifierVar, ) { ( self.public.asset_id.map(|id| id.as_known(cs, Public)), TransferParticipantsVar::new_known(cs, self, Derived), commitment_scheme.as_known(cs, Public), - utxo_set.as_known(cs, Public), + utxo_set_verifier.as_known(cs, Public), ) } @@ -757,7 +763,7 @@ where base_asset_id: Option<C::AssetIdVar>, participants: TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS>, commitment_scheme: C::CommitmentSchemeVar, - utxo_set: C::UtxoSetVar, + utxo_set_verifier: C::UtxoSetVerifierVar, cs: &mut ConstraintSystem<C>, ) { let mut sender_sum = C::AssetBalanceVar::from_default(cs, Secret); @@ -779,7 +785,8 @@ where participants.receivers.into_iter(), |c| match c { Either::Left(sender) => { - let asset = sender.get_well_formed_asset(cs, &commitment_scheme, &utxo_set); + let asset = + sender.get_well_formed_asset(cs, &commitment_scheme, &utxo_set_verifier); sender_sum += asset.value; asset.id } @@ -807,7 +814,7 @@ where #[inline] pub fn generate_context<R>( commitment_scheme: &C::CommitmentScheme, - utxo_set: &C::UtxoSet, + utxo_set_verifier: &C::UtxoSetVerifier, rng: &mut R, ) -> Option<Result<(ProvingContext<C>, VerifyingContext<C>), ProofSystemError<C>>> where @@ -817,13 +824,13 @@ where return None; } let mut cs = C::ProofSystem::for_unknown(); - let (base_asset_id, participants, commitment_scheme, utxo_set) = - Self::unknown_variables(commitment_scheme, utxo_set, &mut cs); + let (base_asset_id, participants, commitment_scheme, utxo_set_verifier) = + Self::unknown_variables(commitment_scheme, utxo_set_verifier, &mut cs); Self::build_constraints( base_asset_id, participants, commitment_scheme, - utxo_set, + utxo_set_verifier, &mut cs, ); Some(C::ProofSystem::generate_context(cs, rng)) @@ -837,7 +844,7 @@ where pub fn generate_proof<R>( &self, commitment_scheme: &C::CommitmentScheme, - utxo_set: &C::UtxoSet, + utxo_set_verifier: &C::UtxoSetVerifier, context: &ProvingContext<C>, rng: &mut R, ) -> Result<ShapedProof<C>, ProofSystemError<C>> @@ -849,18 +856,18 @@ where return Ok(shape.into()); } let mut cs = C::ProofSystem::for_known(); - let (base_asset_id, participants, commitment_scheme, utxo_set) = - self.known_variables(commitment_scheme, utxo_set, &mut cs); + let (base_asset_id, participants, commitment_scheme, utxo_set_verifier) = + self.known_variables(commitment_scheme, utxo_set_verifier, &mut cs); Self::build_constraints( base_asset_id, participants, commitment_scheme, - utxo_set, + utxo_set_verifier, &mut cs, ); Ok(ShapedProof::new_proof( shape, - C::ProofSystem::generate_proof(cs, context, rng)?, + C::ProofSystem::prove(cs, context, rng)?, )) } @@ -869,7 +876,7 @@ where pub fn into_post<R>( self, commitment_scheme: &C::CommitmentScheme, - utxo_set: &C::UtxoSet, + utxo_set_verifier: &C::UtxoSetVerifier, context: &ProvingContext<C>, rng: &mut R, ) -> Result<TransferPost<C>, ProofSystemError<C>> @@ -877,7 +884,12 @@ where R: CryptoRng + RngCore + ?Sized, { Ok(TransferPost { - validity_proof: self.generate_proof(commitment_scheme, utxo_set, context, rng)?, + validity_proof: self.generate_proof( + commitment_scheme, + utxo_set_verifier, + context, + rng, + )?, sender_posts: IntoIterator::into_iter(self.secret.senders) .map(Sender::into_post) .collect(), diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 8059ba665..72f665266 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -42,7 +42,7 @@ use core::{ mem, ops::Range, }; -use manta_crypto::{Set, VerifiedSet}; +use manta_crypto::set::VerifiedSet; use manta_util::{fallible_array_map, into_array_unchecked, iter::IteratorExt}; use rand::{ distributions::{Distribution, Standard}, @@ -231,8 +231,8 @@ where /// Encryption Error EncryptionError(IntegratedEncryptionSchemeError<C>), - /// Containment Error - ContainmentError(<C::UtxoSet as VerifiedSet>::ContainmentError), + /// Missing [`Utxo`] Membership Proof + MissingUtxoMembershipProof, /// Insufficient Balance InsufficientBalance(Asset), @@ -793,7 +793,7 @@ where // FIXME: Should this ever error? We should check the capacity at `updates`, then // insert should always work here. - let _ = self.utxo_set.insert(utxo); + let _ = self.utxo_set.insert(&utxo); } Ok(SyncResponse::new(assets)) } @@ -865,7 +865,7 @@ where .into() .into_post( &self.commitment_scheme, - &self.utxo_set, + &self.utxo_set.verifier(), &self.proving_context, &mut self.rng, ) @@ -890,12 +890,8 @@ where Standard: Distribution<AssetParameters<C>>, { assert!( - SENDERS > 1, - "The transfer shape must include at least two senders." - ); - assert!( - RECEIVERS > 1, - "The transfer shape must include at least two receivers." + (SENDERS > 1) && (RECEIVERS > 1), + "The transfer shape must include at least two senders and two receivers." ); assert!( !pre_senders.is_empty(), @@ -909,8 +905,10 @@ where let mut accumulators = Vec::new(); let mut iter = pre_senders.into_iter().chunk_by::<SENDERS>(); for chunk in &mut iter { - let senders = fallible_array_map(chunk, |ps| ps.try_upgrade(&self.utxo_set)) - .map_err(Error::ContainmentError)?; + let senders = fallible_array_map(chunk, |ps| { + ps.try_upgrade(&self.utxo_set) + .ok_or(Error::MissingUtxoMembershipProof) + })?; let (receivers, accumulator, mut zeroes) = self.signer.next_accumulated_receiver::<_, _, RECEIVERS>( @@ -1049,8 +1047,8 @@ where pre_senders .into_iter() .map(|ps| ps.try_upgrade(&self.utxo_set)) - .collect::<Result<Vec<_>, _>>() - .map_err(Error::ContainmentError)?, + .collect::<Option<Vec<_>>>() + .ok_or(Error::MissingUtxoMembershipProof)?, ); let change_receiver = self.next_change(asset.id, change)?; diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index a82d59a2a..9f6f7b61a 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -37,4 +37,4 @@ test = [] [dependencies] derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } manta-util = { path = "../manta-util", default-features = false } -rand = { version = "0.8.4", default-features = false } +rand_core = { version = "0.6.3", default-features = false } diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index ecfa92ed5..67d90aead 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -29,7 +29,7 @@ use core::{ hash::Hash, marker::PhantomData, }; -use rand::{CryptoRng, RngCore}; +use rand_core::{CryptoRng, RngCore}; /// Allocation Mode pub trait AllocationMode { @@ -499,7 +499,7 @@ pub trait ProofSystem { R: CryptoRng + RngCore + ?Sized; /// Returns a proof that the constraint system `self` is consistent. - fn generate_proof<R>( + fn prove<R>( cs: Self::ConstraintSystem, context: &Self::ProvingContext, rng: &mut R, @@ -508,7 +508,7 @@ pub trait ProofSystem { R: CryptoRng + RngCore + ?Sized; /// Verifies that a proof generated from this proof system is valid. - fn verify_proof( + fn verify( context: &Self::VerifyingContext, proof: &Self::Proof, ) -> Result<Self::Verification, Self::Error>; @@ -926,9 +926,6 @@ pub mod test { P: ProofSystem, R: CryptoRng + RngCore, { - P::verify_proof( - verifying_context, - &P::generate_proof(cs, proving_context, rng)?, - ) + P::verify(verifying_context, &P::prove(cs, proving_context, rng)?) } } diff --git a/manta-crypto/src/ies.rs b/manta-crypto/src/ies.rs index 9dbb7d915..44d2fbb29 100644 --- a/manta-crypto/src/ies.rs +++ b/manta-crypto/src/ies.rs @@ -19,7 +19,7 @@ // FIXME: add zeroize for secret keys use core::{fmt::Debug, hash::Hash}; -use rand::{CryptoRng, RngCore}; +use rand_core::{CryptoRng, RngCore}; pub(super) mod prelude { #[doc(inline)] diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index 67c4b219a..04d2fa6e4 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -34,4 +34,3 @@ pub mod set; pub use commitment::prelude::*; pub use ies::prelude::*; pub use prf::*; -pub use set::prelude::*; diff --git a/manta-crypto/src/set.rs b/manta-crypto/src/set.rs index 36e8b4b35..1990190dd 100644 --- a/manta-crypto/src/set.rs +++ b/manta-crypto/src/set.rs @@ -14,25 +14,28 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Sets and Verified Sets - -// FIXME: We should probably have something like a "verified set verification handle" which a -// verified set can give to someone who wants to check a containment proof, since in general -// we don't actually need access to the set itself, or having access to the set would be be -// possible in any real implementation. -// FIXME: The `Set::contains` method is not really something we can always implement properly. -// FIXME: Should we just get rid of `Set` and just ensure we can get proofs working? - -pub(super) mod prelude { - #[doc(inline)] - pub use super::{Set, VerifiedSet}; -} +//! Verified Sets -/// Set Trait -pub trait Set { - /// Item Stored in the [`Set`] +/// Verified Set Trait +pub trait VerifiedSet { + /// Item Type type Item; + /// Public Part of the [`Item`](Self::Item) Membership Proof + type Public; + + /// Secret Part of the [`Item`](Self::Item) Membership Proof + type Secret; + + /// [`MembershipProof`] Verifier Type + type Verifier: Verifier<Item = Self::Item, Public = Self::Public, Secret = Self::Secret>; + + /// Returns a new verifier for `self`. + fn verifier(&self) -> Self::Verifier; + + /// Returns the maximum number of elements that can be stored in `self`. + fn capacity(&self) -> usize; + /// Returns the number of elements that are contained in `self`. fn len(&self) -> usize; @@ -42,95 +45,85 @@ pub trait Set { self.len() == 0 } - /// Returns `true` if `item` is stored in `self`. - fn contains(&self, item: &Self::Item) -> bool; + /// Inserts `item` into `self`. + fn insert(&mut self, item: &Self::Item) -> bool; - /// Tries to insert the `item` into `self`, returning the item back if it was already - /// contained in `self`. - fn try_insert(&mut self, item: Self::Item) -> Result<(), Self::Item>; + /// Returns `true` if `public` is a valid input for the current state of `self`. + fn verify(&self, public: &Self::Public) -> bool; - /// Inserts the `item` into `self`, returning `true` if the `item` was not contained and - /// `false` if the item was already contained in `self`. + /// Generates a proof that the given `item` is stored in `self`. + fn get_membership_proof(&self, item: &Self::Item) -> Option<MembershipProof<Self>>; + + /// Returns `true` if `item` is stored in `self`. + /// + /// # Implementation Note + /// + /// This method must at least return `true` for `item` whenever a valid proof of membership + /// exists. It may return `true` in other cases when `self` knows that it has `item` stored but + /// cannot return a proof for it. #[inline] - fn insert(&mut self, item: Self::Item) -> bool { - self.try_insert(item).is_err() + fn contains(&self, item: &Self::Item) -> bool { + self.get_membership_proof(item).is_some() } } -/// Containment Proof for a [`VerifiedSet`] -pub struct ContainmentProof<S> +/// Verified Set Verifier +pub trait Verifier { + /// Item Type + type Item; + + /// Public Part of the [`Item`](Self::Item) Membership Proof + type Public; + + /// Secret Part of the [`Item`](Self::Item) Membership Proof + type Secret; + + /// Verifies that `public` and `secret` form a proof to the fact that `item` is contained in + /// the verified set which returned `self`. + fn verify(&self, public: &Self::Public, secret: &Self::Secret, item: &Self::Item) -> bool; +} + +/// Membership Proof for a [`VerifiedSet`] +pub struct MembershipProof<S> where S: VerifiedSet + ?Sized, { - /// Public Input - public_input: S::Public, + /// Public Proof Part + public: S::Public, - /// Secret Witness - secret_witness: S::Secret, + /// Secret Proof Part + secret: S::Secret, } -impl<S> ContainmentProof<S> +impl<S> MembershipProof<S> where S: VerifiedSet + ?Sized, { - /// Builds a new [`ContainmentProof`] from `public_input` and `secret_witness`. + /// Builds a new [`MembershipProof`] from `public` and `secret`. #[inline] - pub fn new(public_input: S::Public, secret_witness: S::Secret) -> Self { - Self { - public_input, - secret_witness, - } + pub fn new(public: S::Public, secret: S::Secret) -> Self { + Self { public, secret } } - /// Returns [`S::Public`](VerifiedSet::Public) discarding the [`ContainmentProof`]. + /// Returns [`S::Public`](VerifiedSet::Public) discarding the [`MembershipProof`]. #[inline] - pub fn into_public_input(self) -> S::Public { - self.public_input + pub fn into_public(self) -> S::Public { + self.public } - /// Verifies that the `item` is contained in some [`VerifiedSet`]. + /// Returns `true` if the public part of `self` is a valid input for the current state of `set`. #[inline] - pub fn verify(&self, set: &S, item: &S::Item) -> bool { - set.check_containment_proof(&self.public_input, &self.secret_witness, item) + pub fn verify_public(&self, set: &S) -> bool { + set.verify(&self.public) } - /// Returns `true` if `self.public_input` is a valid input for the current state of `set`. + /// Verifies that the `item` is contained in some [`VerifiedSet`]. #[inline] - pub fn check_public_input(&self, set: &S) -> bool { - set.check_public_input(&self.public_input) + pub fn verify(&self, verifier: &S::Verifier, item: &S::Item) -> bool { + verifier.verify(&self.public, &self.secret, item) } } -/// Verified Set Trait -pub trait VerifiedSet: Set { - /// Public Input for [`Item`](Set::Item) Containment - type Public; - - /// Secret Witness for [`Item`](Set::Item) Containment - type Secret; - - /// Error Generating a [`ContainmentProof`] - type ContainmentError; - - /// Returns `true` if `public_input` is a valid input for the current state of `self`. - fn check_public_input(&self, public_input: &Self::Public) -> bool; - - /// Returns `true` if `public_input` and `secret_witness` make up a valid proof that `item` - /// is stored in `self`. - fn check_containment_proof( - &self, - public_input: &Self::Public, - secret_witness: &Self::Secret, - item: &Self::Item, - ) -> bool; - - /// Generates a proof that the given `item` is stored in `self`. - fn get_containment_proof( - &self, - item: &Self::Item, - ) -> Result<ContainmentProof<Self>, Self::ContainmentError>; -} - /// Constraint System Gadgets for Sets and Verified Sets pub mod constraint { use super::*; @@ -140,26 +133,26 @@ pub mod constraint { }; use core::marker::PhantomData; - /// Containment Proof Allocation Mode Entry + /// Membership Proof Allocation Mode Entry #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] - pub struct ContainmentProofModeEntry<PublicMode, SecretMode> { - /// Public Input Allocation Mode + pub struct MembershipProofModeEntry<PublicMode, SecretMode> { + /// Public Allocation Mode pub public: PublicMode, - /// Secret Witness Allocation Mode + /// Secret Allocation Mode pub secret: SecretMode, } - impl<PublicMode, SecretMode> ContainmentProofModeEntry<PublicMode, SecretMode> { - /// Builds a new [`ContainmentProofModeEntry`] from a `public` mode and a `secret` mode. + impl<PublicMode, SecretMode> MembershipProofModeEntry<PublicMode, SecretMode> { + /// Builds a new [`MembershipProofModeEntry`] from a `public` mode and a `secret` mode. #[inline] pub fn new(public: PublicMode, secret: SecretMode) -> Self { Self { public, secret } } } - impl<PublicMode, SecretMode> From<Derived> for ContainmentProofModeEntry<PublicMode, SecretMode> + impl<PublicMode, SecretMode> From<Derived> for MembershipProofModeEntry<PublicMode, SecretMode> where PublicMode: From<Derived>, SecretMode: From<Derived>, @@ -170,77 +163,74 @@ pub mod constraint { } } - /// Containment Proof Allocation Mode + /// Membership Proof Allocation Mode #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct ContainmentProofMode<PublicMode, SecretMode>(PhantomData<(PublicMode, SecretMode)>) + pub struct MembershipProofMode<PublicMode, SecretMode>(PhantomData<(PublicMode, SecretMode)>) where PublicMode: AllocationMode, SecretMode: AllocationMode; - impl<PublicMode, SecretMode> AllocationMode for ContainmentProofMode<PublicMode, SecretMode> + impl<PublicMode, SecretMode> AllocationMode for MembershipProofMode<PublicMode, SecretMode> where PublicMode: AllocationMode, SecretMode: AllocationMode, { - type Known = ContainmentProofModeEntry<PublicMode::Known, SecretMode::Known>; - type Unknown = ContainmentProofModeEntry<PublicMode::Unknown, SecretMode::Unknown>; + type Known = MembershipProofModeEntry<PublicMode::Known, SecretMode::Known>; + type Unknown = MembershipProofModeEntry<PublicMode::Unknown, SecretMode::Unknown>; } - /// Containment Proof Variable - pub struct ContainmentProofVar<S, C> + /// Membership Proof Variable + pub struct MembershipProofVar<S, C> where S: VerifiedSet + ?Sized, C: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, { - /// Public Input - public_input: Var<S::Public, C>, + /// Public Proof Part + public: Var<S::Public, C>, - /// Secret Witness - secret_witness: Var<S::Secret, C>, + /// Secret Proof Part + secret: Var<S::Secret, C>, } - impl<S, C> ContainmentProofVar<S, C> + impl<S, C> MembershipProofVar<S, C> where S: VerifiedSet + ?Sized, C: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, { - /// Builds a new [`ContainmentProofVar`] from `public_input` and `secret_witness`. + /// Builds a new [`MembershipProofVar`] from `public` and `secret`. #[inline] - pub fn new(public_input: Var<S::Public, C>, secret_witness: Var<S::Secret, C>) -> Self { - Self { - public_input, - secret_witness, - } + pub fn new(public: Var<S::Public, C>, secret: Var<S::Secret, C>) -> Self { + Self { public, secret } } /// Asserts that `self` is a valid proof to the fact that `item` is stored in the /// verified set. #[inline] - pub fn assert_validity<V>(&self, set: &V, item: &V::ItemVar, cs: &mut C) + pub fn assert_validity<V>(&self, verifier: &V, item: &V::ItemVar, cs: &mut C) where C: ConstraintSystem, - V: VerifiedSetVariable<C, Type = S>, + V: VerifierVariable<C, Type = S::Verifier>, { - set.assert_valid_containment_proof(&self.public_input, &self.secret_witness, item, cs) + verifier.assert_valid_membership_proof(&self.public, &self.secret, item, cs) } } - impl<S, C> Variable<C> for ContainmentProofVar<S, C> + impl<S, C> Variable<C> for MembershipProofVar<S, C> where S: VerifiedSet + ?Sized, C: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, { - type Type = ContainmentProof<S>; + type Type = MembershipProof<S>; - type Mode = ContainmentProofMode<Mode<S::Public, C>, Mode<S::Secret, C>>; + type Mode = MembershipProofMode<Mode<S::Public, C>, Mode<S::Secret, C>>; #[inline] fn new(cs: &mut C, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self::new( - cs.allocate_known(&this.public_input, mode.public), - cs.allocate_known(&this.secret_witness, mode.secret), + cs.allocate_known(&this.public, mode.public), + cs.allocate_known(&this.secret, mode.secret), ), Allocation::Unknown(mode) => Self::new( unknown::<S::Public, _>(cs, mode.public), @@ -250,45 +240,45 @@ pub mod constraint { } } - impl<S, C> HasAllocation<C> for ContainmentProof<S> + impl<S, C> HasAllocation<C> for MembershipProof<S> where S: VerifiedSet + ?Sized, C: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, { - type Variable = ContainmentProofVar<S, C>; - type Mode = ContainmentProofMode<Mode<S::Public, C>, Mode<S::Secret, C>>; + type Variable = MembershipProofVar<S, C>; + type Mode = MembershipProofMode<Mode<S::Public, C>, Mode<S::Secret, C>>; } - /// Public Input Type for [`VerifiedSetVariable`] - pub type PublicInputType<V, C> = <<V as Variable<C>>::Type as VerifiedSet>::Public; + /// Public Proof Part for [`VerifierVariable`] + pub type PublicType<V, C> = <<V as Variable<C>>::Type as Verifier>::Public; - /// Secret Witness Type for [`VerifiedSetVariable`] - pub type SecretWitnessType<V, C> = <<V as Variable<C>>::Type as VerifiedSet>::Secret; + /// Secret Proof Part for [`VerifierVariable`] + pub type SecretType<V, C> = <<V as Variable<C>>::Type as Verifier>::Secret; - /// Public Input Variable for [`VerifiedSetVariable`] - pub type PublicInputVar<V, C> = Var<PublicInputType<V, C>, C>; + /// Public Proof Part Variable for [`VerifierVariable`] + pub type PublicVar<V, C> = Var<PublicType<V, C>, C>; - /// Secret Witness Variable for [`VerifiedSetVariable`] - pub type SecretWitnessVar<V, C> = Var<SecretWitnessType<V, C>, C>; + /// Secret Proof Part Variable for [`VerifierVariable`] + pub type SecretVar<V, C> = Var<SecretType<V, C>, C>; /// Verified Set Variable - pub trait VerifiedSetVariable<C>: Variable<C> + pub trait VerifierVariable<C>: Variable<C> where C: ConstraintSystem - + HasVariable<PublicInputType<Self, C>> - + HasVariable<SecretWitnessType<Self, C>> + + HasVariable<PublicType<Self, C>> + + HasVariable<SecretType<Self, C>> + ?Sized, - Self::Type: VerifiedSet, + Self::Type: Verifier, { /// Item Variable - type ItemVar: Variable<C, Type = <Self::Type as Set>::Item>; + type ItemVar: Variable<C, Type = <Self::Type as Verifier>::Item>; - /// Asserts that `public_input` and `secret_witness` form a proof to the fact that `item` - /// is stored in `self`. - fn assert_valid_containment_proof( + /// Asserts that `public` and `secret` form a proof to the fact that `item` is stored in + /// `self`. + fn assert_valid_membership_proof( &self, - public_input: &PublicInputVar<Self, C>, - secret_witness: &SecretWitnessVar<Self, C>, + public: &PublicVar<Self, C>, + secret: &SecretVar<Self, C>, item: &Self::ItemVar, cs: &mut C, ); diff --git a/manta-pay/src/accounting/config.rs b/manta-pay/src/accounting/config.rs index 856abbe20..4e2414f08 100644 --- a/manta-pay/src/accounting/config.rs +++ b/manta-pay/src/accounting/config.rs @@ -20,7 +20,7 @@ // enabled when using whichever backend. use crate::{ - accounting::ledger::{UtxoSet, UtxoSetVar}, + accounting::ledger::{UtxoSet, UtxoSetVerifier, UtxoSetVerifierVar}, crypto::{ commitment::pedersen::{self, PedersenWindow}, constraint::arkworks::{ @@ -114,13 +114,11 @@ impl merkle_tree::Configuration for Configuration { impl merkle_tree_constraint::Configuration for Configuration { type ConstraintField = ConstraintField; - type LeafHashVar = CRHGadget< PedersenCommitmentProjectiveCurve, PedersenCommitmentProjectiveCurveVar, PedersenCommitmentWindowParameters, >; - type InnerHashVar = CRHGadget< PedersenCommitmentProjectiveCurve, PedersenCommitmentProjectiveCurveVar, @@ -155,5 +153,6 @@ impl transfer::Configuration for Configuration { type AssetBalanceVar = AssetBalanceVar<ConstraintField>; type IntegratedEncryptionScheme = IES; type UtxoSet = UtxoSet; - type UtxoSetVar = UtxoSetVar; + type UtxoSetVerifier = UtxoSetVerifier; + type UtxoSetVerifierVar = UtxoSetVerifierVar; } diff --git a/manta-pay/src/accounting/ledger.rs b/manta-pay/src/accounting/ledger.rs index d79f1f682..2f803d78d 100644 --- a/manta-pay/src/accounting/ledger.rs +++ b/manta-pay/src/accounting/ledger.rs @@ -35,7 +35,7 @@ use manta_accounting::identity; use manta_crypto::{ constraint::{self, reflection::HasAllocation, Allocation, Constant, Variable}, merkle_tree::{self, single_leaf::SingleLeaf, Tree}, - set::{constraint::VerifiedSetVariable, ContainmentProof, Set, VerifiedSet}, + set::{constraint::VerifierVariable, MembershipProof, VerifiedSet, Verifier}, }; use manta_util::{as_bytes, concatenate, into_array_unchecked}; @@ -133,66 +133,55 @@ impl UtxoSet { } } -impl Set for UtxoSet { +impl VerifiedSet for UtxoSet { type Item = Utxo; + type Public = Root; + + type Secret = Path; + + type Verifier = UtxoSetVerifier; + #[inline] - fn len(&self) -> usize { - // FIXME: Implement. + fn capacity(&self) -> usize { todo!() } #[inline] - fn contains(&self, item: &Self::Item) -> bool { - self.utxo_exists(item) + fn len(&self) -> usize { + // FIXME: Implement. + todo!() } #[inline] - fn try_insert(&mut self, item: Self::Item) -> Result<(), Self::Item> { - // TODO: Distinguish between both kinds of errors. - if self.utxo_exists(&item) { - return Err(item); + fn insert(&mut self, item: &Self::Item) -> bool { + if self.utxo_exists(item) { + return false; } - if !self.shards[Self::shard_index(&item)] + if !self.shards[Self::shard_index(item)] .utxos - .push(&self.parameters, &as_bytes!(&item)) + .push(&self.parameters, &as_bytes!(item)) { - return Err(item); + return false; } // FIXME: self.utxos.insert(item); - Ok(()) + true } -} - -impl VerifiedSet for UtxoSet { - type Public = Root; - - type Secret = Path; - - type ContainmentError = (); #[inline] - fn check_public_input(&self, public_input: &Self::Public) -> bool { - self.root_exists(public_input) + fn verifier(&self) -> Self::Verifier { + UtxoSetVerifier { + parameters: self.parameters.clone(), + } } #[inline] - fn check_containment_proof( - &self, - public_input: &Self::Public, - secret_witness: &Self::Secret, - item: &Self::Item, - ) -> bool { - // FIXME: Leaf should be `Utxo` not `[u8]`. - self.parameters - .verify_path(secret_witness, public_input, &as_bytes!(item)) + fn verify(&self, public: &Self::Public) -> bool { + self.root_exists(public) } #[inline] - fn get_containment_proof( - &self, - item: &Self::Item, - ) -> Result<ContainmentProof<Self>, Self::ContainmentError> { + fn get_membership_proof(&self, item: &Self::Item) -> Option<MembershipProof<Self>> { let _ = item; // TODO: Return a more informative error. @@ -210,14 +199,41 @@ impl VerifiedSet for UtxoSet { todo!() } + + #[inline] + fn contains(&self, item: &Self::Item) -> bool { + self.utxo_exists(item) + } +} + +/// UTXO Set Verifier +#[derive(Clone)] +pub struct UtxoSetVerifier { + /// Merkle Tree Parameters + parameters: Parameters, +} + +impl Verifier for UtxoSetVerifier { + type Item = Utxo; + + type Public = Root; + + type Secret = Path; + + #[inline] + fn verify(&self, public: &Self::Public, secret: &Self::Secret, item: &Self::Item) -> bool { + // FIXME: Leaf should be `Utxo` not `[u8]`. + self.parameters + .verify_path(secret, public, &as_bytes!(item)) + } } -/// UTXO Set Variable +/// UTXO Set Verifier Variable #[derive(Clone)] -pub struct UtxoSetVar(ParametersVar); +pub struct UtxoSetVerifierVar(ParametersVar); -impl Variable<ConstraintSystem> for UtxoSetVar { - type Type = UtxoSet; +impl Variable<ConstraintSystem> for UtxoSetVerifierVar { + type Type = UtxoSetVerifier; type Mode = Constant; @@ -228,25 +244,24 @@ impl Variable<ConstraintSystem> for UtxoSetVar { } } -impl HasAllocation<ConstraintSystem> for UtxoSet { - type Variable = UtxoSetVar; +impl HasAllocation<ConstraintSystem> for UtxoSetVerifier { + type Variable = UtxoSetVerifierVar; type Mode = Constant; } -impl VerifiedSetVariable<ConstraintSystem> for UtxoSetVar { +impl VerifierVariable<ConstraintSystem> for UtxoSetVerifierVar { type ItemVar = UtxoVar; #[inline] - fn assert_valid_containment_proof( + fn assert_valid_membership_proof( &self, - public_input: &RootVar, - secret_witness: &PathVar, + public: &RootVar, + secret: &PathVar, item: &UtxoVar, cs: &mut ConstraintSystem, ) { let _ = cs; - self.0 - .assert_verified(public_input, secret_witness, &concatenate!(item)) + self.0.assert_verified(public, secret, &concatenate!(item)) } } diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs index 475bb4c10..c5c5c7068 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs @@ -81,7 +81,7 @@ where } #[inline] - fn generate_proof<R>( + fn prove<R>( cs: Self::ConstraintSystem, context: &Self::ProvingContext, rng: &mut R, @@ -104,7 +104,7 @@ where } #[inline] - fn verify_proof( + fn verify( context: &Self::VerifyingContext, proof: &Self::Proof, ) -> Result<Self::Verification, Self::Error> { From 6f717eb12f68aa0a598392e003fa2d7dd6146197 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 12 Oct 2021 04:07:06 -0400 Subject: [PATCH 091/275] WIP: remove explicit dependence on rand crate --- manta-accounting/Cargo.toml | 1 - manta-accounting/src/asset.rs | 61 ++-- manta-accounting/src/identity.rs | 168 +++------- manta-accounting/src/transfer.rs | 37 +-- manta-accounting/src/wallet/signer.rs | 104 ++---- manta-crypto/src/lib.rs | 1 + manta-crypto/src/rand.rs | 312 ++++++++++++++++++ manta-pay/Cargo.toml | 3 +- manta-pay/src/accounting/config.rs | 7 +- manta-pay/src/crypto/commitment/pedersen.rs | 44 ++- .../arkworks/proof_systems/groth16.rs | 7 +- manta-pay/src/crypto/ies.rs | 6 +- manta-pay/src/crypto/prf/blake2s.rs | 16 +- manta-util/Cargo.toml | 3 - manta-util/src/lib.rs | 4 - manta-util/src/rand.rs | 158 --------- 16 files changed, 498 insertions(+), 434 deletions(-) create mode 100644 manta-crypto/src/rand.rs delete mode 100644 manta-util/src/rand.rs diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 43d2acf43..cd7114b01 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -43,7 +43,6 @@ derivative = { version = "2.2.0", default-features = false, features = ["use_cor derive_more = { version = "0.99.16", default-features = false, features = ["add", "add_assign", "display", "from", "mul", "mul_assign", "sum"] } manta-crypto = { path = "../manta-crypto", default-features = false } manta-util = { path = "../manta-util", default-features = false } -rand = { version = "0.8.4", default-features = false } zeroize = { version = "1.4.2", optional = true, default-features = false, features = ["alloc", "zeroize_derive"] } [dev-dependencies] diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 9e3b1fb6e..bc9914652 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -35,15 +35,14 @@ use core::{ use derive_more::{ Add, AddAssign, Display, Div, DivAssign, From, Mul, MulAssign, Product, Sub, SubAssign, Sum, }; -use manta_crypto::constraint::{ - reflection::{unknown, HasAllocation, HasVariable, Var}, - Allocation, PublicOrSecret, Secret, Variable, +use manta_crypto::{ + constraint::{ + reflection::{unknown, HasAllocation, HasVariable, Var}, + Allocation, PublicOrSecret, Secret, Variable, + }, + rand::{Rand, RngCore, Sample, Standard}, }; use manta_util::{array_map, fallible_array_map, into_array_unchecked, Concat, ConcatAccumulator}; -use rand::{ - distributions::{Distribution, Standard}, - Rng, RngCore, -}; pub(super) mod prelude { #[doc(inline)] @@ -88,10 +87,16 @@ impl AssetId { } } -impl Distribution<AssetId> for Standard { +impl<D> Sample<D> for AssetId +where + AssetIdType: Sample<D>, +{ #[inline] - fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> AssetId { - AssetId(self.sample(rng)) + fn sample<R>(distribution: &D, rng: &mut R) -> Self + where + R: RngCore + ?Sized, + { + Self(rng.sample(distribution)) } } @@ -185,10 +190,16 @@ impl AssetBalance { } } -impl Distribution<AssetBalance> for Standard { +impl<D> Sample<D> for AssetBalance +where + AssetBalanceType: Sample<D>, +{ #[inline] - fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> AssetBalance { - AssetBalance(self.sample(rng)) + fn sample<R>(distribution: &D, rng: &mut R) -> Self + where + R: RngCore + ?Sized, + { + Self(rng.sample(distribution)) } } @@ -227,9 +238,11 @@ pub(crate) fn sample_asset_balances<R, const N: usize>(rng: &mut R) -> AssetBala where R: RngCore + ?Sized, { - // FIXME: We have to use this implementation because of a bug in `rand`. - // See `https://github.com/rust-random/rand/pull/1173`. - into_array_unchecked(rng.sample_iter(Standard).take(N).collect::<Vec<_>>()) + let mut balances = Vec::with_capacity(N); + for _ in 0..N { + balances.push(rng.gen()); + } + into_array_unchecked(balances) } /// Change Iterator @@ -447,10 +460,14 @@ impl From<Asset> for (AssetId, AssetBalance) { } } -impl Distribution<Asset> for Standard { +impl Sample for Asset { #[inline] - fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> Asset { - Asset::new(self.sample(rng), self.sample(rng)) + fn sample<R>(distribution: &Standard, rng: &mut R) -> Self + where + R: RngCore + ?Sized, + { + let _ = distribution; + Self::new(rng.gen(), rng.gen()) } } @@ -812,7 +829,7 @@ mod test { #[test] fn asset_into_and_from_bytes() { let mut rng = thread_rng(); - let asset = rng.gen::<Asset>(); + let asset = Asset::gen(&mut rng); assert_eq!(asset, Asset::from_bytes(asset.into_bytes())); let mut asset_bytes = [0; Asset::SIZE]; rng.fill_bytes(&mut asset_bytes); @@ -823,8 +840,8 @@ mod test { #[test] fn asset_arithmetic() { let mut rng = thread_rng(); - let mut asset = Asset::zero(rng.gen()); - let value = rng.gen::<AssetBalance>(); + let mut asset = Asset::zero(AssetId::gen(&mut rng)); + let value = AssetBalance::gen(&mut rng); let _ = asset + value; asset += value; let _ = asset - value; diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 528fc8a06..884fb0f3e 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -23,13 +23,10 @@ use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use manta_crypto::{ commitment::{CommitmentScheme, Input as CommitmentInput}, ies::{self, EncryptedMessage, IntegratedEncryptionScheme}, + rand::{CryptoRand, CryptoRng, CryptoSample, RngCore, SeedableRng, Standard}, set::{MembershipProof, VerifiedSet}, PseudorandomFunctionFamily, }; -use rand::{ - distributions::{Distribution, Standard}, - CryptoRng, RngCore, SeedableRng, -}; pub(super) mod prelude { #[doc(inline)] @@ -41,11 +38,20 @@ pub trait Configuration { /// Secret Key Type type SecretKey: Clone; + /// Pseudorandom Function Family Input Type + type PseudorandomFunctionFamilyInput: CryptoSample; + /// Pseudorandom Function Family Type - type PseudorandomFunctionFamily: PseudorandomFunctionFamily<Seed = Self::SecretKey>; + type PseudorandomFunctionFamily: PseudorandomFunctionFamily< + Seed = Self::SecretKey, + Input = Self::PseudorandomFunctionFamilyInput, + >; + + /// Commitment Scheme Randomness Type + type CommitmentSchemeRandomness: CryptoSample; /// Commitment Scheme Type - type CommitmentScheme: CommitmentScheme + type CommitmentScheme: CommitmentScheme<Randomness = Self::CommitmentSchemeRandomness> + CommitmentInput<PublicKey<Self>> + CommitmentInput<VoidNumberGenerator<Self>> + CommitmentInput<Asset> @@ -221,16 +227,17 @@ where } } -impl<C> Distribution<AssetParameters<C>> for Standard +impl<C> CryptoSample for AssetParameters<C> where C: Configuration, - Standard: Distribution<VoidNumberGenerator<C>> - + Distribution<VoidNumberCommitmentRandomness<C>> - + Distribution<UtxoRandomness<C>>, { #[inline] - fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> AssetParameters<C> { - AssetParameters::new(self.sample(rng), self.sample(rng), self.sample(rng)) + fn sample<R>(distribution: &Standard, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + let _ = distribution; + Self::new(rng.gen(), rng.gen(), rng.gen()) } } @@ -275,44 +282,25 @@ where /// generated across different methods. The `parameters` is always generated immediately after /// creation of the random number generator. #[inline] - fn rng_and_parameters(&self) -> (C::Rng, AssetParameters<C>) - where - Standard: Distribution<AssetParameters<C>>, - { + fn rng_and_parameters(&self) -> (C::Rng, AssetParameters<C>) { let mut rng = C::Rng::from_seed(self.secret_key.clone()); - let parameters = Standard.sample(&mut rng); + let parameters = rng.gen(); (rng, parameters) } /// Generates [`AssetParameters`] for assets that are used by this identity. #[inline] - fn parameters(&self) -> AssetParameters<C> - where - Standard: Distribution<AssetParameters<C>>, - { + fn parameters(&self) -> AssetParameters<C> { let (_, parameters) = self.rng_and_parameters(); parameters } - /// Generates the associated [`AssetParameters`] and asset [`KeyPair`](ies::KeyPair) for - /// this identity. - #[inline] - fn parameters_and_asset_keypair<I>(&self) -> (AssetParameters<C>, ies::KeyPair<I>) - where - I: IntegratedEncryptionScheme<Plaintext = Asset>, - Standard: Distribution<AssetParameters<C>>, - { - let (mut rng, parameters) = self.rng_and_parameters(); - (parameters, I::generate_keys(&mut rng)) - } - /// Generates the associated [`AssetParameters`] and asset [`PublicKey`](ies::PublicKey) for /// this identity. #[inline] fn parameters_and_asset_public_key<I>(&self) -> (AssetParameters<C>, ies::PublicKey<I>) where I: IntegratedEncryptionScheme<Plaintext = Asset>, - Standard: Distribution<AssetParameters<C>>, { let (mut rng, parameters) = self.rng_and_parameters(); (parameters, I::generate_public_key(&mut rng)) @@ -324,7 +312,6 @@ where fn parameters_and_asset_secret_key<I>(&self) -> (AssetParameters<C>, ies::SecretKey<I>) where I: IntegratedEncryptionScheme<Plaintext = Asset>, - Standard: Distribution<AssetParameters<C>>, { let (mut rng, parameters) = self.rng_and_parameters(); (parameters, I::generate_secret_key(&mut rng)) @@ -374,10 +361,7 @@ where self, commitment_scheme: &C::CommitmentScheme, asset: Asset, - ) -> PreSender<C> - where - Standard: Distribution<AssetParameters<C>>, - { + ) -> PreSender<C> { let parameters = self.parameters(); let (public_key, void_number_commitment, utxo) = self.construct_utxo(commitment_scheme, &asset, &parameters); @@ -402,7 +386,6 @@ where ) -> Option<Sender<C, S>> where S: VerifiedSet<Item = Utxo<C>>, - Standard: Distribution<AssetParameters<C>>, { let parameters = self.parameters(); let (public_key, void_number_commitment, utxo) = @@ -430,7 +413,6 @@ where ) -> ShieldedIdentity<C, I> where I: IntegratedEncryptionScheme<Plaintext = Asset>, - Standard: Distribution<AssetParameters<C>>, { ShieldedIdentity { void_number_commitment: self.void_number_commitment(commitment_scheme, &parameters), @@ -444,7 +426,6 @@ where pub fn into_shielded<I>(self, commitment_scheme: &C::CommitmentScheme) -> ShieldedIdentity<C, I> where I: IntegratedEncryptionScheme<Plaintext = Asset>, - Standard: Distribution<AssetParameters<C>>, { let (parameters, asset_public_key) = self.parameters_and_asset_public_key(); self.build_shielded_identity(commitment_scheme, parameters, asset_public_key) @@ -455,7 +436,6 @@ where pub fn into_spend<I>(self) -> Spend<C, I> where I: IntegratedEncryptionScheme<Plaintext = Asset>, - Standard: Distribution<AssetParameters<C>>, { let (_, asset_secret_key) = self.parameters_and_asset_secret_key(); Spend::new(self, asset_secret_key) @@ -469,33 +449,10 @@ where ) -> Result<OpenSpend<C>, I::Error> where I: IntegratedEncryptionScheme<Plaintext = Asset>, - Standard: Distribution<AssetParameters<C>>, { self.into_spend().try_open(encrypted_asset) } - /// Builds a new [`ExternalReceiver`]. - #[inline] - pub fn into_external_receiver<I>( - self, - commitment_scheme: &C::CommitmentScheme, - ) -> ExternalReceiver<C, I> - where - I: IntegratedEncryptionScheme<Plaintext = Asset>, - Standard: Distribution<AssetParameters<C>>, - { - let (parameters, asset_keypair) = self.parameters_and_asset_keypair(); - let (asset_public_key, asset_secret_key) = asset_keypair.into(); - ExternalReceiver { - shielded_identity: self.build_shielded_identity( - commitment_scheme, - parameters, - asset_public_key, - ), - spend: Spend::new(self, asset_secret_key), - } - } - /// Builds a new [`InternalReceiver`]. #[inline] pub fn into_internal_receiver<I, R>( @@ -507,26 +464,28 @@ where where I: IntegratedEncryptionScheme<Plaintext = Asset>, R: CryptoRng + RngCore + ?Sized, - Standard: Distribution<AssetParameters<C>>, { let (parameters, asset_public_key) = self.parameters_and_asset_public_key(); Ok(InternalReceiver { receiver: self .build_shielded_identity(commitment_scheme, parameters, asset_public_key) .into_receiver(commitment_scheme, asset, rng)?, - open_spend: OpenSpend::new(self, asset), + pre_sender: OpenSpend::new(self, asset).into_pre_sender(commitment_scheme), }) } } -impl<C> Distribution<Identity<C>> for Standard +impl<C, D> CryptoSample<D> for Identity<C> where C: Configuration, - Standard: Distribution<SecretKey<C>>, + C::SecretKey: CryptoSample<D>, { #[inline] - fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> Identity<C> { - Identity::new(self.sample(rng)) + fn sample<R>(distribution: &D, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + Self::new(rng.sample(distribution)) } } @@ -556,7 +515,6 @@ where pub fn from_identity(identity: Identity<C>, commitment_scheme: &C::CommitmentScheme) -> Self where I: IntegratedEncryptionScheme<Plaintext = Asset>, - Standard: Distribution<AssetParameters<C>>, { identity.into_shielded(commitment_scheme) } @@ -652,10 +610,7 @@ where /// Builds a new [`Spend`] from an `identity`. #[inline] - pub fn from_identity(identity: Identity<C>) -> Self - where - Standard: Distribution<AssetParameters<C>>, - { + pub fn from_identity(identity: Identity<C>) -> Self { identity.into_spend() } @@ -674,10 +629,7 @@ where self, commitment_scheme: &C::CommitmentScheme, encrypted_asset: EncryptedMessage<I>, - ) -> Result<PreSender<C>, I::Error> - where - Standard: Distribution<AssetParameters<C>>, - { + ) -> Result<PreSender<C>, I::Error> { Ok(self .try_open(&encrypted_asset)? .into_pre_sender(commitment_scheme)) @@ -693,7 +645,6 @@ where ) -> Result<Sender<C, S>, SpendError<I>> where S: VerifiedSet<Item = Utxo<C>>, - Standard: Distribution<AssetParameters<C>>, { self.try_open(&encrypted_asset) .map_err(SpendError::EncryptionError)? @@ -706,7 +657,6 @@ impl<C, I> From<Identity<C>> for Spend<C, I> where C: Configuration, I: IntegratedEncryptionScheme<Plaintext = Asset>, - Standard: Distribution<AssetParameters<C>>, { #[inline] fn from(identity: Identity<C>) -> Self { @@ -714,30 +664,6 @@ where } } -/// External Receiver -pub struct ExternalReceiver<C, I> -where - C: Configuration, - I: IntegratedEncryptionScheme<Plaintext = Asset>, -{ - /// Shielded Identity - pub shielded_identity: ShieldedIdentity<C, I>, - - /// Spend - pub spend: Spend<C, I>, -} - -impl<C, I> From<ExternalReceiver<C, I>> for (ShieldedIdentity<C, I>, Spend<C, I>) -where - C: Configuration, - I: IntegratedEncryptionScheme<Plaintext = Asset>, -{ - #[inline] - fn from(external: ExternalReceiver<C, I>) -> Self { - (external.shielded_identity, external.spend) - } -} - /// Open [`Spend`] pub struct OpenSpend<C> where @@ -773,10 +699,7 @@ where /// Builds a new [`PreSender`] for `self`. #[inline] - pub fn into_pre_sender(self, commitment_scheme: &C::CommitmentScheme) -> PreSender<C> - where - Standard: Distribution<AssetParameters<C>>, - { + pub fn into_pre_sender(self, commitment_scheme: &C::CommitmentScheme) -> PreSender<C> { self.identity.into_pre_sender(commitment_scheme, self.asset) } @@ -789,7 +712,6 @@ where ) -> Option<Sender<C, S>> where S: VerifiedSet<Item = Utxo<C>>, - Standard: Distribution<AssetParameters<C>>, { self.identity .into_sender(commitment_scheme, self.asset, utxo_set) @@ -805,18 +727,18 @@ where /// Receiver pub receiver: Receiver<C, I>, - /// Open Spend - pub open_spend: OpenSpend<C>, + /// Pre-Sender + pub pre_sender: PreSender<C>, } -impl<C, I> From<InternalReceiver<C, I>> for (Receiver<C, I>, OpenSpend<C>) +impl<C, I> From<InternalReceiver<C, I>> for (Receiver<C, I>, PreSender<C>) where C: Configuration, I: IntegratedEncryptionScheme<Plaintext = Asset>, { #[inline] fn from(internal: InternalReceiver<C, I>) -> Self { - (internal.receiver, internal.open_spend) + (internal.receiver, internal.pre_sender) } } @@ -897,10 +819,7 @@ where identity: Identity<C>, commitment_scheme: &C::CommitmentScheme, asset: Asset, - ) -> Self - where - Standard: Distribution<AssetParameters<C>>, - { + ) -> Self { identity.into_pre_sender(commitment_scheme, asset) } @@ -1004,10 +923,7 @@ where commitment_scheme: &C::CommitmentScheme, asset: Asset, utxo_set: &S, - ) -> Option<Self> - where - Standard: Distribution<AssetParameters<C>>, - { + ) -> Option<Self> { identity.into_sender(commitment_scheme, asset, utxo_set) } @@ -1412,7 +1328,7 @@ pub mod constraint { /// Pseudorandom Function Family Input Variable type PseudorandomFunctionFamilyInputVar: Variable< Self::ConstraintSystem, - Type = <Self::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Input, + Type = Self::PseudorandomFunctionFamilyInput, Mode = Secret, >; @@ -1437,7 +1353,7 @@ pub mod constraint { /// Commitment Scheme Randomness Variable type CommitmentSchemeRandomnessVar: Variable< Self::ConstraintSystem, - Type = <Self::CommitmentScheme as CommitmentScheme>::Randomness, + Type = Self::CommitmentSchemeRandomness, Mode = Secret, >; diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 03869de83..0d1ee7635 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -41,13 +41,10 @@ use manta_crypto::{ PublicOrSecret, Secret, Variable, VariableSource, }, ies::{EncryptedMessage, IntegratedEncryptionScheme}, + rand::{CryptoRng, Rand, RngCore, Sample, Standard}, set::{constraint::VerifierVariable, VerifiedSet, Verifier}, }; use manta_util::{create_seal, from_variant_impl, iter::mixed_chain, seal, Either}; -use rand::{ - distributions::{Distribution, Standard}, - CryptoRng, RngCore, -}; /// Returns `true` if the transfer with this shape would have no public side. #[inline] @@ -140,6 +137,10 @@ pub type ReceiverPost<C> = pub type ReceiverPostingKey<C, L> = identity::ReceiverPostingKey<C, <C as Configuration>::IntegratedEncryptionScheme, L>; +/// Internal Receiver Type +pub type InternalReceiver<C> = + identity::InternalReceiver<C, <C as Configuration>::IntegratedEncryptionScheme>; + /// Transfer Encrypted Asset Type pub type EncryptedAsset<C> = EncryptedMessage<<C as Configuration>::IntegratedEncryptionScheme>; @@ -443,13 +444,15 @@ impl Default for PublicTransfer<0, 0> { } } -impl<const SOURCES: usize, const SINKS: usize> Distribution<PublicTransfer<SOURCES, SINKS>> - for Standard -{ +impl<const SOURCES: usize, const SINKS: usize> Sample for PublicTransfer<SOURCES, SINKS> { #[inline] - fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> PublicTransfer<SOURCES, SINKS> { - PublicTransfer::new( - self.sample(rng), + fn sample<R>(distribution: &Standard, rng: &mut R) -> Self + where + R: RngCore + ?Sized, + { + let _ = distribution; + Self::new( + rng.gen(), sample_asset_balances(rng), sample_asset_balances(rng), ) @@ -1136,7 +1139,7 @@ pub trait Shape: sealed::Sealed { /// Canonical Transaction Types pub mod canonical { use super::*; - use crate::identity::{AssetParameters, Identity, PreSender}; + use crate::identity::{Identity, PreSender}; /// Implements [`Shape`] for a given shape type. macro_rules! impl_shape { @@ -1210,7 +1213,6 @@ pub mod canonical { ) -> Result<Mint<C>, IntegratedEncryptionSchemeError<C>> where R: CryptoRng + RngCore + ?Sized, - Standard: Distribution<AssetParameters<C>>, { // TODO: Add convenience method for `Identity::into_receiver`. Ok(Mint::build( @@ -1234,16 +1236,13 @@ pub mod canonical { ) -> Result<(Mint<C>, PreSender<C>), IntegratedEncryptionSchemeError<C>> where R: CryptoRng + RngCore + ?Sized, - Standard: Distribution<AssetParameters<C>>, { - // TODO: Make this more convenient. let asset = Asset::zero(asset_id); - let (receiver, open_spend) = identity - .into_internal_receiver(commitment_scheme, asset, rng)? - .into(); + let internal_receiver = + identity.into_internal_receiver(commitment_scheme, asset, rng)?; Ok(( - Mint::build(asset, receiver), - open_spend.into_pre_sender(commitment_scheme), + Mint::build(asset, internal_receiver.receiver), + internal_receiver.pre_sender, )) } } diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 72f665266..7bac7d7e9 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -21,7 +21,7 @@ use crate::{ asset::{Asset, AssetBalance, AssetId, AssetMap}, fs::{Load, LoadWith, Save, SaveWith}, - identity::{self, AssetParameters, Identity, PreSender, Utxo}, + identity::{self, Identity, PreSender, Utxo}, keys::{ Account, DerivedSecretKeyGenerator, ExternalIndex, Index, InternalIndex, InternalKeyOwned, KeyOwned, @@ -29,8 +29,8 @@ use crate::{ transfer::{ self, canonical::{Mint, PrivateTransfer, Reclaim, Transaction}, - EncryptedAsset, IntegratedEncryptionSchemeError, ProofSystemError, ProvingContext, - Receiver, SecretTransfer, Sender, ShieldedIdentity, Transfer, TransferPost, + EncryptedAsset, IntegratedEncryptionSchemeError, InternalReceiver, ProofSystemError, + ProvingContext, Receiver, SecretTransfer, Sender, ShieldedIdentity, Transfer, TransferPost, }, }; use alloc::{vec, vec::Vec}; @@ -42,12 +42,11 @@ use core::{ mem, ops::Range, }; -use manta_crypto::set::VerifiedSet; -use manta_util::{fallible_array_map, into_array_unchecked, iter::IteratorExt}; -use rand::{ - distributions::{Distribution, Standard}, - CryptoRng, RngCore, +use manta_crypto::{ + rand::{CryptoRng, RngCore}, + set::VerifiedSet, }; +use manta_util::{fallible_array_map, into_array_unchecked, iter::IteratorExt}; /// Rollback Trait pub trait Rollback { @@ -356,7 +355,6 @@ where ) -> Result<PreSender<C>, D::Error> where C: transfer::Configuration<SecretKey = D::SecretKey>, - Standard: Distribution<AssetParameters<C>>, { Ok(self.get(&index)?.into_pre_sender(commitment_scheme, asset)) } @@ -395,7 +393,6 @@ where ) -> Result<ShieldedIdentity<C>, D::Error> where C: transfer::Configuration<SecretKey = D::SecretKey>, - Standard: Distribution<AssetParameters<C>>, { Ok(self .next_external_identity()? @@ -416,7 +413,6 @@ where where C: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, - Standard: Distribution<AssetParameters<C>>, { let mut receivers = Vec::with_capacity(RECEIVERS); let mut pre_senders = Vec::with_capacity(RECEIVERS - 1); @@ -429,12 +425,7 @@ where .into_internal_receiver(commitment_scheme, Asset::zero(asset_id), rng) .map_err(InternalReceiverError::EncryptionError)?; receivers.push(internal_receiver.receiver); - pre_senders.push(KeyOwned::new( - internal_receiver - .open_spend - .into_pre_sender(commitment_scheme), - index, - )); + pre_senders.push(KeyOwned::new(internal_receiver.pre_sender, index)); } Ok((receivers, pre_senders)) } @@ -446,26 +437,16 @@ where commitment_scheme: &C::CommitmentScheme, asset: Asset, rng: &mut R, - ) -> Result<(Receiver<C>, PreSender<C>), InternalReceiverError<D, C>> + ) -> Result<InternalReceiver<C>, InternalReceiverError<D, C>> where C: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, - Standard: Distribution<AssetParameters<C>>, { - // TODO: Simplify this so that `into_internal_receiver` automatically produces a - // `PreSender` instead of an `OpenSpend`. - let internal_receiver = self - .next_internal_identity() + self.next_internal_identity() .map_err(InternalReceiverError::SecretKeyError)? .unwrap() .into_internal_receiver(commitment_scheme, asset, rng) - .map_err(InternalReceiverError::EncryptionError)?; - Ok(( - internal_receiver.receiver, - internal_receiver - .open_spend - .into_pre_sender(commitment_scheme), - )) + .map_err(InternalReceiverError::EncryptionError) } /// Builds the next accumulated receiver. @@ -487,16 +468,15 @@ where where C: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, - Standard: Distribution<AssetParameters<C>>, { let (mut receivers, zero_pre_senders) = self.next_zeroes::<_, _, RECEIVERS>(commitment_scheme, asset_id, rng)?; - let (receiver, pre_sender) = + let internal_receiver = self.next_accumulator(commitment_scheme, asset_id.with(sender_sum), rng)?; - receivers.push(receiver); + receivers.push(internal_receiver.receiver); Ok(( into_array_unchecked(receivers), - pre_sender, + internal_receiver.pre_sender, zero_pre_senders, )) } @@ -512,7 +492,6 @@ where where C: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, - Standard: Distribution<AssetParameters<C>>, { // TODO: Simplify this so that `into_shielded` and `into_receiver` can be replaced by a // one-step `into_receiver` call on `Identity`. @@ -539,7 +518,6 @@ where where C: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, - Standard: Distribution<AssetParameters<C>>, { let (identity, index) = self .next_internal_identity() @@ -564,7 +542,6 @@ where where C: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, - Standard: Distribution<AssetParameters<C>>, { Mint::zero( self.next_internal_identity() @@ -585,7 +562,6 @@ where ) -> Option<(Index<D>, Asset)> where C: transfer::Configuration<SecretKey = D::SecretKey>, - Standard: Distribution<AssetParameters<C>>, { // FIXME: Simplify this implementation. let open_spend = self @@ -593,7 +569,7 @@ where .external_keys(&self.secret_key_source) .find_map(move |ek| { ek.map(move |ek| { - ek.map(move |k| Identity::new(k).try_open(encrypted_asset)) + ek.map(move |k| Identity::<C>::new(k).try_open(encrypted_asset)) .ok() }) .ok() @@ -778,7 +754,6 @@ where fn sync_inner<I>(&mut self, updates: I) -> SyncResult<D, C, Self> where I: Iterator<Item = (Utxo<C>, EncryptedAsset<C>)>, - Standard: Distribution<AssetParameters<C>>, { let mut assets = Vec::new(); for (utxo, encrypted_asset) in updates { @@ -786,7 +761,7 @@ where // `utxo_set`. If the `utxo` is accompanied by an `encrypted_asset` then we // "strong insert", if not we "weak insert". // - if let Some((key, asset)) = self.signer.find_external_asset(&encrypted_asset) { + if let Some((key, asset)) = self.signer.find_external_asset::<C>(&encrypted_asset) { assets.push(asset); self.assets.insert(key, asset); } @@ -808,7 +783,6 @@ where ) -> SyncResult<D, C, Self> where I: IntoIterator<Item = (Utxo<C>, EncryptedAsset<C>)>, - Standard: Distribution<AssetParameters<C>>, { self.start_sync(sync_state); match self.utxo_set.len().checked_sub(starting_index) { @@ -819,10 +793,7 @@ where /// Returns a [`PreSender`] for the key at the given `index`. #[inline] - fn get_pre_sender(&self, index: Index<D>, asset: Asset) -> Result<PreSender<C>, Error<D, C>> - where - Standard: Distribution<AssetParameters<C>>, - { + fn get_pre_sender(&self, index: Index<D>, asset: Asset) -> Result<PreSender<C>, Error<D, C>> { self.signer .get_pre_sender(index, &self.commitment_scheme, asset) .map_err(Error::SecretKeyError) @@ -830,10 +801,7 @@ where /// Selects the pre-senders which collectively own at least `asset`, returning any change. #[inline] - fn select(&mut self, asset: Asset) -> Result<(AssetBalance, Vec<PreSender<C>>), Error<D, C>> - where - Standard: Distribution<AssetParameters<C>>, - { + fn select(&mut self, asset: Asset) -> Result<(AssetBalance, Vec<PreSender<C>>), Error<D, C>> { let selection = self.assets.select(asset); if selection.is_empty() { return Err(Error::InsufficientBalance(asset)); @@ -885,10 +853,7 @@ where Vec<TransferPost<C>>, ), Error<D, C>, - > - where - Standard: Distribution<AssetParameters<C>>, - { + > { assert!( (SENDERS > 1) && (RECEIVERS > 1), "The transfer shape must include at least two senders and two receivers." @@ -943,10 +908,7 @@ where mut new_zeroes: Vec<InternalKeyOwned<D, PreSender<C>>>, pre_senders: &mut Vec<PreSender<C>>, posts: &mut Vec<TransferPost<C>>, - ) -> Result<(), Error<D, C>> - where - Standard: Distribution<AssetParameters<C>>, - { + ) -> Result<(), Error<D, C>> { let mut needed_zeroes = SENDERS - pre_senders.len(); if needed_zeroes == 0 { return Ok(()); @@ -1001,10 +963,7 @@ where &mut self, asset_id: AssetId, change: AssetBalance, - ) -> Result<Receiver<C>, Error<D, C>> - where - Standard: Distribution<AssetParameters<C>>, - { + ) -> Result<Receiver<C>, Error<D, C>> { let asset = asset_id.with(change); let (receiver, index) = self .signer @@ -1032,10 +991,7 @@ where &mut self, asset: Asset, receiver: Option<ShieldedIdentity<C>>, - ) -> SignResult<D, C, Self> - where - Standard: Distribution<AssetParameters<C>>, - { + ) -> SignResult<D, C, Self> { let (change, pre_senders) = self.select(asset)?; let (new_zeroes, mut pre_senders, mut posts) = @@ -1075,10 +1031,7 @@ where &mut self, asset: Asset, receiver: Option<ShieldedIdentity<C>>, - ) -> SignResult<D, C, Self> - where - Standard: Distribution<AssetParameters<C>>, - { + ) -> SignResult<D, C, Self> { let result = self.sign_withdraw_inner(asset, receiver); if result.is_err() { self.rollback(); @@ -1088,10 +1041,7 @@ where /// Signs the `transaction`, generating transfer posts. #[inline] - pub fn sign(&mut self, transaction: Transaction<C>) -> SignResult<D, C, Self> - where - Standard: Distribution<AssetParameters<C>>, - { + pub fn sign(&mut self, transaction: Transaction<C>) -> SignResult<D, C, Self> { self.commit(); match transaction { Transaction::Mint(asset) => { @@ -1136,10 +1086,7 @@ where /// Generates a new [`ShieldedIdentity`] for `self` to receive assets. #[inline] - pub fn external_receiver(&mut self) -> ExternalReceiverResult<D, C, Self> - where - Standard: Distribution<AssetParameters<C>>, - { + pub fn external_receiver(&mut self) -> ExternalReceiverResult<D, C, Self> { self.signer .next_shielded(&self.commitment_scheme) .map_err(Error::SecretKeyError) @@ -1153,7 +1100,6 @@ where C::UtxoSet: Rollback, M: AssetMap<Key = Index<D>>, R: CryptoRng + RngCore, - Standard: Distribution<AssetParameters<C>>, { type SyncFuture = Ready<SyncResult<D, C, Self>>; diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index 04d2fa6e4..f4112604d 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -29,6 +29,7 @@ pub mod commitment; pub mod constraint; pub mod ies; pub mod merkle_tree; +pub mod rand; pub mod set; pub use commitment::prelude::*; diff --git a/manta-crypto/src/rand.rs b/manta-crypto/src/rand.rs new file mode 100644 index 000000000..777739bc8 --- /dev/null +++ b/manta-crypto/src/rand.rs @@ -0,0 +1,312 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Random Number Generators + +use core::{fmt::Debug, hash::Hash, marker::PhantomData}; + +pub use rand_core::{block, CryptoRng, Error, RngCore, SeedableRng}; + +/// Random Number Generator Sized Wrapper +#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct SizedRng<'r, R>( + /// Mutable Reference to Random Number Generator + pub &'r mut R, +) +where + R: ?Sized; + +impl<'r, R> CryptoRng for SizedRng<'r, R> where R: CryptoRng + ?Sized {} + +impl<'r, R> RngCore for SizedRng<'r, R> +where + R: RngCore + ?Sized, +{ + #[inline] + fn next_u32(&mut self) -> u32 { + self.0.next_u32() + } + + #[inline] + fn next_u64(&mut self) -> u64 { + self.0.next_u64() + } + + #[inline] + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.0.fill_bytes(dest) + } + + #[inline] + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + self.0.try_fill_bytes(dest) + } +} + +impl<'r, R> block::BlockRngCore for SizedRng<'r, R> +where + R: block::BlockRngCore + ?Sized, +{ + type Item = R::Item; + + type Results = R::Results; + + #[inline] + fn generate(&mut self, results: &mut Self::Results) { + self.0.generate(results) + } +} + +/// Seed Into Random Number Generator +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "R: Clone"), + Copy(bound = "R: Copy"), + Debug(bound = "R: Debug"), + Default(bound = "R: Default"), + Eq(bound = "R: Eq"), + Hash(bound = "R: Hash"), + Ord(bound = "R: Ord"), + PartialEq(bound = "R: PartialEq"), + PartialOrd(bound = "R: PartialOrd") +)] +pub struct SeedIntoRng<S, R> { + /// Inner Rng + inner: R, + + /// Type Parameter Marker + __: PhantomData<S>, +} + +impl<S, R> SeedIntoRng<S, R> { + /// Builds a new [`SeedIntoRng`] from an existing `inner` random number generator. + #[inline] + fn new(inner: R) -> Self { + Self { + inner, + __: PhantomData, + } + } +} + +impl<S, R> CryptoRng for SeedIntoRng<S, R> where R: CryptoRng {} + +impl<S, R> RngCore for SeedIntoRng<S, R> +where + R: RngCore, +{ + #[inline] + fn next_u32(&mut self) -> u32 { + self.inner.next_u32() + } + + #[inline] + fn next_u64(&mut self) -> u64 { + self.inner.next_u64() + } + + #[inline] + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.inner.fill_bytes(dest) + } + + #[inline] + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + self.inner.try_fill_bytes(dest) + } +} + +impl<S, R> block::BlockRngCore for SeedIntoRng<S, R> +where + R: block::BlockRngCore, +{ + type Item = R::Item; + + type Results = R::Results; + + #[inline] + fn generate(&mut self, results: &mut Self::Results) { + self.inner.generate(results) + } +} + +impl<S, R> SeedableRng for SeedIntoRng<S, R> +where + S: Into<R::Seed> + Default + AsMut<[u8]>, + R: SeedableRng, +{ + type Seed = S; + + #[inline] + fn from_seed(seed: Self::Seed) -> Self { + Self::new(R::from_seed(seed.into())) + } + + #[inline] + fn seed_from_u64(state: u64) -> Self { + Self::new(R::seed_from_u64(state)) + } + + #[inline] + fn from_rng<T: RngCore>(rng: T) -> Result<Self, Error> { + R::from_rng(rng).map(Self::new) + } + + #[cfg(feature = "getrandom")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "getrandom")))] + #[inline] + fn from_entropy() -> Self { + Self::new(R::from_entropy()) + } +} + +/// Standard Distribution +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Standard; + +/// Sampling Trait +pub trait Sample<D = Standard>: Sized { + /// Returns a random value of type `Self`, sampled according to the given `distribution`, + /// generated from the `rng`. + fn sample<R>(distribution: &D, rng: &mut R) -> Self + where + R: RngCore + ?Sized; + + /// Returns a random value of type `Self`, sampled according to the default distribution of + /// type `D`, generated from the `rng`. + #[inline] + fn gen<R>(rng: &mut R) -> Self + where + D: Default, + R: RngCore + ?Sized, + { + Self::sample(&Default::default(), rng) + } +} + +/// Cryptographic Sampling Trait +pub trait CryptoSample<D = Standard>: Sized { + /// Returns a random value of type `Self`, sampled according to the given `distribution`, + /// generated from the `rng`. + fn sample<R>(distribution: &D, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized; + + /// Returns a random value of type `Self`, sampled according to the default distribution of + /// type `D`, generated from the `rng`. + #[inline] + fn gen<R>(rng: &mut R) -> Self + where + D: Default, + R: CryptoRng + RngCore + ?Sized, + { + Self::sample(&Default::default(), rng) + } +} + +/// Generates [`Sample`] implementation for `$type` using conversion from `u32`. +macro_rules! impl_sample_from_u32 { + ($($type:tt),+) => { + $( + impl Sample for $type { + #[inline] + fn sample<R>(distribution: &Standard, rng: &mut R) -> Self + where + R: RngCore + ?Sized, + { + let _ = distribution; + rng.next_u32() as Self + } + } + )+ + }; +} + +impl_sample_from_u32! { u8, u16, u32 } + +impl Sample for u64 { + #[inline] + fn sample<R>(distribution: &Standard, rng: &mut R) -> Self + where + R: RngCore + ?Sized, + { + let _ = distribution; + rng.next_u64() + } +} + +impl Sample for u128 { + #[inline] + fn sample<R>(distribution: &Standard, rng: &mut R) -> Self + where + R: RngCore + ?Sized, + { + let _ = distribution; + ((rng.next_u64() as u128) << 64) | (rng.next_u64() as u128) + } +} + +/// Random Number Generator +pub trait Rand: RngCore { + /// Returns a random value of type `Self`, sampled according to the given `distribution`, + /// generated from the `rng`. + #[inline] + fn sample<D, T>(&mut self, distribution: &D) -> T + where + T: Sample<D>, + { + T::sample(distribution, self) + } + + /// Returns a random value of type `Self`, sampled according to the default distribution of + /// type `D`, generated from the `rng`. + #[inline] + fn gen<D, T>(&mut self) -> T + where + D: Default, + T: Sample<D>, + { + T::gen(self) + } +} + +impl<R> Rand for R where R: RngCore + ?Sized {} + +/// Cryptographically Secure Random Number Generator +pub trait CryptoRand: CryptoRng + RngCore { + /// Returns a random value of type `Self`, sampled according to the given `distribution`, + /// generated from the `rng`. + #[inline] + fn sample<D, T>(&mut self, distribution: &D) -> T + where + T: CryptoSample<D>, + { + T::sample(distribution, self) + } + + /// Returns a random value of type `Self`, sampled according to the default distribution of + /// type `D`, generated from the `rng`. + #[inline] + fn gen<D, T>(&mut self) -> T + where + D: Default, + T: CryptoSample<D>, + { + T::gen(self) + } +} + +impl<R> CryptoRand for R where R: CryptoRng + Rand + ?Sized {} diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 720a83f5e..8af4816c9 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -62,8 +62,7 @@ dusk-plonk = { version = "0.8.2", optional = true, default-features = false } generic-array = { version = "0.14.4", default-features = false } manta-accounting = { path = "../manta-accounting", default-features = false } manta-crypto = { path = "../manta-crypto", default-features = false } -manta-util = { path = "../manta-util", default-features = false, features = ["rand_core"] } -rand = { version = "0.8.4", default-features = false } +manta-util = { path = "../manta-util", default-features = false } rand_chacha = { version = "0.3.1", default-features = false } x25519-dalek = { git = "https://github.com/Manta-Network/x25519-dalek", default-features = false, features = ["u64_backend"] } diff --git a/manta-pay/src/accounting/config.rs b/manta-pay/src/accounting/config.rs index 4e2414f08..e0f2350ed 100644 --- a/manta-pay/src/accounting/config.rs +++ b/manta-pay/src/accounting/config.rs @@ -38,8 +38,9 @@ use ark_bls12_381::Bls12_381; use ark_crypto_primitives::crh::pedersen::{constraints::CRHGadget, CRH}; use ark_ed_on_bls12_381::{constraints::EdwardsVar, EdwardsProjective, Fq}; use manta_accounting::{identity, transfer}; -use manta_crypto::{commitment::CommitmentScheme, merkle_tree, PseudorandomFunctionFamily}; -use manta_util::rand::SeedIntoRng; +use manta_crypto::{ + commitment::CommitmentScheme, merkle_tree, rand::SeedIntoRng, PseudorandomFunctionFamily, +}; use rand_chacha::ChaCha20Rng; /// Pedersen Window Parameters @@ -128,7 +129,9 @@ impl merkle_tree_constraint::Configuration for Configuration { impl identity::Configuration for Configuration { type SecretKey = <Blake2s as PseudorandomFunctionFamily>::Seed; + type PseudorandomFunctionFamilyInput = <Blake2s as PseudorandomFunctionFamily>::Input; type PseudorandomFunctionFamily = Blake2s; + type CommitmentSchemeRandomness = <PedersenCommitment as CommitmentScheme>::Randomness; type CommitmentScheme = PedersenCommitment; type Rng = SeedIntoRng<Self::SecretKey, ChaCha20Rng>; } diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs index cedd60940..ab2133288 100644 --- a/manta-pay/src/crypto/commitment/pedersen.rs +++ b/manta-pay/src/crypto/commitment/pedersen.rs @@ -21,12 +21,11 @@ use ark_crypto_primitives::commitment::{ pedersen::Commitment as ArkPedersenCommitment, CommitmentScheme as ArkCommitmentScheme, }; use ark_ff::bytes::ToBytes; -use manta_crypto::commitment::CommitmentScheme; -use manta_util::{rand::SizedRng, Concat, ConcatAccumulator}; -use rand::{ - distributions::{Distribution, Standard}, - RngCore, +use manta_crypto::{ + commitment::CommitmentScheme, + rand::{CryptoRand, CryptoRng, CryptoSample, RngCore, SizedRng, Standard}, }; +use manta_util::{Concat, ConcatAccumulator}; /// Pedersen Window Parameters Trait // TODO: Remove this comment once `arkworks` writes their own. @@ -60,6 +59,21 @@ where W: PedersenWindow, C: ProjectiveCurve; +impl<W, C> CryptoSample for PedersenCommitmentRandomness<W, C> +where + W: PedersenWindow, + C: ProjectiveCurve, +{ + #[inline] + fn sample<R>(distribution: &Standard, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + // FIXME: Implement. + todo!() + } +} + /// Pedersen Commitment Output #[derive(derivative::Derivative)] #[derivative( @@ -138,14 +152,18 @@ where } } -impl<W, C> Distribution<PedersenCommitment<W, C>> for Standard +impl<W, C> CryptoSample for PedersenCommitment<W, C> where W: PedersenWindow, C: ProjectiveCurve, { #[inline] - fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> PedersenCommitment<W, C> { - PedersenCommitment( + fn sample<R>(distribution: &Standard, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + let _ = distribution; + Self( ArkPedersenCommitment::<_, W>::setup(&mut SizedRng(rng)) .expect("Sampling is not allowed to fail."), ) @@ -315,16 +333,20 @@ pub mod constraint { } } - impl<W, C, GG> Distribution<PedersenCommitmentWrapper<W, C, GG>> for Standard + impl<W, C, GG, D> CryptoSample<D> for PedersenCommitmentWrapper<W, C, GG> where W: PedersenWindow, C: ProjectiveCurve, GG: CurveVar<C, ConstraintField<C>>, for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + PedersenCommitment<W, C>: CryptoSample<D>, { #[inline] - fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> PedersenCommitmentWrapper<W, C, GG> { - PedersenCommitmentWrapper(self.sample(rng), PhantomData) + fn sample<R>(distribution: &D, rng: &mut R) -> PedersenCommitmentWrapper<W, C, GG> + where + R: CryptoRng + RngCore + ?Sized, + { + Self(rng.sample(distribution), PhantomData) } } diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs index c5c5c7068..6592f7bad 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs @@ -28,9 +28,10 @@ use ark_ff::Field; use ark_groth16::{Groth16 as ArkGroth16, PreparedVerifyingKey, Proof, ProvingKey}; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError}; use core::marker::PhantomData; -use manta_crypto::constraint::ProofSystem; -use manta_util::rand::SizedRng; -use rand::{CryptoRng, RngCore}; +use manta_crypto::{ + constraint::ProofSystem, + rand::{CryptoRng, RngCore, SizedRng}, +}; /// Arkworks Groth 16 Proof System #[derive(derivative::Derivative)] diff --git a/manta-pay/src/crypto/ies.rs b/manta-pay/src/crypto/ies.rs index 26ed9d427..d0bd6bf48 100644 --- a/manta-pay/src/crypto/ies.rs +++ b/manta-pay/src/crypto/ies.rs @@ -28,10 +28,10 @@ use generic_array::GenericArray; use manta_accounting::Asset; use manta_crypto::{ ies::{self, KeyPair}, + rand::{CryptoRng, RngCore}, IntegratedEncryptionScheme, }; use manta_util::into_array_unchecked; -use rand::{CryptoRng, RngCore}; use x25519_dalek::{EphemeralSecret, PublicKey as PubKey, StaticSecret}; /// Public Key Type @@ -190,8 +190,8 @@ impl IntegratedEncryptionScheme for IES { #[cfg(test)] mod test { use super::*; - use manta_crypto::ies::test as ies_test; - use rand::{thread_rng, Rng}; + use manta_crypto::{ies::test as ies_test, rand::Rand}; + use rand::thread_rng; /// Tests encryption/decryption of a random asset. #[test] diff --git a/manta-pay/src/crypto/prf/blake2s.rs b/manta-pay/src/crypto/prf/blake2s.rs index 0f546940a..322e0732b 100644 --- a/manta-pay/src/crypto/prf/blake2s.rs +++ b/manta-pay/src/crypto/prf/blake2s.rs @@ -17,7 +17,10 @@ //! Blake2s PRF Implementation use ark_crypto_primitives::prf::{Blake2s as ArkBlake2s, PRF}; -use manta_crypto::PseudorandomFunctionFamily; +use manta_crypto::{ + rand::{CryptoRng, CryptoSample, RngCore, Standard}, + PseudorandomFunctionFamily, +}; use manta_util::{Concat, ConcatAccumulator}; /// Blake2s Pseudorandom Function Family @@ -70,6 +73,17 @@ impl Concat for Blake2sInput { } } +impl CryptoSample for Blake2sInput { + #[inline] + fn sample<R>(distribution: &Standard, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + // FIXME: Implement. + todo!() + } +} + /// Blake2s Pseudorandom Function Family Output #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Blake2sOutput(<ArkBlake2s as PRF>::Output); diff --git a/manta-util/Cargo.toml b/manta-util/Cargo.toml index ebbf6638f..a14a9a7f7 100644 --- a/manta-util/Cargo.toml +++ b/manta-util/Cargo.toml @@ -23,6 +23,3 @@ rustdoc-args = ["--cfg", "doc_cfg"] is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } - -[dependencies] -rand_core = { version = "0.6.3", optional = true, default-features = false } diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index 637c16226..fea2c6ac9 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -30,10 +30,6 @@ mod sealed; pub mod iter; pub mod num; -#[cfg(feature = "rand_core")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "rand_core")))] -pub mod rand; - pub use array::*; pub use concat::*; diff --git a/manta-util/src/rand.rs b/manta-util/src/rand.rs deleted file mode 100644 index f260da955..000000000 --- a/manta-util/src/rand.rs +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Random Number Generator Utilities - -use core::marker::PhantomData; -use rand_core::{block::BlockRngCore, CryptoRng, Error, RngCore, SeedableRng}; - -/// Random Number Generator Sized Wrapper -#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct SizedRng<'r, R>(pub &'r mut R) -where - R: ?Sized; - -impl<'r, R> CryptoRng for SizedRng<'r, R> where R: CryptoRng + ?Sized {} - -impl<'r, R> RngCore for SizedRng<'r, R> -where - R: RngCore + ?Sized, -{ - #[inline] - fn next_u32(&mut self) -> u32 { - self.0.next_u32() - } - - #[inline] - fn next_u64(&mut self) -> u64 { - self.0.next_u64() - } - - #[inline] - fn fill_bytes(&mut self, dest: &mut [u8]) { - self.0.fill_bytes(dest) - } - - #[inline] - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.0.try_fill_bytes(dest) - } -} - -impl<'r, R> BlockRngCore for SizedRng<'r, R> -where - R: BlockRngCore + ?Sized, -{ - type Item = R::Item; - - type Results = R::Results; - - #[inline] - fn generate(&mut self, results: &mut Self::Results) { - self.0.generate(results) - } -} - -/// Seed Into Random Number Generator -pub struct SeedIntoRng<S, R> { - /// Inner Rng - inner: R, - - /// Type Parameter Marker - __: PhantomData<S>, -} - -impl<S, R> SeedIntoRng<S, R> { - /// Builds a new [`SeedIntoRng`] from an existing `inner` random number generator. - #[inline] - fn new(inner: R) -> Self { - Self { - inner, - __: PhantomData, - } - } -} - -impl<S, R> CryptoRng for SeedIntoRng<S, R> where R: CryptoRng {} - -impl<S, R> RngCore for SeedIntoRng<S, R> -where - R: RngCore, -{ - #[inline] - fn next_u32(&mut self) -> u32 { - self.inner.next_u32() - } - - #[inline] - fn next_u64(&mut self) -> u64 { - self.inner.next_u64() - } - - #[inline] - fn fill_bytes(&mut self, dest: &mut [u8]) { - self.inner.fill_bytes(dest) - } - - #[inline] - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.inner.try_fill_bytes(dest) - } -} - -impl<S, R> BlockRngCore for SeedIntoRng<S, R> -where - R: BlockRngCore, -{ - type Item = R::Item; - - type Results = R::Results; - - #[inline] - fn generate(&mut self, results: &mut Self::Results) { - self.inner.generate(results) - } -} - -impl<S, R> SeedableRng for SeedIntoRng<S, R> -where - S: Into<R::Seed> + Default + AsMut<[u8]>, - R: SeedableRng, -{ - type Seed = S; - - #[inline] - fn from_seed(seed: Self::Seed) -> Self { - Self::new(R::from_seed(seed.into())) - } - - #[inline] - fn seed_from_u64(state: u64) -> Self { - Self::new(R::seed_from_u64(state)) - } - - #[inline] - fn from_rng<T: RngCore>(rng: T) -> Result<Self, Error> { - R::from_rng(rng).map(Self::new) - } - - #[cfg(feature = "getrandom")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "getrandom")))] - #[inline] - fn from_entropy() -> Self { - Self::new(R::from_entropy()) - } -} From 47b8f8db4889707b5b80ad9dc6b3a72cade4ab04 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 12 Oct 2021 15:26:42 -0400 Subject: [PATCH 092/275] finish signer implementation --- manta-accounting/src/identity.rs | 68 ++++- manta-accounting/src/keys.rs | 54 ++-- manta-accounting/src/transfer.rs | 45 +-- manta-accounting/src/wallet/ledger.rs | 13 +- manta-accounting/src/wallet/signer.rs | 394 +++++++++++++------------- manta-accounting/src/wallet/state.rs | 13 + manta-crypto/src/merkle_tree/mod.rs | 2 - manta-crypto/src/set.rs | 16 +- 8 files changed, 347 insertions(+), 258 deletions(-) diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 884fb0f3e..69630d5c9 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -453,20 +453,36 @@ where self.into_spend().try_open(encrypted_asset) } - /// Builds a new [`InternalReceiver`]. + /// Builds a new [`Receiver`]. #[inline] - pub fn into_internal_receiver<I, R>( + pub fn into_receiver<I, R>( self, commitment_scheme: &C::CommitmentScheme, asset: Asset, rng: &mut R, - ) -> Result<InternalReceiver<C, I>, I::Error> + ) -> Result<Receiver<C, I>, I::Error> + where + I: IntegratedEncryptionScheme<Plaintext = Asset>, + R: CryptoRng + RngCore + ?Sized, + { + self.into_shielded(commitment_scheme) + .into_receiver(commitment_scheme, asset, rng) + } + + /// Builds a new [`InternalIdentity`]. + #[inline] + pub fn into_internal<I, R>( + self, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + rng: &mut R, + ) -> Result<InternalIdentity<C, I>, I::Error> where I: IntegratedEncryptionScheme<Plaintext = Asset>, R: CryptoRng + RngCore + ?Sized, { let (parameters, asset_public_key) = self.parameters_and_asset_public_key(); - Ok(InternalReceiver { + Ok(InternalIdentity { receiver: self .build_shielded_identity(commitment_scheme, parameters, asset_public_key) .into_receiver(commitment_scheme, asset, rng)?, @@ -718,8 +734,8 @@ where } } -/// Internal Receiver -pub struct InternalReceiver<C, I> +/// Internal Identity +pub struct InternalIdentity<C, I> where C: Configuration, I: IntegratedEncryptionScheme<Plaintext = Asset>, @@ -731,13 +747,33 @@ where pub pre_sender: PreSender<C>, } -impl<C, I> From<InternalReceiver<C, I>> for (Receiver<C, I>, PreSender<C>) +impl<C, I> InternalIdentity<C, I> +where + C: Configuration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, +{ + /// Builds an [`InternalIdentity`] from an [`Identity`] for the given `asset`. + #[inline] + pub fn from_identity<R>( + identity: Identity<C>, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + rng: &mut R, + ) -> Result<Self, I::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + identity.into_internal(commitment_scheme, asset, rng) + } +} + +impl<C, I> From<InternalIdentity<C, I>> for (Receiver<C, I>, PreSender<C>) where C: Configuration, I: IntegratedEncryptionScheme<Plaintext = Asset>, { #[inline] - fn from(internal: InternalReceiver<C, I>) -> Self { + fn from(internal: InternalIdentity<C, I>) -> Self { (internal.receiver, internal.pre_sender) } } @@ -1147,7 +1183,21 @@ where C: Configuration, I: IntegratedEncryptionScheme<Plaintext = Asset>, { - /// Build a [`Receiver`] from a [`ShieldedIdentity`] for the `asset`. + /// Builds a [`Receiver`] from an [`Identity`] for the given `asset`. + #[inline] + pub fn from_identity<R>( + identity: Identity<C>, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + rng: &mut R, + ) -> Result<Self, I::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + identity.into_receiver(commitment_scheme, asset, rng) + } + + /// Builds a [`Receiver`] from a [`ShieldedIdentity`] for the given `asset`. #[inline] pub fn from_shielded<R>( identity: ShieldedIdentity<C, I>, diff --git a/manta-accounting/src/keys.rs b/manta-accounting/src/keys.rs index 6e5fe83f0..3d53a330a 100644 --- a/manta-accounting/src/keys.rs +++ b/manta-accounting/src/keys.rs @@ -214,6 +214,12 @@ where pub fn reduce(self) -> Index<D> { Index::new(self.kind.into(), self.index) } + + /// Wraps an `inner` value into a [`KeyOwned`] with `self` as the index. + #[inline] + pub fn wrap<T>(self, inner: T) -> KeyOwned<D, T, K> { + KeyOwned::new(inner, self) + } } impl<D> Index<D> @@ -316,7 +322,7 @@ where K: Into<KeyKind>, { /// Value Owned by the Key - pub value: T, + pub inner: T, /// Key Index pub index: Index<D, K>, @@ -327,28 +333,28 @@ where D: DerivedSecretKeyGenerator, K: Into<KeyKind>, { - /// Builds a new [`KeyOwned`] for `value` with `index` as the [`Index`]. + /// Builds a new [`KeyOwned`] for `inner` with `index` as the [`Index`]. #[inline] - pub fn new(value: T, index: Index<D, K>) -> Self { - Self { value, index } + pub fn new(inner: T, index: Index<D, K>) -> Self { + Self { inner, index } } - /// Builds a new [`KeyOwned`] for `value` with `kind` and `index` as the [`Index`]. + /// Builds a new [`KeyOwned`] for `inner` with `kind` and `index` as the [`Index`]. #[inline] - pub fn with_kind(value: T, kind: K, index: D::Index) -> Self { - Self::new(value, Index::new(kind, index)) + pub fn with_kind(inner: T, kind: K, index: D::Index) -> Self { + Self::new(inner, Index::new(kind, index)) } - /// Returns the inner [`self.value`](Self::value) dropping the [`self.index`](Self::index). + /// Returns the inner [`self.inner`](Self::inner) dropping the [`self.index`](Self::index). #[inline] pub fn unwrap(self) -> T { - self.value + self.inner } /// Reduces `self` into a [`KeyOwned`] with [`KeyKind`] as the key kind. #[inline] pub fn reduce(self) -> KeyOwned<D, T> { - KeyOwned::new(self.value, self.index.reduce()) + KeyOwned::new(self.inner, self.index.reduce()) } /// Maps the underlying value using `f`. @@ -357,7 +363,7 @@ where where F: FnOnce(T) -> U, { - KeyOwned::new(f(self.value), self.index) + KeyOwned::new(f(self.inner), self.index) } /// Maps the underlying value using `f` and then factors over the `Some` branch. @@ -383,16 +389,16 @@ impl<D, T> KeyOwned<D, T> where D: DerivedSecretKeyGenerator, { - /// Builds a new [`KeyOwned`] for `value` for an external key with `index`. + /// Builds a new [`KeyOwned`] for `inner` for an external key with `index`. #[inline] - pub fn new_external(value: T, index: D::Index) -> Self { - Self::new(value, Index::new_external(index)) + pub fn new_external(inner: T, index: D::Index) -> Self { + Self::new(inner, Index::new_external(index)) } - /// Builds a new [`KeyOwned`] for `value` for an internal key with `index`. + /// Builds a new [`KeyOwned`] for `inner` for an internal key with `index`. #[inline] - pub fn new_internal(value: T, index: D::Index) -> Self { - Self::new(value, Index::new_internal(index)) + pub fn new_internal(inner: T, index: D::Index) -> Self { + Self::new(inner, Index::new_internal(index)) } /// Returns `true` if `self` represents a value owned by an external key. @@ -415,7 +421,7 @@ where { #[inline] fn as_ref(&self) -> &T { - &self.value + &self.inner } } @@ -426,7 +432,7 @@ where { #[inline] fn as_mut(&mut self) -> &mut T { - &mut self.value + &mut self.inner } } @@ -437,7 +443,7 @@ where { #[inline] fn from(key_owned: KeyOwned<D, T, K>) -> Self { - (key_owned.value, key_owned.index) + (key_owned.inner, key_owned.index) } } @@ -449,13 +455,13 @@ where /// Factors the key index over the left value in the pair. #[inline] pub fn left(self) -> (KeyOwned<D, L, K>, R) { - (KeyOwned::new(self.value.0, self.index), self.value.1) + (KeyOwned::new(self.inner.0, self.index), self.inner.1) } /// Factors the key index over the right value in the pair. #[inline] pub fn right(self) -> (L, KeyOwned<D, R, K>) { - (self.value.0, KeyOwned::new(self.value.1, self.index)) + (self.inner.0, KeyOwned::new(self.inner.1, self.index)) } } @@ -467,7 +473,7 @@ where /// Converts a `KeyOwned<D, Option<T>, K>` into an `Option<KeyOwned<D, T, K>>`. #[inline] pub fn collect(self) -> Option<KeyOwned<D, T, K>> { - Some(KeyOwned::new(self.value?, self.index)) + Some(KeyOwned::new(self.inner?, self.index)) } } @@ -490,7 +496,7 @@ where /// Converts a `KeyOwned<D, Result<T, E>, K>` into an `Result<KeyOwned<D, T, K>, E>`. #[inline] pub fn collect(self) -> Result<KeyOwned<D, T, K>, E> { - Ok(KeyOwned::new(self.value?, self.index)) + Ok(KeyOwned::new(self.inner?, self.index)) } /// Converts a `KeyOwned<D, Result<T, E>, K>` into an `Option<KeyOwned<D, T, K>>`. diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 0d1ee7635..0ae512e1b 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -114,33 +114,33 @@ pub trait Configuration: pub type ShieldedIdentity<C> = identity::ShieldedIdentity<C, <C as Configuration>::IntegratedEncryptionScheme>; +/// Transfer Internal Identity Type +pub type InternalIdentity<C> = + identity::InternalIdentity<C, <C as Configuration>::IntegratedEncryptionScheme>; + /// Transfer Spend Type pub type Spend<C> = identity::Spend<C, <C as Configuration>::IntegratedEncryptionScheme>; /// Transfer Sender Type pub type Sender<C> = identity::Sender<C, <C as Configuration>::UtxoSet>; -/// Sender Post Type +/// Transfer Sender Post Type pub type SenderPost<C> = identity::SenderPost<C, <C as Configuration>::UtxoSet>; -/// Sender Posting Key Type +/// Transfer Sender Posting Key Type pub type SenderPostingKey<C, L> = identity::SenderPostingKey<C, <C as Configuration>::UtxoSet, L>; /// Transfer Receiver Type pub type Receiver<C> = identity::Receiver<C, <C as Configuration>::IntegratedEncryptionScheme>; -/// Receiver Post Type +/// Transfer Receiver Post Type pub type ReceiverPost<C> = identity::ReceiverPost<C, <C as Configuration>::IntegratedEncryptionScheme>; -/// Receiver Posting Key Type +/// Transfer Receiver Posting Key Type pub type ReceiverPostingKey<C, L> = identity::ReceiverPostingKey<C, <C as Configuration>::IntegratedEncryptionScheme, L>; -/// Internal Receiver Type -pub type InternalReceiver<C> = - identity::InternalReceiver<C, <C as Configuration>::IntegratedEncryptionScheme>; - /// Transfer Encrypted Asset Type pub type EncryptedAsset<C> = EncryptedMessage<<C as Configuration>::IntegratedEncryptionScheme>; @@ -1214,19 +1214,17 @@ pub mod canonical { where R: CryptoRng + RngCore + ?Sized, { - // TODO: Add convenience method for `Identity::into_receiver`. Ok(Mint::build( asset, - identity.into_shielded(commitment_scheme).into_receiver( - commitment_scheme, - asset, - rng, - )?, + identity.into_receiver(commitment_scheme, asset, rng)?, )) } /// Builds a [`Mint`] from an `identity` for an [`Asset`] with the given `asset_id` but /// zero value. + /// + /// This is particularly useful when constructing transactions accumulated from [`Transfer`] + /// objects and a zero slot on the sender side needs to be filled. #[inline] pub fn zero<R>( identity: Identity<C>, @@ -1238,12 +1236,8 @@ pub mod canonical { R: CryptoRng + RngCore + ?Sized, { let asset = Asset::zero(asset_id); - let internal_receiver = - identity.into_internal_receiver(commitment_scheme, asset, rng)?; - Ok(( - Mint::build(asset, internal_receiver.receiver), - internal_receiver.pre_sender, - )) + let internal = identity.into_internal(commitment_scheme, asset, rng)?; + Ok((Mint::build(asset, internal.receiver), internal.pre_sender)) } } @@ -1285,10 +1279,19 @@ pub mod canonical { /// ```text /// <0, 2, 1, 1> /// ``` + /// + /// The [`ReclaimShape`] is defined in terms of the [`PrivateTransferShape`]. It is defined to + /// have the same number of senders and one secret receiver turned into a public sink. #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd)] pub struct ReclaimShape; - impl_shape!(ReclaimShape, 0, 2, 1, 1); + impl_shape!( + ReclaimShape, + 0, + PrivateTransferShape::SENDERS, + PrivateTransferShape::RECEIVERS - 1, + 1 + ); /// Reclaim Transaction pub type Reclaim<C> = transfer_alias!(C, ReclaimShape); diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs index a8f230025..db0330e62 100644 --- a/manta-accounting/src/wallet/ledger.rs +++ b/manta-accounting/src/wallet/ledger.rs @@ -74,8 +74,8 @@ where /// Pulls all data from the ledger, returning the current [`Checkpoint`](Self::Checkpoint). fn pull_all(&self) -> Self::PullAllFuture; - /// Sends `posts` to the ledger returning the post of the transfer which failed and all - /// following transfer posts. + /// Sends `posts` to the ledger to be appended atomically, returning `false` if the posts were + /// invalid. fn push(&self, posts: Vec<TransferPost<C>>) -> Self::PushFuture; } @@ -137,12 +137,3 @@ pub struct PushResponse { /// Successful Push pub success: bool, } - -/* TODO: -/// -pub struct Ledger<C> -where - C: Configuration, {} - -impl<C> TransferLedger<C> for Ledger<C> where C: Configuration {} -*/ diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 7bac7d7e9..24ba8f88f 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -23,14 +23,15 @@ use crate::{ fs::{Load, LoadWith, Save, SaveWith}, identity::{self, Identity, PreSender, Utxo}, keys::{ - Account, DerivedSecretKeyGenerator, ExternalIndex, Index, InternalIndex, InternalKeyOwned, - KeyOwned, + Account, DerivedSecretKeyGenerator, ExternalKeyOwned, ExternalSecretKey, Index, + InternalIndex, InternalKeyOwned, KeyKind, KeyOwned, }, transfer::{ self, - canonical::{Mint, PrivateTransfer, Reclaim, Transaction}, - EncryptedAsset, IntegratedEncryptionSchemeError, InternalReceiver, ProofSystemError, - ProvingContext, Receiver, SecretTransfer, Sender, ShieldedIdentity, Transfer, TransferPost, + canonical::{Mint, PrivateTransfer, PrivateTransferShape, Reclaim, Transaction}, + EncryptedAsset, IntegratedEncryptionSchemeError, InternalIdentity, ProofSystemError, + ProvingContext, Receiver, SecretTransfer, Sender, Shape, ShieldedIdentity, Transfer, + TransferPost, }, }; use alloc::{vec, vec::Vec}; @@ -246,16 +247,16 @@ where ConnectionError(CE), } -impl<D, C, CE> From<InternalReceiverError<D, C>> for Error<D, C, CE> +impl<D, C, CE> From<InternalIdentityError<D, C>> for Error<D, C, CE> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, { #[inline] - fn from(err: InternalReceiverError<D, C>) -> Self { + fn from(err: InternalIdentityError<D, C>) -> Self { match err { - InternalReceiverError::SecretKeyError(err) => Self::SecretKeyError(err), - InternalReceiverError::EncryptionError(err) => Self::EncryptionError(err), + InternalIdentityError::SecretKeyError(err) => Self::SecretKeyError(err), + InternalIdentityError::EncryptionError(err) => Self::EncryptionError(err), } } } @@ -314,34 +315,17 @@ where /// Returns the identity for a key of the given `index`. #[inline] - pub fn get<C>(&self, index: &Index<D>) -> Result<Identity<C>, D::Error> + pub fn get<C, K>(&self, index: &Index<D, K>) -> Result<Identity<C>, D::Error> where C: identity::Configuration<SecretKey = D::SecretKey>, + K: Clone + Into<KeyKind>, { - index - .key(&self.secret_key_source, self.account.as_ref()) - .map(Identity::new) - } - - /// Returns the identity for an external key of the given `index`. - #[inline] - pub fn get_external<C>(&self, index: &ExternalIndex<D>) -> Result<Identity<C>, D::Error> - where - C: identity::Configuration<SecretKey = D::SecretKey>, - { - index - .key(&self.secret_key_source, self.account.as_ref()) - .map(Identity::new) - } - - /// Returns the identity for an internal key of the given `index`. - #[inline] - pub fn get_internal<C>(&self, index: &InternalIndex<D>) -> Result<Identity<C>, D::Error> - where - C: identity::Configuration<SecretKey = D::SecretKey>, - { - index - .key(&self.secret_key_source, self.account.as_ref()) + self.secret_key_source + .generate_key( + index.kind.clone().into(), + self.account.as_ref(), + &index.index, + ) .map(Identity::new) } @@ -399,84 +383,61 @@ where .into_shielded(commitment_scheme)) } - /// Builds the next zero accumulator receivers and pre-senders. - #[inline] - fn next_zeroes<C, R, const RECEIVERS: usize>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - asset_id: AssetId, - rng: &mut R, - ) -> Result< - (Vec<Receiver<C>>, Vec<InternalKeyOwned<D, PreSender<C>>>), - InternalReceiverError<D, C>, - > - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - R: CryptoRng + RngCore + ?Sized, - { - let mut receivers = Vec::with_capacity(RECEIVERS); - let mut pre_senders = Vec::with_capacity(RECEIVERS - 1); - for _ in 0..RECEIVERS - 1 { - let (identity, index) = self - .next_internal_identity() - .map_err(InternalReceiverError::SecretKeyError)? - .into(); - let internal_receiver = identity - .into_internal_receiver(commitment_scheme, Asset::zero(asset_id), rng) - .map_err(InternalReceiverError::EncryptionError)?; - receivers.push(internal_receiver.receiver); - pre_senders.push(KeyOwned::new(internal_receiver.pre_sender, index)); - } - Ok((receivers, pre_senders)) - } - - /// Builds the next accumulator receiver and pre-sender. + /// Generates a new [`InternalIdentity`] to receive assets in this account via an internal + /// transaction. #[inline] - fn next_accumulator<C, R>( + pub fn next_internal<C, R>( &mut self, commitment_scheme: &C::CommitmentScheme, asset: Asset, rng: &mut R, - ) -> Result<InternalReceiver<C>, InternalReceiverError<D, C>> + ) -> Result<InternalKeyOwned<D, InternalIdentity<C>>, InternalIdentityError<D, C>> where C: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, { self.next_internal_identity() - .map_err(InternalReceiverError::SecretKeyError)? - .unwrap() - .into_internal_receiver(commitment_scheme, asset, rng) - .map_err(InternalReceiverError::EncryptionError) + .map_err(InternalIdentityError::SecretKeyError)? + .map_ok(move |identity| { + identity + .into_internal(commitment_scheme, asset, rng) + .map_err(InternalIdentityError::EncryptionError) + }) } - /// Builds the next accumulated receiver. + /// Builds the next transfer accumulator. #[inline] - pub fn next_accumulated_receiver<C, R, const RECEIVERS: usize>( + pub fn next_accumulator<C, R, const RECEIVERS: usize>( &mut self, commitment_scheme: &C::CommitmentScheme, asset_id: AssetId, sender_sum: AssetBalance, rng: &mut R, - ) -> Result< - ( - [Receiver<C>; RECEIVERS], - PreSender<C>, - Vec<InternalKeyOwned<D, PreSender<C>>>, - ), - InternalReceiverError<D, C>, - > + ) -> Result<TransferAccumulator<D, C, RECEIVERS>, InternalIdentityError<D, C>> where C: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, { - let (mut receivers, zero_pre_senders) = - self.next_zeroes::<_, _, RECEIVERS>(commitment_scheme, asset_id, rng)?; - let internal_receiver = - self.next_accumulator(commitment_scheme, asset_id.with(sender_sum), rng)?; - receivers.push(internal_receiver.receiver); - Ok(( + let mut receivers = Vec::with_capacity(RECEIVERS); + let mut zero_pre_senders = Vec::with_capacity(RECEIVERS - 1); + + for _ in 0..RECEIVERS - 1 { + let (internal, index) = self + .next_internal(commitment_scheme, Asset::zero(asset_id), rng)? + .into(); + receivers.push(internal.receiver); + zero_pre_senders.push(KeyOwned::new(internal.pre_sender, index)); + } + + let internal = self + .next_internal(commitment_scheme, asset_id.with(sender_sum), rng)? + .unwrap(); + + receivers.push(internal.receiver); + + Ok(TransferAccumulator::new( into_array_unchecked(receivers), - internal_receiver.pre_sender, + internal.pre_sender, zero_pre_senders, )) } @@ -488,7 +449,7 @@ where commitment_scheme: &C::CommitmentScheme, asset: Asset, rng: &mut R, - ) -> Result<InternalKeyOwned<D, Receiver<C>>, InternalReceiverError<D, C>> + ) -> Result<InternalKeyOwned<D, Receiver<C>>, InternalIdentityError<D, C>> where C: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, @@ -496,15 +457,9 @@ where // TODO: Simplify this so that `into_shielded` and `into_receiver` can be replaced by a // one-step `into_receiver` call on `Identity`. self.next_internal_identity() - .map_err(InternalReceiverError::SecretKeyError)? - .map_ok(move |identity| { - identity.into_shielded(commitment_scheme).into_receiver( - commitment_scheme, - asset, - rng, - ) - }) - .map_err(InternalReceiverError::EncryptionError) + .map_err(InternalIdentityError::SecretKeyError)? + .map_ok(move |identity| identity.into_receiver(commitment_scheme, asset, rng)) + .map_err(InternalIdentityError::EncryptionError) } /// Builds a [`Mint`] transaction to mint `asset` and returns the index for that asset. @@ -514,20 +469,17 @@ where commitment_scheme: &C::CommitmentScheme, asset: Asset, rng: &mut R, - ) -> Result<(Mint<C>, InternalIndex<D>), InternalReceiverError<D, C>> + ) -> Result<InternalKeyOwned<D, Mint<C>>, InternalIdentityError<D, C>> where C: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, { - let (identity, index) = self - .next_internal_identity() - .map_err(InternalReceiverError::SecretKeyError)? - .into(); - Ok(( - Mint::from_identity(identity, commitment_scheme, asset, rng) - .map_err(InternalReceiverError::EncryptionError)?, - index, - )) + self.next_internal_identity() + .map_err(InternalIdentityError::SecretKeyError)? + .map_ok(|identity| { + Mint::from_identity(identity, commitment_scheme, asset, rng) + .map_err(InternalIdentityError::EncryptionError) + }) } /// Builds a [`Mint`] transaction to mint a zero asset with the given `asset_id`, returning a @@ -538,20 +490,40 @@ where commitment_scheme: &C::CommitmentScheme, asset_id: AssetId, rng: &mut R, - ) -> Result<(Mint<C>, PreSender<C>), InternalReceiverError<D, C>> + ) -> Result<(Mint<C>, PreSender<C>), InternalIdentityError<D, C>> where C: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, { Mint::zero( self.next_internal_identity() - .map_err(InternalReceiverError::SecretKeyError)? + .map_err(InternalIdentityError::SecretKeyError)? .unwrap(), commitment_scheme, asset_id, rng, ) - .map_err(InternalReceiverError::EncryptionError) + .map_err(InternalIdentityError::EncryptionError) + } + + /// Tries to decrypt `encrypted_asset` using the `secret_key`. + #[inline] + fn try_open_asset<C>( + secret_key: Result<ExternalSecretKey<D>, D::Error>, + encrypted_asset: &EncryptedAsset<C>, + ) -> Option<ExternalKeyOwned<D, Asset>> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + { + let KeyOwned { inner, index } = secret_key.ok()?; + Some( + index.wrap( + Identity::<C>::new(inner) + .try_open(encrypted_asset) + .ok()? + .into_asset(), + ), + ) } /// Looks for an index that can decrypt the given `encrypted_asset`. @@ -559,25 +531,17 @@ where pub fn find_external_asset<C>( &mut self, encrypted_asset: &EncryptedAsset<C>, - ) -> Option<(Index<D>, Asset)> + ) -> Option<ExternalKeyOwned<D, Asset>> where C: transfer::Configuration<SecretKey = D::SecretKey>, { - // FIXME: Simplify this implementation. - let open_spend = self + let asset = self .account .external_keys(&self.secret_key_source) - .find_map(move |ek| { - ek.map(move |ek| { - ek.map(move |k| Identity::<C>::new(k).try_open(encrypted_asset)) - .ok() - }) - .ok() - .flatten() - })?; + .find_map(move |k| Self::try_open_asset::<C>(k, encrypted_asset))?; self.account - .conditional_increment_external_range(&open_spend.index.index); - Some((open_spend.index.reduce(), open_spend.value.into_asset())) + .conditional_increment_external_range(&asset.index.index); + Some(asset) } } @@ -632,7 +596,7 @@ where insert: Option<(InternalIndex<D>, Asset)>, /// Pending Insert Zeroes Data - insert_zeroes: Option<(AssetId, Vec<Index<D>>)>, + insert_zeroes: Option<(AssetId, Vec<InternalIndex<D>>)>, /// Pending Remove Data remove: Vec<Index<D>>, @@ -652,7 +616,7 @@ where assets.insert(key.reduce(), asset); } if let Some((asset_id, zeroes)) = self.insert_zeroes.take() { - assets.insert_zeroes(asset_id, zeroes); + assets.insert_zeroes(asset_id, zeroes.into_iter().map(Index::reduce)); } assets.remove_all(mem::take(&mut self.remove)) } @@ -757,18 +721,15 @@ where { let mut assets = Vec::new(); for (utxo, encrypted_asset) in updates { - // TODO: Add optimization path where we have "strong" and "weak" insertions into the - // `utxo_set`. If the `utxo` is accompanied by an `encrypted_asset` then we - // "strong insert", if not we "weak insert". - // - if let Some((key, asset)) = self.signer.find_external_asset::<C>(&encrypted_asset) { - assets.push(asset); - self.assets.insert(key, asset); + if let Some(KeyOwned { inner, index }) = + self.signer.find_external_asset::<C>(&encrypted_asset) + { + assets.push(inner); + self.assets.insert(index.reduce(), inner); + self.utxo_set.insert(&utxo); + } else { + self.utxo_set.insert_non_proving(&utxo); } - - // FIXME: Should this ever error? We should check the capacity at `updates`, then - // insert should always work here. - let _ = self.utxo_set.insert(&utxo); } Ok(SyncResponse::new(assets)) } @@ -784,6 +745,7 @@ where where I: IntoIterator<Item = (Utxo<C>, EncryptedAsset<C>)>, { + // TODO: Capacity check. self.start_sync(sync_state); match self.utxo_set.len().checked_sub(starting_index) { Some(diff) => self.sync_inner(updates.into_iter().skip(diff)), @@ -801,21 +763,18 @@ where /// Selects the pre-senders which collectively own at least `asset`, returning any change. #[inline] - fn select(&mut self, asset: Asset) -> Result<(AssetBalance, Vec<PreSender<C>>), Error<D, C>> { + fn select(&mut self, asset: Asset) -> Result<Selection<C>, Error<D, C>> { let selection = self.assets.select(asset); if selection.is_empty() { return Err(Error::InsufficientBalance(asset)); } - self.pending_assets.remove = selection.keys().cloned().collect(); - let pre_senders = selection .balances .into_iter() .map(move |(k, v)| self.get_pre_sender(k, asset.id.with(v))) .collect::<Result<_, _>>()?; - - Ok((selection.change, pre_senders)) + Ok(Selection::new(selection.change, pre_senders)) } /// Builds a [`TransferPost`] for the given `transfer`. @@ -846,14 +805,8 @@ where &mut self, asset_id: AssetId, mut pre_senders: Vec<PreSender<C>>, - ) -> Result< - ( - Vec<InternalKeyOwned<D, PreSender<C>>>, - Vec<PreSender<C>>, - Vec<TransferPost<C>>, - ), - Error<D, C>, - > { + posts: &mut Vec<TransferPost<C>>, + ) -> Result<[Sender<C>; SENDERS], Error<D, C>> { assert!( (SENDERS > 1) && (RECEIVERS > 1), "The transfer shape must include at least two senders and two receivers." @@ -864,7 +817,6 @@ where ); let mut new_zeroes = Vec::new(); - let mut posts = Vec::new(); while pre_senders.len() > SENDERS { let mut accumulators = Vec::new(); @@ -875,18 +827,17 @@ where .ok_or(Error::MissingUtxoMembershipProof) })?; - let (receivers, accumulator, mut zeroes) = - self.signer.next_accumulated_receiver::<_, _, RECEIVERS>( - &self.commitment_scheme, - asset_id, - senders.iter().map(Sender::asset_value).sum(), - &mut self.rng, - )?; + let mut accumulator = self.signer.next_accumulator::<_, _, RECEIVERS>( + &self.commitment_scheme, + asset_id, + senders.iter().map(Sender::asset_value).sum(), + &mut self.rng, + )?; - posts.push(self.build_post(SecretTransfer::new(senders, receivers))?); + posts.push(self.build_post(SecretTransfer::new(senders, accumulator.receivers))?); - new_zeroes.append(&mut zeroes); - accumulators.push(accumulator); + new_zeroes.append(&mut accumulator.zeroes); + accumulators.push(accumulator.pre_sender); } for pre_sender in accumulators.iter() { @@ -897,7 +848,15 @@ where pre_senders = accumulators; } - Ok((new_zeroes, pre_senders, posts)) + self.prepare_final_pre_senders::<SENDERS>(asset_id, new_zeroes, &mut pre_senders, posts)?; + + Ok(into_array_unchecked( + pre_senders + .into_iter() + .map(move |ps| ps.try_upgrade(&self.utxo_set)) + .collect::<Option<Vec<_>>>() + .ok_or(Error::MissingUtxoMembershipProof)?, + )) } /// Prepare final pre-senders for the transaction. @@ -936,10 +895,7 @@ where self.pending_assets.insert_zeroes = Some(( asset_id, - new_zeroes - .into_iter() - .map(move |z| z.index.reduce()) - .collect(), + new_zeroes.into_iter().map(move |z| z.index).collect(), )); if needed_mints == 0 { @@ -992,32 +948,27 @@ where asset: Asset, receiver: Option<ShieldedIdentity<C>>, ) -> SignResult<D, C, Self> { - let (change, pre_senders) = self.select(asset)?; + let selection = self.select(asset)?; - let (new_zeroes, mut pre_senders, mut posts) = - self.accumulate_transfers::<2, 2>(asset.id, pre_senders)?; + let mut posts = Vec::new(); - self.prepare_final_pre_senders::<2>(asset.id, new_zeroes, &mut pre_senders, &mut posts)?; + const SENDERS: usize = PrivateTransferShape::SENDERS; + const RECEIVERS: usize = PrivateTransferShape::RECEIVERS; - let sender_side = into_array_unchecked( - pre_senders - .into_iter() - .map(|ps| ps.try_upgrade(&self.utxo_set)) - .collect::<Option<Vec<_>>>() - .ok_or(Error::MissingUtxoMembershipProof)?, - ); + let senders = self.accumulate_transfers::<SENDERS, RECEIVERS>( + asset.id, + selection.pre_senders, + &mut posts, + )?; - let change_receiver = self.next_change(asset.id, change)?; + let change = self.next_change(asset.id, selection.change)?; let final_post = match receiver { Some(receiver) => { let receiver = self.prepare_receiver(asset, receiver)?; - self.build_post(PrivateTransfer::build( - sender_side, - [change_receiver, receiver], - ))? + self.build_post(PrivateTransfer::build(senders, [change, receiver]))? } - _ => self.build_post(Reclaim::build(sender_side, change_receiver, asset))?, + _ => self.build_post(Reclaim::build(senders, change, asset))?, }; posts.push(final_post); @@ -1045,9 +996,10 @@ where self.commit(); match transaction { Transaction::Mint(asset) => { - let (mint, owner) = - self.signer - .mint(&self.commitment_scheme, asset, &mut self.rng)?; + let (mint, owner) = self + .signer + .mint(&self.commitment_scheme, asset, &mut self.rng)? + .into(); let mint_post = self.build_post(mint)?; self.pending_assets.insert = Some((owner, asset)); Ok(SignResponse::new(vec![mint_post])) @@ -1153,9 +1105,9 @@ where } } -/// Internal Receiver Error +/// Internal Identity Error /// -/// This `enum` is the error state for any construction of an internal receiver from a derived +/// This `enum` is the error state for any construction of an [`InternalIdentity`] from a derived /// secret key generator. #[derive(derivative::Derivative)] #[derivative( @@ -1166,7 +1118,7 @@ where Hash(bound = "D::Error: Hash, IntegratedEncryptionSchemeError<C>: Hash"), PartialEq(bound = "D::Error: PartialEq, IntegratedEncryptionSchemeError<C>: PartialEq") )] -pub enum InternalReceiverError<D, C> +pub enum InternalIdentityError<D, C> where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, @@ -1177,3 +1129,65 @@ where /// Encryption Error EncryptionError(IntegratedEncryptionSchemeError<C>), } + +/// Transfer Accumulator +pub struct TransferAccumulator<D, C, const RECEIVERS: usize> +where + D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, +{ + /// Receivers + pub receivers: [Receiver<C>; RECEIVERS], + + /// Accumulated Balance Pre-Sender + pub pre_sender: PreSender<C>, + + /// Zero Balance Pre-Senders + pub zeroes: Vec<InternalKeyOwned<D, PreSender<C>>>, +} + +impl<D, C, const RECEIVERS: usize> TransferAccumulator<D, C, RECEIVERS> +where + D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, +{ + /// Builds a new [`TransferAccumulator`] from `receivers`, `pre_sender`, and `zeroes`. + #[inline] + pub fn new( + receivers: [Receiver<C>; RECEIVERS], + pre_sender: PreSender<C>, + zeroes: Vec<InternalKeyOwned<D, PreSender<C>>>, + ) -> Self { + Self { + receivers, + pre_sender, + zeroes, + } + } +} + +/// Pre-Sender Selection +struct Selection<C> +where + C: transfer::Configuration, +{ + /// Selection Change + pub change: AssetBalance, + + /// Selection Pre-Senders + pub pre_senders: Vec<PreSender<C>>, +} + +impl<C> Selection<C> +where + C: transfer::Configuration, +{ + /// Builds a new [`Selection`] from `change` and `pre_senders`. + #[inline] + pub fn new(change: AssetBalance, pre_senders: Vec<PreSender<C>>) -> Self { + Self { + change, + pre_senders, + } + } +} diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index b442fe800..b890b46d6 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -371,3 +371,16 @@ where Self::SignerError(err) } } + +impl<D, C, L, S> From<signer::InternalIdentityError<D, C>> for Error<D, C, L, S> +where + D: DerivedSecretKeyGenerator, + C: Configuration<SecretKey = D::SecretKey>, + L: ledger::Connection<C>, + S: signer::Connection<D, C>, +{ + #[inline] + fn from(err: signer::InternalIdentityError<D, C>) -> Self { + Self::SignerError(err.into()) + } +} diff --git a/manta-crypto/src/merkle_tree/mod.rs b/manta-crypto/src/merkle_tree/mod.rs index 103a2b77e..c9b6e4e6b 100644 --- a/manta-crypto/src/merkle_tree/mod.rs +++ b/manta-crypto/src/merkle_tree/mod.rs @@ -21,8 +21,6 @@ // TODO: Implement [`crate::VerifiedSet`] for [`MerkleTree`]? // TODO: Maybe we should require `INNER_HEIGHT` instead of `HEIGHT` so that we don't have to rely // on the user to check that `HEIGHT >= 2`. -// TODO: Look into optimizations related to default values. Ex: computing the default values once -// and caching them in the tree storage? // FIXME: Get rid of as many `pub(super)` declarations as we can. mod node; diff --git a/manta-crypto/src/set.rs b/manta-crypto/src/set.rs index 1990190dd..e7da71708 100644 --- a/manta-crypto/src/set.rs +++ b/manta-crypto/src/set.rs @@ -16,6 +16,8 @@ //! Verified Sets +// TODO: Should `insert` or `insert_non_proving` be the default? + /// Verified Set Trait pub trait VerifiedSet { /// Item Type @@ -45,9 +47,21 @@ pub trait VerifiedSet { self.len() == 0 } - /// Inserts `item` into `self`. + /// Inserts `item` into `self` with the guarantee that `self` can later return a valid proof of + /// membership for `item` with a call to [`get_membership_proof`](Self::get_membership_proof). fn insert(&mut self, item: &Self::Item) -> bool; + /// Inserts `item` into `self` without the guarantee that `self` with be able to return a proof + /// of membership for `item`. + /// + /// # Implementation Note + /// + /// By default, this method uses [`insert`](Self::insert) to store `item`. + #[inline] + fn insert_non_proving(&mut self, item: &Self::Item) -> bool { + self.insert(item) + } + /// Returns `true` if `public` is a valid input for the current state of `self`. fn verify(&self, public: &Self::Public) -> bool; From f773df60c665bbabcc558d340d13b413f408e335 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 12 Oct 2021 16:58:48 -0400 Subject: [PATCH 093/275] WIP: interface between MerkleTree and VerifiedSet --- manta-accounting/src/identity.rs | 10 +- manta-accounting/src/wallet/signer.rs | 2 +- manta-crypto/src/merkle_tree/full.rs | 8 +- manta-crypto/src/merkle_tree/mod.rs | 1 + manta-crypto/src/merkle_tree/sharded.rs | 78 +++++++++++++ manta-crypto/src/merkle_tree/test.rs | 6 +- manta-crypto/src/merkle_tree/tree.rs | 143 ++++++++++++++++++++++-- manta-crypto/src/set.rs | 125 ++++++++++++--------- manta-pay/src/accounting/ledger.rs | 84 +++++--------- 9 files changed, 321 insertions(+), 136 deletions(-) create mode 100644 manta-crypto/src/merkle_tree/sharded.rs diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 69630d5c9..3b64e6745 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -788,7 +788,7 @@ where S: VerifiedSet<Item = Utxo<C>>, { /// UTXO Membership Proof - utxo_membership_proof: MembershipProof<S>, + utxo_membership_proof: MembershipProof<S::Verifier>, /// Type Parameter Marker __: PhantomData<C>, @@ -802,7 +802,7 @@ where /// Returns `true` if a [`PreSender`] could be upgraded using `self` given the `utxo_set`. #[inline] pub fn can_upgrade(&self, utxo_set: &S) -> bool { - self.utxo_membership_proof.verify_public(utxo_set) + self.utxo_membership_proof.check_public(utxo_set) } /// Upgrades the `pre_sender` to a [`Sender`] by attaching `self` to it. @@ -944,7 +944,7 @@ where utxo: Utxo<C>, /// UTXO Membership Proof - utxo_membership_proof: MembershipProof<S>, + utxo_membership_proof: MembershipProof<S::Verifier>, } impl<C, S> Sender<C, S> @@ -1465,7 +1465,7 @@ pub mod constraint { /// UTXO Membership Proof Variable Type pub type UtxoMembershipProofVar<C, S> = - MembershipProofVar<S, <C as Configuration>::ConstraintSystem>; + MembershipProofVar<<S as VerifiedSet>::Verifier, <C as Configuration>::ConstraintSystem>; /// Asset Parameters Variable pub struct AssetParametersVar<C> @@ -1706,7 +1706,7 @@ pub mod constraint { void_number: VoidNumberVar::<C>::new_unknown(cs, Public), void_number_commitment: VoidNumberCommitmentVar::<C>::new_unknown(cs, Public), utxo: UtxoVar::<C>::new_unknown(cs, Secret), - utxo_membership_proof: MembershipProof::<S>::unknown(cs, mode), + utxo_membership_proof: MembershipProof::<S::Verifier>::unknown(cs, mode), }, } } diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 24ba8f88f..944270fec 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -792,7 +792,7 @@ where .into() .into_post( &self.commitment_scheme, - &self.utxo_set.verifier(), + self.utxo_set.verifier(), &self.proving_context, &mut self.rng, ) diff --git a/manta-crypto/src/merkle_tree/full.rs b/manta-crypto/src/merkle_tree/full.rs index af277cb6d..82fc017e2 100644 --- a/manta-crypto/src/merkle_tree/full.rs +++ b/manta-crypto/src/merkle_tree/full.rs @@ -194,16 +194,14 @@ where LeafDigest<C>: Clone, InnerDigest<C>: Clone, { - type Error = (); - #[inline] - fn path(&self, parameters: &Parameters<C>, index: usize) -> Result<Path<C>, Self::Error> { + fn path(&self, parameters: &Parameters<C>, index: usize) -> Option<Path<C>> { let _ = parameters; if index > 0 && index >= self.len() { - return Err(()); + return None; } let leaf_index = Node(index); - Ok(Path::from_inner( + Some(Path::from_inner( self.get_owned_leaf_sibling(leaf_index), self.inner_digests.path(leaf_index), )) diff --git a/manta-crypto/src/merkle_tree/mod.rs b/manta-crypto/src/merkle_tree/mod.rs index c9b6e4e6b..26c8ad320 100644 --- a/manta-crypto/src/merkle_tree/mod.rs +++ b/manta-crypto/src/merkle_tree/mod.rs @@ -31,6 +31,7 @@ pub mod full; pub mod inner_tree; pub mod partial; pub mod path; +pub mod sharded; pub mod single_leaf; #[cfg(feature = "test")] diff --git a/manta-crypto/src/merkle_tree/sharded.rs b/manta-crypto/src/merkle_tree/sharded.rs new file mode 100644 index 000000000..e68f88142 --- /dev/null +++ b/manta-crypto/src/merkle_tree/sharded.rs @@ -0,0 +1,78 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Sharded Merkle Tree Abstractions + +use crate::merkle_tree::tree::{Configuration, Leaf, Parameters, Tree}; + +/// Sharding Configuration +pub trait Sharding<C> +where + C: Configuration + ?Sized, +{ + /// Sharded Tree Type + type Tree: ShardedTree<C>; + + /// Tree over Shard Roots Type + type RootTree: Tree<C>; + + /// Returns the shard index for the given `leaf`. + fn shard(leaf: &Leaf<C>) -> <Self::Tree as ShardedTree<C>>::Index; +} + +/// Sharded Merkle Tree +pub trait ShardedTree<C> +where + C: Configuration + ?Sized, +{ + /// Shard Index + type Index: Copy + Into<usize>; + + /// Builds a new sharded merkle tree from `parameters`. + fn new(parameters: &Parameters<C>) -> Self; +} + +/// Sharded Merkle Tree +pub struct ShardedMerkleTree<C, S> +where + C: Configuration + ?Sized, + S: Sharding<C>, +{ + /// Sharded Tree + tree: S::Tree, + + /// Tree over the Shard Roots + root_tree: S::RootTree, + + /// Merkle Tree Parameters + parameters: Parameters<C>, +} + +impl<C, S> ShardedMerkleTree<C, S> +where + C: Configuration + ?Sized, + S: Sharding<C>, +{ + /// Builds a new [`ShardedMerkleTree`] from `parameters`. + #[inline] + pub fn new(parameters: Parameters<C>) -> Self { + Self { + tree: S::Tree::new(&parameters), + root_tree: S::RootTree::new(&parameters), + parameters, + } + } +} diff --git a/manta-crypto/src/merkle_tree/test.rs b/manta-crypto/src/merkle_tree/test.rs index 398eba756..05ad2e332 100644 --- a/manta-crypto/src/merkle_tree/test.rs +++ b/manta-crypto/src/merkle_tree/test.rs @@ -16,10 +16,7 @@ //! Testing Framework -use crate::merkle_tree::{ - Configuration, GetPath, GetPathError, Leaf, MerkleTree, Parameters, Tree, -}; -use core::fmt::Debug; +use crate::merkle_tree::{Configuration, GetPath, Leaf, MerkleTree, Parameters, Tree}; /// Tests that a tree constructed with `parameters` can accept at least two leaves without /// failing. @@ -52,7 +49,6 @@ pub fn assert_valid_path<C, T>(tree: &MerkleTree<C, T>, index: usize, leaf: &Lea where C: Configuration + ?Sized, T: Tree<C> + GetPath<C>, - GetPathError<C, T>: Debug, { assert!( tree.path(index) diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index f95d587ce..75d49d542 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -23,9 +23,12 @@ // TODO: Should we add optimization paths for when `cloning` the default value is cheaper than // creating a new one from scratch (for inner digests)? -use crate::merkle_tree::{ - fork::{self, Trunk}, - path::{CurrentPath, Path}, +use crate::{ + merkle_tree::{ + fork::{self, Trunk}, + path::{CurrentPath, Path}, + }, + set::{MembershipProof, VerifiedSet, Verifier}, }; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; @@ -300,21 +303,33 @@ where } } +/// Merkle Tree Leaf Query Mixin +pub trait GetLeaf<C> +where + C: Configuration + ?Sized, +{ + /// Returns the [`LeafDigest`] at the given `index`. + fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>>; + + /// Returns the index of the `leaf_digest` if it is contained in `self`. + fn index_of(&self, leaf_digest: &LeafDigest<C>) -> Option<usize>; + + /// Returns `true` if `leaf_digest` is contained in the tree. + #[inline] + fn contains(&self, leaf_digest: &LeafDigest<C>) -> bool { + self.index_of(leaf_digest).is_some() + } +} + /// Merkle Tree Path Query Mixin pub trait GetPath<C> where C: Configuration + ?Sized, { - /// Path Query Error Type - type Error; - /// Returns the [`Path`] of the leaf at the given `index`. - fn path(&self, parameters: &Parameters<C>, index: usize) -> Result<Path<C>, Self::Error>; + fn path(&self, parameters: &Parameters<C>, index: usize) -> Option<Path<C>>; } -/// Tree Path Query Error Type -pub type GetPathError<C, T> = <T as GetPath<C>>::Error; - /// Digest Type #[derive(derivative::Derivative)] #[derivative( @@ -397,6 +412,22 @@ where } } +impl<C> Verifier for Parameters<C> +where + C: Configuration + ?Sized, +{ + type Item = Leaf<C>; + + type Public = Root<C>; + + type Secret = Path<C>; + + #[inline] + fn verify(&self, public: &Self::Public, secret: &Self::Secret, item: &Self::Item) -> bool { + self.verify_path(secret, public, item) + } +} + /// Merkle Tree Root Wrapper Type #[derive(derivative::Derivative)] #[derivative( @@ -532,9 +563,36 @@ where self.tree.current_path(&self.parameters) } + /// Returns the [`LeafDigest`] at the given `index`. + #[inline] + pub fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>> + where + T: GetLeaf<C>, + { + self.tree.leaf_digest(index) + } + + /// Returns the index of the `leaf_digest` if it is contained in `self`. + #[inline] + pub fn index_of(&self, leaf_digest: &LeafDigest<C>) -> Option<usize> + where + T: GetLeaf<C>, + { + self.tree.index_of(leaf_digest) + } + + /// Returns `true` if `leaf_digest` is contained in the tree. + #[inline] + pub fn contains(&self, leaf_digest: &LeafDigest<C>) -> bool + where + T: GetLeaf<C>, + { + self.tree.contains(leaf_digest) + } + /// Returns the [`Path`] of the leaf at the given `index`. #[inline] - pub fn path(&self, index: usize) -> Result<Path<C>, GetPathError<C, T>> + pub fn path(&self, index: usize) -> Option<Path<C>> where T: GetPath<C>, { @@ -608,3 +666,66 @@ where &mut self.tree } } + +impl<C, T> VerifiedSet for MerkleTree<C, T> +where + C: Configuration + ?Sized, + T: GetLeaf<C> + GetPath<C> + Tree<C>, +{ + type Item = Leaf<C>; + + type Public = Root<C>; + + type Secret = Path<C>; + + type Verifier = Parameters<C>; + + #[inline] + fn verifier(&self) -> &Self::Verifier { + &self.parameters + } + + #[inline] + fn capacity(&self) -> usize { + self.capacity() + } + + #[inline] + fn len(&self) -> usize { + self.len() + } + + #[inline] + fn is_empty(&self) -> bool { + self.is_empty() + } + + #[inline] + fn insert(&mut self, item: &Self::Item) -> bool { + self.push(item) + } + + #[inline] + fn insert_non_proving(&mut self, item: &Self::Item) -> bool { + // FIXME: What to do here? + todo!() + } + + #[inline] + fn check_public(&self, public: &Self::Public) -> bool { + &self.root() == public + } + + #[inline] + fn get_membership_proof(&self, item: &Self::Item) -> Option<MembershipProof<Self::Verifier>> { + Some(MembershipProof::new( + self.root(), + self.path(self.index_of(&self.parameters.digest(item))?)?, + )) + } + + #[inline] + fn contains(&self, item: &Self::Item) -> bool { + self.contains(&self.parameters.digest(item)) + } +} diff --git a/manta-crypto/src/set.rs b/manta-crypto/src/set.rs index e7da71708..d2e29cb7d 100644 --- a/manta-crypto/src/set.rs +++ b/manta-crypto/src/set.rs @@ -18,10 +18,26 @@ // TODO: Should `insert` or `insert_non_proving` be the default? -/// Verified Set Trait +/// Verified Set Verifier +pub trait Verifier { + /// Item Type + type Item: ?Sized; + + /// Public Part of the [`Item`](Self::Item) Membership Proof + type Public; + + /// Secret Part of the [`Item`](Self::Item) Membership Proof + type Secret; + + /// Verifies that `public` and `secret` form a proof to the fact that `item` is contained in + /// the verified set which returned `self`. + fn verify(&self, public: &Self::Public, secret: &Self::Secret, item: &Self::Item) -> bool; +} + +/// Verified Set pub trait VerifiedSet { /// Item Type - type Item; + type Item: ?Sized; /// Public Part of the [`Item`](Self::Item) Membership Proof type Public; @@ -32,8 +48,8 @@ pub trait VerifiedSet { /// [`MembershipProof`] Verifier Type type Verifier: Verifier<Item = Self::Item, Public = Self::Public, Secret = Self::Secret>; - /// Returns a new verifier for `self`. - fn verifier(&self) -> Self::Verifier; + /// Returns the internal verifier for `self`. + fn verifier(&self) -> &Self::Verifier; /// Returns the maximum number of elements that can be stored in `self`. fn capacity(&self) -> usize; @@ -63,10 +79,10 @@ pub trait VerifiedSet { } /// Returns `true` if `public` is a valid input for the current state of `self`. - fn verify(&self, public: &Self::Public) -> bool; + fn check_public(&self, public: &Self::Public) -> bool; /// Generates a proof that the given `item` is stored in `self`. - fn get_membership_proof(&self, item: &Self::Item) -> Option<MembershipProof<Self>>; + fn get_membership_proof(&self, item: &Self::Item) -> Option<MembershipProof<Self::Verifier>>; /// Returns `true` if `item` is stored in `self`. /// @@ -81,59 +97,62 @@ pub trait VerifiedSet { } } -/// Verified Set Verifier -pub trait Verifier { - /// Item Type - type Item; +impl<S> Verifier for S +where + S: VerifiedSet, +{ + type Item = S::Item; - /// Public Part of the [`Item`](Self::Item) Membership Proof - type Public; + type Public = S::Public; - /// Secret Part of the [`Item`](Self::Item) Membership Proof - type Secret; + type Secret = S::Secret; - /// Verifies that `public` and `secret` form a proof to the fact that `item` is contained in - /// the verified set which returned `self`. - fn verify(&self, public: &Self::Public, secret: &Self::Secret, item: &Self::Item) -> bool; + #[inline] + fn verify(&self, public: &Self::Public, secret: &Self::Secret, item: &Self::Item) -> bool { + self.check_public(public) && self.verifier().verify(public, secret, item) + } } -/// Membership Proof for a [`VerifiedSet`] -pub struct MembershipProof<S> +/// Membership Proof for a [`Verifier`] +pub struct MembershipProof<V> where - S: VerifiedSet + ?Sized, + V: Verifier + ?Sized, { /// Public Proof Part - public: S::Public, + public: V::Public, /// Secret Proof Part - secret: S::Secret, + secret: V::Secret, } -impl<S> MembershipProof<S> +impl<V> MembershipProof<V> where - S: VerifiedSet + ?Sized, + V: Verifier + ?Sized, { /// Builds a new [`MembershipProof`] from `public` and `secret`. #[inline] - pub fn new(public: S::Public, secret: S::Secret) -> Self { + pub fn new(public: V::Public, secret: V::Secret) -> Self { Self { public, secret } } - /// Returns [`S::Public`](VerifiedSet::Public) discarding the [`MembershipProof`]. + /// Returns [`V::Public`](Verifier::Public) discarding the [`MembershipProof`]. #[inline] - pub fn into_public(self) -> S::Public { + pub fn into_public(self) -> V::Public { self.public } /// Returns `true` if the public part of `self` is a valid input for the current state of `set`. #[inline] - pub fn verify_public(&self, set: &S) -> bool { - set.verify(&self.public) + pub fn check_public<S>(&self, set: &S) -> bool + where + S: VerifiedSet<Item = V::Item, Public = V::Public, Secret = V::Secret, Verifier = V>, + { + set.check_public(&self.public) } /// Verifies that the `item` is contained in some [`VerifiedSet`]. #[inline] - pub fn verify(&self, verifier: &S::Verifier, item: &S::Item) -> bool { + pub fn verify(&self, verifier: &V, item: &V::Item) -> bool { verifier.verify(&self.public, &self.secret, item) } } @@ -195,49 +214,49 @@ pub mod constraint { } /// Membership Proof Variable - pub struct MembershipProofVar<S, C> + pub struct MembershipProofVar<V, C> where - S: VerifiedSet + ?Sized, - C: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, + V: Verifier + ?Sized, + C: HasVariable<V::Public> + HasVariable<V::Secret> + ?Sized, { /// Public Proof Part - public: Var<S::Public, C>, + public: Var<V::Public, C>, /// Secret Proof Part - secret: Var<S::Secret, C>, + secret: Var<V::Secret, C>, } - impl<S, C> MembershipProofVar<S, C> + impl<V, C> MembershipProofVar<V, C> where - S: VerifiedSet + ?Sized, - C: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, + V: Verifier + ?Sized, + C: HasVariable<V::Public> + HasVariable<V::Secret> + ?Sized, { /// Builds a new [`MembershipProofVar`] from `public` and `secret`. #[inline] - pub fn new(public: Var<S::Public, C>, secret: Var<S::Secret, C>) -> Self { + pub fn new(public: Var<V::Public, C>, secret: Var<V::Secret, C>) -> Self { Self { public, secret } } /// Asserts that `self` is a valid proof to the fact that `item` is stored in the /// verified set. #[inline] - pub fn assert_validity<V>(&self, verifier: &V, item: &V::ItemVar, cs: &mut C) + pub fn assert_validity<VV>(&self, verifier: &VV, item: &VV::ItemVar, cs: &mut C) where C: ConstraintSystem, - V: VerifierVariable<C, Type = S::Verifier>, + VV: VerifierVariable<C, Type = V>, { verifier.assert_valid_membership_proof(&self.public, &self.secret, item, cs) } } - impl<S, C> Variable<C> for MembershipProofVar<S, C> + impl<V, C> Variable<C> for MembershipProofVar<V, C> where - S: VerifiedSet + ?Sized, - C: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, + V: Verifier + ?Sized, + C: HasVariable<V::Public> + HasVariable<V::Secret> + ?Sized, { - type Type = MembershipProof<S>; + type Type = MembershipProof<V>; - type Mode = MembershipProofMode<Mode<S::Public, C>, Mode<S::Secret, C>>; + type Mode = MembershipProofMode<Mode<V::Public, C>, Mode<V::Secret, C>>; #[inline] fn new(cs: &mut C, allocation: Allocation<Self::Type, Self::Mode>) -> Self { @@ -247,20 +266,20 @@ pub mod constraint { cs.allocate_known(&this.secret, mode.secret), ), Allocation::Unknown(mode) => Self::new( - unknown::<S::Public, _>(cs, mode.public), - unknown::<S::Secret, _>(cs, mode.secret), + unknown::<V::Public, _>(cs, mode.public), + unknown::<V::Secret, _>(cs, mode.secret), ), } } } - impl<S, C> HasAllocation<C> for MembershipProof<S> + impl<V, C> HasAllocation<C> for MembershipProof<V> where - S: VerifiedSet + ?Sized, - C: HasVariable<S::Public> + HasVariable<S::Secret> + ?Sized, + V: Verifier + ?Sized, + C: HasVariable<V::Public> + HasVariable<V::Secret> + ?Sized, { - type Variable = MembershipProofVar<S, C>; - type Mode = MembershipProofMode<Mode<S::Public, C>, Mode<S::Secret, C>>; + type Variable = MembershipProofVar<V, C>; + type Mode = MembershipProofMode<Mode<V::Public, C>, Mode<V::Secret, C>>; } /// Public Proof Part for [`VerifierVariable`] diff --git a/manta-pay/src/accounting/ledger.rs b/manta-pay/src/accounting/ledger.rs index 2f803d78d..e438d58e8 100644 --- a/manta-pay/src/accounting/ledger.rs +++ b/manta-pay/src/accounting/ledger.rs @@ -20,11 +20,8 @@ // represents the "native" ledger rather than the blockchain ledger. use crate::{ - accounting::config::{Configuration, ConstraintSystem, ProofSystem}, - crypto::{ - ies::EncryptedAsset, - merkle_tree::{constraint as merkle_tree_constraint, ConfigConverter}, - }, + accounting::config::{Configuration, ConstraintSystem}, + crypto::merkle_tree::{constraint as merkle_tree_constraint, ConfigConverter}, }; use alloc::{collections::BTreeSet, vec, vec::Vec}; use blake2::{ @@ -33,15 +30,12 @@ use blake2::{ }; use manta_accounting::identity; use manta_crypto::{ - constraint::{self, reflection::HasAllocation, Allocation, Constant, Variable}, + constraint::{reflection::HasAllocation, Allocation, Constant, Variable}, merkle_tree::{self, single_leaf::SingleLeaf, Tree}, set::{constraint::VerifierVariable, MembershipProof, VerifiedSet, Verifier}, }; use manta_util::{as_bytes, concatenate, into_array_unchecked}; -/// Void Number -type VoidNumber = identity::VoidNumber<Configuration>; - /// Unspent Transaction Output type Utxo = identity::Utxo<Configuration>; @@ -76,6 +70,23 @@ pub struct UtxoShard { utxos: SingleLeaf<ConfigConverter<Configuration>>, } +/// UTXO Set Verifier +#[derive(Clone)] +pub struct UtxoSetVerifier(Parameters); + +impl Verifier for UtxoSetVerifier { + type Item = Utxo; + + type Public = Root; + + type Secret = Path; + + #[inline] + fn verify(&self, public: &Self::Public, secret: &Self::Secret, item: &Self::Item) -> bool { + self.0.verify(public, secret, &as_bytes!(item)) + } +} + /// UTXO Set #[derive(Clone)] pub struct UtxoSet { @@ -86,7 +97,7 @@ pub struct UtxoSet { _utxos: BTreeSet<Utxo>, /// Merkle Tree Parameters - parameters: Parameters, + parameters: UtxoSetVerifier, } impl UtxoSet { @@ -98,7 +109,7 @@ impl UtxoSet { Self { shards: into_array_unchecked(vec![Default::default(); Self::SHARD_COUNT]), _utxos: Default::default(), - parameters, + parameters: UtxoSetVerifier(parameters), } } @@ -160,7 +171,7 @@ impl VerifiedSet for UtxoSet { } if !self.shards[Self::shard_index(item)] .utxos - .push(&self.parameters, &as_bytes!(item)) + .push(&self.parameters.0, &as_bytes!(item)) { return false; } @@ -169,19 +180,17 @@ impl VerifiedSet for UtxoSet { } #[inline] - fn verifier(&self) -> Self::Verifier { - UtxoSetVerifier { - parameters: self.parameters.clone(), - } + fn verifier(&self) -> &Self::Verifier { + &self.parameters } #[inline] - fn verify(&self, public: &Self::Public) -> bool { + fn check_public(&self, public: &Self::Public) -> bool { self.root_exists(public) } #[inline] - fn get_membership_proof(&self, item: &Self::Item) -> Option<MembershipProof<Self>> { + fn get_membership_proof(&self, item: &Self::Item) -> Option<MembershipProof<Self::Verifier>> { let _ = item; // TODO: Return a more informative error. @@ -206,28 +215,6 @@ impl VerifiedSet for UtxoSet { } } -/// UTXO Set Verifier -#[derive(Clone)] -pub struct UtxoSetVerifier { - /// Merkle Tree Parameters - parameters: Parameters, -} - -impl Verifier for UtxoSetVerifier { - type Item = Utxo; - - type Public = Root; - - type Secret = Path; - - #[inline] - fn verify(&self, public: &Self::Public, secret: &Self::Secret, item: &Self::Item) -> bool { - // FIXME: Leaf should be `Utxo` not `[u8]`. - self.parameters - .verify_path(secret, public, &as_bytes!(item)) - } -} - /// UTXO Set Verifier Variable #[derive(Clone)] pub struct UtxoSetVerifierVar(ParametersVar); @@ -240,7 +227,7 @@ impl Variable<ConstraintSystem> for UtxoSetVerifierVar { #[inline] fn new(ps: &mut ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { let (this, mode) = allocation.into_known(); - Self(this.parameters.known(ps, mode)) + Self(this.0.known(ps, mode)) } } @@ -264,18 +251,3 @@ impl VerifierVariable<ConstraintSystem> for UtxoSetVerifierVar { self.0.assert_verified(public, secret, &concatenate!(item)) } } - -/// Ledger -pub struct Ledger { - /// Void Numbers - _void_numbers: Vec<VoidNumber>, - - /// Unspent Transaction Outputs - _utxos: UtxoSet, - - /// Encrypted Assets - _encrypted_assets: Vec<EncryptedAsset>, - - /// Verifying Context - _verifying_context: <ProofSystem as constraint::ProofSystem>::VerifyingContext, -} From 404b33f7792999f662de64ccd3d3ea44f624fc4b Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 13 Oct 2021 03:39:04 -0400 Subject: [PATCH 094/275] WIP: improve merkle tree interfaces --- manta-accounting/src/identity.rs | 5 +- manta-accounting/src/wallet/ledger.rs | 4 +- manta-accounting/src/wallet/signer.rs | 22 +- manta-crypto/src/merkle_tree/full.rs | 47 +++- manta-crypto/src/merkle_tree/mod.rs | 6 +- manta-crypto/src/merkle_tree/partial.rs | 10 +- manta-crypto/src/merkle_tree/sharded.rs | 5 +- .../{single_leaf.rs => single_path.rs} | 20 +- manta-crypto/src/merkle_tree/test.rs | 6 +- manta-crypto/src/merkle_tree/tree.rs | 224 +++++++++++++----- manta-crypto/src/set.rs | 9 +- manta-pay/src/accounting/ledger.rs | 10 +- 12 files changed, 269 insertions(+), 99 deletions(-) rename manta-crypto/src/merkle_tree/{single_leaf.rs => single_path.rs} (91%) diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 3b64e6745..adf569aa4 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -859,13 +859,14 @@ where identity.into_pre_sender(commitment_scheme, asset) } - /// Inserts the [`Utxo`] corresponding to `self` into the `utxo_set`. + /// Inserts the [`Utxo`] corresponding to `self` into the `utxo_set` with the intention of + /// returning a proof later by a call to [`get_proof`](Self::get_proof). #[inline] pub fn insert_utxo<S>(&self, utxo_set: &mut S) -> bool where S: VerifiedSet<Item = Utxo<C>>, { - utxo_set.insert(&self.utxo) + utxo_set.insert_provable(&self.utxo) } /// Requests the membership proof of `self.utxo` from `utxo_set` so that we can turn `self` diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs index db0330e62..a3ad3bc93 100644 --- a/manta-accounting/src/wallet/ledger.rs +++ b/manta-accounting/src/wallet/ledger.rs @@ -16,8 +16,8 @@ //! Ledger Source -// TODO: Move to streams so we can process some of the data as it's incoming. -// TODO: Add non-atomic transactions? +// TODO: Move to streams so we can process some of the data as it is incoming. +// TODO: Add non-atomic transactions. See similar comment in `crate::wallet::signer`. use crate::{ identity::{Utxo, VoidNumber}, diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 944270fec..0117ec62b 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -16,7 +16,12 @@ //! Wallet Signer -// TODO: Use universal transfers instead of just the canonical ones. +// TODO: Add wallet recovery i.e. remove the assumption that a new signer represents a completely +// new derived secret key generator. +// TODO: Allow for non-atomic signing, i.e. rollback state to something in-between two calls to +// `sign`. Will have to upgrade `Rollback` and `manta_crypto::merkle_tree::fork` as well. +// TODO: Add checkpointing/garbage-collection in `utxo_set` so we can remove old UTXOs once they +// are irrelevant. Once we create a sender and its transaction succeeds we can drop the UTXO. use crate::{ asset::{Asset, AssetBalance, AssetId, AssetMap}, @@ -726,9 +731,9 @@ where { assets.push(inner); self.assets.insert(index.reduce(), inner); - self.utxo_set.insert(&utxo); + self.utxo_set.insert_provable(&utxo); } else { - self.utxo_set.insert_non_proving(&utxo); + self.utxo_set.insert(&utxo); } } Ok(SyncResponse::new(assets)) @@ -877,7 +882,9 @@ where needed_zeroes -= zeroes.len(); for zero in zeroes { - pre_senders.push(self.get_pre_sender(zero, Asset::zero(asset_id))?); + let next = self.get_pre_sender(zero, Asset::zero(asset_id))?; + next.insert_utxo(&mut self.utxo_set); + pre_senders.push(next); } if needed_zeroes == 0 { @@ -888,7 +895,11 @@ where for _ in 0..needed_zeroes { match new_zeroes.pop() { - Some(zero) => pre_senders.push(zero.unwrap()), + Some(zero) => { + let next = zero.unwrap(); + next.insert_utxo(&mut self.utxo_set); + pre_senders.push(next); + } _ => break, } } @@ -906,6 +917,7 @@ where let (mint, pre_sender) = self.signer .mint_zero(&self.commitment_scheme, asset_id, &mut self.rng)?; + pre_sender.insert_utxo(&mut self.utxo_set); pre_senders.push(pre_sender); posts.push(self.build_post(mint)?); } diff --git a/manta-crypto/src/merkle_tree/full.rs b/manta-crypto/src/merkle_tree/full.rs index 82fc017e2..0b3082989 100644 --- a/manta-crypto/src/merkle_tree/full.rs +++ b/manta-crypto/src/merkle_tree/full.rs @@ -21,8 +21,8 @@ use crate::merkle_tree::{ capacity, inner_tree::{BTreeMap, InnerMap, InnerTree}, - Configuration, CurrentPath, GetPath, InnerDigest, LeafDigest, MerkleTree, Node, Parameters, - Path, Root, Tree, + Configuration, CurrentPath, InnerDigest, LeafDigest, MerkleTree, Node, Parameters, Path, + PathError, Root, Tree, WithProofs, }; use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; @@ -163,6 +163,12 @@ where Root(self.root().clone()) } + #[inline] + fn matching_root(&self, parameters: &Parameters<C>, root: &Root<C>) -> bool { + let _ = parameters; + self.root() == &root.0 + } + #[inline] fn current_path(&self, parameters: &Parameters<C>) -> CurrentPath<C> { let _ = parameters; @@ -187,21 +193,44 @@ where } } -impl<C, M> GetPath<C> for Full<C, M> +impl<C, M> WithProofs<C> for Full<C, M> where C: Configuration + ?Sized, - M: InnerMap<C>, - LeafDigest<C>: Clone, + M: InnerMap<C> + Default, + LeafDigest<C>: Clone + PartialEq, InnerDigest<C>: Clone, { #[inline] - fn path(&self, parameters: &Parameters<C>, index: usize) -> Option<Path<C>> { + fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>> { + self.leaf_digests.get(index) + } + + #[inline] + fn index_of(&self, leaf_digest: &LeafDigest<C>) -> Option<usize> { + self.leaf_digests.iter().position(move |d| d == leaf_digest) + } + + #[inline] + fn maybe_push_provable_digest<F>( + &mut self, + parameters: &Parameters<C>, + leaf_digest: F, + ) -> Option<bool> + where + F: FnOnce() -> Option<LeafDigest<C>>, + { + self.maybe_push_digest(parameters, leaf_digest) + } + + #[inline] + fn path(&self, parameters: &Parameters<C>, index: usize) -> Result<Path<C>, PathError> { let _ = parameters; - if index > 0 && index >= self.len() { - return None; + let len = self.len(); + if index > 0 && index >= len { + return Err(PathError::IndexTooLarge(len)); } let leaf_index = Node(index); - Some(Path::from_inner( + Ok(Path::from_inner( self.get_owned_leaf_sibling(leaf_index), self.inner_digests.path(leaf_index), )) diff --git a/manta-crypto/src/merkle_tree/mod.rs b/manta-crypto/src/merkle_tree/mod.rs index 26c8ad320..59bf11393 100644 --- a/manta-crypto/src/merkle_tree/mod.rs +++ b/manta-crypto/src/merkle_tree/mod.rs @@ -18,7 +18,6 @@ // TODO: Should `Leaf` move into `Tree`/`Configuration` since we might want the tree to have // special kinds of leaf input (metadata along with just the digest)? -// TODO: Implement [`crate::VerifiedSet`] for [`MerkleTree`]? // TODO: Maybe we should require `INNER_HEIGHT` instead of `HEIGHT` so that we don't have to rely // on the user to check that `HEIGHT >= 2`. // FIXME: Get rid of as many `pub(super)` declarations as we can. @@ -31,8 +30,9 @@ pub mod full; pub mod inner_tree; pub mod partial; pub mod path; -pub mod sharded; -pub mod single_leaf; +pub mod single_path; + +// TODO: pub mod sharded; #[cfg(feature = "test")] #[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] diff --git a/manta-crypto/src/merkle_tree/partial.rs b/manta-crypto/src/merkle_tree/partial.rs index a554a0a40..c69697c17 100644 --- a/manta-crypto/src/merkle_tree/partial.rs +++ b/manta-crypto/src/merkle_tree/partial.rs @@ -197,6 +197,12 @@ where Root(self.root().clone()) } + #[inline] + fn matching_root(&self, parameters: &Parameters<C>, root: &Root<C>) -> bool { + let _ = parameters; + self.root() == &root.0 + } + #[inline] fn current_path(&self, parameters: &Parameters<C>) -> CurrentPath<C> { let _ = parameters; @@ -221,9 +227,9 @@ where } } -/* TODO: Implement `GetPath` for `Partial` +/* TODO: Implement `WithProofs` for `Partial` -impl<C, M> GetPath<C> for Partial<C, M> +impl<C, M> WithProofs<C> for Partial<C, M> where C: Configuration + ?Sized, M: InnerMap<C>, diff --git a/manta-crypto/src/merkle_tree/sharded.rs b/manta-crypto/src/merkle_tree/sharded.rs index e68f88142..f4cf151fc 100644 --- a/manta-crypto/src/merkle_tree/sharded.rs +++ b/manta-crypto/src/merkle_tree/sharded.rs @@ -30,7 +30,7 @@ where type RootTree: Tree<C>; /// Returns the shard index for the given `leaf`. - fn shard(leaf: &Leaf<C>) -> <Self::Tree as ShardedTree<C>>::Index; + fn shard(leaf: &Leaf<C>) -> usize; } /// Sharded Merkle Tree @@ -38,9 +38,6 @@ pub trait ShardedTree<C> where C: Configuration + ?Sized, { - /// Shard Index - type Index: Copy + Into<usize>; - /// Builds a new sharded merkle tree from `parameters`. fn new(parameters: &Parameters<C>) -> Self; } diff --git a/manta-crypto/src/merkle_tree/single_leaf.rs b/manta-crypto/src/merkle_tree/single_path.rs similarity index 91% rename from manta-crypto/src/merkle_tree/single_leaf.rs rename to manta-crypto/src/merkle_tree/single_path.rs index 0bcbd2a71..532cb11fb 100644 --- a/manta-crypto/src/merkle_tree/single_leaf.rs +++ b/manta-crypto/src/merkle_tree/single_path.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Single Leaf Merkle Tree Storage +//! Single Path Merkle Tree Storage // TODO: Should we be storing the root? Can we have a version where we don't? @@ -37,10 +37,10 @@ pub enum Length { Full, } -/// Single Leaf Merkle Tree Type -pub type SingleLeafMerkleTree<C> = MerkleTree<C, SingleLeaf<C>>; +/// Single Path Merkle Tree Type +pub type SinglePathMerkleTree<C> = MerkleTree<C, SinglePath<C>>; -/// Single Leaf Merkle Tree Backing Structure +/// Single Path Merkle Tree Backing Structure #[derive(derivative::Derivative)] #[derivative( Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), @@ -50,7 +50,7 @@ pub type SingleLeafMerkleTree<C> = MerkleTree<C, SingleLeaf<C>>; Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") )] -pub struct SingleLeaf<C> +pub struct SinglePath<C> where C: Configuration + ?Sized, { @@ -64,7 +64,7 @@ where root: Root<C>, } -impl<C> SingleLeaf<C> +impl<C> SinglePath<C> where C: Configuration + ?Sized, { @@ -117,7 +117,7 @@ where } } -impl<C> Tree<C> for SingleLeaf<C> +impl<C> Tree<C> for SinglePath<C> where C: Configuration + ?Sized, LeafDigest<C>: Clone, @@ -150,6 +150,12 @@ where self.root.clone() } + #[inline] + fn matching_root(&self, parameters: &Parameters<C>, root: &Root<C>) -> bool { + let _ = parameters; + &self.root == root + } + #[inline] fn current_path(&self, parameters: &Parameters<C>) -> CurrentPath<C> { let _ = parameters; diff --git a/manta-crypto/src/merkle_tree/test.rs b/manta-crypto/src/merkle_tree/test.rs index 05ad2e332..993916114 100644 --- a/manta-crypto/src/merkle_tree/test.rs +++ b/manta-crypto/src/merkle_tree/test.rs @@ -16,7 +16,7 @@ //! Testing Framework -use crate::merkle_tree::{Configuration, GetPath, Leaf, MerkleTree, Parameters, Tree}; +use crate::merkle_tree::{Configuration, Leaf, MerkleTree, Parameters, Tree, WithProofs}; /// Tests that a tree constructed with `parameters` can accept at least two leaves without /// failing. @@ -42,13 +42,13 @@ where tree.into_parameters() } -/// Tests path construction by checking that the path generated by `query` on `tree` is a valid +/// Tests path construction by checking that the path at the given `index` on `tree` is a valid /// [`Path`](super::Path) for `leaf`. #[inline] pub fn assert_valid_path<C, T>(tree: &MerkleTree<C, T>, index: usize, leaf: &Leaf<C>) where C: Configuration + ?Sized, - T: Tree<C> + GetPath<C>, + T: Tree<C> + WithProofs<C>, { assert!( tree.path(index) diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index 75d49d542..6376ec782 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -212,6 +212,12 @@ where /// Returns the [`Root`] of the merkle tree. fn root(&self, parameters: &Parameters<C>) -> Root<C>; + /// Returns `true` if `root` matches the root of `self`. + #[inline] + fn matching_root(&self, parameters: &Parameters<C>, root: &Root<C>) -> bool { + &self.root(parameters) == root + } + /// Returns the [`CurrentPath`] of the current (i.e. right-most) leaf. fn current_path(&self, parameters: &Parameters<C>) -> CurrentPath<C>; @@ -303,31 +309,85 @@ where } } -/// Merkle Tree Leaf Query Mixin -pub trait GetLeaf<C> +/// Merkle Tree Provable Mixin +pub trait WithProofs<C> where C: Configuration + ?Sized, { - /// Returns the [`LeafDigest`] at the given `index`. + /// Returns the leaf digest at the given `index`. + /// + /// # Implementation Note + /// + /// This method is allowed to return `None` even if `index` is less than the current length of + /// the tree. See [`index_of`](Self::index_of) for more. fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>>; /// Returns the index of the `leaf_digest` if it is contained in `self`. + /// + /// # Implementation Note + /// + /// This method is allowed to return `None` even if `leaf_digest` was inserted with a call to + /// [`push_digest`](Tree::push_digest). This method need only return an index for leaves which + /// are inserted with a call to [`push_provable`](Self::push_provable). fn index_of(&self, leaf_digest: &LeafDigest<C>) -> Option<usize>; - /// Returns `true` if `leaf_digest` is contained in the tree. + /// Returns `true` if `leaf_digest` is provably stored in `self`. + /// + /// See the [`index_of`](Self::index_of) and [`push_provable`](Self::push_provable) methods + /// for more. #[inline] fn contains(&self, leaf_digest: &LeafDigest<C>) -> bool { self.index_of(leaf_digest).is_some() } + + /// Checks if a leaf can be inserted into the tree and if it can, it runs `leaf_digest` to + /// extract a leaf digest to insert, returning `None` if there was no leaf digest. If this + /// method is successful, the digest inserted will have an accompanying proof that can be + /// returned by a call to the [`path`](Self::path) method. + fn maybe_push_provable_digest<F>( + &mut self, + parameters: &Parameters<C>, + leaf_digest: F, + ) -> Option<bool> + where + F: FnOnce() -> Option<LeafDigest<C>>; + + /// Appends `leaf_digest` to the end of the tree, retaining its path for later use with a call + /// to the [`path`](Self::path) method. + #[inline] + fn push_provable_digest<F>(&mut self, parameters: &Parameters<C>, leaf_digest: F) -> bool + where + F: FnOnce() -> LeafDigest<C>, + { + self.maybe_push_provable_digest(parameters, move || Some(leaf_digest())) + .unwrap() + } + + /// Appends `leaf` to the end of the tree, retaining its path for later use with a call to the + /// [`path`](Self::path) method. + #[inline] + fn push_provable(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> bool { + self.push_provable_digest(parameters, move || parameters.digest(leaf)) + } + + /// Returns the path for the leaf stored at the given `index` if it exists. + fn path(&self, parameters: &Parameters<C>, index: usize) -> Result<Path<C>, PathError>; } -/// Merkle Tree Path Query Mixin -pub trait GetPath<C> -where - C: Configuration + ?Sized, -{ - /// Returns the [`Path`] of the leaf at the given `index`. - fn path(&self, parameters: &Parameters<C>, index: usize) -> Option<Path<C>>; +/// Path Error +/// +/// This `struct` is returned by the [`path`](WithProofs::path) method of the [`WithProofs`] trait. +/// See its documentation for more. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum PathError { + /// Path for the given index was not stored in the tree + MissingPath, + + /// Given index exceeded the length of the tree + IndexTooLarge( + /// Length of the tree + usize, + ), } /// Digest Type @@ -485,12 +545,16 @@ where T: Tree<C>, { /// Builds a new [`MerkleTree`]. + /// + /// See [`Tree::new`] for more. #[inline] pub fn new(parameters: Parameters<C>) -> Self { Self::from_tree(T::new(&parameters), parameters) } /// Builds a new [`MerkleTree`] with the given `leaves`. + /// + /// See [`Tree::from_iter`] for more. #[inline] pub fn from_iter<'l, L>(parameters: Parameters<C>, leaves: L) -> Option<Self> where @@ -504,6 +568,8 @@ where } /// Builds a new [`MerkleTree`] with the given `leaves`. + /// + /// See [`Tree::from_slice`] for more. #[inline] pub fn from_slice(parameters: Parameters<C>, leaves: &[Leaf<C>]) -> Option<Self> where @@ -527,104 +593,150 @@ where &self.parameters } + /// Returns the number of leaves that can fit in this merkle tree. + /// + /// See [`capacity`] for more. + #[inline] + pub fn capacity(&self) -> usize { + capacity::<C>() + } + /// Returns the length of this merkle tree. + /// + /// See [`Tree::len`] for more. #[inline] pub fn len(&self) -> usize { self.tree.len() } /// Returns `true` if this merkle tree is empty. + /// + /// See [`Tree::is_empty`] for more. #[inline] pub fn is_empty(&self) -> bool { self.tree.is_empty() } - /// Returns the number of leaves that can fit in this merkle tree. - #[inline] - pub fn capacity(&self) -> usize { - capacity::<C>() - } - /// Returns the current (i.e right-most) leaf. + /// + /// See [`Tree::current_leaf`] for more. #[inline] pub fn current_leaf(&self) -> LeafDigest<C> { self.tree.current_leaf() } /// Returns the [`Root`] of the merkle tree. + /// + /// See [`Tree::root`] for more. #[inline] pub fn root(&self) -> Root<C> { self.tree.root(&self.parameters) } + /// Returns `true` if `root` matches the root of `self`. + /// + /// See [`Tree::matching_root`] for more. + #[inline] + fn matching_root(&self, root: &Root<C>) -> bool { + self.tree.matching_root(&self.parameters, root) + } + /// Returns the [`CurrentPath`] of the current (i.e right-most) leaf. + /// + /// See [`Tree::current_path`] for more. #[inline] pub fn current_path(&self) -> CurrentPath<C> { self.tree.current_path(&self.parameters) } - /// Returns the [`LeafDigest`] at the given `index`. + /// Inserts `leaf` at the next avaiable leaf node of the tree, returning `false` if the + /// leaf could not be inserted because the tree has exhausted its capacity. + /// + /// See [`Tree::push`] for more. #[inline] - pub fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>> + pub fn push(&mut self, leaf: &Leaf<C>) -> bool { + self.tree.push(&self.parameters, leaf) + } + + /// Appends an iterator of leaves at the end of the tree, returning `false` if the `leaves` + /// could not be inserted because the tree has exhausted its capacity. + /// + /// See [`Tree::extend`] for more. + #[inline] + pub fn extend<'l, L>(&mut self, leaves: L) -> bool where - T: GetLeaf<C>, + Leaf<C>: 'l, + L: IntoIterator<Item = &'l Leaf<C>>, { - self.tree.leaf_digest(index) + self.tree.extend(&self.parameters, leaves) } - /// Returns the index of the `leaf_digest` if it is contained in `self`. + /// Appends a slice of leaves at the end of the tree, returning `false` if the `leaves` could + /// not be inserted because the tree has exhausted its capacity. + /// + /// See [`Tree::extend_slice`] for more. #[inline] - pub fn index_of(&self, leaf_digest: &LeafDigest<C>) -> Option<usize> + pub fn extend_slice(&mut self, leaves: &[Leaf<C>]) -> bool where - T: GetLeaf<C>, + Leaf<C>: Sized, { - self.tree.index_of(leaf_digest) + self.tree.extend_slice(&self.parameters, leaves) } - /// Returns `true` if `leaf_digest` is contained in the tree. + /// Returns the leaf digest at the given `index`. + /// + /// See [`WithProofs::leaf_digest`] for more. #[inline] - pub fn contains(&self, leaf_digest: &LeafDigest<C>) -> bool + pub fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>> where - T: GetLeaf<C>, + T: WithProofs<C>, { - self.tree.contains(leaf_digest) + self.tree.leaf_digest(index) } - /// Returns the [`Path`] of the leaf at the given `index`. + /// Returns the index of the `leaf_digest` if it is contained in `self`. + /// + /// See [`WithProofs::index_of`] for more. #[inline] - pub fn path(&self, index: usize) -> Option<Path<C>> + pub fn index_of(&self, leaf_digest: &LeafDigest<C>) -> Option<usize> where - T: GetPath<C>, + T: WithProofs<C>, { - self.tree.path(&self.parameters, index) + self.tree.index_of(leaf_digest) } - /// Inserts `leaf` at the next avaiable leaf node of the tree, returning `false` if the - /// leaf could not be inserted because the tree has exhausted its capacity. + /// Returns `true` if `leaf_digest` is provably stored in `self`. + /// + /// See [`WithProofs::contains`] for more. #[inline] - pub fn push(&mut self, leaf: &Leaf<C>) -> bool { - self.tree.push(&self.parameters, leaf) + pub fn contains(&self, leaf_digest: &LeafDigest<C>) -> bool + where + T: WithProofs<C>, + { + self.tree.contains(leaf_digest) } - /// Appends an iterator of leaves at the end of the tree, returning `false` if the `leaves` - /// could not be inserted because the tree has exhausted its capacity. + /// Appends `leaf` to the end of the tree, retaining its path for later use with a call to the + /// [`path`](Self::path) method. + /// + /// See [`WithProofs::push_provable`] for more. #[inline] - pub fn extend<'l, L>(&mut self, leaves: L) -> bool + pub fn push_provable(&mut self, leaf: &Leaf<C>) -> bool where - Leaf<C>: 'l, - L: IntoIterator<Item = &'l Leaf<C>>, + T: WithProofs<C>, { - self.tree.extend(&self.parameters, leaves) + self.tree.push_provable(&self.parameters, leaf) } - /// Appends a slice of leaves at the end of the tree, returning `false` if the `leaves` could - /// not be inserted because the tree has exhausted its capacity. + /// Returns the path for the leaf stored at the given `index` if it exists. + /// + /// See [`WithProofs::path`] for more. #[inline] - pub fn extend_slice(&mut self, leaves: &[Leaf<C>]) -> bool + pub fn path(&self, index: usize) -> Result<Path<C>, PathError> where - Leaf<C>: Sized, + T: WithProofs<C>, { - self.tree.extend_slice(&self.parameters, leaves) + self.tree.path(&self.parameters, index) } /// Converts `self` into a fork-able merkle tree. @@ -670,7 +782,7 @@ where impl<C, T> VerifiedSet for MerkleTree<C, T> where C: Configuration + ?Sized, - T: GetLeaf<C> + GetPath<C> + Tree<C>, + T: Tree<C> + WithProofs<C>, { type Item = Leaf<C>; @@ -701,26 +813,26 @@ where } #[inline] - fn insert(&mut self, item: &Self::Item) -> bool { - self.push(item) + fn insert_provable(&mut self, item: &Self::Item) -> bool { + self.push_provable(item) } #[inline] - fn insert_non_proving(&mut self, item: &Self::Item) -> bool { - // FIXME: What to do here? - todo!() + fn insert(&mut self, item: &Self::Item) -> bool { + self.push(item) } #[inline] fn check_public(&self, public: &Self::Public) -> bool { - &self.root() == public + self.matching_root(public) } #[inline] fn get_membership_proof(&self, item: &Self::Item) -> Option<MembershipProof<Self::Verifier>> { Some(MembershipProof::new( self.root(), - self.path(self.index_of(&self.parameters.digest(item))?)?, + self.path(self.index_of(&self.parameters.digest(item))?) + .ok()?, )) } diff --git a/manta-crypto/src/set.rs b/manta-crypto/src/set.rs index d2e29cb7d..9249f9fa0 100644 --- a/manta-crypto/src/set.rs +++ b/manta-crypto/src/set.rs @@ -65,17 +65,18 @@ pub trait VerifiedSet { /// Inserts `item` into `self` with the guarantee that `self` can later return a valid proof of /// membership for `item` with a call to [`get_membership_proof`](Self::get_membership_proof). - fn insert(&mut self, item: &Self::Item) -> bool; + fn insert_provable(&mut self, item: &Self::Item) -> bool; /// Inserts `item` into `self` without the guarantee that `self` with be able to return a proof /// of membership for `item`. /// /// # Implementation Note /// - /// By default, this method uses [`insert`](Self::insert) to store `item`. + /// By default, this method uses [`insert_provable`](Self::insert_provable) to store `item` + /// in `self`. #[inline] - fn insert_non_proving(&mut self, item: &Self::Item) -> bool { - self.insert(item) + fn insert(&mut self, item: &Self::Item) -> bool { + self.insert_provable(item) } /// Returns `true` if `public` is a valid input for the current state of `self`. diff --git a/manta-pay/src/accounting/ledger.rs b/manta-pay/src/accounting/ledger.rs index e438d58e8..3165ca290 100644 --- a/manta-pay/src/accounting/ledger.rs +++ b/manta-pay/src/accounting/ledger.rs @@ -31,7 +31,7 @@ use blake2::{ use manta_accounting::identity; use manta_crypto::{ constraint::{reflection::HasAllocation, Allocation, Constant, Variable}, - merkle_tree::{self, single_leaf::SingleLeaf, Tree}, + merkle_tree::{self, single_path::SinglePath, Tree}, set::{constraint::VerifierVariable, MembershipProof, VerifiedSet, Verifier}, }; use manta_util::{as_bytes, concatenate, into_array_unchecked}; @@ -67,7 +67,7 @@ pub struct UtxoShard { root: Root, /// Unspent Transaction Outputs - utxos: SingleLeaf<ConfigConverter<Configuration>>, + utxos: SinglePath<ConfigConverter<Configuration>>, } /// UTXO Set Verifier @@ -179,6 +179,12 @@ impl VerifiedSet for UtxoSet { true } + #[inline] + fn insert_provable(&mut self, item: &Self::Item) -> bool { + // FIXME: This is not implementable! + false + } + #[inline] fn verifier(&self) -> &Self::Verifier { &self.parameters From 54711b742782713c6414b7fef13f63650b6a4b43 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 13 Oct 2021 17:21:32 -0400 Subject: [PATCH 095/275] port test sampling from manta-api --- Cargo.toml | 1 + manta-accounting/src/asset.rs | 31 +- manta-accounting/src/fs.rs | 4 + manta-accounting/src/identity.rs | 60 +++- manta-accounting/src/transfer.rs | 306 ++++++++++++++++++-- manta-accounting/src/wallet/ledger.rs | 11 +- manta-accounting/src/wallet/mod.rs | 4 + manta-accounting/src/wallet/signer.rs | 36 +-- manta-accounting/src/wallet/state.rs | 9 +- manta-accounting/src/wallet/test.rs | 17 ++ manta-cli/Cargo.toml | 38 +++ manta-cli/LICENSE | 1 + manta-cli/README.md | 1 + manta-cli/src/lib.rs | 22 ++ manta-crypto/src/merkle_tree/fork.rs | 1 + manta-crypto/src/merkle_tree/tree.rs | 48 ++- manta-crypto/src/rand.rs | 98 +++---- manta-pay/src/crypto/commitment/pedersen.rs | 16 +- manta-pay/src/crypto/prf/blake2s.rs | 6 +- 19 files changed, 551 insertions(+), 159 deletions(-) create mode 100644 manta-accounting/src/wallet/test.rs create mode 100644 manta-cli/Cargo.toml create mode 120000 manta-cli/LICENSE create mode 100644 manta-cli/README.md create mode 100644 manta-cli/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index fdb51002f..b885a6d63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "manta", "manta-accounting", + "manta-cli", "manta-codec", "manta-crypto", "manta-pay", diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index bc9914652..e87e5f7ff 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -40,7 +40,7 @@ use manta_crypto::{ reflection::{unknown, HasAllocation, HasVariable, Var}, Allocation, PublicOrSecret, Secret, Variable, }, - rand::{Rand, RngCore, Sample, Standard}, + rand::{CryptoRng, Rand, RngCore, Sample, Standard}, }; use manta_util::{array_map, fallible_array_map, into_array_unchecked, Concat, ConcatAccumulator}; @@ -50,7 +50,7 @@ pub(super) mod prelude { } /// [`AssetId`] Base Type -type AssetIdType = u32; +pub type AssetIdType = u32; /// Asset Id Type #[derive(Clone, Copy, Debug, Default, Display, Eq, From, Hash, Ord, PartialEq, PartialOrd)] @@ -92,9 +92,9 @@ where AssetIdType: Sample<D>, { #[inline] - fn sample<R>(distribution: &D, rng: &mut R) -> Self + fn sample<R>(distribution: D, rng: &mut R) -> Self where - R: RngCore + ?Sized, + R: CryptoRng + RngCore + ?Sized, { Self(rng.sample(distribution)) } @@ -108,7 +108,7 @@ impl From<AssetId> for [u8; AssetId::SIZE] { } /// [`AssetBalance`] Base Type -type AssetBalanceType = u128; +pub type AssetBalanceType = u128; /// Asset Balance Type #[derive( @@ -195,9 +195,9 @@ where AssetBalanceType: Sample<D>, { #[inline] - fn sample<R>(distribution: &D, rng: &mut R) -> Self + fn sample<R>(distribution: D, rng: &mut R) -> Self where - R: RngCore + ?Sized, + R: CryptoRng + RngCore + ?Sized, { Self(rng.sample(distribution)) } @@ -232,19 +232,6 @@ impl<'a> Sum<&'a AssetBalance> for AssetBalance { /// [`AssetBalance`] Array Type pub type AssetBalances<const N: usize> = [AssetBalance; N]; -/// Samples asset balances from `rng`. -#[inline] -pub(crate) fn sample_asset_balances<R, const N: usize>(rng: &mut R) -> AssetBalances<N> -where - R: RngCore + ?Sized, -{ - let mut balances = Vec::with_capacity(N); - for _ in 0..N { - balances.push(rng.gen()); - } - into_array_unchecked(balances) -} - /// Change Iterator /// /// An iterator over [`AssetBalance`] change amounts. @@ -462,9 +449,9 @@ impl From<Asset> for (AssetId, AssetBalance) { impl Sample for Asset { #[inline] - fn sample<R>(distribution: &Standard, rng: &mut R) -> Self + fn sample<R>(distribution: Standard, rng: &mut R) -> Self where - R: RngCore + ?Sized, + R: CryptoRng + RngCore + ?Sized, { let _ = distribution; Self::new(rng.gen(), rng.gen()) diff --git a/manta-accounting/src/fs.rs b/manta-accounting/src/fs.rs index 70eda8368..671fc674a 100644 --- a/manta-accounting/src/fs.rs +++ b/manta-accounting/src/fs.rs @@ -16,6 +16,10 @@ //! Encrypted Filesystem Primitives +// FIXME: Change this to a "payload parsing" scheme, like serdes but ensure it gets encrypted +// before saving and decrypted after loading. So we need something like EncryptedSerialize +// and DecryptedDeserialize. + /// Filesystem Encrypted Loading pub trait Load: Sized { /// Path Type diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index adf569aa4..7a832b8a5 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -17,13 +17,16 @@ //! Identities, Senders, and Receivers // FIXME: Check the secret key APIs. +// FIXME: Remove `UtxoSet` dependence from `identity`, really we only need `UtxoSetVerifier`. +// TODO: Get rid of [`Spend`] and [`OpenSpend`] if possible. They don't seem to be that useful. +// See `crate::wallet::signer`. use crate::asset::{Asset, AssetBalance, AssetId, AssetVar}; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use manta_crypto::{ commitment::{CommitmentScheme, Input as CommitmentInput}, ies::{self, EncryptedMessage, IntegratedEncryptionScheme}, - rand::{CryptoRand, CryptoRng, CryptoSample, RngCore, SeedableRng, Standard}, + rand::{CryptoRng, Rand, RngCore, Sample, SeedableRng, Standard, TrySample}, set::{MembershipProof, VerifiedSet}, PseudorandomFunctionFamily, }; @@ -39,7 +42,7 @@ pub trait Configuration { type SecretKey: Clone; /// Pseudorandom Function Family Input Type - type PseudorandomFunctionFamilyInput: CryptoSample; + type PseudorandomFunctionFamilyInput: Sample; /// Pseudorandom Function Family Type type PseudorandomFunctionFamily: PseudorandomFunctionFamily< @@ -48,7 +51,7 @@ pub trait Configuration { >; /// Commitment Scheme Randomness Type - type CommitmentSchemeRandomness: CryptoSample; + type CommitmentSchemeRandomness: Sample; /// Commitment Scheme Type type CommitmentScheme: CommitmentScheme<Randomness = Self::CommitmentSchemeRandomness> @@ -227,12 +230,12 @@ where } } -impl<C> CryptoSample for AssetParameters<C> +impl<C> Sample for AssetParameters<C> where C: Configuration, { #[inline] - fn sample<R>(distribution: &Standard, rng: &mut R) -> Self + fn sample<R>(distribution: Standard, rng: &mut R) -> Self where R: CryptoRng + RngCore + ?Sized, { @@ -491,13 +494,13 @@ where } } -impl<C, D> CryptoSample<D> for Identity<C> +impl<C, D> Sample<D> for Identity<C> where C: Configuration, - C::SecretKey: CryptoSample<D>, + C::SecretKey: Sample<D>, { #[inline] - fn sample<R>(distribution: &D, rng: &mut R) -> Self + fn sample<R>(distribution: D, rng: &mut R) -> Self where R: CryptoRng + RngCore + ?Sized, { @@ -917,6 +920,20 @@ where } } +impl<C> Sample<&C::CommitmentScheme> for PreSender<C> +where + C: Configuration, + C::SecretKey: Sample, +{ + #[inline] + fn sample<R>(distribution: &C::CommitmentScheme, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + Identity::gen(rng).into_pre_sender(distribution, rng.gen()) + } +} + /// Sender pub struct Sender<C, S> where @@ -1224,6 +1241,16 @@ where self.asset.value } + /// Inserts the [`Utxo`] corresponding to `self` into the `utxo_set` with the intention of + /// returning a proof later by a call to [`get_proof`](PreSender::get_proof). + #[inline] + pub fn insert_utxo<S>(&self, utxo_set: &mut S) -> bool + where + S: VerifiedSet<Item = Utxo<C>>, + { + utxo_set.insert_provable(&self.utxo) + } + /// Extracts ledger posting data for this receiver. #[inline] pub fn into_post(self) -> ReceiverPost<C, I> { @@ -1234,6 +1261,23 @@ where } } +impl<C, I> TrySample<&C::CommitmentScheme> for Receiver<C, I> +where + C: Configuration, + C::SecretKey: Sample, + I: IntegratedEncryptionScheme<Plaintext = Asset>, +{ + type Error = I::Error; + + #[inline] + fn try_sample<R>(distribution: &C::CommitmentScheme, rng: &mut R) -> Result<Self, Self::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + Identity::gen(rng).into_receiver(distribution, rng.gen(), rng) + } +} + /// Receiver Ledger pub trait ReceiverLedger<C, I> where diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 0ae512e1b..6a4ef551f 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -23,9 +23,10 @@ // TODO: Have a compile-time way to check if proof generation is used for a certain shape, // so that the `generate_context`/`generate_proof` method can only exist on the right // shape implementations, instead of failing at runtime with `None`. +// FIXME: Remove `UtxoSet` dependence from `transfer`, really we only need `UtxoSetVerifier`. use crate::{ - asset::{sample_asset_balances, Asset, AssetBalance, AssetBalances, AssetId}, + asset::{Asset, AssetBalance, AssetBalances, AssetId}, identity::{ self, constraint::UtxoVar, ReceiverLedger, ReceiverPostError, SenderLedger, SenderPostError, Utxo, @@ -41,7 +42,7 @@ use manta_crypto::{ PublicOrSecret, Secret, Variable, VariableSource, }, ies::{EncryptedMessage, IntegratedEncryptionScheme}, - rand::{CryptoRng, Rand, RngCore, Sample, Standard}, + rand::{CryptoRng, RngCore}, set::{constraint::VerifierVariable, VerifiedSet, Verifier}, }; use manta_util::{create_seal, from_variant_impl, iter::mixed_chain, seal, Either}; @@ -444,21 +445,6 @@ impl Default for PublicTransfer<0, 0> { } } -impl<const SOURCES: usize, const SINKS: usize> Sample for PublicTransfer<SOURCES, SINKS> { - #[inline] - fn sample<R>(distribution: &Standard, rng: &mut R) -> Self - where - R: RngCore + ?Sized, - { - let _ = distribution; - Self::new( - rng.gen(), - sample_asset_balances(rng), - sample_asset_balances(rng), - ) - } -} - /// Secret Transfer Protocol pub struct SecretTransfer<C, const SENDERS: usize, const RECEIVERS: usize> where @@ -1300,18 +1286,18 @@ pub mod canonical { where C: Configuration, { - /// Builds a [`Reclaim`] from `senders`, `receiver`, and `reclaim`. + /// Builds a [`Reclaim`] from `senders`, `receivers`, and `reclaim`. #[inline] pub fn build( senders: [Sender<C>; ReclaimShape::SENDERS], - receiver: Receiver<C>, + receivers: [Receiver<C>; ReclaimShape::RECEIVERS], reclaim: Asset, ) -> Self { Self::new( reclaim.id, Default::default(), senders, - [receiver], + receivers, [reclaim.value], ) } @@ -1372,25 +1358,287 @@ pub mod canonical { } } -/* TODO: /// Testing Framework #[cfg(feature = "test")] #[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] pub mod test { use super::*; - use canonical::Mint; - use rand::Rng; + use crate::{asset::AssetBalanceType, identity::PreSender}; + use manta_crypto::rand::{Rand, Sample, Standard, TrySample}; + use manta_util::into_array_unchecked; + + /// Test Sampling Distributions + pub mod distribution { + use super::*; + + /// [`PublicTransfer`] Sampling Distribution + pub type PublicTransfer = Standard; + + /// Fixed Asset [`PublicTransfer`] Sampling Distribution + #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] + pub struct FixedPublicTransfer(pub Asset); + + /// [`SecretTransfer`] Sampling Distribution + pub type SecretTransfer<'c, C> = Transfer<'c, C>; + + /// Fixed Asset [`SecretTransfer`] Sampling Distribution + pub struct FixedSecretTransfer<'c, C> + where + C: Configuration, + { + /// Asset + pub asset: Asset, + + /// Base Distribution + pub base: SecretTransfer<'c, C>, + } + + /// [`Transfer`] Sampling Distribution + pub struct Transfer<'c, C> + where + C: Configuration, + { + /// Commitment Scheme + pub commitment_scheme: &'c C::CommitmentScheme, + + /// UTXO Set + pub utxo_set: &'c mut C::UtxoSet, + } + /// Fixed Asset [`Transfer`] Sampling Distribution + pub struct FixedTransfer<'c, C> + where + C: Configuration, + { + /// Asset Id + pub asset_id: AssetId, + + /// Public Value Exchanged + pub public_value: AssetBalance, + + /// Secret Value Exchanged + pub secret_value: AssetBalance, + + /// Base Distribution + pub base: Transfer<'c, C>, + } + } + + /// Samples a distribution over `count`-many values summing to `total`. + /// + /// # Warning /// + /// This is a naive algorithm and should only be used for testing purposes. #[inline] - pub fn sample_sender<C, R>(commitment_scheme: &C::CommitmentScheme, rng: &mut R) + pub fn value_distribution<R>( + count: usize, + total: AssetBalance, + rng: &mut R, + ) -> Vec<AssetBalance> where - C: Configuration, R: CryptoRng + RngCore + ?Sized, { - // TODO: let _ = Mint::from_identity(rng.gen(), commitment_scheme, rng.gen(), rng); - let _ = (commitment_scheme, rng); - todo!() + if count == 0 { + return Default::default(); + } + let mut result = Vec::with_capacity(count + 1); + result.push(AssetBalance(0)); + for _ in 1..count { + result.push(AssetBalance(AssetBalanceType::gen(rng) % total.0)); + } + result.push(total); + result.sort_unstable(); + for i in 0..count { + result[i] = result[i + 1] - result[i]; + } + result.pop().unwrap(); + result + } + + /// Samples asset balances from `rng`. + /// + /// # Warning + /// + /// This is a naive algorithm and should only be used for testing purposes. + #[inline] + pub fn sample_asset_balances<R, const N: usize>( + total: AssetBalance, + rng: &mut R, + ) -> AssetBalances<N> + where + R: CryptoRng + RngCore + ?Sized, + { + into_array_unchecked(value_distribution(N, total, rng)) + } + + impl<const SOURCES: usize, const SINKS: usize> Sample<distribution::PublicTransfer> + for PublicTransfer<SOURCES, SINKS> + { + #[inline] + fn sample<R>(distribution: distribution::PublicTransfer, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + let _ = distribution; + Self::sample(distribution::FixedPublicTransfer(rng.gen()), rng) + } + } + + impl<const SOURCES: usize, const SINKS: usize> Sample<distribution::FixedPublicTransfer> + for PublicTransfer<SOURCES, SINKS> + { + #[inline] + fn sample<R>(distribution: distribution::FixedPublicTransfer, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + Self::new( + distribution.0.id, + sample_asset_balances(distribution.0.value, rng), + sample_asset_balances(distribution.0.value, rng), + ) + } + } + + impl<C, const SENDERS: usize, const RECEIVERS: usize> + TrySample<distribution::SecretTransfer<'_, C>> for SecretTransfer<C, SENDERS, RECEIVERS> + where + C: Configuration, + C::SecretKey: Sample, + { + type Error = IntegratedEncryptionSchemeError<C>; + + #[inline] + fn try_sample<R>( + distribution: distribution::SecretTransfer<C>, + rng: &mut R, + ) -> Result<Self, Self::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + Self::try_sample( + distribution::FixedSecretTransfer { + asset: rng.gen(), + base: distribution, + }, + rng, + ) + } + } + + impl<C, const SENDERS: usize, const RECEIVERS: usize> + TrySample<distribution::FixedSecretTransfer<'_, C>> + for SecretTransfer<C, SENDERS, RECEIVERS> + where + C: Configuration, + C::SecretKey: Sample, + { + type Error = IntegratedEncryptionSchemeError<C>; + + #[inline] + fn try_sample<R>( + distribution: distribution::FixedSecretTransfer<C>, + rng: &mut R, + ) -> Result<Self, Self::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + Ok(Self::new( + into_array_unchecked( + (0..SENDERS) + .into_iter() + .map(|_| { + let pre_sender = + PreSender::sample(distribution.base.commitment_scheme, rng); + pre_sender.insert_utxo(distribution.base.utxo_set); + pre_sender.try_upgrade(distribution.base.utxo_set).unwrap() + }) + .collect::<Vec<_>>(), + ), + into_array_unchecked( + (0..RECEIVERS) + .into_iter() + .map(|_| rng.try_sample(distribution.base.commitment_scheme)) + .collect::<Result<Vec<_>, _>>()?, + ), + )) + } + } + + impl< + C, + const SOURCES: usize, + const SENDERS: usize, + const RECEIVERS: usize, + const SINKS: usize, + > TrySample<distribution::Transfer<'_, C>> + for Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS> + where + C: Configuration, + C::SecretKey: Sample, + { + type Error = IntegratedEncryptionSchemeError<C>; + + #[inline] + fn try_sample<R>( + distribution: distribution::Transfer<C>, + rng: &mut R, + ) -> Result<Self, Self::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + Self::try_sample( + distribution::FixedTransfer { + asset_id: rng.gen(), + public_value: rng.gen(), + secret_value: rng.gen(), + base: distribution, + }, + rng, + ) + } + } + + impl< + C, + const SOURCES: usize, + const SENDERS: usize, + const RECEIVERS: usize, + const SINKS: usize, + > TrySample<distribution::FixedTransfer<'_, C>> + for Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS> + where + C: Configuration, + C::SecretKey: Sample, + { + type Error = IntegratedEncryptionSchemeError<C>; + + #[inline] + fn try_sample<R>( + distribution: distribution::FixedTransfer<C>, + rng: &mut R, + ) -> Result<Self, Self::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + Self::check_sender_side(); + Self::check_receiver_side(); + SecretTransfer::<C, SENDERS, RECEIVERS>::check_size_overflow(); + Ok(Self { + public: PublicTransfer::sample( + distribution::FixedPublicTransfer( + distribution.asset_id.with(distribution.public_value), + ), + rng, + ), + secret: SecretTransfer::try_sample( + distribution::FixedSecretTransfer { + asset: distribution.asset_id.with(distribution.secret_value), + base: distribution.base, + }, + rng, + )?, + }) + } } } -*/ diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs index a3ad3bc93..2e638abfa 100644 --- a/manta-accounting/src/wallet/ledger.rs +++ b/manta-accounting/src/wallet/ledger.rs @@ -92,7 +92,7 @@ pub type PullAllResult<C, L> = Result<PullAllResponse<C, L>, <L as Connection<C> /// Ledger Source Push Result /// /// See the [`push`](Connection::push) method on [`Connection`] for more information. -pub type PushResult<C, L> = Result<PushResponse, <L as Connection<C>>::Error>; +pub type PushResult<C, L> = Result<PushResponse<C, L>, <L as Connection<C>>::Error>; /// Ledger Source Pull Response /// @@ -133,7 +133,14 @@ where /// /// This `struct` is created by the [`push`](Connection::push) method on [`Connection`]. /// See its documentation for more. -pub struct PushResponse { +pub struct PushResponse<C, L> +where + C: Configuration, + L: Connection<C> + ?Sized, +{ + /// Current Ledger Checkpoint + pub checkpoint: L::Checkpoint, + /// Successful Push pub success: bool, } diff --git a/manta-accounting/src/wallet/mod.rs b/manta-accounting/src/wallet/mod.rs index 71db8716d..e38a5adc6 100644 --- a/manta-accounting/src/wallet/mod.rs +++ b/manta-accounting/src/wallet/mod.rs @@ -21,4 +21,8 @@ mod state; pub mod ledger; pub mod signer; +#[cfg(feature = "test")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] +pub mod test; + pub use state::*; diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 0117ec62b..ec2292fe0 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -442,8 +442,8 @@ where Ok(TransferAccumulator::new( into_array_unchecked(receivers), - internal.pre_sender, zero_pre_senders, + internal.pre_sender, )) } @@ -841,14 +841,15 @@ where posts.push(self.build_post(SecretTransfer::new(senders, accumulator.receivers))?); + for zero in accumulator.zeroes.iter() { + zero.as_ref().insert_utxo(&mut self.utxo_set); + } + accumulator.pre_sender.insert_utxo(&mut self.utxo_set); + new_zeroes.append(&mut accumulator.zeroes); accumulators.push(accumulator.pre_sender); } - for pre_sender in accumulators.iter() { - pre_sender.insert_utxo(&mut self.utxo_set); - } - accumulators.append(&mut iter.remainder()); pre_senders = accumulators; } @@ -882,9 +883,7 @@ where needed_zeroes -= zeroes.len(); for zero in zeroes { - let next = self.get_pre_sender(zero, Asset::zero(asset_id))?; - next.insert_utxo(&mut self.utxo_set); - pre_senders.push(next); + pre_senders.push(self.get_pre_sender(zero, Asset::zero(asset_id))?); } if needed_zeroes == 0 { @@ -895,11 +894,7 @@ where for _ in 0..needed_zeroes { match new_zeroes.pop() { - Some(zero) => { - let next = zero.unwrap(); - next.insert_utxo(&mut self.utxo_set); - pre_senders.push(next); - } + Some(zero) => pre_senders.push(zero.unwrap()), _ => break, } } @@ -917,7 +912,6 @@ where let (mint, pre_sender) = self.signer .mint_zero(&self.commitment_scheme, asset_id, &mut self.rng)?; - pre_sender.insert_utxo(&mut self.utxo_set); pre_senders.push(pre_sender); posts.push(self.build_post(mint)?); } @@ -980,7 +974,7 @@ where let receiver = self.prepare_receiver(asset, receiver)?; self.build_post(PrivateTransfer::build(senders, [change, receiver]))? } - _ => self.build_post(Reclaim::build(senders, change, asset))?, + _ => self.build_post(Reclaim::build(senders, [change], asset))?, }; posts.push(final_post); @@ -1151,11 +1145,11 @@ where /// Receivers pub receivers: [Receiver<C>; RECEIVERS], - /// Accumulated Balance Pre-Sender - pub pre_sender: PreSender<C>, - /// Zero Balance Pre-Senders pub zeroes: Vec<InternalKeyOwned<D, PreSender<C>>>, + + /// Accumulated Balance Pre-Sender + pub pre_sender: PreSender<C>, } impl<D, C, const RECEIVERS: usize> TransferAccumulator<D, C, RECEIVERS> @@ -1163,17 +1157,17 @@ where D: DerivedSecretKeyGenerator, C: transfer::Configuration<SecretKey = D::SecretKey>, { - /// Builds a new [`TransferAccumulator`] from `receivers`, `pre_sender`, and `zeroes`. + /// Builds a new [`TransferAccumulator`] from `receivers`, `zeroes`, and `pre_sender`. #[inline] pub fn new( receivers: [Receiver<C>; RECEIVERS], - pre_sender: PreSender<C>, zeroes: Vec<InternalKeyOwned<D, PreSender<C>>>, + pre_sender: PreSender<C>, ) -> Self { Self { receivers, - pre_sender, zeroes, + pre_sender, } } } diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index b890b46d6..378d05190 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -310,15 +310,20 @@ where .map_err(Error::InsufficientBalance)?; let SignResponse { posts } = self.signer.sign(transaction).await?; match self.ledger.push(posts).await { - Ok(PushResponse { success: true }) => { + Ok(PushResponse { + checkpoint, + success: true, + }) => { self.try_commit().await; match balance_update { TransactionKind::Deposit(asset) => self.assets.deposit(asset), TransactionKind::Withdraw(asset) => self.assets.withdraw_unchecked(asset), } + self.checkpoint = checkpoint; Ok(true) } - Ok(PushResponse { success: false }) => { + Ok(PushResponse { success: false, .. }) => { + // FIXME: What about the checkpoint? self.try_rollback().await; Ok(false) } diff --git a/manta-accounting/src/wallet/test.rs b/manta-accounting/src/wallet/test.rs new file mode 100644 index 000000000..a95f706ad --- /dev/null +++ b/manta-accounting/src/wallet/test.rs @@ -0,0 +1,17 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Testing Framework diff --git a/manta-cli/Cargo.toml b/manta-cli/Cargo.toml new file mode 100644 index 000000000..84bf670a1 --- /dev/null +++ b/manta-cli/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "manta-cli" +edition = "2018" +version = "0.3.0" +authors = ["Manta Network <contact@manta.network>"] +readme = "README.md" +license-file = "LICENSE" +repository = "https://github.com/Manta-Network/manta-rs" +homepage = "https://github.com/Manta-Network" +documentation = "https://github.com/Manta-Network/manta-rs" +categories = [""] +keywords = [""] +description = "Manta CLI" +publish = false + +[package.metadata.docs.rs] +# To build locally: +# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --no-deps --open +all-features = true +rustdoc-args = ["--cfg", "doc_cfg"] + +[badges] +is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } +is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } +maintenance = { status = "actively-developed" } + +[features] +# Standard Library +std = [] + +[dependencies] +manta-accounting = { path = "../manta-accounting", default-features = false } +manta-crypto = { path = "../manta-crypto", default-features = false } +manta-pay = { path = "../manta-pay", default-features = false } + +[dev-dependencies] +manta-crypto = { path = "../manta-crypto", features = ["test"] } +manta-accounting = { path = "../manta-accounting", features = ["test"] } diff --git a/manta-cli/LICENSE b/manta-cli/LICENSE new file mode 120000 index 000000000..ea5b60640 --- /dev/null +++ b/manta-cli/LICENSE @@ -0,0 +1 @@ +../LICENSE \ No newline at end of file diff --git a/manta-cli/README.md b/manta-cli/README.md new file mode 100644 index 000000000..0a794e609 --- /dev/null +++ b/manta-cli/README.md @@ -0,0 +1 @@ +# manta-cli diff --git a/manta-cli/src/lib.rs b/manta-cli/src/lib.rs new file mode 100644 index 000000000..648f32031 --- /dev/null +++ b/manta-cli/src/lib.rs @@ -0,0 +1,22 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Manta CLI + +#![cfg_attr(not(any(feature = "std", test)), no_std)] +#![cfg_attr(doc_cfg, feature(doc_cfg))] +#![forbid(rustdoc::broken_intra_doc_links)] +#![forbid(missing_docs)] diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs index 8fa9892a9..63841b430 100644 --- a/manta-crypto/src/merkle_tree/fork.rs +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -19,6 +19,7 @@ // TODO: Think about whether we want to keep the `raw::MerkleTreePointerFamily` API sealed or not. // TODO: Implement derive-able traits for these types. // TODO: See if we can get rid of the smart pointer logic. +// TODO: Implement partial merging. Will need to upgrade `PartialInnerTree`. use crate::merkle_tree::{ capacity, diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index 6376ec782..c8174aad9 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -309,7 +309,23 @@ where } } -/// Merkle Tree Provable Mixin +/// Path Error +/// +/// This `struct` is returned by the [`path`](WithProofs::path) method of the [`WithProofs`] trait. +/// See its documentation for more. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum PathError { + /// Path for the given index was not stored in the tree + MissingPath, + + /// Given index exceeded the length of the tree + IndexTooLarge( + /// Length of the tree + usize, + ), +} + +/// Merkle Tree Membership Proof Mixin pub trait WithProofs<C> where C: Configuration + ?Sized, @@ -374,21 +390,25 @@ where fn path(&self, parameters: &Parameters<C>, index: usize) -> Result<Path<C>, PathError>; } -/// Path Error -/// -/// This `struct` is returned by the [`path`](WithProofs::path) method of the [`WithProofs`] trait. -/// See its documentation for more. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum PathError { - /// Path for the given index was not stored in the tree - MissingPath, +/* TODO: +/// Merkle Tree Removable Membership Proof Mixin +pub trait WithRemovableProofs<C>: WithProofs<C> +where + C: Configuration + ?Sized, +{ + /// + fn remove_paths<R>(&mut self, range: R) -> bool + where + R: RangeBounds<usize>; + + /// + #[inline] + fn remove_path(&mut self, index: usize) -> bool { + self.remove_paths(index..=index) + } - /// Given index exceeded the length of the tree - IndexTooLarge( - /// Length of the tree - usize, - ), } +*/ /// Digest Type #[derive(derivative::Derivative)] diff --git a/manta-crypto/src/rand.rs b/manta-crypto/src/rand.rs index 777739bc8..44c0134cd 100644 --- a/manta-crypto/src/rand.rs +++ b/manta-crypto/src/rand.rs @@ -181,27 +181,7 @@ pub struct Standard; pub trait Sample<D = Standard>: Sized { /// Returns a random value of type `Self`, sampled according to the given `distribution`, /// generated from the `rng`. - fn sample<R>(distribution: &D, rng: &mut R) -> Self - where - R: RngCore + ?Sized; - - /// Returns a random value of type `Self`, sampled according to the default distribution of - /// type `D`, generated from the `rng`. - #[inline] - fn gen<R>(rng: &mut R) -> Self - where - D: Default, - R: RngCore + ?Sized, - { - Self::sample(&Default::default(), rng) - } -} - -/// Cryptographic Sampling Trait -pub trait CryptoSample<D = Standard>: Sized { - /// Returns a random value of type `Self`, sampled according to the given `distribution`, - /// generated from the `rng`. - fn sample<R>(distribution: &D, rng: &mut R) -> Self + fn sample<R>(distribution: D, rng: &mut R) -> Self where R: CryptoRng + RngCore + ?Sized; @@ -213,7 +193,7 @@ pub trait CryptoSample<D = Standard>: Sized { D: Default, R: CryptoRng + RngCore + ?Sized, { - Self::sample(&Default::default(), rng) + Self::sample(Default::default(), rng) } } @@ -223,7 +203,7 @@ macro_rules! impl_sample_from_u32 { $( impl Sample for $type { #[inline] - fn sample<R>(distribution: &Standard, rng: &mut R) -> Self + fn sample<R>(distribution: Standard, rng: &mut R) -> Self where R: RngCore + ?Sized, { @@ -239,9 +219,9 @@ impl_sample_from_u32! { u8, u16, u32 } impl Sample for u64 { #[inline] - fn sample<R>(distribution: &Standard, rng: &mut R) -> Self + fn sample<R>(distribution: Standard, rng: &mut R) -> Self where - R: RngCore + ?Sized, + R: CryptoRng + RngCore + ?Sized, { let _ = distribution; rng.next_u64() @@ -250,63 +230,81 @@ impl Sample for u64 { impl Sample for u128 { #[inline] - fn sample<R>(distribution: &Standard, rng: &mut R) -> Self + fn sample<R>(distribution: Standard, rng: &mut R) -> Self where - R: RngCore + ?Sized, + R: CryptoRng + RngCore + ?Sized, { let _ = distribution; ((rng.next_u64() as u128) << 64) | (rng.next_u64() as u128) } } -/// Random Number Generator -pub trait Rand: RngCore { - /// Returns a random value of type `Self`, sampled according to the given `distribution`, - /// generated from the `rng`. - #[inline] - fn sample<D, T>(&mut self, distribution: &D) -> T +/// Fallible Sampling Trait +pub trait TrySample<D = Standard>: Sized { + /// Error Type + type Error; + + /// Tries to return a random value of type `Self`, sampled according to the given + /// `distribution`, generated from the `rng`. + fn try_sample<R>(distribution: D, rng: &mut R) -> Result<Self, Self::Error> where - T: Sample<D>, - { - T::sample(distribution, self) - } + R: CryptoRng + RngCore + ?Sized; - /// Returns a random value of type `Self`, sampled according to the default distribution of - /// type `D`, generated from the `rng`. + /// Tries to return a random value of type `Self`, sampled according to the default + /// distribution of type `D`, generated from the `rng`. #[inline] - fn gen<D, T>(&mut self) -> T + fn try_gen<R>(rng: &mut R) -> Result<Self, Self::Error> where D: Default, - T: Sample<D>, + R: CryptoRng + RngCore + ?Sized, { - T::gen(self) + Self::try_sample(Default::default(), rng) } } -impl<R> Rand for R where R: RngCore + ?Sized {} - -/// Cryptographically Secure Random Number Generator -pub trait CryptoRand: CryptoRng + RngCore { +/// Random Number Generator +pub trait Rand: CryptoRng + RngCore { /// Returns a random value of type `Self`, sampled according to the given `distribution`, /// generated from the `rng`. #[inline] - fn sample<D, T>(&mut self, distribution: &D) -> T + fn sample<D, T>(&mut self, distribution: D) -> T where - T: CryptoSample<D>, + T: Sample<D>, { T::sample(distribution, self) } + /// Tries to return a random value of type `Self`, sampled according to the given + /// `distribution`, generated from the `rng`. + #[inline] + fn try_sample<D, T>(&mut self, distribution: D) -> Result<T, T::Error> + where + T: TrySample<D>, + { + T::try_sample(distribution, self) + } + /// Returns a random value of type `Self`, sampled according to the default distribution of /// type `D`, generated from the `rng`. #[inline] fn gen<D, T>(&mut self) -> T where D: Default, - T: CryptoSample<D>, + T: Sample<D>, { T::gen(self) } + + /// Tries to return a random value of type `Self`, sampled according to the default + /// distribution of type `D`, generated from the `rng`. + #[inline] + fn try_gen<D, T>(&mut self) -> Result<T, T::Error> + where + D: Default, + T: TrySample<D>, + { + T::try_gen(self) + } } -impl<R> CryptoRand for R where R: CryptoRng + Rand + ?Sized {} +impl<R> Rand for R where R: CryptoRng + RngCore + ?Sized {} diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs index ab2133288..697a82b03 100644 --- a/manta-pay/src/crypto/commitment/pedersen.rs +++ b/manta-pay/src/crypto/commitment/pedersen.rs @@ -23,7 +23,7 @@ use ark_crypto_primitives::commitment::{ use ark_ff::bytes::ToBytes; use manta_crypto::{ commitment::CommitmentScheme, - rand::{CryptoRand, CryptoRng, CryptoSample, RngCore, SizedRng, Standard}, + rand::{CryptoRng, Rand, RngCore, Sample, SizedRng, Standard}, }; use manta_util::{Concat, ConcatAccumulator}; @@ -59,13 +59,13 @@ where W: PedersenWindow, C: ProjectiveCurve; -impl<W, C> CryptoSample for PedersenCommitmentRandomness<W, C> +impl<W, C> Sample for PedersenCommitmentRandomness<W, C> where W: PedersenWindow, C: ProjectiveCurve, { #[inline] - fn sample<R>(distribution: &Standard, rng: &mut R) -> Self + fn sample<R>(distribution: Standard, rng: &mut R) -> Self where R: CryptoRng + RngCore + ?Sized, { @@ -152,13 +152,13 @@ where } } -impl<W, C> CryptoSample for PedersenCommitment<W, C> +impl<W, C> Sample for PedersenCommitment<W, C> where W: PedersenWindow, C: ProjectiveCurve, { #[inline] - fn sample<R>(distribution: &Standard, rng: &mut R) -> Self + fn sample<R>(distribution: Standard, rng: &mut R) -> Self where R: CryptoRng + RngCore + ?Sized, { @@ -333,16 +333,16 @@ pub mod constraint { } } - impl<W, C, GG, D> CryptoSample<D> for PedersenCommitmentWrapper<W, C, GG> + impl<W, C, GG, D> Sample<D> for PedersenCommitmentWrapper<W, C, GG> where W: PedersenWindow, C: ProjectiveCurve, GG: CurveVar<C, ConstraintField<C>>, for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - PedersenCommitment<W, C>: CryptoSample<D>, + PedersenCommitment<W, C>: Sample<D>, { #[inline] - fn sample<R>(distribution: &D, rng: &mut R) -> PedersenCommitmentWrapper<W, C, GG> + fn sample<R>(distribution: D, rng: &mut R) -> PedersenCommitmentWrapper<W, C, GG> where R: CryptoRng + RngCore + ?Sized, { diff --git a/manta-pay/src/crypto/prf/blake2s.rs b/manta-pay/src/crypto/prf/blake2s.rs index 322e0732b..38912a44d 100644 --- a/manta-pay/src/crypto/prf/blake2s.rs +++ b/manta-pay/src/crypto/prf/blake2s.rs @@ -18,7 +18,7 @@ use ark_crypto_primitives::prf::{Blake2s as ArkBlake2s, PRF}; use manta_crypto::{ - rand::{CryptoRng, CryptoSample, RngCore, Standard}, + rand::{CryptoRng, RngCore, Sample, Standard}, PseudorandomFunctionFamily, }; use manta_util::{Concat, ConcatAccumulator}; @@ -73,9 +73,9 @@ impl Concat for Blake2sInput { } } -impl CryptoSample for Blake2sInput { +impl Sample for Blake2sInput { #[inline] - fn sample<R>(distribution: &Standard, rng: &mut R) -> Self + fn sample<R>(distribution: Standard, rng: &mut R) -> Self where R: CryptoRng + RngCore + ?Sized, { From 6470ca065e28567c824200d34b13cc292050373d Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 13 Oct 2021 18:31:34 -0400 Subject: [PATCH 096/275] fix!: correct "fixed asset sampling" algorithm for \`Transfer\` BREAKING CHANGE: remove \`public_value\` and \`secret_value\` from FixedTransfer distribution --- manta-accounting/src/identity.rs | 16 +++ manta-accounting/src/transfer.rs | 170 ++++++++++++++++++++++--------- 2 files changed, 138 insertions(+), 48 deletions(-) diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 7a832b8a5..e1608e8a2 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -508,6 +508,22 @@ where } } +impl<C, D> TrySample<D> for Identity<C> +where + C: Configuration, + C::SecretKey: TrySample<D>, +{ + type Error = <C::SecretKey as TrySample<D>>::Error; + + #[inline] + fn try_sample<R>(distribution: D, rng: &mut R) -> Result<Self, Self::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + Ok(Self::new(rng.try_sample(distribution)?)) + } +} + /// Shielded Identity pub struct ShieldedIdentity<C, I> where diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 6a4ef551f..ba1988bab 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -1363,25 +1363,25 @@ pub mod canonical { #[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] pub mod test { use super::*; - use crate::{asset::AssetBalanceType, identity::PreSender}; + use crate::{asset::AssetBalanceType, identity::Identity}; use manta_crypto::rand::{Rand, Sample, Standard, TrySample}; - use manta_util::into_array_unchecked; + use manta_util::{array_map, fallible_array_map, into_array_unchecked}; /// Test Sampling Distributions pub mod distribution { use super::*; - /// [`PublicTransfer`] Sampling Distribution + /// [`PublicTransfer`](super::PublicTransfer) Sampling Distribution pub type PublicTransfer = Standard; - /// Fixed Asset [`PublicTransfer`] Sampling Distribution + /// Fixed Asset [`PublicTransfer`](super::PublicTransfer) Sampling Distribution #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct FixedPublicTransfer(pub Asset); - /// [`SecretTransfer`] Sampling Distribution + /// [`SecretTransfer`](super::SecretTransfer) Sampling Distribution pub type SecretTransfer<'c, C> = Transfer<'c, C>; - /// Fixed Asset [`SecretTransfer`] Sampling Distribution + /// Fixed Asset [`SecretTransfer`](super::SecretTransfer) Sampling Distribution pub struct FixedSecretTransfer<'c, C> where C: Configuration, @@ -1393,7 +1393,100 @@ pub mod test { pub base: SecretTransfer<'c, C>, } - /// [`Transfer`] Sampling Distribution + impl<'c, C> FixedSecretTransfer<'c, C> + where + C: Configuration, + C::SecretKey: Sample, + { + /// Tries to sample a [`super::SecretTransfer`] using custom sender and receiver asset + /// totals. + #[inline] + pub(super) fn try_sample_custom_totals< + R, + const SENDERS: usize, + const RECEIVERS: usize, + >( + asset_id: AssetId, + sender_total: AssetBalance, + receiver_total: AssetBalance, + commitment_scheme: &C::CommitmentScheme, + utxo_set: &mut C::UtxoSet, + rng: &mut R, + ) -> Result< + super::SecretTransfer<C, SENDERS, RECEIVERS>, + IntegratedEncryptionSchemeError<C>, + > + where + R: CryptoRng + RngCore + ?Sized, + { + FixedSecretTransfer::<C>::try_sample_custom_distribution( + asset_id, + sample_asset_balances::<_, SENDERS>(sender_total, rng), + sample_asset_balances::<_, RECEIVERS>(receiver_total, rng), + commitment_scheme, + utxo_set, + rng, + ) + } + + /// Tries to sample a [`super::SecretTransfer`] with custom sender and receiver asset + /// value distributions. + #[inline] + pub(super) fn try_sample_custom_distribution< + R, + const SENDERS: usize, + const RECEIVERS: usize, + >( + asset_id: AssetId, + senders: AssetBalances<SENDERS>, + receivers: AssetBalances<RECEIVERS>, + commitment_scheme: &C::CommitmentScheme, + utxo_set: &mut C::UtxoSet, + rng: &mut R, + ) -> Result< + super::SecretTransfer<C, SENDERS, RECEIVERS>, + IntegratedEncryptionSchemeError<C>, + > + where + R: CryptoRng + RngCore + ?Sized, + { + Ok(super::SecretTransfer::new( + array_map(senders, |v| { + let pre_sender = + Identity::gen(rng).into_pre_sender(commitment_scheme, asset_id.with(v)); + pre_sender.insert_utxo(utxo_set); + pre_sender.try_upgrade(utxo_set).unwrap() + }), + fallible_array_map(receivers, |v| { + Identity::gen(rng).into_receiver(commitment_scheme, asset_id.with(v), rng) + })?, + )) + } + + /// Tries to sample a [`super::SecretTransfer`]. + #[inline] + pub(super) fn try_sample<R, const SENDERS: usize, const RECEIVERS: usize>( + self, + rng: &mut R, + ) -> Result< + super::SecretTransfer<C, SENDERS, RECEIVERS>, + IntegratedEncryptionSchemeError<C>, + > + where + R: CryptoRng + RngCore + ?Sized, + { + Self::try_sample_custom_totals( + self.asset.id, + self.asset.value, + self.asset.value, + self.base.commitment_scheme, + self.base.utxo_set, + rng, + ) + } + } + + /// [`Transfer`](super::Transfer) Sampling Distribution pub struct Transfer<'c, C> where C: Configuration, @@ -1405,19 +1498,13 @@ pub mod test { pub utxo_set: &'c mut C::UtxoSet, } - /// Fixed Asset [`Transfer`] Sampling Distribution + /// Fixed Asset [`Transfer`](super::Transfer) Sampling Distribution pub struct FixedTransfer<'c, C> where C: Configuration, { - /// Asset Id - pub asset_id: AssetId, - - /// Public Value Exchanged - pub public_value: AssetBalance, - - /// Secret Value Exchanged - pub secret_value: AssetBalance, + /// Asset + pub asset: Asset, /// Base Distribution pub base: Transfer<'c, C>, @@ -1543,25 +1630,7 @@ pub mod test { where R: CryptoRng + RngCore + ?Sized, { - Ok(Self::new( - into_array_unchecked( - (0..SENDERS) - .into_iter() - .map(|_| { - let pre_sender = - PreSender::sample(distribution.base.commitment_scheme, rng); - pre_sender.insert_utxo(distribution.base.utxo_set); - pre_sender.try_upgrade(distribution.base.utxo_set).unwrap() - }) - .collect::<Vec<_>>(), - ), - into_array_unchecked( - (0..RECEIVERS) - .into_iter() - .map(|_| rng.try_sample(distribution.base.commitment_scheme)) - .collect::<Result<Vec<_>, _>>()?, - ), - )) + distribution.try_sample(rng) } } @@ -1589,9 +1658,7 @@ pub mod test { { Self::try_sample( distribution::FixedTransfer { - asset_id: rng.gen(), - public_value: rng.gen(), - secret_value: rng.gen(), + asset: rng.gen(), base: distribution, }, rng, @@ -1624,18 +1691,25 @@ pub mod test { Self::check_sender_side(); Self::check_receiver_side(); SecretTransfer::<C, SENDERS, RECEIVERS>::check_size_overflow(); + + let asset = distribution.asset; + let mut input = value_distribution(SOURCES + SENDERS, asset.value, rng); + let mut output = value_distribution(RECEIVERS + SINKS, asset.value, rng); + let secret_input = input.split_off(SOURCES); + let public_output = output.split_off(RECEIVERS); + Ok(Self { - public: PublicTransfer::sample( - distribution::FixedPublicTransfer( - distribution.asset_id.with(distribution.public_value), - ), - rng, + public: PublicTransfer::new( + asset.id, + into_array_unchecked(input), + into_array_unchecked(public_output), ), - secret: SecretTransfer::try_sample( - distribution::FixedSecretTransfer { - asset: distribution.asset_id.with(distribution.secret_value), - base: distribution.base, - }, + secret: distribution::FixedSecretTransfer::try_sample_custom_distribution( + asset.id, + into_array_unchecked(secret_input), + into_array_unchecked(output), + distribution.base.commitment_scheme, + distribution.base.utxo_set, rng, )?, }) From 212141f1d9bdd1c614647004c992819b9419e1e2 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 14 Oct 2021 18:07:16 -0400 Subject: [PATCH 097/275] wip: setting up testing framework for circuits --- Cargo.toml | 1 + manta-accounting/Cargo.toml | 2 +- manta-accounting/src/identity.rs | 10 +- manta-accounting/src/transfer.rs | 36 ++-- manta-cli/Cargo.toml | 2 +- manta-codec/Cargo.toml | 2 +- manta-crypto/Cargo.toml | 5 +- manta-crypto/src/merkle_tree/inner_tree.rs | 1 + manta-crypto/src/merkle_tree/test.rs | 83 +++++++- manta-crypto/src/merkle_tree/tree.rs | 20 +- manta-crypto/src/rand.rs | 23 ++- manta-pay/Cargo.toml | 4 +- manta-pay/src/accounting/config.rs | 5 +- manta-pay/src/accounting/identity.rs | 78 +++++++- manta-pay/src/accounting/keys.rs | 8 +- manta-pay/src/accounting/ledger.rs | 189 +++--------------- manta-pay/src/crypto/commitment/pedersen.rs | 6 +- .../arkworks/proof_systems/groth16.rs | 55 ++--- manta-pay/src/crypto/merkle_tree.rs | 85 ++++++-- manta-pay/src/crypto/prf/blake2s.rs | 17 +- manta-util/Cargo.toml | 2 +- manta/Cargo.toml | 2 +- 22 files changed, 374 insertions(+), 262 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b885a6d63..4c0b9246e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,5 @@ [workspace] +resolver = "2" members = [ "manta", "manta-accounting", diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index cd7114b01..30b3e5845 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -15,7 +15,7 @@ publish = false [package.metadata.docs.rs] # To build locally: -# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --no-deps --open +# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --open all-features = true rustdoc-args = ["--cfg", "doc_cfg"] diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index e1608e8a2..7f4c8df8b 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -1419,7 +1419,7 @@ pub mod constraint { use super::*; use manta_crypto::{ constraint::{ - reflection::{HasAllocation, HasVariable, Var}, + reflection::{HasAllocation, HasVariable}, Allocation, Constant, ConstraintSystem, Derived, Equal, Public, PublicOrSecret, Secret, Variable, }, @@ -1642,16 +1642,14 @@ pub mod constraint { { /// Checks if `self` is a well-formed sender and returns its asset. #[inline] - pub fn get_well_formed_asset( + pub fn get_well_formed_asset<V>( self, cs: &mut C::ConstraintSystem, commitment_scheme: &C::CommitmentSchemeVar, - utxo_set_verifier: &Var<S::Verifier, C::ConstraintSystem>, + utxo_set_verifier: &V, ) -> AssetVar<C::ConstraintSystem> where - S::Verifier: HasAllocation<C::ConstraintSystem>, - <S::Verifier as HasAllocation<C::ConstraintSystem>>::Variable: - VerifierVariable<C::ConstraintSystem, ItemVar = UtxoVar<C>>, + V: VerifierVariable<C::ConstraintSystem, ItemVar = UtxoVar<C>, Type = S::Verifier>, { // Well-formed check: // diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index ba1988bab..855a4456f 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -43,7 +43,7 @@ use manta_crypto::{ }, ies::{EncryptedMessage, IntegratedEncryptionScheme}, rand::{CryptoRng, RngCore}, - set::{constraint::VerifierVariable, VerifiedSet, Verifier}, + set::{constraint::VerifierVariable, VerifiedSet}, }; use manta_util::{create_seal, from_variant_impl, iter::mixed_chain, seal, Either}; @@ -93,20 +93,22 @@ pub trait Configuration: type IntegratedEncryptionScheme: IntegratedEncryptionScheme<Plaintext = Asset>; /// Verified Set for [`Utxo`] - type UtxoSet: VerifiedSet<Item = Utxo<Self>, Verifier = Self::UtxoSetVerifier>; + type UtxoSet: VerifiedSet<Item = Utxo<Self>>; //, Verifier = Self::UtxoSetVerifier>; + /* /// Verified Set Verifier for [`Utxo`] type UtxoSetVerifier: Verifier< - Item = Utxo<Self>, - Public = <Self::UtxoSet as VerifiedSet>::Public, - Secret = <Self::UtxoSet as VerifiedSet>::Secret, - > + HasAllocation<ConstraintSystem<Self>, Variable = Self::UtxoSetVerifierVar, Mode = Constant>; + Item = Utxo<Self>, + Public = <Self::UtxoSet as VerifiedSet>::Public, + Secret = <Self::UtxoSet as VerifiedSet>::Secret, + >; + */ /// Verified Set Verifier Variable for [`Utxo`] type UtxoSetVerifierVar: VerifierVariable< ConstraintSystem<Self>, ItemVar = UtxoVar<Self>, - Type = Self::UtxoSetVerifier, + Type = <Self::UtxoSet as VerifiedSet>::Verifier, Mode = Constant, >; } @@ -159,6 +161,9 @@ pub type SenderVar<C> = identity::constraint::SenderVar<C, <C as Configuration>: pub type ReceiverVar<C> = identity::constraint::ReceiverVar<C, <C as Configuration>::IntegratedEncryptionScheme>; +/// Transfer UTXO Set Verifier Type +pub type UtxoSetVerifier<C> = <<C as Configuration>::UtxoSet as VerifiedSet>::Verifier; + /// Transfer Proving Context Type pub type ProvingContext<C> = <<C as Configuration>::ProofSystem as ProofSystem>::ProvingContext; @@ -556,7 +561,7 @@ where pub fn into_post<R>( self, commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &C::UtxoSetVerifier, + utxo_set_verifier: &UtxoSetVerifier<C>, context: &ProvingContext<C>, rng: &mut R, ) -> Result<TransferPost<C>, ProofSystemError<C>> @@ -704,7 +709,7 @@ where #[inline] fn unknown_variables( commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &C::UtxoSetVerifier, + utxo_set_verifier: &UtxoSetVerifier<C>, cs: &mut ConstraintSystem<C>, ) -> ( Option<C::AssetIdVar>, @@ -730,7 +735,7 @@ where fn known_variables( &self, commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &C::UtxoSetVerifier, + utxo_set_verifier: &UtxoSetVerifier<C>, cs: &mut ConstraintSystem<C>, ) -> ( Option<C::AssetIdVar>, @@ -803,7 +808,7 @@ where #[inline] pub fn generate_context<R>( commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &C::UtxoSetVerifier, + utxo_set_verifier: &UtxoSetVerifier<C>, rng: &mut R, ) -> Option<Result<(ProvingContext<C>, VerifyingContext<C>), ProofSystemError<C>>> where @@ -833,7 +838,7 @@ where pub fn generate_proof<R>( &self, commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &C::UtxoSetVerifier, + utxo_set_verifier: &UtxoSetVerifier<C>, context: &ProvingContext<C>, rng: &mut R, ) -> Result<ShapedProof<C>, ProofSystemError<C>> @@ -865,7 +870,7 @@ where pub fn into_post<R>( self, commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &C::UtxoSetVerifier, + utxo_set_verifier: &UtxoSetVerifier<C>, context: &ProvingContext<C>, rng: &mut R, ) -> Result<TransferPost<C>, ProofSystemError<C>> @@ -991,15 +996,16 @@ from_variant_impl!(TransferPostError, Sender, SenderPostError); from_variant_impl!(TransferPostError, Receiver, ReceiverPostError); /// Transfer Post +// FIXME: Add public data pub struct TransferPost<C> where C: Configuration, { /// Sender Posts - pub sender_posts: Vec<SenderPost<C>>, + sender_posts: Vec<SenderPost<C>>, /// Receiver Posts - pub receiver_posts: Vec<ReceiverPost<C>>, + receiver_posts: Vec<ReceiverPost<C>>, /// Validity Proof /// diff --git a/manta-cli/Cargo.toml b/manta-cli/Cargo.toml index 84bf670a1..4232b482d 100644 --- a/manta-cli/Cargo.toml +++ b/manta-cli/Cargo.toml @@ -15,7 +15,7 @@ publish = false [package.metadata.docs.rs] # To build locally: -# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --no-deps --open +# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --open all-features = true rustdoc-args = ["--cfg", "doc_cfg"] diff --git a/manta-codec/Cargo.toml b/manta-codec/Cargo.toml index 6e34c7c5c..a42dea7fc 100644 --- a/manta-codec/Cargo.toml +++ b/manta-codec/Cargo.toml @@ -15,7 +15,7 @@ publish = false [package.metadata.docs.rs] # To build locally: -# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --no-deps --open +# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --open all-features = true rustdoc-args = ["--cfg", "doc_cfg"] diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index 9f6f7b61a..0044cf8c4 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -15,7 +15,7 @@ publish = false [package.metadata.docs.rs] # To build locally: -# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --no-deps --open +# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --open all-features = true rustdoc-args = ["--cfg", "doc_cfg"] @@ -28,6 +28,9 @@ maintenance = { status = "actively-developed" } # Constraint System Gadgets # TODO: constraints = [] +# Enable `getrandom` Entropy Source +getrandom = ["rand_core/getrandom"] + # Standard Library std = [] diff --git a/manta-crypto/src/merkle_tree/inner_tree.rs b/manta-crypto/src/merkle_tree/inner_tree.rs index 35fca7ddf..9084c1ea6 100644 --- a/manta-crypto/src/merkle_tree/inner_tree.rs +++ b/manta-crypto/src/merkle_tree/inner_tree.rs @@ -262,6 +262,7 @@ where pub type HashMap<C, S = hash_map::RandomState> = hash_map::HashMap<usize, InnerDigest<C>, S>; #[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] impl<C, S> InnerMap<C> for HashMap<C, S> where C: Configuration + ?Sized, diff --git a/manta-crypto/src/merkle_tree/test.rs b/manta-crypto/src/merkle_tree/test.rs index 993916114..45bd03d5f 100644 --- a/manta-crypto/src/merkle_tree/test.rs +++ b/manta-crypto/src/merkle_tree/test.rs @@ -16,7 +16,88 @@ //! Testing Framework -use crate::merkle_tree::{Configuration, Leaf, MerkleTree, Parameters, Tree, WithProofs}; +use crate::{ + merkle_tree::{ + Configuration, HashConfiguration, InnerHashParameters, Leaf, LeafHashParameters, + MerkleTree, Parameters, Tree, WithProofs, + }, + rand::{CryptoRng, RngCore, Sample}, +}; +use core::{fmt::Debug, hash::Hash}; + +/// Hash Parameter Sampling +pub trait HashParameterSampling: HashConfiguration { + /// Leaf Hash Parameter Distribution + type LeafHashParameterDistribution; + + /// Inner Hash Parameter Distribution + type InnerHashParameterDistribution; + + /// Sample leaf hash parameters from `distribution` using the given `rng`. + fn sample_leaf_hash_parameters<R>( + distribution: Self::LeafHashParameterDistribution, + rng: &mut R, + ) -> LeafHashParameters<Self> + where + R: CryptoRng + RngCore + ?Sized; + + /// Sample inner hash parameters from `distribution` using the given `rng`. + fn sample_inner_hash_parameters<R>( + distribution: Self::InnerHashParameterDistribution, + rng: &mut R, + ) -> InnerHashParameters<Self> + where + R: CryptoRng + RngCore + ?Sized; +} + +/// Hash Parameter Distribution +#[derive(derivative::Derivative)] +#[derivative( + Clone( + bound = "C::LeafHashParameterDistribution: Clone, C::InnerHashParameterDistribution: Clone" + ), + Copy( + bound = "C::LeafHashParameterDistribution: Copy, C::InnerHashParameterDistribution: Copy" + ), + Debug( + bound = "C::LeafHashParameterDistribution: Debug, C::InnerHashParameterDistribution: Debug" + ), + Default( + bound = "C::LeafHashParameterDistribution: Default, C::InnerHashParameterDistribution: Default" + ), + Eq(bound = "C::LeafHashParameterDistribution: Eq, C::InnerHashParameterDistribution: Eq"), + Hash( + bound = "C::LeafHashParameterDistribution: Hash, C::InnerHashParameterDistribution: Hash" + ), + PartialEq(bound = "C::LeafHashParameterDistribution: PartialEq, + C::InnerHashParameterDistribution: PartialEq") +)] +pub struct HashParameterDistribution<C> +where + C: HashParameterSampling + ?Sized, +{ + /// Leaf Hash Parameter Distribution + pub leaf: C::LeafHashParameterDistribution, + + /// Inner Hash Parameter Distribution + pub inner: C::InnerHashParameterDistribution, +} + +impl<C> Sample<HashParameterDistribution<C>> for Parameters<C> +where + C: HashParameterSampling + ?Sized, +{ + #[inline] + fn sample<R>(distribution: HashParameterDistribution<C>, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + Self::new( + C::sample_leaf_hash_parameters(distribution.leaf, rng), + C::sample_inner_hash_parameters(distribution.inner, rng), + ) + } +} /// Tests that a tree constructed with `parameters` can accept at least two leaves without /// failing. diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index c8174aad9..a31be2439 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -132,7 +132,7 @@ where pub type Leaf<C> = <<C as HashConfiguration>::LeafHash as LeafHash>::Leaf; /// Leaf Hash Parameters Type -pub type LeafHashParamters<C> = <<C as HashConfiguration>::LeafHash as LeafHash>::Parameters; +pub type LeafHashParameters<C> = <<C as HashConfiguration>::LeafHash as LeafHash>::Parameters; /// Leaf Hash Digest Type pub type LeafDigest<C> = <<C as HashConfiguration>::LeafHash as LeafHash>::Output; @@ -434,20 +434,20 @@ where /// Merkle Tree Parameters #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "LeafHashParamters<C>: Clone, InnerHashParameters<C>: Clone"), - Copy(bound = "LeafHashParamters<C>: Copy, InnerHashParameters<C>: Copy"), - Debug(bound = "LeafHashParamters<C>: Debug, InnerHashParameters<C>: Debug"), - Default(bound = "LeafHashParamters<C>: Default, InnerHashParameters<C>: Default"), - Eq(bound = "LeafHashParamters<C>: Eq, InnerHashParameters<C>: Eq"), - Hash(bound = "LeafHashParamters<C>: Hash, InnerHashParameters<C>: Hash"), - PartialEq(bound = "LeafHashParamters<C>: PartialEq, InnerHashParameters<C>: PartialEq") + Clone(bound = "LeafHashParameters<C>: Clone, InnerHashParameters<C>: Clone"), + Copy(bound = "LeafHashParameters<C>: Copy, InnerHashParameters<C>: Copy"), + Debug(bound = "LeafHashParameters<C>: Debug, InnerHashParameters<C>: Debug"), + Default(bound = "LeafHashParameters<C>: Default, InnerHashParameters<C>: Default"), + Eq(bound = "LeafHashParameters<C>: Eq, InnerHashParameters<C>: Eq"), + Hash(bound = "LeafHashParameters<C>: Hash, InnerHashParameters<C>: Hash"), + PartialEq(bound = "LeafHashParameters<C>: PartialEq, InnerHashParameters<C>: PartialEq") )] pub struct Parameters<C> where C: HashConfiguration + ?Sized, { /// Leaf Hash Parameters - pub leaf: LeafHashParamters<C>, + pub leaf: LeafHashParameters<C>, /// Inner Hash Parameters pub inner: InnerHashParameters<C>, @@ -459,7 +459,7 @@ where { /// Builds a new [`Parameters`] from `leaf` and `inner` parameters. #[inline] - pub fn new(leaf: LeafHashParamters<C>, inner: InnerHashParameters<C>) -> Self { + pub fn new(leaf: LeafHashParameters<C>, inner: InnerHashParameters<C>) -> Self { Self { leaf, inner } } diff --git a/manta-crypto/src/rand.rs b/manta-crypto/src/rand.rs index 44c0134cd..01503cde5 100644 --- a/manta-crypto/src/rand.rs +++ b/manta-crypto/src/rand.rs @@ -16,7 +16,9 @@ //! Random Number Generators -use core::{fmt::Debug, hash::Hash, marker::PhantomData}; +use alloc::vec::Vec; +use core::{fmt::Debug, hash::Hash, iter::repeat, marker::PhantomData}; +use manta_util::into_array_unchecked; pub use rand_core::{block, CryptoRng, Error, RngCore, SeedableRng}; @@ -239,6 +241,25 @@ impl Sample for u128 { } } +impl<D, T, const N: usize> Sample<D> for [T; N] +where + D: Clone, + T: Sample<D>, +{ + #[inline] + fn sample<R>(distribution: D, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + into_array_unchecked( + repeat(distribution) + .take(N) + .map(move |d| T::sample(d, rng)) + .collect::<Vec<_>>(), + ) + } +} + /// Fallible Sampling Trait pub trait TrySample<D = Standard>: Sized { /// Error Type diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 8af4816c9..4470ef239 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -15,7 +15,7 @@ publish = false [package.metadata.docs.rs] # To build locally: -# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --no-deps --open +# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --open all-features = true rustdoc-args = ["--cfg", "doc_cfg"] @@ -67,6 +67,6 @@ rand_chacha = { version = "0.3.1", default-features = false } x25519-dalek = { git = "https://github.com/Manta-Network/x25519-dalek", default-features = false, features = ["u64_backend"] } [dev-dependencies] -manta-crypto = { path = "../manta-crypto", features = ["test"] } manta-accounting = { path = "../manta-accounting", features = ["test"] } +manta-crypto = { path = "../manta-crypto", features = ["test"] } rand = "0.8.4" diff --git a/manta-pay/src/accounting/config.rs b/manta-pay/src/accounting/config.rs index e0f2350ed..3ec3c69c7 100644 --- a/manta-pay/src/accounting/config.rs +++ b/manta-pay/src/accounting/config.rs @@ -20,7 +20,7 @@ // enabled when using whichever backend. use crate::{ - accounting::ledger::{UtxoSet, UtxoSetVerifier, UtxoSetVerifierVar}, + accounting::identity::{constraint::UtxoSetVerifierVar, Utxo, UtxoSet}, crypto::{ commitment::pedersen::{self, PedersenWindow}, constraint::arkworks::{ @@ -90,7 +90,7 @@ pub type ProofSystem = Groth16<Bls12_381>; pub struct Configuration; impl ArkMerkleTreeConfiguration for Configuration { - type Leaf = [u8]; + type Leaf = Utxo; type LeafHash = ArkPedersenCommitment; type InnerHash = ArkPedersenCommitment; type Height = u8; @@ -156,6 +156,5 @@ impl transfer::Configuration for Configuration { type AssetBalanceVar = AssetBalanceVar<ConstraintField>; type IntegratedEncryptionScheme = IES; type UtxoSet = UtxoSet; - type UtxoSetVerifier = UtxoSetVerifier; type UtxoSetVerifierVar = UtxoSetVerifierVar; } diff --git a/manta-pay/src/accounting/identity.rs b/manta-pay/src/accounting/identity.rs index ef7288cf4..ed9934782 100644 --- a/manta-pay/src/accounting/identity.rs +++ b/manta-pay/src/accounting/identity.rs @@ -16,8 +16,12 @@ //! Identity Implementations -use crate::accounting::config::Configuration; +use crate::{accounting::config::Configuration, crypto::merkle_tree::ConfigConverter}; use manta_accounting::{identity, transfer}; +use manta_crypto::merkle_tree::{self, full::Full}; + +/// Unspent Transaction Output +pub type Utxo = identity::Utxo<Configuration>; /// Asset Parameters pub type AssetParameters = identity::AssetParameters<Configuration>; @@ -53,3 +57,75 @@ pub type ReceiverPost = identity::ReceiverPost< Configuration, <Configuration as transfer::Configuration>::IntegratedEncryptionScheme, >; + +/// UTXO Set Parameters +pub type Parameters = merkle_tree::Parameters<ConfigConverter<Configuration>>; + +/// UTXO Set Root +pub type Root = merkle_tree::Root<ConfigConverter<Configuration>>; + +/// UTXO Set Path +pub type Path = merkle_tree::Path<ConfigConverter<Configuration>>; + +/// UTXO Set +// FIXME: Change this to sharded merkle tree. +pub type UtxoSet = + merkle_tree::MerkleTree<ConfigConverter<Configuration>, Full<ConfigConverter<Configuration>>>; + +/// Identity Constraint System Variables +pub mod constraint { + use super::*; + use crate::{ + accounting::config::ConstraintSystem, + crypto::merkle_tree::constraint as merkle_tree_constraint, + }; + use manta_crypto::{ + constraint::{reflection::HasAllocation, Allocation, Constant, Variable}, + set::constraint::VerifierVariable, + }; + use manta_util::concatenate; + + /// UTXO Variable + pub type UtxoVar = identity::constraint::UtxoVar<Configuration>; + + /// UTXO Set Parameters Variable + pub type ParametersVar = merkle_tree_constraint::ParametersVar<Configuration>; + + /// UTXO Set Root Variable + pub type RootVar = merkle_tree_constraint::RootVar<Configuration>; + + /// UTXO Set Path Variable + pub type PathVar = merkle_tree_constraint::PathVar<Configuration>; + + /// UTXO Set Verifier Variable + #[derive(Clone)] + pub struct UtxoSetVerifierVar(ParametersVar); + + impl Variable<ConstraintSystem> for UtxoSetVerifierVar { + type Type = Parameters; + + type Mode = Constant; + + #[inline] + fn new(ps: &mut ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + let (this, mode) = allocation.into_known(); + Self(this.known(ps, mode)) + } + } + + impl VerifierVariable<ConstraintSystem> for UtxoSetVerifierVar { + type ItemVar = UtxoVar; + + #[inline] + fn assert_valid_membership_proof( + &self, + public: &RootVar, + secret: &PathVar, + item: &UtxoVar, + cs: &mut ConstraintSystem, + ) { + let _ = cs; + self.0.assert_verified(public, secret, &concatenate!(item)) + } + } +} diff --git a/manta-pay/src/accounting/keys.rs b/manta-pay/src/accounting/keys.rs index 693b73b28..f6222150c 100644 --- a/manta-pay/src/accounting/keys.rs +++ b/manta-pay/src/accounting/keys.rs @@ -24,12 +24,12 @@ //! [`BIP-0044`]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki use alloc::{format, string::String}; -use bip32::Seed; +use bip32::{Seed, XPrv}; use core::{marker::PhantomData, num::ParseIntError, str::FromStr}; use manta_accounting::keys::{DerivedSecretKeyGenerator, DerivedSecretKeyParameter, KeyKind}; use manta_util::{create_seal, seal}; -pub use bip32::{Error, Mnemonic, XPrv as SecretKey}; +pub use bip32::{Error, Mnemonic}; create_seal! {} @@ -197,7 +197,7 @@ impl<C> DerivedSecretKeyGenerator for DerivedKeySecret<C> where C: CoinType, { - type SecretKey = SecretKey; + type SecretKey = XPrv; type Account = AccountParameter; @@ -212,6 +212,6 @@ where account: &Self::Account, index: &Self::Index, ) -> Result<Self::SecretKey, Self::Error> { - SecretKey::derive_from_path(&self.seed, &path_string::<C>(kind, account, index).parse()?) + XPrv::derive_from_path(&self.seed, &path_string::<C>(kind, account, index).parse()?) } } diff --git a/manta-pay/src/accounting/ledger.rs b/manta-pay/src/accounting/ledger.rs index 3165ca290..04e4a9ea2 100644 --- a/manta-pay/src/accounting/ledger.rs +++ b/manta-pay/src/accounting/ledger.rs @@ -20,45 +20,19 @@ // represents the "native" ledger rather than the blockchain ledger. use crate::{ - accounting::config::{Configuration, ConstraintSystem}, - crypto::merkle_tree::{constraint as merkle_tree_constraint, ConfigConverter}, + accounting::{ + config::Configuration, + identity::{Parameters, Root, Utxo}, + }, + crypto::merkle_tree::ConfigConverter, }; use alloc::{collections::BTreeSet, vec, vec::Vec}; use blake2::{ digest::{Update, VariableOutput}, VarBlake2s, }; -use manta_accounting::identity; -use manta_crypto::{ - constraint::{reflection::HasAllocation, Allocation, Constant, Variable}, - merkle_tree::{self, single_path::SinglePath, Tree}, - set::{constraint::VerifierVariable, MembershipProof, VerifiedSet, Verifier}, -}; -use manta_util::{as_bytes, concatenate, into_array_unchecked}; - -/// Unspent Transaction Output -type Utxo = identity::Utxo<Configuration>; - -/// UTXO Variable -type UtxoVar = identity::constraint::UtxoVar<Configuration>; - -/// UTXO Shard Root -type Root = merkle_tree::Root<ConfigConverter<Configuration>>; - -/// UTXO Shard Root Variable -type RootVar = merkle_tree_constraint::RootVar<Configuration>; - -/// UTXO Set Parameters -type Parameters = merkle_tree::Parameters<ConfigConverter<Configuration>>; - -/// UTXO Set Parameters Variable -type ParametersVar = merkle_tree_constraint::ParametersVar<Configuration>; - -/// UTXO Set Path -type Path = merkle_tree::Path<ConfigConverter<Configuration>>; - -/// UTXO Set Path Variable -type PathVar = merkle_tree_constraint::PathVar<Configuration>; +use manta_crypto::merkle_tree::{single_path::SinglePath, Tree}; +use manta_util::{as_bytes, into_array_unchecked}; /// UTXO Shard #[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] @@ -70,46 +44,29 @@ pub struct UtxoShard { utxos: SinglePath<ConfigConverter<Configuration>>, } -/// UTXO Set Verifier -#[derive(Clone)] -pub struct UtxoSetVerifier(Parameters); - -impl Verifier for UtxoSetVerifier { - type Item = Utxo; - - type Public = Root; - - type Secret = Path; - - #[inline] - fn verify(&self, public: &Self::Public, secret: &Self::Secret, item: &Self::Item) -> bool { - self.0.verify(public, secret, &as_bytes!(item)) - } -} - -/// UTXO Set +/// UTXO Set Ledger #[derive(Clone)] -pub struct UtxoSet { +pub struct UtxoSetLedger { /// UTXO Shards - shards: [UtxoShard; Self::SHARD_COUNT], + pub shards: [UtxoShard; Self::SHARD_COUNT], /// UTXO Set - _utxos: BTreeSet<Utxo>, + pub utxos: BTreeSet<[u8; 32]>, /// Merkle Tree Parameters - parameters: UtxoSetVerifier, + pub parameters: Parameters, } -impl UtxoSet { +impl UtxoSetLedger { const SHARD_COUNT: usize = 256; - /// Builds a new [`UtxoSet`]. + /// Builds a new [`UtxoSetLedger`]. #[inline] pub fn new(parameters: Parameters) -> Self { Self { shards: into_array_unchecked(vec![Default::default(); Self::SHARD_COUNT]), - _utxos: Default::default(), - parameters: UtxoSetVerifier(parameters), + utxos: Default::default(), + parameters, } } @@ -138,122 +95,22 @@ impl UtxoSet { /// Returns `true` if the `utxo` belongs to the shard it would be stored in. #[inline] pub fn utxo_exists(&self, utxo: &Utxo) -> bool { - let _ = utxo; - // TODO: self.utxos.contains(utxo) - todo!() - } -} - -impl VerifiedSet for UtxoSet { - type Item = Utxo; - - type Public = Root; - - type Secret = Path; - - type Verifier = UtxoSetVerifier; - - #[inline] - fn capacity(&self) -> usize { - todo!() - } - - #[inline] - fn len(&self) -> usize { - // FIXME: Implement. - todo!() + self.utxos.contains(as_bytes!(utxo).as_slice()) } + /// #[inline] - fn insert(&mut self, item: &Self::Item) -> bool { - if self.utxo_exists(item) { + pub fn insert(&mut self, utxo: &Utxo) -> bool { + if self.utxo_exists(utxo) { return false; } - if !self.shards[Self::shard_index(item)] + if !self.shards[Self::shard_index(utxo)] .utxos - .push(&self.parameters.0, &as_bytes!(item)) + .push(&self.parameters, utxo) { return false; } - // FIXME: self.utxos.insert(item); + self.utxos.insert(into_array_unchecked(as_bytes!(utxo))); true } - - #[inline] - fn insert_provable(&mut self, item: &Self::Item) -> bool { - // FIXME: This is not implementable! - false - } - - #[inline] - fn verifier(&self) -> &Self::Verifier { - &self.parameters - } - - #[inline] - fn check_public(&self, public: &Self::Public) -> bool { - self.root_exists(public) - } - - #[inline] - fn get_membership_proof(&self, item: &Self::Item) -> Option<MembershipProof<Self::Verifier>> { - let _ = item; - - // TODO: Return a more informative error. - - /* TODO: - let utxos = &self.shards[Self::shard_index(item)].utxos; - match utxos.iter().position(move |u| u == item) { - Some(index) => MerkleTree::new(&self.parameters, utxos) - .ok_or(())? - .get_containment_proof(index) - .ok_or(()), - _ => Err(()), - } - */ - - todo!() - } - - #[inline] - fn contains(&self, item: &Self::Item) -> bool { - self.utxo_exists(item) - } -} - -/// UTXO Set Verifier Variable -#[derive(Clone)] -pub struct UtxoSetVerifierVar(ParametersVar); - -impl Variable<ConstraintSystem> for UtxoSetVerifierVar { - type Type = UtxoSetVerifier; - - type Mode = Constant; - - #[inline] - fn new(ps: &mut ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - let (this, mode) = allocation.into_known(); - Self(this.0.known(ps, mode)) - } -} - -impl HasAllocation<ConstraintSystem> for UtxoSetVerifier { - type Variable = UtxoSetVerifierVar; - type Mode = Constant; -} - -impl VerifierVariable<ConstraintSystem> for UtxoSetVerifierVar { - type ItemVar = UtxoVar; - - #[inline] - fn assert_valid_membership_proof( - &self, - public: &RootVar, - secret: &PathVar, - item: &UtxoVar, - cs: &mut ConstraintSystem, - ) { - let _ = cs; - self.0.assert_verified(public, secret, &concatenate!(item)) - } } diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs index 697a82b03..e33807c74 100644 --- a/manta-pay/src/crypto/commitment/pedersen.rs +++ b/manta-pay/src/crypto/commitment/pedersen.rs @@ -20,7 +20,7 @@ use alloc::vec::Vec; use ark_crypto_primitives::commitment::{ pedersen::Commitment as ArkPedersenCommitment, CommitmentScheme as ArkCommitmentScheme, }; -use ark_ff::bytes::ToBytes; +use ark_ff::{bytes::ToBytes, UniformRand}; use manta_crypto::{ commitment::CommitmentScheme, rand::{CryptoRng, Rand, RngCore, Sample, SizedRng, Standard}, @@ -69,8 +69,8 @@ where where R: CryptoRng + RngCore + ?Sized, { - // FIXME: Implement. - todo!() + let _ = distribution; + Self(ArkPedersenCommitmentRandomness::<W, _>::rand(rng)) } } diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs index 6592f7bad..c5568bef5 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs @@ -137,20 +137,24 @@ where } } -/* FIXME: #[cfg(test)] mod test { use crate::accounting::{ - ledger::UtxoSet, + identity::UtxoSet, transfer::{Mint, PrivateTransfer, Reclaim}, }; - use rand::{thread_rng, Rng}; + use manta_accounting::transfer::test::distribution; + use manta_crypto::{ + rand::{Rand, TrySample}, + set::VerifiedSet, + }; + use rand::thread_rng; /// Tests the generation of proving/verifying keys for [`PrivateTransfer`]. #[test] fn generate_private_transfer_keys() { let mut rng = thread_rng(); - PrivateTransfer::generate_context(&rng.gen(), &UtxoSet::new(rng.gen()), &mut rng) + PrivateTransfer::generate_context(&rng.gen(), &rng.gen(), &mut rng) .unwrap() .unwrap(); } @@ -159,7 +163,7 @@ mod test { #[test] fn generate_reclaim_keys() { let mut rng = thread_rng(); - Reclaim::generate_context(&rng.gen(), &UtxoSet::new(rng.gen()), &mut rng) + Reclaim::generate_context(&rng.gen(), &rng.gen(), &mut rng) .unwrap() .unwrap(); } @@ -168,40 +172,43 @@ mod test { /// [`Mint`] does not require a proof. #[test] #[should_panic] - fn generate_mint_keys_is_impossible() { + fn generating_mint_keys_is_impossible() { let mut rng = thread_rng(); - Mint::generate_context(&rng.gen(), &UtxoSet::new(rng.gen()), &mut rng) + Mint::generate_context(&rng.gen(), &rng.gen(), &mut rng) .unwrap() .unwrap(); } /// #[test] - fn test_private_transfer() { - /* TODO: + fn private_transfer() { let mut rng = thread_rng(); let commitment_scheme = rng.gen(); let mut utxo_set = UtxoSet::new(rng.gen()); - let base = rng.gen::<Identity>(); - let shielded = base.into_shielded(); - - let mint_asset = rng.gen(); - let mint = Mint::build( - mint_asset, - shielded.into_receiver(&commitment_scheme, mint_asset, &mut rng), - ); + let private_transfer = PrivateTransfer::try_sample( + distribution::Transfer { + commitment_scheme: &commitment_scheme, + utxo_set: &mut utxo_set, + }, + &mut rng, + ) + .unwrap(); let (proving_key, verifying_key) = - PrivateTransfer::generate_context(&commitment_scheme, &utxo_set, &mut rng) + PrivateTransfer::generate_context(&commitment_scheme, utxo_set.verifier(), &mut rng) .unwrap() .unwrap(); - let secret_transfer = PrivateTransfer::build( - [rng.gen().into_sender(), rng.gen().into_sender()], - [rng.gen().into_receiver(), rng.gen().into_receiver()], - ); - */ + let post = private_transfer + .into_post( + &commitment_scheme, + utxo_set.verifier(), + &proving_key, + &mut rng, + ) + .unwrap(); + + // TODO: let _ = post.validate(&ledger).unwrap(); } } -*/ diff --git a/manta-pay/src/crypto/merkle_tree.rs b/manta-pay/src/crypto/merkle_tree.rs index f72e1a3d7..a3668a71f 100644 --- a/manta-pay/src/crypto/merkle_tree.rs +++ b/manta-pay/src/crypto/merkle_tree.rs @@ -23,7 +23,10 @@ use ark_crypto_primitives::{ }; use ark_ff::{to_bytes, ToBytes}; use core::marker::PhantomData; -use manta_crypto::merkle_tree::{self, InnerHash, LeafHash}; +use manta_crypto::{ + merkle_tree::{self, InnerHash, LeafHash}, + rand::{CryptoRng, RngCore, SizedRng, Standard}, +}; use manta_util::{as_bytes, Concat}; /// Arkworks Leaf Hash Converter @@ -34,6 +37,22 @@ where L: Concat<Item = u8> + ?Sized, LH: CRH; +impl<L, LH> LeafHashConverter<L, LH> +where + L: Concat<Item = u8> + ?Sized, + LH: CRH, +{ + /// Sample leaf hash parameters using `rng`. + #[inline] + pub fn sample_parameters<R>(rng: &mut R) -> LH::Parameters + where + R: CryptoRng + RngCore + ?Sized, + { + LH::setup(&mut SizedRng(rng)) + .expect("Leaf hash parameter generation is not allowed to fail.") + } +} + impl<L, LH> LeafHash for LeafHashConverter<L, LH> where L: Concat<Item = u8> + ?Sized, @@ -67,6 +86,16 @@ where LH: CRH, IH: TwoToOneCRH, { + /// Sample inner hash parameters using `rng`. + #[inline] + pub fn sample_parameters<R>(rng: &mut R) -> IH::Parameters + where + R: CryptoRng + RngCore + ?Sized, + { + IH::setup(&mut SizedRng(rng)) + .expect("Inner hash parameter generation is not allowed to fail.") + } + /// Evaluates the inner hash function for `IH` using `parameters`. #[inline] fn evaluate<T>(parameters: &IH::Parameters, lhs: &T, rhs: &T) -> IH::Output @@ -179,6 +208,40 @@ where type TwoToOneHash = C::InnerHash; } +#[cfg(test)] +impl<C> merkle_tree::test::HashParameterSampling for ConfigConverter<C> +where + C: Configuration, +{ + type LeafHashParameterDistribution = Standard; + + type InnerHashParameterDistribution = Standard; + + #[inline] + fn sample_leaf_hash_parameters<R>( + distribution: Self::LeafHashParameterDistribution, + rng: &mut R, + ) -> merkle_tree::LeafHashParameters<Self> + where + R: CryptoRng + RngCore + ?Sized, + { + let _ = distribution; + <ConfigConverter<C> as merkle_tree::HashConfiguration>::LeafHash::sample_parameters(rng) + } + + #[inline] + fn sample_inner_hash_parameters<R>( + distribution: Self::InnerHashParameterDistribution, + rng: &mut R, + ) -> merkle_tree::InnerHashParameters<Self> + where + R: CryptoRng + RngCore + ?Sized, + { + let _ = distribution; + <ConfigConverter<C> as merkle_tree::HashConfiguration>::InnerHash::sample_parameters(rng) + } +} + /// Merkle Tree Constraint System Variables pub mod constraint { use super::*; @@ -430,22 +493,10 @@ pub mod constraint { ns!(cs.cs, "path variable secret witness"), full(&Self::convert_path(this)), ), - _ => { - // FIXME: We can't use `empty` here. What do we do? - // - // > The circuit we output must contain the height of the merkle tree - // we are using for containment proofs. Since this is mandatory, - // arkworks just forces you to build a path variable from a real path - // even if you are just trying to build the circuit keys. So to solve - // this, we need to find a way to mock the path of the correct height - // (sample it from some distribution) so that when we create the - // variable, it will have the necessary constraints to build the keys. - // - PathVarInnerType::new_witness( - ns!(cs.cs, "path variable secret witness"), - full(&Self::default_path()), - ) - } + _ => PathVarInnerType::new_witness( + ns!(cs.cs, "path variable secret witness"), + full(&Self::default_path()), + ), } .expect("Variable allocation is not allowed to fail."), ) diff --git a/manta-pay/src/crypto/prf/blake2s.rs b/manta-pay/src/crypto/prf/blake2s.rs index 38912a44d..7bd0c028e 100644 --- a/manta-pay/src/crypto/prf/blake2s.rs +++ b/manta-pay/src/crypto/prf/blake2s.rs @@ -18,7 +18,7 @@ use ark_crypto_primitives::prf::{Blake2s as ArkBlake2s, PRF}; use manta_crypto::{ - rand::{CryptoRng, RngCore, Sample, Standard}, + rand::{CryptoRng, Rand, RngCore, Sample, Standard}, PseudorandomFunctionFamily, }; use manta_util::{Concat, ConcatAccumulator}; @@ -57,6 +57,17 @@ impl From<Blake2sSeed> for [u8; 32] { } } +impl Sample for Blake2sSeed { + #[inline] + fn sample<R>(distribution: Standard, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + let _ = distribution; + Self(rng.gen()) + } +} + /// Blake2s Pseudorandom Function Family Input #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Blake2sInput(<ArkBlake2s as PRF>::Input); @@ -79,8 +90,8 @@ impl Sample for Blake2sInput { where R: CryptoRng + RngCore + ?Sized, { - // FIXME: Implement. - todo!() + let _ = distribution; + Self(rng.gen()) } } diff --git a/manta-util/Cargo.toml b/manta-util/Cargo.toml index a14a9a7f7..7f554e3b0 100644 --- a/manta-util/Cargo.toml +++ b/manta-util/Cargo.toml @@ -15,7 +15,7 @@ publish = false [package.metadata.docs.rs] # To build locally: -# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --no-deps --open +# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --open all-features = true rustdoc-args = ["--cfg", "doc_cfg"] diff --git a/manta/Cargo.toml b/manta/Cargo.toml index edb52a53a..e97cdfe68 100644 --- a/manta/Cargo.toml +++ b/manta/Cargo.toml @@ -15,7 +15,7 @@ publish = false [package.metadata.docs.rs] # To build locally: -# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --no-deps --open +# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --open all-features = true rustdoc-args = ["--cfg", "doc_cfg"] From bffb1621a4c7e73296ef49a05c53a928b2d74175 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 15 Oct 2021 02:52:19 -0400 Subject: [PATCH 098/275] wip: fix circuit public input handling logic --- Cargo.toml | 49 +++++- manta-accounting/Cargo.toml | 4 +- manta-accounting/src/identity.rs | 147 ++++++----------- manta-accounting/src/transfer.rs | 155 +++++++++++------- manta-cli/Cargo.toml | 6 +- manta-codec/Cargo.toml | 2 +- manta-crypto/Cargo.toml | 2 +- manta-crypto/src/commitment.rs | 51 +++--- manta-crypto/src/constraint.rs | 23 ++- manta-pay/Cargo.toml | 6 +- manta-pay/src/accounting/mod.rs | 1 - manta-pay/src/accounting/wallet.rs | 17 -- manta-pay/src/crypto/commitment/pedersen.rs | 12 +- .../arkworks/proof_systems/groth16.rs | 17 +- manta/Cargo.toml | 46 ------ manta/LICENSE | 1 - manta/README.md | 1 - {manta/src => src}/lib.rs | 4 +- 18 files changed, 267 insertions(+), 277 deletions(-) delete mode 100644 manta-pay/src/accounting/wallet.rs delete mode 100644 manta/Cargo.toml delete mode 120000 manta/LICENSE delete mode 100644 manta/README.md rename {manta/src => src}/lib.rs (86%) diff --git a/Cargo.toml b/Cargo.toml index 4c0b9246e..bcc817fc6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,53 @@ +[package] +name = "manta" +edition = "2018" +version = "0.3.0" +authors = ["Manta Network <contact@manta.network>"] +readme = "README.md" +license-file = "LICENSE" +repository = "https://github.com/Manta-Network/manta-rs" +homepage = "https://github.com/Manta-Network" +documentation = "https://github.com/Manta-Network/manta-rs" +categories = [""] +keywords = [""] +description = "Manta Network main crate." +publish = false + +[package.metadata.docs.rs] +# To build locally: +# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --open +all-features = true +rustdoc-args = ["--cfg", "doc_cfg"] + +[badges] +is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } +is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } +maintenance = { status = "actively-developed" } + +[features] +# Default Features +default = [] + +# Standard Library +std = [] + +# Test Frameworks +test = ["manta-accounting/test", "manta-crypto/test"] + +[dependencies] +manta-accounting = { path = "manta-accounting" } +manta-codec = { path = "manta-codec" } +manta-crypto = { path = "manta-crypto" } +manta-pay = { path = "manta-pay" } +parity-scale-codec = { version = "2.2.0", default-features = false } + +[dev-dependencies] +manta-accounting = { path = "manta-accounting", features = ["test"] } +manta-crypto = { path = "manta-crypto", features = ["test"] } + [workspace] resolver = "2" members = [ - "manta", "manta-accounting", "manta-cli", "manta-codec", @@ -9,3 +55,4 @@ members = [ "manta-pay", "manta-util", ] + diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 30b3e5845..ffe9f9d99 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -41,8 +41,8 @@ test = [] cocoon = { version = "0.3.0", optional = true, default-features = false } derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } derive_more = { version = "0.99.16", default-features = false, features = ["add", "add_assign", "display", "from", "mul", "mul_assign", "sum"] } -manta-crypto = { path = "../manta-crypto", default-features = false } -manta-util = { path = "../manta-util", default-features = false } +manta-crypto = { path = "../manta-crypto" } +manta-util = { path = "../manta-util" } zeroize = { version = "1.4.2", optional = true, default-features = false, features = ["alloc", "zeroize_derive"] } [dev-dependencies] diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 7f4c8df8b..590c45e62 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -168,7 +168,7 @@ where )] pub struct AssetParameters<C> where - C: Configuration, + C: Configuration + ?Sized, { /// Void Number Generator pub void_number_generator: VoidNumberGenerator<C>, @@ -182,7 +182,7 @@ where impl<C> AssetParameters<C> where - C: Configuration, + C: Configuration + ?Sized, { /// Builds a new [`AssetParameters`]. #[inline] @@ -232,7 +232,7 @@ where impl<C> Sample for AssetParameters<C> where - C: Configuration, + C: Configuration + ?Sized, { #[inline] fn sample<R>(distribution: Standard, rng: &mut R) -> Self @@ -247,7 +247,7 @@ where /// Account Identity pub struct Identity<C> where - C: Configuration, + C: Configuration + ?Sized, { /// Secret Key secret_key: SecretKey<C>, @@ -255,7 +255,7 @@ where impl<C> Identity<C> where - C: Configuration, + C: Configuration + ?Sized, { /// Builds a new [`Identity`] from a [`SecretKey`]. #[inline] @@ -496,7 +496,7 @@ where impl<C, D> Sample<D> for Identity<C> where - C: Configuration, + C: Configuration + ?Sized, C::SecretKey: Sample<D>, { #[inline] @@ -510,7 +510,7 @@ where impl<C, D> TrySample<D> for Identity<C> where - C: Configuration, + C: Configuration + ?Sized, C::SecretKey: TrySample<D>, { type Error = <C::SecretKey as TrySample<D>>::Error; @@ -527,7 +527,7 @@ where /// Shielded Identity pub struct ShieldedIdentity<C, I> where - C: Configuration, + C: Configuration + ?Sized, I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// UTXO Randomness @@ -542,7 +542,7 @@ where impl<C, I> ShieldedIdentity<C, I> where - C: Configuration, + C: Configuration + ?Sized, I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// Builds a new [`ShieldedIdentity`] from `identity` and `commitment_scheme`. @@ -614,7 +614,7 @@ where /// Spending Information pub struct Spend<C, I> where - C: Configuration, + C: Configuration + ?Sized, I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// Spender Identity @@ -626,7 +626,7 @@ where impl<C, I> Spend<C, I> where - C: Configuration, + C: Configuration + ?Sized, I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// Builds a new `Spend` from an `Identity` and an `ies::SecretKey<I>`. @@ -690,7 +690,7 @@ where impl<C, I> From<Identity<C>> for Spend<C, I> where - C: Configuration, + C: Configuration + ?Sized, I: IntegratedEncryptionScheme<Plaintext = Asset>, { #[inline] @@ -702,7 +702,7 @@ where /// Open [`Spend`] pub struct OpenSpend<C> where - C: Configuration, + C: Configuration + ?Sized, { /// Spender Identity identity: Identity<C>, @@ -713,7 +713,7 @@ where impl<C> OpenSpend<C> where - C: Configuration, + C: Configuration + ?Sized, { /// Builds a new `OpenSpend` from an `Identity` and an `Asset`. /// @@ -756,7 +756,7 @@ where /// Internal Identity pub struct InternalIdentity<C, I> where - C: Configuration, + C: Configuration + ?Sized, I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// Receiver @@ -768,7 +768,7 @@ where impl<C, I> InternalIdentity<C, I> where - C: Configuration, + C: Configuration + ?Sized, I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// Builds an [`InternalIdentity`] from an [`Identity`] for the given `asset`. @@ -788,7 +788,7 @@ where impl<C, I> From<InternalIdentity<C, I>> for (Receiver<C, I>, PreSender<C>) where - C: Configuration, + C: Configuration + ?Sized, I: IntegratedEncryptionScheme<Plaintext = Asset>, { #[inline] @@ -803,7 +803,7 @@ where /// See its documentation for more. pub struct SenderProof<C, S> where - C: Configuration, + C: Configuration + ?Sized, S: VerifiedSet<Item = Utxo<C>>, { /// UTXO Membership Proof @@ -815,7 +815,7 @@ where impl<C, S> SenderProof<C, S> where - C: Configuration, + C: Configuration + ?Sized, S: VerifiedSet<Item = Utxo<C>>, { /// Returns `true` if a [`PreSender`] could be upgraded using `self` given the `utxo_set`. @@ -840,7 +840,7 @@ where /// Pre-Sender pub struct PreSender<C> where - C: Configuration, + C: Configuration + ?Sized, { /// Secret Key secret_key: SecretKey<C>, @@ -866,7 +866,7 @@ where impl<C> PreSender<C> where - C: Configuration, + C: Configuration + ?Sized, { /// Builds a new [`PreSender`] for this `asset` from an `identity`. #[inline] @@ -938,7 +938,7 @@ where impl<C> Sample<&C::CommitmentScheme> for PreSender<C> where - C: Configuration, + C: Configuration + ?Sized, C::SecretKey: Sample, { #[inline] @@ -953,7 +953,7 @@ where /// Sender pub struct Sender<C, S> where - C: Configuration, + C: Configuration + ?Sized, S: VerifiedSet<Item = Utxo<C>>, { /// Secret Key @@ -983,7 +983,7 @@ where impl<C, S> Sender<C, S> where - C: Configuration, + C: Configuration + ?Sized, S: VerifiedSet<Item = Utxo<C>>, { /// Builds a new [`Sender`] for this `asset` from an `identity`. @@ -1039,7 +1039,7 @@ where /// Sender Ledger pub trait SenderLedger<C, S> where - C: Configuration, + C: Configuration + ?Sized, S: VerifiedSet<Item = Utxo<C>>, { /// Valid [`VoidNumber`] Posting Key @@ -1092,7 +1092,7 @@ where void_number: Self::ValidVoidNumber, utxo_state: Self::ValidUtxoState, super_key: &Self::SuperPostingKey, - ) -> bool; + ); } /// Sender Post Error @@ -1112,7 +1112,7 @@ pub enum SenderPostError { /// Sender Post pub struct SenderPost<C, S> where - C: Configuration, + C: Configuration + ?Sized, S: VerifiedSet<Item = Utxo<C>>, { /// Void Number @@ -1124,14 +1124,14 @@ where impl<C, S> SenderPost<C, S> where - C: Configuration, + C: Configuration + ?Sized, S: VerifiedSet<Item = Utxo<C>>, { /// Validates `self` on the sender `ledger`. #[inline] pub fn validate<L>(self, ledger: &L) -> Result<SenderPostingKey<C, S, L>, SenderPostError> where - L: SenderLedger<C, S>, + L: SenderLedger<C, S> + ?Sized, { Ok(SenderPostingKey { void_number: match ledger.is_unspent(self.void_number) { @@ -1150,7 +1150,7 @@ where impl<C, S> From<Sender<C, S>> for SenderPost<C, S> where - C: Configuration, + C: Configuration + ?Sized, S: VerifiedSet<Item = Utxo<C>>, { #[inline] @@ -1162,9 +1162,9 @@ where /// Sender Posting Key pub struct SenderPostingKey<C, S, L> where - C: Configuration, + C: Configuration + ?Sized, S: VerifiedSet<Item = Utxo<C>>, - L: SenderLedger<C, S>, + L: SenderLedger<C, S> + ?Sized, { /// Void Number Posting Key void_number: L::ValidVoidNumber, @@ -1175,13 +1175,13 @@ where impl<C, S, L> SenderPostingKey<C, S, L> where - C: Configuration, + C: Configuration + ?Sized, S: VerifiedSet<Item = Utxo<C>>, - L: SenderLedger<C, S>, + L: SenderLedger<C, S> + ?Sized, { /// Posts `self` to the sender `ledger`. #[inline] - pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) -> bool { + pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) { ledger.spend( self.void_number, self.utxo_membership_proof_public, @@ -1193,7 +1193,7 @@ where /// Receiver pub struct Receiver<C, I> where - C: Configuration, + C: Configuration + ?Sized, I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// Asset @@ -1214,7 +1214,7 @@ where impl<C, I> Receiver<C, I> where - C: Configuration, + C: Configuration + ?Sized, I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// Builds a [`Receiver`] from an [`Identity`] for the given `asset`. @@ -1279,7 +1279,7 @@ where impl<C, I> TrySample<&C::CommitmentScheme> for Receiver<C, I> where - C: Configuration, + C: Configuration + ?Sized, C::SecretKey: Sample, I: IntegratedEncryptionScheme<Plaintext = Asset>, { @@ -1297,7 +1297,7 @@ where /// Receiver Ledger pub trait ReceiverLedger<C, I> where - C: Configuration, + C: Configuration + ?Sized, I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// Valid [`Utxo`] Posting Key @@ -1330,7 +1330,7 @@ where utxo: Self::ValidUtxo, encrypted_asset: EncryptedMessage<I>, super_key: &Self::SuperPostingKey, - ) -> bool; + ); } /// Receiver Post Error @@ -1345,7 +1345,7 @@ pub enum ReceiverPostError { /// Receiver Post pub struct ReceiverPost<C, I> where - C: Configuration, + C: Configuration + ?Sized, I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// Unspent Transaction Output @@ -1357,7 +1357,7 @@ where impl<C, I> ReceiverPost<C, I> where - C: Configuration, + C: Configuration + ?Sized, I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// Validates `self` on the receiver `ledger`. @@ -1378,7 +1378,7 @@ where impl<C, I> From<Receiver<C, I>> for ReceiverPost<C, I> where - C: Configuration, + C: Configuration + ?Sized, I: IntegratedEncryptionScheme<Plaintext = Asset>, { #[inline] @@ -1390,9 +1390,9 @@ where /// Receiver Posting Key pub struct ReceiverPostingKey<C, I, L> where - C: Configuration, + C: Configuration + ?Sized, I: IntegratedEncryptionScheme<Plaintext = Asset>, - L: ReceiverLedger<C, I>, + L: ReceiverLedger<C, I> + ?Sized, { /// Utxo Posting Key utxo: L::ValidUtxo, @@ -1403,13 +1403,13 @@ where impl<C, I, L> ReceiverPostingKey<C, I, L> where - C: Configuration, + C: Configuration + ?Sized, I: IntegratedEncryptionScheme<Plaintext = Asset>, - L: ReceiverLedger<C, I>, + L: ReceiverLedger<C, I> + ?Sized, { /// Posts `self` to the receiver `ledger`. #[inline] - pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) -> bool { + pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) { ledger.register(self.utxo, self.encrypted_asset, super_key) } } @@ -1651,31 +1651,10 @@ pub mod constraint { where V: VerifierVariable<C::ConstraintSystem, ItemVar = UtxoVar<C>, Type = S::Verifier>, { - // Well-formed check: - // - // 1. pk = PRF(sk, 0) [public: (), secret: (pk, sk)] - // 2. vn = PRF(sk, rho) [public: (vn), secret: (sk, rho)] - // 3. k = COM(pk || rho, r) [public: (k), secret: (pk, rho, r)] - // 4. cm = COM(asset || k, s) [public: (), secret: (cm, asset, k, s)] - // 5. is_path(cm, path, root) == true [public: (root), secret: (cm, path)] - // - // FIXME: should `k` be private or not? - - // 1. Check public key: - // ``` - // pk = PRF(sk, 0) - // ``` - // where public: {}, secret: {pk, sk}. cs.assert_eq( &self.public_key, &C::PseudorandomFunctionFamilyVar::evaluate_zero(&self.secret_key), ); - - // 2. Check void number: - // ``` - // vn = PRF(sk, rho) - // ``` - // where public: {vn}, secret: {sk, rho}. cs.assert_eq( &self.void_number, &C::PseudorandomFunctionFamilyVar::evaluate( @@ -1683,12 +1662,6 @@ pub mod constraint { &self.parameters.void_number_generator, ), ); - - // 3. Check void number commitment: - // ``` - // k = COM(pk || rho, r) - // ``` - // where public: {k}, secret: {pk, rho, r}. cs.assert_eq( &self.void_number_commitment, &generate_void_number_commitment( @@ -1698,12 +1671,6 @@ pub mod constraint { &self.parameters.void_number_commitment_randomness, ), ); - - // 4. Check UTXO: - // ``` - // cm = COM(asset || k, s) - // ``` - // where public: {}, secret: {cm, asset, k, s}. cs.assert_eq( &self.utxo, &generate_utxo( @@ -1713,15 +1680,8 @@ pub mod constraint { &self.parameters.utxo_randomness, ), ); - - // 5. Check UTXO membership proof: - // ``` - // is_path(cm, path, root) == true - // ``` - // where public: {root}, secret: {cm, path}. self.utxo_membership_proof .assert_validity(utxo_set_verifier, &self.utxo, cs); - self.asset } } @@ -1752,7 +1712,7 @@ pub mod constraint { void_number_commitment: VoidNumberCommitmentVar::<C>::new_known( cs, &this.void_number_commitment, - Public, + Secret, ), utxo: UtxoVar::<C>::new_known(cs, &this.utxo, Secret), utxo_membership_proof: this.utxo_membership_proof.known(cs, mode), @@ -1763,7 +1723,7 @@ pub mod constraint { asset: Asset::unknown(cs, mode), parameters: AssetParameters::unknown(cs, mode), void_number: VoidNumberVar::<C>::new_unknown(cs, Public), - void_number_commitment: VoidNumberCommitmentVar::<C>::new_unknown(cs, Public), + void_number_commitment: VoidNumberCommitmentVar::<C>::new_unknown(cs, Secret), utxo: UtxoVar::<C>::new_unknown(cs, Secret), utxo_membership_proof: MembershipProof::<S::Verifier>::unknown(cs, mode), }, @@ -1810,13 +1770,6 @@ pub mod constraint { I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// Checks if `self` is a well-formed receiver and returns its asset. - /// - /// This [`ReceiverVar`] is well-formed whenever: - /// ```text - /// utxo = COM(asset || k, s) - /// ``` - /// where `k` is `self.void_number_commitment` and `s` is `self.utxo_randomness`. In this - /// equation we have `{ utxo } : Public`, `{ asset, k, s } : Secret`. #[inline] pub fn get_well_formed_asset( self, diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 855a4456f..e6c496498 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -38,8 +38,8 @@ use manta_crypto::{ constraint::{ self, reflection::{HasAllocation, HasVariable}, - Allocation, Constant, ConstraintSystem as _, Derived, Equal, ProofSystem, Public, - PublicOrSecret, Secret, Variable, VariableSource, + Allocation, Constant, ConstraintSystem as _, Derived, Equal, Input as ProofSystemInput, + ProofSystem, Public, PublicOrSecret, Secret, Variable, VariableSource, }, ies::{EncryptedMessage, IntegratedEncryptionScheme}, rand::{CryptoRng, RngCore}, @@ -79,6 +79,12 @@ pub trait Configuration: /// Proof System type ProofSystem: ProofSystem<ConstraintSystem = ConstraintSystem<Self>>; + /* TODO: + + ProofSystemInput<AssetId> + + ProofSystemInput<AssetBalance> + + ProofSystemInput<SenderPost<Self>> + + ProofSystemInput<ReceiverPost<Self>>; + */ /// Asset Id Variable type AssetIdVar: Variable<ConstraintSystem<Self>, Mode = PublicOrSecret, Type = AssetId> @@ -93,16 +99,7 @@ pub trait Configuration: type IntegratedEncryptionScheme: IntegratedEncryptionScheme<Plaintext = Asset>; /// Verified Set for [`Utxo`] - type UtxoSet: VerifiedSet<Item = Utxo<Self>>; //, Verifier = Self::UtxoSetVerifier>; - - /* - /// Verified Set Verifier for [`Utxo`] - type UtxoSetVerifier: Verifier< - Item = Utxo<Self>, - Public = <Self::UtxoSet as VerifiedSet>::Public, - Secret = <Self::UtxoSet as VerifiedSet>::Secret, - >; - */ + type UtxoSet: VerifiedSet<Item = Utxo<Self>>; /// Verified Set Verifier Variable for [`Utxo`] type UtxoSetVerifierVar: VerifierVariable< @@ -199,8 +196,8 @@ where /// /// This type must be restricted so that it can only be constructed by this implementation /// of [`TransferLedger`]. This is to prevent that [`SenderPostingKey::post`] and - /// [`ReceiverPostingKey::post`] are called before [`is_valid`](Self::is_valid), - /// [`SenderPost::validate`], and [`ReceiverPost::validate`]. + /// [`ReceiverPostingKey::post`] are called before [`SenderPost::validate`], + /// [`ReceiverPost::validate`], and [`is_valid`](Self::is_valid). type ValidProof: Copy; /// Super Posting Key @@ -214,7 +211,31 @@ where /// /// This should always succeed on inputs that demonstrate that they do not require a /// proof, by revealing their transaction shape. - fn is_valid(&self, proof: ShapedProof<C>) -> Option<Self::ValidProof>; + fn is_valid( + &self, + asset_id: Option<AssetId>, + sources: &[AssetBalance], + senders: &[SenderPostingKey<C, Self>], + receivers: &[ReceiverPostingKey<C, Self>], + sinks: &[AssetBalance], + proof: ShapedProof<C>, + ) -> Option<Self::ValidProof>; + + /// Updates the public balances in the ledger, finishing the transaction. + /// + /// # Safety + /// + /// This method can only be called once we check that `proof` is a valid proof and that + /// `senders` and `receivers` are valid participants in the transaction. See + /// [`is_valid`](Self::is_valid) for more. + fn update_public_balances( + &mut self, + asset_id: AssetId, + sources: Vec<AssetBalance>, + sinks: Vec<AssetBalance>, + proof: Self::ValidProof, + super_key: &TransferLedgerSuperPostingKey<C, Self>, + ); } /// Dynamic Transfer Shape @@ -884,12 +905,15 @@ where context, rng, )?, + asset_id: self.public.asset_id, + sources: self.public.sources.into(), sender_posts: IntoIterator::into_iter(self.secret.senders) .map(Sender::into_post) .collect(), receiver_posts: IntoIterator::into_iter(self.secret.receivers) .map(Receiver::into_post) .collect(), + sinks: self.public.sinks.into(), }) } } @@ -996,17 +1020,25 @@ from_variant_impl!(TransferPostError, Sender, SenderPostError); from_variant_impl!(TransferPostError, Receiver, ReceiverPostError); /// Transfer Post -// FIXME: Add public data pub struct TransferPost<C> where C: Configuration, { + /// Asset Id + asset_id: Option<AssetId>, + + /// Sources + sources: Vec<AssetBalance>, + /// Sender Posts sender_posts: Vec<SenderPost<C>>, /// Receiver Posts receiver_posts: Vec<ReceiverPost<C>>, + /// Sinks + sinks: Vec<AssetBalance>, + /// Validity Proof /// /// This value is only inhabited by a proof when the transfer shape requires one. @@ -1029,21 +1061,33 @@ where where L: TransferLedger<C>, { + let sender_posting_keys = self + .sender_posts + .into_iter() + .map(move |s| s.validate(ledger)) + .collect::<Result<Vec<_>, _>>()?; + let receiver_posting_keys = self + .receiver_posts + .into_iter() + .map(move |r| r.validate(ledger)) + .collect::<Result<Vec<_>, _>>()?; Ok(TransferPostingKey { - sender_posting_keys: self - .sender_posts - .into_iter() - .map(move |s| s.validate(ledger)) - .collect::<Result<_, _>>()?, - receiver_posting_keys: self - .receiver_posts - .into_iter() - .map(move |r| r.validate(ledger)) - .collect::<Result<_, _>>()?, - validity_proof: match ledger.is_valid(self.validity_proof) { + validity_proof: match ledger.is_valid( + self.asset_id, + &self.sources, + &sender_posting_keys, + &receiver_posting_keys, + &self.sinks, + self.validity_proof, + ) { Some(key) => key, _ => return Err(TransferPostError::InvalidProof), }, + asset_id: self.asset_id, + sources: self.sources, + sender_posting_keys, + receiver_posting_keys, + sinks: self.sinks, }) } } @@ -1054,12 +1098,21 @@ where C: Configuration, L: TransferLedger<C>, { + /// Asset Id + asset_id: Option<AssetId>, + + /// Sources + sources: Vec<AssetBalance>, + /// Sender Posting Keys sender_posting_keys: Vec<SenderPostingKey<C, L>>, /// Receiver Posting Keys receiver_posting_keys: Vec<ReceiverPostingKey<C, L>>, + /// Sinks + sinks: Vec<AssetBalance>, + /// Validity Proof Posting Key validity_proof: L::ValidProof, } @@ -1069,41 +1122,25 @@ where C: Configuration, L: TransferLedger<C>, { - /// Posts `senders` to the transfer `ledger`. - #[inline] - fn post_senders( - senders: Vec<SenderPostingKey<C, L>>, - proof: &L::ValidProof, - super_key: &TransferLedgerSuperPostingKey<C, L>, - ledger: &mut L, - ) -> bool { - senders - .into_iter() - .all(|k| k.post(&(*proof, *super_key), ledger)) - } - - /// Posts `receivers` to the transfer `ledger`. - #[inline] - fn post_receivers( - receivers: Vec<ReceiverPostingKey<C, L>>, - proof: &L::ValidProof, - super_key: &TransferLedgerSuperPostingKey<C, L>, - ledger: &mut L, - ) -> bool { - receivers - .into_iter() - .all(|k| k.post(&(*proof, *super_key), ledger)) - } - /// Posts `self` to the transfer `ledger`. + /// + /// # Safety + /// + /// This method assumes that posting `self` to `ledger` is atomic and cannot fail. See + /// [`SenderLedger::spend`] and [`ReceiverLedger::register`] for more information on the + /// contract for this method. #[inline] - pub fn post(self, super_key: &TransferLedgerSuperPostingKey<C, L>, ledger: &mut L) -> bool { - // FIXME: This needs to be atomic! Add a `commit/rollback` method somewhere? Or can the - // ledger keep track of its own atomicity, so we have an "atomic-until-next-error" - // kind of behavior. + pub fn post(self, super_key: &TransferLedgerSuperPostingKey<C, L>, ledger: &mut L) { let proof = self.validity_proof; - Self::post_senders(self.sender_posting_keys, &proof, super_key, ledger) - && Self::post_receivers(self.receiver_posting_keys, &proof, super_key, ledger) + for key in self.sender_posting_keys { + key.post(&(proof, *super_key), ledger); + } + for key in self.receiver_posting_keys { + key.post(&(proof, *super_key), ledger); + } + if let Some(asset_id) = self.asset_id { + ledger.update_public_balances(asset_id, self.sources, self.sinks, proof, super_key) + } } } diff --git a/manta-cli/Cargo.toml b/manta-cli/Cargo.toml index 4232b482d..b7e67ede4 100644 --- a/manta-cli/Cargo.toml +++ b/manta-cli/Cargo.toml @@ -29,9 +29,9 @@ maintenance = { status = "actively-developed" } std = [] [dependencies] -manta-accounting = { path = "../manta-accounting", default-features = false } -manta-crypto = { path = "../manta-crypto", default-features = false } -manta-pay = { path = "../manta-pay", default-features = false } +manta-accounting = { path = "../manta-accounting" } +manta-crypto = { path = "../manta-crypto" } +manta-pay = { path = "../manta-pay" } [dev-dependencies] manta-crypto = { path = "../manta-crypto", features = ["test"] } diff --git a/manta-codec/Cargo.toml b/manta-codec/Cargo.toml index a42dea7fc..9da4432fe 100644 --- a/manta-codec/Cargo.toml +++ b/manta-codec/Cargo.toml @@ -35,5 +35,5 @@ std = [] ark-serialize = { version = "0.3.0", default-features = false } ark-std = { version = "0.3.0", default-features = false } displaydoc = { version = "0.2.3", default-features = false } -manta-util = { path = "../manta-util", default-features = false } +manta-util = { path = "../manta-util" } scale-codec = { package = "parity-scale-codec", version = "2.3.1", default-features = false } diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index 0044cf8c4..2b3758778 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -39,5 +39,5 @@ test = [] [dependencies] derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } -manta-util = { path = "../manta-util", default-features = false } +manta-util = { path = "../manta-util" } rand_core = { version = "0.6.3", default-features = false } diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index ed4053aaf..68770cb9b 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -26,8 +26,8 @@ pub(crate) mod prelude { /// Commitment Scheme pub trait CommitmentScheme { - /// Commitment Input Buffer Type - type InputBuffer: Default; + /// Commitment Input Type + type Input: Default; /// Commitment Randomness Parameter Type type Randomness; @@ -41,8 +41,8 @@ pub trait CommitmentScheme { Builder::new(self) } - /// Commits the `input` buffer with the given `randomness` parameter. - fn commit(&self, input: Self::InputBuffer, randomness: &Self::Randomness) -> Self::Output; + /// Commits the `input` with the given `randomness` parameter. + fn commit(&self, input: Self::Input, randomness: &Self::Randomness) -> Self::Output; /// Commits with an empty input using the given `randomness` parameter. #[inline] @@ -50,8 +50,7 @@ pub trait CommitmentScheme { self.start().commit(randomness) } - /// Commits the single `input` by filling a new input buffer and then commiting with the given - /// `randomness` parameter. + /// Commits the single `input` value with the given `randomness` parameter. #[inline] fn commit_one<T>(&self, input: &T, randomness: &Self::Randomness) -> Self::Output where @@ -67,31 +66,31 @@ pub trait Input<T>: CommitmentScheme where T: ?Sized, { - /// Extends the input buffer with `input`. - fn extend(buffer: &mut Self::InputBuffer, input: &T); + /// Extends the `input` with the `next` element. + fn extend(input: &mut Self::Input, next: &T); } -impl<C, I> Input<I> for C +impl<C, T> Input<T> for C where C: CommitmentScheme + ?Sized, - C::InputBuffer: ConcatAccumulator<I::Item>, - I: Concat + ?Sized, + C::Input: ConcatAccumulator<T::Item>, + T: Concat + ?Sized, { #[inline] - fn extend(buffer: &mut C::InputBuffer, input: &I) { - input.concat(buffer) + fn extend(input: &mut Self::Input, next: &T) { + next.concat(input) } } /// Commitment Builder #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "C::InputBuffer: Clone"), - Copy(bound = "C::InputBuffer: Copy"), - Debug(bound = "C: Debug, C::InputBuffer: Debug"), - Eq(bound = "C: Eq, C::InputBuffer: Eq"), - Hash(bound = "C: Hash, C::InputBuffer: Hash"), - PartialEq(bound = "C: PartialEq, C::InputBuffer: PartialEq") + Clone(bound = "C::Input: Clone"), + Copy(bound = "C::Input: Copy"), + Debug(bound = "C: Debug, C::Input: Debug"), + Eq(bound = "C: Eq, C::Input: Eq"), + Hash(bound = "C: Hash, C::Input: Hash"), + PartialEq(bound = "C: PartialEq, C::Input: PartialEq") )] pub struct Builder<'c, C> where @@ -100,8 +99,8 @@ where /// Commitment Scheme commitment_scheme: &'c C, - /// Input Buffer - input_buffer: C::InputBuffer, + /// Commitment Input + input: C::Input, } impl<'c, C> Builder<'c, C> @@ -113,25 +112,25 @@ where pub fn new(commitment_scheme: &'c C) -> Self { Self { commitment_scheme, - input_buffer: Default::default(), + input: Default::default(), } } - /// Updates the builder with new `input`. + /// Updates the builder with the `next` input. #[inline] - pub fn update<T>(mut self, input: &T) -> Self + pub fn update<T>(mut self, next: &T) -> Self where T: ?Sized, C: Input<T>, { - C::extend(&mut self.input_buffer, input); + C::extend(&mut self.input, next); self } /// Commits to the input stored in the builder with the given `randomness`. #[inline] pub fn commit(self, randomness: &C::Randomness) -> C::Output { - self.commitment_scheme.commit(self.input_buffer, randomness) + self.commitment_scheme.commit(self.input, randomness) } } diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index 67d90aead..fa1e43c07 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -473,12 +473,15 @@ pub trait ProofSystem { /// Verifying Context Type type VerifyingContext; + /// Verification Input Type + type Input: ?Sized; + /// Proof Type type Proof; /// Verification Type /// - /// Usually this is just `bool`. + /// For non-recursive proof systems this is just `bool`. type Verification; /// Error Type @@ -509,11 +512,21 @@ pub trait ProofSystem { /// Verifies that a proof generated from this proof system is valid. fn verify( - context: &Self::VerifyingContext, + input: &Self::Input, proof: &Self::Proof, + context: &Self::VerifyingContext, ) -> Result<Self::Verification, Self::Error>; } +/// Proof System Input +pub trait Input<T>: ProofSystem +where + T: ?Sized, +{ + /// Extends the `input` with the `next` element. + fn extend(input: &mut Self::Input, next: &T); +} + /// Derived Allocation Mode #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Derived; @@ -907,6 +920,7 @@ pub mod types { pub type Usize<C> = Var<usize, C>; } +/* FIXME: Need to reconsider how to do this: /// Testing Framework #[cfg(feature = "test")] #[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] @@ -917,15 +931,16 @@ pub mod test { /// the `verifying_context`. #[inline] pub fn verify_constructed_proof<P, R>( + cs: P::ConstraintSystem, proving_context: &P::ProvingContext, verifying_context: &P::VerifyingContext, - cs: P::ConstraintSystem, rng: &mut R, ) -> Result<P::Verification, P::Error> where P: ProofSystem, R: CryptoRng + RngCore, { - P::verify(verifying_context, &P::prove(cs, proving_context, rng)?) + P::verify(&P::prove(cs, proving_context, rng)?, verifying_context) } } +*/ diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 4470ef239..3529b082d 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -60,9 +60,9 @@ blake2 = { version = "0.9.2", default-features = false } derivative = { version = "2.2.0", default-features = false } dusk-plonk = { version = "0.8.2", optional = true, default-features = false } generic-array = { version = "0.14.4", default-features = false } -manta-accounting = { path = "../manta-accounting", default-features = false } -manta-crypto = { path = "../manta-crypto", default-features = false } -manta-util = { path = "../manta-util", default-features = false } +manta-accounting = { path = "../manta-accounting" } +manta-crypto = { path = "../manta-crypto" } +manta-util = { path = "../manta-util" } rand_chacha = { version = "0.3.1", default-features = false } x25519-dalek = { git = "https://github.com/Manta-Network/x25519-dalek", default-features = false, features = ["u64_backend"] } diff --git a/manta-pay/src/accounting/mod.rs b/manta-pay/src/accounting/mod.rs index 196963849..7c833991d 100644 --- a/manta-pay/src/accounting/mod.rs +++ b/manta-pay/src/accounting/mod.rs @@ -21,4 +21,3 @@ pub mod identity; pub mod keys; pub mod ledger; pub mod transfer; -pub mod wallet; diff --git a/manta-pay/src/accounting/wallet.rs b/manta-pay/src/accounting/wallet.rs deleted file mode 100644 index 363f135e5..000000000 --- a/manta-pay/src/accounting/wallet.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Wallet Implementation diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs index e33807c74..5253ccc61 100644 --- a/manta-pay/src/crypto/commitment/pedersen.rs +++ b/manta-pay/src/crypto/commitment/pedersen.rs @@ -136,14 +136,14 @@ where W: PedersenWindow, C: ProjectiveCurve, { - type InputBuffer = Vec<u8>; + type Input = Vec<u8>; type Randomness = PedersenCommitmentRandomness<W, C>; type Output = PedersenCommitmentOutput<W, C>; #[inline] - fn commit(&self, input: Self::InputBuffer, randomness: &Self::Randomness) -> Self::Output { + fn commit(&self, input: Self::Input, randomness: &Self::Randomness) -> Self::Output { // FIXME: Make a note about the failure properties of commitment schemes. PedersenCommitmentOutput( ArkPedersenCommitment::<_, W>::commit(&self.0, &input, &randomness.0) @@ -321,14 +321,14 @@ pub mod constraint { GG: CurveVar<C, ConstraintField<C>>, for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, { - type InputBuffer = <PedersenCommitment<W, C> as CommitmentScheme>::InputBuffer; + type Input = <PedersenCommitment<W, C> as CommitmentScheme>::Input; type Randomness = <PedersenCommitment<W, C> as CommitmentScheme>::Randomness; type Output = PedersenCommitmentOutputWrapper<W, C, GG>; #[inline] - fn commit(&self, input: Self::InputBuffer, randomness: &Self::Randomness) -> Self::Output { + fn commit(&self, input: Self::Input, randomness: &Self::Randomness) -> Self::Output { self.0.commit(input, randomness).into() } } @@ -573,14 +573,14 @@ pub mod constraint { GG: CurveVar<C, ConstraintField<C>>, for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, { - type InputBuffer = InputBuffer<ConstraintField<C>>; + type Input = InputBuffer<ConstraintField<C>>; type Randomness = PedersenCommitmentRandomnessVar<W, C>; type Output = PedersenCommitmentOutputVar<W, C, GG>; #[inline] - fn commit(&self, input: Self::InputBuffer, randomness: &Self::Randomness) -> Self::Output { + fn commit(&self, input: Self::Input, randomness: &Self::Randomness) -> Self::Output { PedersenCommitmentOutputVar::new( CommGadget::<_, _, W>::commit(&self.0, &input, &randomness.0) .expect("Failure outcomes are not accepted."), diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs index c5568bef5..e5c901720 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs @@ -21,7 +21,6 @@ use crate::crypto::constraint::arkworks::{ constraint_system::SynthesisResult, ArkConstraintSystem, }; -use alloc::vec::Vec; use ark_crypto_primitives::SNARK; use ark_ec::PairingEngine; use ark_ff::Field; @@ -50,7 +49,9 @@ where type VerifyingContext = PreparedVerifyingKey<E>; - type Proof = (Vec<E::Fr>, Proof<E>); + type Input = [E::Fr]; + + type Proof = Proof<E>; type Verification = bool; @@ -90,26 +91,28 @@ where where R: CryptoRng + RngCore + ?Sized, { + /* TODO: let input = cs .cs .borrow() .ok_or(SynthesisError::MissingCS)? .instance_assignment .clone(); - let proof = ArkGroth16::prove( + */ + ArkGroth16::prove( context, ConstraintSynthesizerWrapper(cs), &mut SizedRng(rng), - )?; - Ok((input, proof)) + ) } #[inline] fn verify( - context: &Self::VerifyingContext, + input: &Self::Input, proof: &Self::Proof, + context: &Self::VerifyingContext, ) -> Result<Self::Verification, Self::Error> { - ArkGroth16::verify_with_processed_vk(context, &proof.0, &proof.1) + ArkGroth16::verify_with_processed_vk(context, input, proof) } } diff --git a/manta/Cargo.toml b/manta/Cargo.toml deleted file mode 100644 index e97cdfe68..000000000 --- a/manta/Cargo.toml +++ /dev/null @@ -1,46 +0,0 @@ -[package] -name = "manta" -edition = "2018" -version = "0.3.0" -authors = ["Manta Network <contact@manta.network>"] -readme = "README.md" -license-file = "LICENSE" -repository = "https://github.com/Manta-Network/manta-rs" -homepage = "https://github.com/Manta-Network" -documentation = "https://github.com/Manta-Network/manta-rs" -categories = [""] -keywords = [""] -description = "Manta Network main crate." -publish = false - -[package.metadata.docs.rs] -# To build locally: -# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --open -all-features = true -rustdoc-args = ["--cfg", "doc_cfg"] - -[badges] -is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } -is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } -maintenance = { status = "actively-developed" } - -[features] -# Default Features -default = [] - -# Standard Library -std = [] - -# Test Frameworks -test = ["manta-accounting/test", "manta-crypto/test"] - -[dependencies] -manta-accounting = { path = "../manta-accounting", default-features = false } -manta-codec = { path = "../manta-codec", default-features = false } -manta-crypto = { path = "../manta-crypto", default-features = false } -manta-pay = { path = "../manta-pay", default-features = false } -parity-scale-codec = { version = "2.2.0", default-features = false } - -[dev-dependencies] -manta-accounting = { path = "../manta-accounting", features = ["test"] } -manta-crypto = { path = "../manta-crypto", features = ["test"] } diff --git a/manta/LICENSE b/manta/LICENSE deleted file mode 120000 index ea5b60640..000000000 --- a/manta/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE \ No newline at end of file diff --git a/manta/README.md b/manta/README.md deleted file mode 100644 index 339b4bf22..000000000 --- a/manta/README.md +++ /dev/null @@ -1 +0,0 @@ -# manta diff --git a/manta/src/lib.rs b/src/lib.rs similarity index 86% rename from manta/src/lib.rs rename to src/lib.rs index 761ba2b2c..6414f9731 100644 --- a/manta/src/lib.rs +++ b/src/lib.rs @@ -14,10 +14,12 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! The Manta Network +//! Manta Network // TODO: Given the structure of `manta-pay` does this crate make sense anymore? Maybe only as // `accounting` + `crypto`? +// TODO: Reconsider how `manta-codec` is designed. Do we need it? Should it just be part of +// `manta-pay` or some `substrate`-specific interoperability crates? #![cfg_attr(not(any(feature = "std", test)), no_std)] #![cfg_attr(doc_cfg, feature(doc_cfg))] From 66812bd4b59d42d2cc417814dfd47642257adcd2 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 17 Oct 2021 14:01:06 -0400 Subject: [PATCH 099/275] feat: start prototyping manta-cli --- manta-cli/Cargo.toml | 7 ++-- manta-cli/src/command/mod.rs | 21 +++++++++++ manta-cli/src/lib.rs | 1 - manta-cli/src/main.rs | 73 ++++++++++++++++++++++++++++++++++++ 4 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 manta-cli/src/command/mod.rs create mode 100644 manta-cli/src/main.rs diff --git a/manta-cli/Cargo.toml b/manta-cli/Cargo.toml index b7e67ede4..a4e515fe6 100644 --- a/manta-cli/Cargo.toml +++ b/manta-cli/Cargo.toml @@ -24,11 +24,12 @@ is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } -[features] -# Standard Library -std = [] +[[bin]] +name = "manta" +path = "src/main.rs" [dependencies] +clap = "~3.0.0-beta.4" manta-accounting = { path = "../manta-accounting" } manta-crypto = { path = "../manta-crypto" } manta-pay = { path = "../manta-pay" } diff --git a/manta-cli/src/command/mod.rs b/manta-cli/src/command/mod.rs new file mode 100644 index 000000000..b39467897 --- /dev/null +++ b/manta-cli/src/command/mod.rs @@ -0,0 +1,21 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Manta CLI Commands + +#![cfg_attr(doc_cfg, feature(doc_cfg))] +#![forbid(rustdoc::broken_intra_doc_links)] +#![forbid(missing_docs)] diff --git a/manta-cli/src/lib.rs b/manta-cli/src/lib.rs index 648f32031..fdaa03b4b 100644 --- a/manta-cli/src/lib.rs +++ b/manta-cli/src/lib.rs @@ -16,7 +16,6 @@ //! Manta CLI -#![cfg_attr(not(any(feature = "std", test)), no_std)] #![cfg_attr(doc_cfg, feature(doc_cfg))] #![forbid(rustdoc::broken_intra_doc_links)] #![forbid(missing_docs)] diff --git a/manta-cli/src/main.rs b/manta-cli/src/main.rs new file mode 100644 index 000000000..eb2b91d86 --- /dev/null +++ b/manta-cli/src/main.rs @@ -0,0 +1,73 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Manta CLI + +// NOTE: In order to customize the help messages and formatting of the CLI, some objects are not +// documented and the documentation is instead written in macros or omitted entirely. +// TODO: In order to hook up a node instance here, we will want to eventually move this out of +// `manta-rs` into its own repo. + +use clap::{crate_version, AppSettings, Clap}; + +mod command; + +/// Manta Network's Command Line Interface +#[derive(Clap)] +#[clap( + name = "manta", + version = crate_version!(), + setting = AppSettings::PropagateVersion, + setting = AppSettings::AllArgsOverrideSelf, + after_help = "For more information about Manta, see 'https://github.com/Manta-Network'." +)] +struct Args { + /// Path to configuration file + #[clap(short, long, value_name = "PATH")] + config: Option<String>, + + /// Set the verbosity level + #[clap(short, long, parse(from_occurrences))] + verbose: u8, + + #[clap(subcommand)] + command: Command, +} + +#[derive(Clap)] +enum Command { + /// Run the testing suite and tools + Test { + /// Set the verbosity level + #[clap(short, long, parse(from_occurrences))] + verbose: u8, + }, + + /// Interact with a local wallet + Wallet { + /// Set the verbosity level + #[clap(short, long, parse(from_occurrences))] + verbose: u8, + }, +} + +fn main() { + let args = Args::parse(); + match args.command { + Command::Test { .. } => println!("Test ..."), + Command::Wallet { .. } => println!("Wallet ..."), + } +} From 2a4f552c3cdf24047727ef41ae6c402431efc6a5 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 17 Oct 2021 14:59:01 -0400 Subject: [PATCH 100/275] fix: move to new universal transfer protocol BREAKING CHANGES: updated `TransferLedger`, `TransferPost`, `TransferPostingKey`, `Transfer::generate_context`, `Transfer::generate_proof`, removed `ShapedProof` --- manta-accounting/src/transfer.rs | 331 ++++++++++++-------------- manta-accounting/src/wallet/ledger.rs | 2 +- manta-accounting/src/wallet/signer.rs | 3 +- manta-cli/Cargo.toml | 2 +- manta-pay/src/crypto/merkle_tree.rs | 5 +- 5 files changed, 160 insertions(+), 183 deletions(-) diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index e6c496498..8e00d3393 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -38,8 +38,8 @@ use manta_crypto::{ constraint::{ self, reflection::{HasAllocation, HasVariable}, - Allocation, Constant, ConstraintSystem as _, Derived, Equal, Input as ProofSystemInput, - ProofSystem, Public, PublicOrSecret, Secret, Variable, VariableSource, + Allocation, Constant, ConstraintSystem as _, Derived, Equal, ProofSystem, Public, + PublicOrSecret, Secret, Variable, VariableSource, }, ies::{EncryptedMessage, IntegratedEncryptionScheme}, rand::{CryptoRng, RngCore}, @@ -47,9 +47,9 @@ use manta_crypto::{ }; use manta_util::{create_seal, from_variant_impl, iter::mixed_chain, seal, Either}; -/// Returns `true` if the transfer with this shape would have no public side. +/// Returns `true` if the transfer with this shape would have no public participants. #[inline] -const fn has_no_public_side( +const fn has_no_public_participants( sources: usize, senders: usize, receivers: usize, @@ -59,13 +59,6 @@ const fn has_no_public_side( sources == 0 && sinks == 0 } -/// Returns `true` if the transfer with this shape requires a proof. -#[inline] -const fn requires_proof(sources: usize, senders: usize, receivers: usize, sinks: usize) -> bool { - let _ = (sources, receivers, sinks); - senders > 0 -} - /// [`Transfer`] Configuration pub trait Configuration: identity::constraint::Configuration<ConstraintSystem = ConstraintSystem<Self>> @@ -173,10 +166,21 @@ pub type Proof<C> = <<C as Configuration>::ProofSystem as ProofSystem>::Proof; /// Transfer Proof System Error Type pub type ProofSystemError<C> = <<C as Configuration>::ProofSystem as ProofSystem>::Error; +/// Transfer Source Posting Key Type +pub type SourcePostingKey<C, L> = <L as TransferLedger<C>>::ValidSourceBalance; + /// Transfer Ledger Super Posting Key Type pub type TransferLedgerSuperPostingKey<C, L> = <L as TransferLedger<C>>::SuperPostingKey; /// Transfer Ledger +/// +/// # Safety +/// +/// The [`TransferLedger`] interface describes the conditions for a valid ledger update but only for +/// the state that is described by a [`TransferPost`]. Independently of this trait, any ledger +/// implementation still needs to check that source and sink accounts are associated to a real +/// public address. However, checking public balances is done in the +/// [`check_source_balances`](Self::check_source_balances) method. pub trait TransferLedger<C>: SenderLedger< C, @@ -190,6 +194,14 @@ pub trait TransferLedger<C>: where C: Configuration, { + /// Valid [`AssetBalance`] for [`TransferPost`] source + /// + /// # Safety + /// + /// This type must be restricted so that it can only be constructed by this implementation of + /// [`TransferLedger`]. + type ValidSourceBalance; + /// Valid [`Proof`] Posting Key /// /// # Safety @@ -197,7 +209,8 @@ where /// This type must be restricted so that it can only be constructed by this implementation /// of [`TransferLedger`]. This is to prevent that [`SenderPostingKey::post`] and /// [`ReceiverPostingKey::post`] are called before [`SenderPost::validate`], - /// [`ReceiverPost::validate`], and [`is_valid`](Self::is_valid). + /// [`ReceiverPost::validate`], [`check_source_balances`](Self::check_source_balances), and + /// [`is_valid`](Self::is_valid). type ValidProof: Copy; /// Super Posting Key @@ -205,20 +218,22 @@ where /// Type that allows super-traits of [`TransferLedger`] to customize posting key behavior. type SuperPostingKey: Copy; + /// Checks that the balances associated to the source accounts are sufficient to withdraw the + /// amount given in `sources`. + fn check_source_balances( + &self, + sources: Vec<AssetBalance>, + ) -> Result<Vec<Self::ValidSourceBalance>, InsufficientPublicBalance>; + /// Checks that the transfer `proof` is valid. - /// - /// # Implementation Note - /// - /// This should always succeed on inputs that demonstrate that they do not require a - /// proof, by revealing their transaction shape. fn is_valid( &self, asset_id: Option<AssetId>, - sources: &[AssetBalance], + sources: &[Self::ValidSourceBalance], senders: &[SenderPostingKey<C, Self>], receivers: &[ReceiverPostingKey<C, Self>], sinks: &[AssetBalance], - proof: ShapedProof<C>, + proof: Proof<C>, ) -> Option<Self::ValidProof>; /// Updates the public balances in the ledger, finishing the transaction. @@ -231,7 +246,7 @@ where fn update_public_balances( &mut self, asset_id: AssetId, - sources: Vec<AssetBalance>, + sources: Vec<Self::ValidSourceBalance>, sinks: Vec<AssetBalance>, proof: Self::ValidProof, super_key: &TransferLedgerSuperPostingKey<C, Self>, @@ -266,10 +281,13 @@ impl DynamicShape { } } - /// Returns `true` whenever a transfer of the given shape `self` requires a validity proof. + /// Builds a new [`DynamicShape`] using the static [`Shape`] given by `S`. #[inline] - pub const fn requires_proof(&self) -> bool { - requires_proof(self.sources, self.senders, self.receivers, self.sinks) + pub fn from_static<S>() -> Self + where + S: Shape, + { + Self::new(S::SOURCES, S::SENDERS, S::RECEIVERS, S::SINKS) } /// Checks if `self` matches the static [`Shape`] given by `S`. @@ -292,102 +310,7 @@ where #[inline] fn from(shape: S) -> Self { let _ = shape; - Self { - sources: S::SOURCES, - senders: S::SENDERS, - receivers: S::RECEIVERS, - sinks: S::SINKS, - } - } -} - -/// Transfer Shape with Possible Validity [`Proof`] -pub enum ShapedProof<C> -where - C: Configuration, -{ - /// Shape with a Validity Proof - WithProof(ShapedProofEntry<C>), - - /// Shape with no Proof - NoProof(DynamicShape), -} - -impl<C> ShapedProof<C> -where - C: Configuration, -{ - /// Builds a new [`ShapedProof`] for the given `shape` and `proof`. - #[inline] - fn new_proof(shape: DynamicShape, proof: Proof<C>) -> Self { - Self::WithProof(ShapedProofEntry::new(shape, proof)) - } - - /// Returns the shape of the transfer which generated `self`. - #[inline] - pub fn shape(&self) -> &DynamicShape { - match self { - Self::WithProof(ShapedProofEntry { shape, .. }) => shape, - Self::NoProof(shape) => shape, - } - } - - /// Returns the validity proof for the transfer which generated `self`. - #[inline] - pub fn proof(&self) -> Option<&Proof<C>> { - match self { - Self::WithProof(ShapedProofEntry { proof, .. }) => Some(proof), - _ => None, - } - } -} - -impl<C> From<DynamicShape> for ShapedProof<C> -where - C: Configuration, -{ - #[inline] - fn from(shape: DynamicShape) -> Self { - Self::NoProof(shape) - } -} - -/// Entry for [`ShapedProof`] with a [`Proof`] -pub struct ShapedProofEntry<C> -where - C: Configuration, -{ - /// Transfer Shape - shape: DynamicShape, - - /// Validity Proof - proof: Proof<C>, -} - -impl<C> ShapedProofEntry<C> -where - C: Configuration, -{ - /// Builds a new [`ShapedProofEntry`] for the given `shape` and `proof`. - #[inline] - fn new(shape: DynamicShape, proof: Proof<C>) -> Self { - Self { shape, proof } - } - - /// Returns the validity `proof` along with its `shape`. - #[inline] - pub fn open(self) -> (DynamicShape, Proof<C>) { - (self.shape, self.proof) - } -} - -impl<C> From<ShapedProofEntry<C>> for (DynamicShape, Proof<C>) -where - C: Configuration, -{ - #[inline] - fn from(entry: ShapedProofEntry<C>) -> Self { - entry.open() + Self::from_static::<S>() } } @@ -413,7 +336,7 @@ impl<const SOURCES: usize, const SINKS: usize> PublicTransfer<SOURCES, SINKS> { sinks: AssetBalances<SINKS>, ) -> Self { Self::new_unchecked( - if has_no_public_side(SOURCES, 0, 0, SINKS) { + if has_no_public_participants(SOURCES, 0, 0, SINKS) { None } else { Some(asset_id) @@ -488,36 +411,41 @@ where C: Configuration, { /// Maximum Number of Senders - pub const MAXIMUM_SENDER_COUNT: usize = 32; + pub const MAXIMUM_SENDER_COUNT: usize = 16; /// Maximum Number of Receivers - pub const MAXIMUM_RECEIVER_COUNT: usize = 32; + pub const MAXIMUM_RECEIVER_COUNT: usize = 16; /// Builds a new [`SecretTransfer`]. #[inline] pub fn new(senders: [Sender<C>; SENDERS], receivers: [Receiver<C>; RECEIVERS]) -> Self { - Self::check_sender_side(); - Self::check_receiver_side(); - Self::check_size_overflow(); + Self::check_shape(); Self::new_unchecked(senders, receivers) } + /// Checks that the [`SecretTransfer`] has a valid shape. + #[inline] + fn check_shape() { + Self::check_sender_shape(); + Self::check_receiver_shape(); + Self::check_size_overflow(); + } + /// Checks that the sender side is not empty. #[inline] - fn check_sender_side() { + fn check_sender_shape() { assert_ne!(SENDERS, 0, "Not enough senders.") } /// Checks that the receiver side is not empty. #[inline] - fn check_receiver_side() { + fn check_receiver_shape() { assert_ne!(RECEIVERS, 0, "Not enough receivers.") } /// Checks that the number of senders and/or receivers does not exceed the allocation limit. #[inline] fn check_size_overflow() { - // FIXME: Should we have arrays of senders and receivers or use vectors? match ( SENDERS > Self::MAXIMUM_SENDER_COUNT, RECEIVERS > Self::MAXIMUM_RECEIVER_COUNT, @@ -638,34 +566,40 @@ where receivers: [Receiver<C>; RECEIVERS], sinks: AssetBalances<SINKS>, ) -> Self { - Self::check_sender_side(); - Self::check_receiver_side(); - SecretTransfer::<C, SENDERS, RECEIVERS>::check_size_overflow(); + Self::check_shape(); Self::new_unchecked(asset_id, sources, senders, receivers, sinks) } - /// Checks that the sender side is not empty. + /// Checks that the [`Transfer`] has a valid shape. #[inline] - fn check_sender_side() { + fn check_shape() { + Self::check_input_shape(); + Self::check_output_shape(); + SecretTransfer::<C, SENDERS, RECEIVERS>::check_size_overflow(); + } + + /// Checks that the input side is not empty. + #[inline] + fn check_input_shape() { assert_ne!( SOURCES + SENDERS, 0, - "Not enough participants on the sender side." + "Not enough participants on the input side." ) } - /// Checks that the receiver side is not empty. + /// Checks that the output side is not empty. #[inline] - fn check_receiver_side() { + fn check_output_shape() { assert_ne!( RECEIVERS + SINKS, 0, - "Not enough participants on the receiver side." + "Not enough participants on the output side." ) } - /// Builds a new [`Transfer`] without checking the number of participants on the sender and - /// receiver side. + /// Builds a new [`Transfer`] without checking the number of participants on the input and + /// output sides. #[inline] fn new_unchecked( asset_id: AssetId, @@ -720,10 +654,22 @@ where self.public.sink_sum() } + /// Returns the sum of the asset values of the sources and senders in this transfer. + #[inline] + pub fn input_sum(&self) -> AssetBalance { + self.source_sum() + self.sender_sum() + } + + /// Returns the sum of the asset values of the receivers and sinks in this transfer. + #[inline] + pub fn output_sum(&self) -> AssetBalance { + self.receiver_sum() + self.sink_sum() + } + /// Checks that the transaction is balanced. #[inline] pub fn is_balanced(&self) -> bool { - self.source_sum() + self.sender_sum() == self.receiver_sum() + self.sink_sum() + self.input_sum() == self.output_sum() } /// Generates the unknown variables for the validity proof. @@ -738,7 +684,7 @@ where C::CommitmentSchemeVar, C::UtxoSetVerifierVar, ) { - let base_asset_id = if has_no_public_side(SOURCES, SENDERS, RECEIVERS, SINKS) { + let base_asset_id = if has_no_public_participants(SOURCES, SENDERS, RECEIVERS, SINKS) { None } else { Some(C::AssetIdVar::new_unknown(cs, Public)) @@ -822,22 +768,16 @@ where cs.assert_eq(&sender_sum, &receiver_sum); } - /// Generates a verifier for this transfer shape. - /// - /// Returns `None` if proof generation does not apply for this kind of transfer. - #[allow(clippy::type_complexity)] // FIXME: We will have to refactor this at some point. + /// Generates a proving and verifying context for this transfer shape. #[inline] pub fn generate_context<R>( commitment_scheme: &C::CommitmentScheme, utxo_set_verifier: &UtxoSetVerifier<C>, rng: &mut R, - ) -> Option<Result<(ProvingContext<C>, VerifyingContext<C>), ProofSystemError<C>>> + ) -> Result<(ProvingContext<C>, VerifyingContext<C>), ProofSystemError<C>> where R: CryptoRng + RngCore + ?Sized, { - if !requires_proof(SOURCES, SENDERS, RECEIVERS, SINKS) { - return None; - } let mut cs = C::ProofSystem::for_unknown(); let (base_asset_id, participants, commitment_scheme, utxo_set_verifier) = Self::unknown_variables(commitment_scheme, utxo_set_verifier, &mut cs); @@ -848,13 +788,10 @@ where utxo_set_verifier, &mut cs, ); - Some(C::ProofSystem::generate_context(cs, rng)) + C::ProofSystem::generate_context(cs, rng) } /// Generates a validity proof for this transfer. - /// - /// Returns `Ok(ShapedProof::NoProof(_))` if proof generation does not apply for this kind - /// of transfer. #[inline] pub fn generate_proof<R>( &self, @@ -862,14 +799,10 @@ where utxo_set_verifier: &UtxoSetVerifier<C>, context: &ProvingContext<C>, rng: &mut R, - ) -> Result<ShapedProof<C>, ProofSystemError<C>> + ) -> Result<Proof<C>, ProofSystemError<C>> where R: CryptoRng + RngCore + ?Sized, { - let shape = DynamicShape::new(SOURCES, SENDERS, RECEIVERS, SINKS); - if !shape.requires_proof() { - return Ok(shape.into()); - } let mut cs = C::ProofSystem::for_known(); let (base_asset_id, participants, commitment_scheme, utxo_set_verifier) = self.known_variables(commitment_scheme, utxo_set_verifier, &mut cs); @@ -880,10 +813,7 @@ where utxo_set_verifier, &mut cs, ); - Ok(ShapedProof::new_proof( - shape, - C::ProofSystem::prove(cs, context, rng)?, - )) + C::ProofSystem::prove(cs, context, rng) } /// Converts `self` into its ledger post. @@ -1001,9 +931,31 @@ where } } +/// Insufficient Public Balance Error +/// +/// This `enum` is the error state of the [`TransferLedger::check_source_balances`] method. See its +/// documentation for more. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct InsufficientPublicBalance { + /// Index of the Public Address + pub index: usize, + + /// Current Balance + pub balance: AssetBalance, + + /// Amount Attempting to Withdraw + pub withdraw: AssetBalance, +} + /// Transfer Post Error +/// +/// This `enum` is the error state of the [`TransferPost::validate`] method. See its documentation +/// for more. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum TransferPostError { + /// Insufficient Public Balance + InsufficientPublicBalance(InsufficientPublicBalance), + /// Sender Post Error Sender(SenderPostError), @@ -1040,9 +992,7 @@ where sinks: Vec<AssetBalance>, /// Validity Proof - /// - /// This value is only inhabited by a proof when the transfer shape requires one. - validity_proof: ShapedProof<C>, + validity_proof: Proof<C>, } impl<C> TransferPost<C> @@ -1051,8 +1001,13 @@ where { /// Returns the shape of the transfer which generated this post. #[inline] - pub fn shape(&self) -> &DynamicShape { - self.validity_proof.shape() + pub fn shape(&self) -> DynamicShape { + DynamicShape::new( + self.sources.len(), + self.sender_posts.len(), + self.receiver_posts.len(), + self.sinks.len(), + ) } /// Validates `self` on the transfer `ledger`. @@ -1061,6 +1016,9 @@ where where L: TransferLedger<C>, { + let source_posting_keys = ledger + .check_source_balances(self.sources) + .map_err(TransferPostError::InsufficientPublicBalance)?; let sender_posting_keys = self .sender_posts .into_iter() @@ -1074,7 +1032,7 @@ where Ok(TransferPostingKey { validity_proof: match ledger.is_valid( self.asset_id, - &self.sources, + &source_posting_keys, &sender_posting_keys, &receiver_posting_keys, &self.sinks, @@ -1084,7 +1042,7 @@ where _ => return Err(TransferPostError::InvalidProof), }, asset_id: self.asset_id, - sources: self.sources, + source_posting_keys, sender_posting_keys, receiver_posting_keys, sinks: self.sinks, @@ -1101,8 +1059,8 @@ where /// Asset Id asset_id: Option<AssetId>, - /// Sources - sources: Vec<AssetBalance>, + /// Source Posting Keys + source_posting_keys: Vec<SourcePostingKey<C, L>>, /// Sender Posting Keys sender_posting_keys: Vec<SenderPostingKey<C, L>>, @@ -1122,6 +1080,17 @@ where C: Configuration, L: TransferLedger<C>, { + /// Returns the shape of the transfer which generated this posting key. + #[inline] + pub fn shape(&self) -> DynamicShape { + DynamicShape::new( + self.source_posting_keys.len(), + self.sender_posting_keys.len(), + self.receiver_posting_keys.len(), + self.sinks.len(), + ) + } + /// Posts `self` to the transfer `ledger`. /// /// # Safety @@ -1139,7 +1108,13 @@ where key.post(&(proof, *super_key), ledger); } if let Some(asset_id) = self.asset_id { - ledger.update_public_balances(asset_id, self.sources, self.sinks, proof, super_key) + ledger.update_public_balances( + asset_id, + self.source_posting_keys, + self.sinks, + proof, + super_key, + ) } } } @@ -1148,9 +1123,9 @@ create_seal! {} /// Transfer Shapes /// -/// This trait identifies a transfer shape, i.e. the number and type of participants on the sender -/// and receiver side of the transaction. This trait is sealed and can only be used with the -/// existing implementations. +/// This trait identifies a transfer shape, i.e. the number and type of participants on the input +/// and output sides of the transaction. This trait is sealed and can only be used with the +/// [existing canonical implementations](canonical). pub trait Shape: sealed::Sealed { /// Number of Sources const SOURCES: usize; @@ -1253,7 +1228,7 @@ pub mod canonical { /// zero value. /// /// This is particularly useful when constructing transactions accumulated from [`Transfer`] - /// objects and a zero slot on the sender side needs to be filled. + /// objects and a zero slot on the input side needs to be filled. #[inline] pub fn zero<R>( identity: Identity<C>, @@ -1731,9 +1706,7 @@ pub mod test { where R: CryptoRng + RngCore + ?Sized, { - Self::check_sender_side(); - Self::check_receiver_side(); - SecretTransfer::<C, SENDERS, RECEIVERS>::check_size_overflow(); + Self::check_shape(); let asset = distribution.asset; let mut input = value_distribution(SOURCES + SENDERS, asset.value, rng); diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs index 2e638abfa..81d5da960 100644 --- a/manta-accounting/src/wallet/ledger.rs +++ b/manta-accounting/src/wallet/ledger.rs @@ -16,7 +16,7 @@ //! Ledger Source -// TODO: Move to streams so we can process some of the data as it is incoming. +// TODO: Move to asynchronous streaming model so we can process some of the data as it is incoming. // TODO: Add non-atomic transactions. See similar comment in `crate::wallet::signer`. use crate::{ diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index ec2292fe0..cca495894 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -750,8 +750,9 @@ where where I: IntoIterator<Item = (Utxo<C>, EncryptedAsset<C>)>, { - // TODO: Capacity check. self.start_sync(sync_state); + + // FIXME: Do a capacity check on the current UTXO set. match self.utxo_set.len().checked_sub(starting_index) { Some(diff) => self.sync_inner(updates.into_iter().skip(diff)), _ => Err(Error::InconsistentSynchronization), diff --git a/manta-cli/Cargo.toml b/manta-cli/Cargo.toml index a4e515fe6..5107b858b 100644 --- a/manta-cli/Cargo.toml +++ b/manta-cli/Cargo.toml @@ -29,7 +29,7 @@ name = "manta" path = "src/main.rs" [dependencies] -clap = "~3.0.0-beta.4" +clap = "~3.0.0-beta.5" manta-accounting = { path = "../manta-accounting" } manta-crypto = { path = "../manta-crypto" } manta-pay = { path = "../manta-pay" } diff --git a/manta-pay/src/crypto/merkle_tree.rs b/manta-pay/src/crypto/merkle_tree.rs index a3668a71f..70017e588 100644 --- a/manta-pay/src/crypto/merkle_tree.rs +++ b/manta-pay/src/crypto/merkle_tree.rs @@ -25,10 +25,13 @@ use ark_ff::{to_bytes, ToBytes}; use core::marker::PhantomData; use manta_crypto::{ merkle_tree::{self, InnerHash, LeafHash}, - rand::{CryptoRng, RngCore, SizedRng, Standard}, + rand::{CryptoRng, RngCore, SizedRng}, }; use manta_util::{as_bytes, Concat}; +#[cfg(test)] +use manta_crypto::rand::Standard; + /// Arkworks Leaf Hash Converter #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] From 466b071148a0c48fe77792f3f40600083547cd2c Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 17 Oct 2021 16:48:43 -0400 Subject: [PATCH 101/275] feat: add public input generation for `Transfer` circuits --- manta-accounting/src/identity.rs | 50 ++++++- manta-accounting/src/transfer.rs | 76 +++++++++-- manta-crypto/src/constraint.rs | 4 +- manta-pay/src/accounting/identity.rs | 7 +- .../arkworks/proof_systems/groth16.rs | 128 ++++++++++++------ 5 files changed, 197 insertions(+), 68 deletions(-) diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 590c45e62..e9a94ae6c 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -1116,10 +1116,10 @@ where S: VerifiedSet<Item = Utxo<C>>, { /// Void Number - void_number: VoidNumber<C>, + pub(super) void_number: VoidNumber<C>, /// UTXO Membership Proof Public Part - utxo_membership_proof_public: S::Public, + pub(super) utxo_membership_proof_public: S::Public, } impl<C, S> SenderPost<C, S> @@ -1349,10 +1349,10 @@ where I: IntegratedEncryptionScheme<Plaintext = Asset>, { /// Unspent Transaction Output - utxo: Utxo<C>, + pub(super) utxo: Utxo<C>, /// Encrypted [`Asset`] - encrypted_asset: EncryptedMessage<I>, + pub(super) encrypted_asset: EncryptedMessage<I>, } impl<C, I> ReceiverPost<C, I> @@ -1420,8 +1420,8 @@ pub mod constraint { use manta_crypto::{ constraint::{ reflection::{HasAllocation, HasVariable}, - Allocation, Constant, ConstraintSystem, Derived, Equal, Public, PublicOrSecret, Secret, - Variable, + Allocation, Constant, ConstraintSystem, Derived, Equal, Input as ProofSystemInput, + ProofSystem, Public, PublicOrSecret, Secret, Variable, }, set::constraint::{MembershipProofVar, VerifierVariable}, }; @@ -1742,6 +1742,27 @@ pub mod constraint { type Mode = Derived; } + impl<C, S> SenderPost<C, S> + where + C: Configuration, + S: VerifiedSet<Item = Utxo<C>>, + C::ConstraintSystem: HasVariable<S::Public, Mode = Public>, + { + /// Extends proof public input with `self`. + #[inline] + pub fn extend_input<P>(&self, input: &mut P::Input) + where + P: ProofSystem<ConstraintSystem = C::ConstraintSystem> + + ProofSystemInput<VoidNumber<C>> + + ProofSystemInput<S::Public>, + { + // TODO: Add a "public part" trait that extracts the public part of `Sender` (using + // `SenderVar` to determine the types), then generate this method automatically. + P::extend(input, &self.void_number); + P::extend(input, &self.utxo_membership_proof_public); + } + } + /// Receiver Variable pub struct ReceiverVar<C, I> where @@ -1838,4 +1859,21 @@ pub mod constraint { type Variable = ReceiverVar<C, I>; type Mode = Derived; } + + impl<C, I> ReceiverPost<C, I> + where + C: Configuration, + I: IntegratedEncryptionScheme<Plaintext = Asset>, + { + /// Extends proof public input with `self`. + #[inline] + pub fn extend_input<P>(&self, input: &mut P::Input) + where + P: ProofSystem<ConstraintSystem = C::ConstraintSystem> + ProofSystemInput<Utxo<C>>, + { + // TODO: Add a "public part" trait that extracts the public part of `Receiver` (using + // `ReceiverVar` to determine the types), then generate this method automatically. + P::extend(input, &self.utxo); + } + } } diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 8e00d3393..4f794298b 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -19,17 +19,13 @@ // FIXME: Make sure that either (a) no empty transfer can be built, or (b) empty transfers work // properly i.e. do nothing. // TODO: See if we can get rid of the `Copy` restriction on `ValidProof` and `SuperPostingKey`. -// TODO: Add `generate_context`/`generate_proof` logic to `SecretTransfer`. -// TODO: Have a compile-time way to check if proof generation is used for a certain shape, -// so that the `generate_context`/`generate_proof` method can only exist on the right -// shape implementations, instead of failing at runtime with `None`. // FIXME: Remove `UtxoSet` dependence from `transfer`, really we only need `UtxoSetVerifier`. use crate::{ asset::{Asset, AssetBalance, AssetBalances, AssetId}, identity::{ self, constraint::UtxoVar, ReceiverLedger, ReceiverPostError, SenderLedger, - SenderPostError, Utxo, + SenderPostError, Utxo, VoidNumber, }, }; use alloc::vec::Vec; @@ -38,8 +34,8 @@ use manta_crypto::{ constraint::{ self, reflection::{HasAllocation, HasVariable}, - Allocation, Constant, ConstraintSystem as _, Derived, Equal, ProofSystem, Public, - PublicOrSecret, Secret, Variable, VariableSource, + Allocation, Constant, ConstraintSystem as _, Derived, Equal, Input as ProofSystemInput, + ProofSystem, Public, PublicOrSecret, Secret, Variable, VariableSource, }, ies::{EncryptedMessage, IntegratedEncryptionScheme}, rand::{CryptoRng, RngCore}, @@ -71,13 +67,12 @@ pub trait Configuration: + HasVariable<<Self::UtxoSet as VerifiedSet>::Secret, Mode = Secret>; /// Proof System - type ProofSystem: ProofSystem<ConstraintSystem = ConstraintSystem<Self>>; - /* TODO: - + ProofSystemInput<AssetId> - + ProofSystemInput<AssetBalance> - + ProofSystemInput<SenderPost<Self>> - + ProofSystemInput<ReceiverPost<Self>>; - */ + type ProofSystem: ProofSystem<ConstraintSystem = ConstraintSystem<Self>> + + ProofSystemInput<AssetId> + + ProofSystemInput<AssetBalance> + + ProofSystemInput<VoidNumber<Self>> + + ProofSystemInput<<Self::UtxoSet as VerifiedSet>::Public> + + ProofSystemInput<Utxo<Self>>; /// Asset Id Variable type AssetIdVar: Variable<ConstraintSystem<Self>, Mode = PublicOrSecret, Type = AssetId> @@ -163,6 +158,9 @@ pub type VerifyingContext<C> = <<C as Configuration>::ProofSystem as ProofSystem /// Transfer Proof Type pub type Proof<C> = <<C as Configuration>::ProofSystem as ProofSystem>::Proof; +/// Transfer Proof System Input Type +pub type ProofInput<C> = <<C as Configuration>::ProofSystem as ProofSystem>::Input; + /// Transfer Proof System Error Type pub type ProofSystemError<C> = <<C as Configuration>::ProofSystem as ProofSystem>::Error; @@ -1010,6 +1008,30 @@ where ) } + /// Generates the public input for the [`Transfer`] validation proof. + #[inline] + pub fn generate_proof_input(&self) -> ProofInput<C> { + // TODO: See comments in `crate::identity::constraint` about automatically deriving this + // method from possibly `TransferParticipantsVar`? + let mut input = Default::default(); + if let Some(asset_id) = self.asset_id { + C::ProofSystem::extend(&mut input, &asset_id); + } + self.sources + .iter() + .for_each(|source| C::ProofSystem::extend(&mut input, source)); + self.sender_posts + .iter() + .for_each(|post| post.extend_input::<C::ProofSystem>(&mut input)); + self.receiver_posts + .iter() + .for_each(|post| post.extend_input::<C::ProofSystem>(&mut input)); + self.sinks + .iter() + .for_each(|sink| C::ProofSystem::extend(&mut input, sink)); + input + } + /// Validates `self` on the transfer `ledger`. #[inline] pub fn validate<L>(self, ledger: &L) -> Result<TransferPostingKey<C, L>, TransferPostError> @@ -1731,4 +1753,30 @@ pub mod test { }) } } + + impl< + C, + const SOURCES: usize, + const SENDERS: usize, + const RECEIVERS: usize, + const SINKS: usize, + > Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS> + where + C: Configuration, + { + /// Samples proving and verifying contexts from `rng`. + #[inline] + pub fn sample_context<CD, VD, R>( + rng: &mut R, + ) -> Result<(ProvingContext<C>, VerifyingContext<C>), ProofSystemError<C>> + where + CD: Default, + VD: Default, + C::CommitmentScheme: Sample<CD>, + UtxoSetVerifier<C>: Sample<VD>, + R: CryptoRng + RngCore + ?Sized, + { + Self::generate_context(&rng.gen(), &rng.gen(), rng) + } + } } diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index fa1e43c07..dcd99c1f2 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -474,7 +474,7 @@ pub trait ProofSystem { type VerifyingContext; /// Verification Input Type - type Input: ?Sized; + type Input: Default; /// Proof Type type Proof; @@ -523,7 +523,7 @@ pub trait Input<T>: ProofSystem where T: ?Sized, { - /// Extends the `input` with the `next` element. + /// Extend the `input` with the `next` element. fn extend(input: &mut Self::Input, next: &T); } diff --git a/manta-pay/src/accounting/identity.rs b/manta-pay/src/accounting/identity.rs index ed9934782..461ea9081 100644 --- a/manta-pay/src/accounting/identity.rs +++ b/manta-pay/src/accounting/identity.rs @@ -20,11 +20,14 @@ use crate::{accounting::config::Configuration, crypto::merkle_tree::ConfigConver use manta_accounting::{identity, transfer}; use manta_crypto::merkle_tree::{self, full::Full}; +/// Asset Parameters +pub type AssetParameters = identity::AssetParameters<Configuration>; + /// Unspent Transaction Output pub type Utxo = identity::Utxo<Configuration>; -/// Asset Parameters -pub type AssetParameters = identity::AssetParameters<Configuration>; +/// Void Number +pub type VoidNumber = identity::VoidNumber<Configuration>; /// Sender Type pub type Sender = diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs index e5c901720..8f03be02d 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs @@ -18,20 +18,47 @@ // FIXME: Move these tests elsewhere since they are rather general. -use crate::crypto::constraint::arkworks::{ - constraint_system::SynthesisResult, ArkConstraintSystem, +use crate::{ + accounting::identity::{Root, Utxo, VoidNumber}, + crypto::constraint::arkworks::{constraint_system::SynthesisResult, ArkConstraintSystem}, }; +use alloc::vec::Vec; use ark_crypto_primitives::SNARK; use ark_ec::PairingEngine; use ark_ff::Field; use ark_groth16::{Groth16 as ArkGroth16, PreparedVerifyingKey, Proof, ProvingKey}; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError}; use core::marker::PhantomData; +use manta_accounting::asset::{AssetBalance, AssetId}; use manta_crypto::{ - constraint::ProofSystem, + constraint::{Input, ProofSystem}, rand::{CryptoRng, RngCore, SizedRng}, }; +/// Constraint Synthesizer Wrapper +struct ConstraintSynthesizerWrapper<F>(ArkConstraintSystem<F>) +where + F: Field; + +impl<F> ConstraintSynthesizer<F> for ConstraintSynthesizerWrapper<F> +where + F: Field, +{ + #[inline] + fn generate_constraints(self, cs: ConstraintSystemRef<F>) -> SynthesisResult { + let precomputed_cs = self + .0 + .cs + .into_inner() + .expect("We own this constraint system so we can consume it."); + let mut target_cs = cs + .borrow_mut() + .expect("This is given to us to mutate so it can't be borrowed by anyone else."); + *target_cs = precomputed_cs; + Ok(()) + } +} + /// Arkworks Groth 16 Proof System #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -49,7 +76,7 @@ where type VerifyingContext = PreparedVerifyingKey<E>; - type Input = [E::Fr]; + type Input = Vec<E::Fr>; type Proof = Proof<E>; @@ -91,7 +118,7 @@ where where R: CryptoRng + RngCore + ?Sized, { - /* TODO: + /* TODO[remove]: let input = cs .cs .borrow() @@ -116,27 +143,48 @@ where } } -/// Constraint Synthesizer Wrapper -struct ConstraintSynthesizerWrapper<F>(ArkConstraintSystem<F>) +impl<E> Input<AssetId> for Groth16<E> where - F: Field; + E: PairingEngine, +{ + fn extend(input: &mut Self::Input, next: &AssetId) { + todo!() + } +} -impl<F> ConstraintSynthesizer<F> for ConstraintSynthesizerWrapper<F> +impl<E> Input<AssetBalance> for Groth16<E> where - F: Field, + E: PairingEngine, { - #[inline] - fn generate_constraints(self, cs: ConstraintSystemRef<F>) -> SynthesisResult { - let precomputed_cs = self - .0 - .cs - .into_inner() - .expect("We own this constraint system so we can consume it."); - let mut target_cs = cs - .borrow_mut() - .expect("This is given to us to mutate so it can't be borrowed by anyone else."); - *target_cs = precomputed_cs; - Ok(()) + fn extend(input: &mut Self::Input, next: &AssetBalance) { + todo!() + } +} + +impl<E> Input<VoidNumber> for Groth16<E> +where + E: PairingEngine, +{ + fn extend(input: &mut Self::Input, next: &VoidNumber) { + todo!() + } +} + +impl<E> Input<Root> for Groth16<E> +where + E: PairingEngine, +{ + fn extend(input: &mut Self::Input, next: &Root) { + todo!() + } +} + +impl<E> Input<Utxo> for Groth16<E> +where + E: PairingEngine, +{ + fn extend(input: &mut Self::Input, next: &Utxo) { + todo!() } } @@ -153,33 +201,22 @@ mod test { }; use rand::thread_rng; - /// Tests the generation of proving/verifying keys for [`PrivateTransfer`]. + /// Tests the generation of proving/verifying keys for [`Mint`]. #[test] - fn generate_private_transfer_keys() { - let mut rng = thread_rng(); - PrivateTransfer::generate_context(&rng.gen(), &rng.gen(), &mut rng) - .unwrap() - .unwrap(); + fn sample_mint_keys() { + Mint::sample_context(&mut thread_rng()).unwrap(); } - /// Tests the generation of proving/verifying keys for [`Reclaim`]. + /// Tests the generation of proving/verifying keys for [`PrivateTransfer`]. #[test] - fn generate_reclaim_keys() { - let mut rng = thread_rng(); - Reclaim::generate_context(&rng.gen(), &rng.gen(), &mut rng) - .unwrap() - .unwrap(); + fn sample_private_transfer_keys() { + PrivateTransfer::sample_context(&mut thread_rng()).unwrap(); } - /// Tries to generate proving/verifying keys for [`Mint`] but this does not work because - /// [`Mint`] does not require a proof. + /// Tests the generation of proving/verifying keys for [`Reclaim`]. #[test] - #[should_panic] - fn generating_mint_keys_is_impossible() { - let mut rng = thread_rng(); - Mint::generate_context(&rng.gen(), &rng.gen(), &mut rng) - .unwrap() - .unwrap(); + fn sample_reclaim_keys() { + Reclaim::sample_context(&mut thread_rng()).unwrap(); } /// @@ -200,7 +237,6 @@ mod test { let (proving_key, verifying_key) = PrivateTransfer::generate_context(&commitment_scheme, utxo_set.verifier(), &mut rng) - .unwrap() .unwrap(); let post = private_transfer @@ -212,6 +248,10 @@ mod test { ) .unwrap(); - // TODO: let _ = post.validate(&ledger).unwrap(); + /* TODO: + let input = post.generate_proof_input(); + let proof = post.validity_proof; + assert!(Groth16::verify(input, proof)); + */ } } From dd867c8b9bb032f60d7378c567690dc431476367 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 17 Oct 2021 17:52:50 -0400 Subject: [PATCH 102/275] wip: finishing proof-checking tests --- manta-accounting/src/transfer.rs | 14 ++- .../arkworks/proof_systems/groth16.rs | 116 +++++++++++++++--- 2 files changed, 111 insertions(+), 19 deletions(-) diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 4f794298b..13afb484e 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -67,7 +67,7 @@ pub trait Configuration: + HasVariable<<Self::UtxoSet as VerifiedSet>::Secret, Mode = Secret>; /// Proof System - type ProofSystem: ProofSystem<ConstraintSystem = ConstraintSystem<Self>> + type ProofSystem: ProofSystem<ConstraintSystem = ConstraintSystem<Self>, Verification = bool> + ProofSystemInput<AssetId> + ProofSystemInput<AssetBalance> + ProofSystemInput<VoidNumber<Self>> @@ -1779,4 +1779,16 @@ pub mod test { Self::generate_context(&rng.gen(), &rng.gen(), rng) } } + + /// Asserts that `post` has a valid internal proof, verifying with the given `context`. + #[inline] + pub fn assert_valid_proof<C>(post: &TransferPost<C>, context: &VerifyingContext<C>) + where + C: Configuration, + { + assert!(matches!( + C::ProofSystem::verify(&post.generate_proof_input(), &post.validity_proof, context), + Ok(true) + )) + } } diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs index 8f03be02d..9615dbdba 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs @@ -147,8 +147,9 @@ impl<E> Input<AssetId> for Groth16<E> where E: PairingEngine, { + #[inline] fn extend(input: &mut Self::Input, next: &AssetId) { - todo!() + input.push(next.0.into()); } } @@ -156,8 +157,9 @@ impl<E> Input<AssetBalance> for Groth16<E> where E: PairingEngine, { + #[inline] fn extend(input: &mut Self::Input, next: &AssetBalance) { - todo!() + input.push(next.0.into()); } } @@ -165,8 +167,13 @@ impl<E> Input<VoidNumber> for Groth16<E> where E: PairingEngine, { + #[inline] fn extend(input: &mut Self::Input, next: &VoidNumber) { - todo!() + /* TODO: + use ark_ff::ToConstraintField; + input.append(&mut ToConstraintField::to_field_elements(VoidNumber::deserialize(next)) + .expect("Conversion to constraint field elements is not allowed to fail.")); + */ } } @@ -174,7 +181,13 @@ impl<E> Input<Root> for Groth16<E> where E: PairingEngine, { + #[inline] fn extend(input: &mut Self::Input, next: &Root) { + /* TODO: + use ark_ff::ToConstraintField; + input.append(&mut ToConstraintField::to_field_elements(Root::deserialize(next)) + .expect("Conversion to constraint field elements is not allowed to fail.")); + */ todo!() } } @@ -183,7 +196,13 @@ impl<E> Input<Utxo> for Groth16<E> where E: PairingEngine, { + #[inline] fn extend(input: &mut Self::Input, next: &Utxo) { + /* TODO: + use ark_ff::ToConstraintField; + input.append(&mut ToConstraintField::to_field_elements(Utxo::deserialize(next)) + .expect("Conversion to constraint field elements is not allowed to fail.")); + */ todo!() } } @@ -194,7 +213,7 @@ mod test { identity::UtxoSet, transfer::{Mint, PrivateTransfer, Reclaim}, }; - use manta_accounting::transfer::test::distribution; + use manta_accounting::transfer::test::{assert_valid_proof, distribution}; use manta_crypto::{ rand::{Rand, TrySample}, set::VerifiedSet, @@ -219,7 +238,39 @@ mod test { Reclaim::sample_context(&mut thread_rng()).unwrap(); } - /// + /// Tests the generation of a [`Mint`]. + #[test] + fn mint() { + let mut rng = thread_rng(); + let commitment_scheme = rng.gen(); + let mut utxo_set = UtxoSet::new(rng.gen()); + + let mint = Mint::try_sample( + distribution::Transfer { + commitment_scheme: &commitment_scheme, + utxo_set: &mut utxo_set, + }, + &mut rng, + ) + .unwrap(); + + let (proving_key, verifying_key) = + Mint::generate_context(&commitment_scheme, utxo_set.verifier(), &mut rng).unwrap(); + + assert_valid_proof( + &mint + .into_post( + &commitment_scheme, + utxo_set.verifier(), + &proving_key, + &mut rng, + ) + .unwrap(), + &verifying_key, + ); + } + + /// Tests the generation of a [`PrivateTransfer`]. #[test] fn private_transfer() { let mut rng = thread_rng(); @@ -239,19 +290,48 @@ mod test { PrivateTransfer::generate_context(&commitment_scheme, utxo_set.verifier(), &mut rng) .unwrap(); - let post = private_transfer - .into_post( - &commitment_scheme, - utxo_set.verifier(), - &proving_key, - &mut rng, - ) - .unwrap(); + assert_valid_proof( + &private_transfer + .into_post( + &commitment_scheme, + utxo_set.verifier(), + &proving_key, + &mut rng, + ) + .unwrap(), + &verifying_key, + ); + } - /* TODO: - let input = post.generate_proof_input(); - let proof = post.validity_proof; - assert!(Groth16::verify(input, proof)); - */ + /// Tests the generation of a [`Reclaim`]. + #[test] + fn reclaim() { + let mut rng = thread_rng(); + let commitment_scheme = rng.gen(); + let mut utxo_set = UtxoSet::new(rng.gen()); + + let reclaim = Reclaim::try_sample( + distribution::Transfer { + commitment_scheme: &commitment_scheme, + utxo_set: &mut utxo_set, + }, + &mut rng, + ) + .unwrap(); + + let (proving_key, verifying_key) = + Reclaim::generate_context(&commitment_scheme, utxo_set.verifier(), &mut rng).unwrap(); + + assert_valid_proof( + &reclaim + .into_post( + &commitment_scheme, + utxo_set.verifier(), + &proving_key, + &mut rng, + ) + .unwrap(), + &verifying_key, + ); } } From 9ee7702c0604178d41ebb3dbdedfb0ed9e9ca3ee Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 18 Oct 2021 00:37:22 -0400 Subject: [PATCH 103/275] wip: add tests for proof verification for all transfers --- manta-accounting/src/identity.rs | 1 + manta-accounting/src/transfer.rs | 123 +++++--- manta-pay/src/accounting/identity.rs | 3 + manta-pay/src/accounting/transfer.rs | 63 +++++ manta-pay/src/crypto/commitment/pedersen.rs | 41 ++- .../arkworks/proof_systems/groth16.rs | 163 +---------- manta-pay/src/crypto/merkle_tree.rs | 19 +- manta-pay/src/crypto/prf/blake2s.rs | 18 +- manta-util/src/iter/mixed_chain.rs | 262 ------------------ manta-util/src/iter/mod.rs | 2 - manta-util/src/lib.rs | 10 - 11 files changed, 227 insertions(+), 478 deletions(-) delete mode 100644 manta-util/src/iter/mixed_chain.rs diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index e9a94ae6c..80feab46a 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -16,6 +16,7 @@ //! Identities, Senders, and Receivers +// FIXME: Rename `AssetParameters`, since they are about identities not assets. // FIXME: Check the secret key APIs. // FIXME: Remove `UtxoSet` dependence from `identity`, really we only need `UtxoSetVerifier`. // TODO: Get rid of [`Spend`] and [`OpenSpend`] if possible. They don't seem to be that useful. diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 13afb484e..1983e7e61 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -18,8 +18,9 @@ // FIXME: Make sure that either (a) no empty transfer can be built, or (b) empty transfers work // properly i.e. do nothing. -// TODO: See if we can get rid of the `Copy` restriction on `ValidProof` and `SuperPostingKey`. // FIXME: Remove `UtxoSet` dependence from `transfer`, really we only need `UtxoSetVerifier`. +// TODO: See if we can get rid of the `Copy` restriction on `ValidProof` and `SuperPostingKey`. +// TODO: See if we can get rid of `PublicTransfer` and `SecretTransfer` and just use `Transfer`. use crate::{ asset::{Asset, AssetBalance, AssetBalances, AssetId}, @@ -41,7 +42,7 @@ use manta_crypto::{ rand::{CryptoRng, RngCore}, set::{constraint::VerifierVariable, VerifiedSet}, }; -use manta_util::{create_seal, from_variant_impl, iter::mixed_chain, seal, Either}; +use manta_util::{create_seal, from_variant_impl, seal}; /// Returns `true` if the transfer with this shape would have no public participants. #[inline] @@ -716,7 +717,7 @@ where ) } - /// Builds constraints for transfer validity proof/verifier. + /// Builds constraints for transfer validity proof. #[inline] fn build_constraints( base_asset_id: Option<C::AssetIdVar>, @@ -725,45 +726,36 @@ where utxo_set_verifier: C::UtxoSetVerifierVar, cs: &mut ConstraintSystem<C>, ) { - let mut sender_sum = C::AssetBalanceVar::from_default(cs, Secret); - let mut receiver_sum = C::AssetBalanceVar::from_default(cs, Secret); + let mut input_sum = C::AssetBalanceVar::from_default(cs, Secret); + let mut output_sum = C::AssetBalanceVar::from_default(cs, Secret); + let mut secret_asset_ids = Vec::new(); - participants - .sources - .into_iter() - .for_each(|source| sender_sum += source); + for source in participants.sources { + input_sum += source; + } - participants - .sinks - .into_iter() - .for_each(|sink| receiver_sum += sink); - - #[allow(clippy::needless_collect)] // NOTE: `cs` is being mutated, we need to collect. - let secret_asset_ids = mixed_chain( - participants.senders.into_iter(), - participants.receivers.into_iter(), - |c| match c { - Either::Left(sender) => { - let asset = - sender.get_well_formed_asset(cs, &commitment_scheme, &utxo_set_verifier); - sender_sum += asset.value; - asset.id - } - Either::Right(receiver) => { - let asset = receiver.get_well_formed_asset(cs, &commitment_scheme); - receiver_sum += asset.value; - asset.id - } - }, - ) - .collect::<Vec<_>>(); + for sender in participants.senders { + let asset = sender.get_well_formed_asset(cs, &commitment_scheme, &utxo_set_verifier); + input_sum += asset.value; + secret_asset_ids.push(asset.id); + } + + for receiver in participants.receivers { + let asset = receiver.get_well_formed_asset(cs, &commitment_scheme); + output_sum += asset.value; + secret_asset_ids.push(asset.id); + } + + for sink in participants.sinks { + output_sum += sink; + } + + cs.assert_eq(&input_sum, &output_sum); match base_asset_id { Some(asset_id) => cs.assert_all_eq_to_base(&asset_id, secret_asset_ids.iter()), _ => cs.assert_all_eq(secret_asset_ids.iter()), } - - cs.assert_eq(&sender_sum, &receiver_sum); } /// Generates a proving and verifying context for this transfer shape. @@ -1440,6 +1432,11 @@ pub mod test { { /// Tries to sample a [`super::SecretTransfer`] using custom sender and receiver asset /// totals. + /// + /// # Safety + /// + /// This method does not check the input/output size constraints found in the + /// [`super::SecretTransfer::check_shape`] method. #[inline] pub(super) fn try_sample_custom_totals< R, @@ -1471,6 +1468,11 @@ pub mod test { /// Tries to sample a [`super::SecretTransfer`] with custom sender and receiver asset /// value distributions. + /// + /// # Safety + /// + /// This method does not check the input/output size constraints found in the + /// [`super::SecretTransfer::check_shape`] method. #[inline] pub(super) fn try_sample_custom_distribution< R, @@ -1490,7 +1492,7 @@ pub mod test { where R: CryptoRng + RngCore + ?Sized, { - Ok(super::SecretTransfer::new( + Ok(super::SecretTransfer::new_unchecked( array_map(senders, |v| { let pre_sender = Identity::gen(rng).into_pre_sender(commitment_scheme, asset_id.with(v)); @@ -1515,6 +1517,7 @@ pub mod test { where R: CryptoRng + RngCore + ?Sized, { + super::SecretTransfer::<C, SENDERS, RECEIVERS>::check_shape(); Self::try_sample_custom_totals( self.asset.id, self.asset.value, @@ -1778,6 +1781,49 @@ pub mod test { { Self::generate_context(&rng.gen(), &rng.gen(), rng) } + + /// Samples a [`Transfer`] and checks whether its internal proof is valid relative to the + /// given `commitment_scheme` and `utxo_set`. + #[inline] + pub fn sample_and_check_proof<R>( + commitment_scheme: &C::CommitmentScheme, + utxo_set: &mut C::UtxoSet, + rng: &mut R, + ) -> Result<bool, ProofSystemError<C>> + where + C::SecretKey: Sample, + R: CryptoRng + RngCore + ?Sized, + { + let transfer = Self::try_sample( + distribution::Transfer { + commitment_scheme, + utxo_set, + }, + rng, + ) + .ok() + .expect("This test is not checking whether sampling works properly."); + + let (proving_key, verifying_key) = + Self::generate_context(commitment_scheme, utxo_set.verifier(), rng)?; + + has_valid_proof( + &transfer.into_post(commitment_scheme, utxo_set.verifier(), &proving_key, rng)?, + &verifying_key, + ) + } + } + + /// Checks if `post` has a valid internal proof, verifying with the given `context`. + #[inline] + pub fn has_valid_proof<C>( + post: &TransferPost<C>, + context: &VerifyingContext<C>, + ) -> Result<bool, ProofSystemError<C>> + where + C: Configuration, + { + C::ProofSystem::verify(&post.generate_proof_input(), &post.validity_proof, context) } /// Asserts that `post` has a valid internal proof, verifying with the given `context`. @@ -1786,9 +1832,6 @@ pub mod test { where C: Configuration, { - assert!(matches!( - C::ProofSystem::verify(&post.generate_proof_input(), &post.validity_proof, context), - Ok(true) - )) + assert!(matches!(has_valid_proof(post, context), Ok(true))) } } diff --git a/manta-pay/src/accounting/identity.rs b/manta-pay/src/accounting/identity.rs index 461ea9081..fc26e0ec7 100644 --- a/manta-pay/src/accounting/identity.rs +++ b/manta-pay/src/accounting/identity.rs @@ -29,6 +29,9 @@ pub type Utxo = identity::Utxo<Configuration>; /// Void Number pub type VoidNumber = identity::VoidNumber<Configuration>; +/// Identity Type +pub type Identity = identity::Identity<Configuration>; + /// Sender Type pub type Sender = identity::Sender<Configuration, <Configuration as transfer::Configuration>::UtxoSet>; diff --git a/manta-pay/src/accounting/transfer.rs b/manta-pay/src/accounting/transfer.rs index 4791a52c2..4fef57528 100644 --- a/manta-pay/src/accounting/transfer.rs +++ b/manta-pay/src/accounting/transfer.rs @@ -30,3 +30,66 @@ pub type Reclaim = canonical::Reclaim<Configuration>; /// Transfer Post Type pub type TransferPost = transfer::TransferPost<Configuration>; + +/// Testing Suite +#[cfg(test)] +mod test { + use crate::accounting::{ + identity::UtxoSet, + transfer::{Mint, PrivateTransfer, Reclaim}, + }; + use manta_crypto::rand::Rand; + use rand::thread_rng; + + /// Tests the generation of proving/verifying contexts for [`Mint`]. + #[test] + fn sample_mint_context() { + Mint::sample_context(&mut thread_rng()).unwrap(); + } + + /// Tests the generation of proving/verifying contexts for [`PrivateTransfer`]. + #[test] + fn sample_private_transfer_context() { + PrivateTransfer::sample_context(&mut thread_rng()).unwrap(); + } + + /// Tests the generation of proving/verifying contexts for [`Reclaim`]. + #[test] + fn sample_reclaim_context() { + Reclaim::sample_context(&mut thread_rng()).unwrap(); + } + + /// Tests the generation of a [`Mint`]. + #[test] + fn mint() { + let mut rng = thread_rng(); + assert!(matches!( + Mint::sample_and_check_proof(&rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng), + Ok(true) + )); + } + + /// Tests the generation of a [`PrivateTransfer`]. + #[test] + fn private_transfer() { + let mut rng = thread_rng(); + assert!(matches!( + PrivateTransfer::sample_and_check_proof( + &rng.gen(), + &mut UtxoSet::new(rng.gen()), + &mut rng + ), + Ok(true) + )); + } + + /// Tests the generation of a [`Reclaim`]. + #[test] + fn reclaim() { + let mut rng = thread_rng(); + assert!(matches!( + Reclaim::sample_and_check_proof(&rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng), + Ok(true) + )); + } +} diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs index 5253ccc61..61507f62e 100644 --- a/manta-pay/src/crypto/commitment/pedersen.rs +++ b/manta-pay/src/crypto/commitment/pedersen.rs @@ -28,7 +28,6 @@ use manta_crypto::{ use manta_util::{Concat, ConcatAccumulator}; /// Pedersen Window Parameters Trait -// TODO: Remove this comment once `arkworks` writes their own. pub use ark_crypto_primitives::commitment::pedersen::Window as PedersenWindow; pub use ark_ec::ProjectiveCurve; @@ -144,7 +143,6 @@ where #[inline] fn commit(&self, input: Self::Input, randomness: &Self::Randomness) -> Self::Output { - // FIXME: Make a note about the failure properties of commitment schemes. PedersenCommitmentOutput( ArkPedersenCommitment::<_, W>::commit(&self.0, &input, &randomness.0) .expect("Failure outcomes are not accepted."), @@ -178,7 +176,7 @@ pub mod constraint { commitment::pedersen::constraints::{CommGadget, ParametersVar, RandomnessVar}, CommitmentGadget, }; - use ark_ff::Field; + use ark_ff::{Field, ToConstraintField}; use ark_r1cs_std::{ alloc::AllocVar, groups::{CurveVar, GroupOpsBounds}, @@ -517,6 +515,43 @@ pub mod constraint { } } + impl<W, C> PedersenCommitmentOutput<W, C> + where + W: PedersenWindow, + C: ProjectiveCurve, + { + /// Extends the `input` vector by constraint field elements that make up `self`. + #[inline] + pub fn extend_input(&self, input: &mut Vec<ConstraintField<C>>) + where + C::Affine: ToConstraintField<ConstraintField<C>>, + { + input.append( + &mut self + .0 + .to_field_elements() + .expect("Conversion to constraint field elements is not allowed to fail."), + ); + } + } + + impl<W, C, GG> PedersenCommitmentOutputWrapper<W, C, GG> + where + W: PedersenWindow, + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, + { + /// Extends the `input` vector by constraint field elements that make up `self`. + #[inline] + pub fn extend_input(&self, input: &mut Vec<ConstraintField<C>>) + where + C::Affine: ToConstraintField<ConstraintField<C>>, + { + self.0.extend_input(input) + } + } + /// Pedersen Commitment Scheme Variable #[derive(derivative::Derivative)] #[derivative(Clone(bound = ""))] diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs index 9615dbdba..91fdbd6bc 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs @@ -16,8 +16,6 @@ //! Arkworks Groth16 Implementation -// FIXME: Move these tests elsewhere since they are rather general. - use crate::{ accounting::identity::{Root, Utxo, VoidNumber}, crypto::constraint::arkworks::{constraint_system::SynthesisResult, ArkConstraintSystem}, @@ -118,14 +116,6 @@ where where R: CryptoRng + RngCore + ?Sized, { - /* TODO[remove]: - let input = cs - .cs - .borrow() - .ok_or(SynthesisError::MissingCS)? - .instance_assignment - .clone(); - */ ArkGroth16::prove( context, ConstraintSynthesizerWrapper(cs), @@ -169,169 +159,26 @@ where { #[inline] fn extend(input: &mut Self::Input, next: &VoidNumber) { - /* TODO: - use ark_ff::ToConstraintField; - input.append(&mut ToConstraintField::to_field_elements(VoidNumber::deserialize(next)) - .expect("Conversion to constraint field elements is not allowed to fail.")); - */ + next.extend_input(input) } } impl<E> Input<Root> for Groth16<E> where - E: PairingEngine, + E: PairingEngine<Fr = ark_ff::Fp256<ark_bls12_381::FrParameters>>, { #[inline] fn extend(input: &mut Self::Input, next: &Root) { - /* TODO: - use ark_ff::ToConstraintField; - input.append(&mut ToConstraintField::to_field_elements(Root::deserialize(next)) - .expect("Conversion to constraint field elements is not allowed to fail.")); - */ - todo!() + crate::crypto::merkle_tree::constraint::root_extend_input(next, input) } } impl<E> Input<Utxo> for Groth16<E> where - E: PairingEngine, + E: PairingEngine<Fr = ark_ff::Fp256<ark_bls12_381::FrParameters>>, { #[inline] fn extend(input: &mut Self::Input, next: &Utxo) { - /* TODO: - use ark_ff::ToConstraintField; - input.append(&mut ToConstraintField::to_field_elements(Utxo::deserialize(next)) - .expect("Conversion to constraint field elements is not allowed to fail.")); - */ - todo!() - } -} - -#[cfg(test)] -mod test { - use crate::accounting::{ - identity::UtxoSet, - transfer::{Mint, PrivateTransfer, Reclaim}, - }; - use manta_accounting::transfer::test::{assert_valid_proof, distribution}; - use manta_crypto::{ - rand::{Rand, TrySample}, - set::VerifiedSet, - }; - use rand::thread_rng; - - /// Tests the generation of proving/verifying keys for [`Mint`]. - #[test] - fn sample_mint_keys() { - Mint::sample_context(&mut thread_rng()).unwrap(); - } - - /// Tests the generation of proving/verifying keys for [`PrivateTransfer`]. - #[test] - fn sample_private_transfer_keys() { - PrivateTransfer::sample_context(&mut thread_rng()).unwrap(); - } - - /// Tests the generation of proving/verifying keys for [`Reclaim`]. - #[test] - fn sample_reclaim_keys() { - Reclaim::sample_context(&mut thread_rng()).unwrap(); - } - - /// Tests the generation of a [`Mint`]. - #[test] - fn mint() { - let mut rng = thread_rng(); - let commitment_scheme = rng.gen(); - let mut utxo_set = UtxoSet::new(rng.gen()); - - let mint = Mint::try_sample( - distribution::Transfer { - commitment_scheme: &commitment_scheme, - utxo_set: &mut utxo_set, - }, - &mut rng, - ) - .unwrap(); - - let (proving_key, verifying_key) = - Mint::generate_context(&commitment_scheme, utxo_set.verifier(), &mut rng).unwrap(); - - assert_valid_proof( - &mint - .into_post( - &commitment_scheme, - utxo_set.verifier(), - &proving_key, - &mut rng, - ) - .unwrap(), - &verifying_key, - ); - } - - /// Tests the generation of a [`PrivateTransfer`]. - #[test] - fn private_transfer() { - let mut rng = thread_rng(); - let commitment_scheme = rng.gen(); - let mut utxo_set = UtxoSet::new(rng.gen()); - - let private_transfer = PrivateTransfer::try_sample( - distribution::Transfer { - commitment_scheme: &commitment_scheme, - utxo_set: &mut utxo_set, - }, - &mut rng, - ) - .unwrap(); - - let (proving_key, verifying_key) = - PrivateTransfer::generate_context(&commitment_scheme, utxo_set.verifier(), &mut rng) - .unwrap(); - - assert_valid_proof( - &private_transfer - .into_post( - &commitment_scheme, - utxo_set.verifier(), - &proving_key, - &mut rng, - ) - .unwrap(), - &verifying_key, - ); - } - - /// Tests the generation of a [`Reclaim`]. - #[test] - fn reclaim() { - let mut rng = thread_rng(); - let commitment_scheme = rng.gen(); - let mut utxo_set = UtxoSet::new(rng.gen()); - - let reclaim = Reclaim::try_sample( - distribution::Transfer { - commitment_scheme: &commitment_scheme, - utxo_set: &mut utxo_set, - }, - &mut rng, - ) - .unwrap(); - - let (proving_key, verifying_key) = - Reclaim::generate_context(&commitment_scheme, utxo_set.verifier(), &mut rng).unwrap(); - - assert_valid_proof( - &reclaim - .into_post( - &commitment_scheme, - utxo_set.verifier(), - &proving_key, - &mut rng, - ) - .unwrap(), - &verifying_key, - ); + next.extend_input(input) } } diff --git a/manta-pay/src/crypto/merkle_tree.rs b/manta-pay/src/crypto/merkle_tree.rs index 70017e588..b5008a03a 100644 --- a/manta-pay/src/crypto/merkle_tree.rs +++ b/manta-pay/src/crypto/merkle_tree.rs @@ -253,7 +253,7 @@ pub mod constraint { crh::constraints::{CRHGadget, TwoToOneCRHGadget}, merkle_tree::{constraints::PathVar as ArkPathVar, Path as ArkPath}, }; - use ark_ff::Field; + use ark_ff::{Field, ToConstraintField}; use ark_r1cs_std::{alloc::AllocVar, boolean::Boolean, eq::EqGadget, uint8::UInt8}; use ark_relations::ns; use manta_crypto::{ @@ -440,6 +440,23 @@ pub mod constraint { type Mode = Public; } + /// Extends the `input` vector by constraint field elements that make up `root`. + #[inline] + pub fn root_extend_input<C>( + root: &Root<ConfigConverter<C>>, + input: &mut Vec<ConstraintField<C>>, + ) where + C: Configuration, + RootInnerType<C>: ToConstraintField<ConstraintField<C>>, + { + input.append( + &mut root + .0 + .to_field_elements() + .expect("Conversion to constraint field elements is not allowed to fail."), + ); + } + /// Merkle Tree Path Inner Type type PathInnerType<C> = ArkPath<ConfigConverter<C>>; diff --git a/manta-pay/src/crypto/prf/blake2s.rs b/manta-pay/src/crypto/prf/blake2s.rs index 7bd0c028e..0eb52e428 100644 --- a/manta-pay/src/crypto/prf/blake2s.rs +++ b/manta-pay/src/crypto/prf/blake2s.rs @@ -16,6 +16,7 @@ //! Blake2s PRF Implementation +use alloc::vec::Vec; use ark_crypto_primitives::prf::{Blake2s as ArkBlake2s, PRF}; use manta_crypto::{ rand::{CryptoRng, Rand, RngCore, Sample, Standard}, @@ -139,7 +140,7 @@ pub mod constraint { use ark_crypto_primitives::{ prf::blake2s::constraints::Blake2sGadget as ArkBlake2sVar, PRFGadget, }; - use ark_ff::PrimeField; + use ark_ff::{PrimeField, ToConstraintField}; use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget, uint8::UInt8, ToBytesGadget}; use ark_relations::ns; use core::marker::PhantomData; @@ -334,6 +335,20 @@ pub mod constraint { } } + impl Blake2sOutput { + /// Extends the `input` vector by constraint field elements that make up `self`. + #[inline] + pub fn extend_input<F>(&self, input: &mut Vec<F>) + where + F: PrimeField, + { + input.append( + &mut ToConstraintField::to_field_elements(&self.0) + .expect("Conversion to constraint field elements is not allowed to fail."), + ); + } + } + /// Blake2s Pseudorandom Function Family Variable #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -377,7 +392,6 @@ pub mod constraint { #[inline] fn evaluate(seed: &Self::Seed, input: &Self::Input) -> Self::Output { - // FIXME: Make a note about the failure properties of PRFs. Blake2sOutputVar( ArkBlake2sVar::evaluate(seed.0.as_ref(), input.0.as_ref()) .expect("Failure outcomes are not accepted."), diff --git a/manta-util/src/iter/mixed_chain.rs b/manta-util/src/iter/mixed_chain.rs deleted file mode 100644 index ace57a273..000000000 --- a/manta-util/src/iter/mixed_chain.rs +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Mixed Chain Iterator - -// TODO: Make extract its own public trait to see if we can get some more combinators out of this. -// FIXME: Implement `Debug` for `MixedChain`. - -use crate::Either; -use core::iter::FusedIterator; - -trait Extract<I> -where - I: Iterator, -{ - fn extract(iter: &mut I) -> Option<I::Item>; -} - -struct Next; - -impl<I> Extract<I> for Next -where - I: Iterator, -{ - #[inline] - fn extract(iter: &mut I) -> Option<I::Item> { - iter.next() - } -} - -struct NextBack; - -impl<I> Extract<I> for NextBack -where - I: DoubleEndedIterator, -{ - #[inline] - fn extract(iter: &mut I) -> Option<I::Item> { - iter.next_back() - } -} - -/// An iterator that links two iterators together, in a chain, mapping each iterator so that they -/// have a common type when chained. -#[derive(Clone)] -#[must_use = "iterators are lazy and do nothing unless consumed"] -pub struct MixedChain<A, B, F> { - // NOTE: See the standard library implementation of `Chain` for an explanation on the - // fusing technique used here. - a: Option<A>, - b: Option<B>, - f: F, -} - -impl<A, B, F> MixedChain<A, B, F> { - /// Builds a new [`MixedChain`] iterator. - #[inline] - fn new(a: A, b: B, f: F) -> Self { - Self { - a: Some(a), - b: Some(b), - f, - } - } - - #[inline] - fn extract<T, E>(&mut self) -> Option<T> - where - A: Iterator, - B: Iterator, - F: FnMut(Either<A::Item, B::Item>) -> T, - E: Extract<A> + Extract<B>, - { - let maybe_item = match self.a { - Some(ref mut iter) => match left_map(E::extract(iter), &mut self.f) { - None => { - self.a = None; - None - } - item => item, - }, - _ => None, - }; - match maybe_item { - None => match self.b { - Some(ref mut iter) => right_map(E::extract(iter), &mut self.f), - _ => None, - }, - item => item, - } - } -} - -#[inline] -fn left<F, L, R, T>(item: L, mut f: F) -> T -where - F: FnMut(Either<L, R>) -> T, -{ - f(Either::Left(item)) -} - -#[inline] -fn left_map<F, L, R, T>(item: Option<L>, f: &mut F) -> Option<T> -where - F: FnMut(Either<L, R>) -> T, -{ - item.map(|i| left(i, f)) -} - -#[inline] -fn right<F, L, R, T>(item: R, mut f: F) -> T -where - F: FnMut(Either<L, R>) -> T, -{ - f(Either::Right(item)) -} - -#[inline] -fn right_map<F, L, R, T>(item: Option<R>, f: &mut F) -> Option<T> -where - F: FnMut(Either<L, R>) -> T, -{ - item.map(|i| right(i, f)) -} - -impl<A, B, F, T> Iterator for MixedChain<A, B, F> -where - A: Iterator, - B: Iterator, - F: FnMut(Either<A::Item, B::Item>) -> T, -{ - type Item = T; - - #[inline] - fn next(&mut self) -> Option<Self::Item> { - self.extract::<_, Next>() - } - - #[inline] - // TODO: #[rustc_inherit_overflow_checks] - fn count(self) -> usize { - let Self { a, b, mut f } = self; - let a_count = match a { - Some(a) => a.map(|a| left(a, &mut f)).count(), - None => 0, - }; - let b_count = match b { - Some(b) => b.map(|b| right(b, &mut f)).count(), - None => 0, - }; - a_count + b_count - } - - #[inline] - fn fold<Acc, FoldF>(self, mut acc: Acc, mut fold_f: FoldF) -> Acc - where - FoldF: FnMut(Acc, Self::Item) -> Acc, - { - let Self { a, b, mut f } = self; - if let Some(a) = a { - acc = a.fold(acc, |accum, a| fold_f(accum, left(a, &mut f))); - } - if let Some(b) = b { - acc = b.fold(acc, |accum, b| fold_f(accum, right(b, &mut f))); - } - acc - } - - #[inline] - fn last(self) -> Option<Self::Item> { - // NOTE: Must exhaust a before b. - let Self { a, b, mut f } = self; - let a_last = match a { - Some(a) => a.map(|a| left(a, &mut f)).last(), - None => None, - }; - let b_last = match b { - Some(b) => b.map(|b| right(b, &mut f)).last(), - None => None, - }; - b_last.or(a_last) - } - - #[inline] - fn size_hint(&self) -> (usize, Option<usize>) { - match (&self.a, &self.b) { - (Some(a), Some(b)) => { - let (a_lower, a_upper) = a.size_hint(); - let (b_lower, b_upper) = b.size_hint(); - let lower = a_lower.saturating_add(b_lower); - let upper = match (a_upper, b_upper) { - (Some(x), Some(y)) => x.checked_add(y), - _ => None, - }; - (lower, upper) - } - (Some(a), _) => a.size_hint(), - (_, Some(b)) => b.size_hint(), - _ => (0, Some(0)), - } - } -} - -impl<A, B, F, T> DoubleEndedIterator for MixedChain<A, B, F> -where - A: DoubleEndedIterator, - B: DoubleEndedIterator, - F: FnMut(Either<A::Item, B::Item>) -> T, -{ - #[inline] - fn next_back(&mut self) -> Option<Self::Item> { - self.extract::<_, NextBack>() - } - - #[inline] - fn rfold<Acc, FoldF>(self, mut acc: Acc, mut fold_f: FoldF) -> Acc - where - FoldF: FnMut(Acc, Self::Item) -> Acc, - { - let Self { a, b, mut f } = self; - if let Some(b) = b { - acc = b.rfold(acc, |accum, b| fold_f(accum, right(b, &mut f))); - } - if let Some(a) = a { - acc = a.rfold(acc, |accum, a| fold_f(accum, left(a, &mut f))); - } - acc - } -} - -// NOTE: *Both* must be fused to handle double-ended iterators. -impl<A, B, F, T> FusedIterator for MixedChain<A, B, F> -where - A: FusedIterator, - B: FusedIterator, - F: FnMut(Either<A::Item, B::Item>) -> T, -{ -} - -/// Creates a mixed chain iterator. -#[inline] -pub fn mixed_chain<A, B, F, T>(a: A, b: B, f: F) -> MixedChain<A::IntoIter, B::IntoIter, F> -where - A: IntoIterator, - B: IntoIterator, - F: FnMut(Either<A::Item, B::Item>) -> T, -{ - MixedChain::new(a.into_iter(), b.into_iter(), f) -} diff --git a/manta-util/src/iter/mod.rs b/manta-util/src/iter/mod.rs index 281e7a214..f420b74f7 100644 --- a/manta-util/src/iter/mod.rs +++ b/manta-util/src/iter/mod.rs @@ -17,10 +17,8 @@ //! Iteration Utilities mod chunk_by; -mod mixed_chain; pub use chunk_by::*; -pub use mixed_chain::*; /// Iterator Extensions pub trait IteratorExt: Iterator { diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index fea2c6ac9..efc7d2441 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -46,13 +46,3 @@ macro_rules! from_variant_impl { } }; } - -/// Either Type -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum Either<L, R> { - /// Left Variant - Left(L), - - /// Right Variant - Right(R), -} From 85b494c369b6ed882a6f5d64c242541be1f634c0 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 19 Oct 2021 03:25:05 -0400 Subject: [PATCH 104/275] fix: restructure asset id/value variables to match native computation --- manta-accounting/src/asset.rs | 100 ++++++++----- manta-cli/src/main.rs | 6 +- manta-pay/Cargo.toml | 1 + manta-pay/benches/main.rs | 21 +++ manta-pay/src/accounting/identity.rs | 12 ++ manta-pay/src/crypto/commitment/pedersen.rs | 6 +- .../constraint/arkworks/constraint_system.rs | 137 ++++++++++++------ .../arkworks/proof_systems/groth16.rs | 4 +- manta-pay/src/crypto/prf/blake2s.rs | 2 +- manta-util/src/concat.rs | 17 +++ 10 files changed, 223 insertions(+), 83 deletions(-) create mode 100644 manta-pay/benches/main.rs diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index e87e5f7ff..63890a795 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -87,16 +87,20 @@ impl AssetId { } } -impl<D> Sample<D> for AssetId -where - AssetIdType: Sample<D>, -{ +impl Concat for AssetId { + type Item = u8; + #[inline] - fn sample<R>(distribution: D, rng: &mut R) -> Self + fn concat<A>(&self, accumulator: &mut A) where - R: CryptoRng + RngCore + ?Sized, + A: ConcatAccumulator<Self::Item> + ?Sized, { - Self(rng.sample(distribution)) + accumulator.extend(&self.into_bytes()); + } + + #[inline] + fn size_hint(&self) -> Option<usize> { + Some(Self::SIZE) } } @@ -107,6 +111,19 @@ impl From<AssetId> for [u8; AssetId::SIZE] { } } +impl<D> Sample<D> for AssetId +where + AssetIdType: Sample<D>, +{ + #[inline] + fn sample<R>(distribution: D, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + Self(rng.sample(distribution)) + } +} + /// [`AssetBalance`] Base Type pub type AssetBalanceType = u128; @@ -190,16 +207,20 @@ impl AssetBalance { } } -impl<D> Sample<D> for AssetBalance -where - AssetBalanceType: Sample<D>, -{ +impl Concat for AssetBalance { + type Item = u8; + #[inline] - fn sample<R>(distribution: D, rng: &mut R) -> Self + fn concat<A>(&self, accumulator: &mut A) where - R: CryptoRng + RngCore + ?Sized, + A: ConcatAccumulator<Self::Item> + ?Sized, { - Self(rng.sample(distribution)) + accumulator.extend(&self.into_bytes()); + } + + #[inline] + fn size_hint(&self) -> Option<usize> { + Some(Self::SIZE) } } @@ -219,6 +240,19 @@ impl Mul<AssetBalance> for AssetBalanceType { } } +impl<D> Sample<D> for AssetBalance +where + AssetBalanceType: Sample<D>, +{ + #[inline] + fn sample<R>(distribution: D, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + Self(rng.sample(distribution)) + } +} + impl<'a> Sum<&'a AssetBalance> for AssetBalance { #[inline] fn sum<I>(iter: I) -> Self @@ -391,23 +425,6 @@ impl AddAssign<AssetBalance> for Asset { } } -impl Sub<AssetBalance> for Asset { - type Output = Self; - - #[inline] - fn sub(mut self, rhs: AssetBalance) -> Self::Output { - self -= rhs; - self - } -} - -impl SubAssign<AssetBalance> for Asset { - #[inline] - fn sub_assign(&mut self, rhs: AssetBalance) { - self.value -= rhs; - } -} - impl Concat for Asset { type Item = u8; @@ -416,8 +433,8 @@ impl Concat for Asset { where A: ConcatAccumulator<Self::Item> + ?Sized, { - accumulator.extend(&self.id.into_bytes()); - accumulator.extend(&self.value.into_bytes()); + self.id.concat(accumulator); + self.value.concat(accumulator); } #[inline] @@ -458,6 +475,23 @@ impl Sample for Asset { } } +impl Sub<AssetBalance> for Asset { + type Output = Self; + + #[inline] + fn sub(mut self, rhs: AssetBalance) -> Self::Output { + self -= rhs; + self + } +} + +impl SubAssign<AssetBalance> for Asset { + #[inline] + fn sub_assign(&mut self, rhs: AssetBalance) { + self.value -= rhs; + } +} + /// Asset Id Variable pub type AssetIdVar<C> = Var<AssetId, C>; diff --git a/manta-cli/src/main.rs b/manta-cli/src/main.rs index eb2b91d86..9fdfbf8bf 100644 --- a/manta-cli/src/main.rs +++ b/manta-cli/src/main.rs @@ -21,12 +21,12 @@ // TODO: In order to hook up a node instance here, we will want to eventually move this out of // `manta-rs` into its own repo. -use clap::{crate_version, AppSettings, Clap}; +use clap::{crate_version, AppSettings, Parser}; mod command; /// Manta Network's Command Line Interface -#[derive(Clap)] +#[derive(Parser)] #[clap( name = "manta", version = crate_version!(), @@ -47,7 +47,7 @@ struct Args { command: Command, } -#[derive(Clap)] +#[derive(Parser)] enum Command { /// Run the testing suite and tools Test { diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 3529b082d..121a23cb0 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -67,6 +67,7 @@ rand_chacha = { version = "0.3.1", default-features = false } x25519-dalek = { git = "https://github.com/Manta-Network/x25519-dalek", default-features = false, features = ["u64_backend"] } [dev-dependencies] +criterion = "0.3.5" manta-accounting = { path = "../manta-accounting", features = ["test"] } manta-crypto = { path = "../manta-crypto", features = ["test"] } rand = "0.8.4" diff --git a/manta-pay/benches/main.rs b/manta-pay/benches/main.rs new file mode 100644 index 000000000..ee04bfe0e --- /dev/null +++ b/manta-pay/benches/main.rs @@ -0,0 +1,21 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Benchmarks + +extern crate manta_pay; + +use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion}; diff --git a/manta-pay/src/accounting/identity.rs b/manta-pay/src/accounting/identity.rs index fc26e0ec7..74381f652 100644 --- a/manta-pay/src/accounting/identity.rs +++ b/manta-pay/src/accounting/identity.rs @@ -91,6 +91,18 @@ pub mod constraint { }; use manta_util::concatenate; + /// Sender Variable Type + pub type SenderVar = identity::constraint::SenderVar< + Configuration, + <Configuration as transfer::Configuration>::UtxoSet, + >; + + /// Receiver Variable Type + pub type ReceiverVar = identity::constraint::ReceiverVar< + Configuration, + <Configuration as transfer::Configuration>::IntegratedEncryptionScheme, + >; + /// UTXO Variable pub type UtxoVar = identity::constraint::UtxoVar<Configuration>; diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs index 61507f62e..ac7daa4e6 100644 --- a/manta-pay/src/crypto/commitment/pedersen.rs +++ b/manta-pay/src/crypto/commitment/pedersen.rs @@ -106,7 +106,7 @@ where self.0 .write(&mut buffer) .expect("This does not fail. See the implementation of `Write` for `Vec`."); - accumulator.extend(&buffer); + accumulator.extend(&buffer) } } @@ -439,7 +439,7 @@ pub mod constraint { where A: ConcatAccumulator<Self::Item> + ?Sized, { - accumulator.extend(&self.0.to_bytes().expect("This is not allowed to fail.")); + accumulator.extend(&self.0.to_bytes().expect("This is not allowed to fail.")) } } @@ -531,7 +531,7 @@ pub mod constraint { .0 .to_field_elements() .expect("Conversion to constraint field elements is not allowed to fail."), - ); + ) } } diff --git a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs index 0c0869e17..5810b48e3 100644 --- a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs @@ -19,15 +19,14 @@ use alloc::{vec, vec::Vec}; use ark_ff::{fields::Field, PrimeField}; use ark_r1cs_std::{ - alloc::AllocVar, bits::boolean::Boolean, eq::EqGadget, fields::fp::FpVar, uint8::UInt8, - R1CSVar, ToBytesGadget, + alloc::AllocVar, bits::boolean::Boolean, eq::EqGadget, fields::fp::FpVar, uint8::UInt8, R1CSVar, }; use ark_relations::{ns, r1cs as ark_r1cs}; use core::{borrow::Borrow, ops::AddAssign}; use manta_accounting::{AssetBalance, AssetId}; use manta_crypto::constraint::{ reflection::HasAllocation, types::Bool, Allocation, AllocationMode, ConstraintSystem, Equal, - Public, PublicOrSecret, Secret, Variable, + Public, PublicOrSecret, Secret, Variable, VariableSource, }; use manta_util::{Concat, ConcatAccumulator}; @@ -41,7 +40,7 @@ pub type SynthesisResult<T = ()> = Result<T, ark_r1cs::SynthesisError>; /// This does not work for all variable assignments! For some assignemnts, the variable inherits /// some structure from its input even though the input itself will not form part of the proving /// key and verifying key that we produce after compiling the constraint system. For those cases, -/// some mocking is required. +/// some mocking is required and this function can not be used directly. #[inline] pub fn empty<T>() -> SynthesisResult<T> { Err(ark_r1cs::SynthesisError::AssignmentMissing) @@ -138,7 +137,6 @@ where #[inline] fn assert(&mut self, b: Bool<Self>) { - // FIXME: Is there a more direct way to do assertions? b.enforce_equal(&Boolean::TRUE) .expect("This should never fail.") } @@ -154,27 +152,27 @@ where #[inline] fn new( - ps: &mut ArkConstraintSystem<F>, + cs: &mut ArkConstraintSystem<F>, allocation: Allocation<Self::Type, Self::Mode>, ) -> Self { match allocation { Allocation::Known(this, mode) => match mode { ArkAllocationMode::Constant => { - Self::new_constant(ns!(ps.cs, "boolean constant"), this) + Self::new_constant(ns!(cs.cs, "boolean constant"), this) } ArkAllocationMode::Public => { - Self::new_input(ns!(ps.cs, "boolean input"), full(this)) + Self::new_input(ns!(cs.cs, "boolean input"), full(this)) } ArkAllocationMode::Secret => { - Self::new_witness(ns!(ps.cs, "boolean witness"), full(this)) + Self::new_witness(ns!(cs.cs, "boolean witness"), full(this)) } }, Allocation::Unknown(mode) => match mode { PublicOrSecret::Public => { - Self::new_input(ns!(ps.cs, "boolean input"), empty::<bool>) + Self::new_input(ns!(cs.cs, "boolean input"), empty::<bool>) } PublicOrSecret::Secret => { - Self::new_witness(ns!(ps.cs, "boolean witness"), empty::<bool>) + Self::new_witness(ns!(cs.cs, "boolean witness"), empty::<bool>) } }, } @@ -195,8 +193,8 @@ where F: Field, { #[inline] - fn eq(ps: &mut ArkConstraintSystem<F>, lhs: &Self, rhs: &Self) -> Boolean<F> { - let _ = ps; + fn eq(cs: &mut ArkConstraintSystem<F>, lhs: &Self, rhs: &Self) -> Boolean<F> { + let _ = cs; lhs.is_eq(rhs) .expect("Equality checking is not allowed to fail.") } @@ -219,24 +217,24 @@ where #[inline] fn new( - ps: &mut ArkConstraintSystem<F>, + cs: &mut ArkConstraintSystem<F>, allocation: Allocation<Self::Type, Self::Mode>, ) -> Self { match allocation { Allocation::Known(this, ArkAllocationMode::Constant) => { - Self::new_constant(ns!(ps.cs, "prime field constant"), this.0) + Self::new_constant(ns!(cs.cs, "prime field constant"), this.0) } Allocation::Known(this, ArkAllocationMode::Public) => { - Self::new_input(ns!(ps.cs, "prime field input"), full(this.0)) + Self::new_input(ns!(cs.cs, "prime field input"), full(this.0)) } Allocation::Known(this, ArkAllocationMode::Secret) => { - Self::new_witness(ns!(ps.cs, "prime field witness"), full(this.0)) + Self::new_witness(ns!(cs.cs, "prime field witness"), full(this.0)) } Allocation::Unknown(PublicOrSecret::Public) => { - Self::new_input(ns!(ps.cs, "prime field input"), empty::<F>) + Self::new_input(ns!(cs.cs, "prime field input"), empty::<F>) } Allocation::Unknown(PublicOrSecret::Secret) => { - Self::new_witness(ns!(ps.cs, "prime field witness"), empty::<F>) + Self::new_witness(ns!(cs.cs, "prime field witness"), empty::<F>) } } .expect("Variable allocation is not allowed to fail.") @@ -286,8 +284,7 @@ where UInt8::new_witness_vec(ns!(cs, "byte array secret witness"), this) } Allocation::Unknown(PublicOrSecret::Public) => { - // FIXME: What goes here? - todo!() + UInt8::new_input_vec(ns!(cs, "byte array public input"), &[0; N]) } Allocation::Unknown(PublicOrSecret::Secret) => { UInt8::new_witness_vec(ns!(cs, "byte array secret witness"), &vec![None; N]) @@ -343,10 +340,10 @@ where #[inline] fn new( - ps: &mut ArkConstraintSystem<F>, + cs: &mut ArkConstraintSystem<F>, allocation: Allocation<Self::Type, Self::Mode>, ) -> Self { - Self::allocate(&ps.cs, allocation) + Self::allocate(&cs.cs, allocation) } } @@ -361,9 +358,27 @@ where /// Asset Id Variable #[derive(derivative::Derivative)] #[derivative(Clone, Debug)] -pub struct AssetIdVar<F>(FpVar<F>) +pub struct AssetIdVar<F> where - F: PrimeField; + F: PrimeField, +{ + /// Field Point + field_point: FpVar<F>, + + /// Byte Array + bytes: ByteArrayVar<F, { AssetId::SIZE }>, +} + +impl<F> AssetIdVar<F> +where + F: PrimeField, +{ + /// Builds a new [`AssetIdVar`] from `field_point` and `bytes`. + #[inline] + fn new(field_point: FpVar<F>, bytes: ByteArrayVar<F, { AssetId::SIZE }>) -> Self { + Self { field_point, bytes } + } +} impl<F> Concat for AssetIdVar<F> where @@ -376,7 +391,7 @@ where where A: ConcatAccumulator<Self::Item> + ?Sized, { - accumulator.extend(&self.0.to_bytes().expect("This is not allowed to fail.")) + self.bytes.concat(accumulator) } } @@ -390,10 +405,19 @@ where #[inline] fn new( - ps: &mut ArkConstraintSystem<F>, + cs: &mut ArkConstraintSystem<F>, allocation: Allocation<Self::Type, Self::Mode>, ) -> Self { - Self(allocation.map_allocate(ps, move |this| Fp(F::from(this.0)))) + match allocation { + Allocation::Known(this, mode) => Self::new( + Fp(F::from(this.0)).as_known(cs, mode), + this.into_bytes().as_known(cs, mode), + ), + Allocation::Unknown(mode) => Self::new( + Fp::as_unknown(cs, mode), + <[u8; AssetId::SIZE]>::as_unknown(cs, mode), + ), + } } } @@ -410,10 +434,11 @@ where F: PrimeField, { #[inline] - fn eq(ps: &mut ArkConstraintSystem<F>, lhs: &Self, rhs: &Self) -> Boolean<F> { - let _ = ps; - lhs.0 - .is_eq(&rhs.0) + fn eq(cs: &mut ArkConstraintSystem<F>, lhs: &Self, rhs: &Self) -> Boolean<F> { + // TODO: Is `field_point` or `bytes` faster? + let _ = cs; + lhs.field_point + .is_eq(&rhs.field_point) .expect("Equality checking is not allowed to fail.") } } @@ -421,9 +446,27 @@ where /// Asset Balance Variable #[derive(derivative::Derivative)] #[derivative(Clone, Debug)] -pub struct AssetBalanceVar<F>(FpVar<F>) +pub struct AssetBalanceVar<F> where - F: PrimeField; + F: PrimeField, +{ + /// Field Point + field_point: FpVar<F>, + + /// Byte Array + bytes: ByteArrayVar<F, { AssetBalance::SIZE }>, +} + +impl<F> AssetBalanceVar<F> +where + F: PrimeField, +{ + /// Builds a new [`AssetBalanceVar`] from `field_point` and `bytes`. + #[inline] + fn new(field_point: FpVar<F>, bytes: ByteArrayVar<F, { AssetBalance::SIZE }>) -> Self { + Self { field_point, bytes } + } +} impl<F> Concat for AssetBalanceVar<F> where @@ -436,7 +479,7 @@ where where A: ConcatAccumulator<Self::Item> + ?Sized, { - accumulator.extend(&self.0.to_bytes().expect("This is not allowed to fail.")) + self.bytes.concat(accumulator) } } @@ -450,10 +493,19 @@ where #[inline] fn new( - ps: &mut ArkConstraintSystem<F>, + cs: &mut ArkConstraintSystem<F>, allocation: Allocation<Self::Type, Self::Mode>, ) -> Self { - Self(allocation.map_allocate(ps, move |this| Fp(F::from(this.0)))) + match allocation { + Allocation::Known(this, mode) => Self::new( + Fp(F::from(this.0)).as_known(cs, mode), + this.into_bytes().as_known(cs, mode), + ), + Allocation::Unknown(mode) => Self::new( + Fp::as_unknown(cs, mode), + <[u8; AssetBalance::SIZE]>::as_unknown(cs, mode), + ), + } } } @@ -470,10 +522,11 @@ where F: PrimeField, { #[inline] - fn eq(ps: &mut ArkConstraintSystem<F>, lhs: &Self, rhs: &Self) -> Boolean<F> { - let _ = ps; - lhs.0 - .is_eq(&rhs.0) + fn eq(cs: &mut ArkConstraintSystem<F>, lhs: &Self, rhs: &Self) -> Boolean<F> { + // TODO: Is `field_point` or `bytes` faster? + let _ = cs; + lhs.field_point + .is_eq(&rhs.field_point) .expect("Equality checking is not allowed to fail.") } } @@ -484,6 +537,6 @@ where { #[inline] fn add_assign(&mut self, rhs: Self) { - self.0 += rhs.0 + self.field_point += rhs.field_point } } diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs index 91fdbd6bc..cf5cecae4 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs @@ -23,7 +23,7 @@ use crate::{ use alloc::vec::Vec; use ark_crypto_primitives::SNARK; use ark_ec::PairingEngine; -use ark_ff::Field; +use ark_ff::{Field, ToConstraintField}; use ark_groth16::{Groth16 as ArkGroth16, PreparedVerifyingKey, Proof, ProvingKey}; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError}; use core::marker::PhantomData; @@ -140,6 +140,7 @@ where #[inline] fn extend(input: &mut Self::Input, next: &AssetId) { input.push(next.0.into()); + input.append(&mut next.into_bytes().to_field_elements().unwrap()); } } @@ -150,6 +151,7 @@ where #[inline] fn extend(input: &mut Self::Input, next: &AssetBalance) { input.push(next.0.into()); + input.append(&mut next.into_bytes().to_field_elements().unwrap()); } } diff --git a/manta-pay/src/crypto/prf/blake2s.rs b/manta-pay/src/crypto/prf/blake2s.rs index 0eb52e428..b679ab583 100644 --- a/manta-pay/src/crypto/prf/blake2s.rs +++ b/manta-pay/src/crypto/prf/blake2s.rs @@ -30,7 +30,7 @@ pub struct Blake2s; /// Blake2s Pseudorandom Function Family Seed #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Blake2sSeed(pub(crate) <ArkBlake2s as PRF>::Seed); +pub struct Blake2sSeed(<ArkBlake2s as PRF>::Seed); impl AsMut<[u8]> for Blake2sSeed { #[inline] diff --git a/manta-util/src/concat.rs b/manta-util/src/concat.rs index bb6cc93e4..27002dda3 100644 --- a/manta-util/src/concat.rs +++ b/manta-util/src/concat.rs @@ -175,6 +175,23 @@ impl<T, const N: usize> Concat for [T; N] { } } +impl<T> Concat for Vec<T> { + type Item = T; + + #[inline] + fn concat<A>(&self, accumulator: &mut A) + where + A: ConcatAccumulator<T> + ?Sized, + { + accumulator.extend(self) + } + + #[inline] + fn size_hint(&self) -> Option<usize> { + Some(self.len()) + } +} + /// Concatenates `$item`s together by building a [`ConcatAccumulator`] and running /// [`Concat::concat`] over each `$item`. #[macro_export] From 5f2e720dec90f41c26e19cd3382004fbe1d03f87 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 19 Oct 2021 17:03:34 -0400 Subject: [PATCH 105/275] feat: add transfer circuit benchmarks and size measurements --- manta-accounting/src/asset.rs | 17 +- manta-accounting/src/fs.rs | 2 +- manta-accounting/src/identity.rs | 4 +- manta-accounting/src/keys.rs | 4 +- manta-accounting/src/transfer.rs | 87 ++++--- manta-accounting/src/wallet/signer.rs | 14 +- manta-accounting/src/wallet/state.rs | 6 +- manta-cli/Cargo.toml | 3 +- manta-crypto/src/commitment.rs | 4 +- manta-crypto/src/constraint.rs | 212 +++++++++++++++--- manta-crypto/src/ies.rs | 4 +- manta-crypto/src/merkle_tree/inner_tree.rs | 8 +- manta-crypto/src/merkle_tree/test.rs | 2 +- manta-crypto/src/merkle_tree/tree.rs | 4 +- manta-crypto/src/rand.rs | 10 +- manta-crypto/src/set.rs | 2 +- manta-pay/Cargo.toml | 8 + manta-pay/benches/main.rs | 160 ++++++++++++- manta-pay/src/accounting/identity.rs | 2 +- manta-pay/src/accounting/keys.rs | 5 +- manta-pay/src/accounting/transfer.rs | 21 +- manta-pay/src/crypto/commitment/pedersen.rs | 10 +- .../constraint/arkworks/constraint_system.rs | 66 +++--- .../arkworks/proof_systems/groth16.rs | 11 +- manta-pay/src/crypto/ies.rs | 1 + manta-pay/src/crypto/merkle_tree.rs | 9 +- manta-pay/src/crypto/prf/blake2s.rs | 10 +- manta-util/src/concat.rs | 20 +- 28 files changed, 536 insertions(+), 170 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 63890a795..6f806d60f 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -622,13 +622,8 @@ impl<const N: usize> TryFrom<[Asset; N]> for AssetCollection<N> { let mut base_id = None; let values = fallible_array_map(array, move |asset| { let result = match base_id { - Some(id) => { - if id == asset.id { - Ok(asset.value) - } else { - Err(counter) - } - } + Some(id) if id == asset.id => Ok(asset.value), + Some(_) => Err(counter), _ => { base_id = Some(asset.id); Ok(asset.value) @@ -681,7 +676,7 @@ pub trait AssetMap: Default { I: IntoIterator<Item = (Self::Key, AssetBalance)>, { iter.into_iter() - .for_each(move |(key, value)| self.insert(key, id.with(value))) + .for_each(move |(key, value)| self.insert(key, id.with(value))); } /// Inserts all of the assets in `iter` using a fixed `id` and zero value. @@ -691,7 +686,7 @@ pub trait AssetMap: Default { I: IntoIterator<Item = Self::Key>, { iter.into_iter() - .for_each(move |key| self.insert(key, Asset::zero(id))) + .for_each(move |key| self.insert(key, Asset::zero(id))); } /// Removes the `key` from the map. @@ -702,7 +697,7 @@ pub trait AssetMap: Default { where I: IntoIterator<Item = Self::Key>, { - iter.into_iter().for_each(move |key| self.remove(key)) + iter.into_iter().for_each(move |key| self.remove(key)); } } @@ -874,7 +869,7 @@ mod test { fn test_change_iterator() { let mut rng = thread_rng(); for _ in 0..0xFFF { - let amount = AssetBalance(rng.gen_range(0..0xFFFFFF)); + let amount = AssetBalance(rng.gen_range(0..0xFFFF_FFFF)); let n = rng.gen_range(1..0xFFFF); let change = amount.make_change(n).unwrap().collect::<Vec<_>>(); assert_eq!(n, change.len()); diff --git a/manta-accounting/src/fs.rs b/manta-accounting/src/fs.rs index 671fc674a..99f495d8c 100644 --- a/manta-accounting/src/fs.rs +++ b/manta-accounting/src/fs.rs @@ -359,7 +359,7 @@ pub mod cocoon { // Close the testing directory. dir.close() - .expect("Temporary directory should have closed.") + .expect("Temporary directory should have closed."); } } } diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 80feab46a..d17387890 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -1187,7 +1187,7 @@ where self.void_number, self.utxo_membership_proof_public, super_key, - ) + ); } } @@ -1411,7 +1411,7 @@ where /// Posts `self` to the receiver `ledger`. #[inline] pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) { - ledger.register(self.utxo, self.encrypted_asset, super_key) + ledger.register(self.utxo, self.encrypted_asset, super_key); } } diff --git a/manta-accounting/src/keys.rs b/manta-accounting/src/keys.rs index 3d53a330a..57ec37ea6 100644 --- a/manta-accounting/src/keys.rs +++ b/manta-accounting/src/keys.rs @@ -206,7 +206,7 @@ where /// Increments the internal `index`. #[inline] pub fn increment(&mut self) { - self.index.increment() + self.index.increment(); } /// Reduces `self` into an [`Index`] with [`KeyKind`] as the key kind. @@ -837,7 +837,7 @@ where #[inline] pub fn conditional_increment_external_range(&mut self, index: &D::Index) { if &self.external_indices.start == index { - self.external_indices.start.increment() + self.external_indices.start.increment(); } } diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 1983e7e61..5e962ff1c 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -433,13 +433,13 @@ where /// Checks that the sender side is not empty. #[inline] fn check_sender_shape() { - assert_ne!(SENDERS, 0, "Not enough senders.") + assert_ne!(SENDERS, 0, "Not enough senders."); } /// Checks that the receiver side is not empty. #[inline] fn check_receiver_shape() { - assert_ne!(RECEIVERS, 0, "Not enough receivers.") + assert_ne!(RECEIVERS, 0, "Not enough receivers."); } /// Checks that the number of senders and/or receivers does not exceed the allocation limit. @@ -584,7 +584,7 @@ where SOURCES + SENDERS, 0, "Not enough participants on the input side." - ) + ); } /// Checks that the output side is not empty. @@ -594,7 +594,7 @@ where RECEIVERS + SINKS, 0, "Not enough participants on the output side." - ) + ); } /// Builds a new [`Transfer`] without checking the number of participants on the input and @@ -758,16 +758,12 @@ where } } - /// Generates a proving and verifying context for this transfer shape. + /// Generates the constraint system for an unknown transfer. #[inline] - pub fn generate_context<R>( + pub fn unknown_constraints( commitment_scheme: &C::CommitmentScheme, utxo_set_verifier: &UtxoSetVerifier<C>, - rng: &mut R, - ) -> Result<(ProvingContext<C>, VerifyingContext<C>), ProofSystemError<C>> - where - R: CryptoRng + RngCore + ?Sized, - { + ) -> ConstraintSystem<C> { let mut cs = C::ProofSystem::for_unknown(); let (base_asset_id, participants, commitment_scheme, utxo_set_verifier) = Self::unknown_variables(commitment_scheme, utxo_set_verifier, &mut cs); @@ -778,21 +774,16 @@ where utxo_set_verifier, &mut cs, ); - C::ProofSystem::generate_context(cs, rng) + cs } - /// Generates a validity proof for this transfer. + /// Generates the constraint system for a known transfer. #[inline] - pub fn generate_proof<R>( + pub fn known_constraints( &self, commitment_scheme: &C::CommitmentScheme, utxo_set_verifier: &UtxoSetVerifier<C>, - context: &ProvingContext<C>, - rng: &mut R, - ) -> Result<Proof<C>, ProofSystemError<C>> - where - R: CryptoRng + RngCore + ?Sized, - { + ) -> ConstraintSystem<C> { let mut cs = C::ProofSystem::for_known(); let (base_asset_id, participants, commitment_scheme, utxo_set_verifier) = self.known_variables(commitment_scheme, utxo_set_verifier, &mut cs); @@ -803,7 +794,37 @@ where utxo_set_verifier, &mut cs, ); - C::ProofSystem::prove(cs, context, rng) + cs + } + + /// Generates a proving and verifying context for this transfer shape. + #[inline] + pub fn generate_context<R>( + commitment_scheme: &C::CommitmentScheme, + utxo_set_verifier: &UtxoSetVerifier<C>, + rng: &mut R, + ) -> Result<(ProvingContext<C>, VerifyingContext<C>), ProofSystemError<C>> + where + R: CryptoRng + RngCore + ?Sized, + { + Self::unknown_constraints(commitment_scheme, utxo_set_verifier) + .generate_context::<C::ProofSystem, _>(rng) + } + + /// Generates a validity proof for this transfer. + #[inline] + pub fn is_valid<R>( + &self, + commitment_scheme: &C::CommitmentScheme, + utxo_set_verifier: &UtxoSetVerifier<C>, + context: &ProvingContext<C>, + rng: &mut R, + ) -> Result<Proof<C>, ProofSystemError<C>> + where + R: CryptoRng + RngCore + ?Sized, + { + self.known_constraints(commitment_scheme, utxo_set_verifier) + .prove::<C::ProofSystem, _>(context, rng) } /// Converts `self` into its ledger post. @@ -819,12 +840,7 @@ where R: CryptoRng + RngCore + ?Sized, { Ok(TransferPost { - validity_proof: self.generate_proof( - commitment_scheme, - utxo_set_verifier, - context, - rng, - )?, + validity_proof: self.is_valid(commitment_scheme, utxo_set_verifier, context, rng)?, asset_id: self.public.asset_id, sources: self.public.sources.into(), sender_posts: IntoIterator::into_iter(self.secret.senders) @@ -1128,7 +1144,7 @@ where self.sinks, proof, super_key, - ) + ); } } } @@ -1767,6 +1783,19 @@ pub mod test { where C: Configuration, { + /// Samples constraint system for unknown transfer from `rng`. + #[inline] + pub fn sample_unknown_constraints<CD, VD, R>(rng: &mut R) -> ConstraintSystem<C> + where + CD: Default, + VD: Default, + C::CommitmentScheme: Sample<CD>, + UtxoSetVerifier<C>: Sample<VD>, + R: CryptoRng + RngCore + ?Sized, + { + Self::unknown_constraints(&rng.gen(), &rng.gen()) + } + /// Samples proving and verifying contexts from `rng`. #[inline] pub fn sample_context<CD, VD, R>( @@ -1832,6 +1861,6 @@ pub mod test { where C: Configuration, { - assert!(matches!(has_valid_proof(post, context), Ok(true))) + assert!(matches!(has_valid_proof(post, context), Ok(true))); } } diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index cca495894..c053e643d 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -623,13 +623,13 @@ where if let Some((asset_id, zeroes)) = self.insert_zeroes.take() { assets.insert_zeroes(asset_id, zeroes.into_iter().map(Index::reduce)); } - assets.remove_all(mem::take(&mut self.remove)) + assets.remove_all(mem::take(&mut self.remove)); } /// Clears the pending asset map. #[inline] fn rollback(&mut self) { - *self = Default::default() + *self = Default::default(); } } @@ -842,7 +842,7 @@ where posts.push(self.build_post(SecretTransfer::new(senders, accumulator.receivers))?); - for zero in accumulator.zeroes.iter() { + for zero in &accumulator.zeroes { zero.as_ref().insert_utxo(&mut self.utxo_set); } accumulator.pre_sender.insert_utxo(&mut self.utxo_set); @@ -955,13 +955,12 @@ where asset: Asset, receiver: Option<ShieldedIdentity<C>>, ) -> SignResult<D, C, Self> { - let selection = self.select(asset)?; - - let mut posts = Vec::new(); - const SENDERS: usize = PrivateTransferShape::SENDERS; const RECEIVERS: usize = PrivateTransferShape::RECEIVERS; + let selection = self.select(asset)?; + + let mut posts = Vec::new(); let senders = self.accumulate_transfers::<SENDERS, RECEIVERS>( asset.id, selection.pre_senders, @@ -969,7 +968,6 @@ where )?; let change = self.next_change(asset.id, selection.change)?; - let final_post = match receiver { Some(receiver) => { let receiver = self.prepare_receiver(asset, receiver)?; diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 378d05190..5ae5e7a38 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -61,7 +61,7 @@ pub trait BalanceState { where I: IntoIterator<Item = Asset>, { - assets.into_iter().for_each(move |a| self.deposit(a)) + assets.into_iter().for_each(move |a| self.deposit(a)); } /// Withdraws `asset` from the balance state without checking if it would overdraw. @@ -92,7 +92,7 @@ impl BalanceState for Vec<Asset> { #[inline] fn deposit(&mut self, asset: Asset) { - self.push(asset) + self.push(asset); } #[inline] @@ -101,7 +101,7 @@ impl BalanceState for Vec<Asset> { withdraw_unchecked( self.iter_mut().find_map(move |a| a.value_of_mut(asset.id)), asset.value, - ) + ); } } } diff --git a/manta-cli/Cargo.toml b/manta-cli/Cargo.toml index 5107b858b..491e1487c 100644 --- a/manta-cli/Cargo.toml +++ b/manta-cli/Cargo.toml @@ -35,5 +35,6 @@ manta-crypto = { path = "../manta-crypto" } manta-pay = { path = "../manta-pay" } [dev-dependencies] -manta-crypto = { path = "../manta-crypto", features = ["test"] } manta-accounting = { path = "../manta-accounting", features = ["test"] } +manta-crypto = { path = "../manta-crypto", features = ["test"] } +manta-pay = { path = "../manta-pay", features = ["test"] } diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index 68770cb9b..ba37278a1 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -78,7 +78,7 @@ where { #[inline] fn extend(input: &mut Self::Input, next: &T) { - next.concat(input) + next.concat(input); } } @@ -154,6 +154,6 @@ pub mod test { C: CommitmentScheme + Input<T> + ?Sized, C::Output: Debug + PartialEq, { - assert_eq!(&commitment_scheme.commit_one(input, randomness), output) + assert_eq!(&commitment_scheme.commit_one(input, randomness), output); } } diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index dcd99c1f2..179d04ea0 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -20,7 +20,7 @@ // TODO: Add derive trait to implement `HasAllocation` for structs (and enums?). // TODO: Add more convenience functions for allocating unknown variables. // FIXME: Leverage the type system to constrain allocation to only unknown modes for verifier -// generation and only known modes for proof generation, instead of relying on the `setup_*` +// generation and only known modes for proof generation, instead of relying on the `for_*` // methods to "do the right thing". use core::{ @@ -380,7 +380,7 @@ pub trait ConstraintSystem { where I: IntoIterator<Item = Self::Bool>, { - iter.into_iter().for_each(move |b| self.assert(b)) + iter.into_iter().for_each(move |b| self.assert(b)); } /// Generates a boolean that represents the fact that `lhs` and `rhs` may be equal. @@ -398,7 +398,7 @@ pub trait ConstraintSystem { where V: Variable<Self> + Equal<Self>, { - V::assert_eq(self, lhs, rhs) + V::assert_eq(self, lhs, rhs); } /// Asserts that all the elements in `iter` are equal to some `base` element. @@ -408,7 +408,7 @@ pub trait ConstraintSystem { V: 't + Variable<Self> + Equal<Self>, I: IntoIterator<Item = &'t V>, { - V::assert_all_eq_to_base(self, base, iter) + V::assert_all_eq_to_base(self, base, iter); } /// Asserts that all the elements in `iter` are equal. @@ -418,7 +418,32 @@ pub trait ConstraintSystem { V: 't + Variable<Self> + Equal<Self>, I: IntoIterator<Item = &'t V>, { - V::assert_all_eq(self, iter) + V::assert_all_eq(self, iter); + } + + /// Returns proving and verifying contexts for the constraints contained in `self`. + #[inline] + fn generate_context<P, R>( + self, + rng: &mut R, + ) -> Result<(P::ProvingContext, P::VerifyingContext), P::Error> + where + Self: Sized, + P: ProofSystem<ConstraintSystem = Self>, + R: CryptoRng + RngCore + ?Sized, + { + P::generate_context(self, rng) + } + + /// Returns a proof that the constraint system `self` is consistent. + #[inline] + fn prove<P, R>(self, context: &P::ProvingContext, rng: &mut R) -> Result<P::Proof, P::Error> + where + Self: Sized, + P: ProofSystem<ConstraintSystem = Self>, + R: CryptoRng + RngCore + ?Sized, + { + P::prove(self, context, rng) } } @@ -434,7 +459,7 @@ where #[inline] fn assert_eq(cs: &mut C, lhs: &Self, rhs: &Self) { let boolean = Self::eq(cs, lhs, rhs); - cs.assert(boolean) + cs.assert(boolean); } /// Asserts that all the elements in `iter` are equal to some `base` element. @@ -444,7 +469,7 @@ where I: IntoIterator<Item = &'t Self>, { for item in iter { - Self::assert_eq(cs, base, item) + Self::assert_eq(cs, base, item); } } @@ -457,7 +482,7 @@ where { let mut iter = iter.into_iter(); if let Some(base) = iter.next() { - Self::assert_all_eq_to_base(cs, base, iter) + Self::assert_all_eq_to_base(cs, base, iter); } } } @@ -488,12 +513,14 @@ pub trait ProofSystem { type Error; /// Returns a constraint system which is setup to build proving and verifying contexts. + #[must_use] fn for_unknown() -> Self::ConstraintSystem; /// Returns a constraint system which is setup to build a proof. + #[must_use] fn for_known() -> Self::ConstraintSystem; - /// Returns proving and verifying contexts for the constraints contained in `self`. + /// Returns proving and verifying contexts for the constraints contained in `cs`. fn generate_context<R>( cs: Self::ConstraintSystem, rng: &mut R, @@ -501,7 +528,7 @@ pub trait ProofSystem { where R: CryptoRng + RngCore + ?Sized; - /// Returns a proof that the constraint system `self` is consistent. + /// Returns a proof that the constraint system `cs` is consistent. fn prove<R>( cs: Self::ConstraintSystem, context: &Self::ProvingContext, @@ -626,7 +653,7 @@ impl PublicOrSecret { pub const fn public(self) -> Option<Public> { match self { Self::Public => Some(Public), - _ => None, + Self::Secret => None, } } @@ -641,7 +668,7 @@ impl PublicOrSecret { pub const fn secret(self) -> Option<Secret> { match self { Self::Secret => Some(Secret), - _ => None, + Self::Public => None, } } } @@ -714,6 +741,142 @@ where } } +/// Constraint System Measurement +pub mod measure { + use super::*; + + /// Constraint System Measurement + pub trait Measure<M> + where + M: ?Sized, + { + /// Returns the number of constraints stored in `self`. + fn constraint_count(&self) -> usize; + + /// Returns the number of variables allocated with the given `mode`. + fn variable_count(&self, mode: M) -> usize; + + /// Returns the number of constraints and number and kind of variables stored in `self`. + #[inline] + fn measure(&self) -> SizeReport<M> + where + M: MeasureVariables, + { + M::measure(self) + } + } + + /// Constraint System Variable Allocation Measurement + pub trait MeasureVariables { + /// Measurement Type + type Measurement; + + /// Counts the number of constraints and the number of variables allocated with all of the + /// possible modes in `Self`, returning a [`SizeReport`]. + fn measure<C>(cs: &C) -> SizeReport<Self> + where + C: Measure<Self> + ?Sized; + } + + /// Constraint System Size Measurement Report + #[derive(derivative::Derivative)] + #[derivative( + Clone(bound = "M::Measurement: Clone"), + Copy(bound = "M::Measurement: Copy"), + Debug(bound = "M::Measurement: Debug"), + Default(bound = "M::Measurement: Default"), + Eq(bound = "M::Measurement: Eq"), + Hash(bound = "M::Measurement: Hash"), + PartialEq(bound = "M::Measurement: PartialEq") + )] + pub struct SizeReport<M> + where + M: MeasureVariables + ?Sized, + { + /// Number of constraints + pub constraint_count: usize, + + /// Number of variables + pub variable_count: M::Measurement, + } + + impl<M> SizeReport<M> + where + M: MeasureVariables + ?Sized, + { + /// Builds a new [`SizeReport`] from `constraint_count` and `variable_count`. + #[inline] + pub fn new(constraint_count: usize, variable_count: M::Measurement) -> Self { + Self { + constraint_count, + variable_count, + } + } + + /// Builds a new [`SizeReport`] with the given `cs` to measure its constraint count and + /// `variable_count` as the number of variables in the resulting report. + #[inline] + pub fn with<C>(cs: &C, variable_count: M::Measurement) -> Self + where + C: Measure<M> + ?Sized, + { + Self::new(cs.constraint_count(), variable_count) + } + } + + impl MeasureVariables for Public { + type Measurement = usize; + + #[inline] + fn measure<C>(cs: &C) -> SizeReport<Self> + where + C: Measure<Self> + ?Sized, + { + SizeReport::with(cs, cs.variable_count(Public)) + } + } + + impl MeasureVariables for Secret { + type Measurement = usize; + + #[inline] + fn measure<C>(cs: &C) -> SizeReport<Self> + where + C: Measure<Self> + ?Sized, + { + SizeReport::with(cs, cs.variable_count(Secret)) + } + } + + impl MeasureVariables for PublicOrSecret { + type Measurement = PublicOrSecretMeasurement; + + #[inline] + fn measure<C>(cs: &C) -> SizeReport<Self> + where + C: Measure<Self> + ?Sized, + { + SizeReport::with( + cs, + PublicOrSecretMeasurement { + public: cs.variable_count(PublicOrSecret::Public), + secret: cs.variable_count(PublicOrSecret::Secret), + }, + ) + } + } + + /// [`PublicOrSecret`] Measurement + #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] + pub struct PublicOrSecretMeasurement { + /// Public Variable Count + pub public: usize, + + /// Secret Variable Count + pub secret: usize, + } +} + /// Opt-In Compile-Time Reflection Capabilities /// /// See [`HasAllocation`] and [`HasVariable`] for more information. @@ -919,28 +1082,3 @@ pub mod types { /// Pointer-Sized Unsigned Integer Variable Type pub type Usize<C> = Var<usize, C>; } - -/* FIXME: Need to reconsider how to do this: -/// Testing Framework -#[cfg(feature = "test")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] -pub mod test { - use super::*; - - /// Builds a proof from `cs` and the `proving_context` and then tries to verify it with - /// the `verifying_context`. - #[inline] - pub fn verify_constructed_proof<P, R>( - cs: P::ConstraintSystem, - proving_context: &P::ProvingContext, - verifying_context: &P::VerifyingContext, - rng: &mut R, - ) -> Result<P::Verification, P::Error> - where - P: ProofSystem, - R: CryptoRng + RngCore, - { - P::verify(&P::prove(cs, proving_context, rng)?, verifying_context) - } -} -*/ diff --git a/manta-crypto/src/ies.rs b/manta-crypto/src/ies.rs index 44d2fbb29..ff2c2ac3c 100644 --- a/manta-crypto/src/ies.rs +++ b/manta-crypto/src/ies.rs @@ -288,7 +288,7 @@ where SecretKey::new(self.secret_key) } - /// Encrypts the `plaintext` with `self, returning an [`EncryptedMessage`]. + /// Encrypts the `plaintext` with `self`, returning an [`EncryptedMessage`]. #[inline] pub fn encrypt<R>( self, @@ -434,6 +434,6 @@ pub mod test { assert_eq!( plaintext, &reconstructed_plaintext, "Plaintext didn't match decrypted ciphertext." - ) + ); } } diff --git a/manta-crypto/src/merkle_tree/inner_tree.rs b/manta-crypto/src/merkle_tree/inner_tree.rs index 9084c1ea6..e6f70bdbb 100644 --- a/manta-crypto/src/merkle_tree/inner_tree.rs +++ b/manta-crypto/src/merkle_tree/inner_tree.rs @@ -176,7 +176,7 @@ impl Iterator for InnerNodeIter { #[inline] fn size_hint(&self) -> (usize, Option<usize>) { - let len = self.node.map(move |n| n.depth + 1).unwrap_or(0); + let len = self.node.map_or(0, move |n| n.depth + 1); (len, Some(len)) } } @@ -234,7 +234,7 @@ where #[inline] fn set(&mut self, index: usize, inner_digest: InnerDigest<C>) { - (**self).set(index, inner_digest) + (**self).set(index, inner_digest); } } @@ -464,7 +464,7 @@ where /// Sets the current root to `root`. #[inline] fn set_root(&mut self, root: InnerDigest<C>) { - self.map.set(0, root) + self.map.set(0, root); } /// Inserts the new `inner_digest` at `node` in the tree, and returns a reference to @@ -728,7 +728,7 @@ where /// Inserts the `base` inner digest corresponding to the leaf at `leaf_index` into the tree. #[inline] pub fn insert(&mut self, parameters: &Parameters<C>, leaf_index: Node, base: InnerDigest<C>) { - self.inner_tree.insert(parameters, leaf_index, base) + self.inner_tree.insert(parameters, leaf_index, base); } /// Computes the inner path of the leaf given by `leaf_index` without checking if diff --git a/manta-crypto/src/merkle_tree/test.rs b/manta-crypto/src/merkle_tree/test.rs index 45bd03d5f..6b7ebbf0b 100644 --- a/manta-crypto/src/merkle_tree/test.rs +++ b/manta-crypto/src/merkle_tree/test.rs @@ -136,5 +136,5 @@ where .expect("Only valid queries are accepted.") .verify(&tree.parameters, &tree.root(), leaf), "Path returned from tree was not valid." - ) + ); } diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index a31be2439..6debf8da8 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -147,18 +147,20 @@ pub type InnerDigest<C> = <<C as HashConfiguration>::InnerHash as InnerHash>::Ou /// parameter. /// /// The capacity of a merkle tree with height `H` is `2^(H-1)`. +#[must_use] #[inline] pub fn capacity<C>() -> usize where C: Configuration + ?Sized, { - 1usize << (C::HEIGHT.into() - 1) + 1_usize << (C::HEIGHT.into() - 1) } /// Returns the path length of the merkle tree with the given [`C::HEIGHT`](Configuration::HEIGHT) /// parameter. /// /// The path length of a merkle tree with height `H` is `H - 2`. +#[must_use] #[inline] pub fn path_length<C>() -> usize where diff --git a/manta-crypto/src/rand.rs b/manta-crypto/src/rand.rs index 01503cde5..228315080 100644 --- a/manta-crypto/src/rand.rs +++ b/manta-crypto/src/rand.rs @@ -49,7 +49,7 @@ where #[inline] fn fill_bytes(&mut self, dest: &mut [u8]) { - self.0.fill_bytes(dest) + self.0.fill_bytes(dest); } #[inline] @@ -68,7 +68,7 @@ where #[inline] fn generate(&mut self, results: &mut Self::Results) { - self.0.generate(results) + self.0.generate(results); } } @@ -122,7 +122,7 @@ where #[inline] fn fill_bytes(&mut self, dest: &mut [u8]) { - self.inner.fill_bytes(dest) + self.inner.fill_bytes(dest); } #[inline] @@ -141,7 +141,7 @@ where #[inline] fn generate(&mut self, results: &mut Self::Results) { - self.inner.generate(results) + self.inner.generate(results); } } @@ -237,7 +237,7 @@ impl Sample for u128 { R: CryptoRng + RngCore + ?Sized, { let _ = distribution; - ((rng.next_u64() as u128) << 64) | (rng.next_u64() as u128) + (u128::from(rng.next_u64()) << 64) | u128::from(rng.next_u64()) } } diff --git a/manta-crypto/src/set.rs b/manta-crypto/src/set.rs index 9249f9fa0..ddea5d481 100644 --- a/manta-crypto/src/set.rs +++ b/manta-crypto/src/set.rs @@ -246,7 +246,7 @@ pub mod constraint { C: ConstraintSystem, VV: VerifierVariable<C, Type = V>, { - verifier.assert_valid_membership_proof(&self.public, &self.secret, item, cs) + verifier.assert_valid_membership_proof(&self.public, &self.secret, item, cs); } } diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 121a23cb0..4d8e7f877 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -24,6 +24,10 @@ is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } +[[bench]] +name = "main" +harness = false + [features] # Default Features default = ["arkworks-groth16"] @@ -42,6 +46,9 @@ dusk-network-plonk = ["dusk-network-zkp", "dusk-plonk"] # Enable All Dusk-Network Zero-Knowledge Proof Backends dusk-network-zkp-all = ["dusk-network-plonk"] +# Testing Frameworks +test = ["manta-accounting/test", "manta-crypto/test"] + # Standard Library std = [] @@ -70,4 +77,5 @@ x25519-dalek = { git = "https://github.com/Manta-Network/x25519-dalek", default- criterion = "0.3.5" manta-accounting = { path = "../manta-accounting", features = ["test"] } manta-crypto = { path = "../manta-crypto", features = ["test"] } +manta-pay = { path = ".", features = ["test"] } rand = "0.8.4" diff --git a/manta-pay/benches/main.rs b/manta-pay/benches/main.rs index ee04bfe0e..48c136770 100644 --- a/manta-pay/benches/main.rs +++ b/manta-pay/benches/main.rs @@ -18,4 +18,162 @@ extern crate manta_pay; -use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion}; +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use manta_accounting::transfer::test; +use manta_crypto::{ + rand::{Rand, TrySample}, + set::VerifiedSet, +}; +use manta_pay::accounting::{ + identity::UtxoSet, + transfer::{Mint, PrivateTransfer, Reclaim}, +}; +use rand::thread_rng; + +/// Runs the following benchmarks for a given transfer: +/// 1. Constraint generation +/// 2. TODO: Constraint satisfaction check +/// 3. TODO: ZKP Setup generating Proving Key and Verifying Key +/// 4. TODO: ZKP Prove with Proving Key +/// 5. TODO: ZKP Verify with Verifying Key +/// 6. TODO: ZKP Verify with Processed Verifying Key +macro_rules! transfer_benchmark { + ( + $c:ident, + $name:expr, + $rng:ident, + $transfer:ident, + $commitment_scheme: ident, + $utxo_set_verifier: ident + ) => { + let mut group = $c.benchmark_group($name); + + group.bench_function("generate-constraints", |b| { + b.iter(|| { + black_box($transfer.known_constraints(&$commitment_scheme, &$utxo_set_verifier)); + }) + }); + + let _known_constraints = + $transfer.known_constraints(&$commitment_scheme, &$utxo_set_verifier); + + /* TODO: + group.bench_function("is-satisfied", |b| { + b.iter(|| { + black_box(constraints.is_satisfied().unwrap()); + }) + }); + */ + + /* TODO: + group.bench_function("zkp-setup", |b| { + b.iter_batched( + || $circuit.clone(), + |c| { + black_box(Groth::generate_(c, &mut $rng).unwrap()); + }, + BatchSize::SmallInput, + ) + }); + + let (pk, vk) = Groth::circuit_specific_setup($circuit.clone(), &mut $rng).unwrap(); + + group.bench_function("zkp-prove", |b| { + b.iter_batched( + || $circuit.clone(), + |c| { + black_box(Groth::prove(&pk, c, &mut $rng).unwrap()); + }, + BatchSize::SmallInput, + ) + }); + + let proof = Groth::prove(&pk, $circuit.clone(), &mut $rng).unwrap(); + let input = $circuit.raw_input().unwrap(); + + group.bench_function("zkp-verify", |b| { + b.iter(|| { + black_box(Groth::verify(&vk, &input, &proof).unwrap()); + }) + }); + + let pvk = Groth::process_vk(&vk).unwrap(); + + group.bench_function("zkp-verify-processed", |b| { + b.iter(|| { + black_box(Groth::verify_with_processed_vk(&pvk, &input, &proof).unwrap()); + }) + }); + */ + + group.finish(); + }; +} + +/// Runs the circuit benchmark for the [`Mint`] transfer. +fn mint(c: &mut Criterion) { + let mut rng = thread_rng(); + let commitment_scheme = rng.gen(); + let mut utxo_set = UtxoSet::new(rng.gen()); + let mint = Mint::try_sample( + test::distribution::Transfer { + commitment_scheme: &commitment_scheme, + utxo_set: &mut utxo_set, + }, + &mut rng, + ) + .unwrap(); + let utxo_set_verifier = utxo_set.verifier(); + transfer_benchmark!(c, "Mint", rng, mint, commitment_scheme, utxo_set_verifier); +} + +/// Runs the circuit benchmark for the [`PrivateTransfer`] transfer. +fn private_transfer(c: &mut Criterion) { + let mut rng = thread_rng(); + let commitment_scheme = rng.gen(); + let mut utxo_set = UtxoSet::new(rng.gen()); + let private_transfer = PrivateTransfer::try_sample( + test::distribution::Transfer { + commitment_scheme: &commitment_scheme, + utxo_set: &mut utxo_set, + }, + &mut rng, + ) + .unwrap(); + let utxo_set_verifier = utxo_set.verifier(); + transfer_benchmark!( + c, + "PrivateTransfer", + rng, + private_transfer, + commitment_scheme, + utxo_set_verifier + ); +} + +/// Runs the circuit benchmark for the [`Reclaim`] transfer. +fn reclaim(c: &mut Criterion) { + let mut rng = thread_rng(); + let commitment_scheme = rng.gen(); + let mut utxo_set = UtxoSet::new(rng.gen()); + let reclaim = Reclaim::try_sample( + test::distribution::Transfer { + commitment_scheme: &commitment_scheme, + utxo_set: &mut utxo_set, + }, + &mut rng, + ) + .unwrap(); + let utxo_set_verifier = utxo_set.verifier(); + transfer_benchmark!( + c, + "Reclaim", + rng, + reclaim, + commitment_scheme, + utxo_set_verifier + ); +} + +criterion_group!(circuits, mint, private_transfer, reclaim); +criterion_main!(circuits); diff --git a/manta-pay/src/accounting/identity.rs b/manta-pay/src/accounting/identity.rs index 74381f652..589856c1a 100644 --- a/manta-pay/src/accounting/identity.rs +++ b/manta-pay/src/accounting/identity.rs @@ -143,7 +143,7 @@ pub mod constraint { cs: &mut ConstraintSystem, ) { let _ = cs; - self.0.assert_verified(public, secret, &concatenate!(item)) + self.0.assert_verified(public, secret, &concatenate!(item)); } } } diff --git a/manta-pay/src/accounting/keys.rs b/manta-pay/src/accounting/keys.rs index f6222150c..70f32c39f 100644 --- a/manta-pay/src/accounting/keys.rs +++ b/manta-pay/src/accounting/keys.rs @@ -166,8 +166,9 @@ where C: CoinType, { /// Converts a `mnemonic` phrase into a [`DerivedKeySecret`], locking it with `password`. + #[must_use] #[inline] - pub fn from_mnemonic(mnemonic: Mnemonic, password: &str) -> Self { + pub fn new(mnemonic: Mnemonic, password: &str) -> Self { Self { seed: mnemonic.to_seed(password), __: PhantomData, @@ -178,6 +179,8 @@ where /// Computes the [`BIP-0044`] path string for the given coin settings. /// /// [`BIP-0044`]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki +#[must_use] +#[inline] pub fn path_string<C>(kind: KeyKind, account: &AccountParameter, index: &IndexParameter) -> String where C: CoinType, diff --git a/manta-pay/src/accounting/transfer.rs b/manta-pay/src/accounting/transfer.rs index 4fef57528..96e5c0033 100644 --- a/manta-pay/src/accounting/transfer.rs +++ b/manta-pay/src/accounting/transfer.rs @@ -35,28 +35,41 @@ pub type TransferPost = transfer::TransferPost<Configuration>; #[cfg(test)] mod test { use crate::accounting::{ + config, identity::UtxoSet, transfer::{Mint, PrivateTransfer, Reclaim}, }; - use manta_crypto::rand::Rand; + use manta_crypto::{ + constraint::{measure::Measure, ProofSystem}, + rand::Rand, + }; use rand::thread_rng; /// Tests the generation of proving/verifying contexts for [`Mint`]. #[test] fn sample_mint_context() { - Mint::sample_context(&mut thread_rng()).unwrap(); + let mut rng = thread_rng(); + let cs = Mint::sample_unknown_constraints(&mut rng); + println!("Mint: {:?}", cs.measure()); + config::ProofSystem::generate_context(cs, &mut rng).unwrap(); } /// Tests the generation of proving/verifying contexts for [`PrivateTransfer`]. #[test] fn sample_private_transfer_context() { - PrivateTransfer::sample_context(&mut thread_rng()).unwrap(); + let mut rng = thread_rng(); + let cs = PrivateTransfer::sample_unknown_constraints(&mut rng); + println!("PrivateTransfer: {:?}", cs.measure()); + config::ProofSystem::generate_context(cs, &mut rng).unwrap(); } /// Tests the generation of proving/verifying contexts for [`Reclaim`]. #[test] fn sample_reclaim_context() { - Reclaim::sample_context(&mut thread_rng()).unwrap(); + let mut rng = thread_rng(); + let cs = Reclaim::sample_unknown_constraints(&mut rng); + println!("Reclaim: {:?}", cs.measure()); + config::ProofSystem::generate_context(cs, &mut rng).unwrap(); } /// Tests the generation of a [`Mint`]. diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs index ac7daa4e6..585672298 100644 --- a/manta-pay/src/crypto/commitment/pedersen.rs +++ b/manta-pay/src/crypto/commitment/pedersen.rs @@ -106,7 +106,7 @@ where self.0 .write(&mut buffer) .expect("This does not fail. See the implementation of `Write` for `Vec`."); - accumulator.extend(&buffer) + accumulator.extend(&buffer); } } @@ -258,7 +258,7 @@ pub mod constraint { where A: ConcatAccumulator<Self::Item> + ?Sized, { - self.0.concat(accumulator) + self.0.concat(accumulator); } } @@ -439,7 +439,7 @@ pub mod constraint { where A: ConcatAccumulator<Self::Item> + ?Sized, { - accumulator.extend(&self.0.to_bytes().expect("This is not allowed to fail.")) + accumulator.extend(&self.0.to_bytes().expect("This is not allowed to fail.")); } } @@ -531,7 +531,7 @@ pub mod constraint { .0 .to_field_elements() .expect("Conversion to constraint field elements is not allowed to fail."), - ) + ); } } @@ -548,7 +548,7 @@ pub mod constraint { where C::Affine: ToConstraintField<ConstraintField<C>>, { - self.0.extend_input(input) + self.0.extend_input(input); } } diff --git a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs index 5810b48e3..4c35ce3c1 100644 --- a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs @@ -25,8 +25,8 @@ use ark_relations::{ns, r1cs as ark_r1cs}; use core::{borrow::Borrow, ops::AddAssign}; use manta_accounting::{AssetBalance, AssetId}; use manta_crypto::constraint::{ - reflection::HasAllocation, types::Bool, Allocation, AllocationMode, ConstraintSystem, Equal, - Public, PublicOrSecret, Secret, Variable, VariableSource, + measure::Measure, reflection::HasAllocation, types::Bool, Allocation, AllocationMode, + ConstraintSystem, Equal, Public, PublicOrSecret, Secret, Variable, VariableSource, }; use manta_util::{Concat, ConcatAccumulator}; @@ -138,7 +138,25 @@ where #[inline] fn assert(&mut self, b: Bool<Self>) { b.enforce_equal(&Boolean::TRUE) - .expect("This should never fail.") + .expect("This should never fail."); + } +} + +impl<F> Measure<PublicOrSecret> for ArkConstraintSystem<F> +where + F: Field, +{ + #[inline] + fn constraint_count(&self) -> usize { + self.cs.num_constraints() + } + + #[inline] + fn variable_count(&self, mode: PublicOrSecret) -> usize { + match mode { + PublicOrSecret::Public => self.cs.num_instance_variables(), + PublicOrSecret::Secret => self.cs.num_witness_variables(), + } } } @@ -156,25 +174,21 @@ where allocation: Allocation<Self::Type, Self::Mode>, ) -> Self { match allocation { - Allocation::Known(this, mode) => match mode { - ArkAllocationMode::Constant => { - Self::new_constant(ns!(cs.cs, "boolean constant"), this) - } - ArkAllocationMode::Public => { - Self::new_input(ns!(cs.cs, "boolean input"), full(this)) - } - ArkAllocationMode::Secret => { - Self::new_witness(ns!(cs.cs, "boolean witness"), full(this)) - } - }, - Allocation::Unknown(mode) => match mode { - PublicOrSecret::Public => { - Self::new_input(ns!(cs.cs, "boolean input"), empty::<bool>) - } - PublicOrSecret::Secret => { - Self::new_witness(ns!(cs.cs, "boolean witness"), empty::<bool>) - } - }, + Allocation::Known(this, ArkAllocationMode::Constant) => { + Self::new_constant(ns!(cs.cs, "boolean constant"), this) + } + Allocation::Known(this, ArkAllocationMode::Public) => { + Self::new_input(ns!(cs.cs, "boolean input"), full(this)) + } + Allocation::Known(this, ArkAllocationMode::Secret) => { + Self::new_witness(ns!(cs.cs, "boolean witness"), full(this)) + } + Allocation::Unknown(PublicOrSecret::Public) => { + Self::new_input(ns!(cs.cs, "boolean input"), empty::<bool>) + } + Allocation::Unknown(PublicOrSecret::Secret) => { + Self::new_witness(ns!(cs.cs, "boolean witness"), empty::<bool>) + } } .expect("Variable allocation is not allowed to fail.") } @@ -326,7 +340,7 @@ where where A: ConcatAccumulator<Self::Item> + ?Sized, { - accumulator.extend(&self.0) + accumulator.extend(&self.0); } } @@ -391,7 +405,7 @@ where where A: ConcatAccumulator<Self::Item> + ?Sized, { - self.bytes.concat(accumulator) + self.bytes.concat(accumulator); } } @@ -479,7 +493,7 @@ where where A: ConcatAccumulator<Self::Item> + ?Sized, { - self.bytes.concat(accumulator) + self.bytes.concat(accumulator); } } @@ -537,6 +551,6 @@ where { #[inline] fn add_assign(&mut self, rhs: Self) { - self.field_point += rhs.field_point + self.field_point += rhs.field_point; } } diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs index cf5cecae4..4005a852a 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs @@ -18,7 +18,10 @@ use crate::{ accounting::identity::{Root, Utxo, VoidNumber}, - crypto::constraint::arkworks::{constraint_system::SynthesisResult, ArkConstraintSystem}, + crypto::{ + constraint::arkworks::{constraint_system::SynthesisResult, ArkConstraintSystem}, + merkle_tree::constraint::root_extend_input, + }, }; use alloc::vec::Vec; use ark_crypto_primitives::SNARK; @@ -161,7 +164,7 @@ where { #[inline] fn extend(input: &mut Self::Input, next: &VoidNumber) { - next.extend_input(input) + next.extend_input(input); } } @@ -171,7 +174,7 @@ where { #[inline] fn extend(input: &mut Self::Input, next: &Root) { - crate::crypto::merkle_tree::constraint::root_extend_input(next, input) + root_extend_input(next, input); } } @@ -181,6 +184,6 @@ where { #[inline] fn extend(input: &mut Self::Input, next: &Utxo) { - next.extend_input(input) + next.extend_input(input); } } diff --git a/manta-pay/src/crypto/ies.rs b/manta-pay/src/crypto/ies.rs index d0bd6bf48..552c936f9 100644 --- a/manta-pay/src/crypto/ies.rs +++ b/manta-pay/src/crypto/ies.rs @@ -41,6 +41,7 @@ pub type PublicKey = [u8; 32]; pub type SecretKey = [u8; 32]; /// `GCM` Tag Size +#[allow(clippy::cast_possible_truncation)] // NOTE: GCM Tag Size should be smaller than `2^32`. const GCM_TAG_SIZE: usize = (aes_gcm::C_MAX - aes_gcm::P_MAX) as usize; /// Asset Ciphertext Type diff --git a/manta-pay/src/crypto/merkle_tree.rs b/manta-pay/src/crypto/merkle_tree.rs index b5008a03a..01897b5fa 100644 --- a/manta-pay/src/crypto/merkle_tree.rs +++ b/manta-pay/src/crypto/merkle_tree.rs @@ -16,6 +16,8 @@ //! Arkworks Merkle Tree Wrappers +// TODO: Move as much constraint code to `manta_crypto` as possible. + use alloc::{vec, vec::Vec}; use ark_crypto_primitives::{ crh::{TwoToOneCRH, CRH}, @@ -29,7 +31,7 @@ use manta_crypto::{ }; use manta_util::{as_bytes, Concat}; -#[cfg(test)] +#[cfg(feature = "test")] use manta_crypto::rand::Standard; /// Arkworks Leaf Hash Converter @@ -211,7 +213,8 @@ where type TwoToOneHash = C::InnerHash; } -#[cfg(test)] +#[cfg(feature = "test")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] impl<C> merkle_tree::test::HashParameterSampling for ConfigConverter<C> where C: Configuration, @@ -346,7 +349,7 @@ pub mod constraint { ) { self.verify(root, path, leaf) .enforce_equal(&Boolean::TRUE) - .expect("This is not allowed to fail.") + .expect("This is not allowed to fail."); } } diff --git a/manta-pay/src/crypto/prf/blake2s.rs b/manta-pay/src/crypto/prf/blake2s.rs index b679ab583..b27772875 100644 --- a/manta-pay/src/crypto/prf/blake2s.rs +++ b/manta-pay/src/crypto/prf/blake2s.rs @@ -47,7 +47,7 @@ impl Concat for Blake2sSeed { where A: ConcatAccumulator<Self::Item> + ?Sized, { - self.0.concat(accumulator) + self.0.concat(accumulator); } } @@ -81,7 +81,7 @@ impl Concat for Blake2sInput { where A: ConcatAccumulator<Self::Item> + ?Sized, { - self.0.concat(accumulator) + self.0.concat(accumulator); } } @@ -108,7 +108,7 @@ impl Concat for Blake2sOutput { where A: ConcatAccumulator<Self::Item> + ?Sized, { - self.0.concat(accumulator) + self.0.concat(accumulator); } } @@ -170,7 +170,7 @@ pub mod constraint { where A: ConcatAccumulator<Self::Item> + ?Sized, { - self.0.concat(accumulator) + self.0.concat(accumulator); } } @@ -217,7 +217,7 @@ pub mod constraint { where A: ConcatAccumulator<Self::Item> + ?Sized, { - self.0.concat(accumulator) + self.0.concat(accumulator); } } diff --git a/manta-util/src/concat.rs b/manta-util/src/concat.rs index 27002dda3..746cb8be4 100644 --- a/manta-util/src/concat.rs +++ b/manta-util/src/concat.rs @@ -59,17 +59,17 @@ where { #[inline] fn extend(&mut self, buffer: &[T]) { - (**self).extend(buffer) + (**self).extend(buffer); } #[inline] fn reserve(&mut self, additional: usize) { - (**self).reserve(additional) + (**self).reserve(additional); } #[inline] fn shrink_to_fit(&mut self) { - (**self).shrink_to_fit() + (**self).shrink_to_fit(); } } @@ -79,17 +79,17 @@ where { #[inline] fn extend(&mut self, buffer: &[T]) { - self.extend_from_slice(buffer) + self.extend_from_slice(buffer); } #[inline] fn reserve(&mut self, additional: usize) { - self.reserve(additional) + self.reserve(additional); } #[inline] fn shrink_to_fit(&mut self) { - self.shrink_to_fit() + self.shrink_to_fit(); } } @@ -125,7 +125,7 @@ pub trait Concat { if let Some(capacity) = self.size_hint() { accumulator.reserve(capacity); } - self.concat(accumulator) + self.concat(accumulator); } /// Constructs a default accumulator and accumulates over `self`, reserving the appropriate @@ -149,7 +149,7 @@ impl<T> Concat for [T] { where A: ConcatAccumulator<T> + ?Sized, { - accumulator.extend(self) + accumulator.extend(self); } #[inline] @@ -166,7 +166,7 @@ impl<T, const N: usize> Concat for [T; N] { where A: ConcatAccumulator<T> + ?Sized, { - accumulator.extend(self) + accumulator.extend(self); } #[inline] @@ -183,7 +183,7 @@ impl<T> Concat for Vec<T> { where A: ConcatAccumulator<T> + ?Sized, { - accumulator.extend(self) + accumulator.extend(self); } #[inline] From 7cd6a4024e6efe4283c449970dc918b72d33e993 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 20 Oct 2021 01:58:01 -0400 Subject: [PATCH 106/275] wip: optimize circuits --- manta-accounting/src/identity.rs | 76 +++++----------- manta-accounting/src/transfer.rs | 48 +++++----- manta-crypto/src/prf.rs | 5 +- .../constraint/arkworks/constraint_system.rs | 89 +++++++++---------- manta-pay/src/crypto/prf/blake2s.rs | 17 ---- 5 files changed, 88 insertions(+), 147 deletions(-) diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index d17387890..38078c947 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -56,7 +56,6 @@ pub trait Configuration { /// Commitment Scheme Type type CommitmentScheme: CommitmentScheme<Randomness = Self::CommitmentSchemeRandomness> - + CommitmentInput<PublicKey<Self>> + CommitmentInput<VoidNumberGenerator<Self>> + CommitmentInput<Asset> + CommitmentInput<VoidNumberCommitment<Self>>; @@ -84,9 +83,6 @@ type CommitmentSchemeOutput<C> = /// Secret Key Type pub type SecretKey<C> = <C as Configuration>::SecretKey; -/// Public Key Type -pub type PublicKey<C> = PseudorandomFunctionFamilyOutput<C>; - /// Void Number Generator Type pub type VoidNumberGenerator<C> = PseudorandomFunctionFamilyInput<C>; @@ -105,23 +101,18 @@ pub type UtxoRandomness<C> = CommitmentSchemeRandomness<C>; /// UTXO Type pub type Utxo<C> = CommitmentSchemeOutput<C>; -/// Generates a void number commitment from `public_key`, `void_number_generator`, and +/// Generates a void number commitment from `void_number_generator` and /// `void_number_commitment_randomness`. #[inline] -pub fn generate_void_number_commitment<CS, PK, VNG>( +pub fn generate_void_number_commitment<CS, VNG>( commitment_scheme: &CS, - public_key: &PK, void_number_generator: &VNG, void_number_commitment_randomness: &CS::Randomness, ) -> CS::Output where - CS: CommitmentScheme + CommitmentInput<PK> + CommitmentInput<VNG>, + CS: CommitmentScheme + CommitmentInput<VNG>, { - commitment_scheme - .start() - .update(public_key) - .update(void_number_generator) - .commit(void_number_commitment_randomness) + commitment_scheme.commit_one(void_number_generator, void_number_commitment_randomness) } /// Generates a UTXO from `asset`, `void_number_commitment`, and `utxo_randomness`. @@ -199,16 +190,14 @@ where } } - /// Generates a new void number commitment using `public_key`. + /// Generates a new void number commitment. #[inline] pub fn void_number_commitment( &self, commitment_scheme: &C::CommitmentScheme, - public_key: &PublicKey<C>, ) -> VoidNumberCommitment<C> { generate_void_number_commitment( commitment_scheme, - public_key, &self.void_number_generator, &self.void_number_commitment_randomness, ) @@ -321,12 +310,6 @@ where (parameters, I::generate_secret_key(&mut rng)) } - /// Returns the public key associated with this identity. - #[inline] - fn public_key(&self) -> PublicKey<C> { - C::PseudorandomFunctionFamily::evaluate_zero(&self.secret_key) - } - /// Generates a new void number using the `void_number_generator` parameter. #[inline] fn void_number(&self, void_number_generator: &VoidNumberGenerator<C>) -> VoidNumber<C> { @@ -341,22 +324,20 @@ where commitment_scheme: &C::CommitmentScheme, parameters: &AssetParameters<C>, ) -> VoidNumberCommitment<C> { - parameters.void_number_commitment(commitment_scheme, &self.public_key()) + parameters.void_number_commitment(commitment_scheme) } - /// Returns the [`PublicKey`], [`VoidNumberCommitment`], and [`Utxo`] for this identity. + /// Returns the [`VoidNumberCommitment`], and [`Utxo`] for this identity. #[inline] fn construct_utxo( &self, commitment_scheme: &C::CommitmentScheme, asset: &Asset, parameters: &AssetParameters<C>, - ) -> (PublicKey<C>, VoidNumberCommitment<C>, Utxo<C>) { - let public_key = self.public_key(); - let void_number_commitment = - parameters.void_number_commitment(commitment_scheme, &public_key); + ) -> (VoidNumberCommitment<C>, Utxo<C>) { + let void_number_commitment = parameters.void_number_commitment(commitment_scheme); let utxo = parameters.utxo(commitment_scheme, asset, &void_number_commitment); - (public_key, void_number_commitment, utxo) + (void_number_commitment, utxo) } /// Builds a new [`PreSender`] for the given `asset`. @@ -367,12 +348,11 @@ where asset: Asset, ) -> PreSender<C> { let parameters = self.parameters(); - let (public_key, void_number_commitment, utxo) = + let (void_number_commitment, utxo) = self.construct_utxo(commitment_scheme, &asset, &parameters); PreSender { void_number: self.void_number(&parameters.void_number_generator), secret_key: self.secret_key, - public_key, asset, parameters, void_number_commitment, @@ -392,13 +372,12 @@ where S: VerifiedSet<Item = Utxo<C>>, { let parameters = self.parameters(); - let (public_key, void_number_commitment, utxo) = + let (void_number_commitment, utxo) = self.construct_utxo(commitment_scheme, &asset, &parameters); Some(Sender { utxo_membership_proof: utxo_set.get_membership_proof(&utxo)?, void_number: self.void_number(&parameters.void_number_generator), secret_key: self.secret_key, - public_key, asset, parameters, void_number_commitment, @@ -846,9 +825,6 @@ where /// Secret Key secret_key: SecretKey<C>, - /// Public Key - public_key: PublicKey<C>, - /// Asset asset: Asset, @@ -916,7 +892,6 @@ where { Sender { secret_key: self.secret_key, - public_key: self.public_key, asset: self.asset, parameters: self.parameters, void_number: self.void_number, @@ -960,9 +935,6 @@ where /// Secret Key secret_key: SecretKey<C>, - /// Public Key - public_key: PublicKey<C>, - /// Asset asset: Asset, @@ -1018,7 +990,6 @@ where pub fn downgrade(self) -> PreSender<C> { PreSender { secret_key: self.secret_key, - public_key: self.public_key, asset: self.asset, parameters: self.parameters, void_number: self.void_number, @@ -1480,8 +1451,7 @@ pub mod constraint { type CommitmentSchemeVar: CommitmentScheme< Randomness = Self::CommitmentSchemeRandomnessVar, Output = Self::CommitmentSchemeOutputVar, - > + CommitmentInput<PublicKeyVar<Self>> - + CommitmentInput<VoidNumberGeneratorVar<Self>> + > + CommitmentInput<VoidNumberGeneratorVar<Self>> + CommitmentInput<AssetVar<Self::ConstraintSystem>> + CommitmentInput<VoidNumberCommitmentVar<Self>> + Variable<Self::ConstraintSystem, Type = Self::CommitmentScheme, Mode = Constant>; @@ -1504,9 +1474,6 @@ pub mod constraint { /// Secret Key Variable Type pub type SecretKeyVar<C> = <C as Configuration>::SecretKeyVar; - /// Public Key Variable Type - pub type PublicKeyVar<C> = PseudorandomFunctionFamilyOutputVar<C>; - /// Void Number Generator Variable Type pub type VoidNumberGeneratorVar<C> = PseudorandomFunctionFamilyInputVar<C>; @@ -1613,9 +1580,6 @@ pub mod constraint { /// Secret Key secret_key: SecretKeyVar<C>, - /// Public Key - public_key: PublicKeyVar<C>, - /// Asset asset: AssetVar<C::ConstraintSystem>, @@ -1652,10 +1616,6 @@ pub mod constraint { where V: VerifierVariable<C::ConstraintSystem, ItemVar = UtxoVar<C>, Type = S::Verifier>, { - cs.assert_eq( - &self.public_key, - &C::PseudorandomFunctionFamilyVar::evaluate_zero(&self.secret_key), - ); cs.assert_eq( &self.void_number, &C::PseudorandomFunctionFamilyVar::evaluate( @@ -1663,15 +1623,20 @@ pub mod constraint { &self.parameters.void_number_generator, ), ); + + // TODO: Prepare commitment input during allocation instead of here, could reduce + // constraint/variable count. cs.assert_eq( &self.void_number_commitment, &generate_void_number_commitment( commitment_scheme, - &self.public_key, &self.parameters.void_number_generator, &self.parameters.void_number_commitment_randomness, ), ); + + // TODO: Prepare commitment input during allocation instead of here, could reduce + // constraint/variable count. cs.assert_eq( &self.utxo, &generate_utxo( @@ -1681,6 +1646,7 @@ pub mod constraint { &self.parameters.utxo_randomness, ), ); + self.utxo_membership_proof .assert_validity(utxo_set_verifier, &self.utxo, cs); self.asset @@ -1706,7 +1672,6 @@ pub mod constraint { match allocation { Allocation::Known(this, mode) => Self { secret_key: SecretKeyVar::<C>::new_known(cs, &this.secret_key, mode), - public_key: PublicKeyVar::<C>::new_known(cs, &this.public_key, Secret), asset: this.asset.known(cs, mode), parameters: this.parameters.known(cs, mode), void_number: VoidNumberVar::<C>::new_known(cs, &this.void_number, Public), @@ -1720,7 +1685,6 @@ pub mod constraint { }, Allocation::Unknown(mode) => Self { secret_key: SecretKeyVar::<C>::new_unknown(cs, mode), - public_key: PublicKeyVar::<C>::new_unknown(cs, Secret), asset: Asset::unknown(cs, mode), parameters: AssetParameters::unknown(cs, mode), void_number: VoidNumberVar::<C>::new_unknown(cs, Public), diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 5e962ff1c..e58ac2532 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -30,7 +30,7 @@ use crate::{ }, }; use alloc::vec::Vec; -use core::{fmt::Debug, hash::Hash, ops::AddAssign}; +use core::{fmt::Debug, hash::Hash, ops::Add}; use manta_crypto::{ constraint::{ self, @@ -82,7 +82,7 @@ pub trait Configuration: /// Asset Balance Variable type AssetBalanceVar: Variable<ConstraintSystem<Self>, Mode = PublicOrSecret, Type = AssetBalance> + Equal<ConstraintSystem<Self>> - + AddAssign; + + Add<Output = Self::AssetBalanceVar>; /// Integrated Encryption Scheme for [`Asset`] type IntegratedEncryptionScheme: IntegratedEncryptionScheme<Plaintext = Asset>; @@ -726,29 +726,31 @@ where utxo_set_verifier: C::UtxoSetVerifierVar, cs: &mut ConstraintSystem<C>, ) { - let mut input_sum = C::AssetBalanceVar::from_default(cs, Secret); - let mut output_sum = C::AssetBalanceVar::from_default(cs, Secret); - let mut secret_asset_ids = Vec::new(); + let mut secret_asset_ids = Vec::with_capacity(SENDERS + RECEIVERS); - for source in participants.sources { - input_sum += source; - } - - for sender in participants.senders { - let asset = sender.get_well_formed_asset(cs, &commitment_scheme, &utxo_set_verifier); - input_sum += asset.value; - secret_asset_ids.push(asset.id); - } - - for receiver in participants.receivers { - let asset = receiver.get_well_formed_asset(cs, &commitment_scheme); - output_sum += asset.value; - secret_asset_ids.push(asset.id); - } + let input_sum = participants + .senders + .into_iter() + .map(|s| { + let asset = s.get_well_formed_asset(cs, &commitment_scheme, &utxo_set_verifier); + secret_asset_ids.push(asset.id); + asset.value + }) + .chain(participants.sources) + .reduce(Add::add) + .unwrap(); - for sink in participants.sinks { - output_sum += sink; - } + let output_sum = participants + .receivers + .into_iter() + .map(|r| { + let asset = r.get_well_formed_asset(cs, &commitment_scheme); + secret_asset_ids.push(asset.id); + asset.value + }) + .chain(participants.sinks) + .reduce(Add::add) + .unwrap(); cs.assert_eq(&input_sum, &output_sum); diff --git a/manta-crypto/src/prf.rs b/manta-crypto/src/prf.rs index 92d4a72fd..0e69de3a9 100644 --- a/manta-crypto/src/prf.rs +++ b/manta-crypto/src/prf.rs @@ -22,14 +22,11 @@ pub trait PseudorandomFunctionFamily { type Seed: ?Sized; /// PRF Input Type - type Input; + type Input: ?Sized; /// PRF Output Type type Output; /// Evaluates the PRF at the `seed` and `input`. fn evaluate(seed: &Self::Seed, input: &Self::Input) -> Self::Output; - - /// Evaluates the PRF at the `seed` with input set to a zero value. - fn evaluate_zero(seed: &Self::Seed) -> Self::Output; } diff --git a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs index 4c35ce3c1..2e9cc828e 100644 --- a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs @@ -19,10 +19,13 @@ use alloc::{vec, vec::Vec}; use ark_ff::{fields::Field, PrimeField}; use ark_r1cs_std::{ - alloc::AllocVar, bits::boolean::Boolean, eq::EqGadget, fields::fp::FpVar, uint8::UInt8, R1CSVar, + alloc::AllocVar, bits::boolean::Boolean, eq::EqGadget, fields::fp::FpVar, uint8::UInt8, }; use ark_relations::{ns, r1cs as ark_r1cs}; -use core::{borrow::Borrow, ops::AddAssign}; +use core::{ + borrow::Borrow, + ops::{Add, AddAssign}, +}; use manta_accounting::{AssetBalance, AssetId}; use manta_crypto::constraint::{ measure::Measure, reflection::HasAllocation, types::Bool, Allocation, AllocationMode, @@ -270,45 +273,6 @@ pub struct ByteArrayVar<F, const N: usize>(Vec<UInt8<F>>) where F: Field; -impl<F, const N: usize> ByteArrayVar<F, N> -where - F: Field, -{ - /// Returns an reference to the internal arkworks constriant system. - #[inline] - pub(crate) fn constraint_system_ref(&self) -> ark_r1cs::ConstraintSystemRef<F> { - self.0.cs() - } - - /// Allocates a new byte vector according to the `allocation` entry. - #[inline] - pub(crate) fn allocate( - cs: &ark_r1cs::ConstraintSystemRef<F>, - allocation: Allocation<[u8; N], PublicOrSecret>, - ) -> Self - where - F: PrimeField, - { - Self( - match allocation { - Allocation::Known(this, PublicOrSecret::Public) => { - UInt8::new_input_vec(ns!(cs, "byte array public input"), this) - } - Allocation::Known(this, PublicOrSecret::Secret) => { - UInt8::new_witness_vec(ns!(cs, "byte array secret witness"), this) - } - Allocation::Unknown(PublicOrSecret::Public) => { - UInt8::new_input_vec(ns!(cs, "byte array public input"), &[0; N]) - } - Allocation::Unknown(PublicOrSecret::Secret) => { - UInt8::new_witness_vec(ns!(cs, "byte array secret witness"), &vec![None; N]) - } - } - .expect("Variable allocation is not allowed to fail."), - ) - } -} - impl<F, const N: usize> AsRef<[UInt8<F>]> for ByteArrayVar<F, N> where F: Field, @@ -357,7 +321,23 @@ where cs: &mut ArkConstraintSystem<F>, allocation: Allocation<Self::Type, Self::Mode>, ) -> Self { - Self::allocate(&cs.cs, allocation) + Self( + match allocation { + Allocation::Known(this, PublicOrSecret::Public) => { + UInt8::new_input_vec(ns!(cs.cs, "byte array public input"), this) + } + Allocation::Known(this, PublicOrSecret::Secret) => { + UInt8::new_witness_vec(ns!(cs.cs, "byte array secret witness"), this) + } + Allocation::Unknown(PublicOrSecret::Public) => { + UInt8::new_input_vec(ns!(cs.cs, "byte array public input"), &[0; N]) + } + Allocation::Unknown(PublicOrSecret::Secret) => { + UInt8::new_witness_vec(ns!(cs.cs, "byte array secret witness"), &vec![None; N]) + } + } + .expect("Variable allocation is not allowed to fail."), + ) } } @@ -468,7 +448,7 @@ where field_point: FpVar<F>, /// Byte Array - bytes: ByteArrayVar<F, { AssetBalance::SIZE }>, + bytes: Option<ByteArrayVar<F, { AssetBalance::SIZE }>>, } impl<F> AssetBalanceVar<F> @@ -477,7 +457,7 @@ where { /// Builds a new [`AssetBalanceVar`] from `field_point` and `bytes`. #[inline] - fn new(field_point: FpVar<F>, bytes: ByteArrayVar<F, { AssetBalance::SIZE }>) -> Self { + fn new(field_point: FpVar<F>, bytes: Option<ByteArrayVar<F, { AssetBalance::SIZE }>>) -> Self { Self { field_point, bytes } } } @@ -493,7 +473,9 @@ where where A: ConcatAccumulator<Self::Item> + ?Sized, { - self.bytes.concat(accumulator); + if let Some(bytes) = &self.bytes { + bytes.concat(accumulator); + } } } @@ -513,11 +495,11 @@ where match allocation { Allocation::Known(this, mode) => Self::new( Fp(F::from(this.0)).as_known(cs, mode), - this.into_bytes().as_known(cs, mode), + Some(this.into_bytes().as_known(cs, mode)), ), Allocation::Unknown(mode) => Self::new( Fp::as_unknown(cs, mode), - <[u8; AssetBalance::SIZE]>::as_unknown(cs, mode), + Some(<[u8; AssetBalance::SIZE]>::as_unknown(cs, mode)), ), } } @@ -545,6 +527,19 @@ where } } +impl<F> Add for AssetBalanceVar<F> +where + F: PrimeField, +{ + type Output = Self; + + #[inline] + fn add(self, rhs: Self) -> Self { + // TODO: This is not a good abstraction. + Self::new(self.field_point + rhs.field_point, None) + } +} + impl<F> AddAssign for AssetBalanceVar<F> where F: PrimeField, diff --git a/manta-pay/src/crypto/prf/blake2s.rs b/manta-pay/src/crypto/prf/blake2s.rs index b27772875..f7e42817e 100644 --- a/manta-pay/src/crypto/prf/blake2s.rs +++ b/manta-pay/src/crypto/prf/blake2s.rs @@ -126,11 +126,6 @@ impl PseudorandomFunctionFamily for Blake2s { .expect("As of arkworks 0.3.0, this never fails."), ) } - - #[inline] - fn evaluate_zero(seed: &Self::Seed) -> Self::Output { - Self::evaluate(seed, &Default::default()) - } } /// Blake2s PRF Constraint System Implementations @@ -397,17 +392,5 @@ pub mod constraint { .expect("Failure outcomes are not accepted."), ) } - - #[inline] - fn evaluate_zero(seed: &Self::Seed) -> Self::Output { - // FIXME: This is super hacky! Find a more sustainable way to do this. - Self::evaluate( - seed, - &Blake2sInputVar(ByteArrayVar::allocate( - &seed.0.constraint_system_ref(), - Allocation::Known(&[0; 32], Secret.into()), - )), - ) - } } } From 5f1fd3152c4f57b47de217d5d756f7a988c3ff49 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sat, 23 Oct 2021 02:41:21 -0400 Subject: [PATCH 107/275] feat(docs): add comments about some special cases of signer behavior --- Cargo.toml | 24 ++++++++++++------------ manta-accounting/src/wallet/signer.rs | 12 ++++++++++++ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bcc817fc6..8457376cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ homepage = "https://github.com/Manta-Network" documentation = "https://github.com/Manta-Network/manta-rs" categories = [""] keywords = [""] -description = "Manta Network main crate." +description = "Rust Crates for the Manta Network Ecosystem" publish = false [package.metadata.docs.rs] @@ -24,6 +24,17 @@ is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } +[workspace] +resolver = "2" +members = [ + "manta-accounting", + "manta-cli", + "manta-codec", + "manta-crypto", + "manta-pay", + "manta-util", +] + [features] # Default Features default = [] @@ -45,14 +56,3 @@ parity-scale-codec = { version = "2.2.0", default-features = false } manta-accounting = { path = "manta-accounting", features = ["test"] } manta-crypto = { path = "manta-crypto", features = ["test"] } -[workspace] -resolver = "2" -members = [ - "manta-accounting", - "manta-cli", - "manta-codec", - "manta-crypto", - "manta-pay", - "manta-util", -] - diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index c053e643d..181132608 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -22,6 +22,9 @@ // `sign`. Will have to upgrade `Rollback` and `manta_crypto::merkle_tree::fork` as well. // TODO: Add checkpointing/garbage-collection in `utxo_set` so we can remove old UTXOs once they // are irrelevant. Once we create a sender and its transaction succeeds we can drop the UTXO. +// TODO: Should have a mode on the signer where we return a generic error which reveals no detail +// about what went wrong during signing. The kind of error returned from a signing could +// reveal information about the internal state (privacy leak, not a secrecy leak). use crate::{ asset::{Asset, AssetBalance, AssetId, AssetMap}, @@ -729,6 +732,15 @@ where if let Some(KeyOwned { inner, index }) = self.signer.find_external_asset::<C>(&encrypted_asset) { + // TODO: We can actually check at this point whether the `utxo` is valid, by + // computing the utxo that lives in the sender of `index`, this way, a future + // call to `try_upgrade` should never fail. Also, if the `utxo` is replaced + // by the encrypted note (in a future version of the protocol), we can + // even remove the entire failure branch alltogether since the ledger would + // have to verify the correctness of the encrypted note before storing it. + // Currently, if the `utxo` doesn't match, it should just be stored in the + // verified set as non-provable since it can never be used, it is burnt. + // assets.push(inner); self.assets.insert(index.reduce(), inner); self.utxo_set.insert_provable(&utxo); From d38eaadec9bb357c40cc26079079e0c75e07733a Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sat, 23 Oct 2021 02:50:56 -0400 Subject: [PATCH 108/275] fix(docs): fix previous comments --- manta-accounting/src/wallet/signer.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 181132608..897832c68 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -462,8 +462,6 @@ where C: transfer::Configuration<SecretKey = D::SecretKey>, R: CryptoRng + RngCore + ?Sized, { - // TODO: Simplify this so that `into_shielded` and `into_receiver` can be replaced by a - // one-step `into_receiver` call on `Identity`. self.next_internal_identity() .map_err(InternalIdentityError::SecretKeyError)? .map_ok(move |identity| identity.into_receiver(commitment_scheme, asset, rng)) @@ -732,14 +730,19 @@ where if let Some(KeyOwned { inner, index }) = self.signer.find_external_asset::<C>(&encrypted_asset) { - // TODO: We can actually check at this point whether the `utxo` is valid, by - // computing the utxo that lives in the sender of `index`, this way, a future - // call to `try_upgrade` should never fail. Also, if the `utxo` is replaced - // by the encrypted note (in a future version of the protocol), we can - // even remove the entire failure branch alltogether since the ledger would - // have to verify the correctness of the encrypted note before storing it. - // Currently, if the `utxo` doesn't match, it should just be stored in the - // verified set as non-provable since it can never be used, it is burnt. + // FIXME: We need to actually check at this point whether the `utxo` is valid, by + // computing the UTXO that lives at the `Sender` of `index`, this way, a + // future call to `try_upgrade` will never fail. If the call to `try_upgrade` + // fails, we need to mark the coin as burnt, or it will show up again in + // later calls to the signer (during coin selection). Currently, if the + // `utxo` does not match it should be stored in the verified set as + // non-provable and the asset should not be added to the asset map, since the + // asset is effectively burnt. + // + // If, in a future version of the protocol, the `utxo` is replaced by a + // ZKP-compatible encrypted note, we might be able to remove this check + // since the ledger would have to guarantee that the asset is not burnt, + // whereas, right now, it does not. // assets.push(inner); self.assets.insert(index.reduce(), inner); From 999bf37130c9090c57fc305df3215bb22934fc07 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 10 Nov 2021 00:24:03 -0500 Subject: [PATCH 109/275] wip: start rewrite to new spec --- manta-accounting/Cargo.toml | 2 +- manta-accounting/src/identity.rs | 2 +- manta-accounting/src/transfer.rs | 4 +- manta-accounting/src/wallet/signer.rs | 6 - manta-crypto/src/encryption/ies.rs | 439 ++++++++++++++++++++++++++ manta-crypto/src/encryption/mod.rs | 21 ++ manta-crypto/src/lib.rs | 3 +- manta-pay/src/crypto/ies.rs | 3 +- 8 files changed, 467 insertions(+), 13 deletions(-) create mode 100644 manta-crypto/src/encryption/ies.rs create mode 100644 manta-crypto/src/encryption/mod.rs diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index ffe9f9d99..296dfe4ce 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -43,7 +43,7 @@ derivative = { version = "2.2.0", default-features = false, features = ["use_cor derive_more = { version = "0.99.16", default-features = false, features = ["add", "add_assign", "display", "from", "mul", "mul_assign", "sum"] } manta-crypto = { path = "../manta-crypto" } manta-util = { path = "../manta-util" } -zeroize = { version = "1.4.2", optional = true, default-features = false, features = ["alloc", "zeroize_derive"] } +zeroize = { version = "1.4.3", optional = true, default-features = false, features = ["alloc", "zeroize_derive"] } [dev-dependencies] rand = "0.8.4" diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 38078c947..859e8a9f7 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -26,7 +26,7 @@ use crate::asset::{Asset, AssetBalance, AssetId, AssetVar}; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use manta_crypto::{ commitment::{CommitmentScheme, Input as CommitmentInput}, - ies::{self, EncryptedMessage, IntegratedEncryptionScheme}, + encryption::ies::{self, EncryptedMessage, IntegratedEncryptionScheme}, rand::{CryptoRng, Rand, RngCore, Sample, SeedableRng, Standard, TrySample}, set::{MembershipProof, VerifiedSet}, PseudorandomFunctionFamily, diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index e58ac2532..ec7fdfba6 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -38,7 +38,7 @@ use manta_crypto::{ Allocation, Constant, ConstraintSystem as _, Derived, Equal, Input as ProofSystemInput, ProofSystem, Public, PublicOrSecret, Secret, Variable, VariableSource, }, - ies::{EncryptedMessage, IntegratedEncryptionScheme}, + encryption::ies::{EncryptedMessage, IntegratedEncryptionScheme}, rand::{CryptoRng, RngCore}, set::{constraint::VerifierVariable, VerifiedSet}, }; @@ -1048,6 +1048,8 @@ where where L: TransferLedger<C>, { + // FIXME: The ledger needs to check whether or not senders and receivers are unique. Should + // this be done in this method or is it something the ledger can do "per-validation". let source_posting_keys = ledger .check_source_balances(self.sources) .map_err(TransferPostError::InsufficientPublicBalance)?; diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 897832c68..de7a2fbef 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -738,12 +738,6 @@ where // `utxo` does not match it should be stored in the verified set as // non-provable and the asset should not be added to the asset map, since the // asset is effectively burnt. - // - // If, in a future version of the protocol, the `utxo` is replaced by a - // ZKP-compatible encrypted note, we might be able to remove this check - // since the ledger would have to guarantee that the asset is not burnt, - // whereas, right now, it does not. - // assets.push(inner); self.assets.insert(index.reduce(), inner); self.utxo_set.insert_provable(&utxo); diff --git a/manta-crypto/src/encryption/ies.rs b/manta-crypto/src/encryption/ies.rs new file mode 100644 index 000000000..ff2c2ac3c --- /dev/null +++ b/manta-crypto/src/encryption/ies.rs @@ -0,0 +1,439 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Integrated Encryption Schemes and Encrypted Messages + +// FIXME: add zeroize for secret keys + +use core::{fmt::Debug, hash::Hash}; +use rand_core::{CryptoRng, RngCore}; + +pub(super) mod prelude { + #[doc(inline)] + pub use super::IntegratedEncryptionScheme; +} + +/// Integrated Encryption Scheme Trait +pub trait IntegratedEncryptionScheme { + /// Public Key Type + type PublicKey; + + /// Secret Key Type + type SecretKey; + + /// Plaintext Type + type Plaintext; + + /// Ciphertext Type + type Ciphertext; + + /// Encryption/Decryption Error Type + type Error; + + /// Generates a public/secret keypair. + fn generate_keys<R>(rng: &mut R) -> KeyPair<Self> + where + R: CryptoRng + RngCore + ?Sized; + + /// Generates a public key. + /// + /// This enables an optimization path whenever decryption is not necessary. + #[inline] + fn generate_public_key<R>(rng: &mut R) -> PublicKey<Self> + where + R: CryptoRng + RngCore + ?Sized, + { + Self::generate_keys(rng).into_public() + } + + /// Generates a secret key. + /// + /// This enables an optimization path whenever encryption is not necessary. + #[inline] + fn generate_secret_key<R>(rng: &mut R) -> SecretKey<Self> + where + R: CryptoRng + RngCore + ?Sized, + { + Self::generate_keys(rng).into_secret() + } + + /// Encrypts the `plaintext` with the `public_key`, generating an [`EncryptedMessage`]. + fn encrypt<R>( + plaintext: &Self::Plaintext, + public_key: Self::PublicKey, + rng: &mut R, + ) -> Result<EncryptedMessage<Self>, Self::Error> + where + R: CryptoRng + RngCore + ?Sized; + + /// Generates a public/secret keypair and then encrypts the `plaintext` with the generated + /// public key, returning an [`EncryptedMessage`] and a [`SecretKey`]. + #[inline] + fn generate_keys_and_encrypt<R>( + plaintext: &Self::Plaintext, + rng: &mut R, + ) -> Result<(EncryptedMessage<Self>, SecretKey<Self>), Self::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + Self::generate_keys(rng).encrypt(plaintext, rng) + } + + /// Generates a public key and then encrypts the `plaintext` with the generated public key, + /// returning an [`EncryptedMessage`]. + /// + /// This enables an optimization path whenever decryption is not necessary. + #[inline] + fn generate_public_key_and_encrypt<R>( + plaintext: &Self::Plaintext, + rng: &mut R, + ) -> Result<EncryptedMessage<Self>, Self::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + Self::generate_public_key(rng).encrypt(plaintext, rng) + } + + /// Decrypts the `ciphertext` with the `secret_key`, returning the + /// [`Plaintext`](Self::Plaintext). + fn decrypt( + ciphertext: &Self::Ciphertext, + secret_key: Self::SecretKey, + ) -> Result<Self::Plaintext, Self::Error>; + + /// Generates a secret key and then decrypts the `ciphertext` with the generated secret key, + /// returing the [`Plaintext`](Self::Plaintext). + /// + /// This enables an optimization path whenever encryption is not necessary. + #[inline] + fn generate_secret_key_and_decrypt<R>( + ciphertext: &Self::Ciphertext, + rng: &mut R, + ) -> Result<Self::Plaintext, Self::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + Self::decrypt(ciphertext, Self::generate_secret_key(rng).secret_key) + } +} + +/// [`IntegratedEncryptionScheme`] Public Key +pub struct PublicKey<I> +where + I: IntegratedEncryptionScheme + ?Sized, +{ + /// Public Key + public_key: I::PublicKey, +} + +impl<I> PublicKey<I> +where + I: IntegratedEncryptionScheme + ?Sized, +{ + /// Builds a new [`PublicKey`] from `I::PublicKey`. + #[inline] + pub fn new(public_key: I::PublicKey) -> Self { + Self { public_key } + } + + /// Generates a public key. + /// + /// This enables an optimization path whenever decryption is not necessary. + #[inline] + pub fn generate<R>(rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + I::generate_public_key(rng) + } + + /// Encrypts the `plaintext` with `self`, returning an [`EncryptedMessage`]. + #[inline] + pub fn encrypt<R>( + self, + plaintext: &I::Plaintext, + rng: &mut R, + ) -> Result<EncryptedMessage<I>, I::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + I::encrypt(plaintext, self.public_key, rng) + } + + /// Generates a public key and then encrypts the `plaintext` with the generated public key, + /// returning an [`EncryptedMessage`]. + /// + /// This enables an optimization path whenever decryption is not necessary. + #[inline] + pub fn generate_and_encrypt<R>( + plaintext: &I::Plaintext, + rng: &mut R, + ) -> Result<EncryptedMessage<I>, I::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + I::generate_public_key_and_encrypt(plaintext, rng) + } +} + +/// [`IntegratedEncryptionScheme`] Secret Key +pub struct SecretKey<I> +where + I: IntegratedEncryptionScheme + ?Sized, +{ + /// Secret Key + secret_key: I::SecretKey, +} + +impl<I> SecretKey<I> +where + I: IntegratedEncryptionScheme + ?Sized, +{ + /// Builds a new [`SecretKey`] from `I::SecretKey`. + #[inline] + pub fn new(secret_key: I::SecretKey) -> Self { + Self { secret_key } + } + + /// Generates a secret key. + /// + /// This enables an optimization path whenever encryption is not necessary. + #[inline] + pub fn generate<R>(rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + I::generate_secret_key(rng) + } + + /// Decrypts the `message` with `self` returning the + /// [`Plaintext`](IntegratedEncryptionScheme::Plaintext). + #[inline] + pub fn decrypt(self, message: &EncryptedMessage<I>) -> Result<I::Plaintext, I::Error> { + message.decrypt(self) + } + + /// Generates a secret key and then decrypts the `message` with the generated secret key, + /// returing the [`Plaintext`](IntegratedEncryptionScheme::Plaintext). + /// + /// This enables an optimization path whenever encryption is not necessary. + #[inline] + pub fn generate_and_decrypt<R>( + message: &EncryptedMessage<I>, + rng: &mut R, + ) -> Result<I::Plaintext, I::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + I::generate_secret_key(rng).decrypt(message) + } +} + +/// [`IntegratedEncryptionScheme`] Key Pair +pub struct KeyPair<I> +where + I: IntegratedEncryptionScheme + ?Sized, +{ + /// Public Key + public_key: I::PublicKey, + + /// Secret Key + secret_key: I::SecretKey, +} + +impl<I> KeyPair<I> +where + I: IntegratedEncryptionScheme + ?Sized, +{ + /// Builds a new [`KeyPair`] from a `public_key` and a `secret_key`. + #[inline] + pub fn new(public_key: I::PublicKey, secret_key: I::SecretKey) -> Self { + Self { + public_key, + secret_key, + } + } + + /// Generates a public/secret keypair. + #[inline] + pub fn generate<R>(rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + I::generate_keys(rng) + } + + /// Returns the public side of the key pair. + #[inline] + fn into_public(self) -> PublicKey<I> { + PublicKey::new(self.public_key) + } + + /// Returns the secret side of the key pair. + #[inline] + fn into_secret(self) -> SecretKey<I> { + SecretKey::new(self.secret_key) + } + + /// Encrypts the `plaintext` with `self`, returning an [`EncryptedMessage`]. + #[inline] + pub fn encrypt<R>( + self, + plaintext: &I::Plaintext, + rng: &mut R, + ) -> Result<(EncryptedMessage<I>, SecretKey<I>), I::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + let (public_key, secret_key) = self.into(); + Ok((public_key.encrypt(plaintext, rng)?, secret_key)) + } +} + +impl<I> From<KeyPair<I>> for (PublicKey<I>, SecretKey<I>) +where + I: IntegratedEncryptionScheme + ?Sized, +{ + #[inline] + fn from(keypair: KeyPair<I>) -> Self { + ( + PublicKey::new(keypair.public_key), + SecretKey::new(keypair.secret_key), + ) + } +} + +/// Encrypted Message +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "I::Ciphertext: Clone"), + Copy(bound = "I::Ciphertext: Copy"), + Debug(bound = "I::Ciphertext: Debug"), + Default(bound = "I::Ciphertext: Default"), + Eq(bound = "I::Ciphertext: Eq"), + Hash(bound = "I::Ciphertext: Hash"), + PartialEq(bound = "I::Ciphertext: PartialEq") +)] +pub struct EncryptedMessage<I> +where + I: IntegratedEncryptionScheme + ?Sized, +{ + /// Message Ciphertext + ciphertext: I::Ciphertext, +} + +impl<I> EncryptedMessage<I> +where + I: IntegratedEncryptionScheme + ?Sized, +{ + /// Builds a new [`EncryptedMessage`] from + /// [`I::Ciphertext`](IntegratedEncryptionScheme::Ciphertext). + #[inline] + pub fn new(ciphertext: I::Ciphertext) -> Self { + Self { ciphertext } + } + + /// Encrypts the `plaintext` with the `public_key`, returning an [`EncryptedMessage`]. + #[inline] + pub fn encrypt<R>( + plaintext: &I::Plaintext, + public_key: I::PublicKey, + rng: &mut R, + ) -> Result<Self, I::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + I::encrypt(plaintext, public_key, rng) + } + + /// Generates a public/secret keypair and then encrypts the `plaintext` with the generated + /// public key, returning an [`EncryptedMessage`] and a [`SecretKey`]. + #[inline] + pub fn generate_keys_and_encrypt<R>( + plaintext: &I::Plaintext, + rng: &mut R, + ) -> Result<(Self, SecretKey<I>), I::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + I::generate_keys_and_encrypt(plaintext, rng) + } + + /// Generates a public key and then encrypts the `plaintext` with the generated public key, + /// returning an [`EncryptedMessage`]. + /// + /// This enables an optimization path whenever decryption is not necessary. + #[inline] + pub fn generate_public_key_and_encrypt<R>( + plaintext: &I::Plaintext, + rng: &mut R, + ) -> Result<Self, I::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + I::generate_public_key_and_encrypt(plaintext, rng) + } + + /// Decrypts `self` with the `secret_key` returning the + /// [`Plaintext`](IntegratedEncryptionScheme::Plaintext). + #[inline] + pub fn decrypt(&self, secret_key: SecretKey<I>) -> Result<I::Plaintext, I::Error> { + I::decrypt(&self.ciphertext, secret_key.secret_key) + } + + /// Generates a secret key and then decrypts `self` with the generated secret key, + /// returing the [`Plaintext`](IntegratedEncryptionScheme::Plaintext). + /// + /// This enables an optimization path whenever encryption is not necessary. + #[inline] + pub fn generate_secret_key_and_decrypt<R>(&self, rng: &mut R) -> Result<I::Plaintext, I::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + I::generate_secret_key_and_decrypt(&self.ciphertext, rng) + } +} + +/// Testing Framework +#[cfg(feature = "test")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] +pub mod test { + use super::*; + use core::fmt::Debug; + + /// Tests encryption/decryption of a sample `plaintext`. + #[inline] + pub fn assert_decryption_of_encryption<I, R>(plaintext: &I::Plaintext, rng: &mut R) + where + I: IntegratedEncryptionScheme, + I::Plaintext: Debug + PartialEq, + I::Error: Debug, + R: CryptoRng + RngCore + ?Sized, + { + let (public_key, secret_key) = I::generate_keys(rng).into(); + let reconstructed_plaintext = secret_key + .decrypt( + &public_key + .encrypt(plaintext, rng) + .expect("Unable to encrypt plaintext."), + ) + .expect("Unable to decrypt plaintext."); + assert_eq!( + plaintext, &reconstructed_plaintext, + "Plaintext didn't match decrypted ciphertext." + ); + } +} diff --git a/manta-crypto/src/encryption/mod.rs b/manta-crypto/src/encryption/mod.rs new file mode 100644 index 000000000..3d9b2c679 --- /dev/null +++ b/manta-crypto/src/encryption/mod.rs @@ -0,0 +1,21 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Encryption Primitives + +pub mod ies; + +pub use ies::prelude::*; diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index f4112604d..99a7efd56 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -27,11 +27,10 @@ mod prf; pub mod commitment; pub mod constraint; -pub mod ies; +pub mod encryption; pub mod merkle_tree; pub mod rand; pub mod set; pub use commitment::prelude::*; -pub use ies::prelude::*; pub use prf::*; diff --git a/manta-pay/src/crypto/ies.rs b/manta-pay/src/crypto/ies.rs index 552c936f9..6381d724a 100644 --- a/manta-pay/src/crypto/ies.rs +++ b/manta-pay/src/crypto/ies.rs @@ -27,9 +27,8 @@ use blake2::{Blake2s, Digest}; use generic_array::GenericArray; use manta_accounting::Asset; use manta_crypto::{ - ies::{self, KeyPair}, + encryption::ies::{self, IntegratedEncryptionScheme, KeyPair}, rand::{CryptoRng, RngCore}, - IntegratedEncryptionScheme, }; use manta_util::into_array_unchecked; use x25519_dalek::{EphemeralSecret, PublicKey as PubKey, StaticSecret}; From fcee857af288aaa1b8e05a59dfcb19af314f9507 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 10 Nov 2021 16:05:24 -0500 Subject: [PATCH 110/275] chore: update version number --- Cargo.toml | 2 +- manta-accounting/Cargo.toml | 2 +- manta-cli/Cargo.toml | 2 +- manta-codec/Cargo.toml | 2 +- manta-crypto/Cargo.toml | 2 +- manta-pay/Cargo.toml | 2 +- manta-pay/src/crypto/ies.rs | 2 +- manta-util/Cargo.toml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8457376cc..d15475eb2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "manta" edition = "2018" -version = "0.3.0" +version = "0.4.0" authors = ["Manta Network <contact@manta.network>"] readme = "README.md" license-file = "LICENSE" diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 296dfe4ce..898e3e97a 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "manta-accounting" edition = "2018" -version = "0.3.0" +version = "0.4.0" authors = ["Manta Network <contact@manta.network>"] readme = "README.md" license-file = "LICENSE" diff --git a/manta-cli/Cargo.toml b/manta-cli/Cargo.toml index 491e1487c..f10c0a39d 100644 --- a/manta-cli/Cargo.toml +++ b/manta-cli/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "manta-cli" edition = "2018" -version = "0.3.0" +version = "0.4.0" authors = ["Manta Network <contact@manta.network>"] readme = "README.md" license-file = "LICENSE" diff --git a/manta-codec/Cargo.toml b/manta-codec/Cargo.toml index 9da4432fe..e8e727beb 100644 --- a/manta-codec/Cargo.toml +++ b/manta-codec/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "manta-codec" edition = "2018" -version = "0.3.0" +version = "0.4.0" authors = ["Manta Network <contact@manta.network>"] readme = "README.md" license-file = "LICENSE" diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index 2b3758778..6708e9180 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "manta-crypto" edition = "2018" -version = "0.3.0" +version = "0.4.0" authors = ["Manta Network <contact@manta.network>"] readme = "README.md" license-file = "LICENSE" diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 4d8e7f877..a023def3d 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "manta-pay" edition = "2018" -version = "0.3.0" +version = "0.4.0" authors = ["Manta Network <contact@manta.network>"] readme = "README.md" license-file = "LICENSE" diff --git a/manta-pay/src/crypto/ies.rs b/manta-pay/src/crypto/ies.rs index 6381d724a..6bbfec7ad 100644 --- a/manta-pay/src/crypto/ies.rs +++ b/manta-pay/src/crypto/ies.rs @@ -190,7 +190,7 @@ impl IntegratedEncryptionScheme for IES { #[cfg(test)] mod test { use super::*; - use manta_crypto::{ies::test as ies_test, rand::Rand}; + use manta_crypto::{encryption::ies::test as ies_test, rand::Rand}; use rand::thread_rng; /// Tests encryption/decryption of a random asset. diff --git a/manta-util/Cargo.toml b/manta-util/Cargo.toml index 7f554e3b0..1534af460 100644 --- a/manta-util/Cargo.toml +++ b/manta-util/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "manta-util" edition = "2018" -version = "0.3.0" +version = "0.4.0" authors = ["Manta Network <contact@manta.network>"] readme = "README.md" license-file = "LICENSE" From bdb68ded6cb83dbff81583d9bb28907898d3d482 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 10 Nov 2021 19:40:01 -0500 Subject: [PATCH 111/275] wip: add Accumulator abstractions --- manta-crypto/Cargo.toml | 2 +- manta-crypto/src/accumulator.rs | 385 +++++++++++++++++++++++++++ manta-crypto/src/lib.rs | 1 + manta-crypto/src/merkle_tree/tree.rs | 90 ++++++- 4 files changed, 473 insertions(+), 5 deletions(-) create mode 100644 manta-crypto/src/accumulator.rs diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index 6708e9180..2a7e4f123 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -26,7 +26,7 @@ maintenance = { status = "actively-developed" } [features] # Constraint System Gadgets -# TODO: constraints = [] +constraint = [] # Enable `getrandom` Entropy Source getrandom = ["rand_core/getrandom"] diff --git a/manta-crypto/src/accumulator.rs b/manta-crypto/src/accumulator.rs new file mode 100644 index 000000000..938d8fa4b --- /dev/null +++ b/manta-crypto/src/accumulator.rs @@ -0,0 +1,385 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Dynamic Cryptographic Accumulators + +/// Accumulator Membership Verifier +pub trait Verifier { + /// Item Type + type Item: ?Sized; + + /// Public Checkpoint Type + type Checkpoint; + + /// Secret Witness Type + type Witness; + + /// Verifies that `item` is stored in a known accumulator with `checkpoint` and `witness`. + fn verify( + &self, + item: &Self::Item, + checkpoint: &Self::Checkpoint, + witness: &Self::Witness, + ) -> bool; +} + +/// Accumulator +pub trait Accumulator { + /// Item Type + type Item: ?Sized; + + /// Public Checkpoint Type + type Checkpoint; + + /// Secret Witness Type + type Witness; + + /// Returns `true` if the accumulated value of `self` matches the given `checkpoint`. + fn matching_checkpoint(&self, checkpoint: &Self::Checkpoint) -> bool; + + /// Inserts `item` into `self` with the guarantee that `self` can later return a valid + /// membership proof for `item` with a call to [`prove`](Self::prove). This method returns + /// `false` if the maximum capacity of the accumulator would be exceeded by inserting `item`. + fn insert(&mut self, item: &Self::Item) -> bool; + + /// Returns a membership proof for `item` if it is contained in `self`. + fn prove(&self, item: &Self::Item) -> Option<MembershipProof<Self>>; + + /// Returns `true` if `item` is stored in `self`. + /// + /// # Implementation Note + /// + /// This method must at least return `true` for `item` whenever a valid proof of membership + /// exists. It may return `true` in other cases when `self` knows that it has `item` stored but + /// cannot return a proof for it, like in the case of [`OptimizedAccumulator`] implementations. + /// In other words, this method is allowed to return false negatives, but not false positives. + #[inline] + fn contains(&self, item: &Self::Item) -> bool { + self.prove(item).is_some() + } + + /// Verifies that `item` is stored in `self` with `checkpoint` and `witness`. + fn verify( + &self, + item: &Self::Item, + checkpoint: &Self::Checkpoint, + witness: &Self::Witness, + ) -> bool; +} + +impl<A> Verifier for A +where + A: Accumulator + ?Sized, +{ + type Item = A::Item; + + type Checkpoint = A::Checkpoint; + + type Witness = A::Witness; + + #[inline] + fn verify( + &self, + item: &Self::Item, + checkpoint: &Self::Checkpoint, + witness: &Self::Witness, + ) -> bool { + self.verify(item, checkpoint, witness) + } +} + +/// Optimized Accumulator +pub trait OptimizedAccumulator: Accumulator { + /// Inserts `item` into `self` without the guarantee that `self` with be able to return a proof + /// of membership for `item`. This method returns `false` if the maximum capacity of the + /// accumulator would be exceeded by inserting `item`. + /// + /// # Implementation Note + /// + /// By default, this method uses [`insert`] to store `item` in `self`. Since this method may + /// insert items which will never have membership proofs, the [`contains`] method is allowed to + /// return `false` for those items if necessary. In other words, the [`contains`] method is + /// allowed to return false negatives, but not false positives. + /// + /// [`insert`]: Accumulator::insert + /// [`contains`]: Accumulator::contains + #[inline] + fn insert_nonprovable(&mut self, item: &Self::Item) -> bool { + self.insert(item) + } +} + +/// Accumulator Membership Proof +pub struct MembershipProof<V> +where + V: Verifier + ?Sized, +{ + /// Public Checkpoint + checkpoint: V::Checkpoint, + + /// Secret Witness + witness: V::Witness, +} + +impl<V> MembershipProof<V> +where + V: Verifier + ?Sized, +{ + /// Builds a new [`MembershipProof`] from `checkpoint` and `witness`. + #[inline] + pub fn new(checkpoint: V::Checkpoint, witness: V::Witness) -> Self { + Self { + checkpoint, + witness, + } + } + + /// Converts `self` into its checkpoint, dropping the [`V::Witness`](Verifier::Witness). + #[inline] + pub fn into_checkpoint(self) -> V::Checkpoint { + self.checkpoint + } + + /// Returns `true` if the accumulated value of `accumulator` matches the internal checkpoint + /// inside of `self`. + #[inline] + pub fn matching_checkpoint<A>(&self, accumulator: &A) -> bool + where + A: Accumulator<Item = V::Item, Checkpoint = V::Checkpoint, Witness = V::Witness>, + { + accumulator.matching_checkpoint(&self.checkpoint) + } + + /// Verifies that `item` is stored in a known accumulator using `verifier`. + #[inline] + pub fn verify(&self, item: &V::Item, verifier: &V) -> bool { + verifier.verify(item, &self.checkpoint, &self.witness) + } +} + +/// Constraint System Gadgets for Accumulators +#[cfg(feature = "constraint")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "constraint")))] +pub mod constraint { + use super::*; + use crate::constraint::{ + reflection::{unknown, HasAllocation, HasVariable, Mode, Var}, + Allocation, AllocationMode, AllocationSystem, ConstraintSystem, Derived, Variable, + }; + use core::marker::PhantomData; + + /// Membership Proof Allocation Mode Entry + #[derive(derivative::Derivative)] + #[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] + pub struct MembershipProofModeEntry<CheckpointMode, WitnessMode> { + /// Public Checkpoint Allocation Mode + pub checkpoint: CheckpointMode, + + /// Secret Witness Allocation Mode + pub witness: WitnessMode, + } + + impl<CheckpointMode, WitnessMode> MembershipProofModeEntry<CheckpointMode, WitnessMode> { + /// Builds a new [`MembershipProofModeEntry`] from a `checkpoint` mode and a `witness` mode. + #[inline] + pub fn new(checkpoint: CheckpointMode, witness: WitnessMode) -> Self { + Self { + checkpoint, + witness, + } + } + } + + impl<CheckpointMode, WitnessMode> From<Derived> + for MembershipProofModeEntry<CheckpointMode, WitnessMode> + where + CheckpointMode: From<Derived>, + WitnessMode: From<Derived>, + { + #[inline] + fn from(d: Derived) -> Self { + Self::new(d.into(), d.into()) + } + } + + /// Membership Proof Allocation Mode + #[derive(derivative::Derivative)] + #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct MembershipProofMode<CheckpointMode, WitnessMode>( + PhantomData<(CheckpointMode, WitnessMode)>, + ) + where + CheckpointMode: AllocationMode, + WitnessMode: AllocationMode; + + impl<CheckpointMode, WitnessMode> AllocationMode + for MembershipProofMode<CheckpointMode, WitnessMode> + where + CheckpointMode: AllocationMode, + WitnessMode: AllocationMode, + { + type Known = MembershipProofModeEntry<CheckpointMode::Known, WitnessMode::Known>; + type Unknown = MembershipProofModeEntry<CheckpointMode::Unknown, WitnessMode::Unknown>; + } + + /// Membership Proof Variable + pub struct MembershipProofVar<V, C> + where + V: Verifier + ?Sized, + C: HasVariable<V::Checkpoint> + HasVariable<V::Witness> + ?Sized, + { + /// Public Checkpoint Variable + checkpoint: Var<V::Checkpoint, C>, + + /// Secret Witness Variable + witness: Var<V::Witness, C>, + } + + impl<V, C> MembershipProofVar<V, C> + where + V: Verifier + ?Sized, + C: HasVariable<V::Checkpoint> + HasVariable<V::Witness> + ?Sized, + { + /// Builds a new [`MembershipProofVar`] from `checkpoint` and `witness` variables. + #[inline] + pub fn new(checkpoint: Var<V::Checkpoint, C>, witness: Var<V::Witness, C>) -> Self { + Self { + checkpoint, + witness, + } + } + + /// Asserts that `self` is a valid proof to the fact that `item` is stored in some known + /// accumulator. + #[inline] + pub fn assert_validity<VV>(&self, item: &VV::ItemVar, verifier: &VV, cs: &mut C) + where + C: ConstraintSystem, + VV: VerifierVariable<C, Type = V>, + { + verifier.assert_valid_membership_proof(item, &self.checkpoint, &self.witness, cs); + } + } + + impl<V, C> Variable<C> for MembershipProofVar<V, C> + where + V: Verifier + ?Sized, + C: HasVariable<V::Checkpoint> + HasVariable<V::Witness> + ?Sized, + { + type Type = MembershipProof<V>; + + type Mode = MembershipProofMode<Mode<V::Checkpoint, C>, Mode<V::Witness, C>>; + + #[inline] + fn new(cs: &mut C, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + match allocation { + Allocation::Known(this, mode) => Self::new( + cs.allocate_known(&this.checkpoint, mode.checkpoint), + cs.allocate_known(&this.witness, mode.witness), + ), + Allocation::Unknown(mode) => Self::new( + unknown::<V::Checkpoint, _>(cs, mode.checkpoint), + unknown::<V::Witness, _>(cs, mode.witness), + ), + } + } + } + + impl<V, C> HasAllocation<C> for MembershipProof<V> + where + V: Verifier + ?Sized, + C: HasVariable<V::Checkpoint> + HasVariable<V::Witness> + ?Sized, + { + type Variable = MembershipProofVar<V, C>; + type Mode = MembershipProofMode<Mode<V::Checkpoint, C>, Mode<V::Witness, C>>; + } + + /// Public Checkpoint Type for [`VerifierVariable`] + pub type CheckpointType<V, C> = <<V as Variable<C>>::Type as Verifier>::Checkpoint; + + /// Secret Witness Type for [`VerifierVariable`] + pub type WitnessType<V, C> = <<V as Variable<C>>::Type as Verifier>::Witness; + + /// Public Checkpoint Variable Type for [`VerifierVariable`] + pub type CheckpointVar<V, C> = Var<CheckpointType<V, C>, C>; + + /// Secret Witness Variable Type for [`VerifierVariable`] + pub type WitnessVar<V, C> = Var<WitnessType<V, C>, C>; + + /// Verified Set Variable + pub trait VerifierVariable<C>: Variable<C> + where + C: ConstraintSystem + + HasVariable<CheckpointType<Self, C>> + + HasVariable<WitnessType<Self, C>> + + ?Sized, + Self::Type: Verifier, + { + /// Item Variable + type ItemVar: Variable<C, Type = <Self::Type as Verifier>::Item>; + + /// Asserts that `checkpoint` and `witness` form a proof to the fact that `item` is stored + /// in some known accumulator. + fn assert_valid_membership_proof( + &self, + item: &Self::ItemVar, + checkpoint: &CheckpointVar<Self, C>, + witness: &WitnessVar<Self, C>, + cs: &mut C, + ); + } +} + +/// Testing Framework +#[cfg(feature = "constraint")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "constraint")))] +pub mod test { + use super::*; + + /// + #[inline] + pub fn assert_unique_checkpoints<'i, A, I>(accumulator: &mut A, iter: I) + where + A: Accumulator, + A::Item: 'i, + I: IntoIterator<Item = &'i A::Item>, + { + todo!() + } + + /// + #[inline] + pub fn assert_provable_membership<A>(accumulator: &mut A, item: &A::Item) + where + A: Accumulator, + { + assert!( + accumulator.insert(item), + "Item could not be inserted into the accumulator." + ); + assert!( + accumulator.contains(item), + "Item was supposed to be contained in the accumulator after insertion." + ); + match accumulator.prove(item) { + Some(proof) => assert!( + proof.verify(item, accumulator), + "The accumulator was supposed to return a valid membership proof for an inserted item." + ), + _ => panic!("Item was supposed to be contained in the accumulator after insertion."), + } + } +} diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index 99a7efd56..54b0c8a8a 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -25,6 +25,7 @@ extern crate alloc; mod prf; +pub mod accumulator; pub mod commitment; pub mod constraint; pub mod encryption; diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index 6debf8da8..6726a4958 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -24,11 +24,12 @@ // creating a new one from scratch (for inner digests)? use crate::{ + accumulator::{Accumulator, MembershipProof, OptimizedAccumulator, Verifier}, merkle_tree::{ fork::{self, Trunk}, path::{CurrentPath, Path}, }, - set::{MembershipProof, VerifiedSet, Verifier}, + set, }; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; @@ -494,7 +495,7 @@ where } } -impl<C> Verifier for Parameters<C> +impl<C> set::Verifier for Parameters<C> where C: Configuration + ?Sized, { @@ -510,6 +511,27 @@ where } } +impl<C> Verifier for Parameters<C> +where + C: Configuration + ?Sized, +{ + type Item = Leaf<C>; + + type Checkpoint = Root<C>; + + type Witness = Path<C>; + + #[inline] + fn verify( + &self, + item: &Self::Item, + checkpoint: &Self::Checkpoint, + witness: &Self::Witness, + ) -> bool { + self.verify_path(witness, checkpoint, item) + } +} + /// Merkle Tree Root Wrapper Type #[derive(derivative::Derivative)] #[derivative( @@ -801,7 +823,7 @@ where } } -impl<C, T> VerifiedSet for MerkleTree<C, T> +impl<C, T> set::VerifiedSet for MerkleTree<C, T> where C: Configuration + ?Sized, T: Tree<C> + WithProofs<C>, @@ -850,7 +872,46 @@ where } #[inline] - fn get_membership_proof(&self, item: &Self::Item) -> Option<MembershipProof<Self::Verifier>> { + fn get_membership_proof( + &self, + item: &Self::Item, + ) -> Option<set::MembershipProof<Self::Verifier>> { + Some(set::MembershipProof::new( + self.root(), + self.path(self.index_of(&self.parameters.digest(item))?) + .ok()?, + )) + } + + #[inline] + fn contains(&self, item: &Self::Item) -> bool { + self.contains(&self.parameters.digest(item)) + } +} + +impl<C, T> Accumulator for MerkleTree<C, T> +where + C: Configuration + ?Sized, + T: Tree<C> + WithProofs<C>, +{ + type Item = Leaf<C>; + + type Checkpoint = Root<C>; + + type Witness = Path<C>; + + #[inline] + fn matching_checkpoint(&self, checkpoint: &Self::Checkpoint) -> bool { + self.matching_root(checkpoint) + } + + #[inline] + fn insert(&mut self, item: &Self::Item) -> bool { + self.push_provable(item) + } + + #[inline] + fn prove(&self, item: &Self::Item) -> Option<MembershipProof<Self>> { Some(MembershipProof::new( self.root(), self.path(self.index_of(&self.parameters.digest(item))?) @@ -862,4 +923,25 @@ where fn contains(&self, item: &Self::Item) -> bool { self.contains(&self.parameters.digest(item)) } + + #[inline] + fn verify( + &self, + item: &Self::Item, + checkpoint: &Self::Checkpoint, + witness: &Self::Witness, + ) -> bool { + self.parameters.verify(item, checkpoint, witness) + } +} + +impl<C, T> OptimizedAccumulator for MerkleTree<C, T> +where + C: Configuration + ?Sized, + T: Tree<C> + WithProofs<C>, +{ + #[inline] + fn insert_nonprovable(&mut self, item: &Self::Item) -> bool { + self.push(item) + } } From 9e4cc68421a233747ed8078a3009e85880a65a06 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 10 Nov 2021 23:38:28 -0500 Subject: [PATCH 112/275] feat: replace VerifiedSet/Verifier with Accumulator/Verifier using correct semantics --- manta-accounting/Cargo.toml | 2 +- manta-accounting/src/identity.rs | 239 ++++++++++--------- manta-accounting/src/transfer.rs | 140 +++++++---- manta-accounting/src/wallet/signer.rs | 144 +++++++----- manta-crypto/src/accumulator.rs | 212 +++++++++++++---- manta-crypto/src/lib.rs | 1 - manta-crypto/src/merkle_tree/tree.rs | 109 +++------ manta-crypto/src/set.rs | 320 -------------------------- manta-pay/benches/main.rs | 18 +- manta-pay/src/accounting/config.rs | 4 +- manta-pay/src/accounting/identity.rs | 19 +- 11 files changed, 515 insertions(+), 693 deletions(-) delete mode 100644 manta-crypto/src/set.rs diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 898e3e97a..4b9fbc695 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -41,7 +41,7 @@ test = [] cocoon = { version = "0.3.0", optional = true, default-features = false } derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } derive_more = { version = "0.99.16", default-features = false, features = ["add", "add_assign", "display", "from", "mul", "mul_assign", "sum"] } -manta-crypto = { path = "../manta-crypto" } +manta-crypto = { path = "../manta-crypto", features = ["constraint"] } manta-util = { path = "../manta-util" } zeroize = { version = "1.4.3", optional = true, default-features = false, features = ["alloc", "zeroize_derive"] } diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 859e8a9f7..edefb6b7d 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -18,17 +18,16 @@ // FIXME: Rename `AssetParameters`, since they are about identities not assets. // FIXME: Check the secret key APIs. -// FIXME: Remove `UtxoSet` dependence from `identity`, really we only need `UtxoSetVerifier`. // TODO: Get rid of [`Spend`] and [`OpenSpend`] if possible. They don't seem to be that useful. // See `crate::wallet::signer`. use crate::asset::{Asset, AssetBalance, AssetId, AssetVar}; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use manta_crypto::{ + accumulator::{Accumulator, MembershipProof, Verifier}, commitment::{CommitmentScheme, Input as CommitmentInput}, encryption::ies::{self, EncryptedMessage, IntegratedEncryptionScheme}, rand::{CryptoRng, Rand, RngCore, Sample, SeedableRng, Standard, TrySample}, - set::{MembershipProof, VerifiedSet}, PseudorandomFunctionFamily, }; @@ -367,15 +366,15 @@ where commitment_scheme: &C::CommitmentScheme, asset: Asset, utxo_set: &S, - ) -> Option<Sender<C, S>> + ) -> Option<Sender<C, S::Verifier>> where - S: VerifiedSet<Item = Utxo<C>>, + S: Accumulator<Item = Utxo<C>>, { let parameters = self.parameters(); let (void_number_commitment, utxo) = self.construct_utxo(commitment_scheme, &asset, &parameters); Some(Sender { - utxo_membership_proof: utxo_set.get_membership_proof(&utxo)?, + utxo_membership_proof: utxo_set.prove(&utxo)?, void_number: self.void_number(&parameters.void_number_generator), secret_key: self.secret_key, asset, @@ -657,9 +656,9 @@ where commitment_scheme: &C::CommitmentScheme, encrypted_asset: EncryptedMessage<I>, utxo_set: &S, - ) -> Result<Sender<C, S>, SpendError<I>> + ) -> Result<Sender<C, S::Verifier>, SpendError<I>> where - S: VerifiedSet<Item = Utxo<C>>, + S: Accumulator<Item = Utxo<C>>, { self.try_open(&encrypted_asset) .map_err(SpendError::EncryptionError)? @@ -724,9 +723,9 @@ where self, commitment_scheme: &C::CommitmentScheme, utxo_set: &S, - ) -> Option<Sender<C, S>> + ) -> Option<Sender<C, S::Verifier>> where - S: VerifiedSet<Item = Utxo<C>>, + S: Accumulator<Item = Utxo<C>>, { self.identity .into_sender(commitment_scheme, self.asset, utxo_set) @@ -781,27 +780,35 @@ where /// /// This `struct` is created by the [`get_proof`](PreSender::get_proof) method on [`PreSender`]. /// See its documentation for more. -pub struct SenderProof<C, S> +pub struct SenderProof<C, V> where C: Configuration + ?Sized, - S: VerifiedSet<Item = Utxo<C>>, + V: Verifier<Item = Utxo<C>> + ?Sized, { /// UTXO Membership Proof - utxo_membership_proof: MembershipProof<S::Verifier>, + utxo_membership_proof: MembershipProof<V>, /// Type Parameter Marker __: PhantomData<C>, } -impl<C, S> SenderProof<C, S> +impl<C, V> SenderProof<C, V> where C: Configuration + ?Sized, - S: VerifiedSet<Item = Utxo<C>>, + V: Verifier<Item = Utxo<C>> + ?Sized, { /// Returns `true` if a [`PreSender`] could be upgraded using `self` given the `utxo_set`. #[inline] - pub fn can_upgrade(&self, utxo_set: &S) -> bool { - self.utxo_membership_proof.check_public(utxo_set) + pub fn can_upgrade<S>(&self, utxo_set: &S) -> bool + where + S: Accumulator< + Item = V::Item, + Checkpoint = V::Checkpoint, + Witness = V::Witness, + Verifier = V, + >, + { + self.utxo_membership_proof.matching_checkpoint(utxo_set) } /// Upgrades the `pre_sender` to a [`Sender`] by attaching `self` to it. @@ -812,7 +819,7 @@ where /// `true`. Otherwise, using the sender returned here will most likely return an error when /// posting to the ledger. #[inline] - pub fn upgrade(self, pre_sender: PreSender<C>) -> Sender<C, S> { + pub fn upgrade(self, pre_sender: PreSender<C>) -> Sender<C, V> { pre_sender.upgrade(self) } } @@ -860,20 +867,20 @@ where #[inline] pub fn insert_utxo<S>(&self, utxo_set: &mut S) -> bool where - S: VerifiedSet<Item = Utxo<C>>, + S: Accumulator<Item = Utxo<C>>, { - utxo_set.insert_provable(&self.utxo) + utxo_set.insert(&self.utxo) } /// Requests the membership proof of `self.utxo` from `utxo_set` so that we can turn `self` /// into a [`Sender`]. #[inline] - pub fn get_proof<S>(&self, utxo_set: &S) -> Option<SenderProof<C, S>> + pub fn get_proof<S>(&self, utxo_set: &S) -> Option<SenderProof<C, S::Verifier>> where - S: VerifiedSet<Item = Utxo<C>>, + S: Accumulator<Item = Utxo<C>>, { Some(SenderProof { - utxo_membership_proof: utxo_set.get_membership_proof(&self.utxo)?, + utxo_membership_proof: utxo_set.prove(&self.utxo)?, __: PhantomData, }) } @@ -886,9 +893,9 @@ where /// Otherwise, using the sender returned here will most likely return an error when posting to /// the ledger. #[inline] - pub fn upgrade<S>(self, proof: SenderProof<C, S>) -> Sender<C, S> + pub fn upgrade<V>(self, proof: SenderProof<C, V>) -> Sender<C, V> where - S: VerifiedSet<Item = Utxo<C>>, + V: Verifier<Item = Utxo<C>> + ?Sized, { Sender { secret_key: self.secret_key, @@ -903,9 +910,9 @@ where /// Tries to convert `self` into a [`Sender`] by getting a proof from `utxo_set`. #[inline] - pub fn try_upgrade<S>(self, utxo_set: &S) -> Option<Sender<C, S>> + pub fn try_upgrade<S>(self, utxo_set: &S) -> Option<Sender<C, S::Verifier>> where - S: VerifiedSet<Item = Utxo<C>>, + S: Accumulator<Item = Utxo<C>>, { let proof = self.get_proof(utxo_set)?; Some(self.upgrade(proof)) @@ -927,10 +934,10 @@ where } /// Sender -pub struct Sender<C, S> +pub struct Sender<C, V> where C: Configuration + ?Sized, - S: VerifiedSet<Item = Utxo<C>>, + V: Verifier<Item = Utxo<C>> + ?Sized, { /// Secret Key secret_key: SecretKey<C>, @@ -951,22 +958,30 @@ where utxo: Utxo<C>, /// UTXO Membership Proof - utxo_membership_proof: MembershipProof<S::Verifier>, + utxo_membership_proof: MembershipProof<V>, } -impl<C, S> Sender<C, S> +impl<C, V> Sender<C, V> where C: Configuration + ?Sized, - S: VerifiedSet<Item = Utxo<C>>, + V: Verifier<Item = Utxo<C>> + ?Sized, { /// Builds a new [`Sender`] for this `asset` from an `identity`. #[inline] - pub fn from_identity( + pub fn from_identity<S>( identity: Identity<C>, commitment_scheme: &C::CommitmentScheme, asset: Asset, utxo_set: &S, - ) -> Option<Self> { + ) -> Option<Self> + where + S: Accumulator< + Item = V::Item, + Checkpoint = V::Checkpoint, + Witness = V::Witness, + Verifier = V, + >, + { identity.into_sender(commitment_scheme, asset, utxo_set) } @@ -1000,19 +1015,19 @@ where /// Extracts ledger posting data for this sender. #[inline] - pub fn into_post(self) -> SenderPost<C, S> { + pub fn into_post(self) -> SenderPost<C, V> { SenderPost { void_number: self.void_number, - utxo_membership_proof_public: self.utxo_membership_proof.into_public(), + utxo_checkpoint: self.utxo_membership_proof.into_checkpoint(), } } } /// Sender Ledger -pub trait SenderLedger<C, S> +pub trait SenderLedger<C, V> where C: Configuration + ?Sized, - S: VerifiedSet<Item = Utxo<C>>, + V: Verifier<Item = Utxo<C>> + ?Sized, { /// Valid [`VoidNumber`] Posting Key /// @@ -1021,20 +1036,20 @@ where /// This type must be some wrapper around [`VoidNumber`] which can only be constructed by this /// implementation of [`SenderLedger`]. This is to prevent that [`spend`](Self::spend) is /// called before [`is_unspent`](Self::is_unspent) and - /// [`is_valid_utxo_state`](Self::is_valid_utxo_state). + /// [`is_matching_checkpoint`](Self::is_matching_checkpoint). type ValidVoidNumber; /// Valid Utxo State Posting Key /// /// # Safety /// - /// This type must be some wrapper around [`S::Public`] which can only be constructed by this + /// This type must be some wrapper around [`S::Checkpoint`] which can only be constructed by this /// implementation of [`SenderLedger`]. This is to prevent that [`spend`](Self::spend) is /// called before [`is_unspent`](Self::is_unspent) and - /// [`is_valid_utxo_state`](Self::is_valid_utxo_state). + /// [`is_matching_checkpoint`](Self::is_matching_checkpoint). /// - /// [`S::Public`]: VerifiedSet::Public - type ValidUtxoState; + /// [`S::Checkpoint`]: Verifier::Checkpoint + type ValidUtxoCheckpoint; /// Super Posting Key /// @@ -1046,12 +1061,15 @@ where /// Existence of such a void number could indicate a possible double-spend. fn is_unspent(&self, void_number: VoidNumber<C>) -> Option<Self::ValidVoidNumber>; - /// Checks if the `public_input` is up-to-date with the current state of the UTXO set that is - /// stored on the ledger. + /// Checks if the `checkpoint` matches the current checkpoint of the UTXO set that is stored on + /// the ledger. /// /// Failure to match the ledger state means that the sender was constructed under an invalid or /// older state of the ledger. - fn is_valid_utxo_state(&self, public_input: S::Public) -> Option<Self::ValidUtxoState>; + fn is_matching_checkpoint( + &self, + checkpoint: V::Checkpoint, + ) -> Option<Self::ValidUtxoCheckpoint>; /// Posts the `void_number` to the ledger, spending the asset. /// @@ -1062,7 +1080,7 @@ where fn spend( &mut self, void_number: Self::ValidVoidNumber, - utxo_state: Self::ValidUtxoState, + utxo_checkpoint: Self::ValidUtxoCheckpoint, super_key: &Self::SuperPostingKey, ); } @@ -1078,87 +1096,79 @@ pub enum SenderPostError { /// Invalid UTXO State Error /// /// The sender was not constructed under the current state of the UTXO set. - InvalidUtxoState, + InvalidUtxoCheckpoint, } /// Sender Post -pub struct SenderPost<C, S> +pub struct SenderPost<C, V> where C: Configuration + ?Sized, - S: VerifiedSet<Item = Utxo<C>>, + V: Verifier<Item = Utxo<C>> + ?Sized, { /// Void Number pub(super) void_number: VoidNumber<C>, - /// UTXO Membership Proof Public Part - pub(super) utxo_membership_proof_public: S::Public, + /// UTXO Checkpoint + pub(super) utxo_checkpoint: V::Checkpoint, } -impl<C, S> SenderPost<C, S> +impl<C, V> SenderPost<C, V> where C: Configuration + ?Sized, - S: VerifiedSet<Item = Utxo<C>>, + V: Verifier<Item = Utxo<C>> + ?Sized, { /// Validates `self` on the sender `ledger`. #[inline] - pub fn validate<L>(self, ledger: &L) -> Result<SenderPostingKey<C, S, L>, SenderPostError> + pub fn validate<L>(self, ledger: &L) -> Result<SenderPostingKey<C, V, L>, SenderPostError> where - L: SenderLedger<C, S> + ?Sized, + L: SenderLedger<C, V> + ?Sized, { Ok(SenderPostingKey { - void_number: match ledger.is_unspent(self.void_number) { - Some(key) => key, - _ => return Err(SenderPostError::AssetSpent), - }, - utxo_membership_proof_public: match ledger - .is_valid_utxo_state(self.utxo_membership_proof_public) - { - Some(key) => key, - _ => return Err(SenderPostError::InvalidUtxoState), - }, + void_number: ledger + .is_unspent(self.void_number) + .ok_or(SenderPostError::AssetSpent)?, + utxo_checkpoint: ledger + .is_matching_checkpoint(self.utxo_checkpoint) + .ok_or(SenderPostError::InvalidUtxoCheckpoint)?, }) } } -impl<C, S> From<Sender<C, S>> for SenderPost<C, S> +impl<C, V> From<Sender<C, V>> for SenderPost<C, V> where C: Configuration + ?Sized, - S: VerifiedSet<Item = Utxo<C>>, + V: Verifier<Item = Utxo<C>> + ?Sized, { #[inline] - fn from(sender: Sender<C, S>) -> Self { + fn from(sender: Sender<C, V>) -> Self { sender.into_post() } } /// Sender Posting Key -pub struct SenderPostingKey<C, S, L> +pub struct SenderPostingKey<C, V, L> where C: Configuration + ?Sized, - S: VerifiedSet<Item = Utxo<C>>, - L: SenderLedger<C, S> + ?Sized, + V: Verifier<Item = Utxo<C>> + ?Sized, + L: SenderLedger<C, V> + ?Sized, { /// Void Number Posting Key void_number: L::ValidVoidNumber, - /// UTXO Membership Proof Public Part Posting Key - utxo_membership_proof_public: L::ValidUtxoState, + /// UTXO Checkpoint Posting Key + utxo_checkpoint: L::ValidUtxoCheckpoint, } -impl<C, S, L> SenderPostingKey<C, S, L> +impl<C, V, L> SenderPostingKey<C, V, L> where C: Configuration + ?Sized, - S: VerifiedSet<Item = Utxo<C>>, - L: SenderLedger<C, S> + ?Sized, + V: Verifier<Item = Utxo<C>> + ?Sized, + L: SenderLedger<C, V> + ?Sized, { /// Posts `self` to the sender `ledger`. #[inline] pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) { - ledger.spend( - self.void_number, - self.utxo_membership_proof_public, - super_key, - ); + ledger.spend(self.void_number, self.utxo_checkpoint, super_key); } } @@ -1234,9 +1244,9 @@ where #[inline] pub fn insert_utxo<S>(&self, utxo_set: &mut S) -> bool where - S: VerifiedSet<Item = Utxo<C>>, + S: Accumulator<Item = Utxo<C>>, { - utxo_set.insert_provable(&self.utxo) + utxo_set.insert(&self.utxo) } /// Extracts ledger posting data for this receiver. @@ -1339,10 +1349,9 @@ where L: ReceiverLedger<C, I>, { Ok(ReceiverPostingKey { - utxo: match ledger.is_not_registered(self.utxo) { - Some(key) => key, - _ => return Err(ReceiverPostError::AssetRegistered), - }, + utxo: ledger + .is_not_registered(self.utxo) + .ok_or(ReceiverPostError::AssetRegistered)?, encrypted_asset: self.encrypted_asset, }) } @@ -1390,12 +1399,12 @@ where pub mod constraint { use super::*; use manta_crypto::{ + accumulator::constraint::{MembershipProofVar, VerifierVariable}, constraint::{ reflection::{HasAllocation, HasVariable}, Allocation, Constant, ConstraintSystem, Derived, Equal, Input as ProofSystemInput, ProofSystem, Public, PublicOrSecret, Secret, Variable, }, - set::constraint::{MembershipProofVar, VerifierVariable}, }; /// [`Identity`] Constraint System Configuration @@ -1493,8 +1502,8 @@ pub mod constraint { pub type UtxoVar<C> = CommitmentSchemeOutputVar<C>; /// UTXO Membership Proof Variable Type - pub type UtxoMembershipProofVar<C, S> = - MembershipProofVar<<S as VerifiedSet>::Verifier, <C as Configuration>::ConstraintSystem>; + pub type UtxoMembershipProofVar<C, V> = + MembershipProofVar<V, <C as Configuration>::ConstraintSystem>; /// Asset Parameters Variable pub struct AssetParametersVar<C> @@ -1571,11 +1580,11 @@ pub mod constraint { } /// Sender Variable - pub struct SenderVar<C, S> + pub struct SenderVar<C, V> where C: Configuration, - S: VerifiedSet<Item = Utxo<C>>, - C::ConstraintSystem: HasVariable<S::Public> + HasVariable<S::Secret>, + V: Verifier<Item = Utxo<C>> + ?Sized, + C::ConstraintSystem: HasVariable<V::Checkpoint> + HasVariable<V::Witness>, { /// Secret Key secret_key: SecretKeyVar<C>, @@ -1596,25 +1605,25 @@ pub mod constraint { utxo: UtxoVar<C>, /// UTXO Membership Proof - utxo_membership_proof: UtxoMembershipProofVar<C, S>, + utxo_membership_proof: UtxoMembershipProofVar<C, V>, } - impl<C, S> SenderVar<C, S> + impl<C, V> SenderVar<C, V> where C: Configuration, - S: VerifiedSet<Item = Utxo<C>>, - C::ConstraintSystem: HasVariable<S::Public> + HasVariable<S::Secret>, + V: Verifier<Item = Utxo<C>> + ?Sized, + C::ConstraintSystem: HasVariable<V::Checkpoint> + HasVariable<V::Witness>, { /// Checks if `self` is a well-formed sender and returns its asset. #[inline] - pub fn get_well_formed_asset<V>( + pub fn get_well_formed_asset<VV>( self, cs: &mut C::ConstraintSystem, commitment_scheme: &C::CommitmentSchemeVar, - utxo_set_verifier: &V, + utxo_set_verifier: &VV, ) -> AssetVar<C::ConstraintSystem> where - V: VerifierVariable<C::ConstraintSystem, ItemVar = UtxoVar<C>, Type = S::Verifier>, + VV: VerifierVariable<C::ConstraintSystem, ItemVar = UtxoVar<C>, Type = V>, { cs.assert_eq( &self.void_number, @@ -1648,19 +1657,19 @@ pub mod constraint { ); self.utxo_membership_proof - .assert_validity(utxo_set_verifier, &self.utxo, cs); + .assert_validity(&self.utxo, utxo_set_verifier, cs); self.asset } } - impl<C, S> Variable<C::ConstraintSystem> for SenderVar<C, S> + impl<C, V> Variable<C::ConstraintSystem> for SenderVar<C, V> where C: Configuration, - S: VerifiedSet<Item = Utxo<C>>, + V: Verifier<Item = Utxo<C>> + ?Sized, C::ConstraintSystem: - HasVariable<S::Public, Mode = Public> + HasVariable<S::Secret, Mode = Secret>, + HasVariable<V::Checkpoint, Mode = Public> + HasVariable<V::Witness, Mode = Secret>, { - type Type = Sender<C, S>; + type Type = Sender<C, V>; type Mode = Derived; @@ -1690,28 +1699,28 @@ pub mod constraint { void_number: VoidNumberVar::<C>::new_unknown(cs, Public), void_number_commitment: VoidNumberCommitmentVar::<C>::new_unknown(cs, Secret), utxo: UtxoVar::<C>::new_unknown(cs, Secret), - utxo_membership_proof: MembershipProof::<S::Verifier>::unknown(cs, mode), + utxo_membership_proof: MembershipProof::<V>::unknown(cs, mode), }, } } } - impl<C, S> HasAllocation<C::ConstraintSystem> for Sender<C, S> + impl<C, V> HasAllocation<C::ConstraintSystem> for Sender<C, V> where C: Configuration, - S: VerifiedSet<Item = Utxo<C>>, + V: Verifier<Item = Utxo<C>> + ?Sized, C::ConstraintSystem: - HasVariable<S::Public, Mode = Public> + HasVariable<S::Secret, Mode = Secret>, + HasVariable<V::Checkpoint, Mode = Public> + HasVariable<V::Witness, Mode = Secret>, { - type Variable = SenderVar<C, S>; + type Variable = SenderVar<C, V>; type Mode = Derived; } - impl<C, S> SenderPost<C, S> + impl<C, V> SenderPost<C, V> where C: Configuration, - S: VerifiedSet<Item = Utxo<C>>, - C::ConstraintSystem: HasVariable<S::Public, Mode = Public>, + V: Verifier<Item = Utxo<C>> + ?Sized, + C::ConstraintSystem: HasVariable<V::Checkpoint, Mode = Public>, { /// Extends proof public input with `self`. #[inline] @@ -1719,12 +1728,12 @@ pub mod constraint { where P: ProofSystem<ConstraintSystem = C::ConstraintSystem> + ProofSystemInput<VoidNumber<C>> - + ProofSystemInput<S::Public>, + + ProofSystemInput<V::Checkpoint>, { // TODO: Add a "public part" trait that extracts the public part of `Sender` (using // `SenderVar` to determine the types), then generate this method automatically. P::extend(input, &self.void_number); - P::extend(input, &self.utxo_membership_proof_public); + P::extend(input, &self.utxo_checkpoint); } } diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index ec7fdfba6..dacff3128 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -18,7 +18,6 @@ // FIXME: Make sure that either (a) no empty transfer can be built, or (b) empty transfers work // properly i.e. do nothing. -// FIXME: Remove `UtxoSet` dependence from `transfer`, really we only need `UtxoSetVerifier`. // TODO: See if we can get rid of the `Copy` restriction on `ValidProof` and `SuperPostingKey`. // TODO: See if we can get rid of `PublicTransfer` and `SecretTransfer` and just use `Transfer`. @@ -32,6 +31,7 @@ use crate::{ use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash, ops::Add}; use manta_crypto::{ + accumulator::{constraint::VerifierVariable, Verifier}, constraint::{ self, reflection::{HasAllocation, HasVariable}, @@ -40,7 +40,6 @@ use manta_crypto::{ }, encryption::ies::{EncryptedMessage, IntegratedEncryptionScheme}, rand::{CryptoRng, RngCore}, - set::{constraint::VerifierVariable, VerifiedSet}, }; use manta_util::{create_seal, from_variant_impl, seal}; @@ -64,15 +63,15 @@ pub trait Configuration: type ConstraintSystem: constraint::ConstraintSystem + HasVariable<AssetId, Variable = Self::AssetIdVar, Mode = PublicOrSecret> + HasVariable<AssetBalance, Variable = Self::AssetBalanceVar, Mode = PublicOrSecret> - + HasVariable<<Self::UtxoSet as VerifiedSet>::Public, Mode = Public> - + HasVariable<<Self::UtxoSet as VerifiedSet>::Secret, Mode = Secret>; + + HasVariable<<Self::UtxoSetVerifier as Verifier>::Checkpoint, Mode = Public> + + HasVariable<<Self::UtxoSetVerifier as Verifier>::Witness, Mode = Secret>; /// Proof System type ProofSystem: ProofSystem<ConstraintSystem = ConstraintSystem<Self>, Verification = bool> + ProofSystemInput<AssetId> + ProofSystemInput<AssetBalance> + ProofSystemInput<VoidNumber<Self>> - + ProofSystemInput<<Self::UtxoSet as VerifiedSet>::Public> + + ProofSystemInput<<Self::UtxoSetVerifier as Verifier>::Checkpoint> + ProofSystemInput<Utxo<Self>>; /// Asset Id Variable @@ -87,14 +86,14 @@ pub trait Configuration: /// Integrated Encryption Scheme for [`Asset`] type IntegratedEncryptionScheme: IntegratedEncryptionScheme<Plaintext = Asset>; - /// Verified Set for [`Utxo`] - type UtxoSet: VerifiedSet<Item = Utxo<Self>>; + /// Accumulator Verifier for [`Utxo`] + type UtxoSetVerifier: Verifier<Item = Utxo<Self>>; - /// Verified Set Verifier Variable for [`Utxo`] + /// Accumulator Verifier Variable for [`Utxo`] type UtxoSetVerifierVar: VerifierVariable< ConstraintSystem<Self>, ItemVar = UtxoVar<Self>, - Type = <Self::UtxoSet as VerifiedSet>::Verifier, + Type = Self::UtxoSetVerifier, Mode = Constant, >; } @@ -111,13 +110,14 @@ pub type InternalIdentity<C> = pub type Spend<C> = identity::Spend<C, <C as Configuration>::IntegratedEncryptionScheme>; /// Transfer Sender Type -pub type Sender<C> = identity::Sender<C, <C as Configuration>::UtxoSet>; +pub type Sender<C> = identity::Sender<C, <C as Configuration>::UtxoSetVerifier>; /// Transfer Sender Post Type -pub type SenderPost<C> = identity::SenderPost<C, <C as Configuration>::UtxoSet>; +pub type SenderPost<C> = identity::SenderPost<C, <C as Configuration>::UtxoSetVerifier>; /// Transfer Sender Posting Key Type -pub type SenderPostingKey<C, L> = identity::SenderPostingKey<C, <C as Configuration>::UtxoSet, L>; +pub type SenderPostingKey<C, L> = + identity::SenderPostingKey<C, <C as Configuration>::UtxoSetVerifier, L>; /// Transfer Receiver Type pub type Receiver<C> = identity::Receiver<C, <C as Configuration>::IntegratedEncryptionScheme>; @@ -141,15 +141,12 @@ pub type IntegratedEncryptionSchemeError<C> = pub type ConstraintSystem<C> = <C as Configuration>::ConstraintSystem; /// Transfer Sender Variable Type -pub type SenderVar<C> = identity::constraint::SenderVar<C, <C as Configuration>::UtxoSet>; +pub type SenderVar<C> = identity::constraint::SenderVar<C, <C as Configuration>::UtxoSetVerifier>; /// Transfer Receiver Type pub type ReceiverVar<C> = identity::constraint::ReceiverVar<C, <C as Configuration>::IntegratedEncryptionScheme>; -/// Transfer UTXO Set Verifier Type -pub type UtxoSetVerifier<C> = <<C as Configuration>::UtxoSet as VerifiedSet>::Verifier; - /// Transfer Proving Context Type pub type ProvingContext<C> = <<C as Configuration>::ProofSystem as ProofSystem>::ProvingContext; @@ -183,7 +180,7 @@ pub type TransferLedgerSuperPostingKey<C, L> = <L as TransferLedger<C>>::SuperPo pub trait TransferLedger<C>: SenderLedger< C, - C::UtxoSet, + C::UtxoSetVerifier, SuperPostingKey = (Self::ValidProof, TransferLedgerSuperPostingKey<C, Self>), > + ReceiverLedger< C, @@ -509,7 +506,7 @@ where pub fn into_post<R>( self, commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &UtxoSetVerifier<C>, + utxo_set_verifier: &C::UtxoSetVerifier, context: &ProvingContext<C>, rng: &mut R, ) -> Result<TransferPost<C>, ProofSystemError<C>> @@ -675,7 +672,7 @@ where #[inline] fn unknown_variables( commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &UtxoSetVerifier<C>, + utxo_set_verifier: &C::UtxoSetVerifier, cs: &mut ConstraintSystem<C>, ) -> ( Option<C::AssetIdVar>, @@ -701,7 +698,7 @@ where fn known_variables( &self, commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &UtxoSetVerifier<C>, + utxo_set_verifier: &C::UtxoSetVerifier, cs: &mut ConstraintSystem<C>, ) -> ( Option<C::AssetIdVar>, @@ -764,7 +761,7 @@ where #[inline] pub fn unknown_constraints( commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &UtxoSetVerifier<C>, + utxo_set_verifier: &C::UtxoSetVerifier, ) -> ConstraintSystem<C> { let mut cs = C::ProofSystem::for_unknown(); let (base_asset_id, participants, commitment_scheme, utxo_set_verifier) = @@ -784,7 +781,7 @@ where pub fn known_constraints( &self, commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &UtxoSetVerifier<C>, + utxo_set_verifier: &C::UtxoSetVerifier, ) -> ConstraintSystem<C> { let mut cs = C::ProofSystem::for_known(); let (base_asset_id, participants, commitment_scheme, utxo_set_verifier) = @@ -803,7 +800,7 @@ where #[inline] pub fn generate_context<R>( commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &UtxoSetVerifier<C>, + utxo_set_verifier: &C::UtxoSetVerifier, rng: &mut R, ) -> Result<(ProvingContext<C>, VerifyingContext<C>), ProofSystemError<C>> where @@ -818,7 +815,7 @@ where pub fn is_valid<R>( &self, commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &UtxoSetVerifier<C>, + utxo_set_verifier: &C::UtxoSetVerifier, context: &ProvingContext<C>, rng: &mut R, ) -> Result<Proof<C>, ProofSystemError<C>> @@ -834,7 +831,7 @@ where pub fn into_post<R>( self, commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &UtxoSetVerifier<C>, + utxo_set_verifier: &C::UtxoSetVerifier, context: &ProvingContext<C>, rng: &mut R, ) -> Result<TransferPost<C>, ProofSystemError<C>> @@ -1416,7 +1413,10 @@ pub mod canonical { pub mod test { use super::*; use crate::{asset::AssetBalanceType, identity::Identity}; - use manta_crypto::rand::{Rand, Sample, Standard, TrySample}; + use manta_crypto::{ + accumulator::Accumulator, + rand::{Rand, Sample, Standard, TrySample}, + }; use manta_util::{array_map, fallible_array_map, into_array_unchecked}; /// Test Sampling Distributions @@ -1431,24 +1431,32 @@ pub mod test { pub struct FixedPublicTransfer(pub Asset); /// [`SecretTransfer`](super::SecretTransfer) Sampling Distribution - pub type SecretTransfer<'c, C> = Transfer<'c, C>; + pub type SecretTransfer<'c, C, S> = Transfer<'c, C, S>; /// Fixed Asset [`SecretTransfer`](super::SecretTransfer) Sampling Distribution - pub struct FixedSecretTransfer<'c, C> + pub struct FixedSecretTransfer<'c, C, S> where C: Configuration, + S: Accumulator< + Item = <C::UtxoSetVerifier as Verifier>::Item, + Verifier = C::UtxoSetVerifier, + >, { /// Asset pub asset: Asset, /// Base Distribution - pub base: SecretTransfer<'c, C>, + pub base: SecretTransfer<'c, C, S>, } - impl<'c, C> FixedSecretTransfer<'c, C> + impl<'c, C, S> FixedSecretTransfer<'c, C, S> where C: Configuration, C::SecretKey: Sample, + S: Accumulator< + Item = <C::UtxoSetVerifier as Verifier>::Item, + Verifier = C::UtxoSetVerifier, + >, { /// Tries to sample a [`super::SecretTransfer`] using custom sender and receiver asset /// totals. @@ -1467,7 +1475,7 @@ pub mod test { sender_total: AssetBalance, receiver_total: AssetBalance, commitment_scheme: &C::CommitmentScheme, - utxo_set: &mut C::UtxoSet, + utxo_set: &mut S, rng: &mut R, ) -> Result< super::SecretTransfer<C, SENDERS, RECEIVERS>, @@ -1476,7 +1484,7 @@ pub mod test { where R: CryptoRng + RngCore + ?Sized, { - FixedSecretTransfer::<C>::try_sample_custom_distribution( + FixedSecretTransfer::<C, S>::try_sample_custom_distribution( asset_id, sample_asset_balances::<_, SENDERS>(sender_total, rng), sample_asset_balances::<_, RECEIVERS>(receiver_total, rng), @@ -1503,7 +1511,7 @@ pub mod test { senders: AssetBalances<SENDERS>, receivers: AssetBalances<RECEIVERS>, commitment_scheme: &C::CommitmentScheme, - utxo_set: &mut C::UtxoSet, + utxo_set: &mut S, rng: &mut R, ) -> Result< super::SecretTransfer<C, SENDERS, RECEIVERS>, @@ -1550,27 +1558,35 @@ pub mod test { } /// [`Transfer`](super::Transfer) Sampling Distribution - pub struct Transfer<'c, C> + pub struct Transfer<'c, C, S> where C: Configuration, + S: Accumulator< + Item = <C::UtxoSetVerifier as Verifier>::Item, + Verifier = C::UtxoSetVerifier, + >, { /// Commitment Scheme pub commitment_scheme: &'c C::CommitmentScheme, /// UTXO Set - pub utxo_set: &'c mut C::UtxoSet, + pub utxo_set: &'c mut S, } /// Fixed Asset [`Transfer`](super::Transfer) Sampling Distribution - pub struct FixedTransfer<'c, C> + pub struct FixedTransfer<'c, C, S> where C: Configuration, + S: Accumulator< + Item = <C::UtxoSetVerifier as Verifier>::Item, + Verifier = C::UtxoSetVerifier, + >, { /// Asset pub asset: Asset, /// Base Distribution - pub base: Transfer<'c, C>, + pub base: Transfer<'c, C, S>, } } @@ -1650,17 +1666,21 @@ pub mod test { } } - impl<C, const SENDERS: usize, const RECEIVERS: usize> - TrySample<distribution::SecretTransfer<'_, C>> for SecretTransfer<C, SENDERS, RECEIVERS> + impl<C, S, const SENDERS: usize, const RECEIVERS: usize> + TrySample<distribution::SecretTransfer<'_, C, S>> for SecretTransfer<C, SENDERS, RECEIVERS> where C: Configuration, C::SecretKey: Sample, + S: Accumulator< + Item = <C::UtxoSetVerifier as Verifier>::Item, + Verifier = C::UtxoSetVerifier, + >, { type Error = IntegratedEncryptionSchemeError<C>; #[inline] fn try_sample<R>( - distribution: distribution::SecretTransfer<C>, + distribution: distribution::SecretTransfer<C, S>, rng: &mut R, ) -> Result<Self, Self::Error> where @@ -1676,18 +1696,22 @@ pub mod test { } } - impl<C, const SENDERS: usize, const RECEIVERS: usize> - TrySample<distribution::FixedSecretTransfer<'_, C>> + impl<C, S, const SENDERS: usize, const RECEIVERS: usize> + TrySample<distribution::FixedSecretTransfer<'_, C, S>> for SecretTransfer<C, SENDERS, RECEIVERS> where C: Configuration, C::SecretKey: Sample, + S: Accumulator< + Item = <C::UtxoSetVerifier as Verifier>::Item, + Verifier = C::UtxoSetVerifier, + >, { type Error = IntegratedEncryptionSchemeError<C>; #[inline] fn try_sample<R>( - distribution: distribution::FixedSecretTransfer<C>, + distribution: distribution::FixedSecretTransfer<C, S>, rng: &mut R, ) -> Result<Self, Self::Error> where @@ -1699,21 +1723,26 @@ pub mod test { impl< C, + S, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize, - > TrySample<distribution::Transfer<'_, C>> + > TrySample<distribution::Transfer<'_, C, S>> for Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS> where C: Configuration, C::SecretKey: Sample, + S: Accumulator< + Item = <C::UtxoSetVerifier as Verifier>::Item, + Verifier = C::UtxoSetVerifier, + >, { type Error = IntegratedEncryptionSchemeError<C>; #[inline] fn try_sample<R>( - distribution: distribution::Transfer<C>, + distribution: distribution::Transfer<C, S>, rng: &mut R, ) -> Result<Self, Self::Error> where @@ -1731,21 +1760,26 @@ pub mod test { impl< C, + S, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize, - > TrySample<distribution::FixedTransfer<'_, C>> + > TrySample<distribution::FixedTransfer<'_, C, S>> for Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS> where C: Configuration, C::SecretKey: Sample, + S: Accumulator< + Item = <C::UtxoSetVerifier as Verifier>::Item, + Verifier = C::UtxoSetVerifier, + >, { type Error = IntegratedEncryptionSchemeError<C>; #[inline] fn try_sample<R>( - distribution: distribution::FixedTransfer<C>, + distribution: distribution::FixedTransfer<C, S>, rng: &mut R, ) -> Result<Self, Self::Error> where @@ -1794,7 +1828,7 @@ pub mod test { CD: Default, VD: Default, C::CommitmentScheme: Sample<CD>, - UtxoSetVerifier<C>: Sample<VD>, + C::UtxoSetVerifier: Sample<VD>, R: CryptoRng + RngCore + ?Sized, { Self::unknown_constraints(&rng.gen(), &rng.gen()) @@ -1809,7 +1843,7 @@ pub mod test { CD: Default, VD: Default, C::CommitmentScheme: Sample<CD>, - UtxoSetVerifier<C>: Sample<VD>, + C::UtxoSetVerifier: Sample<VD>, R: CryptoRng + RngCore + ?Sized, { Self::generate_context(&rng.gen(), &rng.gen(), rng) @@ -1818,13 +1852,17 @@ pub mod test { /// Samples a [`Transfer`] and checks whether its internal proof is valid relative to the /// given `commitment_scheme` and `utxo_set`. #[inline] - pub fn sample_and_check_proof<R>( + pub fn sample_and_check_proof<S, R>( commitment_scheme: &C::CommitmentScheme, - utxo_set: &mut C::UtxoSet, + utxo_set: &mut S, rng: &mut R, ) -> Result<bool, ProofSystemError<C>> where C::SecretKey: Sample, + S: Accumulator< + Item = <C::UtxoSetVerifier as Verifier>::Item, + Verifier = C::UtxoSetVerifier, + >, R: CryptoRng + RngCore + ?Sized, { let transfer = Self::try_sample( diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index de7a2fbef..3d1215712 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -16,15 +16,16 @@ //! Wallet Signer -// TODO: Add wallet recovery i.e. remove the assumption that a new signer represents a completely -// new derived secret key generator. -// TODO: Allow for non-atomic signing, i.e. rollback state to something in-between two calls to -// `sign`. Will have to upgrade `Rollback` and `manta_crypto::merkle_tree::fork` as well. -// TODO: Add checkpointing/garbage-collection in `utxo_set` so we can remove old UTXOs once they -// are irrelevant. Once we create a sender and its transaction succeeds we can drop the UTXO. -// TODO: Should have a mode on the signer where we return a generic error which reveals no detail -// about what went wrong during signing. The kind of error returned from a signing could -// reveal information about the internal state (privacy leak, not a secrecy leak). +// FIXME: Change the name of `TransferAccumulator`, its not an `Accumulator`. +// TODO: Add wallet recovery i.e. remove the assumption that a new signer represents a completely +// new derived secret key generator. +// TODO: Allow for non-atomic signing, i.e. rollback state to something in-between two calls to +// sign`. Will have to upgrade `Rollback` and `manta_crypto::merkle_tree::fork` as well. +// TODO: Add checkpointing/garbage-collection in `utxo_set` so we can remove old UTXOs once they +// are irrelevant. Once we create a sender and its transaction succeeds we can drop the UTXO. +// TODO: Should have a mode on the signer where we return a generic error which reveals no detail +// about what went wrong during signing. The kind of error returned from a signing could +// reveal information about the internal state (privacy leak, not a secrecy leak). use crate::{ asset::{Asset, AssetBalance, AssetId, AssetMap}, @@ -52,8 +53,11 @@ use core::{ ops::Range, }; use manta_crypto::{ + accumulator::{ + Accumulator, ConstantCapacityAccumulator, ExactSizeAccumulator, OptimizedAccumulator, + Verifier, + }, rand::{CryptoRng, RngCore}, - set::VerifiedSet, }; use manta_util::{fallible_array_map, into_array_unchecked, iter::IteratorExt}; @@ -634,17 +638,36 @@ where } } +/// Signer Configuration +pub trait Configuration: transfer::Configuration { + /// Derived Secret Key Generator Type + type DerivedSecretKeyGenerator: DerivedSecretKeyGenerator<SecretKey = Self::SecretKey>; + + /// [`Utxo`] Accumulator Type + type UtxoSet: Accumulator< + Item = <Self::UtxoSetVerifier as Verifier>::Item, + Checkpoint = <Self::UtxoSetVerifier as Verifier>::Checkpoint, + Witness = <Self::UtxoSetVerifier as Verifier>::Witness, + Verifier = Self::UtxoSetVerifier, + > + ConstantCapacityAccumulator + + ExactSizeAccumulator + + OptimizedAccumulator + + Rollback; + + /// Asset Map Type + type AssetMap: AssetMap<Key = Index<Self::DerivedSecretKeyGenerator>>; + + /// Random Number Generator Type + type EntropySource: CryptoRng + RngCore; +} + /// Full Signer -pub struct FullSigner<D, C, M, R> +pub struct FullSigner<C> where - D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, - C::UtxoSet: Rollback, - M: AssetMap<Key = Index<D>>, - R: CryptoRng + RngCore, + C: Configuration, { /// Signer - signer: Signer<D>, + signer: Signer<C::DerivedSecretKeyGenerator>, /// Commitment Scheme commitment_scheme: C::CommitmentScheme, @@ -656,33 +679,29 @@ where utxo_set: C::UtxoSet, /// Asset Distribution - assets: M, + assets: C::AssetMap, /// Pending Asset Distribution - pending_assets: PendingAssetMap<D>, + pending_assets: PendingAssetMap<C::DerivedSecretKeyGenerator>, /// Random Number Generator - rng: R, + rng: C::EntropySource, } -impl<D, C, M, R> FullSigner<D, C, M, R> +impl<C> FullSigner<C> where - D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, - C::UtxoSet: Rollback, - M: AssetMap<Key = Index<D>>, - R: CryptoRng + RngCore, + C: Configuration, { /// Builds a new [`FullSigner`]. #[inline] fn new_inner( - signer: Signer<D>, + signer: Signer<C::DerivedSecretKeyGenerator>, commitment_scheme: C::CommitmentScheme, proving_context: ProvingContext<C>, utxo_set: C::UtxoSet, - assets: M, - pending_assets: PendingAssetMap<D>, - rng: R, + assets: C::AssetMap, + pending_assets: PendingAssetMap<C::DerivedSecretKeyGenerator>, + rng: C::EntropySource, ) -> Self { Self { signer, @@ -699,11 +718,11 @@ where /// `proving_context`, and `rng`, using a default [`Utxo`] set and asset distribution. #[inline] pub fn new( - secret_key_source: D, - account: D::Account, + secret_key_source: C::DerivedSecretKeyGenerator, + account: <C::DerivedSecretKeyGenerator as DerivedSecretKeyGenerator>::Account, commitment_scheme: C::CommitmentScheme, proving_context: ProvingContext<C>, - rng: R, + rng: C::EntropySource, ) -> Self where C::UtxoSet: Default, @@ -721,7 +740,7 @@ where /// Updates the internal ledger state, returning the new asset distribution. #[inline] - fn sync_inner<I>(&mut self, updates: I) -> SyncResult<D, C, Self> + fn sync_inner<I>(&mut self, updates: I) -> SyncResult<C::DerivedSecretKeyGenerator, C, Self> where I: Iterator<Item = (Utxo<C>, EncryptedAsset<C>)>, { @@ -740,9 +759,9 @@ where // asset is effectively burnt. assets.push(inner); self.assets.insert(index.reduce(), inner); - self.utxo_set.insert_provable(&utxo); - } else { self.utxo_set.insert(&utxo); + } else { + self.utxo_set.insert_nonprovable(&utxo); } } Ok(SyncResponse::new(assets)) @@ -755,7 +774,7 @@ where sync_state: SyncState, starting_index: usize, updates: I, - ) -> SyncResult<D, C, Self> + ) -> SyncResult<C::DerivedSecretKeyGenerator, C, Self> where I: IntoIterator<Item = (Utxo<C>, EncryptedAsset<C>)>, { @@ -770,7 +789,11 @@ where /// Returns a [`PreSender`] for the key at the given `index`. #[inline] - fn get_pre_sender(&self, index: Index<D>, asset: Asset) -> Result<PreSender<C>, Error<D, C>> { + fn get_pre_sender( + &self, + index: Index<C::DerivedSecretKeyGenerator>, + asset: Asset, + ) -> Result<PreSender<C>, Error<C::DerivedSecretKeyGenerator, C>> { self.signer .get_pre_sender(index, &self.commitment_scheme, asset) .map_err(Error::SecretKeyError) @@ -778,7 +801,10 @@ where /// Selects the pre-senders which collectively own at least `asset`, returning any change. #[inline] - fn select(&mut self, asset: Asset) -> Result<Selection<C>, Error<D, C>> { + fn select( + &mut self, + asset: Asset, + ) -> Result<Selection<C>, Error<C::DerivedSecretKeyGenerator, C>> { let selection = self.assets.select(asset); if selection.is_empty() { return Err(Error::InsufficientBalance(asset)); @@ -802,7 +828,7 @@ where >( &mut self, transfer: impl Into<Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>>, - ) -> Result<TransferPost<C>, Error<D, C>> { + ) -> Result<TransferPost<C>, Error<C::DerivedSecretKeyGenerator, C>> { transfer .into() .into_post( @@ -821,7 +847,7 @@ where asset_id: AssetId, mut pre_senders: Vec<PreSender<C>>, posts: &mut Vec<TransferPost<C>>, - ) -> Result<[Sender<C>; SENDERS], Error<D, C>> { + ) -> Result<[Sender<C>; SENDERS], Error<C::DerivedSecretKeyGenerator, C>> { assert!( (SENDERS > 1) && (RECEIVERS > 1), "The transfer shape must include at least two senders and two receivers." @@ -880,10 +906,10 @@ where fn prepare_final_pre_senders<const SENDERS: usize>( &mut self, asset_id: AssetId, - mut new_zeroes: Vec<InternalKeyOwned<D, PreSender<C>>>, + mut new_zeroes: Vec<InternalKeyOwned<C::DerivedSecretKeyGenerator, PreSender<C>>>, pre_senders: &mut Vec<PreSender<C>>, posts: &mut Vec<TransferPost<C>>, - ) -> Result<(), Error<D, C>> { + ) -> Result<(), Error<C::DerivedSecretKeyGenerator, C>> { let mut needed_zeroes = SENDERS - pre_senders.len(); if needed_zeroes == 0 { return Ok(()); @@ -935,7 +961,7 @@ where &mut self, asset_id: AssetId, change: AssetBalance, - ) -> Result<Receiver<C>, Error<D, C>> { + ) -> Result<Receiver<C>, Error<C::DerivedSecretKeyGenerator, C>> { let asset = asset_id.with(change); let (receiver, index) = self .signer @@ -951,7 +977,7 @@ where &mut self, asset: Asset, receiver: ShieldedIdentity<C>, - ) -> Result<Receiver<C>, Error<D, C>> { + ) -> Result<Receiver<C>, Error<C::DerivedSecretKeyGenerator, C>> { receiver .into_receiver(&self.commitment_scheme, asset, &mut self.rng) .map_err(Error::EncryptionError) @@ -963,7 +989,7 @@ where &mut self, asset: Asset, receiver: Option<ShieldedIdentity<C>>, - ) -> SignResult<D, C, Self> { + ) -> SignResult<C::DerivedSecretKeyGenerator, C, Self> { const SENDERS: usize = PrivateTransferShape::SENDERS; const RECEIVERS: usize = PrivateTransferShape::RECEIVERS; @@ -996,7 +1022,7 @@ where &mut self, asset: Asset, receiver: Option<ShieldedIdentity<C>>, - ) -> SignResult<D, C, Self> { + ) -> SignResult<C::DerivedSecretKeyGenerator, C, Self> { let result = self.sign_withdraw_inner(asset, receiver); if result.is_err() { self.rollback(); @@ -1006,7 +1032,10 @@ where /// Signs the `transaction`, generating transfer posts. #[inline] - pub fn sign(&mut self, transaction: Transaction<C>) -> SignResult<D, C, Self> { + pub fn sign( + &mut self, + transaction: Transaction<C>, + ) -> SignResult<C::DerivedSecretKeyGenerator, C, Self> { self.commit(); match transaction { Transaction::Mint(asset) => { @@ -1052,30 +1081,29 @@ where /// Generates a new [`ShieldedIdentity`] for `self` to receive assets. #[inline] - pub fn external_receiver(&mut self) -> ExternalReceiverResult<D, C, Self> { + pub fn external_receiver( + &mut self, + ) -> ExternalReceiverResult<C::DerivedSecretKeyGenerator, C, Self> { self.signer .next_shielded(&self.commitment_scheme) .map_err(Error::SecretKeyError) } } -impl<D, C, M, R> Connection<D, C> for FullSigner<D, C, M, R> +impl<C> Connection<C::DerivedSecretKeyGenerator, C> for FullSigner<C> where - D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, - C::UtxoSet: Rollback, - M: AssetMap<Key = Index<D>>, - R: CryptoRng + RngCore, + C: Configuration, { - type SyncFuture = Ready<SyncResult<D, C, Self>>; + type SyncFuture = Ready<SyncResult<C::DerivedSecretKeyGenerator, C, Self>>; - type SignFuture = Ready<SignResult<D, C, Self>>; + type SignFuture = Ready<SignResult<C::DerivedSecretKeyGenerator, C, Self>>; type CommitFuture = Ready<Result<(), Self::Error>>; type RollbackFuture = Ready<Result<(), Self::Error>>; - type ExternalReceiverFuture = Ready<ExternalReceiverResult<D, C, Self>>; + type ExternalReceiverFuture = + Ready<ExternalReceiverResult<C::DerivedSecretKeyGenerator, C, Self>>; type Error = Infallible; diff --git a/manta-crypto/src/accumulator.rs b/manta-crypto/src/accumulator.rs index 938d8fa4b..8f8b87263 100644 --- a/manta-crypto/src/accumulator.rs +++ b/manta-crypto/src/accumulator.rs @@ -16,6 +16,25 @@ //! Dynamic Cryptographic Accumulators +/// Matching Set +/// +/// This is a generalization of a single-element matching system, where there can be multiple +/// elements to match against. +pub trait MatchingSet<T> { + /// Checks if `t` matches any element in `self`. + fn contains(&self, t: &T) -> bool; +} + +impl<T> MatchingSet<T> for T +where + T: PartialEq, +{ + #[inline] + fn contains(&self, t: &T) -> bool { + self.eq(t) + } +} + /// Accumulator Membership Verifier pub trait Verifier { /// Item Type @@ -36,6 +55,27 @@ pub trait Verifier { ) -> bool; } +impl<V> Verifier for &V +where + V: Verifier + ?Sized, +{ + type Item = V::Item; + + type Checkpoint = V::Checkpoint; + + type Witness = V::Witness; + + #[inline] + fn verify( + &self, + item: &Self::Item, + checkpoint: &Self::Checkpoint, + witness: &Self::Witness, + ) -> bool { + (*self).verify(item, checkpoint, witness) + } +} + /// Accumulator pub trait Accumulator { /// Item Type @@ -47,8 +87,30 @@ pub trait Accumulator { /// Secret Witness Type type Witness; - /// Returns `true` if the accumulated value of `self` matches the given `checkpoint`. - fn matching_checkpoint(&self, checkpoint: &Self::Checkpoint) -> bool; + /// Verifier Type + type Verifier: Verifier<Item = Self::Item, Checkpoint = Self::Checkpoint, Witness = Self::Witness> + + ?Sized; + + /// Checkpoint Matching Set Type + type CheckpointSet: MatchingSet<Self::Checkpoint>; + + /// Returns the verifier for `self`. + fn verifier(&self) -> &Self::Verifier; + + /// Returns the checkpoint matching set for the current state of `self`. + fn checkpoints(&self) -> Self::CheckpointSet; + + /// Returns `true` if `checkpoint` is contained in the current checkpoint matching set + /// associated to `self`. + /// + /// # Implementation Note + /// + /// This method is an optimization path for implementations of [`Accumulator`] which can do a + /// checkpoint matching without having to return an entire owned [`Self::CheckpointSet`]. + #[inline] + fn matching_checkpoint(&self, checkpoint: &Self::Checkpoint) -> bool { + self.checkpoints().contains(checkpoint) + } /// Inserts `item` into `self` with the guarantee that `self` can later return a valid /// membership proof for `item` with a call to [`prove`](Self::prove). This method returns @@ -56,7 +118,7 @@ pub trait Accumulator { fn insert(&mut self, item: &Self::Item) -> bool; /// Returns a membership proof for `item` if it is contained in `self`. - fn prove(&self, item: &Self::Item) -> Option<MembershipProof<Self>>; + fn prove(&self, item: &Self::Item) -> Option<MembershipProof<Self::Verifier>>; /// Returns `true` if `item` is stored in `self`. /// @@ -70,17 +132,9 @@ pub trait Accumulator { fn contains(&self, item: &Self::Item) -> bool { self.prove(item).is_some() } - - /// Verifies that `item` is stored in `self` with `checkpoint` and `witness`. - fn verify( - &self, - item: &Self::Item, - checkpoint: &Self::Checkpoint, - witness: &Self::Witness, - ) -> bool; } -impl<A> Verifier for A +impl<A> Accumulator for &mut A where A: Accumulator + ?Sized, { @@ -90,14 +144,56 @@ where type Witness = A::Witness; + type Verifier = A::Verifier; + + type CheckpointSet = A::CheckpointSet; + #[inline] - fn verify( - &self, - item: &Self::Item, - checkpoint: &Self::Checkpoint, - witness: &Self::Witness, - ) -> bool { - self.verify(item, checkpoint, witness) + fn verifier(&self) -> &Self::Verifier { + (**self).verifier() + } + + #[inline] + fn checkpoints(&self) -> Self::CheckpointSet { + (**self).checkpoints() + } + + #[inline] + fn matching_checkpoint(&self, checkpoint: &Self::Checkpoint) -> bool { + (**self).matching_checkpoint(checkpoint) + } + + #[inline] + fn insert(&mut self, item: &Self::Item) -> bool { + (**self).insert(item) + } + + #[inline] + fn prove(&self, item: &Self::Item) -> Option<MembershipProof<Self::Verifier>> { + (**self).prove(item) + } + + #[inline] + fn contains(&self, item: &Self::Item) -> bool { + (**self).contains(item) + } +} + +/// Constant Capacity Accumulator +pub trait ConstantCapacityAccumulator: Accumulator { + /// Returns the total number of items that can be stored in `self`. + fn capacity() -> usize; +} + +/// Exact Size Accumulator +pub trait ExactSizeAccumulator: Accumulator { + /// Returns the number of items stored in `self`. + fn len(&self) -> usize; + + /// Returns `true` if the length of `self` is zero. + #[inline] + fn is_empty(&self) -> bool { + self.len() == 0 } } @@ -117,7 +213,7 @@ pub trait OptimizedAccumulator: Accumulator { /// [`insert`]: Accumulator::insert /// [`contains`]: Accumulator::contains #[inline] - fn insert_nonprovable(&mut self, item: &Self::Item) -> bool { + fn insert_nonprovable(&mut self, item: &<Self::Verifier as Verifier>::Item) -> bool { self.insert(item) } } @@ -153,12 +249,26 @@ where self.checkpoint } - /// Returns `true` if the accumulated value of `accumulator` matches the internal checkpoint - /// inside of `self`. + /// Returns `true` if the checkpoint associated to `self` is contained in `checkpoints`. + #[inline] + pub fn checkpoint_contained_in<S>(&self, checkpoints: &S) -> bool + where + S: MatchingSet<V::Checkpoint>, + { + checkpoints.contains(&self.checkpoint) + } + + /// Returns `true` if the checkpoint associated to `self` is contained in the current + /// checkpoint matching set associated to `accumulator`. #[inline] pub fn matching_checkpoint<A>(&self, accumulator: &A) -> bool where - A: Accumulator<Item = V::Item, Checkpoint = V::Checkpoint, Witness = V::Witness>, + A: Accumulator< + Item = V::Item, + Checkpoint = V::Checkpoint, + Witness = V::Witness, + Verifier = V, + >, { accumulator.matching_checkpoint(&self.checkpoint) } @@ -344,25 +454,16 @@ pub mod constraint { } /// Testing Framework -#[cfg(feature = "constraint")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "constraint")))] +#[cfg(feature = "test")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] pub mod test { use super::*; + use alloc::vec::Vec; + use core::fmt::Debug; - /// + /// Asserts that `accumulator` can prove the membership of `item` after it is inserted. #[inline] - pub fn assert_unique_checkpoints<'i, A, I>(accumulator: &mut A, iter: I) - where - A: Accumulator, - A::Item: 'i, - I: IntoIterator<Item = &'i A::Item>, - { - todo!() - } - - /// - #[inline] - pub fn assert_provable_membership<A>(accumulator: &mut A, item: &A::Item) + pub fn assert_provable_membership<A>(accumulator: &mut A, item: &A::Item) -> A::Checkpoint where A: Accumulator, { @@ -374,12 +475,35 @@ pub mod test { accumulator.contains(item), "Item was supposed to be contained in the accumulator after insertion." ); - match accumulator.prove(item) { - Some(proof) => assert!( - proof.verify(item, accumulator), - "The accumulator was supposed to return a valid membership proof for an inserted item." - ), - _ => panic!("Item was supposed to be contained in the accumulator after insertion."), + if let Some(proof) = accumulator.prove(item) { + assert!( + proof.verify(item, accumulator.verifier()), + "Invalid proof returned for inserted item." + ); + proof.into_checkpoint() + } else { + panic!("Item was supposed to be contained in the accumulator after insertion.") + } + } + + /// Asserts that the `accumulator` yields unique checkpoints after every insertion of items from + /// `iter`. + #[inline] + pub fn assert_unique_checkpoints<'i, A, I>(accumulator: &mut A, iter: I) + where + A: Accumulator, + A::Item: 'i, + A::Checkpoint: Debug + PartialEq, + I: IntoIterator<Item = &'i A::Item>, + { + let checkpoints = iter + .into_iter() + .map(move |item| assert_provable_membership(accumulator, item)) + .collect::<Vec<_>>(); + for (i, x) in checkpoints.iter().enumerate() { + for (j, y) in checkpoints.iter().enumerate().skip(i + 1) { + assert_ne!(x, y, "Found matching checkpoints at {:?} and {:?}.", i, j) + } } } } diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index 54b0c8a8a..538dc6db1 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -31,7 +31,6 @@ pub mod constraint; pub mod encryption; pub mod merkle_tree; pub mod rand; -pub mod set; pub use commitment::prelude::*; pub use prf::*; diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index 6726a4958..3b0278a8c 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -24,12 +24,14 @@ // creating a new one from scratch (for inner digests)? use crate::{ - accumulator::{Accumulator, MembershipProof, OptimizedAccumulator, Verifier}, + accumulator::{ + Accumulator, ConstantCapacityAccumulator, ExactSizeAccumulator, MembershipProof, + OptimizedAccumulator, Verifier, + }, merkle_tree::{ fork::{self, Trunk}, path::{CurrentPath, Path}, }, - set, }; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; @@ -495,22 +497,6 @@ where } } -impl<C> set::Verifier for Parameters<C> -where - C: Configuration + ?Sized, -{ - type Item = Leaf<C>; - - type Public = Root<C>; - - type Secret = Path<C>; - - #[inline] - fn verify(&self, public: &Self::Public, secret: &Self::Secret, item: &Self::Item) -> bool { - self.verify_path(secret, public, item) - } -} - impl<C> Verifier for Parameters<C> where C: Configuration + ?Sized, @@ -823,60 +809,44 @@ where } } -impl<C, T> set::VerifiedSet for MerkleTree<C, T> +impl<C, T> Accumulator for MerkleTree<C, T> where C: Configuration + ?Sized, T: Tree<C> + WithProofs<C>, { type Item = Leaf<C>; - type Public = Root<C>; + type Checkpoint = Root<C>; - type Secret = Path<C>; + type Witness = Path<C>; type Verifier = Parameters<C>; - #[inline] - fn verifier(&self) -> &Self::Verifier { - &self.parameters - } + type CheckpointSet = Self::Checkpoint; #[inline] - fn capacity(&self) -> usize { - self.capacity() - } - - #[inline] - fn len(&self) -> usize { - self.len() + fn verifier(&self) -> &Parameters<C> { + &self.parameters } #[inline] - fn is_empty(&self) -> bool { - self.is_empty() + fn checkpoints(&self) -> Self::CheckpointSet { + self.root() } #[inline] - fn insert_provable(&mut self, item: &Self::Item) -> bool { - self.push_provable(item) + fn matching_checkpoint(&self, checkpoint: &Self::Checkpoint) -> bool { + self.matching_root(checkpoint) } #[inline] fn insert(&mut self, item: &Self::Item) -> bool { - self.push(item) - } - - #[inline] - fn check_public(&self, public: &Self::Public) -> bool { - self.matching_root(public) + self.push_provable(item) } #[inline] - fn get_membership_proof( - &self, - item: &Self::Item, - ) -> Option<set::MembershipProof<Self::Verifier>> { - Some(set::MembershipProof::new( + fn prove(&self, item: &Self::Item) -> Option<MembershipProof<Self::Verifier>> { + Some(MembershipProof::new( self.root(), self.path(self.index_of(&self.parameters.digest(item))?) .ok()?, @@ -889,49 +859,30 @@ where } } -impl<C, T> Accumulator for MerkleTree<C, T> +impl<C, T> ConstantCapacityAccumulator for MerkleTree<C, T> where C: Configuration + ?Sized, T: Tree<C> + WithProofs<C>, { - type Item = Leaf<C>; - - type Checkpoint = Root<C>; - - type Witness = Path<C>; - - #[inline] - fn matching_checkpoint(&self, checkpoint: &Self::Checkpoint) -> bool { - self.matching_root(checkpoint) - } - #[inline] - fn insert(&mut self, item: &Self::Item) -> bool { - self.push_provable(item) - } - - #[inline] - fn prove(&self, item: &Self::Item) -> Option<MembershipProof<Self>> { - Some(MembershipProof::new( - self.root(), - self.path(self.index_of(&self.parameters.digest(item))?) - .ok()?, - )) + fn capacity() -> usize { + capacity::<C>() } +} +impl<C, T> ExactSizeAccumulator for MerkleTree<C, T> +where + C: Configuration + ?Sized, + T: Tree<C> + WithProofs<C>, +{ #[inline] - fn contains(&self, item: &Self::Item) -> bool { - self.contains(&self.parameters.digest(item)) + fn len(&self) -> usize { + self.len() } #[inline] - fn verify( - &self, - item: &Self::Item, - checkpoint: &Self::Checkpoint, - witness: &Self::Witness, - ) -> bool { - self.parameters.verify(item, checkpoint, witness) + fn is_empty(&self) -> bool { + self.is_empty() } } diff --git a/manta-crypto/src/set.rs b/manta-crypto/src/set.rs deleted file mode 100644 index ddea5d481..000000000 --- a/manta-crypto/src/set.rs +++ /dev/null @@ -1,320 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Verified Sets - -// TODO: Should `insert` or `insert_non_proving` be the default? - -/// Verified Set Verifier -pub trait Verifier { - /// Item Type - type Item: ?Sized; - - /// Public Part of the [`Item`](Self::Item) Membership Proof - type Public; - - /// Secret Part of the [`Item`](Self::Item) Membership Proof - type Secret; - - /// Verifies that `public` and `secret` form a proof to the fact that `item` is contained in - /// the verified set which returned `self`. - fn verify(&self, public: &Self::Public, secret: &Self::Secret, item: &Self::Item) -> bool; -} - -/// Verified Set -pub trait VerifiedSet { - /// Item Type - type Item: ?Sized; - - /// Public Part of the [`Item`](Self::Item) Membership Proof - type Public; - - /// Secret Part of the [`Item`](Self::Item) Membership Proof - type Secret; - - /// [`MembershipProof`] Verifier Type - type Verifier: Verifier<Item = Self::Item, Public = Self::Public, Secret = Self::Secret>; - - /// Returns the internal verifier for `self`. - fn verifier(&self) -> &Self::Verifier; - - /// Returns the maximum number of elements that can be stored in `self`. - fn capacity(&self) -> usize; - - /// Returns the number of elements that are contained in `self`. - fn len(&self) -> usize; - - /// Returns `true` if `self` contains no elements. - #[inline] - fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Inserts `item` into `self` with the guarantee that `self` can later return a valid proof of - /// membership for `item` with a call to [`get_membership_proof`](Self::get_membership_proof). - fn insert_provable(&mut self, item: &Self::Item) -> bool; - - /// Inserts `item` into `self` without the guarantee that `self` with be able to return a proof - /// of membership for `item`. - /// - /// # Implementation Note - /// - /// By default, this method uses [`insert_provable`](Self::insert_provable) to store `item` - /// in `self`. - #[inline] - fn insert(&mut self, item: &Self::Item) -> bool { - self.insert_provable(item) - } - - /// Returns `true` if `public` is a valid input for the current state of `self`. - fn check_public(&self, public: &Self::Public) -> bool; - - /// Generates a proof that the given `item` is stored in `self`. - fn get_membership_proof(&self, item: &Self::Item) -> Option<MembershipProof<Self::Verifier>>; - - /// Returns `true` if `item` is stored in `self`. - /// - /// # Implementation Note - /// - /// This method must at least return `true` for `item` whenever a valid proof of membership - /// exists. It may return `true` in other cases when `self` knows that it has `item` stored but - /// cannot return a proof for it. - #[inline] - fn contains(&self, item: &Self::Item) -> bool { - self.get_membership_proof(item).is_some() - } -} - -impl<S> Verifier for S -where - S: VerifiedSet, -{ - type Item = S::Item; - - type Public = S::Public; - - type Secret = S::Secret; - - #[inline] - fn verify(&self, public: &Self::Public, secret: &Self::Secret, item: &Self::Item) -> bool { - self.check_public(public) && self.verifier().verify(public, secret, item) - } -} - -/// Membership Proof for a [`Verifier`] -pub struct MembershipProof<V> -where - V: Verifier + ?Sized, -{ - /// Public Proof Part - public: V::Public, - - /// Secret Proof Part - secret: V::Secret, -} - -impl<V> MembershipProof<V> -where - V: Verifier + ?Sized, -{ - /// Builds a new [`MembershipProof`] from `public` and `secret`. - #[inline] - pub fn new(public: V::Public, secret: V::Secret) -> Self { - Self { public, secret } - } - - /// Returns [`V::Public`](Verifier::Public) discarding the [`MembershipProof`]. - #[inline] - pub fn into_public(self) -> V::Public { - self.public - } - - /// Returns `true` if the public part of `self` is a valid input for the current state of `set`. - #[inline] - pub fn check_public<S>(&self, set: &S) -> bool - where - S: VerifiedSet<Item = V::Item, Public = V::Public, Secret = V::Secret, Verifier = V>, - { - set.check_public(&self.public) - } - - /// Verifies that the `item` is contained in some [`VerifiedSet`]. - #[inline] - pub fn verify(&self, verifier: &V, item: &V::Item) -> bool { - verifier.verify(&self.public, &self.secret, item) - } -} - -/// Constraint System Gadgets for Sets and Verified Sets -pub mod constraint { - use super::*; - use crate::constraint::{ - reflection::{unknown, HasAllocation, HasVariable, Mode, Var}, - Allocation, AllocationMode, AllocationSystem, ConstraintSystem, Derived, Variable, - }; - use core::marker::PhantomData; - - /// Membership Proof Allocation Mode Entry - #[derive(derivative::Derivative)] - #[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] - pub struct MembershipProofModeEntry<PublicMode, SecretMode> { - /// Public Allocation Mode - pub public: PublicMode, - - /// Secret Allocation Mode - pub secret: SecretMode, - } - - impl<PublicMode, SecretMode> MembershipProofModeEntry<PublicMode, SecretMode> { - /// Builds a new [`MembershipProofModeEntry`] from a `public` mode and a `secret` mode. - #[inline] - pub fn new(public: PublicMode, secret: SecretMode) -> Self { - Self { public, secret } - } - } - - impl<PublicMode, SecretMode> From<Derived> for MembershipProofModeEntry<PublicMode, SecretMode> - where - PublicMode: From<Derived>, - SecretMode: From<Derived>, - { - #[inline] - fn from(d: Derived) -> Self { - Self::new(d.into(), d.into()) - } - } - - /// Membership Proof Allocation Mode - #[derive(derivative::Derivative)] - #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct MembershipProofMode<PublicMode, SecretMode>(PhantomData<(PublicMode, SecretMode)>) - where - PublicMode: AllocationMode, - SecretMode: AllocationMode; - - impl<PublicMode, SecretMode> AllocationMode for MembershipProofMode<PublicMode, SecretMode> - where - PublicMode: AllocationMode, - SecretMode: AllocationMode, - { - type Known = MembershipProofModeEntry<PublicMode::Known, SecretMode::Known>; - type Unknown = MembershipProofModeEntry<PublicMode::Unknown, SecretMode::Unknown>; - } - - /// Membership Proof Variable - pub struct MembershipProofVar<V, C> - where - V: Verifier + ?Sized, - C: HasVariable<V::Public> + HasVariable<V::Secret> + ?Sized, - { - /// Public Proof Part - public: Var<V::Public, C>, - - /// Secret Proof Part - secret: Var<V::Secret, C>, - } - - impl<V, C> MembershipProofVar<V, C> - where - V: Verifier + ?Sized, - C: HasVariable<V::Public> + HasVariable<V::Secret> + ?Sized, - { - /// Builds a new [`MembershipProofVar`] from `public` and `secret`. - #[inline] - pub fn new(public: Var<V::Public, C>, secret: Var<V::Secret, C>) -> Self { - Self { public, secret } - } - - /// Asserts that `self` is a valid proof to the fact that `item` is stored in the - /// verified set. - #[inline] - pub fn assert_validity<VV>(&self, verifier: &VV, item: &VV::ItemVar, cs: &mut C) - where - C: ConstraintSystem, - VV: VerifierVariable<C, Type = V>, - { - verifier.assert_valid_membership_proof(&self.public, &self.secret, item, cs); - } - } - - impl<V, C> Variable<C> for MembershipProofVar<V, C> - where - V: Verifier + ?Sized, - C: HasVariable<V::Public> + HasVariable<V::Secret> + ?Sized, - { - type Type = MembershipProof<V>; - - type Mode = MembershipProofMode<Mode<V::Public, C>, Mode<V::Secret, C>>; - - #[inline] - fn new(cs: &mut C, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - match allocation { - Allocation::Known(this, mode) => Self::new( - cs.allocate_known(&this.public, mode.public), - cs.allocate_known(&this.secret, mode.secret), - ), - Allocation::Unknown(mode) => Self::new( - unknown::<V::Public, _>(cs, mode.public), - unknown::<V::Secret, _>(cs, mode.secret), - ), - } - } - } - - impl<V, C> HasAllocation<C> for MembershipProof<V> - where - V: Verifier + ?Sized, - C: HasVariable<V::Public> + HasVariable<V::Secret> + ?Sized, - { - type Variable = MembershipProofVar<V, C>; - type Mode = MembershipProofMode<Mode<V::Public, C>, Mode<V::Secret, C>>; - } - - /// Public Proof Part for [`VerifierVariable`] - pub type PublicType<V, C> = <<V as Variable<C>>::Type as Verifier>::Public; - - /// Secret Proof Part for [`VerifierVariable`] - pub type SecretType<V, C> = <<V as Variable<C>>::Type as Verifier>::Secret; - - /// Public Proof Part Variable for [`VerifierVariable`] - pub type PublicVar<V, C> = Var<PublicType<V, C>, C>; - - /// Secret Proof Part Variable for [`VerifierVariable`] - pub type SecretVar<V, C> = Var<SecretType<V, C>, C>; - - /// Verified Set Variable - pub trait VerifierVariable<C>: Variable<C> - where - C: ConstraintSystem - + HasVariable<PublicType<Self, C>> - + HasVariable<SecretType<Self, C>> - + ?Sized, - Self::Type: Verifier, - { - /// Item Variable - type ItemVar: Variable<C, Type = <Self::Type as Verifier>::Item>; - - /// Asserts that `public` and `secret` form a proof to the fact that `item` is stored in - /// `self`. - fn assert_valid_membership_proof( - &self, - public: &PublicVar<Self, C>, - secret: &SecretVar<Self, C>, - item: &Self::ItemVar, - cs: &mut C, - ); - } -} diff --git a/manta-pay/benches/main.rs b/manta-pay/benches/main.rs index 48c136770..1d6edd584 100644 --- a/manta-pay/benches/main.rs +++ b/manta-pay/benches/main.rs @@ -21,8 +21,8 @@ extern crate manta_pay; use criterion::{black_box, criterion_group, criterion_main, Criterion}; use manta_accounting::transfer::test; use manta_crypto::{ + accumulator::Accumulator, rand::{Rand, TrySample}, - set::VerifiedSet, }; use manta_pay::accounting::{ identity::UtxoSet, @@ -123,8 +123,7 @@ fn mint(c: &mut Criterion) { &mut rng, ) .unwrap(); - let utxo_set_verifier = utxo_set.verifier(); - transfer_benchmark!(c, "Mint", rng, mint, commitment_scheme, utxo_set_verifier); + transfer_benchmark!(c, "Mint", rng, mint, commitment_scheme, utxo_set); } /// Runs the circuit benchmark for the [`PrivateTransfer`] transfer. @@ -140,14 +139,13 @@ fn private_transfer(c: &mut Criterion) { &mut rng, ) .unwrap(); - let utxo_set_verifier = utxo_set.verifier(); transfer_benchmark!( c, "PrivateTransfer", rng, private_transfer, commitment_scheme, - utxo_set_verifier + utxo_set ); } @@ -164,15 +162,7 @@ fn reclaim(c: &mut Criterion) { &mut rng, ) .unwrap(); - let utxo_set_verifier = utxo_set.verifier(); - transfer_benchmark!( - c, - "Reclaim", - rng, - reclaim, - commitment_scheme, - utxo_set_verifier - ); + transfer_benchmark!(c, "Reclaim", rng, reclaim, commitment_scheme, utxo_set); } criterion_group!(circuits, mint, private_transfer, reclaim); diff --git a/manta-pay/src/accounting/config.rs b/manta-pay/src/accounting/config.rs index 3ec3c69c7..6bb834d4e 100644 --- a/manta-pay/src/accounting/config.rs +++ b/manta-pay/src/accounting/config.rs @@ -20,7 +20,7 @@ // enabled when using whichever backend. use crate::{ - accounting::identity::{constraint::UtxoSetVerifierVar, Utxo, UtxoSet}, + accounting::identity::{constraint::UtxoSetVerifierVar, Parameters, Utxo}, crypto::{ commitment::pedersen::{self, PedersenWindow}, constraint::arkworks::{ @@ -155,6 +155,6 @@ impl transfer::Configuration for Configuration { type AssetIdVar = AssetIdVar<ConstraintField>; type AssetBalanceVar = AssetBalanceVar<ConstraintField>; type IntegratedEncryptionScheme = IES; - type UtxoSet = UtxoSet; + type UtxoSetVerifier = Parameters; type UtxoSetVerifierVar = UtxoSetVerifierVar; } diff --git a/manta-pay/src/accounting/identity.rs b/manta-pay/src/accounting/identity.rs index 589856c1a..ce0adcf9a 100644 --- a/manta-pay/src/accounting/identity.rs +++ b/manta-pay/src/accounting/identity.rs @@ -34,7 +34,7 @@ pub type Identity = identity::Identity<Configuration>; /// Sender Type pub type Sender = - identity::Sender<Configuration, <Configuration as transfer::Configuration>::UtxoSet>; + identity::Sender<Configuration, <Configuration as transfer::Configuration>::UtxoSetVerifier>; /// Receiver Type pub type Receiver = identity::Receiver< @@ -55,8 +55,10 @@ pub type Spend = identity::Spend< >; /// Sender Post Type -pub type SenderPost = - identity::SenderPost<Configuration, <Configuration as transfer::Configuration>::UtxoSet>; +pub type SenderPost = identity::SenderPost< + Configuration, + <Configuration as transfer::Configuration>::UtxoSetVerifier, +>; /// Receiver Post Type pub type ReceiverPost = identity::ReceiverPost< @@ -86,15 +88,15 @@ pub mod constraint { crypto::merkle_tree::constraint as merkle_tree_constraint, }; use manta_crypto::{ + accumulator::constraint::VerifierVariable, constraint::{reflection::HasAllocation, Allocation, Constant, Variable}, - set::constraint::VerifierVariable, }; use manta_util::concatenate; /// Sender Variable Type pub type SenderVar = identity::constraint::SenderVar< Configuration, - <Configuration as transfer::Configuration>::UtxoSet, + <Configuration as transfer::Configuration>::UtxoSetVerifier, >; /// Receiver Variable Type @@ -137,13 +139,14 @@ pub mod constraint { #[inline] fn assert_valid_membership_proof( &self, - public: &RootVar, - secret: &PathVar, item: &UtxoVar, + checkpoint: &RootVar, + witness: &PathVar, cs: &mut ConstraintSystem, ) { let _ = cs; - self.0.assert_verified(public, secret, &concatenate!(item)); + self.0 + .assert_verified(checkpoint, witness, &concatenate!(item)); } } } From 210a23484d0d60eda77d04f8c055634cf25c266c Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 11 Nov 2021 13:05:38 -0500 Subject: [PATCH 113/275] feat: add abstractions for encryption primitives - key-agreement scheme - key-diversification scheme - symmetric-key encryption scheme - key derivation function - hybrid public-key encryption scheme --- manta-crypto/src/encryption/mod.rs | 162 +++++++++++ manta-crypto/src/ies.rs | 439 ----------------------------- manta-crypto/src/key.rs | 95 +++++++ manta-crypto/src/lib.rs | 1 + 4 files changed, 258 insertions(+), 439 deletions(-) delete mode 100644 manta-crypto/src/ies.rs create mode 100644 manta-crypto/src/key.rs diff --git a/manta-crypto/src/encryption/mod.rs b/manta-crypto/src/encryption/mod.rs index 3d9b2c679..c072ad61a 100644 --- a/manta-crypto/src/encryption/mod.rs +++ b/manta-crypto/src/encryption/mod.rs @@ -19,3 +19,165 @@ pub mod ies; pub use ies::prelude::*; + +use crate::key::KeyAgreementScheme; + +/// Symmetric-Key Encryption Scheme +/// +/// # Specification +/// +/// All implementations of this trait must adhere to the following properties: +/// +/// 1. **Invertibility**: For all possible inputs, the following function returns `true`: +/// +/// ```text +/// fn invertibility(key: Key, plaintext: Plaintext) -> bool { +/// matches!(decrypt(key, &encrypt(key, plaintext.clone())), Some(p) if p == plaintext) +/// } +/// ``` +pub trait SymmetricKeyEncryptionScheme { + /// Encryption/Decryption Key Type + type Key; + + /// Plaintext Type + type Plaintext; + + /// Ciphertext Type + type Ciphertext; + + /// Encrypts `plaintext` using `key`. + fn encrypt(key: Self::Key, plaintext: Self::Plaintext) -> Self::Ciphertext; + + /// Tries to decrypt `ciphertext` using `key`. + fn decrypt(key: Self::Key, ciphertext: &Self::Ciphertext) -> Option<Self::Plaintext>; +} + +/// Key-Derivation Function +pub trait KeyDerivationFunction { + /// Shared Secret Type + type SharedSecret; + + /// Encryption/Decryption Key Type + type Key; + + /// Key-Agreement Scheme Type + type KeyAgreementScheme: KeyAgreementScheme<SharedSecret = Self::SharedSecret>; + + /// Symmetric-Key Encryption Scheme Type + type SymmetricKeyEncryptionScheme: SymmetricKeyEncryptionScheme<Key = Self::Key>; + + /// Derives an encryption/decryption key from a given `shared_secret`. + fn derive(shared_secret: Self::SharedSecret) -> Self::Key; +} + +/// Hybrid Public Key Encryption Scheme +pub trait HybridPublicKeyEncryptionScheme { + /// Secret Key Type + type SecretKey; + + /// Public Key Type + type PublicKey; + + /// Plaintext Type + type Plaintext; + + /// Ciphertext Type + type Ciphertext; + + /// Key-Agreement Scheme Type + type KeyAgreementScheme: KeyAgreementScheme< + SecretKey = Self::SecretKey, + PublicKey = Self::PublicKey, + >; + + /// Symmetric-Key Encryption Scheme Type + type SymmetricKeyEncryptionScheme: SymmetricKeyEncryptionScheme< + Plaintext = Self::Plaintext, + Ciphertext = Self::Ciphertext, + >; + + /// Key-Derivation Function Type + type KeyDerivationFunction: KeyDerivationFunction< + SharedSecret = <Self::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret, + Key = <Self::SymmetricKeyEncryptionScheme as SymmetricKeyEncryptionScheme>::Key, + >; +} + +/// Encrypted Message +pub struct EncryptedMessage<H> +where + H: HybridPublicKeyEncryptionScheme + ?Sized, +{ + /// Ciphertext + ciphertext: H::Ciphertext, + + /// Ephemeral Public Key + ephemeral_public_key: H::PublicKey, +} + +impl<H> EncryptedMessage<H> +where + H: HybridPublicKeyEncryptionScheme + ?Sized, +{ + /// Builds a new [`EncryptedMessage`] containing an encrypted `plaintext` using `public_key` + /// and an `ephemeral_secret_key`. + #[inline] + pub fn new( + public_key: &H::PublicKey, + ephemeral_secret_key: H::SecretKey, + plaintext: H::Plaintext, + ) -> Self { + Self { + ciphertext: H::SymmetricKeyEncryptionScheme::encrypt( + H::KeyDerivationFunction::derive(H::KeyAgreementScheme::agree( + &ephemeral_secret_key, + public_key, + )), + plaintext, + ), + ephemeral_public_key: H::KeyAgreementScheme::derive(ephemeral_secret_key), + } + } + + /// Tries to decrypt `self` using `secret_key`, returning back `Err(self)` if the `secret_key` + /// was unable to decrypt the message. + #[inline] + pub fn decrypt(self, secret_key: &H::SecretKey) -> Result<DecryptedMessage<H>, Self> { + match H::SymmetricKeyEncryptionScheme::decrypt( + H::KeyDerivationFunction::derive(H::KeyAgreementScheme::agree( + secret_key, + &self.ephemeral_public_key, + )), + &self.ciphertext, + ) { + Some(plaintext) => Ok(DecryptedMessage::new(plaintext, self.ephemeral_public_key)), + _ => Err(self), + } + } +} + +/// Decrypted Message +pub struct DecryptedMessage<H> +where + H: HybridPublicKeyEncryptionScheme + ?Sized, +{ + /// Plaintext + pub plaintext: H::Plaintext, + + /// Ephemeral Public Key + pub ephemeral_public_key: H::PublicKey, +} + +impl<H> DecryptedMessage<H> +where + H: HybridPublicKeyEncryptionScheme + ?Sized, +{ + /// Builds a new [`DecryptedMessage`] from `plaintext` and `ephemeral_public_key`. + #[inline] + pub fn new(plaintext: H::Plaintext, ephemeral_public_key: H::PublicKey) -> Self { + Self { + plaintext, + ephemeral_public_key, + } + } +} diff --git a/manta-crypto/src/ies.rs b/manta-crypto/src/ies.rs deleted file mode 100644 index ff2c2ac3c..000000000 --- a/manta-crypto/src/ies.rs +++ /dev/null @@ -1,439 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Integrated Encryption Schemes and Encrypted Messages - -// FIXME: add zeroize for secret keys - -use core::{fmt::Debug, hash::Hash}; -use rand_core::{CryptoRng, RngCore}; - -pub(super) mod prelude { - #[doc(inline)] - pub use super::IntegratedEncryptionScheme; -} - -/// Integrated Encryption Scheme Trait -pub trait IntegratedEncryptionScheme { - /// Public Key Type - type PublicKey; - - /// Secret Key Type - type SecretKey; - - /// Plaintext Type - type Plaintext; - - /// Ciphertext Type - type Ciphertext; - - /// Encryption/Decryption Error Type - type Error; - - /// Generates a public/secret keypair. - fn generate_keys<R>(rng: &mut R) -> KeyPair<Self> - where - R: CryptoRng + RngCore + ?Sized; - - /// Generates a public key. - /// - /// This enables an optimization path whenever decryption is not necessary. - #[inline] - fn generate_public_key<R>(rng: &mut R) -> PublicKey<Self> - where - R: CryptoRng + RngCore + ?Sized, - { - Self::generate_keys(rng).into_public() - } - - /// Generates a secret key. - /// - /// This enables an optimization path whenever encryption is not necessary. - #[inline] - fn generate_secret_key<R>(rng: &mut R) -> SecretKey<Self> - where - R: CryptoRng + RngCore + ?Sized, - { - Self::generate_keys(rng).into_secret() - } - - /// Encrypts the `plaintext` with the `public_key`, generating an [`EncryptedMessage`]. - fn encrypt<R>( - plaintext: &Self::Plaintext, - public_key: Self::PublicKey, - rng: &mut R, - ) -> Result<EncryptedMessage<Self>, Self::Error> - where - R: CryptoRng + RngCore + ?Sized; - - /// Generates a public/secret keypair and then encrypts the `plaintext` with the generated - /// public key, returning an [`EncryptedMessage`] and a [`SecretKey`]. - #[inline] - fn generate_keys_and_encrypt<R>( - plaintext: &Self::Plaintext, - rng: &mut R, - ) -> Result<(EncryptedMessage<Self>, SecretKey<Self>), Self::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - Self::generate_keys(rng).encrypt(plaintext, rng) - } - - /// Generates a public key and then encrypts the `plaintext` with the generated public key, - /// returning an [`EncryptedMessage`]. - /// - /// This enables an optimization path whenever decryption is not necessary. - #[inline] - fn generate_public_key_and_encrypt<R>( - plaintext: &Self::Plaintext, - rng: &mut R, - ) -> Result<EncryptedMessage<Self>, Self::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - Self::generate_public_key(rng).encrypt(plaintext, rng) - } - - /// Decrypts the `ciphertext` with the `secret_key`, returning the - /// [`Plaintext`](Self::Plaintext). - fn decrypt( - ciphertext: &Self::Ciphertext, - secret_key: Self::SecretKey, - ) -> Result<Self::Plaintext, Self::Error>; - - /// Generates a secret key and then decrypts the `ciphertext` with the generated secret key, - /// returing the [`Plaintext`](Self::Plaintext). - /// - /// This enables an optimization path whenever encryption is not necessary. - #[inline] - fn generate_secret_key_and_decrypt<R>( - ciphertext: &Self::Ciphertext, - rng: &mut R, - ) -> Result<Self::Plaintext, Self::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - Self::decrypt(ciphertext, Self::generate_secret_key(rng).secret_key) - } -} - -/// [`IntegratedEncryptionScheme`] Public Key -pub struct PublicKey<I> -where - I: IntegratedEncryptionScheme + ?Sized, -{ - /// Public Key - public_key: I::PublicKey, -} - -impl<I> PublicKey<I> -where - I: IntegratedEncryptionScheme + ?Sized, -{ - /// Builds a new [`PublicKey`] from `I::PublicKey`. - #[inline] - pub fn new(public_key: I::PublicKey) -> Self { - Self { public_key } - } - - /// Generates a public key. - /// - /// This enables an optimization path whenever decryption is not necessary. - #[inline] - pub fn generate<R>(rng: &mut R) -> Self - where - R: CryptoRng + RngCore + ?Sized, - { - I::generate_public_key(rng) - } - - /// Encrypts the `plaintext` with `self`, returning an [`EncryptedMessage`]. - #[inline] - pub fn encrypt<R>( - self, - plaintext: &I::Plaintext, - rng: &mut R, - ) -> Result<EncryptedMessage<I>, I::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - I::encrypt(plaintext, self.public_key, rng) - } - - /// Generates a public key and then encrypts the `plaintext` with the generated public key, - /// returning an [`EncryptedMessage`]. - /// - /// This enables an optimization path whenever decryption is not necessary. - #[inline] - pub fn generate_and_encrypt<R>( - plaintext: &I::Plaintext, - rng: &mut R, - ) -> Result<EncryptedMessage<I>, I::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - I::generate_public_key_and_encrypt(plaintext, rng) - } -} - -/// [`IntegratedEncryptionScheme`] Secret Key -pub struct SecretKey<I> -where - I: IntegratedEncryptionScheme + ?Sized, -{ - /// Secret Key - secret_key: I::SecretKey, -} - -impl<I> SecretKey<I> -where - I: IntegratedEncryptionScheme + ?Sized, -{ - /// Builds a new [`SecretKey`] from `I::SecretKey`. - #[inline] - pub fn new(secret_key: I::SecretKey) -> Self { - Self { secret_key } - } - - /// Generates a secret key. - /// - /// This enables an optimization path whenever encryption is not necessary. - #[inline] - pub fn generate<R>(rng: &mut R) -> Self - where - R: CryptoRng + RngCore + ?Sized, - { - I::generate_secret_key(rng) - } - - /// Decrypts the `message` with `self` returning the - /// [`Plaintext`](IntegratedEncryptionScheme::Plaintext). - #[inline] - pub fn decrypt(self, message: &EncryptedMessage<I>) -> Result<I::Plaintext, I::Error> { - message.decrypt(self) - } - - /// Generates a secret key and then decrypts the `message` with the generated secret key, - /// returing the [`Plaintext`](IntegratedEncryptionScheme::Plaintext). - /// - /// This enables an optimization path whenever encryption is not necessary. - #[inline] - pub fn generate_and_decrypt<R>( - message: &EncryptedMessage<I>, - rng: &mut R, - ) -> Result<I::Plaintext, I::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - I::generate_secret_key(rng).decrypt(message) - } -} - -/// [`IntegratedEncryptionScheme`] Key Pair -pub struct KeyPair<I> -where - I: IntegratedEncryptionScheme + ?Sized, -{ - /// Public Key - public_key: I::PublicKey, - - /// Secret Key - secret_key: I::SecretKey, -} - -impl<I> KeyPair<I> -where - I: IntegratedEncryptionScheme + ?Sized, -{ - /// Builds a new [`KeyPair`] from a `public_key` and a `secret_key`. - #[inline] - pub fn new(public_key: I::PublicKey, secret_key: I::SecretKey) -> Self { - Self { - public_key, - secret_key, - } - } - - /// Generates a public/secret keypair. - #[inline] - pub fn generate<R>(rng: &mut R) -> Self - where - R: CryptoRng + RngCore + ?Sized, - { - I::generate_keys(rng) - } - - /// Returns the public side of the key pair. - #[inline] - fn into_public(self) -> PublicKey<I> { - PublicKey::new(self.public_key) - } - - /// Returns the secret side of the key pair. - #[inline] - fn into_secret(self) -> SecretKey<I> { - SecretKey::new(self.secret_key) - } - - /// Encrypts the `plaintext` with `self`, returning an [`EncryptedMessage`]. - #[inline] - pub fn encrypt<R>( - self, - plaintext: &I::Plaintext, - rng: &mut R, - ) -> Result<(EncryptedMessage<I>, SecretKey<I>), I::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - let (public_key, secret_key) = self.into(); - Ok((public_key.encrypt(plaintext, rng)?, secret_key)) - } -} - -impl<I> From<KeyPair<I>> for (PublicKey<I>, SecretKey<I>) -where - I: IntegratedEncryptionScheme + ?Sized, -{ - #[inline] - fn from(keypair: KeyPair<I>) -> Self { - ( - PublicKey::new(keypair.public_key), - SecretKey::new(keypair.secret_key), - ) - } -} - -/// Encrypted Message -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "I::Ciphertext: Clone"), - Copy(bound = "I::Ciphertext: Copy"), - Debug(bound = "I::Ciphertext: Debug"), - Default(bound = "I::Ciphertext: Default"), - Eq(bound = "I::Ciphertext: Eq"), - Hash(bound = "I::Ciphertext: Hash"), - PartialEq(bound = "I::Ciphertext: PartialEq") -)] -pub struct EncryptedMessage<I> -where - I: IntegratedEncryptionScheme + ?Sized, -{ - /// Message Ciphertext - ciphertext: I::Ciphertext, -} - -impl<I> EncryptedMessage<I> -where - I: IntegratedEncryptionScheme + ?Sized, -{ - /// Builds a new [`EncryptedMessage`] from - /// [`I::Ciphertext`](IntegratedEncryptionScheme::Ciphertext). - #[inline] - pub fn new(ciphertext: I::Ciphertext) -> Self { - Self { ciphertext } - } - - /// Encrypts the `plaintext` with the `public_key`, returning an [`EncryptedMessage`]. - #[inline] - pub fn encrypt<R>( - plaintext: &I::Plaintext, - public_key: I::PublicKey, - rng: &mut R, - ) -> Result<Self, I::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - I::encrypt(plaintext, public_key, rng) - } - - /// Generates a public/secret keypair and then encrypts the `plaintext` with the generated - /// public key, returning an [`EncryptedMessage`] and a [`SecretKey`]. - #[inline] - pub fn generate_keys_and_encrypt<R>( - plaintext: &I::Plaintext, - rng: &mut R, - ) -> Result<(Self, SecretKey<I>), I::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - I::generate_keys_and_encrypt(plaintext, rng) - } - - /// Generates a public key and then encrypts the `plaintext` with the generated public key, - /// returning an [`EncryptedMessage`]. - /// - /// This enables an optimization path whenever decryption is not necessary. - #[inline] - pub fn generate_public_key_and_encrypt<R>( - plaintext: &I::Plaintext, - rng: &mut R, - ) -> Result<Self, I::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - I::generate_public_key_and_encrypt(plaintext, rng) - } - - /// Decrypts `self` with the `secret_key` returning the - /// [`Plaintext`](IntegratedEncryptionScheme::Plaintext). - #[inline] - pub fn decrypt(&self, secret_key: SecretKey<I>) -> Result<I::Plaintext, I::Error> { - I::decrypt(&self.ciphertext, secret_key.secret_key) - } - - /// Generates a secret key and then decrypts `self` with the generated secret key, - /// returing the [`Plaintext`](IntegratedEncryptionScheme::Plaintext). - /// - /// This enables an optimization path whenever encryption is not necessary. - #[inline] - pub fn generate_secret_key_and_decrypt<R>(&self, rng: &mut R) -> Result<I::Plaintext, I::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - I::generate_secret_key_and_decrypt(&self.ciphertext, rng) - } -} - -/// Testing Framework -#[cfg(feature = "test")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] -pub mod test { - use super::*; - use core::fmt::Debug; - - /// Tests encryption/decryption of a sample `plaintext`. - #[inline] - pub fn assert_decryption_of_encryption<I, R>(plaintext: &I::Plaintext, rng: &mut R) - where - I: IntegratedEncryptionScheme, - I::Plaintext: Debug + PartialEq, - I::Error: Debug, - R: CryptoRng + RngCore + ?Sized, - { - let (public_key, secret_key) = I::generate_keys(rng).into(); - let reconstructed_plaintext = secret_key - .decrypt( - &public_key - .encrypt(plaintext, rng) - .expect("Unable to encrypt plaintext."), - ) - .expect("Unable to decrypt plaintext."); - assert_eq!( - plaintext, &reconstructed_plaintext, - "Plaintext didn't match decrypted ciphertext." - ); - } -} diff --git a/manta-crypto/src/key.rs b/manta-crypto/src/key.rs new file mode 100644 index 000000000..f5ec5d2d0 --- /dev/null +++ b/manta-crypto/src/key.rs @@ -0,0 +1,95 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Key Primitives + +/// Key-Agreement Scheme +/// +/// # Specification +/// +/// All implementations of this trait must adhere to the following properties: +/// +/// 1. **Agreement**: For all possible inputs, the following function returns `true`: +/// +/// ```text +/// fn agreement(lhs: SecretKey, rhs: SecretKey) -> bool { +/// agree(lhs, derive(rhs)) == agree(rhs, derive(lhs)) +/// } +/// ``` +/// This ensures that both parties in the shared computation will arrive at the same conclusion +/// about the value of the [`SharedSecret`](Self::SharedSecret). +pub trait KeyAgreementScheme { + /// Secret Key Type + type SecretKey; + + /// Public Key Type + type PublicKey; + + /// Shared Secret Type + type SharedSecret; + + /// Derives a public key corresponding to `secret_key`. This public key should be sent to the + /// other party involved in the shared computation. + fn derive(secret_key: Self::SecretKey) -> Self::PublicKey; + + /// Computes the shared secret given the known `secret_key` and the given `public_key`. + fn agree(secret_key: &Self::SecretKey, public_key: &Self::PublicKey) -> Self::SharedSecret; +} + +/// Key-Diversification Scheme +/// +/// # Specification +/// +/// All implementations of this trait must adhere to the following properties: +/// +/// 1. **Derivation Invariance**: For all possible inputs, the following function returns `true`: +/// +/// ```text +/// fn derivation_invariance(diversifier: SecretKey, lhs: SecretKey, rhs: SecretKey) -> bool { +/// combine_secret_keys( +/// diversifier, +/// KeyAgreementScheme::derive(lhs), +/// KeyAgreementScheme::derive(rhs) +/// ) == combine_public_keys(KeyAgreementScheme::derive(diversifier), lhs, rhs) +/// } +/// ``` +pub trait KeyDiversificationScheme { + /// Secret Key Type + type SecretKey; + + /// Public Key Type + type PublicKey; + + /// Key-Agreement Scheme Type + type KeyAgreementScheme: KeyAgreementScheme< + PublicKey = Self::PublicKey, + SecretKey = Self::SecretKey, + >; + + /// Derives a new public key from a given `diversifier` and the `lhs` and `rhs` public keys. + fn combine_public_keys( + diversifier: Self::SecretKey, + lhs: Self::PublicKey, + rhs: Self::PublicKey, + ) -> Self::PublicKey; + + /// Derives a new secret key from a given `diversifier` and the `lhs` and `rhs` secret keys. + fn combine_secret_keys( + diversifier: Self::PublicKey, + lhs: Self::SecretKey, + rhs: Self::SecretKey, + ) -> Self::SecretKey; +} diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index 538dc6db1..3f32e32cc 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -29,6 +29,7 @@ pub mod accumulator; pub mod commitment; pub mod constraint; pub mod encryption; +pub mod key; pub mod merkle_tree; pub mod rand; From fa582ecf777303f13f0779ea7bdcb2f1efccf511 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 11 Nov 2021 13:17:27 -0500 Subject: [PATCH 114/275] fix: ensure all KDF constraints are fixed --- manta-crypto/src/encryption/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/manta-crypto/src/encryption/mod.rs b/manta-crypto/src/encryption/mod.rs index c072ad61a..47be6fbae 100644 --- a/manta-crypto/src/encryption/mod.rs +++ b/manta-crypto/src/encryption/mod.rs @@ -100,6 +100,8 @@ pub trait HybridPublicKeyEncryptionScheme { type KeyDerivationFunction: KeyDerivationFunction< SharedSecret = <Self::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret, Key = <Self::SymmetricKeyEncryptionScheme as SymmetricKeyEncryptionScheme>::Key, + KeyAgreementScheme = Self::KeyAgreementScheme, + SymmetricKeyEncryptionScheme = Self::SymmetricKeyEncryptionScheme, >; } From 13143a1cd753e1c11b8827a64d9321a557ffbd8b Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 11 Nov 2021 17:38:40 -0500 Subject: [PATCH 115/275] wip: start multi-use address system --- manta-accounting/src/address.rs | 275 +++++++++++++++++++ manta-accounting/src/identity.rs | 2 +- manta-accounting/src/{keys.rs => key.rs} | 0 manta-accounting/src/lib.rs | 3 +- manta-accounting/src/wallet/signer.rs | 2 +- manta-accounting/src/wallet/state.rs | 2 +- manta-crypto/src/commitment.rs | 5 - manta-crypto/src/encryption/ies.rs | 5 - manta-crypto/src/encryption/mod.rs | 77 ++++-- manta-crypto/src/key.rs | 62 ++--- manta-crypto/src/lib.rs | 9 +- manta-pay/src/accounting/config.rs | 2 +- manta-pay/src/accounting/{keys.rs => key.rs} | 2 +- manta-pay/src/accounting/mod.rs | 2 +- manta-pay/src/crypto/prf/blake2s.rs | 2 +- 15 files changed, 364 insertions(+), 86 deletions(-) create mode 100644 manta-accounting/src/address.rs rename manta-accounting/src/{keys.rs => key.rs} (100%) rename manta-pay/src/accounting/{keys.rs => key.rs} (98%) diff --git a/manta-accounting/src/address.rs b/manta-accounting/src/address.rs new file mode 100644 index 000000000..ad516a7d2 --- /dev/null +++ b/manta-accounting/src/address.rs @@ -0,0 +1,275 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Address Scheme + +// TODO: Implement a more general "view key" system, for arbitrarily-many view keys. + +use core::marker::PhantomData; +use manta_crypto::{ + encryption::{DecryptedMessage, EncryptedMessage, HybridPublicKeyEncryptionScheme}, + key::KeyAgreementScheme, +}; +use manta_util::{create_seal, seal}; + +create_seal! {} + +/// Key Type Marker Trait +/// +/// This trait identifies a key type for [`SecretKey`] and [`PublicKey`]. This trait is sealed and +/// can only be used with the existing implementations. +pub trait KeyType: sealed::Sealed { + /// Spending capability for this key type. + const IS_SPEND: bool; +} + +/// Implements the [`KeyType`] trait on the type with the given `$name`. +macro_rules! impl_key_type { + ($name:ty, $is_spend:expr) => { + seal!($name); + impl KeyType for $name { + const IS_SPEND: bool = $is_spend; + } + }; +} + +/// Spend Key Type +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, PartialOrd)] +pub struct Spend; + +impl_key_type!(Spend, true); + +/// View Key Type +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, PartialOrd)] +pub struct View; + +impl_key_type!(View, false); + +/// Secret Spend Key Type +pub type SecretSpendKey<K> = SecretKey<K, Spend>; + +/// Public Spend Key Type +pub type PublicSpendKey<K> = PublicKey<K, Spend>; + +/// Secret View Key Type +pub type SecretViewKey<K> = SecretKey<K, View>; + +/// Public View Key Type +pub type PublicViewKey<K> = PublicKey<K, View>; + +/// Secret Key +pub struct SecretKey<K, T> +where + K: KeyAgreementScheme, + T: KeyType, +{ + /// Secret Key + secret_key: K::SecretKey, + + /// Type Parameter Marker + __: PhantomData<T>, +} + +impl<K, T> SecretKey<K, T> +where + K: KeyAgreementScheme, + T: KeyType, +{ + /// Builds a new [`SecretKey`] from `secret_key`. + #[inline] + fn new(secret_key: K::SecretKey) -> Self { + Self { + secret_key, + __: PhantomData, + } + } + + /// Derives the corresponding [`PublicKey`] of the same [`KeyType`] as `self`, from a borrowed + /// value. + #[inline] + pub fn derive(&self) -> PublicKey<K, T> { + PublicKey::new(K::derive(&self.secret_key)) + } + + /// Derives the corresponding [`PublicKey`] of the same [`KeyType`] as `self`, from an owned + /// value. + #[inline] + pub fn derive_owned(self) -> PublicKey<K, T> { + PublicKey::new(K::derive_owned(self.secret_key)) + } +} + +impl<H> SecretViewKey<H> +where + H: HybridPublicKeyEncryptionScheme, +{ + /// Decrypts `message` using `self`. + /// + /// This method uses the [`decrypt`](EncryptedMessage::decrypt) method of [`EncryptedMessage`]. + /// See its documentation for more. + #[inline] + pub fn decrypt( + &self, + message: EncryptedMessage<H>, + ) -> Result<DecryptedMessage<H>, EncryptedMessage<H>> { + message.decrypt(&self.secret_key) + } +} + +/// Public Key +pub struct PublicKey<K, T> +where + K: KeyAgreementScheme, + T: KeyType, +{ + /// Public Key + public_key: K::PublicKey, + + /// Type Parameter Marker + __: PhantomData<T>, +} + +impl<K, T> PublicKey<K, T> +where + K: KeyAgreementScheme, + T: KeyType, +{ + /// Builds a new [`PublicKey`] from `public_key`. + #[inline] + fn new(public_key: K::PublicKey) -> Self { + Self { + public_key, + __: PhantomData, + } + } +} + +impl<H> PublicViewKey<H> +where + H: HybridPublicKeyEncryptionScheme, +{ + /// Encrypts `plaintext` using `self` and `ephemeral_secret_key`. + /// + /// This method uses the [`new`](EncryptedMessage::new) method of [`EncryptedMessage`]. + /// See its documentation for more. + #[inline] + pub fn encrypt( + &self, + ephemeral_secret_key: H::SecretKey, + plaintext: H::Plaintext, + ) -> EncryptedMessage<H> { + EncryptedMessage::new(&self.public_key, ephemeral_secret_key, plaintext) + } +} + +/// Spending Key +pub struct SpendingKey<K> +where + K: KeyAgreementScheme, +{ + /// Spend Part of the Spending Key + spend: SecretSpendKey<K>, + + /// View Part of the Spending Key + view: SecretViewKey<K>, +} + +impl<K> SpendingKey<K> +where + K: KeyAgreementScheme, +{ + /// Builds a new [`SpendingKey`] from `spend` and `view`. + #[inline] + fn new(spend: SecretSpendKey<K>, view: SecretViewKey<K>) -> Self { + Self { spend, view } + } + + /// Returns the [`SecretViewKey`] component of `self`. + #[inline] + pub fn viewing_key(&self) -> &SecretViewKey<K> { + &self.view + } + + /// Returns the [`ReceivingKey`] corresponding to `self`. + #[inline] + pub fn receiving_key(&self) -> ReceivingKey<K> { + ReceivingKey::new(self.spend.derive(), self.view.derive()) + } + + /// Converts `self` into its corresponding [`ReceivingKey`]. + #[inline] + pub fn into_receiving_key(self) -> ReceivingKey<K> { + ReceivingKey::new(self.spend.derive_owned(), self.view.derive_owned()) + } +} + +impl<H> SpendingKey<H> +where + H: HybridPublicKeyEncryptionScheme, +{ + /// Decrypts `message` using `self`. + /// + /// This method uses the [`decrypt`](EncryptedMessage::decrypt) method of [`EncryptedMessage`]. + /// See its documentation for more. + #[inline] + pub fn decrypt( + &self, + message: EncryptedMessage<H>, + ) -> Result<DecryptedMessage<H>, EncryptedMessage<H>> { + self.viewing_key().decrypt(message) + } +} + +/// Receiving Key +pub struct ReceivingKey<K> +where + K: KeyAgreementScheme, +{ + /// Spend Part of the Receiving Key + spend: PublicSpendKey<K>, + + /// View Part of the Receiving Key + view: PublicViewKey<K>, +} + +impl<K> ReceivingKey<K> +where + K: KeyAgreementScheme, +{ + /// Builds a new [`ReceivingKey`] from `spend` and `view`. + #[inline] + fn new(spend: PublicSpendKey<K>, view: PublicViewKey<K>) -> Self { + Self { spend, view } + } +} + +impl<H> ReceivingKey<H> +where + H: HybridPublicKeyEncryptionScheme, +{ + /// Encrypts `plaintext` using `self` and `ephemeral_secret_key`. + /// + /// This method uses the [`new`](EncryptedMessage::new) method of [`EncryptedMessage`]. + /// See its documentation for more. + #[inline] + pub fn encrypt( + &self, + ephemeral_secret_key: H::SecretKey, + plaintext: H::Plaintext, + ) -> EncryptedMessage<H> { + self.view.encrypt(ephemeral_secret_key, plaintext) + } +} diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index edefb6b7d..3dad21c73 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -27,8 +27,8 @@ use manta_crypto::{ accumulator::{Accumulator, MembershipProof, Verifier}, commitment::{CommitmentScheme, Input as CommitmentInput}, encryption::ies::{self, EncryptedMessage, IntegratedEncryptionScheme}, + prf::PseudorandomFunctionFamily, rand::{CryptoRng, Rand, RngCore, Sample, SeedableRng, Standard, TrySample}, - PseudorandomFunctionFamily, }; pub(super) mod prelude { diff --git a/manta-accounting/src/keys.rs b/manta-accounting/src/key.rs similarity index 100% rename from manta-accounting/src/keys.rs rename to manta-accounting/src/key.rs diff --git a/manta-accounting/src/lib.rs b/manta-accounting/src/lib.rs index 005b01d85..d4e818ba3 100644 --- a/manta-accounting/src/lib.rs +++ b/manta-accounting/src/lib.rs @@ -27,10 +27,11 @@ extern crate derive_more; #[cfg(feature = "cocoon")] extern crate cocoon as cocoon_crate; +pub mod address; pub mod asset; pub mod fs; pub mod identity; -pub mod keys; +pub mod key; pub mod transfer; pub mod wallet; diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 3d1215712..5662d72b7 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -31,7 +31,7 @@ use crate::{ asset::{Asset, AssetBalance, AssetId, AssetMap}, fs::{Load, LoadWith, Save, SaveWith}, identity::{self, Identity, PreSender, Utxo}, - keys::{ + key::{ Account, DerivedSecretKeyGenerator, ExternalKeyOwned, ExternalSecretKey, Index, InternalIndex, InternalKeyOwned, KeyKind, KeyOwned, }, diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 5ae5e7a38..41593f10c 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -18,7 +18,7 @@ use crate::{ asset::{Asset, AssetBalance, AssetId}, - keys::DerivedSecretKeyGenerator, + key::DerivedSecretKeyGenerator, transfer::{ canonical::{Transaction, TransactionKind}, Configuration, ShieldedIdentity, diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index ba37278a1..26a4870ff 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -19,11 +19,6 @@ use core::{fmt::Debug, hash::Hash}; use manta_util::{Concat, ConcatAccumulator}; -pub(crate) mod prelude { - #[doc(inline)] - pub use super::CommitmentScheme; -} - /// Commitment Scheme pub trait CommitmentScheme { /// Commitment Input Type diff --git a/manta-crypto/src/encryption/ies.rs b/manta-crypto/src/encryption/ies.rs index ff2c2ac3c..1ac034318 100644 --- a/manta-crypto/src/encryption/ies.rs +++ b/manta-crypto/src/encryption/ies.rs @@ -21,11 +21,6 @@ use core::{fmt::Debug, hash::Hash}; use rand_core::{CryptoRng, RngCore}; -pub(super) mod prelude { - #[doc(inline)] - pub use super::IntegratedEncryptionScheme; -} - /// Integrated Encryption Scheme Trait pub trait IntegratedEncryptionScheme { /// Public Key Type diff --git a/manta-crypto/src/encryption/mod.rs b/manta-crypto/src/encryption/mod.rs index 47be6fbae..257417f14 100644 --- a/manta-crypto/src/encryption/mod.rs +++ b/manta-crypto/src/encryption/mod.rs @@ -16,9 +16,11 @@ //! Encryption Primitives -pub mod ies; +// TODO: Make `KeyDerivationFunction` more general (don't require the `SymmetricKeyEncryptionScheme` +// dependency) and move it to `crate::key`. -pub use ies::prelude::*; +// TODO: remove +pub mod ies; use crate::key::KeyAgreementScheme; @@ -105,10 +107,57 @@ pub trait HybridPublicKeyEncryptionScheme { >; } +impl<H> KeyAgreementScheme for H +where + H: HybridPublicKeyEncryptionScheme, +{ + type SecretKey = <H::KeyAgreementScheme as KeyAgreementScheme>::SecretKey; + + type PublicKey = <H::KeyAgreementScheme as KeyAgreementScheme>::PublicKey; + + type SharedSecret = <H::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret; + + #[inline] + fn derive(secret_key: &Self::SecretKey) -> Self::PublicKey { + H::KeyAgreementScheme::derive(secret_key) + } + + #[inline] + fn derive_owned(secret_key: Self::SecretKey) -> Self::PublicKey { + H::KeyAgreementScheme::derive_owned(secret_key) + } + + #[inline] + fn agree(secret_key: &Self::SecretKey, public_key: &Self::PublicKey) -> Self::SharedSecret { + H::KeyAgreementScheme::agree(secret_key, public_key) + } +} + +impl<H> SymmetricKeyEncryptionScheme for H +where + H: HybridPublicKeyEncryptionScheme, +{ + type Key = <H::SymmetricKeyEncryptionScheme as SymmetricKeyEncryptionScheme>::Key; + + type Plaintext = <H::SymmetricKeyEncryptionScheme as SymmetricKeyEncryptionScheme>::Plaintext; + + type Ciphertext = <H::SymmetricKeyEncryptionScheme as SymmetricKeyEncryptionScheme>::Ciphertext; + + #[inline] + fn encrypt(key: Self::Key, plaintext: Self::Plaintext) -> Self::Ciphertext { + H::SymmetricKeyEncryptionScheme::encrypt(key, plaintext) + } + + #[inline] + fn decrypt(key: Self::Key, ciphertext: &Self::Ciphertext) -> Option<Self::Plaintext> { + H::SymmetricKeyEncryptionScheme::decrypt(key, ciphertext) + } +} + /// Encrypted Message pub struct EncryptedMessage<H> where - H: HybridPublicKeyEncryptionScheme + ?Sized, + H: HybridPublicKeyEncryptionScheme, { /// Ciphertext ciphertext: H::Ciphertext, @@ -119,7 +168,7 @@ where impl<H> EncryptedMessage<H> where - H: HybridPublicKeyEncryptionScheme + ?Sized, + H: HybridPublicKeyEncryptionScheme, { /// Builds a new [`EncryptedMessage`] containing an encrypted `plaintext` using `public_key` /// and an `ephemeral_secret_key`. @@ -130,14 +179,11 @@ where plaintext: H::Plaintext, ) -> Self { Self { - ciphertext: H::SymmetricKeyEncryptionScheme::encrypt( - H::KeyDerivationFunction::derive(H::KeyAgreementScheme::agree( - &ephemeral_secret_key, - public_key, - )), + ciphertext: H::encrypt( + H::KeyDerivationFunction::derive(H::agree(&ephemeral_secret_key, public_key)), plaintext, ), - ephemeral_public_key: H::KeyAgreementScheme::derive(ephemeral_secret_key), + ephemeral_public_key: H::derive_owned(ephemeral_secret_key), } } @@ -145,11 +191,8 @@ where /// was unable to decrypt the message. #[inline] pub fn decrypt(self, secret_key: &H::SecretKey) -> Result<DecryptedMessage<H>, Self> { - match H::SymmetricKeyEncryptionScheme::decrypt( - H::KeyDerivationFunction::derive(H::KeyAgreementScheme::agree( - secret_key, - &self.ephemeral_public_key, - )), + match H::decrypt( + H::KeyDerivationFunction::derive(H::agree(secret_key, &self.ephemeral_public_key)), &self.ciphertext, ) { Some(plaintext) => Ok(DecryptedMessage::new(plaintext, self.ephemeral_public_key)), @@ -161,7 +204,7 @@ where /// Decrypted Message pub struct DecryptedMessage<H> where - H: HybridPublicKeyEncryptionScheme + ?Sized, + H: HybridPublicKeyEncryptionScheme, { /// Plaintext pub plaintext: H::Plaintext, @@ -172,7 +215,7 @@ where impl<H> DecryptedMessage<H> where - H: HybridPublicKeyEncryptionScheme + ?Sized, + H: HybridPublicKeyEncryptionScheme, { /// Builds a new [`DecryptedMessage`] from `plaintext` and `ephemeral_public_key`. #[inline] diff --git a/manta-crypto/src/key.rs b/manta-crypto/src/key.rs index f5ec5d2d0..4a96b5aae 100644 --- a/manta-crypto/src/key.rs +++ b/manta-crypto/src/key.rs @@ -43,53 +43,23 @@ pub trait KeyAgreementScheme { /// Derives a public key corresponding to `secret_key`. This public key should be sent to the /// other party involved in the shared computation. - fn derive(secret_key: Self::SecretKey) -> Self::PublicKey; + fn derive(secret_key: &Self::SecretKey) -> Self::PublicKey; + + /// Derives a public key corresponding to `secret_key`. This public key should be sent to the + /// other party involved in the shared computation. + /// + /// # Implementation Note + /// + /// This method is an optimization path for [`derive`] when we own the `secret_key` value, and + /// by default, uses [`derive`] as its implementation. This method must return the same value + /// as [`derive`] on the same input. + /// + /// [`derive`]: Self::derive + #[inline] + fn derive_owned(secret_key: Self::SecretKey) -> Self::PublicKey { + Self::derive(&secret_key) + } /// Computes the shared secret given the known `secret_key` and the given `public_key`. fn agree(secret_key: &Self::SecretKey, public_key: &Self::PublicKey) -> Self::SharedSecret; } - -/// Key-Diversification Scheme -/// -/// # Specification -/// -/// All implementations of this trait must adhere to the following properties: -/// -/// 1. **Derivation Invariance**: For all possible inputs, the following function returns `true`: -/// -/// ```text -/// fn derivation_invariance(diversifier: SecretKey, lhs: SecretKey, rhs: SecretKey) -> bool { -/// combine_secret_keys( -/// diversifier, -/// KeyAgreementScheme::derive(lhs), -/// KeyAgreementScheme::derive(rhs) -/// ) == combine_public_keys(KeyAgreementScheme::derive(diversifier), lhs, rhs) -/// } -/// ``` -pub trait KeyDiversificationScheme { - /// Secret Key Type - type SecretKey; - - /// Public Key Type - type PublicKey; - - /// Key-Agreement Scheme Type - type KeyAgreementScheme: KeyAgreementScheme< - PublicKey = Self::PublicKey, - SecretKey = Self::SecretKey, - >; - - /// Derives a new public key from a given `diversifier` and the `lhs` and `rhs` public keys. - fn combine_public_keys( - diversifier: Self::SecretKey, - lhs: Self::PublicKey, - rhs: Self::PublicKey, - ) -> Self::PublicKey; - - /// Derives a new secret key from a given `diversifier` and the `lhs` and `rhs` secret keys. - fn combine_secret_keys( - diversifier: Self::PublicKey, - lhs: Self::SecretKey, - rhs: Self::SecretKey, - ) -> Self::SecretKey; -} diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index 3f32e32cc..5cf16172b 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -23,15 +23,14 @@ extern crate alloc; -mod prf; - pub mod accumulator; pub mod commitment; -pub mod constraint; pub mod encryption; pub mod key; pub mod merkle_tree; +pub mod prf; pub mod rand; -pub use commitment::prelude::*; -pub use prf::*; +#[cfg(feature = "constraint")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "constraint")))] +pub mod constraint; diff --git a/manta-pay/src/accounting/config.rs b/manta-pay/src/accounting/config.rs index 6bb834d4e..9f82d8070 100644 --- a/manta-pay/src/accounting/config.rs +++ b/manta-pay/src/accounting/config.rs @@ -39,7 +39,7 @@ use ark_crypto_primitives::crh::pedersen::{constraints::CRHGadget, CRH}; use ark_ed_on_bls12_381::{constraints::EdwardsVar, EdwardsProjective, Fq}; use manta_accounting::{identity, transfer}; use manta_crypto::{ - commitment::CommitmentScheme, merkle_tree, rand::SeedIntoRng, PseudorandomFunctionFamily, + commitment::CommitmentScheme, merkle_tree, prf::PseudorandomFunctionFamily, rand::SeedIntoRng, }; use rand_chacha::ChaCha20Rng; diff --git a/manta-pay/src/accounting/keys.rs b/manta-pay/src/accounting/key.rs similarity index 98% rename from manta-pay/src/accounting/keys.rs rename to manta-pay/src/accounting/key.rs index 70f32c39f..dd01222cf 100644 --- a/manta-pay/src/accounting/keys.rs +++ b/manta-pay/src/accounting/key.rs @@ -26,7 +26,7 @@ use alloc::{format, string::String}; use bip32::{Seed, XPrv}; use core::{marker::PhantomData, num::ParseIntError, str::FromStr}; -use manta_accounting::keys::{DerivedSecretKeyGenerator, DerivedSecretKeyParameter, KeyKind}; +use manta_accounting::key::{DerivedSecretKeyGenerator, DerivedSecretKeyParameter, KeyKind}; use manta_util::{create_seal, seal}; pub use bip32::{Error, Mnemonic}; diff --git a/manta-pay/src/accounting/mod.rs b/manta-pay/src/accounting/mod.rs index 7c833991d..8fe3d4ae7 100644 --- a/manta-pay/src/accounting/mod.rs +++ b/manta-pay/src/accounting/mod.rs @@ -18,6 +18,6 @@ pub mod config; pub mod identity; -pub mod keys; +pub mod key; pub mod ledger; pub mod transfer; diff --git a/manta-pay/src/crypto/prf/blake2s.rs b/manta-pay/src/crypto/prf/blake2s.rs index f7e42817e..00d4727d6 100644 --- a/manta-pay/src/crypto/prf/blake2s.rs +++ b/manta-pay/src/crypto/prf/blake2s.rs @@ -19,8 +19,8 @@ use alloc::vec::Vec; use ark_crypto_primitives::prf::{Blake2s as ArkBlake2s, PRF}; use manta_crypto::{ + prf::PseudorandomFunctionFamily, rand::{CryptoRng, Rand, RngCore, Sample, Standard}, - PseudorandomFunctionFamily, }; use manta_util::{Concat, ConcatAccumulator}; From b4e67c9d68745b42aaa43d7ae53afe9d7b1bf20a Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 12 Nov 2021 01:04:58 -0500 Subject: [PATCH 116/275] fix: simplify key interfaces and abstractions --- manta-accounting/src/address.rs | 56 +++++++++++++++- manta-crypto/src/accumulator.rs | 2 + manta-crypto/src/encryption/mod.rs | 104 ++--------------------------- manta-crypto/src/key.rs | 14 ++++ 4 files changed, 75 insertions(+), 101 deletions(-) diff --git a/manta-accounting/src/address.rs b/manta-accounting/src/address.rs index ad516a7d2..0725ae792 100644 --- a/manta-accounting/src/address.rs +++ b/manta-accounting/src/address.rs @@ -90,7 +90,7 @@ where { /// Builds a new [`SecretKey`] from `secret_key`. #[inline] - fn new(secret_key: K::SecretKey) -> Self { + pub fn new(secret_key: K::SecretKey) -> Self { Self { secret_key, __: PhantomData, @@ -112,6 +112,17 @@ where } } +impl<K> SecretSpendKey<K> +where + K: KeyAgreementScheme, +{ + /// Generates the spending secret associated to `self` and the `ephemeral_public_key`. + #[inline] + pub fn spending_secret(&self, ephemeral_public_key: &K::PublicKey) -> K::SharedSecret { + K::agree(&self.secret_key, ephemeral_public_key) + } +} + impl<H> SecretViewKey<H> where H: HybridPublicKeyEncryptionScheme, @@ -148,6 +159,11 @@ where T: KeyType, { /// Builds a new [`PublicKey`] from `public_key`. + /// + /// # Implementation Note + /// + /// This method is intentionally private, since these keys should only be constructed by some + /// call to the appropriate `derive` method. #[inline] fn new(public_key: K::PublicKey) -> Self { Self { @@ -157,6 +173,17 @@ where } } +impl<K> PublicSpendKey<K> +where + K: KeyAgreementScheme, +{ + /// Generates the spending secret associated to `self` and the `ephemeral_secret_key`. + #[inline] + pub fn spending_secret(&self, ephemeral_secret_key: &K::SecretKey) -> K::SharedSecret { + K::agree(ephemeral_secret_key, &self.public_key) + } +} + impl<H> PublicViewKey<H> where H: HybridPublicKeyEncryptionScheme, @@ -193,7 +220,7 @@ where { /// Builds a new [`SpendingKey`] from `spend` and `view`. #[inline] - fn new(spend: SecretSpendKey<K>, view: SecretViewKey<K>) -> Self { + pub fn new(spend: SecretSpendKey<K>, view: SecretViewKey<K>) -> Self { Self { spend, view } } @@ -214,6 +241,12 @@ where pub fn into_receiving_key(self) -> ReceivingKey<K> { ReceivingKey::new(self.spend.derive_owned(), self.view.derive_owned()) } + + /// Generates the spending secret associated to `self` and the `ephemeral_public_key`. + #[inline] + pub fn spending_secret(&self, ephemeral_public_key: &K::PublicKey) -> K::SharedSecret { + self.spend.spending_secret(ephemeral_public_key) + } } impl<H> SpendingKey<H> @@ -229,7 +262,7 @@ where &self, message: EncryptedMessage<H>, ) -> Result<DecryptedMessage<H>, EncryptedMessage<H>> { - self.viewing_key().decrypt(message) + self.view.decrypt(message) } } @@ -250,10 +283,27 @@ where K: KeyAgreementScheme, { /// Builds a new [`ReceivingKey`] from `spend` and `view`. + /// + /// # Implementation Note + /// + /// This method is intentionally private, since these keys should only be constructed by some + /// call to the appropriate `derive` method. #[inline] fn new(spend: PublicSpendKey<K>, view: PublicViewKey<K>) -> Self { Self { spend, view } } + + /// Returns the [`PublicViewKey`] component of `self`. + #[inline] + pub fn viewing_key(&self) -> &PublicViewKey<K> { + &self.view + } + + /// Generates the spending secret associated to `self` and the `ephemeral_secret_key`. + #[inline] + pub fn spending_secret(&self, ephemeral_secret_key: &K::SecretKey) -> K::SharedSecret { + self.spend.spending_secret(ephemeral_secret_key) + } } impl<H> ReceivingKey<H> diff --git a/manta-crypto/src/accumulator.rs b/manta-crypto/src/accumulator.rs index 8f8b87263..2bf6bc5a2 100644 --- a/manta-crypto/src/accumulator.rs +++ b/manta-crypto/src/accumulator.rs @@ -16,6 +16,8 @@ //! Dynamic Cryptographic Accumulators +// TODO: See if we can modify `Accumulator` so that it can extend the `Verifier` trait directly. + /// Matching Set /// /// This is a generalization of a single-element matching system, where there can be multiple diff --git a/manta-crypto/src/encryption/mod.rs b/manta-crypto/src/encryption/mod.rs index 257417f14..63f729867 100644 --- a/manta-crypto/src/encryption/mod.rs +++ b/manta-crypto/src/encryption/mod.rs @@ -16,13 +16,10 @@ //! Encryption Primitives -// TODO: Make `KeyDerivationFunction` more general (don't require the `SymmetricKeyEncryptionScheme` -// dependency) and move it to `crate::key`. - // TODO: remove pub mod ies; -use crate::key::KeyAgreementScheme; +use crate::key::{KeyAgreementScheme, KeyDerivationFunction}; /// Symmetric-Key Encryption Scheme /// @@ -54,106 +51,17 @@ pub trait SymmetricKeyEncryptionScheme { fn decrypt(key: Self::Key, ciphertext: &Self::Ciphertext) -> Option<Self::Plaintext>; } -/// Key-Derivation Function -pub trait KeyDerivationFunction { - /// Shared Secret Type - type SharedSecret; - - /// Encryption/Decryption Key Type - type Key; - - /// Key-Agreement Scheme Type - type KeyAgreementScheme: KeyAgreementScheme<SharedSecret = Self::SharedSecret>; - - /// Symmetric-Key Encryption Scheme Type - type SymmetricKeyEncryptionScheme: SymmetricKeyEncryptionScheme<Key = Self::Key>; - - /// Derives an encryption/decryption key from a given `shared_secret`. - fn derive(shared_secret: Self::SharedSecret) -> Self::Key; -} - /// Hybrid Public Key Encryption Scheme -pub trait HybridPublicKeyEncryptionScheme { - /// Secret Key Type - type SecretKey; - - /// Public Key Type - type PublicKey; - - /// Plaintext Type - type Plaintext; - - /// Ciphertext Type - type Ciphertext; - - /// Key-Agreement Scheme Type - type KeyAgreementScheme: KeyAgreementScheme< - SecretKey = Self::SecretKey, - PublicKey = Self::PublicKey, - >; - - /// Symmetric-Key Encryption Scheme Type - type SymmetricKeyEncryptionScheme: SymmetricKeyEncryptionScheme< - Plaintext = Self::Plaintext, - Ciphertext = Self::Ciphertext, - >; - +pub trait HybridPublicKeyEncryptionScheme: + KeyAgreementScheme + SymmetricKeyEncryptionScheme +{ /// Key-Derivation Function Type type KeyDerivationFunction: KeyDerivationFunction< - SharedSecret = <Self::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret, - Key = <Self::SymmetricKeyEncryptionScheme as SymmetricKeyEncryptionScheme>::Key, - KeyAgreementScheme = Self::KeyAgreementScheme, - SymmetricKeyEncryptionScheme = Self::SymmetricKeyEncryptionScheme, + KeyAgreementScheme = Self, + Key = <Self as SymmetricKeyEncryptionScheme>::Key, >; } -impl<H> KeyAgreementScheme for H -where - H: HybridPublicKeyEncryptionScheme, -{ - type SecretKey = <H::KeyAgreementScheme as KeyAgreementScheme>::SecretKey; - - type PublicKey = <H::KeyAgreementScheme as KeyAgreementScheme>::PublicKey; - - type SharedSecret = <H::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret; - - #[inline] - fn derive(secret_key: &Self::SecretKey) -> Self::PublicKey { - H::KeyAgreementScheme::derive(secret_key) - } - - #[inline] - fn derive_owned(secret_key: Self::SecretKey) -> Self::PublicKey { - H::KeyAgreementScheme::derive_owned(secret_key) - } - - #[inline] - fn agree(secret_key: &Self::SecretKey, public_key: &Self::PublicKey) -> Self::SharedSecret { - H::KeyAgreementScheme::agree(secret_key, public_key) - } -} - -impl<H> SymmetricKeyEncryptionScheme for H -where - H: HybridPublicKeyEncryptionScheme, -{ - type Key = <H::SymmetricKeyEncryptionScheme as SymmetricKeyEncryptionScheme>::Key; - - type Plaintext = <H::SymmetricKeyEncryptionScheme as SymmetricKeyEncryptionScheme>::Plaintext; - - type Ciphertext = <H::SymmetricKeyEncryptionScheme as SymmetricKeyEncryptionScheme>::Ciphertext; - - #[inline] - fn encrypt(key: Self::Key, plaintext: Self::Plaintext) -> Self::Ciphertext { - H::SymmetricKeyEncryptionScheme::encrypt(key, plaintext) - } - - #[inline] - fn decrypt(key: Self::Key, ciphertext: &Self::Ciphertext) -> Option<Self::Plaintext> { - H::SymmetricKeyEncryptionScheme::decrypt(key, ciphertext) - } -} - /// Encrypted Message pub struct EncryptedMessage<H> where diff --git a/manta-crypto/src/key.rs b/manta-crypto/src/key.rs index 4a96b5aae..42a31ad6d 100644 --- a/manta-crypto/src/key.rs +++ b/manta-crypto/src/key.rs @@ -63,3 +63,17 @@ pub trait KeyAgreementScheme { /// Computes the shared secret given the known `secret_key` and the given `public_key`. fn agree(secret_key: &Self::SecretKey, public_key: &Self::PublicKey) -> Self::SharedSecret; } + +/// Key-Derivation Function +pub trait KeyDerivationFunction { + /// Key Agreement Scheme Type + type KeyAgreementScheme: KeyAgreementScheme; + + /// Output Key Type + type Key; + + /// Derives an output key from `shared_secret` computed from a key-agreement scheme. + fn derive( + shared_secret: <Self::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret, + ) -> Self::Key; +} From 5be7c0476714522ed2d4413b423961911dc8eecf Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 12 Nov 2021 01:12:16 -0500 Subject: [PATCH 117/275] feat: rename AssetBalance to AssetValue to better reflect semantics --- manta-accounting/src/asset.rs | 160 +++++++++--------- manta-accounting/src/identity.rs | 8 +- manta-accounting/src/transfer.rs | 113 ++++++------- manta-accounting/src/wallet/signer.rs | 12 +- manta-accounting/src/wallet/state.rs | 16 +- manta-pay/src/accounting/config.rs | 4 +- .../constraint/arkworks/constraint_system.rs | 32 ++-- .../arkworks/proof_systems/groth16.rs | 6 +- 8 files changed, 172 insertions(+), 179 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 6f806d60f..e04f4c58d 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -16,10 +16,10 @@ //! Assets -// TODO: Add macro to build `AssetId` and `AssetBalance`. +// TODO: Add macro to build `AssetId` and `AssetValue`. // TODO: Implement all `rand` sampling traits. -// TODO: Should we rename `AssetBalance` to `AssetValue` to be more consistent? -// TODO: Implement `Concat` for `AssetId` and `AssetBalance`. +// TODO: Should we rename `AssetValue` to `AssetValue` to be more consistent? +// TODO: Implement `Concat` for `AssetId` and `AssetValue`. // TODO: Add implementations for `AssetMap` using key-value maps like `BTreeMap` and `HashMap` use alloc::vec::Vec; @@ -46,7 +46,7 @@ use manta_util::{array_map, fallible_array_map, into_array_unchecked, Concat, Co pub(super) mod prelude { #[doc(inline)] - pub use super::{Asset, AssetBalance, AssetBalances, AssetId}; + pub use super::{Asset, AssetId, AssetValue, AssetValues}; } /// [`AssetId`] Base Type @@ -68,9 +68,9 @@ impl AssetId { pub const SIZE: usize = (Self::BITS / 8) as usize; /// Constructs a new [`Asset`] with `self` as the [`AssetId`] and `value` as the - /// [`AssetBalance`]. + /// [`AssetValue`]. #[inline] - pub const fn with(self, value: AssetBalance) -> Asset { + pub const fn with(self, value: AssetValue) -> Asset { Asset::new(self, value) } @@ -124,10 +124,10 @@ where } } -/// [`AssetBalance`] Base Type -pub type AssetBalanceType = u128; +/// [`AssetValue`] Base Type +pub type AssetValueType = u128; -/// Asset Balance Type +/// Asset Value Type #[derive( Add, AddAssign, @@ -152,19 +152,19 @@ pub type AssetBalanceType = u128; Sum, )] #[from(forward)] -pub struct AssetBalance( - /// [`Asset`] Balance - pub AssetBalanceType, +pub struct AssetValue( + /// [`Asset`] Value + pub AssetValueType, ); -impl AssetBalance { +impl AssetValue { /// The size of this type in bits. - pub const BITS: u32 = AssetBalanceType::BITS; + pub const BITS: u32 = AssetValueType::BITS; /// The size of this type in bytes. pub const SIZE: usize = (Self::BITS / 8) as usize; - /// Constructs a new [`Asset`] with `self` as the [`AssetBalance`] and `id` as the [`AssetId`]. + /// Constructs a new [`Asset`] with `self` as the [`AssetValue`] and `id` as the [`AssetId`]. #[inline] pub const fn with(self, id: AssetId) -> Asset { Asset::new(id, self) @@ -173,7 +173,7 @@ impl AssetBalance { /// Converts a byte array into `self`. #[inline] pub const fn from_bytes(bytes: [u8; Self::SIZE]) -> Self { - Self(AssetBalanceType::from_le_bytes(bytes)) + Self(AssetValueType::from_le_bytes(bytes)) } /// Converts `self` into a byte array. @@ -207,7 +207,7 @@ impl AssetBalance { } } -impl Concat for AssetBalance { +impl Concat for AssetValue { type Item = u8; #[inline] @@ -224,25 +224,25 @@ impl Concat for AssetBalance { } } -impl From<AssetBalance> for [u8; AssetBalance::SIZE] { +impl From<AssetValue> for [u8; AssetValue::SIZE] { #[inline] - fn from(entry: AssetBalance) -> Self { + fn from(entry: AssetValue) -> Self { entry.into_bytes() } } -impl Mul<AssetBalance> for AssetBalanceType { - type Output = AssetBalanceType; +impl Mul<AssetValue> for AssetValueType { + type Output = AssetValueType; #[inline] - fn mul(self, rhs: AssetBalance) -> Self::Output { + fn mul(self, rhs: AssetValue) -> Self::Output { self * rhs.0 } } -impl<D> Sample<D> for AssetBalance +impl<D> Sample<D> for AssetValue where - AssetBalanceType: Sample<D>, + AssetValueType: Sample<D>, { #[inline] fn sample<R>(distribution: D, rng: &mut R) -> Self @@ -253,29 +253,29 @@ where } } -impl<'a> Sum<&'a AssetBalance> for AssetBalance { +impl<'a> Sum<&'a AssetValue> for AssetValue { #[inline] fn sum<I>(iter: I) -> Self where - I: Iterator<Item = &'a AssetBalance>, + I: Iterator<Item = &'a AssetValue>, { iter.copied().sum() } } -/// [`AssetBalance`] Array Type -pub type AssetBalances<const N: usize> = [AssetBalance; N]; +/// [`AssetValue`] Array Type +pub type AssetValues<const N: usize> = [AssetValue; N]; /// Change Iterator /// -/// An iterator over [`AssetBalance`] change amounts. +/// An iterator over [`AssetValue`] change amounts. /// -/// This `struct` is created by the [`make_change`](AssetBalance::make_change) method on -/// [`AssetBalance`]. See its documentation for more. +/// This `struct` is created by the [`make_change`](AssetValue::make_change) method on +/// [`AssetValue`]. See its documentation for more. #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct Change { /// Base Amount - base: AssetBalanceType, + base: AssetValueType, /// Remainder to be Divided remainder: usize, @@ -290,8 +290,8 @@ pub struct Change { impl Change { /// Builds a new [`Change`] iterator for `amount` into `n` pieces. #[inline] - const fn new(amount: AssetBalanceType, n: usize) -> Option<Self> { - let n_div = n as AssetBalanceType; + const fn new(amount: AssetValueType, n: usize) -> Option<Self> { + let n_div = n as AssetValueType; match amount.checked_div(n_div) { Some(base) => Some(Self { base, @@ -305,7 +305,7 @@ impl Change { } impl Iterator for Change { - type Item = AssetBalance; + type Item = AssetValue; #[inline] fn next(&mut self) -> Option<Self::Item> { @@ -314,7 +314,7 @@ impl Iterator for Change { } let amount = self.base + (self.index < self.remainder) as u128; self.index += 1; - Some(AssetBalance(amount)) + Some(AssetValue(amount)) } #[inline] @@ -336,26 +336,26 @@ pub struct Asset { pub id: AssetId, /// Asset Value - pub value: AssetBalance, + pub value: AssetValue, } impl Asset { /// The size of the data in this type in bits. - pub const BITS: u32 = AssetId::BITS + AssetBalance::BITS; + pub const BITS: u32 = AssetId::BITS + AssetValue::BITS; /// The size of the data in this type in bytes. pub const SIZE: usize = (Self::BITS / 8) as usize; /// Builds a new [`Asset`] from an `id` and a `value`. #[inline] - pub const fn new(id: AssetId, value: AssetBalance) -> Self { + pub const fn new(id: AssetId, value: AssetValue) -> Self { Self { id, value } } /// Builds a new zero [`Asset`] with the given `id`. #[inline] pub const fn zero(id: AssetId) -> Self { - Self::new(id, AssetBalance(0)) + Self::new(id, AssetValue(0)) } /// Returns `true` if `self` is a zero [`Asset`] of some [`AssetId`]. @@ -376,7 +376,7 @@ impl Asset { let split = (AssetId::BITS / 8) as usize; Self::new( AssetId::from_bytes(into_array_unchecked(&bytes[..split])), - AssetBalance::from_bytes(into_array_unchecked(&bytes[split..])), + AssetValue::from_bytes(into_array_unchecked(&bytes[split..])), ) } @@ -388,7 +388,7 @@ impl Asset { /// Returns [`self.value`](Self::value) if the given `id` matches [`self.id`](Self::id). #[inline] - pub const fn value_of(&self, id: AssetId) -> Option<AssetBalance> { + pub const fn value_of(&self, id: AssetId) -> Option<AssetValue> { if self.id.0 == id.0 { Some(self.value) } else { @@ -399,7 +399,7 @@ impl Asset { /// Returns a mutable reference to [`self.value`](Self::value) if the given `id` matches /// [`self.id`](Self::id). #[inline] - pub fn value_of_mut(&mut self, id: AssetId) -> Option<&mut AssetBalance> { + pub fn value_of_mut(&mut self, id: AssetId) -> Option<&mut AssetValue> { if self.id.0 == id.0 { Some(&mut self.value) } else { @@ -408,19 +408,19 @@ impl Asset { } } -impl Add<AssetBalance> for Asset { +impl Add<AssetValue> for Asset { type Output = Self; #[inline] - fn add(mut self, rhs: AssetBalance) -> Self::Output { + fn add(mut self, rhs: AssetValue) -> Self::Output { self += rhs; self } } -impl AddAssign<AssetBalance> for Asset { +impl AddAssign<AssetValue> for Asset { #[inline] - fn add_assign(&mut self, rhs: AssetBalance) { + fn add_assign(&mut self, rhs: AssetValue) { self.value += rhs; } } @@ -457,7 +457,7 @@ impl From<Asset> for [u8; Asset::SIZE] { } } -impl From<Asset> for (AssetId, AssetBalance) { +impl From<Asset> for (AssetId, AssetValue) { #[inline] fn from(asset: Asset) -> Self { (asset.id, asset.value) @@ -475,19 +475,19 @@ impl Sample for Asset { } } -impl Sub<AssetBalance> for Asset { +impl Sub<AssetValue> for Asset { type Output = Self; #[inline] - fn sub(mut self, rhs: AssetBalance) -> Self::Output { + fn sub(mut self, rhs: AssetValue) -> Self::Output { self -= rhs; self } } -impl SubAssign<AssetBalance> for Asset { +impl SubAssign<AssetValue> for Asset { #[inline] - fn sub_assign(&mut self, rhs: AssetBalance) { + fn sub_assign(&mut self, rhs: AssetValue) { self.value -= rhs; } } @@ -495,32 +495,32 @@ impl SubAssign<AssetBalance> for Asset { /// Asset Id Variable pub type AssetIdVar<C> = Var<AssetId, C>; -/// Asset Balance Variable -pub type AssetBalanceVar<C> = Var<AssetBalance, C>; +/// Asset Value Variable +pub type AssetValueVar<C> = Var<AssetValue, C>; /// Asset Variable pub struct AssetVar<C> where C: HasVariable<AssetId, Mode = PublicOrSecret> - + HasVariable<AssetBalance, Mode = PublicOrSecret> + + HasVariable<AssetValue, Mode = PublicOrSecret> + ?Sized, { /// Asset Id pub id: AssetIdVar<C>, /// Asset Value - pub value: AssetBalanceVar<C>, + pub value: AssetValueVar<C>, } impl<C> AssetVar<C> where C: HasVariable<AssetId, Mode = PublicOrSecret> - + HasVariable<AssetBalance, Mode = PublicOrSecret> + + HasVariable<AssetValue, Mode = PublicOrSecret> + ?Sized, { /// Builds a new [`AssetVar`] from an `id` and a `value`. #[inline] - pub fn new(id: AssetIdVar<C>, value: AssetBalanceVar<C>) -> Self { + pub fn new(id: AssetIdVar<C>, value: AssetValueVar<C>) -> Self { Self { id, value } } } @@ -528,10 +528,10 @@ where impl<C> Concat for AssetVar<C> where C: HasVariable<AssetId, Mode = PublicOrSecret> - + HasVariable<AssetBalance, Mode = PublicOrSecret> + + HasVariable<AssetValue, Mode = PublicOrSecret> + ?Sized, AssetIdVar<C>: Concat, - AssetBalanceVar<C>: Concat<Item = <AssetIdVar<C> as Concat>::Item>, + AssetValueVar<C>: Concat<Item = <AssetIdVar<C> as Concat>::Item>, { type Item = <AssetIdVar<C> as Concat>::Item; @@ -548,7 +548,7 @@ where impl<C> Variable<C> for AssetVar<C> where C: HasVariable<AssetId, Mode = PublicOrSecret> - + HasVariable<AssetBalance, Mode = PublicOrSecret> + + HasVariable<AssetValue, Mode = PublicOrSecret> + ?Sized, { type Type = Asset; @@ -564,7 +564,7 @@ where ), Allocation::Unknown(mode) => Self::new( unknown::<AssetId, _>(cs, mode.into()), - unknown::<AssetBalance, _>(cs, mode.into()), + unknown::<AssetValue, _>(cs, mode.into()), ), } } @@ -573,7 +573,7 @@ where impl<C> HasAllocation<C> for Asset where C: HasVariable<AssetId, Mode = PublicOrSecret> - + HasVariable<AssetBalance, Mode = PublicOrSecret> + + HasVariable<AssetValue, Mode = PublicOrSecret> + ?Sized, { type Variable = AssetVar<C>; @@ -588,13 +588,13 @@ pub struct AssetCollection<const N: usize> { pub id: AssetId, /// Asset Values - pub values: [AssetBalance; N], + pub values: [AssetValue; N], } impl<const N: usize> AssetCollection<N> { /// Generates a collection of assets with matching [`AssetId`]. #[inline] - pub const fn new(id: AssetId, values: [AssetBalance; N]) -> Self { + pub const fn new(id: AssetId, values: [AssetValue; N]) -> Self { Self { id, values } } } @@ -645,7 +645,7 @@ impl<const N: usize> TryFrom<[Asset; N]> for AssetCollection<N> { pub trait AssetMap: Default { /// Key Type /// - /// Keys are used to access the underlying asset balances. + /// Keys are used to access the underlying asset values. type Key; // TODO: Turn `select` and `zeroes` back into iterator returning methods. @@ -673,7 +673,7 @@ pub trait AssetMap: Default { #[inline] fn insert_all_same<I>(&mut self, id: AssetId, iter: I) where - I: IntoIterator<Item = (Self::Key, AssetBalance)>, + I: IntoIterator<Item = (Self::Key, AssetValue)>, { iter.into_iter() .for_each(move |(key, value)| self.insert(key, id.with(value))); @@ -710,10 +710,10 @@ where M: AssetMap + ?Sized, { /// Change Amount - pub change: AssetBalance, + pub change: AssetValue, - /// Asset Balance Distribution - pub balances: Vec<(M::Key, AssetBalance)>, + /// Asset Value Distribution + pub values: Vec<(M::Key, AssetValue)>, } impl<M> Selection<M> @@ -723,24 +723,24 @@ where /// Returns `true` if `self` is an empty [`Selection`]. #[inline] pub fn is_empty(&self) -> bool { - self.balances.is_empty() + self.values.is_empty() } - /// Returns an iterator over [`self.balances`](Self::balances) by reference. + /// Returns an iterator over [`self.values`](Self::values) by reference. #[inline] pub fn iter(&self) -> SelectionIter<M> { - SelectionIter::new(self.balances.iter()) + SelectionIter::new(self.values.iter()) } - /// Returns an iterator over the keys in [`self.balances`](Self::balances) by reference. + /// Returns an iterator over the keys in [`self.values`](Self::values) by reference. #[inline] pub fn keys(&self) -> SelectionKeys<M> { - SelectionKeys::new(self.balances.iter().map(move |(key, _)| key)) + SelectionKeys::new(self.values.iter().map(move |(key, _)| key)) } } /// [`SelectionIter`] Iterator Type -type SelectionIterType<'s, M> = slice::Iter<'s, (<M as AssetMap>::Key, AssetBalance)>; +type SelectionIterType<'s, M> = slice::Iter<'s, (<M as AssetMap>::Key, AssetValue)>; /// Selection Iterator /// @@ -772,7 +772,7 @@ impl<'s, M> Iterator for SelectionIter<'s, M> where M: AssetMap + ?Sized, { - type Item = &'s (M::Key, AssetBalance); + type Item = &'s (M::Key, AssetValue); #[inline] fn next(&mut self) -> Option<Self::Item> { @@ -787,7 +787,7 @@ where /// [`SelectionKeys`] Map Function Type type SelectionKeysMapFnType<'s, M> = - fn(&'s (<M as AssetMap>::Key, AssetBalance)) -> &'s <M as AssetMap>::Key; + fn(&'s (<M as AssetMap>::Key, AssetValue)) -> &'s <M as AssetMap>::Key; /// [`SelectionKeys`] Iterator Type type SelectionKeysType<'s, M> = iter::Map<SelectionIterType<'s, M>, SelectionKeysMapFnType<'s, M>>; @@ -857,7 +857,7 @@ mod test { fn asset_arithmetic() { let mut rng = thread_rng(); let mut asset = Asset::zero(AssetId::gen(&mut rng)); - let value = AssetBalance::gen(&mut rng); + let value = AssetValue::gen(&mut rng); let _ = asset + value; asset += value; let _ = asset - value; @@ -869,7 +869,7 @@ mod test { fn test_change_iterator() { let mut rng = thread_rng(); for _ in 0..0xFFF { - let amount = AssetBalance(rng.gen_range(0..0xFFFF_FFFF)); + let amount = AssetValue(rng.gen_range(0..0xFFFF_FFFF)); let n = rng.gen_range(1..0xFFFF); let change = amount.make_change(n).unwrap().collect::<Vec<_>>(); assert_eq!(n, change.len()); diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 3dad21c73..7e710beb7 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -21,7 +21,7 @@ // TODO: Get rid of [`Spend`] and [`OpenSpend`] if possible. They don't seem to be that useful. // See `crate::wallet::signer`. -use crate::asset::{Asset, AssetBalance, AssetId, AssetVar}; +use crate::asset::{Asset, AssetId, AssetValue, AssetVar}; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use manta_crypto::{ accumulator::{Accumulator, MembershipProof, Verifier}, @@ -993,7 +993,7 @@ where /// Returns the asset value for this sender. #[inline] - pub(super) fn asset_value(&self) -> AssetBalance { + pub(super) fn asset_value(&self) -> AssetValue { self.asset.value } @@ -1235,7 +1235,7 @@ where /// Returns the asset value for this receiver. #[inline] - pub(crate) fn asset_value(&self) -> AssetBalance { + pub(crate) fn asset_value(&self) -> AssetValue { self.asset.value } @@ -1412,7 +1412,7 @@ pub mod constraint { /// Constraint System type ConstraintSystem: ConstraintSystem + HasVariable<AssetId, Mode = PublicOrSecret> - + HasVariable<AssetBalance, Mode = PublicOrSecret>; + + HasVariable<AssetValue, Mode = PublicOrSecret>; /// Secret Key Variable type SecretKeyVar: Variable<Self::ConstraintSystem, Type = Self::SecretKey, Mode = Secret>; diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index dacff3128..551523d3c 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -22,7 +22,7 @@ // TODO: See if we can get rid of `PublicTransfer` and `SecretTransfer` and just use `Transfer`. use crate::{ - asset::{Asset, AssetBalance, AssetBalances, AssetId}, + asset::{Asset, AssetId, AssetValue, AssetValues}, identity::{ self, constraint::UtxoVar, ReceiverLedger, ReceiverPostError, SenderLedger, SenderPostError, Utxo, VoidNumber, @@ -62,14 +62,14 @@ pub trait Configuration: /// Constraint System type ConstraintSystem: constraint::ConstraintSystem + HasVariable<AssetId, Variable = Self::AssetIdVar, Mode = PublicOrSecret> - + HasVariable<AssetBalance, Variable = Self::AssetBalanceVar, Mode = PublicOrSecret> + + HasVariable<AssetValue, Variable = Self::AssetValueVar, Mode = PublicOrSecret> + HasVariable<<Self::UtxoSetVerifier as Verifier>::Checkpoint, Mode = Public> + HasVariable<<Self::UtxoSetVerifier as Verifier>::Witness, Mode = Secret>; /// Proof System type ProofSystem: ProofSystem<ConstraintSystem = ConstraintSystem<Self>, Verification = bool> + ProofSystemInput<AssetId> - + ProofSystemInput<AssetBalance> + + ProofSystemInput<AssetValue> + ProofSystemInput<VoidNumber<Self>> + ProofSystemInput<<Self::UtxoSetVerifier as Verifier>::Checkpoint> + ProofSystemInput<Utxo<Self>>; @@ -78,10 +78,10 @@ pub trait Configuration: type AssetIdVar: Variable<ConstraintSystem<Self>, Mode = PublicOrSecret, Type = AssetId> + Equal<ConstraintSystem<Self>>; - /// Asset Balance Variable - type AssetBalanceVar: Variable<ConstraintSystem<Self>, Mode = PublicOrSecret, Type = AssetBalance> + /// Asset Value Variable + type AssetValueVar: Variable<ConstraintSystem<Self>, Mode = PublicOrSecret, Type = AssetValue> + Equal<ConstraintSystem<Self>> - + Add<Output = Self::AssetBalanceVar>; + + Add<Output = Self::AssetValueVar>; /// Integrated Encryption Scheme for [`Asset`] type IntegratedEncryptionScheme: IntegratedEncryptionScheme<Plaintext = Asset>; @@ -190,7 +190,7 @@ pub trait TransferLedger<C>: where C: Configuration, { - /// Valid [`AssetBalance`] for [`TransferPost`] source + /// Valid [`AssetValue`] for [`TransferPost`] source /// /// # Safety /// @@ -218,7 +218,7 @@ where /// amount given in `sources`. fn check_source_balances( &self, - sources: Vec<AssetBalance>, + sources: Vec<AssetValue>, ) -> Result<Vec<Self::ValidSourceBalance>, InsufficientPublicBalance>; /// Checks that the transfer `proof` is valid. @@ -228,7 +228,7 @@ where sources: &[Self::ValidSourceBalance], senders: &[SenderPostingKey<C, Self>], receivers: &[ReceiverPostingKey<C, Self>], - sinks: &[AssetBalance], + sinks: &[AssetValue], proof: Proof<C>, ) -> Option<Self::ValidProof>; @@ -243,7 +243,7 @@ where &mut self, asset_id: AssetId, sources: Vec<Self::ValidSourceBalance>, - sinks: Vec<AssetBalance>, + sinks: Vec<AssetValue>, proof: Self::ValidProof, super_key: &TransferLedgerSuperPostingKey<C, Self>, ); @@ -317,10 +317,10 @@ pub struct PublicTransfer<const SOURCES: usize, const SINKS: usize> { pub asset_id: Option<AssetId>, /// Public Asset Sources - pub sources: AssetBalances<SOURCES>, + pub sources: AssetValues<SOURCES>, /// Public Asset Sinks - pub sinks: AssetBalances<SINKS>, + pub sinks: AssetValues<SINKS>, } impl<const SOURCES: usize, const SINKS: usize> PublicTransfer<SOURCES, SINKS> { @@ -328,8 +328,8 @@ impl<const SOURCES: usize, const SINKS: usize> PublicTransfer<SOURCES, SINKS> { #[inline] pub const fn new( asset_id: AssetId, - sources: AssetBalances<SOURCES>, - sinks: AssetBalances<SINKS>, + sources: AssetValues<SOURCES>, + sinks: AssetValues<SINKS>, ) -> Self { Self::new_unchecked( if has_no_public_participants(SOURCES, 0, 0, SINKS) { @@ -346,8 +346,8 @@ impl<const SOURCES: usize, const SINKS: usize> PublicTransfer<SOURCES, SINKS> { #[inline] const fn new_unchecked( asset_id: Option<AssetId>, - sources: AssetBalances<SOURCES>, - sinks: AssetBalances<SINKS>, + sources: AssetValues<SOURCES>, + sinks: AssetValues<SINKS>, ) -> Self { Self { asset_id, @@ -364,13 +364,13 @@ impl<const SOURCES: usize, const SINKS: usize> PublicTransfer<SOURCES, SINKS> { /// Returns the sum of the asset values of the sources in this transfer. #[inline] - pub fn source_sum(&self) -> AssetBalance { + pub fn source_sum(&self) -> AssetValue { self.sources.iter().sum() } /// Returns the sum of the asset values of the sinks in this transfer. #[inline] - pub fn sink_sum(&self) -> AssetBalance { + pub fn sink_sum(&self) -> AssetValue { self.sinks.iter().sum() } @@ -484,13 +484,13 @@ where /// Returns the sum of the asset values of the senders in this transfer. #[inline] - pub fn sender_sum(&self) -> AssetBalance { + pub fn sender_sum(&self) -> AssetValue { self.senders.iter().map(Sender::asset_value).sum() } /// Returns the sum of the asset values of the receivers in this transfer. #[inline] - pub fn receiver_sum(&self) -> AssetBalance { + pub fn receiver_sum(&self) -> AssetValue { self.receivers.iter().map(Receiver::asset_value).sum() } @@ -557,10 +557,10 @@ where #[inline] pub fn new( asset_id: AssetId, - sources: AssetBalances<SOURCES>, + sources: AssetValues<SOURCES>, senders: [Sender<C>; SENDERS], receivers: [Receiver<C>; RECEIVERS], - sinks: AssetBalances<SINKS>, + sinks: AssetValues<SINKS>, ) -> Self { Self::check_shape(); Self::new_unchecked(asset_id, sources, senders, receivers, sinks) @@ -599,10 +599,10 @@ where #[inline] fn new_unchecked( asset_id: AssetId, - sources: AssetBalances<SOURCES>, + sources: AssetValues<SOURCES>, senders: [Sender<C>; SENDERS], receivers: [Receiver<C>; RECEIVERS], - sinks: AssetBalances<SINKS>, + sinks: AssetValues<SINKS>, ) -> Self { Self { public: PublicTransfer::new(asset_id, sources, sinks), @@ -628,37 +628,37 @@ where /// Returns the sum of the asset values of the sources in this transfer. #[inline] - pub fn source_sum(&self) -> AssetBalance { + pub fn source_sum(&self) -> AssetValue { self.public.source_sum() } /// Returns the sum of the asset values of the senders in this transfer. #[inline] - pub fn sender_sum(&self) -> AssetBalance { + pub fn sender_sum(&self) -> AssetValue { self.secret.sender_sum() } /// Returns the sum of the asset values of the receivers in this transfer. #[inline] - pub fn receiver_sum(&self) -> AssetBalance { + pub fn receiver_sum(&self) -> AssetValue { self.secret.receiver_sum() } /// Returns the sum of the asset values of the sinks in this transfer. #[inline] - pub fn sink_sum(&self) -> AssetBalance { + pub fn sink_sum(&self) -> AssetValue { self.public.sink_sum() } /// Returns the sum of the asset values of the sources and senders in this transfer. #[inline] - pub fn input_sum(&self) -> AssetBalance { + pub fn input_sum(&self) -> AssetValue { self.source_sum() + self.sender_sum() } /// Returns the sum of the asset values of the receivers and sinks in this transfer. #[inline] - pub fn output_sum(&self) -> AssetBalance { + pub fn output_sum(&self) -> AssetValue { self.receiver_sum() + self.sink_sum() } @@ -864,7 +864,7 @@ struct TransferParticipantsVar< C: Configuration, { /// Source Variables - sources: Vec<C::AssetBalanceVar>, + sources: Vec<C::AssetValueVar>, /// Sender Variables senders: Vec<SenderVar<C>>, @@ -873,7 +873,7 @@ struct TransferParticipantsVar< receivers: Vec<ReceiverVar<C>>, /// Sink Variables - sinks: Vec<C::AssetBalanceVar>, + sinks: Vec<C::AssetValueVar>, } impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> @@ -917,7 +917,7 @@ where Allocation::Unknown(mode) => Self { sources: (0..SOURCES) .into_iter() - .map(|_| C::AssetBalanceVar::new_unknown(cs, Public)) + .map(|_| C::AssetValueVar::new_unknown(cs, Public)) .collect(), senders: (0..SENDERS) .into_iter() @@ -929,7 +929,7 @@ where .collect(), sinks: (0..SINKS) .into_iter() - .map(|_| C::AssetBalanceVar::new_unknown(cs, Public)) + .map(|_| C::AssetValueVar::new_unknown(cs, Public)) .collect(), }, } @@ -946,10 +946,10 @@ pub struct InsufficientPublicBalance { pub index: usize, /// Current Balance - pub balance: AssetBalance, + pub balance: AssetValue, /// Amount Attempting to Withdraw - pub withdraw: AssetBalance, + pub withdraw: AssetValue, } /// Transfer Post Error @@ -985,7 +985,7 @@ where asset_id: Option<AssetId>, /// Sources - sources: Vec<AssetBalance>, + sources: Vec<AssetValue>, /// Sender Posts sender_posts: Vec<SenderPost<C>>, @@ -994,7 +994,7 @@ where receiver_posts: Vec<ReceiverPost<C>>, /// Sinks - sinks: Vec<AssetBalance>, + sinks: Vec<AssetValue>, /// Validity Proof validity_proof: Proof<C>, @@ -1100,7 +1100,7 @@ where receiver_posting_keys: Vec<ReceiverPostingKey<C, L>>, /// Sinks - sinks: Vec<AssetBalance>, + sinks: Vec<AssetValue>, /// Validity Proof Posting Key validity_proof: L::ValidProof, @@ -1412,7 +1412,7 @@ pub mod canonical { #[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] pub mod test { use super::*; - use crate::{asset::AssetBalanceType, identity::Identity}; + use crate::{asset::AssetValueType, identity::Identity}; use manta_crypto::{ accumulator::Accumulator, rand::{Rand, Sample, Standard, TrySample}, @@ -1472,8 +1472,8 @@ pub mod test { const RECEIVERS: usize, >( asset_id: AssetId, - sender_total: AssetBalance, - receiver_total: AssetBalance, + sender_total: AssetValue, + receiver_total: AssetValue, commitment_scheme: &C::CommitmentScheme, utxo_set: &mut S, rng: &mut R, @@ -1486,8 +1486,8 @@ pub mod test { { FixedSecretTransfer::<C, S>::try_sample_custom_distribution( asset_id, - sample_asset_balances::<_, SENDERS>(sender_total, rng), - sample_asset_balances::<_, RECEIVERS>(receiver_total, rng), + sample_asset_values::<_, SENDERS>(sender_total, rng), + sample_asset_values::<_, RECEIVERS>(receiver_total, rng), commitment_scheme, utxo_set, rng, @@ -1508,8 +1508,8 @@ pub mod test { const RECEIVERS: usize, >( asset_id: AssetId, - senders: AssetBalances<SENDERS>, - receivers: AssetBalances<RECEIVERS>, + senders: AssetValues<SENDERS>, + receivers: AssetValues<RECEIVERS>, commitment_scheme: &C::CommitmentScheme, utxo_set: &mut S, rng: &mut R, @@ -1596,11 +1596,7 @@ pub mod test { /// /// This is a naive algorithm and should only be used for testing purposes. #[inline] - pub fn value_distribution<R>( - count: usize, - total: AssetBalance, - rng: &mut R, - ) -> Vec<AssetBalance> + pub fn value_distribution<R>(count: usize, total: AssetValue, rng: &mut R) -> Vec<AssetValue> where R: CryptoRng + RngCore + ?Sized, { @@ -1608,9 +1604,9 @@ pub mod test { return Default::default(); } let mut result = Vec::with_capacity(count + 1); - result.push(AssetBalance(0)); + result.push(AssetValue(0)); for _ in 1..count { - result.push(AssetBalance(AssetBalanceType::gen(rng) % total.0)); + result.push(AssetValue(AssetValueType::gen(rng) % total.0)); } result.push(total); result.sort_unstable(); @@ -1621,16 +1617,13 @@ pub mod test { result } - /// Samples asset balances from `rng`. + /// Samples asset values from `rng`. /// /// # Warning /// /// This is a naive algorithm and should only be used for testing purposes. #[inline] - pub fn sample_asset_balances<R, const N: usize>( - total: AssetBalance, - rng: &mut R, - ) -> AssetBalances<N> + pub fn sample_asset_values<R, const N: usize>(total: AssetValue, rng: &mut R) -> AssetValues<N> where R: CryptoRng + RngCore + ?Sized, { @@ -1660,8 +1653,8 @@ pub mod test { { Self::new( distribution.0.id, - sample_asset_balances(distribution.0.value, rng), - sample_asset_balances(distribution.0.value, rng), + sample_asset_values(distribution.0.value, rng), + sample_asset_values(distribution.0.value, rng), ) } } diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 5662d72b7..daca9b36a 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -28,7 +28,7 @@ // reveal information about the internal state (privacy leak, not a secrecy leak). use crate::{ - asset::{Asset, AssetBalance, AssetId, AssetMap}, + asset::{Asset, AssetId, AssetMap, AssetValue}, fs::{Load, LoadWith, Save, SaveWith}, identity::{self, Identity, PreSender, Utxo}, key::{ @@ -423,7 +423,7 @@ where &mut self, commitment_scheme: &C::CommitmentScheme, asset_id: AssetId, - sender_sum: AssetBalance, + sender_sum: AssetValue, rng: &mut R, ) -> Result<TransferAccumulator<D, C, RECEIVERS>, InternalIdentityError<D, C>> where @@ -811,7 +811,7 @@ where } self.pending_assets.remove = selection.keys().cloned().collect(); let pre_senders = selection - .balances + .values .into_iter() .map(move |(k, v)| self.get_pre_sender(k, asset.id.with(v))) .collect::<Result<_, _>>()?; @@ -960,7 +960,7 @@ where fn next_change( &mut self, asset_id: AssetId, - change: AssetBalance, + change: AssetValue, ) -> Result<Receiver<C>, Error<C::DerivedSecretKeyGenerator, C>> { let asset = asset_id.with(change); let (receiver, index) = self @@ -1214,7 +1214,7 @@ where C: transfer::Configuration, { /// Selection Change - pub change: AssetBalance, + pub change: AssetValue, /// Selection Pre-Senders pub pre_senders: Vec<PreSender<C>>, @@ -1226,7 +1226,7 @@ where { /// Builds a new [`Selection`] from `change` and `pre_senders`. #[inline] - pub fn new(change: AssetBalance, pre_senders: Vec<PreSender<C>>) -> Self { + pub fn new(change: AssetValue, pre_senders: Vec<PreSender<C>>) -> Self { Self { change, pre_senders, diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 41593f10c..f1369d67d 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -17,7 +17,7 @@ //! Full Wallet Implementation use crate::{ - asset::{Asset, AssetBalance, AssetId}, + asset::{Asset, AssetId, AssetValue}, key::DerivedSecretKeyGenerator, transfer::{ canonical::{Transaction, TransactionKind}, @@ -43,7 +43,7 @@ use std::{ /// Balance State pub trait BalanceState { /// Returns the current balance associated with this `id`. - fn balance(&self, id: AssetId) -> AssetBalance; + fn balance(&self, id: AssetId) -> AssetValue; /// Returns true if `self` contains at least `asset.value` of the asset of kind `asset.id`. #[inline] @@ -75,7 +75,7 @@ pub trait BalanceState { /// Performs an unchecked withdraw on `balance`, panicking on overflow. #[inline] -fn withdraw_unchecked(balance: Option<&mut AssetBalance>, withdraw: AssetBalance) { +fn withdraw_unchecked(balance: Option<&mut AssetValue>, withdraw: AssetValue) { let balance = balance.expect("Trying to withdraw from a zero balance."); *balance = balance .checked_sub(withdraw) @@ -84,7 +84,7 @@ fn withdraw_unchecked(balance: Option<&mut AssetBalance>, withdraw: AssetBalance impl BalanceState for Vec<Asset> { #[inline] - fn balance(&self, id: AssetId) -> AssetBalance { + fn balance(&self, id: AssetId) -> AssetValue { self.iter() .find_map(move |a| a.value_of(id)) .unwrap_or_default() @@ -110,7 +110,7 @@ impl BalanceState for Vec<Asset> { macro_rules! impl_balance_state_map_body { ($entry:tt) => { #[inline] - fn balance(&self, id: AssetId) -> AssetBalance { + fn balance(&self, id: AssetId) -> AssetValue { self.get(&id).copied().unwrap_or_default() } @@ -136,7 +136,7 @@ macro_rules! impl_balance_state_map_body { } /// B-Tree Map [`BalanceState`] Implementation -pub type BTreeMapBalanceState = BTreeMap<AssetId, AssetBalance>; +pub type BTreeMapBalanceState = BTreeMap<AssetId, AssetValue>; impl BalanceState for BTreeMapBalanceState { impl_balance_state_map_body! { BTreeMapEntry } @@ -145,7 +145,7 @@ impl BalanceState for BTreeMapBalanceState { /// Hash Map [`BalanceState`] Implementation #[cfg(feature = "std")] #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] -pub type HashMapBalanceState<S = RandomState> = HashMap<AssetId, AssetBalance, S>; +pub type HashMapBalanceState<S = RandomState> = HashMap<AssetId, AssetValue, S>; #[cfg(feature = "std")] impl<S> BalanceState for HashMapBalanceState<S> @@ -212,7 +212,7 @@ where /// Returns the current balance associated with this `id`. #[inline] - pub fn balance(&self, id: AssetId) -> AssetBalance { + pub fn balance(&self, id: AssetId) -> AssetValue { self.assets.balance(id) } diff --git a/manta-pay/src/accounting/config.rs b/manta-pay/src/accounting/config.rs index 9f82d8070..ae96e791d 100644 --- a/manta-pay/src/accounting/config.rs +++ b/manta-pay/src/accounting/config.rs @@ -24,7 +24,7 @@ use crate::{ crypto::{ commitment::pedersen::{self, PedersenWindow}, constraint::arkworks::{ - proof_systems::groth16::Groth16, ArkConstraintSystem, AssetBalanceVar, AssetIdVar, + proof_systems::groth16::Groth16, ArkConstraintSystem, AssetIdVar, AssetValueVar, }, ies::IES, merkle_tree::{ @@ -153,7 +153,7 @@ impl transfer::Configuration for Configuration { type ConstraintSystem = ConstraintSystem; type ProofSystem = ProofSystem; type AssetIdVar = AssetIdVar<ConstraintField>; - type AssetBalanceVar = AssetBalanceVar<ConstraintField>; + type AssetValueVar = AssetValueVar<ConstraintField>; type IntegratedEncryptionScheme = IES; type UtxoSetVerifier = Parameters; type UtxoSetVerifierVar = UtxoSetVerifierVar; diff --git a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs index 2e9cc828e..387ac4921 100644 --- a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs @@ -26,7 +26,7 @@ use core::{ borrow::Borrow, ops::{Add, AddAssign}, }; -use manta_accounting::{AssetBalance, AssetId}; +use manta_accounting::{AssetId, AssetValue}; use manta_crypto::constraint::{ measure::Measure, reflection::HasAllocation, types::Bool, Allocation, AllocationMode, ConstraintSystem, Equal, Public, PublicOrSecret, Secret, Variable, VariableSource, @@ -437,10 +437,10 @@ where } } -/// Asset Balance Variable +/// Asset Value Variable #[derive(derivative::Derivative)] #[derivative(Clone, Debug)] -pub struct AssetBalanceVar<F> +pub struct AssetValueVar<F> where F: PrimeField, { @@ -448,21 +448,21 @@ where field_point: FpVar<F>, /// Byte Array - bytes: Option<ByteArrayVar<F, { AssetBalance::SIZE }>>, + bytes: Option<ByteArrayVar<F, { AssetValue::SIZE }>>, } -impl<F> AssetBalanceVar<F> +impl<F> AssetValueVar<F> where F: PrimeField, { - /// Builds a new [`AssetBalanceVar`] from `field_point` and `bytes`. + /// Builds a new [`AssetValueVar`] from `field_point` and `bytes`. #[inline] - fn new(field_point: FpVar<F>, bytes: Option<ByteArrayVar<F, { AssetBalance::SIZE }>>) -> Self { + fn new(field_point: FpVar<F>, bytes: Option<ByteArrayVar<F, { AssetValue::SIZE }>>) -> Self { Self { field_point, bytes } } } -impl<F> Concat for AssetBalanceVar<F> +impl<F> Concat for AssetValueVar<F> where F: PrimeField, { @@ -479,11 +479,11 @@ where } } -impl<F> Variable<ArkConstraintSystem<F>> for AssetBalanceVar<F> +impl<F> Variable<ArkConstraintSystem<F>> for AssetValueVar<F> where F: PrimeField, { - type Type = AssetBalance; + type Type = AssetValue; type Mode = PublicOrSecret; @@ -499,21 +499,21 @@ where ), Allocation::Unknown(mode) => Self::new( Fp::as_unknown(cs, mode), - Some(<[u8; AssetBalance::SIZE]>::as_unknown(cs, mode)), + Some(<[u8; AssetValue::SIZE]>::as_unknown(cs, mode)), ), } } } -impl<F> HasAllocation<ArkConstraintSystem<F>> for AssetBalance +impl<F> HasAllocation<ArkConstraintSystem<F>> for AssetValue where F: PrimeField, { - type Variable = AssetBalanceVar<F>; + type Variable = AssetValueVar<F>; type Mode = PublicOrSecret; } -impl<F> Equal<ArkConstraintSystem<F>> for AssetBalanceVar<F> +impl<F> Equal<ArkConstraintSystem<F>> for AssetValueVar<F> where F: PrimeField, { @@ -527,7 +527,7 @@ where } } -impl<F> Add for AssetBalanceVar<F> +impl<F> Add for AssetValueVar<F> where F: PrimeField, { @@ -540,7 +540,7 @@ where } } -impl<F> AddAssign for AssetBalanceVar<F> +impl<F> AddAssign for AssetValueVar<F> where F: PrimeField, { diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs index 4005a852a..3fb7959d4 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs @@ -30,7 +30,7 @@ use ark_ff::{Field, ToConstraintField}; use ark_groth16::{Groth16 as ArkGroth16, PreparedVerifyingKey, Proof, ProvingKey}; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError}; use core::marker::PhantomData; -use manta_accounting::asset::{AssetBalance, AssetId}; +use manta_accounting::asset::{AssetId, AssetValue}; use manta_crypto::{ constraint::{Input, ProofSystem}, rand::{CryptoRng, RngCore, SizedRng}, @@ -147,12 +147,12 @@ where } } -impl<E> Input<AssetBalance> for Groth16<E> +impl<E> Input<AssetValue> for Groth16<E> where E: PairingEngine, { #[inline] - fn extend(input: &mut Self::Input, next: &AssetBalance) { + fn extend(input: &mut Self::Input, next: &AssetValue) { input.push(next.0.into()); input.append(&mut next.into_bytes().to_field_elements().unwrap()); } From e6ce9e865a7e80a56912e512d0bb1a3afd1394c3 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 12 Nov 2021 19:19:42 -0500 Subject: [PATCH 118/275] wip: start new transfer protocol --- manta-accounting/src/address.rs | 4 +- manta-accounting/src/identity2.rs | 679 +++++++++++++++++++++++++++ manta-accounting/src/lib.rs | 1 + manta-crypto/src/accumulator.rs | 13 +- manta-crypto/src/commitment.rs | 4 + manta-crypto/src/constraint.rs | 27 ++ manta-crypto/src/merkle_tree/tree.rs | 4 +- 7 files changed, 726 insertions(+), 6 deletions(-) create mode 100644 manta-accounting/src/identity2.rs diff --git a/manta-accounting/src/address.rs b/manta-accounting/src/address.rs index 0725ae792..facc94d5a 100644 --- a/manta-accounting/src/address.rs +++ b/manta-accounting/src/address.rs @@ -77,7 +77,7 @@ where T: KeyType, { /// Secret Key - secret_key: K::SecretKey, + pub(crate) secret_key: K::SecretKey, /// Type Parameter Marker __: PhantomData<T>, @@ -208,7 +208,7 @@ where K: KeyAgreementScheme, { /// Spend Part of the Spending Key - spend: SecretSpendKey<K>, + pub(crate) spend: SecretSpendKey<K>, /// View Part of the Spending Key view: SecretViewKey<K>, diff --git a/manta-accounting/src/identity2.rs b/manta-accounting/src/identity2.rs new file mode 100644 index 000000000..9d17fd8ca --- /dev/null +++ b/manta-accounting/src/identity2.rs @@ -0,0 +1,679 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Sender and Receiver Identities + +use crate::address; +use core::marker::PhantomData; +use manta_crypto::{ + accumulator::{Accumulator, MembershipProof, Verifier}, + commitment::{CommitmentScheme, Input as CommitmentInput}, + encryption::{EncryptedMessage, HybridPublicKeyEncryptionScheme}, + key::KeyAgreementScheme, +}; + +/// Identity Configuration +pub trait Configuration { + /// Asset Type + type Asset; + + /// Key Scheme Type + type KeyScheme: KeyAgreementScheme; + + /// Commitment Scheme Type + type CommitmentScheme: CommitmentScheme<Randomness = <Self::KeyScheme as KeyAgreementScheme>::SharedSecret> + + CommitmentInput<Self::Asset> + + CommitmentInput<<Self::KeyScheme as KeyAgreementScheme>::SecretKey>; +} + +/// Spending Key Type +pub type SpendingKey<C> = address::SpendingKey<<C as Configuration>::KeyScheme>; + +/// Receiving Key Type +pub type ReceivingKey<C> = address::ReceivingKey<<C as Configuration>::KeyScheme>; + +/// Secret Key Type +pub type SecretKey<C> = <<C as Configuration>::KeyScheme as KeyAgreementScheme>::SecretKey; + +/// Public Key Type +pub type PublicKey<C> = <<C as Configuration>::KeyScheme as KeyAgreementScheme>::PublicKey; + +/// Trapdoor Type +pub type Trapdoor<C> = <<C as Configuration>::KeyScheme as KeyAgreementScheme>::SharedSecret; + +/// UTXO Type +pub type Utxo<C> = <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Output; + +/// Void Number Type +pub type VoidNumber<C> = <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Output; + +/// Pre-Sender +pub struct PreSender<C> +where + C: Configuration, +{ + /// Spending Key + spending_key: SpendingKey<C>, + + /// Ephemeral Public Key + ephemeral_public_key: PublicKey<C>, + + /// Trapdoor + trapdoor: Trapdoor<C>, + + /// Asset + asset: C::Asset, + + /// Unspent Transaction Output + utxo: Utxo<C>, + + /// Void Number + void_number: VoidNumber<C>, +} + +impl<C> PreSender<C> +where + C: Configuration, +{ + /// Builds a new [`PreSender`] for `spending_key` to spend `asset` with + /// `ephemeral_public_key`. + #[inline] + pub fn new( + spending_key: SpendingKey<C>, + ephemeral_public_key: PublicKey<C>, + asset: C::Asset, + commitment_scheme: &C::CommitmentScheme, + ) -> Self { + let trapdoor = spending_key.spending_secret(&ephemeral_public_key); + Self { + utxo: commitment_scheme.commit_one(&asset, &trapdoor), + void_number: commitment_scheme.commit_one(&spending_key.spend.secret_key, &trapdoor), + spending_key, + ephemeral_public_key, + asset, + trapdoor, + } + } + + /// Inserts the [`Utxo`] corresponding to `self` into the `utxo_set` with the intention of + /// returning a proof later by a call to [`get_proof`](Self::get_proof). + #[inline] + pub fn insert_utxo<S>(&self, utxo_set: &mut S) -> bool + where + S: Accumulator<Item = Utxo<C>>, + { + utxo_set.insert(&self.utxo) + } + + /// Requests the membership proof of the [`Utxo`] corresponding to `self` from `utxo_set` to + /// prepare the conversion from `self` into a [`Sender`]. + #[inline] + pub fn get_proof<S>(&self, utxo_set: &S) -> Option<SenderProof<C, S::Verifier>> + where + S: Accumulator<Item = Utxo<C>>, + { + Some(SenderProof { + utxo_membership_proof: utxo_set.prove(&self.utxo)?, + __: PhantomData, + }) + } + + /// Converts `self` into a [`Sender`] by attaching `proof` to it. + /// + /// # Note + /// + /// When using this method, be sure to check that [`SenderProof::can_upgrade`] returns `true`. + /// Otherwise, using the sender returned here will most likely return an error when posting to + /// the ledger. + #[inline] + pub fn upgrade<V>(self, proof: SenderProof<C, V>) -> Sender<C, V> + where + V: Verifier<Item = Utxo<C>> + ?Sized, + { + Sender { + spending_key: self.spending_key, + ephemeral_public_key: self.ephemeral_public_key, + trapdoor: self.trapdoor, + asset: self.asset, + utxo: self.utxo, + utxo_membership_proof: proof.utxo_membership_proof, + void_number: self.void_number, + } + } + + /// Tries to convert `self` into a [`Sender`] by getting a proof from `utxo_set`. + #[inline] + pub fn try_upgrade<S>(self, utxo_set: &S) -> Option<Sender<C, S::Verifier>> + where + S: Accumulator<Item = Utxo<C>>, + { + Some(self.get_proof(utxo_set)?.upgrade(self)) + } +} + +/// Sender Proof +/// +/// This `struct` is created by the [`get_proof`](PreSender::get_proof) method on [`PreSender`]. +/// See its documentation for more. +pub struct SenderProof<C, V> +where + C: Configuration, + V: Verifier<Item = Utxo<C>> + ?Sized, +{ + /// UTXO Membership Proof + utxo_membership_proof: MembershipProof<V>, + + /// Type Parameter Marker + __: PhantomData<C>, +} + +impl<C, V> SenderProof<C, V> +where + C: Configuration, + V: Verifier<Item = Utxo<C>> + ?Sized, +{ + /// Returns `true` if a [`PreSender`] could be upgraded using `self` given the `utxo_set`. + #[inline] + pub fn can_upgrade<S>(&self, utxo_set: &S) -> bool + where + S: Accumulator< + Item = V::Item, + Checkpoint = V::Checkpoint, + Witness = V::Witness, + Verifier = V, + >, + { + self.utxo_membership_proof.matching_checkpoint(utxo_set) + } + + /// Upgrades the `pre_sender` to a [`Sender`] by attaching `self` to it. + /// + /// # Note + /// + /// When using this method, be sure to check that [`can_upgrade`](Self::can_upgrade) returns + /// `true`. Otherwise, using the sender returned here will most likely return an error when + /// posting to the ledger. + #[inline] + pub fn upgrade(self, pre_sender: PreSender<C>) -> Sender<C, V> { + pre_sender.upgrade(self) + } +} + +/// Sender +pub struct Sender<C, V> +where + C: Configuration, + V: Verifier<Item = Utxo<C>> + ?Sized, +{ + /// Spending Key + spending_key: SpendingKey<C>, + + /// Ephemeral Public Key + ephemeral_public_key: PublicKey<C>, + + /// Trapdoor + trapdoor: Trapdoor<C>, + + /// Asset + asset: C::Asset, + + /// Unspent Transaction Output + utxo: Utxo<C>, + + /// UTXO Membership Proof + utxo_membership_proof: MembershipProof<V>, + + /// Void Number + void_number: VoidNumber<C>, +} + +impl<C, V> Sender<C, V> +where + C: Configuration, + V: Verifier<Item = Utxo<C>>, +{ + /// Reverts `self` back into a [`PreSender`]. + /// + /// This method should be called if the [`Utxo`] membership proof attached to `self` was deemed + /// invalid or had expired. + #[inline] + pub fn downgrade(self) -> PreSender<C> { + PreSender { + spending_key: self.spending_key, + ephemeral_public_key: self.ephemeral_public_key, + trapdoor: self.trapdoor, + asset: self.asset, + utxo: self.utxo, + void_number: self.void_number, + } + } + + /// Extracts the ledger posting data from `self`. + #[inline] + pub fn into_post(self) -> SenderPost<C, V> { + SenderPost { + utxo_checkpoint: self.utxo_membership_proof.into_checkpoint(), + void_number: self.void_number, + } + } +} + +/// Sender Ledger +pub trait SenderLedger<C, V> +where + C: Configuration, + V: Verifier<Item = Utxo<C>>, +{ + /// Valid [`VoidNumber`] Posting Key + /// + /// # Safety + /// + /// This type must be some wrapper around [`VoidNumber`] which can only be constructed by this + /// implementation of [`SenderLedger`]. This is to prevent that [`spend`](Self::spend) is + /// called before [`is_unspent`](Self::is_unspent) and + /// [`is_matching_checkpoint`](Self::is_matching_checkpoint). + type ValidVoidNumber; + + /// Valid Utxo State Posting Key + /// + /// # Safety + /// + /// This type must be some wrapper around [`S::Checkpoint`] which can only be constructed by this + /// implementation of [`SenderLedger`]. This is to prevent that [`spend`](Self::spend) is + /// called before [`is_unspent`](Self::is_unspent) and + /// [`is_matching_checkpoint`](Self::is_matching_checkpoint). + /// + /// [`S::Checkpoint`]: Verifier::Checkpoint + type ValidUtxoCheckpoint; + + /// Super Posting Key + /// + /// Type that allows super-traits of [`SenderLedger`] to customize posting key behavior. + type SuperPostingKey: Copy; + + /// Checks if the ledger already contains the `void_number` in its set of void numbers. + /// + /// Existence of such a void number could indicate a possible double-spend. + fn is_unspent(&self, void_number: VoidNumber<C>) -> Option<Self::ValidVoidNumber>; + + /// Checks if the `checkpoint` matches the current checkpoint of the UTXO set that is stored on + /// the ledger. + /// + /// Failure to match the ledger state means that the sender was constructed under an invalid or + /// older state of the ledger. + fn is_matching_checkpoint( + &self, + checkpoint: V::Checkpoint, + ) -> Option<Self::ValidUtxoCheckpoint>; + + /// Posts the `void_number` to the ledger, spending the asset. + /// + /// # Safety + /// + /// This method can only be called once we check that `void_number` is not already stored on + /// the ledger. See [`is_unspent`](Self::is_unspent). + fn spend( + &mut self, + utxo_checkpoint: Self::ValidUtxoCheckpoint, + void_number: Self::ValidVoidNumber, + super_key: &Self::SuperPostingKey, + ); +} + +/// Sender Post Error +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum SenderPostError { + /// Asset Spent Error + /// + /// The asset has already been spent. + AssetSpent, + + /// Invalid UTXO Checkpoint Error + /// + /// The sender was not constructed under the current state of the UTXO set. + InvalidUtxoCheckpoint, +} + +/// Sender Post +pub struct SenderPost<C, V> +where + C: Configuration, + V: Verifier<Item = Utxo<C>>, +{ + /// UTXO Checkpoint + utxo_checkpoint: V::Checkpoint, + + /// Void Number + void_number: VoidNumber<C>, +} + +impl<C, V> SenderPost<C, V> +where + C: Configuration, + V: Verifier<Item = Utxo<C>>, +{ + /// Validates `self` on the sender `ledger`. + #[inline] + pub fn validate<L>(self, ledger: &L) -> Result<SenderPostingKey<C, V, L>, SenderPostError> + where + L: SenderLedger<C, V>, + { + Ok(SenderPostingKey { + utxo_checkpoint: ledger + .is_matching_checkpoint(self.utxo_checkpoint) + .ok_or(SenderPostError::InvalidUtxoCheckpoint)?, + void_number: ledger + .is_unspent(self.void_number) + .ok_or(SenderPostError::AssetSpent)?, + }) + } +} + +impl<C, V> From<Sender<C, V>> for SenderPost<C, V> +where + C: Configuration, + V: Verifier<Item = Utxo<C>>, +{ + #[inline] + fn from(sender: Sender<C, V>) -> Self { + sender.into_post() + } +} + +/// Sender Posting Key +pub struct SenderPostingKey<C, V, L> +where + C: Configuration, + V: Verifier<Item = Utxo<C>>, + L: SenderLedger<C, V>, +{ + /// UTXO Checkpoint Posting Key + utxo_checkpoint: L::ValidUtxoCheckpoint, + + /// Void Number Posting Key + void_number: L::ValidVoidNumber, +} + +impl<C, V, L> SenderPostingKey<C, V, L> +where + C: Configuration, + V: Verifier<Item = Utxo<C>>, + L: SenderLedger<C, V>, +{ + /// Posts `self` to the sender `ledger`. + #[inline] + pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) { + ledger.spend(self.utxo_checkpoint, self.void_number, super_key); + } +} + +/// Receiver +pub struct Receiver<C> +where + C: Configuration, +{ + /// Receiving Key + receiving_key: ReceivingKey<C>, + + /// Ephemeral Secret Key + ephemeral_secret_key: SecretKey<C>, + + /// Trapdoor + trapdoor: Trapdoor<C>, + + /// Asset + asset: C::Asset, + + /// Unspent Transaction Output + utxo: Utxo<C>, +} + +impl<C> Receiver<C> +where + C: Configuration, +{ + /// Builds a new [`Receiver`] for `receiving_key` to receive `asset` with + /// `ephemeral_secret_key`. + #[inline] + pub fn new( + receiving_key: ReceivingKey<C>, + ephemeral_secret_key: SecretKey<C>, + asset: C::Asset, + commitment_scheme: &C::CommitmentScheme, + ) -> Self { + let trapdoor = receiving_key.spending_secret(&ephemeral_secret_key); + Self { + utxo: commitment_scheme.commit_one(&asset, &trapdoor), + receiving_key, + trapdoor, + ephemeral_secret_key, + asset, + } + } + + /// Extracts the ledger posting data from `self`. + #[inline] + pub fn into_post(self) -> ReceiverPost<C> + where + C::KeyScheme: HybridPublicKeyEncryptionScheme<Plaintext = C::Asset>, + { + ReceiverPost { + utxo: self.utxo, + note: self + .receiving_key + .encrypt(self.ephemeral_secret_key, self.asset), + } + } +} + +/// Receiver Ledger +pub trait ReceiverLedger<C> +where + C: Configuration, + C::KeyScheme: HybridPublicKeyEncryptionScheme<Plaintext = C::Asset>, +{ + /// Valid [`Utxo`] Posting Key + /// + /// # Safety + /// + /// This type must be some wrapper around [`Utxo`] which can only be constructed by this + /// implementation of [`ReceiverLedger`]. This is to prevent that [`register`](Self::register) + /// is called before [`is_not_registered`](Self::is_not_registered). + type ValidUtxo; + + /// Super Posting Key + /// + /// Type that allows super-traits of [`ReceiverLedger`] to customize posting key behavior. + type SuperPostingKey: Copy; + + /// Checks if the ledger already contains the `utxo` in its set of UTXOs. + /// + /// Existence of such a UTXO could indicate a possible double-spend. + fn is_not_registered(&self, utxo: Utxo<C>) -> Option<Self::ValidUtxo>; + + /// Posts the `utxo` and `encrypted_asset` to the ledger, registering the asset. + /// + /// # Safety + /// + /// This method can only be called once we check that `utxo` is not already stored on the + /// ledger. See [`is_not_registered`](Self::is_not_registered). + fn register( + &mut self, + utxo: Self::ValidUtxo, + note: EncryptedMessage<C::KeyScheme>, + super_key: &Self::SuperPostingKey, + ); +} + +/// Receiver Post Error +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum ReceiverPostError { + /// Asset Registered Error + /// + /// The asset has already been registered with the ledger. + AssetRegistered, +} + +/// Receiver Post +pub struct ReceiverPost<C> +where + C: Configuration, + C::KeyScheme: HybridPublicKeyEncryptionScheme<Plaintext = C::Asset>, +{ + /// Unspent Transaction Output + utxo: Utxo<C>, + + /// Encrypted Note + note: EncryptedMessage<C::KeyScheme>, +} + +impl<C> ReceiverPost<C> +where + C: Configuration, + C::KeyScheme: HybridPublicKeyEncryptionScheme<Plaintext = C::Asset>, +{ + /// Validates `self` on the receiver `ledger`. + #[inline] + pub fn validate<L>(self, ledger: &L) -> Result<ReceiverPostingKey<C, L>, ReceiverPostError> + where + L: ReceiverLedger<C>, + { + Ok(ReceiverPostingKey { + utxo: ledger + .is_not_registered(self.utxo) + .ok_or(ReceiverPostError::AssetRegistered)?, + note: self.note, + }) + } +} + +impl<C> From<Receiver<C>> for ReceiverPost<C> +where + C: Configuration, + C::KeyScheme: HybridPublicKeyEncryptionScheme<Plaintext = C::Asset>, +{ + #[inline] + fn from(receiver: Receiver<C>) -> ReceiverPost<C> { + receiver.into_post() + } +} + +/// Receiver Posting Key +pub struct ReceiverPostingKey<C, L> +where + C: Configuration, + C::KeyScheme: HybridPublicKeyEncryptionScheme<Plaintext = C::Asset>, + L: ReceiverLedger<C>, +{ + /// UTXO Posting Key + utxo: L::ValidUtxo, + + /// Encrypted Note + note: EncryptedMessage<C::KeyScheme>, +} + +impl<C, L> ReceiverPostingKey<C, L> +where + C: Configuration, + C::KeyScheme: HybridPublicKeyEncryptionScheme<Plaintext = C::Asset>, + L: ReceiverLedger<C>, +{ + /// Posts `self` to the receiver `ledger`. + #[inline] + pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) { + ledger.register(self.utxo, self.note, super_key); + } +} + +/// Constraint System Gadgets for Identities +pub mod constraint { + use super::*; + use crate::asset::{AssetId, AssetValue, AssetVar}; + use manta_crypto::{ + accumulator::constraint::{MembershipProofVar, VerifierVariable}, + constraint::{reflection::HasVariable, ConstraintSystem, Equal, PublicOrSecret}, + }; + + impl<C, V> Sender<C, V> + where + C: Configuration, + V: Verifier<Item = Utxo<C>> + ?Sized, + { + /// + #[inline] + pub fn get_well_formed_asset<CS>( + self, + commitment_scheme: &C::CommitmentScheme, + utxo_set_verifier: &V, + cs: &mut CS, + ) -> C::Asset + where + CS: ConstraintSystem, + Trapdoor<C>: Equal<CS>, + Utxo<C>: Equal<CS>, + VoidNumber<C>: Equal<CS>, + V: Verifier<Verification = CS::Bool>, + { + cs.assert_eq( + &self.trapdoor, + &self + .spending_key + .spending_secret(&self.ephemeral_public_key), + ); + cs.assert_eq( + &self.utxo, + &commitment_scheme.commit_one(&self.asset, &self.trapdoor), + ); + cs.assert_eq( + &self.void_number, + &commitment_scheme.commit_one(&self.spending_key.spend.secret_key, &self.trapdoor), + ); + cs.assert( + self.utxo_membership_proof + .verify(&self.utxo, utxo_set_verifier), + ); + self.asset + } + } + + impl<C> Receiver<C> + where + C: Configuration, + { + /// + #[inline] + pub fn get_well_formed_asset<CS>( + self, + commitment_scheme: &C::CommitmentScheme, + cs: &mut CS, + ) -> C::Asset + where + CS: ConstraintSystem, + Utxo<C>: Equal<CS>, + { + cs.assert_eq( + &self.utxo, + &commitment_scheme.commit_one( + &self.asset, + &self + .receiving_key + .spending_secret(&self.ephemeral_secret_key), + ), + ); + self.asset + } + } +} diff --git a/manta-accounting/src/lib.rs b/manta-accounting/src/lib.rs index d4e818ba3..59f564563 100644 --- a/manta-accounting/src/lib.rs +++ b/manta-accounting/src/lib.rs @@ -31,6 +31,7 @@ pub mod address; pub mod asset; pub mod fs; pub mod identity; +pub mod identity2; pub mod key; pub mod transfer; pub mod wallet; diff --git a/manta-crypto/src/accumulator.rs b/manta-crypto/src/accumulator.rs index 2bf6bc5a2..74a867568 100644 --- a/manta-crypto/src/accumulator.rs +++ b/manta-crypto/src/accumulator.rs @@ -48,13 +48,16 @@ pub trait Verifier { /// Secret Witness Type type Witness; + /// Verification Type + type Verification; + /// Verifies that `item` is stored in a known accumulator with `checkpoint` and `witness`. fn verify( &self, item: &Self::Item, checkpoint: &Self::Checkpoint, witness: &Self::Witness, - ) -> bool; + ) -> Self::Verification; } impl<V> Verifier for &V @@ -67,13 +70,15 @@ where type Witness = V::Witness; + type Verification = V::Verification; + #[inline] fn verify( &self, item: &Self::Item, checkpoint: &Self::Checkpoint, witness: &Self::Witness, - ) -> bool { + ) -> Self::Verification { (*self).verify(item, checkpoint, witness) } } @@ -277,7 +282,7 @@ where /// Verifies that `item` is stored in a known accumulator using `verifier`. #[inline] - pub fn verify(&self, item: &V::Item, verifier: &V) -> bool { + pub fn verify(&self, item: &V::Item, verifier: &V) -> V::Verification { verifier.verify(item, &self.checkpoint, &self.witness) } } @@ -468,6 +473,7 @@ pub mod test { pub fn assert_provable_membership<A>(accumulator: &mut A, item: &A::Item) -> A::Checkpoint where A: Accumulator, + A::Verifier: Verifier<Verification = bool>, { assert!( accumulator.insert(item), @@ -496,6 +502,7 @@ pub mod test { A: Accumulator, A::Item: 'i, A::Checkpoint: Debug + PartialEq, + A::Verifier: Verifier<Verification = bool>, I: IntoIterator<Item = &'i A::Item>, { let checkpoints = iter diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index 26a4870ff..f9e197444 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -16,6 +16,10 @@ //! Commitment Schemes +// FIXME: Change this so that commiting one value is the default, and commiting a "concatenation" +// of values is the special case. +// TODO: Change `Randomness` to `Trapdoor` + use core::{fmt::Debug, hash::Hash}; use manta_util::{Concat, ConcatAccumulator}; diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index 179d04ea0..bbd10b452 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -447,6 +447,33 @@ pub trait ConstraintSystem { } } +/// Native Constraint System +pub struct Native; + +impl ConstraintSystem for Native { + type Bool = bool; + + #[inline] + fn assert(&mut self, b: Self::Bool) { + assert!(b) + } +} + +impl Variable<Native> for bool { + type Type = bool; + + type Mode = Constant<()>; + + #[inline] + fn new(cs: &mut Native, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + let _ = cs; + match allocation { + Allocation::Known(b, _) => *b, + _ => unreachable!(), + } + } +} + /// Equality Trait pub trait Equal<C>: Variable<C> where diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index 3b0278a8c..28612a0d9 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -507,13 +507,15 @@ where type Witness = Path<C>; + type Verification = bool; + #[inline] fn verify( &self, item: &Self::Item, checkpoint: &Self::Checkpoint, witness: &Self::Witness, - ) -> bool { + ) -> Self::Verification { self.verify_path(witness, checkpoint, item) } } From 2fa8ef48f4bcfe6e0b8228cb86f4b5cf88895ff5 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sat, 13 Nov 2021 03:47:13 -0500 Subject: [PATCH 119/275] wip: build transfer protocol --- manta-accounting/src/address.rs | 325 ----------------------------- manta-accounting/src/identity2.rs | 219 ++++++++++++------- manta-accounting/src/lib.rs | 2 +- manta-accounting/src/transfer.rs | 2 +- manta-accounting/src/transfer2.rs | 88 ++++++++ manta-crypto/src/encryption/mod.rs | 1 + 6 files changed, 233 insertions(+), 404 deletions(-) delete mode 100644 manta-accounting/src/address.rs create mode 100644 manta-accounting/src/transfer2.rs diff --git a/manta-accounting/src/address.rs b/manta-accounting/src/address.rs deleted file mode 100644 index facc94d5a..000000000 --- a/manta-accounting/src/address.rs +++ /dev/null @@ -1,325 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Address Scheme - -// TODO: Implement a more general "view key" system, for arbitrarily-many view keys. - -use core::marker::PhantomData; -use manta_crypto::{ - encryption::{DecryptedMessage, EncryptedMessage, HybridPublicKeyEncryptionScheme}, - key::KeyAgreementScheme, -}; -use manta_util::{create_seal, seal}; - -create_seal! {} - -/// Key Type Marker Trait -/// -/// This trait identifies a key type for [`SecretKey`] and [`PublicKey`]. This trait is sealed and -/// can only be used with the existing implementations. -pub trait KeyType: sealed::Sealed { - /// Spending capability for this key type. - const IS_SPEND: bool; -} - -/// Implements the [`KeyType`] trait on the type with the given `$name`. -macro_rules! impl_key_type { - ($name:ty, $is_spend:expr) => { - seal!($name); - impl KeyType for $name { - const IS_SPEND: bool = $is_spend; - } - }; -} - -/// Spend Key Type -#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, PartialOrd)] -pub struct Spend; - -impl_key_type!(Spend, true); - -/// View Key Type -#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, PartialOrd)] -pub struct View; - -impl_key_type!(View, false); - -/// Secret Spend Key Type -pub type SecretSpendKey<K> = SecretKey<K, Spend>; - -/// Public Spend Key Type -pub type PublicSpendKey<K> = PublicKey<K, Spend>; - -/// Secret View Key Type -pub type SecretViewKey<K> = SecretKey<K, View>; - -/// Public View Key Type -pub type PublicViewKey<K> = PublicKey<K, View>; - -/// Secret Key -pub struct SecretKey<K, T> -where - K: KeyAgreementScheme, - T: KeyType, -{ - /// Secret Key - pub(crate) secret_key: K::SecretKey, - - /// Type Parameter Marker - __: PhantomData<T>, -} - -impl<K, T> SecretKey<K, T> -where - K: KeyAgreementScheme, - T: KeyType, -{ - /// Builds a new [`SecretKey`] from `secret_key`. - #[inline] - pub fn new(secret_key: K::SecretKey) -> Self { - Self { - secret_key, - __: PhantomData, - } - } - - /// Derives the corresponding [`PublicKey`] of the same [`KeyType`] as `self`, from a borrowed - /// value. - #[inline] - pub fn derive(&self) -> PublicKey<K, T> { - PublicKey::new(K::derive(&self.secret_key)) - } - - /// Derives the corresponding [`PublicKey`] of the same [`KeyType`] as `self`, from an owned - /// value. - #[inline] - pub fn derive_owned(self) -> PublicKey<K, T> { - PublicKey::new(K::derive_owned(self.secret_key)) - } -} - -impl<K> SecretSpendKey<K> -where - K: KeyAgreementScheme, -{ - /// Generates the spending secret associated to `self` and the `ephemeral_public_key`. - #[inline] - pub fn spending_secret(&self, ephemeral_public_key: &K::PublicKey) -> K::SharedSecret { - K::agree(&self.secret_key, ephemeral_public_key) - } -} - -impl<H> SecretViewKey<H> -where - H: HybridPublicKeyEncryptionScheme, -{ - /// Decrypts `message` using `self`. - /// - /// This method uses the [`decrypt`](EncryptedMessage::decrypt) method of [`EncryptedMessage`]. - /// See its documentation for more. - #[inline] - pub fn decrypt( - &self, - message: EncryptedMessage<H>, - ) -> Result<DecryptedMessage<H>, EncryptedMessage<H>> { - message.decrypt(&self.secret_key) - } -} - -/// Public Key -pub struct PublicKey<K, T> -where - K: KeyAgreementScheme, - T: KeyType, -{ - /// Public Key - public_key: K::PublicKey, - - /// Type Parameter Marker - __: PhantomData<T>, -} - -impl<K, T> PublicKey<K, T> -where - K: KeyAgreementScheme, - T: KeyType, -{ - /// Builds a new [`PublicKey`] from `public_key`. - /// - /// # Implementation Note - /// - /// This method is intentionally private, since these keys should only be constructed by some - /// call to the appropriate `derive` method. - #[inline] - fn new(public_key: K::PublicKey) -> Self { - Self { - public_key, - __: PhantomData, - } - } -} - -impl<K> PublicSpendKey<K> -where - K: KeyAgreementScheme, -{ - /// Generates the spending secret associated to `self` and the `ephemeral_secret_key`. - #[inline] - pub fn spending_secret(&self, ephemeral_secret_key: &K::SecretKey) -> K::SharedSecret { - K::agree(ephemeral_secret_key, &self.public_key) - } -} - -impl<H> PublicViewKey<H> -where - H: HybridPublicKeyEncryptionScheme, -{ - /// Encrypts `plaintext` using `self` and `ephemeral_secret_key`. - /// - /// This method uses the [`new`](EncryptedMessage::new) method of [`EncryptedMessage`]. - /// See its documentation for more. - #[inline] - pub fn encrypt( - &self, - ephemeral_secret_key: H::SecretKey, - plaintext: H::Plaintext, - ) -> EncryptedMessage<H> { - EncryptedMessage::new(&self.public_key, ephemeral_secret_key, plaintext) - } -} - -/// Spending Key -pub struct SpendingKey<K> -where - K: KeyAgreementScheme, -{ - /// Spend Part of the Spending Key - pub(crate) spend: SecretSpendKey<K>, - - /// View Part of the Spending Key - view: SecretViewKey<K>, -} - -impl<K> SpendingKey<K> -where - K: KeyAgreementScheme, -{ - /// Builds a new [`SpendingKey`] from `spend` and `view`. - #[inline] - pub fn new(spend: SecretSpendKey<K>, view: SecretViewKey<K>) -> Self { - Self { spend, view } - } - - /// Returns the [`SecretViewKey`] component of `self`. - #[inline] - pub fn viewing_key(&self) -> &SecretViewKey<K> { - &self.view - } - - /// Returns the [`ReceivingKey`] corresponding to `self`. - #[inline] - pub fn receiving_key(&self) -> ReceivingKey<K> { - ReceivingKey::new(self.spend.derive(), self.view.derive()) - } - - /// Converts `self` into its corresponding [`ReceivingKey`]. - #[inline] - pub fn into_receiving_key(self) -> ReceivingKey<K> { - ReceivingKey::new(self.spend.derive_owned(), self.view.derive_owned()) - } - - /// Generates the spending secret associated to `self` and the `ephemeral_public_key`. - #[inline] - pub fn spending_secret(&self, ephemeral_public_key: &K::PublicKey) -> K::SharedSecret { - self.spend.spending_secret(ephemeral_public_key) - } -} - -impl<H> SpendingKey<H> -where - H: HybridPublicKeyEncryptionScheme, -{ - /// Decrypts `message` using `self`. - /// - /// This method uses the [`decrypt`](EncryptedMessage::decrypt) method of [`EncryptedMessage`]. - /// See its documentation for more. - #[inline] - pub fn decrypt( - &self, - message: EncryptedMessage<H>, - ) -> Result<DecryptedMessage<H>, EncryptedMessage<H>> { - self.view.decrypt(message) - } -} - -/// Receiving Key -pub struct ReceivingKey<K> -where - K: KeyAgreementScheme, -{ - /// Spend Part of the Receiving Key - spend: PublicSpendKey<K>, - - /// View Part of the Receiving Key - view: PublicViewKey<K>, -} - -impl<K> ReceivingKey<K> -where - K: KeyAgreementScheme, -{ - /// Builds a new [`ReceivingKey`] from `spend` and `view`. - /// - /// # Implementation Note - /// - /// This method is intentionally private, since these keys should only be constructed by some - /// call to the appropriate `derive` method. - #[inline] - fn new(spend: PublicSpendKey<K>, view: PublicViewKey<K>) -> Self { - Self { spend, view } - } - - /// Returns the [`PublicViewKey`] component of `self`. - #[inline] - pub fn viewing_key(&self) -> &PublicViewKey<K> { - &self.view - } - - /// Generates the spending secret associated to `self` and the `ephemeral_secret_key`. - #[inline] - pub fn spending_secret(&self, ephemeral_secret_key: &K::SecretKey) -> K::SharedSecret { - self.spend.spending_secret(ephemeral_secret_key) - } -} - -impl<H> ReceivingKey<H> -where - H: HybridPublicKeyEncryptionScheme, -{ - /// Encrypts `plaintext` using `self` and `ephemeral_secret_key`. - /// - /// This method uses the [`new`](EncryptedMessage::new) method of [`EncryptedMessage`]. - /// See its documentation for more. - #[inline] - pub fn encrypt( - &self, - ephemeral_secret_key: H::SecretKey, - plaintext: H::Plaintext, - ) -> EncryptedMessage<H> { - self.view.encrypt(ephemeral_secret_key, plaintext) - } -} diff --git a/manta-accounting/src/identity2.rs b/manta-accounting/src/identity2.rs index 9d17fd8ca..03ea1f21e 100644 --- a/manta-accounting/src/identity2.rs +++ b/manta-accounting/src/identity2.rs @@ -16,7 +16,6 @@ //! Sender and Receiver Identities -use crate::address; use core::marker::PhantomData; use manta_crypto::{ accumulator::{Accumulator, MembershipProof, Verifier}, @@ -25,6 +24,111 @@ use manta_crypto::{ key::KeyAgreementScheme, }; +/// Key Table +pub trait KeyTable<K> +where + K: KeyAgreementScheme, +{ + /// Query Type + type Query; + + /// Returns the key associated to `query`, generating a new key if `query` does not already + /// correspond to an existing key. + fn get(&mut self, query: Self::Query) -> &K::SecretKey; +} + +/// Spending Key +pub struct SpendingKey<K, T> +where + K: KeyAgreementScheme, + T: KeyTable<K>, +{ + /// Spending Key + spending_key: K::SecretKey, + + /// Viewing Key Table + viewing_key_table: T, +} + +impl<K, T> SpendingKey<K, T> +where + K: KeyAgreementScheme, + T: KeyTable<K>, +{ + /// Builds a new [`SpendingKey`] from `spending_key` and `viewing_key_table`. + #[inline] + pub fn new(spending_key: K::SecretKey, viewing_key_table: T) -> Self { + Self { + spending_key, + viewing_key_table, + } + } + + /// Returns the receiving key for `self` with the viewing key located at the given `query` in + /// the viewing key table. + #[inline] + pub fn receiving_key(&mut self, query: T::Query) -> ReceivingKey<K> { + ReceivingKey { + spend: K::derive(&self.spending_key), + view: K::derive(self.viewing_key_table.get(query)), + } + } + + /// Prepares `self` for spending `asset` with the given `ephemeral_key`. + #[inline] + pub fn pre_sender<C>( + &self, + ephemeral_key: PublicKey<C>, + asset: C::Asset, + commitment_scheme: &C::CommitmentScheme, + ) -> PreSender<C> + where + K::SecretKey: Clone, + C: Configuration<KeyScheme = K>, + { + PreSender::new( + self.spending_key.clone(), + ephemeral_key, + asset, + commitment_scheme, + ) + } +} + +/// Receiving Key +pub struct ReceivingKey<K> +where + K: KeyAgreementScheme, +{ + /// Spend Part of the Receiving Key + pub spend: K::PublicKey, + + /// View Part of the Receiving Key + pub view: K::PublicKey, +} + +impl<K> ReceivingKey<K> +where + K: KeyAgreementScheme, +{ + /// Prepares `self` for receiving `asset` with the given `ephemeral_key`. + #[inline] + pub fn into_receiver<C>( + self, + ephemeral_key: SecretKey<C>, + asset: C::Asset, + commitment_scheme: &C::CommitmentScheme, + ) -> (Receiver<C>, K::PublicKey) + where + C: Configuration<KeyScheme = K>, + { + ( + Receiver::new(self.spend, ephemeral_key, asset, commitment_scheme), + self.view, + ) + } +} + /// Identity Configuration pub trait Configuration { /// Asset Type @@ -39,12 +143,6 @@ pub trait Configuration { + CommitmentInput<<Self::KeyScheme as KeyAgreementScheme>::SecretKey>; } -/// Spending Key Type -pub type SpendingKey<C> = address::SpendingKey<<C as Configuration>::KeyScheme>; - -/// Receiving Key Type -pub type ReceivingKey<C> = address::ReceivingKey<<C as Configuration>::KeyScheme>; - /// Secret Key Type pub type SecretKey<C> = <<C as Configuration>::KeyScheme as KeyAgreementScheme>::SecretKey; @@ -60,16 +158,19 @@ pub type Utxo<C> = <<C as Configuration>::CommitmentScheme as CommitmentScheme>: /// Void Number Type pub type VoidNumber<C> = <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Output; +/// Encrypted Note Type +pub type EncryptedNote<C> = EncryptedMessage<<C as Configuration>::KeyScheme>; + /// Pre-Sender pub struct PreSender<C> where C: Configuration, { - /// Spending Key - spending_key: SpendingKey<C>, + /// Secret Spending Key + spending_key: SecretKey<C>, /// Ephemeral Public Key - ephemeral_public_key: PublicKey<C>, + ephemeral_key: PublicKey<C>, /// Trapdoor trapdoor: Trapdoor<C>, @@ -89,20 +190,20 @@ where C: Configuration, { /// Builds a new [`PreSender`] for `spending_key` to spend `asset` with - /// `ephemeral_public_key`. + /// `ephemeral_key`. #[inline] pub fn new( - spending_key: SpendingKey<C>, - ephemeral_public_key: PublicKey<C>, + spending_key: SecretKey<C>, + ephemeral_key: PublicKey<C>, asset: C::Asset, commitment_scheme: &C::CommitmentScheme, ) -> Self { - let trapdoor = spending_key.spending_secret(&ephemeral_public_key); + let trapdoor = C::KeyScheme::agree(&spending_key, &ephemeral_key); Self { utxo: commitment_scheme.commit_one(&asset, &trapdoor), - void_number: commitment_scheme.commit_one(&spending_key.spend.secret_key, &trapdoor), + void_number: commitment_scheme.commit_one(&spending_key, &trapdoor), spending_key, - ephemeral_public_key, + ephemeral_key, asset, trapdoor, } @@ -145,7 +246,7 @@ where { Sender { spending_key: self.spending_key, - ephemeral_public_key: self.ephemeral_public_key, + ephemeral_key: self.ephemeral_key, trapdoor: self.trapdoor, asset: self.asset, utxo: self.utxo, @@ -218,11 +319,11 @@ where C: Configuration, V: Verifier<Item = Utxo<C>> + ?Sized, { - /// Spending Key - spending_key: SpendingKey<C>, + /// Secret Spend Key + spending_key: SecretKey<C>, /// Ephemeral Public Key - ephemeral_public_key: PublicKey<C>, + ephemeral_key: PublicKey<C>, /// Trapdoor trapdoor: Trapdoor<C>, @@ -253,7 +354,7 @@ where pub fn downgrade(self) -> PreSender<C> { PreSender { spending_key: self.spending_key, - ephemeral_public_key: self.ephemeral_public_key, + ephemeral_key: self.ephemeral_key, trapdoor: self.trapdoor, asset: self.asset, utxo: self.utxo, @@ -382,17 +483,6 @@ where } } -impl<C, V> From<Sender<C, V>> for SenderPost<C, V> -where - C: Configuration, - V: Verifier<Item = Utxo<C>>, -{ - #[inline] - fn from(sender: Sender<C, V>) -> Self { - sender.into_post() - } -} - /// Sender Posting Key pub struct SenderPostingKey<C, V, L> where @@ -425,14 +515,11 @@ pub struct Receiver<C> where C: Configuration, { - /// Receiving Key - receiving_key: ReceivingKey<C>, + /// Public Spending Key + spending_key: PublicKey<C>, /// Ephemeral Secret Key - ephemeral_secret_key: SecretKey<C>, - - /// Trapdoor - trapdoor: Trapdoor<C>, + ephemeral_key: SecretKey<C>, /// Asset asset: C::Asset, @@ -445,36 +532,33 @@ impl<C> Receiver<C> where C: Configuration, { - /// Builds a new [`Receiver`] for `receiving_key` to receive `asset` with - /// `ephemeral_secret_key`. + /// Builds a new [`Receiver`] for `spending_key` to receive `asset` with + /// `ephemeral_key`. #[inline] pub fn new( - receiving_key: ReceivingKey<C>, - ephemeral_secret_key: SecretKey<C>, + spending_key: PublicKey<C>, + ephemeral_key: SecretKey<C>, asset: C::Asset, commitment_scheme: &C::CommitmentScheme, ) -> Self { - let trapdoor = receiving_key.spending_secret(&ephemeral_secret_key); Self { - utxo: commitment_scheme.commit_one(&asset, &trapdoor), - receiving_key, - trapdoor, - ephemeral_secret_key, + utxo: commitment_scheme + .commit_one(&asset, &C::KeyScheme::agree(&ephemeral_key, &spending_key)), + spending_key, + ephemeral_key, asset, } } /// Extracts the ledger posting data from `self`. #[inline] - pub fn into_post(self) -> ReceiverPost<C> + pub fn into_post(self, public_view_key: PublicKey<C>) -> ReceiverPost<C> where C::KeyScheme: HybridPublicKeyEncryptionScheme<Plaintext = C::Asset>, { ReceiverPost { utxo: self.utxo, - note: self - .receiving_key - .encrypt(self.ephemeral_secret_key, self.asset), + note: EncryptedMessage::new(&public_view_key, self.ephemeral_key, self.asset), } } } @@ -513,7 +597,7 @@ where fn register( &mut self, utxo: Self::ValidUtxo, - note: EncryptedMessage<C::KeyScheme>, + note: EncryptedNote<C>, super_key: &Self::SuperPostingKey, ); } @@ -537,7 +621,7 @@ where utxo: Utxo<C>, /// Encrypted Note - note: EncryptedMessage<C::KeyScheme>, + note: EncryptedNote<C>, } impl<C> ReceiverPost<C> @@ -560,17 +644,6 @@ where } } -impl<C> From<Receiver<C>> for ReceiverPost<C> -where - C: Configuration, - C::KeyScheme: HybridPublicKeyEncryptionScheme<Plaintext = C::Asset>, -{ - #[inline] - fn from(receiver: Receiver<C>) -> ReceiverPost<C> { - receiver.into_post() - } -} - /// Receiver Posting Key pub struct ReceiverPostingKey<C, L> where @@ -582,7 +655,7 @@ where utxo: L::ValidUtxo, /// Encrypted Note - note: EncryptedMessage<C::KeyScheme>, + note: EncryptedNote<C>, } impl<C, L> ReceiverPostingKey<C, L> @@ -601,11 +674,7 @@ where /// Constraint System Gadgets for Identities pub mod constraint { use super::*; - use crate::asset::{AssetId, AssetValue, AssetVar}; - use manta_crypto::{ - accumulator::constraint::{MembershipProofVar, VerifierVariable}, - constraint::{reflection::HasVariable, ConstraintSystem, Equal, PublicOrSecret}, - }; + use manta_crypto::constraint::{ConstraintSystem, Equal}; impl<C, V> Sender<C, V> where @@ -629,9 +698,7 @@ pub mod constraint { { cs.assert_eq( &self.trapdoor, - &self - .spending_key - .spending_secret(&self.ephemeral_public_key), + &C::KeyScheme::agree(&self.spending_key, &self.ephemeral_key), ); cs.assert_eq( &self.utxo, @@ -639,7 +706,7 @@ pub mod constraint { ); cs.assert_eq( &self.void_number, - &commitment_scheme.commit_one(&self.spending_key.spend.secret_key, &self.trapdoor), + &commitment_scheme.commit_one(&self.spending_key, &self.trapdoor), ); cs.assert( self.utxo_membership_proof @@ -668,9 +735,7 @@ pub mod constraint { &self.utxo, &commitment_scheme.commit_one( &self.asset, - &self - .receiving_key - .spending_secret(&self.ephemeral_secret_key), + &C::KeyScheme::agree(&self.ephemeral_key, &self.spending_key), ), ); self.asset diff --git a/manta-accounting/src/lib.rs b/manta-accounting/src/lib.rs index 59f564563..ed67b8717 100644 --- a/manta-accounting/src/lib.rs +++ b/manta-accounting/src/lib.rs @@ -27,13 +27,13 @@ extern crate derive_more; #[cfg(feature = "cocoon")] extern crate cocoon as cocoon_crate; -pub mod address; pub mod asset; pub mod fs; pub mod identity; pub mod identity2; pub mod key; pub mod transfer; +pub mod transfer2; pub mod wallet; pub use asset::prelude::*; diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 551523d3c..5fc3ba472 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Transfer Protocols +//! Transfer Protocol // FIXME: Make sure that either (a) no empty transfer can be built, or (b) empty transfers work // properly i.e. do nothing. diff --git a/manta-accounting/src/transfer2.rs b/manta-accounting/src/transfer2.rs new file mode 100644 index 000000000..7f4751401 --- /dev/null +++ b/manta-accounting/src/transfer2.rs @@ -0,0 +1,88 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Transfer Protocol + +use crate::{ + asset::{Asset, AssetId, AssetValue, AssetVar}, + identity2::{self, Utxo}, +}; +use manta_crypto::{ + accumulator::Verifier, + constraint::{ + reflection::HasVariable, Constant, ConstraintSystem, ProofSystem, PublicOrSecret, Variable, + }, + encryption::HybridPublicKeyEncryptionScheme, +}; + +/// Transfer Configuration +pub trait Configuration { + /// Encryption Scheme Type + type EncryptionScheme: HybridPublicKeyEncryptionScheme; + + /// Constraint System Type + type ConstraintSystem: ConstraintSystem + + HasVariable<AssetId, Mode = PublicOrSecret> + + HasVariable<AssetValue, Mode = PublicOrSecret>; + + /// Proof System Type + type ProofSystem: ProofSystem<ConstraintSystem = Self::ConstraintSystem, Verification = bool>; + + /// UTXO Set Verifier + type UtxoSetVerifier: Verifier<Item = Utxo<Self::Config>, Verification = bool>; + + /// UTXO Set Verifier + type UtxoSetVerifierVar: Verifier< + Item = Utxo<Self::ConfigVar>, + Verification = <Self::ConstraintSystem as ConstraintSystem>::Bool, + > + Variable<Self::ConstraintSystem, Mode = Constant, Type = Self::UtxoSetVerifier>; + + /// Identity Configuration Type + type Config: identity2::Configuration<Asset = Asset, KeyScheme = Self::EncryptionScheme>; + + /// Identity Variable Configuration Type + type ConfigVar: identity2::Configuration<Asset = AssetVar<Self::ConstraintSystem>>; +} + +impl<C> identity2::Configuration for C +where + C: Configuration, +{ + type Asset = <C::Config as identity2::Configuration>::Asset; + type KeyScheme = <C::Config as identity2::Configuration>::KeyScheme; + type CommitmentScheme = <C::Config as identity2::Configuration>::CommitmentScheme; +} + +/// +pub type Sender<C> = + identity2::Sender<<C as Configuration>::Config, <C as Configuration>::UtxoSetVerifier>; + +/// +pub type SenderVar<C> = + identity2::Sender<<C as Configuration>::ConfigVar, <C as Configuration>::UtxoSetVerifierVar>; + +/// +pub type SenderPost<C> = + identity2::SenderPost<<C as Configuration>::Config, <C as Configuration>::UtxoSetVerifier>; + +/// +pub type Receiver<C> = identity2::Receiver<<C as Configuration>::Config>; + +/// +pub type ReceiverVar<C> = identity2::Receiver<<C as Configuration>::ConfigVar>; + +/// +pub type ReceiverPost<C> = identity2::ReceiverPost<<C as Configuration>::Config>; diff --git a/manta-crypto/src/encryption/mod.rs b/manta-crypto/src/encryption/mod.rs index 63f729867..d08126bc4 100644 --- a/manta-crypto/src/encryption/mod.rs +++ b/manta-crypto/src/encryption/mod.rs @@ -52,6 +52,7 @@ pub trait SymmetricKeyEncryptionScheme { } /// Hybrid Public Key Encryption Scheme +// FIXME: This should not inherit from these types: pub trait HybridPublicKeyEncryptionScheme: KeyAgreementScheme + SymmetricKeyEncryptionScheme { From f9c20be22b955e6e02f0d682924edda511de6b12 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sat, 13 Nov 2021 16:13:24 -0500 Subject: [PATCH 120/275] wip: optimize proof system abstractions for transfer --- manta-accounting/src/identity2.rs | 119 +++++++++++++++++++---------- manta-accounting/src/transfer2.rs | 17 ++++- manta-crypto/src/encryption/mod.rs | 44 +++++++---- 3 files changed, 124 insertions(+), 56 deletions(-) diff --git a/manta-accounting/src/identity2.rs b/manta-accounting/src/identity2.rs index 03ea1f21e..4e586cfe1 100644 --- a/manta-accounting/src/identity2.rs +++ b/manta-accounting/src/identity2.rs @@ -84,7 +84,7 @@ where ) -> PreSender<C> where K::SecretKey: Clone, - C: Configuration<KeyScheme = K>, + C: Configuration<KeyAgreementScheme = K>, { PreSender::new( self.spending_key.clone(), @@ -120,7 +120,7 @@ where commitment_scheme: &C::CommitmentScheme, ) -> (Receiver<C>, K::PublicKey) where - C: Configuration<KeyScheme = K>, + C: Configuration<KeyAgreementScheme = K>, { ( Receiver::new(self.spend, ephemeral_key, asset, commitment_scheme), @@ -134,23 +134,25 @@ pub trait Configuration { /// Asset Type type Asset; - /// Key Scheme Type - type KeyScheme: KeyAgreementScheme; + /// Key Agreement Scheme Type + type KeyAgreementScheme: KeyAgreementScheme; /// Commitment Scheme Type - type CommitmentScheme: CommitmentScheme<Randomness = <Self::KeyScheme as KeyAgreementScheme>::SharedSecret> - + CommitmentInput<Self::Asset> - + CommitmentInput<<Self::KeyScheme as KeyAgreementScheme>::SecretKey>; + type CommitmentScheme: CommitmentScheme< + Randomness = <Self::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret, + > + CommitmentInput<Self::Asset> + + CommitmentInput<<Self::KeyAgreementScheme as KeyAgreementScheme>::SecretKey>; } /// Secret Key Type -pub type SecretKey<C> = <<C as Configuration>::KeyScheme as KeyAgreementScheme>::SecretKey; +pub type SecretKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::SecretKey; /// Public Key Type -pub type PublicKey<C> = <<C as Configuration>::KeyScheme as KeyAgreementScheme>::PublicKey; +pub type PublicKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::PublicKey; /// Trapdoor Type -pub type Trapdoor<C> = <<C as Configuration>::KeyScheme as KeyAgreementScheme>::SharedSecret; +pub type Trapdoor<C> = + <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret; /// UTXO Type pub type Utxo<C> = <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Output; @@ -158,9 +160,6 @@ pub type Utxo<C> = <<C as Configuration>::CommitmentScheme as CommitmentScheme>: /// Void Number Type pub type VoidNumber<C> = <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Output; -/// Encrypted Note Type -pub type EncryptedNote<C> = EncryptedMessage<<C as Configuration>::KeyScheme>; - /// Pre-Sender pub struct PreSender<C> where @@ -198,7 +197,7 @@ where asset: C::Asset, commitment_scheme: &C::CommitmentScheme, ) -> Self { - let trapdoor = C::KeyScheme::agree(&spending_key, &ephemeral_key); + let trapdoor = C::KeyAgreementScheme::agree(&spending_key, &ephemeral_key); Self { utxo: commitment_scheme.commit_one(&asset, &trapdoor), void_number: commitment_scheme.commit_one(&spending_key, &trapdoor), @@ -542,8 +541,10 @@ where commitment_scheme: &C::CommitmentScheme, ) -> Self { Self { - utxo: commitment_scheme - .commit_one(&asset, &C::KeyScheme::agree(&ephemeral_key, &spending_key)), + utxo: commitment_scheme.commit_one( + &asset, + &C::KeyAgreementScheme::agree(&ephemeral_key, &spending_key), + ), spending_key, ephemeral_key, asset, @@ -552,9 +553,12 @@ where /// Extracts the ledger posting data from `self`. #[inline] - pub fn into_post(self, public_view_key: PublicKey<C>) -> ReceiverPost<C> + pub fn into_post<H>(self, public_view_key: PublicKey<C>) -> ReceiverPost<C, H> where - C::KeyScheme: HybridPublicKeyEncryptionScheme<Plaintext = C::Asset>, + H: HybridPublicKeyEncryptionScheme< + Plaintext = C::Asset, + KeyAgreementScheme = C::KeyAgreementScheme, + >, { ReceiverPost { utxo: self.utxo, @@ -564,10 +568,13 @@ where } /// Receiver Ledger -pub trait ReceiverLedger<C> +pub trait ReceiverLedger<C, H> where C: Configuration, - C::KeyScheme: HybridPublicKeyEncryptionScheme<Plaintext = C::Asset>, + H: HybridPublicKeyEncryptionScheme< + Plaintext = C::Asset, + KeyAgreementScheme = C::KeyAgreementScheme, + >, { /// Valid [`Utxo`] Posting Key /// @@ -597,7 +604,7 @@ where fn register( &mut self, utxo: Self::ValidUtxo, - note: EncryptedNote<C>, + note: EncryptedMessage<H>, super_key: &Self::SuperPostingKey, ); } @@ -612,28 +619,34 @@ pub enum ReceiverPostError { } /// Receiver Post -pub struct ReceiverPost<C> +pub struct ReceiverPost<C, H> where C: Configuration, - C::KeyScheme: HybridPublicKeyEncryptionScheme<Plaintext = C::Asset>, + H: HybridPublicKeyEncryptionScheme< + Plaintext = C::Asset, + KeyAgreementScheme = C::KeyAgreementScheme, + >, { /// Unspent Transaction Output utxo: Utxo<C>, /// Encrypted Note - note: EncryptedNote<C>, + note: EncryptedMessage<H>, } -impl<C> ReceiverPost<C> +impl<C, H> ReceiverPost<C, H> where C: Configuration, - C::KeyScheme: HybridPublicKeyEncryptionScheme<Plaintext = C::Asset>, + H: HybridPublicKeyEncryptionScheme< + Plaintext = C::Asset, + KeyAgreementScheme = C::KeyAgreementScheme, + >, { /// Validates `self` on the receiver `ledger`. #[inline] - pub fn validate<L>(self, ledger: &L) -> Result<ReceiverPostingKey<C, L>, ReceiverPostError> + pub fn validate<L>(self, ledger: &L) -> Result<ReceiverPostingKey<C, H, L>, ReceiverPostError> where - L: ReceiverLedger<C>, + L: ReceiverLedger<C, H>, { Ok(ReceiverPostingKey { utxo: ledger @@ -645,24 +658,30 @@ where } /// Receiver Posting Key -pub struct ReceiverPostingKey<C, L> +pub struct ReceiverPostingKey<C, H, L> where C: Configuration, - C::KeyScheme: HybridPublicKeyEncryptionScheme<Plaintext = C::Asset>, - L: ReceiverLedger<C>, + H: HybridPublicKeyEncryptionScheme< + Plaintext = C::Asset, + KeyAgreementScheme = C::KeyAgreementScheme, + >, + L: ReceiverLedger<C, H>, { /// UTXO Posting Key utxo: L::ValidUtxo, /// Encrypted Note - note: EncryptedNote<C>, + note: EncryptedMessage<H>, } -impl<C, L> ReceiverPostingKey<C, L> +impl<C, H, L> ReceiverPostingKey<C, H, L> where C: Configuration, - C::KeyScheme: HybridPublicKeyEncryptionScheme<Plaintext = C::Asset>, - L: ReceiverLedger<C>, + H: HybridPublicKeyEncryptionScheme< + Plaintext = C::Asset, + KeyAgreementScheme = C::KeyAgreementScheme, + >, + L: ReceiverLedger<C, H>, { /// Posts `self` to the receiver `ledger`. #[inline] @@ -674,11 +693,31 @@ where /// Constraint System Gadgets for Identities pub mod constraint { use super::*; - use manta_crypto::constraint::{ConstraintSystem, Equal}; + use manta_crypto::constraint::{ + reflection::{HasVariable, Var}, + Constant, ConstraintSystem, Equal, PublicOrSecret, + }; + + /// Constraint System Configuration + pub trait Configuration<C>: + super::Configuration< + Asset = Var<C::Asset, Self::ConstraintSystem>, + KeyAgreementScheme = Var<C::KeyAgreementScheme, Self::ConstraintSystem>, + CommitmentScheme = Var<C::CommitmentScheme, Self::ConstraintSystem>, + > + where + C: super::Configuration, + { + /// Constraint System Type + type ConstraintSystem: ConstraintSystem + + HasVariable<C::Asset, Mode = PublicOrSecret> + + HasVariable<C::KeyAgreementScheme, Mode = Constant> + + HasVariable<C::CommitmentScheme, Mode = Constant>; + } impl<C, V> Sender<C, V> where - C: Configuration, + C: super::Configuration, V: Verifier<Item = Utxo<C>> + ?Sized, { /// @@ -698,7 +737,7 @@ pub mod constraint { { cs.assert_eq( &self.trapdoor, - &C::KeyScheme::agree(&self.spending_key, &self.ephemeral_key), + &C::KeyAgreementScheme::agree(&self.spending_key, &self.ephemeral_key), ); cs.assert_eq( &self.utxo, @@ -718,7 +757,7 @@ pub mod constraint { impl<C> Receiver<C> where - C: Configuration, + C: super::Configuration, { /// #[inline] @@ -735,7 +774,7 @@ pub mod constraint { &self.utxo, &commitment_scheme.commit_one( &self.asset, - &C::KeyScheme::agree(&self.ephemeral_key, &self.spending_key), + &C::KeyAgreementScheme::agree(&self.ephemeral_key, &self.spending_key), ), ); self.asset diff --git a/manta-accounting/src/transfer2.rs b/manta-accounting/src/transfer2.rs index 7f4751401..9a51cd28b 100644 --- a/manta-accounting/src/transfer2.rs +++ b/manta-accounting/src/transfer2.rs @@ -23,11 +23,25 @@ use crate::{ use manta_crypto::{ accumulator::Verifier, constraint::{ - reflection::HasVariable, Constant, ConstraintSystem, ProofSystem, PublicOrSecret, Variable, + reflection::{HasVariable, Var}, + Constant, ConstraintSystem, ProofSystem, PublicOrSecret, Variable, }, encryption::HybridPublicKeyEncryptionScheme, }; +/// Transfer Configuration +pub trait Configuration: identity2::Configuration<Asset = Asset> { + /// Encryption Scheme + type EncryptionScheme: HybridPublicKeyEncryptionScheme< + Plaintext = Self::Asset, + KeyAgreementScheme = Self::KeyAgreementScheme, + >; + + /// UTXO Set Verifier + type UtxoSetVerifier: Verifier<Item = Utxo<Self>, Verification = bool>; +} + +/* /// Transfer Configuration pub trait Configuration { /// Encryption Scheme Type @@ -86,3 +100,4 @@ pub type ReceiverVar<C> = identity2::Receiver<<C as Configuration>::ConfigVar>; /// pub type ReceiverPost<C> = identity2::ReceiverPost<<C as Configuration>::Config>; +*/ diff --git a/manta-crypto/src/encryption/mod.rs b/manta-crypto/src/encryption/mod.rs index d08126bc4..9eec2eab5 100644 --- a/manta-crypto/src/encryption/mod.rs +++ b/manta-crypto/src/encryption/mod.rs @@ -52,17 +52,25 @@ pub trait SymmetricKeyEncryptionScheme { } /// Hybrid Public Key Encryption Scheme -// FIXME: This should not inherit from these types: -pub trait HybridPublicKeyEncryptionScheme: - KeyAgreementScheme + SymmetricKeyEncryptionScheme -{ - /// Key-Derivation Function Type +pub trait HybridPublicKeyEncryptionScheme: SymmetricKeyEncryptionScheme { + /// Key Agreement Scheme Type + type KeyAgreementScheme: KeyAgreementScheme; + + /// Key Derivation Function Type type KeyDerivationFunction: KeyDerivationFunction< - KeyAgreementScheme = Self, + KeyAgreementScheme = Self::KeyAgreementScheme, Key = <Self as SymmetricKeyEncryptionScheme>::Key, >; } +/// Secret Key Type +pub type SecretKey<H> = + <<H as HybridPublicKeyEncryptionScheme>::KeyAgreementScheme as KeyAgreementScheme>::SecretKey; + +/// Public Key Type +pub type PublicKey<H> = + <<H as HybridPublicKeyEncryptionScheme>::KeyAgreementScheme as KeyAgreementScheme>::PublicKey; + /// Encrypted Message pub struct EncryptedMessage<H> where @@ -72,7 +80,7 @@ where ciphertext: H::Ciphertext, /// Ephemeral Public Key - ephemeral_public_key: H::PublicKey, + ephemeral_public_key: PublicKey<H>, } impl<H> EncryptedMessage<H> @@ -83,25 +91,31 @@ where /// and an `ephemeral_secret_key`. #[inline] pub fn new( - public_key: &H::PublicKey, - ephemeral_secret_key: H::SecretKey, + public_key: &PublicKey<H>, + ephemeral_secret_key: SecretKey<H>, plaintext: H::Plaintext, ) -> Self { Self { ciphertext: H::encrypt( - H::KeyDerivationFunction::derive(H::agree(&ephemeral_secret_key, public_key)), + H::KeyDerivationFunction::derive(H::KeyAgreementScheme::agree( + &ephemeral_secret_key, + public_key, + )), plaintext, ), - ephemeral_public_key: H::derive_owned(ephemeral_secret_key), + ephemeral_public_key: H::KeyAgreementScheme::derive_owned(ephemeral_secret_key), } } /// Tries to decrypt `self` using `secret_key`, returning back `Err(self)` if the `secret_key` /// was unable to decrypt the message. #[inline] - pub fn decrypt(self, secret_key: &H::SecretKey) -> Result<DecryptedMessage<H>, Self> { + pub fn decrypt(self, secret_key: &SecretKey<H>) -> Result<DecryptedMessage<H>, Self> { match H::decrypt( - H::KeyDerivationFunction::derive(H::agree(secret_key, &self.ephemeral_public_key)), + H::KeyDerivationFunction::derive(H::KeyAgreementScheme::agree( + secret_key, + &self.ephemeral_public_key, + )), &self.ciphertext, ) { Some(plaintext) => Ok(DecryptedMessage::new(plaintext, self.ephemeral_public_key)), @@ -119,7 +133,7 @@ where pub plaintext: H::Plaintext, /// Ephemeral Public Key - pub ephemeral_public_key: H::PublicKey, + pub ephemeral_public_key: PublicKey<H>, } impl<H> DecryptedMessage<H> @@ -128,7 +142,7 @@ where { /// Builds a new [`DecryptedMessage`] from `plaintext` and `ephemeral_public_key`. #[inline] - pub fn new(plaintext: H::Plaintext, ephemeral_public_key: H::PublicKey) -> Self { + pub fn new(plaintext: H::Plaintext, ephemeral_public_key: PublicKey<H>) -> Self { Self { plaintext, ephemeral_public_key, From cfa5afd23e39174cd9656c58a400cd22ff46856c Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 14 Nov 2021 13:21:05 -0500 Subject: [PATCH 121/275] wip: start rewrite of proof system abstraction --- manta-accounting/src/identity2.rs | 159 +++++---- manta-accounting/src/transfer2.rs | 565 +++++++++++++++++++++++++++--- manta-crypto/src/constraint.rs | 26 +- 3 files changed, 630 insertions(+), 120 deletions(-) diff --git a/manta-accounting/src/identity2.rs b/manta-accounting/src/identity2.rs index 4e586cfe1..7fe72ed07 100644 --- a/manta-accounting/src/identity2.rs +++ b/manta-accounting/src/identity2.rs @@ -118,13 +118,16 @@ where ephemeral_key: SecretKey<C>, asset: C::Asset, commitment_scheme: &C::CommitmentScheme, - ) -> (Receiver<C>, K::PublicKey) + ) -> FullReceiver<C> where C: Configuration<KeyAgreementScheme = K>, { - ( - Receiver::new(self.spend, ephemeral_key, asset, commitment_scheme), + FullReceiver::new( + self.spend, self.view, + ephemeral_key, + asset, + commitment_scheme, ) } } @@ -138,10 +141,9 @@ pub trait Configuration { type KeyAgreementScheme: KeyAgreementScheme; /// Commitment Scheme Type - type CommitmentScheme: CommitmentScheme< - Randomness = <Self::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret, - > + CommitmentInput<Self::Asset> - + CommitmentInput<<Self::KeyAgreementScheme as KeyAgreementScheme>::SecretKey>; + type CommitmentScheme: CommitmentScheme<Randomness = Trapdoor<Self>> + + CommitmentInput<Self::Asset> + + CommitmentInput<SecretKey<Self>>; } /// Secret Key Type @@ -154,11 +156,15 @@ pub type PublicKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreemen pub type Trapdoor<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret; -/// UTXO Type -pub type Utxo<C> = <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Output; +/// Commitment Scheme Output Type +pub type CommitmentSchemeOutput<C> = + <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Output; + +/// Unspent Transaction Output Type +pub type Utxo<C> = CommitmentSchemeOutput<C>; /// Void Number Type -pub type VoidNumber<C> = <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Output; +pub type VoidNumber<C> = CommitmentSchemeOutput<C>; /// Pre-Sender pub struct PreSender<C> @@ -171,9 +177,6 @@ where /// Ephemeral Public Key ephemeral_key: PublicKey<C>, - /// Trapdoor - trapdoor: Trapdoor<C>, - /// Asset asset: C::Asset, @@ -204,7 +207,6 @@ where spending_key, ephemeral_key, asset, - trapdoor, } } @@ -246,9 +248,7 @@ where Sender { spending_key: self.spending_key, ephemeral_key: self.ephemeral_key, - trapdoor: self.trapdoor, asset: self.asset, - utxo: self.utxo, utxo_membership_proof: proof.utxo_membership_proof, void_number: self.void_number, } @@ -324,15 +324,9 @@ where /// Ephemeral Public Key ephemeral_key: PublicKey<C>, - /// Trapdoor - trapdoor: Trapdoor<C>, - /// Asset asset: C::Asset, - /// Unspent Transaction Output - utxo: Utxo<C>, - /// UTXO Membership Proof utxo_membership_proof: MembershipProof<V>, @@ -350,13 +344,15 @@ where /// This method should be called if the [`Utxo`] membership proof attached to `self` was deemed /// invalid or had expired. #[inline] - pub fn downgrade(self) -> PreSender<C> { + pub fn downgrade(self, commitment_scheme: &C::CommitmentScheme) -> PreSender<C> { PreSender { + utxo: commitment_scheme.commit_one( + &self.asset, + &C::KeyAgreementScheme::agree(&self.spending_key, &self.ephemeral_key), + ), spending_key: self.spending_key, ephemeral_key: self.ephemeral_key, - trapdoor: self.trapdoor, asset: self.asset, - utxo: self.utxo, void_number: self.void_number, } } @@ -528,6 +524,46 @@ where } impl<C> Receiver<C> +where + C: Configuration, +{ + /// Generates the [`Utxo`] for a [`Receiver`] with the given parameters. + #[inline] + fn generate_utxo( + spending_key: &PublicKey<C>, + ephemeral_key: &SecretKey<C>, + asset: &C::Asset, + commitment_scheme: &C::CommitmentScheme, + ) -> Utxo<C> { + commitment_scheme.commit_one( + asset, + &C::KeyAgreementScheme::agree(ephemeral_key, spending_key), + ) + } +} + +/// Full Receiver +pub struct FullReceiver<C> +where + C: Configuration, +{ + /// Public Spending Key + spending_key: PublicKey<C>, + + /// Public Viewing Key + viewing_key: PublicKey<C>, + + /// Ephemeral Secret Key + ephemeral_key: SecretKey<C>, + + /// Asset + asset: C::Asset, + + /// Unspent Transaction Output + utxo: Utxo<C>, +} + +impl<C> FullReceiver<C> where C: Configuration, { @@ -536,16 +572,20 @@ where #[inline] pub fn new( spending_key: PublicKey<C>, + viewing_key: PublicKey<C>, ephemeral_key: SecretKey<C>, asset: C::Asset, commitment_scheme: &C::CommitmentScheme, ) -> Self { Self { - utxo: commitment_scheme.commit_one( + utxo: Receiver::<C>::generate_utxo( + &spending_key, + &ephemeral_key, &asset, - &C::KeyAgreementScheme::agree(&ephemeral_key, &spending_key), + commitment_scheme, ), spending_key, + viewing_key, ephemeral_key, asset, } @@ -553,7 +593,7 @@ where /// Extracts the ledger posting data from `self`. #[inline] - pub fn into_post<H>(self, public_view_key: PublicKey<C>) -> ReceiverPost<C, H> + pub fn into_post<H>(self) -> ReceiverPost<C, H> where H: HybridPublicKeyEncryptionScheme< Plaintext = C::Asset, @@ -562,7 +602,7 @@ where { ReceiverPost { utxo: self.utxo, - note: EncryptedMessage::new(&public_view_key, self.ephemeral_key, self.asset), + note: EncryptedMessage::new(&self.viewing_key, self.ephemeral_key, self.asset), } } } @@ -693,34 +733,15 @@ where /// Constraint System Gadgets for Identities pub mod constraint { use super::*; - use manta_crypto::constraint::{ - reflection::{HasVariable, Var}, - Constant, ConstraintSystem, Equal, PublicOrSecret, - }; - - /// Constraint System Configuration - pub trait Configuration<C>: - super::Configuration< - Asset = Var<C::Asset, Self::ConstraintSystem>, - KeyAgreementScheme = Var<C::KeyAgreementScheme, Self::ConstraintSystem>, - CommitmentScheme = Var<C::CommitmentScheme, Self::ConstraintSystem>, - > - where - C: super::Configuration, - { - /// Constraint System Type - type ConstraintSystem: ConstraintSystem - + HasVariable<C::Asset, Mode = PublicOrSecret> - + HasVariable<C::KeyAgreementScheme, Mode = Constant> - + HasVariable<C::CommitmentScheme, Mode = Constant>; - } + use manta_crypto::constraint::{ConstraintSystem, Equal}; impl<C, V> Sender<C, V> where - C: super::Configuration, + C: Configuration, V: Verifier<Item = Utxo<C>> + ?Sized, { - /// + /// Returns the asset for `self`, checking if `self` is well-formed in the given constraint + /// system `cs`. #[inline] pub fn get_well_formed_asset<CS>( self, @@ -730,26 +751,17 @@ pub mod constraint { ) -> C::Asset where CS: ConstraintSystem, - Trapdoor<C>: Equal<CS>, - Utxo<C>: Equal<CS>, - VoidNumber<C>: Equal<CS>, + CommitmentSchemeOutput<C>: Equal<CS>, V: Verifier<Verification = CS::Bool>, { - cs.assert_eq( - &self.trapdoor, - &C::KeyAgreementScheme::agree(&self.spending_key, &self.ephemeral_key), - ); - cs.assert_eq( - &self.utxo, - &commitment_scheme.commit_one(&self.asset, &self.trapdoor), - ); + let trapdoor = C::KeyAgreementScheme::agree(&self.spending_key, &self.ephemeral_key); + cs.assert(self.utxo_membership_proof.verify( + &commitment_scheme.commit_one(&self.asset, &trapdoor), + utxo_set_verifier, + )); cs.assert_eq( &self.void_number, - &commitment_scheme.commit_one(&self.spending_key, &self.trapdoor), - ); - cs.assert( - self.utxo_membership_proof - .verify(&self.utxo, utxo_set_verifier), + &commitment_scheme.commit_one(&self.spending_key, &trapdoor), ); self.asset } @@ -757,9 +769,10 @@ pub mod constraint { impl<C> Receiver<C> where - C: super::Configuration, + C: Configuration, { - /// + /// Returns the asset for `self`, checking if `self` is well-formed in the given constraint + /// system `cs`. #[inline] pub fn get_well_formed_asset<CS>( self, @@ -772,9 +785,11 @@ pub mod constraint { { cs.assert_eq( &self.utxo, - &commitment_scheme.commit_one( + &Self::generate_utxo( + &self.spending_key, + &self.ephemeral_key, &self.asset, - &C::KeyAgreementScheme::agree(&self.ephemeral_key, &self.spending_key), + commitment_scheme, ), ); self.asset diff --git a/manta-accounting/src/transfer2.rs b/manta-accounting/src/transfer2.rs index 9a51cd28b..d0370fb50 100644 --- a/manta-accounting/src/transfer2.rs +++ b/manta-accounting/src/transfer2.rs @@ -18,86 +18,565 @@ use crate::{ asset::{Asset, AssetId, AssetValue, AssetVar}, - identity2::{self, Utxo}, + identity2::{self, CommitmentSchemeOutput, PublicKey, Utxo}, }; +use alloc::vec::Vec; +use core::ops::Add; use manta_crypto::{ accumulator::Verifier, + commitment::CommitmentScheme, constraint::{ - reflection::{HasVariable, Var}, - Constant, ConstraintSystem, ProofSystem, PublicOrSecret, Variable, + reflection::{HasEqual, HasVariable, Var}, + Allocation, Constant, ConstraintSystem, Derived, Equal, ProofSystem, Public, + PublicOrSecret, Variable, VariableSource, }, encryption::HybridPublicKeyEncryptionScheme, + key::KeyAgreementScheme, + rand::{CryptoRng, RngCore}, }; +/// Returns `true` if the transfer with this shape would have no public participants. +#[inline] +pub const fn has_no_public_participants( + sources: usize, + senders: usize, + receivers: usize, + sinks: usize, +) -> bool { + let _ = (senders, receivers); + sources == 0 && sinks == 0 +} + /// Transfer Configuration pub trait Configuration: identity2::Configuration<Asset = Asset> { - /// Encryption Scheme + /// Encryption Scheme Type type EncryptionScheme: HybridPublicKeyEncryptionScheme< Plaintext = Self::Asset, KeyAgreementScheme = Self::KeyAgreementScheme, >; - /// UTXO Set Verifier + /// UTXO Set Verifier Type type UtxoSetVerifier: Verifier<Item = Utxo<Self>, Verification = bool>; } -/* -/// Transfer Configuration -pub trait Configuration { - /// Encryption Scheme Type - type EncryptionScheme: HybridPublicKeyEncryptionScheme; - +/// Transfer Proof System Configuration +pub trait ProofSystemConfiguration<C>: + identity2::Configuration<Asset = AssetVar<Self::ConstraintSystem>> +where + C: Configuration, +{ /// Constraint System Type type ConstraintSystem: ConstraintSystem - + HasVariable<AssetId, Mode = PublicOrSecret> - + HasVariable<AssetValue, Mode = PublicOrSecret>; + + HasVariable< + C::KeyAgreementScheme, + Variable = <Self as identity2::Configuration>::KeyAgreementScheme, + Mode = Constant, + > + HasVariable< + C::CommitmentScheme, + Variable = <Self as identity2::Configuration>::CommitmentScheme, + Mode = Constant, + > + HasVariable<AssetId, Variable = Self::AssetId, Mode = PublicOrSecret> + + HasVariable<AssetValue, Variable = Self::AssetValue, Mode = PublicOrSecret> + + HasVariable< + CommitmentSchemeOutput<C>, + Variable = CommitmentSchemeOutput<Self>, + Mode = PublicOrSecret, + > + HasEqual<CommitmentSchemeOutput<Self>>; /// Proof System Type type ProofSystem: ProofSystem<ConstraintSystem = Self::ConstraintSystem, Verification = bool>; - /// UTXO Set Verifier - type UtxoSetVerifier: Verifier<Item = Utxo<Self::Config>, Verification = bool>; + /// Asset Id Variable Type + type AssetId: Variable<Self::ConstraintSystem, Type = AssetId, Mode = PublicOrSecret> + + Equal<Self::ConstraintSystem>; + + /// Asset Value Variable Type + type AssetValue: Variable<Self::ConstraintSystem, Type = AssetValue, Mode = PublicOrSecret> + + Equal<Self::ConstraintSystem> + + Add<Output = Self::AssetValue>; - /// UTXO Set Verifier - type UtxoSetVerifierVar: Verifier< - Item = Utxo<Self::ConfigVar>, + /// UTXO Set Verifier Variable Type + type UtxoSetVerifier: Verifier< + Item = Utxo<Self>, Verification = <Self::ConstraintSystem as ConstraintSystem>::Bool, - > + Variable<Self::ConstraintSystem, Mode = Constant, Type = Self::UtxoSetVerifier>; + > + Variable<Self::ConstraintSystem, Type = C::UtxoSetVerifier, Mode = Constant>; +} + +/// Pre-Sender Type +pub type PreSender<C> = identity2::PreSender<C>; + +/// Sender Type +pub type Sender<C> = identity2::Sender<C, <C as Configuration>::UtxoSetVerifier>; + +/// Sender Post Type +pub type SenderPost<C> = identity2::SenderPost<C, <C as Configuration>::UtxoSetVerifier>; + +/// Receiver Type +pub type Receiver<C> = identity2::Receiver<C>; + +/// Full Receiver Type +pub type FullReceiver<C> = identity2::FullReceiver<C>; + +/// Receiver Post Type +pub type ReceiverPost<C> = identity2::ReceiverPost<C, <C as Configuration>::EncryptionScheme>; + +/// Transfer Proof System Type +type ProofSystemType<C, P> = <P as ProofSystemConfiguration<C>>::ProofSystem; + +/// Transfer Proof System Error Type +pub type ProofSystemError<C, P> = <ProofSystemType<C, P> as ProofSystem>::Error; + +/// Transfer Proving Context Type +pub type ProvingContext<C, P> = <ProofSystemType<C, P> as ProofSystem>::ProvingContext; + +/// Transfer Verifying Context Type +pub type VerifyingContext<C, P> = <ProofSystemType<C, P> as ProofSystem>::VerifyingContext; + +/// Transfer Validity Proof Type +pub type Proof<C, P> = <ProofSystemType<C, P> as ProofSystem>::Proof; + +/// Transfer +pub struct Transfer< + C, + const SOURCES: usize, + const SENDERS: usize, + const RECEIVERS: usize, + const SINKS: usize, +> where + C: Configuration, +{ + /// Asset Id + asset_id: Option<AssetId>, + + /// Sources + sources: [AssetValue; SOURCES], + + /// Senders + senders: [Sender<C>; SENDERS], + + /// Receivers + receivers: [FullReceiver<C>; RECEIVERS], + + /// Sinks + sinks: [AssetValue; SINKS], +} + +impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> + Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS> +where + C: Configuration, +{ + /// Builds a new [`Transfer`] from public and secret information. + #[inline] + fn new( + asset_id: Option<AssetId>, + sources: [AssetValue; SOURCES], + senders: [Sender<C>; SENDERS], + receivers: [FullReceiver<C>; RECEIVERS], + sinks: [AssetValue; SINKS], + ) -> Self { + Self::check_shape(asset_id.is_some()); + Self::new_unchecked(asset_id, sources, senders, receivers, sinks) + } + + /// Checks that the [`Transfer`] has a valid shape. + #[inline] + fn check_shape(has_visible_asset_id: bool) { + Self::has_nonempty_input_shape(); + Self::has_nonempty_output_shape(); + Self::has_visible_asset_id_when_required(has_visible_asset_id); + } + + /// Checks that the input side of the transfer is not empty. + #[inline] + fn has_nonempty_input_shape() { + assert_ne!( + SOURCES + SENDERS, + 0, + "Not enough participants on the input side." + ); + } + + /// Checks that the output side of the transfer is not empty. + #[inline] + fn has_nonempty_output_shape() { + assert_ne!( + RECEIVERS + SINKS, + 0, + "Not enough participants on the output side." + ); + } + + /// Checks that the given `asset_id` for [`Transfer`] building is visible exactly when required. + #[inline] + fn has_visible_asset_id_when_required(has_visible_asset_id: bool) { + if SOURCES > 0 || SINKS > 0 { + assert!( + has_visible_asset_id, + "Missing public asset id when required." + ); + } else { + assert!( + !has_visible_asset_id, + "Given public asset id when not required." + ); + } + } + + /// Builds a new [`Transfer`] without checking the number of participants on the input and + /// output sides. + #[inline] + fn new_unchecked( + asset_id: Option<AssetId>, + sources: [AssetValue; SOURCES], + senders: [Sender<C>; SENDERS], + receivers: [FullReceiver<C>; RECEIVERS], + sinks: [AssetValue; SINKS], + ) -> Self { + Self { + asset_id, + sources, + senders, + receivers, + sinks, + } + } + + /// Generates the unknown variables for the transfer validity proof. + #[inline] + fn unknown_variables<P>( + commitment_scheme: &C::CommitmentScheme, + utxo_set_verifier: &C::UtxoSetVerifier, + cs: &mut P::ConstraintSystem, + ) -> ( + Option<P::AssetId>, + TransferParticipantsVar<C, P, SOURCES, SENDERS, RECEIVERS, SINKS>, + P::CommitmentScheme, + P::UtxoSetVerifier, + ) + where + P: ProofSystemConfiguration<C>, + { + let base_asset_id = if has_no_public_participants(SOURCES, SENDERS, RECEIVERS, SINKS) { + None + } else { + Some(P::AssetId::new_unknown(cs, Public)) + }; + /* + ( + base_asset_id, + TransferParticipantsVar::new_unknown(cs, Derived), + commitment_scheme.as_known(cs, Public), + utxo_set_verifier.as_known(cs, Public), + ) + */ + todo!() + } + + /// Generates the known variables for the transfer validity proof. + #[inline] + fn known_variables<P>( + &self, + commitment_scheme: &C::CommitmentScheme, + utxo_set_verifier: &C::UtxoSetVerifier, + cs: &mut P::ConstraintSystem, + ) -> ( + Option<P::AssetId>, + TransferParticipantsVar<C, P, SOURCES, SENDERS, RECEIVERS, SINKS>, + P::CommitmentScheme, + P::UtxoSetVerifier, + ) + where + P: ProofSystemConfiguration<C>, + { + /* TODO: + ( + self.public.asset_id.map(|id| id.as_known(cs, Public)), + TransferParticipantsVar::new_known(cs, self, Derived), + commitment_scheme.as_known(cs, Public), + utxo_set_verifier.as_known(cs, Public), + ) + */ + todo!() + } + + /// Builds constraints for the transfer validity proof. + #[inline] + fn build_constraints<P>( + base_asset_id: Option<P::AssetId>, + participants: TransferParticipantsVar<C, P, SOURCES, SENDERS, RECEIVERS, SINKS>, + commitment_scheme: P::CommitmentScheme, + utxo_set_verifier: P::UtxoSetVerifier, + cs: &mut P::ConstraintSystem, + ) where + P: ProofSystemConfiguration<C>, + { + let mut secret_asset_ids = Vec::with_capacity(SENDERS + RECEIVERS); + + let input_sum = participants + .senders + .into_iter() + .map(|s| { + let asset = s.get_well_formed_asset(&commitment_scheme, &utxo_set_verifier, cs); + secret_asset_ids.push(asset.id); + asset.value + }) + .chain(participants.sources) + .reduce(Add::add) + .unwrap(); + + let output_sum = participants + .receivers + .into_iter() + .map(|r| { + let asset = r.get_well_formed_asset(&commitment_scheme, cs); + secret_asset_ids.push(asset.id); + asset.value + }) + .chain(participants.sinks) + .reduce(Add::add) + .unwrap(); + + cs.assert_eq(&input_sum, &output_sum); + + match base_asset_id { + Some(asset_id) => cs.assert_all_eq_to_base(&asset_id, secret_asset_ids.iter()), + _ => cs.assert_all_eq(secret_asset_ids.iter()), + } + } + + /// Generates the constraint system for an unknown transfer. + #[inline] + pub fn unknown_constraints<P>( + commitment_scheme: &C::CommitmentScheme, + utxo_set_verifier: &C::UtxoSetVerifier, + ) -> P::ConstraintSystem + where + P: ProofSystemConfiguration<C>, + { + let mut cs = P::ProofSystem::for_unknown(); + let (base_asset_id, participants, commitment_scheme, utxo_set_verifier) = + Self::unknown_variables::<P>(commitment_scheme, utxo_set_verifier, &mut cs); + Self::build_constraints( + base_asset_id, + participants, + commitment_scheme, + utxo_set_verifier, + &mut cs, + ); + cs + } - /// Identity Configuration Type - type Config: identity2::Configuration<Asset = Asset, KeyScheme = Self::EncryptionScheme>; + /// Generates the constraint system for a known transfer. + #[inline] + pub fn known_constraints<P>( + &self, + commitment_scheme: &C::CommitmentScheme, + utxo_set_verifier: &C::UtxoSetVerifier, + ) -> P::ConstraintSystem + where + P: ProofSystemConfiguration<C>, + { + let mut cs = P::ProofSystem::for_known(); + let (base_asset_id, participants, commitment_scheme, utxo_set_verifier) = + self.known_variables::<P>(commitment_scheme, utxo_set_verifier, &mut cs); + Self::build_constraints( + base_asset_id, + participants, + commitment_scheme, + utxo_set_verifier, + &mut cs, + ); + cs + } - /// Identity Variable Configuration Type - type ConfigVar: identity2::Configuration<Asset = AssetVar<Self::ConstraintSystem>>; + /// Generates a proving and verifying context for this transfer shape. + #[inline] + pub fn generate_context<P, R>( + commitment_scheme: &C::CommitmentScheme, + utxo_set_verifier: &C::UtxoSetVerifier, + rng: &mut R, + ) -> Result<(ProvingContext<C, P>, VerifyingContext<C, P>), ProofSystemError<C, P>> + where + P: ProofSystemConfiguration<C>, + R: CryptoRng + RngCore + ?Sized, + { + Self::unknown_constraints::<P>(commitment_scheme, utxo_set_verifier) + .generate_context::<P::ProofSystem, _>(rng) + } + + /// Generates a validity proof for this transfer. + #[inline] + pub fn is_valid<P, R>( + &self, + commitment_scheme: &C::CommitmentScheme, + utxo_set_verifier: &C::UtxoSetVerifier, + context: &ProvingContext<C, P>, + rng: &mut R, + ) -> Result<Proof<C, P>, ProofSystemError<C, P>> + where + P: ProofSystemConfiguration<C>, + R: CryptoRng + RngCore + ?Sized, + { + self.known_constraints::<P>(commitment_scheme, utxo_set_verifier) + .prove::<P::ProofSystem, _>(context, rng) + } + + /// Converts `self` into its ledger post. + #[inline] + pub fn into_post<P, R>( + self, + commitment_scheme: &C::CommitmentScheme, + utxo_set_verifier: &C::UtxoSetVerifier, + context: &ProvingContext<C, P>, + rng: &mut R, + ) -> Result<TransferPost<C, P>, ProofSystemError<C, P>> + where + P: ProofSystemConfiguration<C>, + R: CryptoRng + RngCore + ?Sized, + { + Ok(TransferPost { + validity_proof: self.is_valid::<P, _>( + commitment_scheme, + utxo_set_verifier, + context, + rng, + )?, + asset_id: self.asset_id, + sources: self.sources.into(), + sender_posts: IntoIterator::into_iter(self.senders) + .map(Sender::into_post) + .collect(), + receiver_posts: IntoIterator::into_iter(self.receivers) + .map(FullReceiver::into_post) + .collect(), + sinks: self.sinks.into(), + }) + } } -impl<C> identity2::Configuration for C +/// Transfer Participants Variable +struct TransferParticipantsVar< + C, + P, + const SOURCES: usize, + const SENDERS: usize, + const RECEIVERS: usize, + const SINKS: usize, +> where + C: Configuration, + P: ProofSystemConfiguration<C>, +{ + /// Source Variables + sources: Vec<P::AssetValue>, + + /// Sender Variables + senders: Vec<identity2::Sender<P, P::UtxoSetVerifier>>, + + /// Receiver Variables + receivers: Vec<identity2::Receiver<P>>, + + /// Sink Variables + sinks: Vec<P::AssetValue>, +} + +impl< + C, + P, + const SOURCES: usize, + const SENDERS: usize, + const RECEIVERS: usize, + const SINKS: usize, + > Variable<P::ConstraintSystem> + for TransferParticipantsVar<C, P, SOURCES, SENDERS, RECEIVERS, SINKS> where C: Configuration, + P: ProofSystemConfiguration<C>, { - type Asset = <C::Config as identity2::Configuration>::Asset; - type KeyScheme = <C::Config as identity2::Configuration>::KeyScheme; - type CommitmentScheme = <C::Config as identity2::Configuration>::CommitmentScheme; + type Type = Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>; + + type Mode = Derived; + + #[inline] + fn new(cs: &mut P::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + match allocation { + Allocation::Known(this, mode) => Self { + sources: this + .sources + .iter() + .map(|source| source.as_known(cs, Public)) + .collect(), + senders: this + .senders + .iter() + .map(|sender| { + // + todo!() + }) + .collect(), + receivers: this + .receivers + .iter() + .map(|receiver| { + // + todo!() + }) + .collect(), + sinks: this + .sinks + .iter() + .map(|sink| sink.as_known(cs, Public)) + .collect(), + }, + Allocation::Unknown(mode) => Self { + sources: (0..SOURCES) + .into_iter() + .map(|_| P::AssetValue::new_unknown(cs, Public)) + .collect(), + senders: (0..SENDERS) + .into_iter() + .map(|_| { + // + todo!() + }) + .collect(), + receivers: (0..RECEIVERS) + .into_iter() + .map(|_| { + // + todo!() + }) + .collect(), + sinks: (0..SINKS) + .into_iter() + .map(|_| P::AssetValue::new_unknown(cs, Public)) + .collect(), + }, + } + } } -/// -pub type Sender<C> = - identity2::Sender<<C as Configuration>::Config, <C as Configuration>::UtxoSetVerifier>; +/// Transfer Post +pub struct TransferPost<C, P> +where + C: Configuration, + P: ProofSystemConfiguration<C>, +{ + /// Asset Id + asset_id: Option<AssetId>, -/// -pub type SenderVar<C> = - identity2::Sender<<C as Configuration>::ConfigVar, <C as Configuration>::UtxoSetVerifierVar>; + /// Sources + sources: Vec<AssetValue>, -/// -pub type SenderPost<C> = - identity2::SenderPost<<C as Configuration>::Config, <C as Configuration>::UtxoSetVerifier>; + /// Sender Posts + sender_posts: Vec<SenderPost<C>>, -/// -pub type Receiver<C> = identity2::Receiver<<C as Configuration>::Config>; + /// Receiver Posts + receiver_posts: Vec<ReceiverPost<C>>, -/// -pub type ReceiverVar<C> = identity2::Receiver<<C as Configuration>::ConfigVar>; + /// Sinks + sinks: Vec<AssetValue>, -/// -pub type ReceiverPost<C> = identity2::ReceiverPost<<C as Configuration>::Config>; -*/ + /// Validity Proof + validity_proof: Proof<C, P>, +} diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index bbd10b452..c71a4b1a5 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -387,7 +387,7 @@ pub trait ConstraintSystem { #[inline] fn eq<V>(&mut self, lhs: &V, rhs: &V) -> Self::Bool where - V: Variable<Self> + Equal<Self>, + V: Equal<Self>, { V::eq(self, lhs, rhs) } @@ -396,7 +396,7 @@ pub trait ConstraintSystem { #[inline] fn assert_eq<V>(&mut self, lhs: &V, rhs: &V) where - V: Variable<Self> + Equal<Self>, + V: Equal<Self>, { V::assert_eq(self, lhs, rhs); } @@ -405,7 +405,7 @@ pub trait ConstraintSystem { #[inline] fn assert_all_eq_to_base<'t, V, I>(&mut self, base: &'t V, iter: I) where - V: 't + Variable<Self> + Equal<Self>, + V: 't + Equal<Self>, I: IntoIterator<Item = &'t V>, { V::assert_all_eq_to_base(self, base, iter); @@ -415,7 +415,7 @@ pub trait ConstraintSystem { #[inline] fn assert_all_eq<'t, V, I>(&mut self, iter: I) where - V: 't + Variable<Self> + Equal<Self>, + V: 't + Equal<Self>, I: IntoIterator<Item = &'t V>, { V::assert_all_eq(self, iter); @@ -475,7 +475,7 @@ impl Variable<Native> for bool { } /// Equality Trait -pub trait Equal<C>: Variable<C> +pub trait Equal<C> where C: ConstraintSystem + ?Sized, { @@ -1034,6 +1034,22 @@ pub mod reflection { type Mode = T::Mode; } + /// + pub trait HasEqual<V>: ConstraintSystem { + /// + fn eq(&mut self, lhs: &V, rhs: &V) -> Self::Bool; + } + + impl<C, V> Equal<C> for V + where + C: HasEqual<V>, + { + #[inline] + fn eq(cs: &mut C, lhs: &Self, rhs: &Self) -> C::Bool { + HasEqual::eq(cs, lhs, rhs) + } + } + /// Allocates a new unknown variable into `cs` with the given `mode`. #[inline] pub fn known<T, C>(cs: &mut C, value: &T, mode: KnownMode<T, C>) -> Var<T, C> From 721efafaf21d6ca2fd9aa9d994057c66e6447b60 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 14 Nov 2021 14:58:45 -0500 Subject: [PATCH 122/275] wip: fix Accumulator interface --- manta-accounting/src/identity.rs | 41 ++--- manta-accounting/src/identity2.rs | 53 +++--- manta-accounting/src/transfer.rs | 4 +- manta-accounting/src/transfer2.rs | 229 +++++++++++++++++++++++++- manta-accounting/src/wallet/signer.rs | 2 - manta-accounting/src/wallet/test.rs | 2 + manta-crypto/src/accumulator.rs | 153 ++++++++--------- manta-crypto/src/merkle_tree/tree.rs | 24 ++- 8 files changed, 349 insertions(+), 159 deletions(-) diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 7e710beb7..1ced3ad54 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -801,14 +801,9 @@ where #[inline] pub fn can_upgrade<S>(&self, utxo_set: &S) -> bool where - S: Accumulator< - Item = V::Item, - Checkpoint = V::Checkpoint, - Witness = V::Witness, - Verifier = V, - >, + S: Accumulator<Item = V::Item, Verifier = V>, { - self.utxo_membership_proof.matching_checkpoint(utxo_set) + self.utxo_membership_proof.matching_output(utxo_set) } /// Upgrades the `pre_sender` to a [`Sender`] by attaching `self` to it. @@ -975,12 +970,7 @@ where utxo_set: &S, ) -> Option<Self> where - S: Accumulator< - Item = V::Item, - Checkpoint = V::Checkpoint, - Witness = V::Witness, - Verifier = V, - >, + S: Accumulator<Item = V::Item, Verifier = V>, { identity.into_sender(commitment_scheme, asset, utxo_set) } @@ -1018,7 +1008,7 @@ where pub fn into_post(self) -> SenderPost<C, V> { SenderPost { void_number: self.void_number, - utxo_checkpoint: self.utxo_membership_proof.into_checkpoint(), + utxo_checkpoint: self.utxo_membership_proof.into_output(), } } } @@ -1043,12 +1033,12 @@ where /// /// # Safety /// - /// This type must be some wrapper around [`S::Checkpoint`] which can only be constructed by this + /// This type must be some wrapper around [`S::Output`] which can only be constructed by this /// implementation of [`SenderLedger`]. This is to prevent that [`spend`](Self::spend) is /// called before [`is_unspent`](Self::is_unspent) and /// [`is_matching_checkpoint`](Self::is_matching_checkpoint). /// - /// [`S::Checkpoint`]: Verifier::Checkpoint + /// [`S::Output`]: Verifier::Output type ValidUtxoCheckpoint; /// Super Posting Key @@ -1066,10 +1056,7 @@ where /// /// Failure to match the ledger state means that the sender was constructed under an invalid or /// older state of the ledger. - fn is_matching_checkpoint( - &self, - checkpoint: V::Checkpoint, - ) -> Option<Self::ValidUtxoCheckpoint>; + fn is_matching_checkpoint(&self, checkpoint: V::Output) -> Option<Self::ValidUtxoCheckpoint>; /// Posts the `void_number` to the ledger, spending the asset. /// @@ -1109,7 +1096,7 @@ where pub(super) void_number: VoidNumber<C>, /// UTXO Checkpoint - pub(super) utxo_checkpoint: V::Checkpoint, + pub(super) utxo_checkpoint: V::Output, } impl<C, V> SenderPost<C, V> @@ -1584,7 +1571,7 @@ pub mod constraint { where C: Configuration, V: Verifier<Item = Utxo<C>> + ?Sized, - C::ConstraintSystem: HasVariable<V::Checkpoint> + HasVariable<V::Witness>, + C::ConstraintSystem: HasVariable<V::Output> + HasVariable<V::Witness>, { /// Secret Key secret_key: SecretKeyVar<C>, @@ -1612,7 +1599,7 @@ pub mod constraint { where C: Configuration, V: Verifier<Item = Utxo<C>> + ?Sized, - C::ConstraintSystem: HasVariable<V::Checkpoint> + HasVariable<V::Witness>, + C::ConstraintSystem: HasVariable<V::Output> + HasVariable<V::Witness>, { /// Checks if `self` is a well-formed sender and returns its asset. #[inline] @@ -1667,7 +1654,7 @@ pub mod constraint { C: Configuration, V: Verifier<Item = Utxo<C>> + ?Sized, C::ConstraintSystem: - HasVariable<V::Checkpoint, Mode = Public> + HasVariable<V::Witness, Mode = Secret>, + HasVariable<V::Output, Mode = Public> + HasVariable<V::Witness, Mode = Secret>, { type Type = Sender<C, V>; @@ -1710,7 +1697,7 @@ pub mod constraint { C: Configuration, V: Verifier<Item = Utxo<C>> + ?Sized, C::ConstraintSystem: - HasVariable<V::Checkpoint, Mode = Public> + HasVariable<V::Witness, Mode = Secret>, + HasVariable<V::Output, Mode = Public> + HasVariable<V::Witness, Mode = Secret>, { type Variable = SenderVar<C, V>; type Mode = Derived; @@ -1720,7 +1707,7 @@ pub mod constraint { where C: Configuration, V: Verifier<Item = Utxo<C>> + ?Sized, - C::ConstraintSystem: HasVariable<V::Checkpoint, Mode = Public>, + C::ConstraintSystem: HasVariable<V::Output, Mode = Public>, { /// Extends proof public input with `self`. #[inline] @@ -1728,7 +1715,7 @@ pub mod constraint { where P: ProofSystem<ConstraintSystem = C::ConstraintSystem> + ProofSystemInput<VoidNumber<C>> - + ProofSystemInput<V::Checkpoint>, + + ProofSystemInput<V::Output>, { // TODO: Add a "public part" trait that extracts the public part of `Sender` (using // `SenderVar` to determine the types), then generate this method automatically. diff --git a/manta-accounting/src/identity2.rs b/manta-accounting/src/identity2.rs index 7fe72ed07..86ba4fda7 100644 --- a/manta-accounting/src/identity2.rs +++ b/manta-accounting/src/identity2.rs @@ -289,14 +289,9 @@ where #[inline] pub fn can_upgrade<S>(&self, utxo_set: &S) -> bool where - S: Accumulator< - Item = V::Item, - Checkpoint = V::Checkpoint, - Witness = V::Witness, - Verifier = V, - >, + S: Accumulator<Item = V::Item, Verifier = V>, { - self.utxo_membership_proof.matching_checkpoint(utxo_set) + self.utxo_membership_proof.matching_output(utxo_set) } /// Upgrades the `pre_sender` to a [`Sender`] by attaching `self` to it. @@ -361,7 +356,7 @@ where #[inline] pub fn into_post(self) -> SenderPost<C, V> { SenderPost { - utxo_checkpoint: self.utxo_membership_proof.into_checkpoint(), + utxo_accumulator_output: self.utxo_membership_proof.into_output(), void_number: self.void_number, } } @@ -380,20 +375,20 @@ where /// This type must be some wrapper around [`VoidNumber`] which can only be constructed by this /// implementation of [`SenderLedger`]. This is to prevent that [`spend`](Self::spend) is /// called before [`is_unspent`](Self::is_unspent) and - /// [`is_matching_checkpoint`](Self::is_matching_checkpoint). + /// [`has_matching_utxo_accumulator_output`](Self::has_matching_utxo_accumulator_output). type ValidVoidNumber; - /// Valid Utxo State Posting Key + /// Valid UTXO Accumulator Output Posting Key /// /// # Safety /// - /// This type must be some wrapper around [`S::Checkpoint`] which can only be constructed by this + /// This type must be some wrapper around [`S::Output`] which can only be constructed by this /// implementation of [`SenderLedger`]. This is to prevent that [`spend`](Self::spend) is /// called before [`is_unspent`](Self::is_unspent) and - /// [`is_matching_checkpoint`](Self::is_matching_checkpoint). + /// [`has_matching_utxo_accumulator_output`](Self::has_matching_utxo_accumulator_output). /// - /// [`S::Checkpoint`]: Verifier::Checkpoint - type ValidUtxoCheckpoint; + /// [`S::Output`]: Verifier::Output + type ValidUtxoAccumulatorOutput; /// Super Posting Key /// @@ -405,15 +400,15 @@ where /// Existence of such a void number could indicate a possible double-spend. fn is_unspent(&self, void_number: VoidNumber<C>) -> Option<Self::ValidVoidNumber>; - /// Checks if the `checkpoint` matches the current checkpoint of the UTXO set that is stored on + /// Checks if `output` matches the current accumulated value of the UTXO set that is stored on /// the ledger. /// /// Failure to match the ledger state means that the sender was constructed under an invalid or /// older state of the ledger. - fn is_matching_checkpoint( + fn has_matching_utxo_accumulator_output( &self, - checkpoint: V::Checkpoint, - ) -> Option<Self::ValidUtxoCheckpoint>; + output: V::Output, + ) -> Option<Self::ValidUtxoAccumulatorOutput>; /// Posts the `void_number` to the ledger, spending the asset. /// @@ -423,7 +418,7 @@ where /// the ledger. See [`is_unspent`](Self::is_unspent). fn spend( &mut self, - utxo_checkpoint: Self::ValidUtxoCheckpoint, + utxo_accumulator_output: Self::ValidUtxoAccumulatorOutput, void_number: Self::ValidVoidNumber, super_key: &Self::SuperPostingKey, ); @@ -437,10 +432,10 @@ pub enum SenderPostError { /// The asset has already been spent. AssetSpent, - /// Invalid UTXO Checkpoint Error + /// Invalid UTXO Accumulator Error /// /// The sender was not constructed under the current state of the UTXO set. - InvalidUtxoCheckpoint, + InvalidUtxoAccumulator, } /// Sender Post @@ -449,8 +444,8 @@ where C: Configuration, V: Verifier<Item = Utxo<C>>, { - /// UTXO Checkpoint - utxo_checkpoint: V::Checkpoint, + /// UTXO Accumulator Output + utxo_accumulator_output: V::Output, /// Void Number void_number: VoidNumber<C>, @@ -468,9 +463,9 @@ where L: SenderLedger<C, V>, { Ok(SenderPostingKey { - utxo_checkpoint: ledger - .is_matching_checkpoint(self.utxo_checkpoint) - .ok_or(SenderPostError::InvalidUtxoCheckpoint)?, + utxo_accumulator_output: ledger + .has_matching_utxo_accumulator_output(self.utxo_accumulator_output) + .ok_or(SenderPostError::InvalidUtxoAccumulator)?, void_number: ledger .is_unspent(self.void_number) .ok_or(SenderPostError::AssetSpent)?, @@ -485,8 +480,8 @@ where V: Verifier<Item = Utxo<C>>, L: SenderLedger<C, V>, { - /// UTXO Checkpoint Posting Key - utxo_checkpoint: L::ValidUtxoCheckpoint, + /// UTXO Accumulator Output Posting Key + utxo_accumulator_output: L::ValidUtxoAccumulatorOutput, /// Void Number Posting Key void_number: L::ValidVoidNumber, @@ -501,7 +496,7 @@ where /// Posts `self` to the sender `ledger`. #[inline] pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) { - ledger.spend(self.utxo_checkpoint, self.void_number, super_key); + ledger.spend(self.utxo_accumulator_output, self.void_number, super_key); } } diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 5fc3ba472..55a6fcc06 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -63,7 +63,7 @@ pub trait Configuration: type ConstraintSystem: constraint::ConstraintSystem + HasVariable<AssetId, Variable = Self::AssetIdVar, Mode = PublicOrSecret> + HasVariable<AssetValue, Variable = Self::AssetValueVar, Mode = PublicOrSecret> - + HasVariable<<Self::UtxoSetVerifier as Verifier>::Checkpoint, Mode = Public> + + HasVariable<<Self::UtxoSetVerifier as Verifier>::Output, Mode = Public> + HasVariable<<Self::UtxoSetVerifier as Verifier>::Witness, Mode = Secret>; /// Proof System @@ -71,7 +71,7 @@ pub trait Configuration: + ProofSystemInput<AssetId> + ProofSystemInput<AssetValue> + ProofSystemInput<VoidNumber<Self>> - + ProofSystemInput<<Self::UtxoSetVerifier as Verifier>::Checkpoint> + + ProofSystemInput<<Self::UtxoSetVerifier as Verifier>::Output> + ProofSystemInput<Utxo<Self>>; /// Asset Id Variable diff --git a/manta-accounting/src/transfer2.rs b/manta-accounting/src/transfer2.rs index d0370fb50..4d57c586a 100644 --- a/manta-accounting/src/transfer2.rs +++ b/manta-accounting/src/transfer2.rs @@ -18,7 +18,7 @@ use crate::{ asset::{Asset, AssetId, AssetValue, AssetVar}, - identity2::{self, CommitmentSchemeOutput, PublicKey, Utxo}, + identity2::{self, CommitmentSchemeOutput, Utxo}, }; use alloc::vec::Vec; use core::ops::Add; @@ -30,10 +30,11 @@ use manta_crypto::{ Allocation, Constant, ConstraintSystem, Derived, Equal, ProofSystem, Public, PublicOrSecret, Variable, VariableSource, }, - encryption::HybridPublicKeyEncryptionScheme, + encryption::{EncryptedMessage, HybridPublicKeyEncryptionScheme}, key::KeyAgreementScheme, rand::{CryptoRng, RngCore}, }; +use manta_util::create_seal; /// Returns `true` if the transfer with this shape would have no public participants. #[inline] @@ -102,6 +103,9 @@ where > + Variable<Self::ConstraintSystem, Type = C::UtxoSetVerifier, Mode = Constant>; } +/// Encrypted Note Type +pub type EncryptedNote<C> = EncryptedMessage<<C as Configuration>::EncryptionScheme>; + /// Pre-Sender Type pub type PreSender<C> = identity2::PreSender<C>; @@ -580,3 +584,224 @@ where /// Validity Proof validity_proof: Proof<C, P>, } + +create_seal! {} + +/// Transfer Shapes +/// +/// This trait identifies a transfer shape, i.e. the number and type of participants on the input +/// and output sides of the transaction. This trait is sealed and can only be used with the +/// [existing canonical implementations](canonical). +pub trait Shape: sealed::Sealed { + /// Number of Sources + const SOURCES: usize; + + /// Number of Senders + const SENDERS: usize; + + /// Number of Receivers + const RECEIVERS: usize; + + /// Number of Sinks + const SINKS: usize; +} + +/// Canonical Transaction Types +pub mod canonical { + use super::*; + use crate::identity2::ReceivingKey; + use manta_util::seal; + + /// Implements [`Shape`] for a given shape type. + macro_rules! impl_shape { + ($shape:tt, $sources:expr, $senders:expr, $receivers:expr, $sinks:expr) => { + seal!($shape); + impl Shape for $shape { + const SOURCES: usize = $sources; + const SENDERS: usize = $senders; + const RECEIVERS: usize = $receivers; + const SINKS: usize = $sinks; + } + }; + } + + /// Builds a new alias using the given shape type. + macro_rules! alias_type { + ($type:tt, $t:ident, $shape:tt) => { + $type< + $t, + { $shape::SOURCES }, + { $shape::SENDERS }, + { $shape::RECEIVERS }, + { $shape::SINKS }, + > + } + } + + /// Builds a new [`Transfer`] alias using the given shape type. + macro_rules! transfer_alias { + ($t:ident, $shape:tt) => { + alias_type!(Transfer, $t, $shape) + }; + } + + /// Mint Transaction Shape + /// + /// ```text + /// <1, 0, 1, 0> + /// ``` + #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd)] + pub struct MintShape; + + impl_shape!(MintShape, 1, 0, 1, 0); + + /// Mint Transaction + pub type Mint<C> = transfer_alias!(C, MintShape); + + impl<C> Mint<C> + where + C: Configuration, + { + /// Builds a [`Mint`] from `asset` and `receiver`. + #[inline] + pub fn build(asset: Asset, receiver: FullReceiver<C>) -> Self { + Self::new( + Some(asset.id), + [asset.value], + Default::default(), + [receiver], + Default::default(), + ) + } + } + + /// Private Transfer Transaction Shape + /// + /// ```text + /// <0, 2, 2, 0> + /// ``` + #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd)] + pub struct PrivateTransferShape; + + impl_shape!(PrivateTransferShape, 0, 2, 2, 0); + + /// Private Transfer Transaction + pub type PrivateTransfer<C> = transfer_alias!(C, PrivateTransferShape); + + impl<C> PrivateTransfer<C> + where + C: Configuration, + { + /// Builds a [`PrivateTransfer`] from `senders` and `receivers`. + #[inline] + pub fn build( + senders: [Sender<C>; PrivateTransferShape::SENDERS], + receivers: [FullReceiver<C>; PrivateTransferShape::RECEIVERS], + ) -> Self { + Self::new( + Default::default(), + Default::default(), + senders, + receivers, + Default::default(), + ) + } + } + + /// Reclaim Transaction Shape + /// + /// ```text + /// <0, 2, 1, 1> + /// ``` + /// + /// The [`ReclaimShape`] is defined in terms of the [`PrivateTransferShape`]. It is defined to + /// have the same number of senders and one secret receiver turned into a public sink. + #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd)] + pub struct ReclaimShape; + + impl_shape!( + ReclaimShape, + 0, + PrivateTransferShape::SENDERS, + PrivateTransferShape::RECEIVERS - 1, + 1 + ); + + /// Reclaim Transaction + pub type Reclaim<C> = transfer_alias!(C, ReclaimShape); + + impl<C> Reclaim<C> + where + C: Configuration, + { + /// Builds a [`Reclaim`] from `senders`, `receivers`, and `reclaim`. + #[inline] + pub fn build( + senders: [Sender<C>; ReclaimShape::SENDERS], + receivers: [FullReceiver<C>; ReclaimShape::RECEIVERS], + reclaim: Asset, + ) -> Self { + Self::new( + Some(reclaim.id), + Default::default(), + senders, + receivers, + [reclaim.value], + ) + } + } + + /// Canonical Transaction Type + pub enum Transaction<K> + where + K: KeyAgreementScheme, + { + /// Mint Private Asset + Mint(Asset), + + /// Private Transfer Asset to Receiver + PrivateTransfer(Asset, ReceivingKey<K>), + + /// Reclaim Private Asset + Reclaim(Asset), + } + + impl<K> Transaction<K> + where + K: KeyAgreementScheme, + { + /// Checks that `self` can be executed for a given `balance` state, returning the + /// transaction kind if successful, and returning the asset back if the balance was + /// insufficient. + #[inline] + pub fn check<F>(&self, balance: F) -> Result<TransactionKind, Asset> + where + F: FnOnce(Asset) -> bool, + { + match self { + Self::Mint(asset) => Ok(TransactionKind::Deposit(*asset)), + Self::PrivateTransfer(asset, _) | Self::Reclaim(asset) => { + if balance(*asset) { + Ok(TransactionKind::Withdraw(*asset)) + } else { + Err(*asset) + } + } + } + } + } + + /// Transaction Kind + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] + pub enum TransactionKind { + /// Deposit Transaction + /// + /// A transaction of this kind will result in a deposit of `asset`. + Deposit(Asset), + + /// Withdraw Transaction + /// + /// A transaction of this kind will result in a withdraw of `asset`. + Withdraw(Asset), + } +} diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index daca9b36a..506be4a05 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -646,8 +646,6 @@ pub trait Configuration: transfer::Configuration { /// [`Utxo`] Accumulator Type type UtxoSet: Accumulator< Item = <Self::UtxoSetVerifier as Verifier>::Item, - Checkpoint = <Self::UtxoSetVerifier as Verifier>::Checkpoint, - Witness = <Self::UtxoSetVerifier as Verifier>::Witness, Verifier = Self::UtxoSetVerifier, > + ConstantCapacityAccumulator + ExactSizeAccumulator diff --git a/manta-accounting/src/wallet/test.rs b/manta-accounting/src/wallet/test.rs index a95f706ad..e032a146d 100644 --- a/manta-accounting/src/wallet/test.rs +++ b/manta-accounting/src/wallet/test.rs @@ -15,3 +15,5 @@ // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. //! Testing Framework + +// TODO: Add tests for the asynchronous wallet protocol. diff --git a/manta-crypto/src/accumulator.rs b/manta-crypto/src/accumulator.rs index 74a867568..490fe657a 100644 --- a/manta-crypto/src/accumulator.rs +++ b/manta-crypto/src/accumulator.rs @@ -42,21 +42,22 @@ pub trait Verifier { /// Item Type type Item: ?Sized; - /// Public Checkpoint Type - type Checkpoint; - /// Secret Witness Type type Witness; + /// Output Type + type Output; + /// Verification Type type Verification; - /// Verifies that `item` is stored in a known accumulator with `checkpoint` and `witness`. + /// Verifies that `item` is stored in a known accumulator with accumulated `output` and + /// membership `witness`. fn verify( &self, item: &Self::Item, - checkpoint: &Self::Checkpoint, witness: &Self::Witness, + output: &Self::Output, ) -> Self::Verification; } @@ -66,57 +67,53 @@ where { type Item = V::Item; - type Checkpoint = V::Checkpoint; - type Witness = V::Witness; + type Output = V::Output; + type Verification = V::Verification; #[inline] fn verify( &self, item: &Self::Item, - checkpoint: &Self::Checkpoint, witness: &Self::Witness, + output: &Self::Output, ) -> Self::Verification { - (*self).verify(item, checkpoint, witness) + (*self).verify(item, witness, output) } } +/// Accumulator Output Type +pub type Output<A> = <<A as Accumulator>::Verifier as Verifier>::Output; + /// Accumulator pub trait Accumulator { /// Item Type type Item: ?Sized; - /// Public Checkpoint Type - type Checkpoint; - - /// Secret Witness Type - type Witness; - /// Verifier Type - type Verifier: Verifier<Item = Self::Item, Checkpoint = Self::Checkpoint, Witness = Self::Witness> - + ?Sized; + type Verifier: Verifier<Item = Self::Item> + ?Sized; - /// Checkpoint Matching Set Type - type CheckpointSet: MatchingSet<Self::Checkpoint>; + /// Output Matching Set Type + type OutputSet: MatchingSet<<Self::Verifier as Verifier>::Output>; /// Returns the verifier for `self`. fn verifier(&self) -> &Self::Verifier; - /// Returns the checkpoint matching set for the current state of `self`. - fn checkpoints(&self) -> Self::CheckpointSet; + /// Returns the output matching set for the current state of `self`. + fn outputs(&self) -> Self::OutputSet; - /// Returns `true` if `checkpoint` is contained in the current checkpoint matching set - /// associated to `self`. + /// Returns `true` if `output` is contained in the current output matching set associated to + /// `self`. /// /// # Implementation Note /// /// This method is an optimization path for implementations of [`Accumulator`] which can do a - /// checkpoint matching without having to return an entire owned [`Self::CheckpointSet`]. + /// output matching without having to return an entire owned [`Self::OutputSet`]. #[inline] - fn matching_checkpoint(&self, checkpoint: &Self::Checkpoint) -> bool { - self.checkpoints().contains(checkpoint) + fn matching_output(&self, output: &Output<Self>) -> bool { + self.outputs().contains(output) } /// Inserts `item` into `self` with the guarantee that `self` can later return a valid @@ -147,13 +144,9 @@ where { type Item = A::Item; - type Checkpoint = A::Checkpoint; - - type Witness = A::Witness; - type Verifier = A::Verifier; - type CheckpointSet = A::CheckpointSet; + type OutputSet = A::OutputSet; #[inline] fn verifier(&self) -> &Self::Verifier { @@ -161,13 +154,13 @@ where } #[inline] - fn checkpoints(&self) -> Self::CheckpointSet { - (**self).checkpoints() + fn outputs(&self) -> Self::OutputSet { + (**self).outputs() } #[inline] - fn matching_checkpoint(&self, checkpoint: &Self::Checkpoint) -> bool { - (**self).matching_checkpoint(checkpoint) + fn matching_output(&self, output: &Output<Self>) -> bool { + (**self).matching_output(output) } #[inline] @@ -230,64 +223,58 @@ pub struct MembershipProof<V> where V: Verifier + ?Sized, { - /// Public Checkpoint - checkpoint: V::Checkpoint, - - /// Secret Witness + /// Secret Membership Witness witness: V::Witness, + + /// Accumulator Output + output: V::Output, } impl<V> MembershipProof<V> where V: Verifier + ?Sized, { - /// Builds a new [`MembershipProof`] from `checkpoint` and `witness`. + /// Builds a new [`MembershipProof`] from `witness` and `output`. #[inline] - pub fn new(checkpoint: V::Checkpoint, witness: V::Witness) -> Self { - Self { - checkpoint, - witness, - } + pub fn new(witness: V::Witness, output: V::Output) -> Self { + Self { witness, output } } - /// Converts `self` into its checkpoint, dropping the [`V::Witness`](Verifier::Witness). + /// Returns the accumulated output part of `self`, dropping the + /// [`V::Witness`](Verifier::Witness). #[inline] - pub fn into_checkpoint(self) -> V::Checkpoint { - self.checkpoint + pub fn into_output(self) -> V::Output { + self.output } - /// Returns `true` if the checkpoint associated to `self` is contained in `checkpoints`. + /// Returns `true` if the output associated to `self` is contained in `outputs`. #[inline] - pub fn checkpoint_contained_in<S>(&self, checkpoints: &S) -> bool + pub fn output_contained_in<S>(&self, outputs: &S) -> bool where - S: MatchingSet<V::Checkpoint>, + S: MatchingSet<V::Output>, { - checkpoints.contains(&self.checkpoint) + outputs.contains(&self.output) } - /// Returns `true` if the checkpoint associated to `self` is contained in the current - /// checkpoint matching set associated to `accumulator`. + /// Returns `true` if the output associated to `self` is contained in the current output + /// matching set associated to `accumulator`. #[inline] - pub fn matching_checkpoint<A>(&self, accumulator: &A) -> bool + pub fn matching_output<A>(&self, accumulator: &A) -> bool where - A: Accumulator< - Item = V::Item, - Checkpoint = V::Checkpoint, - Witness = V::Witness, - Verifier = V, - >, + A: Accumulator<Verifier = V>, { - accumulator.matching_checkpoint(&self.checkpoint) + accumulator.matching_output(&self.output) } /// Verifies that `item` is stored in a known accumulator using `verifier`. #[inline] pub fn verify(&self, item: &V::Item, verifier: &V) -> V::Verification { - verifier.verify(item, &self.checkpoint, &self.witness) + verifier.verify(item, &self.witness, &self.output) } } /// Constraint System Gadgets for Accumulators +// TODO[remove]: #[cfg(feature = "constraint")] #[cfg_attr(doc_cfg, doc(cfg(feature = "constraint")))] pub mod constraint { @@ -356,10 +343,10 @@ pub mod constraint { pub struct MembershipProofVar<V, C> where V: Verifier + ?Sized, - C: HasVariable<V::Checkpoint> + HasVariable<V::Witness> + ?Sized, + C: HasVariable<V::Output> + HasVariable<V::Witness> + ?Sized, { /// Public Checkpoint Variable - checkpoint: Var<V::Checkpoint, C>, + checkpoint: Var<V::Output, C>, /// Secret Witness Variable witness: Var<V::Witness, C>, @@ -368,11 +355,11 @@ pub mod constraint { impl<V, C> MembershipProofVar<V, C> where V: Verifier + ?Sized, - C: HasVariable<V::Checkpoint> + HasVariable<V::Witness> + ?Sized, + C: HasVariable<V::Output> + HasVariable<V::Witness> + ?Sized, { /// Builds a new [`MembershipProofVar`] from `checkpoint` and `witness` variables. #[inline] - pub fn new(checkpoint: Var<V::Checkpoint, C>, witness: Var<V::Witness, C>) -> Self { + pub fn new(checkpoint: Var<V::Output, C>, witness: Var<V::Witness, C>) -> Self { Self { checkpoint, witness, @@ -394,21 +381,21 @@ pub mod constraint { impl<V, C> Variable<C> for MembershipProofVar<V, C> where V: Verifier + ?Sized, - C: HasVariable<V::Checkpoint> + HasVariable<V::Witness> + ?Sized, + C: HasVariable<V::Output> + HasVariable<V::Witness> + ?Sized, { type Type = MembershipProof<V>; - type Mode = MembershipProofMode<Mode<V::Checkpoint, C>, Mode<V::Witness, C>>; + type Mode = MembershipProofMode<Mode<V::Output, C>, Mode<V::Witness, C>>; #[inline] fn new(cs: &mut C, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self::new( - cs.allocate_known(&this.checkpoint, mode.checkpoint), + cs.allocate_known(&this.output, mode.checkpoint), cs.allocate_known(&this.witness, mode.witness), ), Allocation::Unknown(mode) => Self::new( - unknown::<V::Checkpoint, _>(cs, mode.checkpoint), + unknown::<V::Output, _>(cs, mode.checkpoint), unknown::<V::Witness, _>(cs, mode.witness), ), } @@ -418,14 +405,14 @@ pub mod constraint { impl<V, C> HasAllocation<C> for MembershipProof<V> where V: Verifier + ?Sized, - C: HasVariable<V::Checkpoint> + HasVariable<V::Witness> + ?Sized, + C: HasVariable<V::Output> + HasVariable<V::Witness> + ?Sized, { type Variable = MembershipProofVar<V, C>; - type Mode = MembershipProofMode<Mode<V::Checkpoint, C>, Mode<V::Witness, C>>; + type Mode = MembershipProofMode<Mode<V::Output, C>, Mode<V::Witness, C>>; } /// Public Checkpoint Type for [`VerifierVariable`] - pub type CheckpointType<V, C> = <<V as Variable<C>>::Type as Verifier>::Checkpoint; + pub type CheckpointType<V, C> = <<V as Variable<C>>::Type as Verifier>::Output; /// Secret Witness Type for [`VerifierVariable`] pub type WitnessType<V, C> = <<V as Variable<C>>::Type as Verifier>::Witness; @@ -470,7 +457,7 @@ pub mod test { /// Asserts that `accumulator` can prove the membership of `item` after it is inserted. #[inline] - pub fn assert_provable_membership<A>(accumulator: &mut A, item: &A::Item) -> A::Checkpoint + pub fn assert_provable_membership<A>(accumulator: &mut A, item: &A::Item) -> Output<A> where A: Accumulator, A::Verifier: Verifier<Verification = bool>, @@ -488,29 +475,29 @@ pub mod test { proof.verify(item, accumulator.verifier()), "Invalid proof returned for inserted item." ); - proof.into_checkpoint() + proof.into_output() } else { panic!("Item was supposed to be contained in the accumulator after insertion.") } } - /// Asserts that the `accumulator` yields unique checkpoints after every insertion of items from - /// `iter`. + /// Asserts that the `accumulator` yields unique accumulated values after every insertion of + /// items from `iter`. #[inline] - pub fn assert_unique_checkpoints<'i, A, I>(accumulator: &mut A, iter: I) + pub fn assert_unique_outputs<'i, A, I>(accumulator: &mut A, iter: I) where A: Accumulator, A::Item: 'i, - A::Checkpoint: Debug + PartialEq, A::Verifier: Verifier<Verification = bool>, + Output<A>: Debug + PartialEq, I: IntoIterator<Item = &'i A::Item>, { - let checkpoints = iter + let outputs = iter .into_iter() .map(move |item| assert_provable_membership(accumulator, item)) .collect::<Vec<_>>(); - for (i, x) in checkpoints.iter().enumerate() { - for (j, y) in checkpoints.iter().enumerate().skip(i + 1) { + for (i, x) in outputs.iter().enumerate() { + for (j, y) in outputs.iter().enumerate().skip(i + 1) { assert_ne!(x, y, "Found matching checkpoints at {:?} and {:?}.", i, j) } } diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index 28612a0d9..c76cf5da2 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -25,7 +25,7 @@ use crate::{ accumulator::{ - Accumulator, ConstantCapacityAccumulator, ExactSizeAccumulator, MembershipProof, + self, Accumulator, ConstantCapacityAccumulator, ExactSizeAccumulator, MembershipProof, OptimizedAccumulator, Verifier, }, merkle_tree::{ @@ -503,20 +503,20 @@ where { type Item = Leaf<C>; - type Checkpoint = Root<C>; - type Witness = Path<C>; + type Output = Root<C>; + type Verification = bool; #[inline] fn verify( &self, item: &Self::Item, - checkpoint: &Self::Checkpoint, witness: &Self::Witness, + output: &Self::Output, ) -> Self::Verification { - self.verify_path(witness, checkpoint, item) + self.verify_path(witness, output, item) } } @@ -818,13 +818,9 @@ where { type Item = Leaf<C>; - type Checkpoint = Root<C>; - - type Witness = Path<C>; - type Verifier = Parameters<C>; - type CheckpointSet = Self::Checkpoint; + type OutputSet = accumulator::Output<Self>; #[inline] fn verifier(&self) -> &Parameters<C> { @@ -832,13 +828,13 @@ where } #[inline] - fn checkpoints(&self) -> Self::CheckpointSet { + fn outputs(&self) -> Self::OutputSet { self.root() } #[inline] - fn matching_checkpoint(&self, checkpoint: &Self::Checkpoint) -> bool { - self.matching_root(checkpoint) + fn matching_output(&self, output: &accumulator::Output<Self>) -> bool { + self.matching_root(output) } #[inline] @@ -849,9 +845,9 @@ where #[inline] fn prove(&self, item: &Self::Item) -> Option<MembershipProof<Self::Verifier>> { Some(MembershipProof::new( - self.root(), self.path(self.index_of(&self.parameters.digest(item))?) .ok()?, + self.root(), )) } From 258df6a4012697fae0e9b19d652879873f1121f6 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 14 Nov 2021 18:50:35 -0500 Subject: [PATCH 123/275] wip: remove old implementations --- manta-accounting/src/identity2.rs | 34 +- manta-accounting/src/key2.rs | 130 +++ manta-accounting/src/lib.rs | 13 +- manta-accounting/src/transfer2.rs | 259 ++--- manta-accounting/src/wallet2/ledger.rs | 146 +++ manta-accounting/src/wallet2/mod.rs | 28 + manta-accounting/src/wallet2/signer.rs | 1249 ++++++++++++++++++++++++ manta-accounting/src/wallet2/state.rs | 377 +++++++ manta-accounting/src/wallet2/test.rs | 19 + 9 files changed, 2112 insertions(+), 143 deletions(-) create mode 100644 manta-accounting/src/key2.rs create mode 100644 manta-accounting/src/wallet2/ledger.rs create mode 100644 manta-accounting/src/wallet2/mod.rs create mode 100644 manta-accounting/src/wallet2/signer.rs create mode 100644 manta-accounting/src/wallet2/state.rs create mode 100644 manta-accounting/src/wallet2/test.rs diff --git a/manta-accounting/src/identity2.rs b/manta-accounting/src/identity2.rs index 86ba4fda7..214501a74 100644 --- a/manta-accounting/src/identity2.rs +++ b/manta-accounting/src/identity2.rs @@ -24,8 +24,8 @@ use manta_crypto::{ key::KeyAgreementScheme, }; -/// Key Table -pub trait KeyTable<K> +/// Viewing Key Table +pub trait ViewingKeyTable<K> where K: KeyAgreementScheme, { @@ -34,30 +34,42 @@ where /// Returns the key associated to `query`, generating a new key if `query` does not already /// correspond to an existing key. - fn get(&mut self, query: Self::Query) -> &K::SecretKey; + fn get_or_generate(&mut self, query: Self::Query) -> &K::SecretKey; +} + +impl<K> ViewingKeyTable<K> for K::SecretKey +where + K: KeyAgreementScheme, +{ + type Query = (); + + #[inline] + fn get_or_generate(&mut self, query: Self::Query) -> &K::SecretKey { + self + } } /// Spending Key -pub struct SpendingKey<K, T> +pub struct SpendingKey<K, V = <K as KeyAgreementScheme>::SecretKey> where K: KeyAgreementScheme, - T: KeyTable<K>, + V: ViewingKeyTable<K>, { /// Spending Key spending_key: K::SecretKey, /// Viewing Key Table - viewing_key_table: T, + viewing_key_table: V, } -impl<K, T> SpendingKey<K, T> +impl<K, V> SpendingKey<K, V> where K: KeyAgreementScheme, - T: KeyTable<K>, + V: ViewingKeyTable<K>, { /// Builds a new [`SpendingKey`] from `spending_key` and `viewing_key_table`. #[inline] - pub fn new(spending_key: K::SecretKey, viewing_key_table: T) -> Self { + pub fn new(spending_key: K::SecretKey, viewing_key_table: V) -> Self { Self { spending_key, viewing_key_table, @@ -67,10 +79,10 @@ where /// Returns the receiving key for `self` with the viewing key located at the given `query` in /// the viewing key table. #[inline] - pub fn receiving_key(&mut self, query: T::Query) -> ReceivingKey<K> { + pub fn receiving_key(&mut self, query: V::Query) -> ReceivingKey<K> { ReceivingKey { spend: K::derive(&self.spending_key), - view: K::derive(self.viewing_key_table.get(query)), + view: K::derive(self.viewing_key_table.get_or_generate(query)), } } diff --git a/manta-accounting/src/key2.rs b/manta-accounting/src/key2.rs new file mode 100644 index 000000000..2d8e0ec8d --- /dev/null +++ b/manta-accounting/src/key2.rs @@ -0,0 +1,130 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Hierarchical Key Tables + +// TODO: Make [`Account`] more useful for managing accounts. + +use core::{convert::TryFrom, fmt::Debug, hash::Hash}; + +/// Hierarchical Key Table Parameter +pub trait HierarchicalKeyTableParameter: Clone + Default + PartialOrd { + /// Increments the key parameter by one unit. + fn increment(&mut self); +} + +/// Hierarchical Key Table +pub trait HierarchicalKeyTable { + /// Account Type + type Account: HierarchicalKeyTableParameter; + + /// Index Type + type Index: HierarchicalKeyTableParameter; + + /// Key Kind Type + type Kind; + + /// Secret Key Type + type SecretKey; + + /// Key Access Error Type + type Error; + + /// Returns the secret key associated to `account` and `index` of the given `kind`. + fn get( + &self, + account: &Self::Account, + index: &Self::Index, + kind: &Self::Kind, + ) -> Result<Self::SecretKey, Self::Error>; +} + +impl<H> HierarchicalKeyTable for &H +where + H: HierarchicalKeyTable, +{ + type Account = H::Account; + + type Index = H::Index; + + type Kind = H::Kind; + + type SecretKey = H::SecretKey; + + type Error = H::Error; + + #[inline] + fn get( + &self, + account: &Self::Account, + index: &Self::Index, + kind: &Self::Kind, + ) -> Result<Self::SecretKey, Self::Error> { + (*self).get(account, index, kind) + } +} + +/// Account Index with Table +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "H: Clone,"), + Copy(bound = "H: Copy, H::Account: Copy, H::Index: Copy"), + Debug(bound = "H: Debug, H::Account: Debug, H::Index: Debug"), + Default(bound = "H: Default"), + Eq(bound = "H: Eq, H::Account: Eq, H::Index: Eq"), + Hash(bound = "H: Hash, H::Account: Hash, H::Index: Hash"), + PartialEq(bound = "H: PartialEq") +)] +pub struct Account<H> +where + H: HierarchicalKeyTable, +{ + /// Hierarchical Key Table + table: H, + + /// Account Identifier + account: H::Account, + + /// Latest Index + latest_index: H::Index, +} + +impl<H> Account<H> +where + H: HierarchicalKeyTable, +{ + /// Builds a new [`Account`] for `table` and the given `account` identifier. + #[inline] + pub fn new(table: H, account: H::Account) -> Self { + Self::with_index(table, account, Default::default()) + } + + /// Builds a new [`Account`] for `table` and the given `account` identifier and `latest_index`. + #[inline] + pub fn with_index(table: H, account: H::Account, latest_index: H::Index) -> Self { + Self { + table, + account, + latest_index, + } + } + + /// Returns the key of the given `kind` for `self`. + #[inline] + pub fn key(&self, kind: &H::Kind) -> Result<H::SecretKey, H::Error> { + self.table.get(&self.account, &self.latest_index, kind) + } +} diff --git a/manta-accounting/src/lib.rs b/manta-accounting/src/lib.rs index ed67b8717..d7b2671fe 100644 --- a/manta-accounting/src/lib.rs +++ b/manta-accounting/src/lib.rs @@ -29,12 +29,11 @@ extern crate cocoon as cocoon_crate; pub mod asset; pub mod fs; -pub mod identity; +// TODO[remove]: pub mod identity; pub mod identity2; -pub mod key; -pub mod transfer; +// TODO[remove]: pub mod key; +pub mod key2; +// TODO[remove]: pub mod transfer; pub mod transfer2; -pub mod wallet; - -pub use asset::prelude::*; -pub use identity::prelude::*; +// TODO[remove]: pub mod wallet; +pub mod wallet2; diff --git a/manta-accounting/src/transfer2.rs b/manta-accounting/src/transfer2.rs index 4d57c586a..5a0553462 100644 --- a/manta-accounting/src/transfer2.rs +++ b/manta-accounting/src/transfer2.rs @@ -58,86 +58,129 @@ pub trait Configuration: identity2::Configuration<Asset = Asset> { /// UTXO Set Verifier Type type UtxoSetVerifier: Verifier<Item = Utxo<Self>, Verification = bool>; -} -/// Transfer Proof System Configuration -pub trait ProofSystemConfiguration<C>: - identity2::Configuration<Asset = AssetVar<Self::ConstraintSystem>> -where - C: Configuration, -{ /// Constraint System Type type ConstraintSystem: ConstraintSystem + HasVariable< - C::KeyAgreementScheme, - Variable = <Self as identity2::Configuration>::KeyAgreementScheme, - Mode = Constant, - > + HasVariable< - C::CommitmentScheme, - Variable = <Self as identity2::Configuration>::CommitmentScheme, + Self::KeyAgreementScheme, + Variable = KeyAgreementSchemeVar<Self>, Mode = Constant, - > + HasVariable<AssetId, Variable = Self::AssetId, Mode = PublicOrSecret> - + HasVariable<AssetValue, Variable = Self::AssetValue, Mode = PublicOrSecret> + > + HasVariable<Self::CommitmentScheme, Variable = CommitmentSchemeVar<Self>, Mode = Constant> + + HasVariable<Self::UtxoSetVerifier, Variable = UtxoSetVerifierVar<Self>, Mode = Constant> + + HasVariable<AssetId, Variable = AssetIdVar<Self>, Mode = PublicOrSecret> + + HasVariable<AssetValue, Variable = AssetValueVar<Self>, Mode = PublicOrSecret> + HasVariable< - CommitmentSchemeOutput<C>, - Variable = CommitmentSchemeOutput<Self>, + CommitmentSchemeOutput<Self>, + Variable = CommitmentSchemeOutput<Self::ConstraintConfiguration>, Mode = PublicOrSecret, - > + HasEqual<CommitmentSchemeOutput<Self>>; + > + HasEqual<CommitmentSchemeOutput<Self::ConstraintConfiguration>>; + + /// Constraint System Configuration + type ConstraintConfiguration: ConstraintConfiguration<Self::ConstraintSystem>; /// Proof System Type type ProofSystem: ProofSystem<ConstraintSystem = Self::ConstraintSystem, Verification = bool>; +} +/// Transfer Constraint System Configuration +pub trait ConstraintConfiguration<CS>: identity2::Configuration<Asset = AssetVar<CS>> +where + CS: ConstraintSystem + + HasVariable<AssetId, Variable = Self::AssetId, Mode = PublicOrSecret> + + HasVariable<AssetValue, Variable = Self::AssetValue, Mode = PublicOrSecret>, +{ /// Asset Id Variable Type - type AssetId: Variable<Self::ConstraintSystem, Type = AssetId, Mode = PublicOrSecret> - + Equal<Self::ConstraintSystem>; + type AssetId: Variable<CS, Type = AssetId, Mode = PublicOrSecret> + Equal<CS>; /// Asset Value Variable Type - type AssetValue: Variable<Self::ConstraintSystem, Type = AssetValue, Mode = PublicOrSecret> - + Equal<Self::ConstraintSystem> + type AssetValue: Variable<CS, Type = AssetValue, Mode = PublicOrSecret> + + Equal<CS> + Add<Output = Self::AssetValue>; /// UTXO Set Verifier Variable Type - type UtxoSetVerifier: Verifier< - Item = Utxo<Self>, - Verification = <Self::ConstraintSystem as ConstraintSystem>::Bool, - > + Variable<Self::ConstraintSystem, Type = C::UtxoSetVerifier, Mode = Constant>; + type UtxoSetVerifier: Verifier<Item = Utxo<Self>, Verification = CS::Bool>; } +/// Spending Key Type +pub type SpendingKey<C> = + identity2::SpendingKey<<C as identity2::Configuration>::KeyAgreementScheme>; + +/// Receiving Key Type +pub type ReceivingKey<C> = + identity2::ReceivingKey<<C as identity2::Configuration>::KeyAgreementScheme>; + /// Encrypted Note Type pub type EncryptedNote<C> = EncryptedMessage<<C as Configuration>::EncryptionScheme>; +/// Constraint System Type +type ConstraintSystemType<C> = <C as Configuration>::ConstraintSystem; + +/// Constraint Configuration Type +type ConstraintConfigurationType<C> = <C as Configuration>::ConstraintConfiguration; + /// Pre-Sender Type pub type PreSender<C> = identity2::PreSender<C>; /// Sender Type pub type Sender<C> = identity2::Sender<C, <C as Configuration>::UtxoSetVerifier>; +/// Sender Variable Type +type SenderVar<C> = identity2::Sender< + ConstraintConfigurationType<C>, + <ConstraintConfigurationType<C> as ConstraintConfiguration<ConstraintSystemType<C>>>::UtxoSetVerifier, +>; + /// Sender Post Type pub type SenderPost<C> = identity2::SenderPost<C, <C as Configuration>::UtxoSetVerifier>; /// Receiver Type pub type Receiver<C> = identity2::Receiver<C>; +/// Receiver Variable Type +type ReceiverVar<C> = identity2::Receiver<<C as Configuration>::ConstraintConfiguration>; + /// Full Receiver Type pub type FullReceiver<C> = identity2::FullReceiver<C>; /// Receiver Post Type pub type ReceiverPost<C> = identity2::ReceiverPost<C, <C as Configuration>::EncryptionScheme>; +/// Asset Id Variable Type +pub type AssetIdVar<C> = + <ConstraintConfigurationType<C> as ConstraintConfiguration<ConstraintSystemType<C>>>::AssetId; + +/// Asset Value Variable Type +pub type AssetValueVar<C> = <ConstraintConfigurationType<C> as ConstraintConfiguration< + ConstraintSystemType<C>, +>>::AssetValue; + +/// Key Agreement Scheme Variable Type +pub type KeyAgreementSchemeVar<C> = + <ConstraintConfigurationType<C> as identity2::Configuration>::KeyAgreementScheme; + +/// Commitment Scheme Variable Type +pub type CommitmentSchemeVar<C> = + <ConstraintConfigurationType<C> as identity2::Configuration>::CommitmentScheme; + +/// UTXO Set Verifier Variable Type +pub type UtxoSetVerifierVar<C> = <ConstraintConfigurationType<C> as ConstraintConfiguration< + ConstraintSystemType<C>, +>>::UtxoSetVerifier; + /// Transfer Proof System Type -type ProofSystemType<C, P> = <P as ProofSystemConfiguration<C>>::ProofSystem; +type ProofSystemType<C> = <C as Configuration>::ProofSystem; /// Transfer Proof System Error Type -pub type ProofSystemError<C, P> = <ProofSystemType<C, P> as ProofSystem>::Error; +pub type ProofSystemError<C> = <ProofSystemType<C> as ProofSystem>::Error; /// Transfer Proving Context Type -pub type ProvingContext<C, P> = <ProofSystemType<C, P> as ProofSystem>::ProvingContext; +pub type ProvingContext<C> = <ProofSystemType<C> as ProofSystem>::ProvingContext; /// Transfer Verifying Context Type -pub type VerifyingContext<C, P> = <ProofSystemType<C, P> as ProofSystem>::VerifyingContext; +pub type VerifyingContext<C> = <ProofSystemType<C> as ProofSystem>::VerifyingContext; /// Transfer Validity Proof Type -pub type Proof<C, P> = <ProofSystemType<C, P> as ProofSystem>::Proof; +pub type Proof<C> = <ProofSystemType<C> as ProofSystem>::Proof; /// Transfer pub struct Transfer< @@ -248,25 +291,22 @@ where /// Generates the unknown variables for the transfer validity proof. #[inline] - fn unknown_variables<P>( + fn unknown_variables( commitment_scheme: &C::CommitmentScheme, utxo_set_verifier: &C::UtxoSetVerifier, - cs: &mut P::ConstraintSystem, + cs: &mut C::ConstraintSystem, ) -> ( - Option<P::AssetId>, - TransferParticipantsVar<C, P, SOURCES, SENDERS, RECEIVERS, SINKS>, - P::CommitmentScheme, - P::UtxoSetVerifier, - ) - where - P: ProofSystemConfiguration<C>, - { + Option<AssetIdVar<C>>, + TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS>, + CommitmentSchemeVar<C>, + UtxoSetVerifierVar<C>, + ) { let base_asset_id = if has_no_public_participants(SOURCES, SENDERS, RECEIVERS, SINKS) { None } else { - Some(P::AssetId::new_unknown(cs, Public)) + Some(AssetIdVar::<C>::new_unknown(cs, Public)) }; - /* + /* TODO: ( base_asset_id, TransferParticipantsVar::new_unknown(cs, Derived), @@ -279,20 +319,17 @@ where /// Generates the known variables for the transfer validity proof. #[inline] - fn known_variables<P>( + fn known_variables( &self, commitment_scheme: &C::CommitmentScheme, utxo_set_verifier: &C::UtxoSetVerifier, - cs: &mut P::ConstraintSystem, + cs: &mut C::ConstraintSystem, ) -> ( - Option<P::AssetId>, - TransferParticipantsVar<C, P, SOURCES, SENDERS, RECEIVERS, SINKS>, - P::CommitmentScheme, - P::UtxoSetVerifier, - ) - where - P: ProofSystemConfiguration<C>, - { + Option<AssetIdVar<C>>, + TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS>, + CommitmentSchemeVar<C>, + UtxoSetVerifierVar<C>, + ) { /* TODO: ( self.public.asset_id.map(|id| id.as_known(cs, Public)), @@ -306,15 +343,13 @@ where /// Builds constraints for the transfer validity proof. #[inline] - fn build_constraints<P>( - base_asset_id: Option<P::AssetId>, - participants: TransferParticipantsVar<C, P, SOURCES, SENDERS, RECEIVERS, SINKS>, - commitment_scheme: P::CommitmentScheme, - utxo_set_verifier: P::UtxoSetVerifier, - cs: &mut P::ConstraintSystem, - ) where - P: ProofSystemConfiguration<C>, - { + fn build_constraints( + base_asset_id: Option<AssetIdVar<C>>, + participants: TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS>, + commitment_scheme: CommitmentSchemeVar<C>, + utxo_set_verifier: UtxoSetVerifierVar<C>, + cs: &mut C::ConstraintSystem, + ) { let mut secret_asset_ids = Vec::with_capacity(SENDERS + RECEIVERS); let input_sum = participants @@ -351,16 +386,13 @@ where /// Generates the constraint system for an unknown transfer. #[inline] - pub fn unknown_constraints<P>( + pub fn unknown_constraints( commitment_scheme: &C::CommitmentScheme, utxo_set_verifier: &C::UtxoSetVerifier, - ) -> P::ConstraintSystem - where - P: ProofSystemConfiguration<C>, - { - let mut cs = P::ProofSystem::for_unknown(); + ) -> C::ConstraintSystem { + let mut cs = C::ProofSystem::for_unknown(); let (base_asset_id, participants, commitment_scheme, utxo_set_verifier) = - Self::unknown_variables::<P>(commitment_scheme, utxo_set_verifier, &mut cs); + Self::unknown_variables(commitment_scheme, utxo_set_verifier, &mut cs); Self::build_constraints( base_asset_id, participants, @@ -373,17 +405,14 @@ where /// Generates the constraint system for a known transfer. #[inline] - pub fn known_constraints<P>( + pub fn known_constraints( &self, commitment_scheme: &C::CommitmentScheme, utxo_set_verifier: &C::UtxoSetVerifier, - ) -> P::ConstraintSystem - where - P: ProofSystemConfiguration<C>, - { - let mut cs = P::ProofSystem::for_known(); + ) -> C::ConstraintSystem { + let mut cs = C::ProofSystem::for_known(); let (base_asset_id, participants, commitment_scheme, utxo_set_verifier) = - self.known_variables::<P>(commitment_scheme, utxo_set_verifier, &mut cs); + self.known_variables(commitment_scheme, utxo_set_verifier, &mut cs); Self::build_constraints( base_asset_id, participants, @@ -396,56 +425,48 @@ where /// Generates a proving and verifying context for this transfer shape. #[inline] - pub fn generate_context<P, R>( + pub fn generate_context<R>( commitment_scheme: &C::CommitmentScheme, utxo_set_verifier: &C::UtxoSetVerifier, rng: &mut R, - ) -> Result<(ProvingContext<C, P>, VerifyingContext<C, P>), ProofSystemError<C, P>> + ) -> Result<(ProvingContext<C>, VerifyingContext<C>), ProofSystemError<C>> where - P: ProofSystemConfiguration<C>, R: CryptoRng + RngCore + ?Sized, { - Self::unknown_constraints::<P>(commitment_scheme, utxo_set_verifier) - .generate_context::<P::ProofSystem, _>(rng) + Self::unknown_constraints(commitment_scheme, utxo_set_verifier) + .generate_context::<C::ProofSystem, _>(rng) } /// Generates a validity proof for this transfer. #[inline] - pub fn is_valid<P, R>( + pub fn is_valid<R>( &self, commitment_scheme: &C::CommitmentScheme, utxo_set_verifier: &C::UtxoSetVerifier, - context: &ProvingContext<C, P>, + context: &ProvingContext<C>, rng: &mut R, - ) -> Result<Proof<C, P>, ProofSystemError<C, P>> + ) -> Result<Proof<C>, ProofSystemError<C>> where - P: ProofSystemConfiguration<C>, R: CryptoRng + RngCore + ?Sized, { - self.known_constraints::<P>(commitment_scheme, utxo_set_verifier) - .prove::<P::ProofSystem, _>(context, rng) + self.known_constraints(commitment_scheme, utxo_set_verifier) + .prove::<C::ProofSystem, _>(context, rng) } /// Converts `self` into its ledger post. #[inline] - pub fn into_post<P, R>( + pub fn into_post<R>( self, commitment_scheme: &C::CommitmentScheme, utxo_set_verifier: &C::UtxoSetVerifier, - context: &ProvingContext<C, P>, + context: &ProvingContext<C>, rng: &mut R, - ) -> Result<TransferPost<C, P>, ProofSystemError<C, P>> + ) -> Result<TransferPost<C>, ProofSystemError<C>> where - P: ProofSystemConfiguration<C>, R: CryptoRng + RngCore + ?Sized, { Ok(TransferPost { - validity_proof: self.is_valid::<P, _>( - commitment_scheme, - utxo_set_verifier, - context, - rng, - )?, + validity_proof: self.is_valid(commitment_scheme, utxo_set_verifier, context, rng)?, asset_id: self.asset_id, sources: self.sources.into(), sender_posts: IntoIterator::into_iter(self.senders) @@ -462,47 +483,37 @@ where /// Transfer Participants Variable struct TransferParticipantsVar< C, - P, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize, > where C: Configuration, - P: ProofSystemConfiguration<C>, { /// Source Variables - sources: Vec<P::AssetValue>, + sources: Vec<AssetValueVar<C>>, /// Sender Variables - senders: Vec<identity2::Sender<P, P::UtxoSetVerifier>>, + senders: Vec<SenderVar<C>>, /// Receiver Variables - receivers: Vec<identity2::Receiver<P>>, + receivers: Vec<ReceiverVar<C>>, /// Sink Variables - sinks: Vec<P::AssetValue>, + sinks: Vec<AssetValueVar<C>>, } -impl< - C, - P, - const SOURCES: usize, - const SENDERS: usize, - const RECEIVERS: usize, - const SINKS: usize, - > Variable<P::ConstraintSystem> - for TransferParticipantsVar<C, P, SOURCES, SENDERS, RECEIVERS, SINKS> +impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> + Variable<C::ConstraintSystem> for TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> where C: Configuration, - P: ProofSystemConfiguration<C>, { type Type = Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>; type Mode = Derived; #[inline] - fn new(cs: &mut P::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self { sources: this @@ -535,7 +546,7 @@ where Allocation::Unknown(mode) => Self { sources: (0..SOURCES) .into_iter() - .map(|_| P::AssetValue::new_unknown(cs, Public)) + .map(|_| AssetValueVar::<C>::new_unknown(cs, Public)) .collect(), senders: (0..SENDERS) .into_iter() @@ -553,7 +564,7 @@ where .collect(), sinks: (0..SINKS) .into_iter() - .map(|_| P::AssetValue::new_unknown(cs, Public)) + .map(|_| AssetValueVar::<C>::new_unknown(cs, Public)) .collect(), }, } @@ -561,10 +572,9 @@ where } /// Transfer Post -pub struct TransferPost<C, P> +pub struct TransferPost<C> where C: Configuration, - P: ProofSystemConfiguration<C>, { /// Asset Id asset_id: Option<AssetId>, @@ -582,7 +592,7 @@ where sinks: Vec<AssetValue>, /// Validity Proof - validity_proof: Proof<C, P>, + validity_proof: Proof<C>, } create_seal! {} @@ -609,7 +619,6 @@ pub trait Shape: sealed::Sealed { /// Canonical Transaction Types pub mod canonical { use super::*; - use crate::identity2::ReceivingKey; use manta_util::seal; /// Implements [`Shape`] for a given shape type. @@ -752,23 +761,23 @@ pub mod canonical { } /// Canonical Transaction Type - pub enum Transaction<K> + pub enum Transaction<C> where - K: KeyAgreementScheme, + C: Configuration, { /// Mint Private Asset Mint(Asset), /// Private Transfer Asset to Receiver - PrivateTransfer(Asset, ReceivingKey<K>), + PrivateTransfer(Asset, ReceivingKey<C>), /// Reclaim Private Asset Reclaim(Asset), } - impl<K> Transaction<K> + impl<C> Transaction<C> where - K: KeyAgreementScheme, + C: Configuration, { /// Checks that `self` can be executed for a given `balance` state, returning the /// transaction kind if successful, and returning the asset back if the balance was diff --git a/manta-accounting/src/wallet2/ledger.rs b/manta-accounting/src/wallet2/ledger.rs new file mode 100644 index 000000000..30ea0d3c8 --- /dev/null +++ b/manta-accounting/src/wallet2/ledger.rs @@ -0,0 +1,146 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Ledger Source + +// TODO: Move to asynchronous streaming model so we can process some of the data as it is incoming. +// TODO: Add non-atomic transactions. See similar comment in `crate::wallet::signer`. + +use crate::{ + identity2::{Utxo, VoidNumber}, + transfer2::{Configuration, EncryptedNote, TransferPost}, +}; +use alloc::vec::Vec; +use core::future::Future; + +/// Ledger Checkpoint +pub trait Checkpoint: Default + PartialOrd { + /// Returns the number of receivers that have participated in transactions on the ledger so far. + fn receiver_index(&self) -> usize; + + /// Returns the number of senders that have participated in transactions on the ledger so far. + fn sender_index(&self) -> usize; +} + +/// Ledger Source Connection +pub trait Connection<C> +where + C: Configuration, +{ + /// Ledger State Checkpoint Type + type Checkpoint: Checkpoint; + + /// Receiver Data Iterator Type + type ReceiverData: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>; + + /// Sender Data Iterator Type + type SenderData: IntoIterator<Item = VoidNumber<C>>; + + /// Pull Future Type + /// + /// Future for the [`pull`](Self::pull) method. + type PullFuture: Future<Output = PullResult<C, Self>>; + + /// Pull All Future Type + /// + /// Future for the [`pull_all`](Self::pull_all) method. + type PullAllFuture: Future<Output = PullAllResult<C, Self>>; + + /// Push Future Type + /// + /// Future for the [`push`](Self::push) method. + type PushFuture: Future<Output = PushResult<C, Self>>; + + /// Error Type + type Error; + + /// Pulls receiver data from the ledger starting from `checkpoint`, returning the current + /// [`Checkpoint`](Self::Checkpoint). + fn pull(&self, checkpoint: &Self::Checkpoint) -> Self::PullFuture; + + /// Pulls all data from the ledger, returning the current [`Checkpoint`](Self::Checkpoint). + fn pull_all(&self) -> Self::PullAllFuture; + + /// Sends `posts` to the ledger to be appended atomically, returning `false` if the posts were + /// invalid. + fn push(&self, posts: Vec<TransferPost<C>>) -> Self::PushFuture; +} + +/// Ledger Source Pull Result +/// +/// See the [`pull`](Connection::pull) method on [`Connection`] for more information. +pub type PullResult<C, L> = Result<PullResponse<C, L>, <L as Connection<C>>::Error>; + +/// Ledger Source Pull All Result +/// +/// See the [`pull_all`](Connection::pull_all) method on [`Connection`] for more information. +pub type PullAllResult<C, L> = Result<PullAllResponse<C, L>, <L as Connection<C>>::Error>; + +/// Ledger Source Push Result +/// +/// See the [`push`](Connection::push) method on [`Connection`] for more information. +pub type PushResult<C, L> = Result<PushResponse<C, L>, <L as Connection<C>>::Error>; + +/// Ledger Source Pull Response +/// +/// This `struct` is created by the [`pull`](Connection::pull) method on [`Connection`]. +/// See its documentation for more. +pub struct PullResponse<C, L> +where + C: Configuration, + L: Connection<C> + ?Sized, +{ + /// Current Ledger Checkpoint + pub checkpoint: L::Checkpoint, + + /// Ledger Receiver Data + pub receiver_data: L::ReceiverData, +} + +/// Ledger Source Pull All Response +/// +/// This `struct` is created by the [`pull_all`](Connection::pull_all) method on [`Connection`]. +/// See its documentation for more. +pub struct PullAllResponse<C, L> +where + C: Configuration, + L: Connection<C> + ?Sized, +{ + /// Current Ledger Checkpoint + pub checkpoint: L::Checkpoint, + + /// Ledger Receiver Data + pub receiver_data: L::ReceiverData, + + /// Ledger Sender Data + pub sender_data: L::SenderData, +} + +/// Ledger Source Push Response +/// +/// This `struct` is created by the [`push`](Connection::push) method on [`Connection`]. +/// See its documentation for more. +pub struct PushResponse<C, L> +where + C: Configuration, + L: Connection<C> + ?Sized, +{ + /// Current Ledger Checkpoint + pub checkpoint: L::Checkpoint, + + /// Successful Push + pub success: bool, +} diff --git a/manta-accounting/src/wallet2/mod.rs b/manta-accounting/src/wallet2/mod.rs new file mode 100644 index 000000000..e38a5adc6 --- /dev/null +++ b/manta-accounting/src/wallet2/mod.rs @@ -0,0 +1,28 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Wallet Abstractions + +mod state; + +pub mod ledger; +pub mod signer; + +#[cfg(feature = "test")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] +pub mod test; + +pub use state::*; diff --git a/manta-accounting/src/wallet2/signer.rs b/manta-accounting/src/wallet2/signer.rs new file mode 100644 index 000000000..1e89958e7 --- /dev/null +++ b/manta-accounting/src/wallet2/signer.rs @@ -0,0 +1,1249 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Wallet Signer + +// FIXME: Change the name of `TransferAccumulator`, its not an `Accumulator`. +// TODO: Add wallet recovery i.e. remove the assumption that a new signer represents a completely +// new derived secret key generator. +// TODO: Allow for non-atomic signing, i.e. rollback state to something in-between two calls to +// sign`. Will have to upgrade `Rollback` and `manta_crypto::merkle_tree::fork` as well. +// TODO: Add checkpointing/garbage-collection in `utxo_set` so we can remove old UTXOs once they +// are irrelevant. Once we create a sender and its transaction succeeds we can drop the UTXO. +// TODO: Should have a mode on the signer where we return a generic error which reveals no detail +// about what went wrong during signing. The kind of error returned from a signing could +// reveal information about the internal state (privacy leak, not a secrecy leak). + +use crate::{ + asset::{Asset, AssetId, AssetMap, AssetValue}, + fs::{Load, LoadWith, Save, SaveWith}, + identity2::{self, PreSender, PublicKey, SecretKey, Utxo}, + key2::{Account, HierarchicalKeyTable}, + transfer2::{ + self, + canonical::{Mint, PrivateTransfer, PrivateTransferShape, Reclaim, Transaction}, + EncryptedNote, FullReceiver, ProofSystemError, ProvingContext, Receiver, ReceivingKey, + Sender, Shape, Transfer, TransferPost, + }, +}; +use alloc::{vec, vec::Vec}; +use core::{ + convert::Infallible, + fmt::Debug, + future::{self, Future, Ready}, + hash::Hash, + mem, + ops::Range, +}; +use manta_crypto::{ + accumulator::{ + Accumulator, ConstantCapacityAccumulator, ExactSizeAccumulator, OptimizedAccumulator, + Verifier, + }, + key::KeyAgreementScheme, + rand::{CryptoRng, RngCore}, +}; +use manta_util::{fallible_array_map, into_array_unchecked, iter::IteratorExt}; + +/// Rollback Trait +pub trait Rollback { + /// Commits `self` to the current state. + /// + /// # Implementation Note + /// + /// Commiting to the current state must be idempotent. Calling [`rollback`](Self::rollback) + /// after [`commit`](Self::commit) must not change the state after the call to + /// [`commit`](Self::commit). + fn commit(&mut self); + + /// Rolls back `self` to the previous state. + /// + /// # Implementation Note + /// + /// Rolling back to the previous state must be idempotent. Calling [`commit`](Self::commit) + /// after [`rollback`](Self::rollback) must not change the state after the call to + /// [`rollback`](Self::rollback). + fn rollback(&mut self); +} + +/// Signer Connection +pub trait Connection<H, C> +where + H: HierarchicalKeyTable<SecretKey = SecretKey<C>>, + C: transfer2::Configuration, +{ + /// Sync Future Type + /// + /// Future for the [`sync`](Self::sync) method. + type SyncFuture: Future<Output = SyncResult<H, C, Self>>; + + /// Sign Future Type + /// + /// Future for the [`sign`](Self::sign) method. + type SignFuture: Future<Output = SignResult<H, C, Self>>; + + /// Sign Commit Future Type + /// + /// Future for the [`commit`](Self::commit) method. + type CommitFuture: Future<Output = Result<(), Self::Error>>; + + /// Sign Rollback Future Type + /// + /// Future for the [`rollback`](Self::rollback) method. + type RollbackFuture: Future<Output = Result<(), Self::Error>>; + + /// Receiver Future Type + /// + /// Future for the [`receiver`](Self::receiver) method. + type ReceiverFuture: Future<Output = ReceiverResult<H, C, Self>>; + + /// Error Type + type Error; + + /// Pushes updates from the ledger to the wallet, synchronizing it with the ledger state and + /// returning an updated asset distribution. Depending on the `sync_state`, the signer will + /// either commit to the current state before synchronizing or rollback to the previous state. + fn sync<I>( + &mut self, + sync_state: SyncState, + starting_index: usize, + updates: I, + ) -> Self::SyncFuture + where + I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>; + + /// Signs a `transaction` and returns the ledger transfer posts if successful. + /// + /// # Safety + /// + /// To preserve consistency, calls to [`sign`](Self::sign) should be followed by a call to + /// either [`commit`](Self::commit), [`rollback`](Self::rollback), or [`sync`](Self::sync) with + /// the appropriate [`SyncState`]. Repeated calls to [`sign`](Self::sign) should automatically + /// commit the current state before signing. + /// + /// See the [`Rollback`] trait for expectations on the behavior of [`commit`](Self::commit) + /// and [`rollback`](Self::rollback). + fn sign(&mut self, transaction: Transaction<C>) -> Self::SignFuture; + + /// Commits to the state after the last call to [`sign`](Self::sign). + /// + /// See the [`Rollback`] trait for expectations on the behavior of [`commit`](Self::commit). + fn commit(&mut self) -> Self::CommitFuture; + + /// Rolls back to the state before the last call to [`sign`](Self::sign). + /// + /// See the [`Rollback`] trait for expectations on the behavior of [`rollback`](Self::rollback). + fn rollback(&mut self) -> Self::RollbackFuture; + + /// Returns a [`ReceivingKey`] for `self` to receive assets. + /// + /// # Note + /// + /// This method does not interact with the other methods on [`Connection`] so it can be called + /// at any point in between calls to [`sync`](Self::sync), [`sign`](Self::sign), and other + /// rollback related methods. + fn receiver(&mut self) -> Self::ReceiverFuture; +} + +/// Synchronization State +/// +/// This `enum` is used by the [`sync`](Connection::sync) method on [`Connection`]. See its +/// documentation for more. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum SyncState { + /// Should commit the current state before synchronizing + Commit, + + /// Should rollback to the previous state before synchronizing + Rollback, +} + +/// Synchronization Result +/// +/// See the [`sync`](Connection::sync) method on [`Connection`] for more information. +pub type SyncResult<H, C, S> = Result<SyncResponse, Error<H, C, <S as Connection<H, C>>::Error>>; + +/// Signing Result +/// +/// See the [`sign`](Connection::sign) method on [`Connection`] for more information. +pub type SignResult<H, C, S> = Result<SignResponse<C>, Error<H, C, <S as Connection<H, C>>::Error>>; + +/// Receiver Result +/// +/// See the [`receiver`](Connection::receiver) method on [`Connection`] for more information. +pub type ReceiverResult<H, C, S> = + Result<ReceivingKey<C>, Error<H, C, <S as Connection<H, C>>::Error>>; + +/// Signer Synchronization Response +/// +/// This `struct` is created by the [`sync`](Connection::sync) method on [`Connection`]. +/// See its documentation for more. +pub struct SyncResponse { + /// Updates to the Asset Distribution + pub assets: Vec<Asset>, +} + +impl SyncResponse { + /// Builds a new [`SyncResponse`] from `assets`. + #[inline] + pub fn new(assets: Vec<Asset>) -> Self { + Self { assets } + } +} + +/// Signer Signing Response +/// +/// This `struct` is created by the [`sign`](Connection::sign) method on [`Connection`]. +/// See its documentation for more. +pub struct SignResponse<C> +where + C: transfer2::Configuration, +{ + /// Transfer Posts + pub posts: Vec<TransferPost<C>>, +} + +impl<C> SignResponse<C> +where + C: transfer2::Configuration, +{ + /// Builds a new [`SignResponse`] from `posts`. + #[inline] + pub fn new(posts: Vec<TransferPost<C>>) -> Self { + Self { posts } + } +} + +/// Signer Error +pub enum Error<H, C, CE = Infallible> +where + H: HierarchicalKeyTable<SecretKey = SecretKey<C>>, + C: transfer2::Configuration, +{ + /// Hierarchical Key Table Error + HierarchicalKeyTableError(H::Error), + + /// Missing [`Utxo`] Membership Proof + MissingUtxoMembershipProof, + + /// Insufficient Balance + InsufficientBalance(Asset), + + /// Proof System Error + ProofSystemError(ProofSystemError<C>), + + /// Inconsistent Synchronization State + InconsistentSynchronization, + + /// Signer Connection Error + ConnectionError(CE), +} + +/// Signer Configuration +pub trait Configuration: transfer2::Configuration { + /// Hierarchical Key Table + type HierarchicalKeyTable: HierarchicalKeyTable<SecretKey = SecretKey<Self>>; + + /// [`Utxo`] Accumulator Type + type UtxoSet: Accumulator< + Item = <Self::UtxoSetVerifier as Verifier>::Item, + Verifier = Self::UtxoSetVerifier, + > + ConstantCapacityAccumulator + + ExactSizeAccumulator + + OptimizedAccumulator + + Rollback; + + /// Asset Map Type + type AssetMap: AssetMap<Key = PublicKey<Self>>; + + /// Random Number Generator Type + type Rng: CryptoRng + RngCore; +} + +/// Pending Asset Map +#[derive(derivative::Derivative)] +#[derivative(Default(bound = ""))] +struct PendingAssetMap<C> +where + C: Configuration, +{ + /// Pending Insert Data + insert: Option<(PublicKey<C>, Asset)>, + + /// Pending Insert Zeroes Data + insert_zeroes: Option<(AssetId, Vec<PublicKey<C>>)>, + + /// Pending Remove Data + remove: Vec<PublicKey<C>>, +} + +impl<C> PendingAssetMap<C> +where + C: Configuration, +{ + /// Commits the pending asset map data to `assets`. + #[inline] + fn commit<M>(&mut self, assets: &mut M) + where + M: AssetMap<Key = PublicKey<C>>, + { + if let Some((key, asset)) = self.insert.take() { + assets.insert(key, asset); + } + if let Some((asset_id, zeroes)) = self.insert_zeroes.take() { + assets.insert_zeroes(asset_id, zeroes); + } + assets.remove_all(mem::take(&mut self.remove)); + } + + /// Clears the pending asset map. + #[inline] + fn rollback(&mut self) { + *self = Default::default(); + } +} + +/// Signer +pub struct Signer<C> +where + C: Configuration, +{ + /// Account Keys + account: Account<C::HierarchicalKeyTable>, + + /// Commitment Scheme + commitment_scheme: C::CommitmentScheme, + + /// Proving Context + proving_context: ProvingContext<C>, + + /// UTXO Set + utxo_set: C::UtxoSet, + + /// Asset Distribution + assets: C::AssetMap, + + /// Pending Asset Distribution + pending_assets: PendingAssetMap<C>, + + /// Random Number Generator + rng: C::Rng, +} + +impl<C> Signer<C> +where + C: Configuration, +{ + /// + #[inline] + fn new_inner( + account: Account<C::HierarchicalKeyTable>, + commitment_scheme: C::CommitmentScheme, + proving_context: ProvingContext<C>, + utxo_set: C::UtxoSet, + assets: C::AssetMap, + pending_assets: PendingAssetMap<C>, + rng: C::Rng, + ) -> Self { + Self { + account, + commitment_scheme, + proving_context, + utxo_set, + assets, + pending_assets, + rng, + } + } + + /// + #[inline] + pub fn new( + account: Account<C::HierarchicalKeyTable>, + commitment_scheme: C::CommitmentScheme, + proving_context: ProvingContext<C>, + rng: C::Rng, + ) -> Self + where + C::UtxoSet: Default, + { + Self::new_inner( + account, + commitment_scheme, + proving_context, + Default::default(), + Default::default(), + Default::default(), + rng, + ) + } + + /// Updates the internal ledger state, returning the new asset distribution. + #[inline] + fn sync_inner<I>(&mut self, updates: I) -> SyncResult<C::HierarchicalKeyTable, C, Self> + where + I: Iterator<Item = (Utxo<C>, EncryptedNote<C>)>, + { + /* TODO: + let mut assets = Vec::new(); + for (utxo, encrypted_asset) in updates { + if let Some(KeyOwned { inner, index }) = + self.signer.find_external_asset::<C>(&encrypted_asset) + { + // FIXME: We need to actually check at this point whether the `utxo` is valid, by + // computing the UTXO that lives at the `Sender` of `index`, this way, a + // future call to `try_upgrade` will never fail. If the call to `try_upgrade` + // fails, we need to mark the coin as burnt, or it will show up again in + // later calls to the signer (during coin selection). Currently, if the + // `utxo` does not match it should be stored in the verified set as + // non-provable and the asset should not be added to the asset map, since the + // asset is effectively burnt. + assets.push(inner); + self.assets.insert(index.reduce(), inner); + self.utxo_set.insert(&utxo); + } else { + self.utxo_set.insert_nonprovable(&utxo); + } + } + Ok(SyncResponse::new(assets)) + */ + todo!() + } + + /// Updates the internal ledger state, returning the new asset distribution. + #[inline] + pub fn sync<I>( + &mut self, + sync_state: SyncState, + starting_index: usize, + updates: I, + ) -> SyncResult<C::HierarchicalKeyTable, C, Self> + where + I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, + { + /* TODO: + self.start_sync(sync_state); + + // FIXME: Do a capacity check on the current UTXO set. + match self.utxo_set.len().checked_sub(starting_index) { + Some(diff) => self.sync_inner(updates.into_iter().skip(diff)), + _ => Err(Error::InconsistentSynchronization), + } + */ + todo!() + } + + /// Selects the pre-senders which collectively own at least `asset`, returning any change. + #[inline] + fn select(&mut self, asset: Asset) -> Result<Selection<C>, Error<C::HierarchicalKeyTable, C>> { + /* TODO: + let selection = self.assets.select(asset); + if selection.is_empty() { + return Err(Error::InsufficientBalance(asset)); + } + self.pending_assets.remove = selection.keys().cloned().collect(); + let pre_senders = selection + .values + .into_iter() + .map(move |(k, v)| self.get_pre_sender(k, asset.id.with(v))) + .collect::<Result<_, _>>()?; + Ok(Selection::new(selection.change, pre_senders)) + */ + todo!() + } + + /// Builds a [`TransferPost`] for the given `transfer`. + #[inline] + fn build_post< + const SOURCES: usize, + const SENDERS: usize, + const RECEIVERS: usize, + const SINKS: usize, + >( + &mut self, + transfer: impl Into<Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>>, + ) -> Result<TransferPost<C>, Error<C::HierarchicalKeyTable, C>> { + transfer + .into() + .into_post( + &self.commitment_scheme, + self.utxo_set.verifier(), + &self.proving_context, + &mut self.rng, + ) + .map_err(Error::ProofSystemError) + } + + /* TODO: + /// Accumulate transfers using the `SENDERS -> RECEIVERS` shape. + #[inline] + fn accumulate_transfers<const SENDERS: usize, const RECEIVERS: usize>( + &mut self, + asset_id: AssetId, + mut pre_senders: Vec<PreSender<C>>, + posts: &mut Vec<TransferPost<C>>, + ) -> Result<[Sender<C>; SENDERS], Error<C::DerivedSecretKeyGenerator, C>> { + assert!( + (SENDERS > 1) && (RECEIVERS > 1), + "The transfer shape must include at least two senders and two receivers." + ); + assert!( + !pre_senders.is_empty(), + "The set of initial senders cannot be empty." + ); + + let mut new_zeroes = Vec::new(); + + while pre_senders.len() > SENDERS { + let mut accumulators = Vec::new(); + let mut iter = pre_senders.into_iter().chunk_by::<SENDERS>(); + for chunk in &mut iter { + let senders = fallible_array_map(chunk, |ps| { + ps.try_upgrade(&self.utxo_set) + .ok_or(Error::MissingUtxoMembershipProof) + })?; + + let mut accumulator = self.signer.next_accumulator::<_, _, RECEIVERS>( + &self.commitment_scheme, + asset_id, + senders.iter().map(Sender::asset_value).sum(), + &mut self.rng, + )?; + + posts.push(self.build_post(SecretTransfer::new(senders, accumulator.receivers))?); + + for zero in &accumulator.zeroes { + zero.as_ref().insert_utxo(&mut self.utxo_set); + } + accumulator.pre_sender.insert_utxo(&mut self.utxo_set); + + new_zeroes.append(&mut accumulator.zeroes); + accumulators.push(accumulator.pre_sender); + } + + accumulators.append(&mut iter.remainder()); + pre_senders = accumulators; + } + + self.prepare_final_pre_senders::<SENDERS>(asset_id, new_zeroes, &mut pre_senders, posts)?; + + Ok(into_array_unchecked( + pre_senders + .into_iter() + .map(move |ps| ps.try_upgrade(&self.utxo_set)) + .collect::<Option<Vec<_>>>() + .ok_or(Error::MissingUtxoMembershipProof)?, + )) + } + + /// Prepare final pre-senders for the transaction. + #[inline] + fn prepare_final_pre_senders<const SENDERS: usize>( + &mut self, + asset_id: AssetId, + mut new_zeroes: Vec<InternalKeyOwned<C::DerivedSecretKeyGenerator, PreSender<C>>>, + pre_senders: &mut Vec<PreSender<C>>, + posts: &mut Vec<TransferPost<C>>, + ) -> Result<(), Error<C::DerivedSecretKeyGenerator, C>> { + let mut needed_zeroes = SENDERS - pre_senders.len(); + if needed_zeroes == 0 { + return Ok(()); + } + + let zeroes = self.assets.zeroes(needed_zeroes, asset_id); + needed_zeroes -= zeroes.len(); + + for zero in zeroes { + pre_senders.push(self.get_pre_sender(zero, Asset::zero(asset_id))?); + } + + if needed_zeroes == 0 { + return Ok(()); + } + + let needed_mints = needed_zeroes.saturating_sub(new_zeroes.len()); + + for _ in 0..needed_zeroes { + match new_zeroes.pop() { + Some(zero) => pre_senders.push(zero.unwrap()), + _ => break, + } + } + + self.pending_assets.insert_zeroes = Some(( + asset_id, + new_zeroes.into_iter().map(move |z| z.index).collect(), + )); + + if needed_mints == 0 { + return Ok(()); + } + + for _ in 0..needed_mints { + let (mint, pre_sender) = + self.signer + .mint_zero(&self.commitment_scheme, asset_id, &mut self.rng)?; + pre_senders.push(pre_sender); + posts.push(self.build_post(mint)?); + } + + Ok(()) + } + + /// Returns the next change receiver for `asset`. + #[inline] + fn next_change( + &mut self, + asset_id: AssetId, + change: AssetValue, + ) -> Result<Receiver<C>, Error<C::DerivedSecretKeyGenerator, C>> { + let asset = asset_id.with(change); + let (receiver, index) = self + .signer + .next_change(&self.commitment_scheme, asset, &mut self.rng)? + .into(); + self.pending_assets.insert = Some((index, asset)); + Ok(receiver) + } + + /// Prepares a given [`ShieldedIdentity`] for receiving `asset`. + #[inline] + pub fn prepare_receiver( + &mut self, + asset: Asset, + receiver: ShieldedIdentity<C>, + ) -> Result<Receiver<C>, Error<C::DerivedSecretKeyGenerator, C>> { + receiver + .into_receiver(&self.commitment_scheme, asset, &mut self.rng) + .map_err(Error::EncryptionError) + } + */ + + /// Signs a withdraw transaction without resetting on error. + #[inline] + fn sign_withdraw_inner( + &mut self, + asset: Asset, + receiver: Option<ReceivingKey<C>>, + ) -> SignResult<C::HierarchicalKeyTable, C, Self> { + /* TODO: + const SENDERS: usize = PrivateTransferShape::SENDERS; + const RECEIVERS: usize = PrivateTransferShape::RECEIVERS; + + let selection = self.select(asset)?; + + let mut posts = Vec::new(); + let senders = self.accumulate_transfers::<SENDERS, RECEIVERS>( + asset.id, + selection.pre_senders, + &mut posts, + )?; + + let change = self.next_change(asset.id, selection.change)?; + let final_post = match receiver { + Some(receiver) => { + let receiver = self.prepare_receiver(asset, receiver)?; + self.build_post(PrivateTransfer::build(senders, [change, receiver]))? + } + _ => self.build_post(Reclaim::build(senders, [change], asset))?, + }; + + posts.push(final_post); + + Ok(SignResponse::new(posts)) + */ + todo!() + } + + /// Signs a withdraw transaction, resetting the internal state on an error. + #[inline] + fn sign_withdraw( + &mut self, + asset: Asset, + receiver: Option<ReceivingKey<C>>, + ) -> SignResult<C::HierarchicalKeyTable, C, Self> { + let result = self.sign_withdraw_inner(asset, receiver); + if result.is_err() { + self.rollback(); + } + result + } + + /// Signs the `transaction`, generating transfer posts. + #[inline] + pub fn sign( + &mut self, + transaction: Transaction<C>, + ) -> SignResult<C::HierarchicalKeyTable, C, Self> { + self.commit(); + match transaction { + Transaction::Mint(asset) => { + /* TODO: + let (mint, owner) = self + .signer + .mint(&self.commitment_scheme, asset, &mut self.rng)? + .into(); + let mint_post = self.build_post(mint)?; + self.pending_assets.insert = Some((owner, asset)); + Ok(SignResponse::new(vec![mint_post])) + */ + todo!() + } + Transaction::PrivateTransfer(asset, receiver) => { + self.sign_withdraw(asset, Some(receiver)) + } + Transaction::Reclaim(asset) => self.sign_withdraw(asset, None), + } + } + + /// Commits to the state after the last call to [`sign`](Self::sign). + #[inline] + pub fn commit(&mut self) { + /* TODO: + self.signer.account.internal_range_shift_to_end(); + self.utxo_set.commit(); + self.pending_assets.commit(&mut self.assets); + */ + todo!() + } + + /// Rolls back to the state before the last call to [`sign`](Self::sign). + #[inline] + pub fn rollback(&mut self) { + /* TODO: + self.signer.account.internal_range_shift_to_start(); + self.utxo_set.rollback(); + self.pending_assets.rollback(); + */ + todo!() + } + + /// Commits or rolls back the state depending on the value of `sync_state`. + #[inline] + pub fn start_sync(&mut self, sync_state: SyncState) { + match sync_state { + SyncState::Commit => self.commit(), + SyncState::Rollback => self.rollback(), + } + } + + /// Generates a new [`ReceivingKey`] for `self` to receive assets. + #[inline] + pub fn receiver(&mut self) -> ReceiverResult<C::HierarchicalKeyTable, C, Self> { + /* TODO: + self.signer + .next_shielded(&self.commitment_scheme) + .map_err(Error::SecretKeyError) + */ + todo!() + } +} + +impl<C> Connection<C::HierarchicalKeyTable, C> for Signer<C> +where + C: Configuration, +{ + type SyncFuture = Ready<SyncResult<C::HierarchicalKeyTable, C, Self>>; + + type SignFuture = Ready<SignResult<C::HierarchicalKeyTable, C, Self>>; + + type CommitFuture = Ready<Result<(), Self::Error>>; + + type RollbackFuture = Ready<Result<(), Self::Error>>; + + type ReceiverFuture = Ready<ReceiverResult<C::HierarchicalKeyTable, C, Self>>; + + type Error = Infallible; + + #[inline] + fn sync<I>( + &mut self, + sync_state: SyncState, + starting_index: usize, + updates: I, + ) -> Self::SyncFuture + where + I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, + { + future::ready(self.sync(sync_state, starting_index, updates)) + } + + #[inline] + fn sign(&mut self, transaction: Transaction<C>) -> Self::SignFuture { + future::ready(self.sign(transaction)) + } + + #[inline] + fn commit(&mut self) -> Self::CommitFuture { + future::ready({ + self.commit(); + Ok(()) + }) + } + + #[inline] + fn rollback(&mut self) -> Self::RollbackFuture { + future::ready({ + self.rollback(); + Ok(()) + }) + } + + #[inline] + fn receiver(&mut self) -> Self::ReceiverFuture { + future::ready(self.receiver()) + } +} + +/// Pre-Sender Selection +struct Selection<C> +where + C: transfer2::Configuration, +{ + /// Selection Change + pub change: AssetValue, + + /// Selection Pre-Senders + pub pre_senders: Vec<PreSender<C>>, +} + +impl<C> Selection<C> +where + C: transfer2::Configuration, +{ + /// Builds a new [`Selection`] from `change` and `pre_senders`. + #[inline] + pub fn new(change: AssetValue, pre_senders: Vec<PreSender<C>>) -> Self { + Self { + change, + pre_senders, + } + } +} + +/* TODO: +impl<D> Signer<D> +where + D: DerivedSecretKeyGenerator, +{ + /// Builds a new [`Signer`] for `account` from a `secret_key_source`. + #[inline] + pub fn new(secret_key_source: D, account: D::Account) -> Self { + Self::with_account(secret_key_source, Account::new(account)) + } + + /// Builds a new [`Signer`] for `account` from a `secret_key_source`. + #[inline] + pub fn with_account(secret_key_source: D, account: Account<D>) -> Self { + Self { + secret_key_source, + account, + } + } + + /// Builds a new [`Signer`] for `account` from a `secret_key_source` with starting ranges + /// `external_indices` and `internal_indices`. + #[inline] + pub fn with_ranges( + secret_key_source: D, + account: D::Account, + external_indices: Range<D::Index>, + internal_indices: Range<D::Index>, + ) -> Self { + Self::with_account( + secret_key_source, + Account::with_ranges(account, external_indices, internal_indices), + ) + } + + /// Returns the next [`Signer`] after `self`, incrementing the account number. + #[inline] + pub fn next(self) -> Self { + Self::with_account(self.secret_key_source, self.account.next()) + } + + + /// Returns a [`PreSender`] for the key at the given `index`. + #[inline] + pub fn get_pre_sender<C>( + &self, + index: Index<D>, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + ) -> Result<PreSender<C>, D::Error> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + { + Ok(self.get(&index)?.into_pre_sender(commitment_scheme, asset)) + } + + /// Generates the next external identity for this signer. + #[inline] + fn next_external_identity<C>(&mut self) -> Result<Identity<C>, D::Error> + where + C: identity::Configuration<SecretKey = D::SecretKey>, + { + Ok(self + .account + .next_external_key(&self.secret_key_source)? + .map(Identity::new) + .unwrap()) + } + + /// Generates the next internal identity for this signer. + #[inline] + fn next_internal_identity<C>(&mut self) -> Result<InternalKeyOwned<D, Identity<C>>, D::Error> + where + C: identity::Configuration<SecretKey = D::SecretKey>, + { + Ok(self + .account + .next_internal_key(&self.secret_key_source)? + .map(Identity::new)) + } + + /// Generates a new [`ShieldedIdentity`] to receive assets to this account via an external + /// transaction. + #[inline] + pub fn next_shielded<C>( + &mut self, + commitment_scheme: &C::CommitmentScheme, + ) -> Result<ShieldedIdentity<C>, D::Error> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + { + Ok(self + .next_external_identity()? + .into_shielded(commitment_scheme)) + } + + /// Generates a new [`InternalIdentity`] to receive assets in this account via an internal + /// transaction. + #[inline] + pub fn next_internal<C, R>( + &mut self, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + rng: &mut R, + ) -> Result<InternalKeyOwned<D, InternalIdentity<C>>, InternalIdentityError<D, C>> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + R: CryptoRng + RngCore + ?Sized, + { + self.next_internal_identity() + .map_err(InternalIdentityError::SecretKeyError)? + .map_ok(move |identity| { + identity + .into_internal(commitment_scheme, asset, rng) + .map_err(InternalIdentityError::EncryptionError) + }) + } + + /// Builds the next transfer accumulator. + #[inline] + pub fn next_accumulator<C, R, const RECEIVERS: usize>( + &mut self, + commitment_scheme: &C::CommitmentScheme, + asset_id: AssetId, + sender_sum: AssetValue, + rng: &mut R, + ) -> Result<TransferAccumulator<D, C, RECEIVERS>, InternalIdentityError<D, C>> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + R: CryptoRng + RngCore + ?Sized, + { + let mut receivers = Vec::with_capacity(RECEIVERS); + let mut zero_pre_senders = Vec::with_capacity(RECEIVERS - 1); + + for _ in 0..RECEIVERS - 1 { + let (internal, index) = self + .next_internal(commitment_scheme, Asset::zero(asset_id), rng)? + .into(); + receivers.push(internal.receiver); + zero_pre_senders.push(KeyOwned::new(internal.pre_sender, index)); + } + + let internal = self + .next_internal(commitment_scheme, asset_id.with(sender_sum), rng)? + .unwrap(); + + receivers.push(internal.receiver); + + Ok(TransferAccumulator::new( + into_array_unchecked(receivers), + zero_pre_senders, + internal.pre_sender, + )) + } + + /// Builds the change receiver for the end of a transaction. + #[inline] + pub fn next_change<C, R>( + &mut self, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + rng: &mut R, + ) -> Result<InternalKeyOwned<D, Receiver<C>>, InternalIdentityError<D, C>> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + R: CryptoRng + RngCore + ?Sized, + { + self.next_internal_identity() + .map_err(InternalIdentityError::SecretKeyError)? + .map_ok(move |identity| identity.into_receiver(commitment_scheme, asset, rng)) + .map_err(InternalIdentityError::EncryptionError) + } + + /// Builds a [`Mint`] transaction to mint `asset` and returns the index for that asset. + #[inline] + pub fn mint<C, R>( + &mut self, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + rng: &mut R, + ) -> Result<InternalKeyOwned<D, Mint<C>>, InternalIdentityError<D, C>> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + R: CryptoRng + RngCore + ?Sized, + { + self.next_internal_identity() + .map_err(InternalIdentityError::SecretKeyError)? + .map_ok(|identity| { + Mint::from_identity(identity, commitment_scheme, asset, rng) + .map_err(InternalIdentityError::EncryptionError) + }) + } + + /// Builds a [`Mint`] transaction to mint a zero asset with the given `asset_id`, returning a + /// [`PreSender`] for that asset. + #[inline] + pub fn mint_zero<C, R>( + &mut self, + commitment_scheme: &C::CommitmentScheme, + asset_id: AssetId, + rng: &mut R, + ) -> Result<(Mint<C>, PreSender<C>), InternalIdentityError<D, C>> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + R: CryptoRng + RngCore + ?Sized, + { + Mint::zero( + self.next_internal_identity() + .map_err(InternalIdentityError::SecretKeyError)? + .unwrap(), + commitment_scheme, + asset_id, + rng, + ) + .map_err(InternalIdentityError::EncryptionError) + } + + /// Tries to decrypt `encrypted_asset` using the `secret_key`. + #[inline] + fn try_open_asset<C>( + secret_key: Result<ExternalSecretKey<D>, D::Error>, + encrypted_asset: &EncryptedAsset<C>, + ) -> Option<ExternalKeyOwned<D, Asset>> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + { + let KeyOwned { inner, index } = secret_key.ok()?; + Some( + index.wrap( + Identity::<C>::new(inner) + .try_open(encrypted_asset) + .ok()? + .into_asset(), + ), + ) + } + + /// Looks for an index that can decrypt the given `encrypted_asset`. + #[inline] + pub fn find_external_asset<C>( + &mut self, + encrypted_asset: &EncryptedAsset<C>, + ) -> Option<ExternalKeyOwned<D, Asset>> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + { + let asset = self + .account + .external_keys(&self.secret_key_source) + .find_map(move |k| Self::try_open_asset::<C>(k, encrypted_asset))?; + self.account + .conditional_increment_external_range(&asset.index.index); + Some(asset) + } +} + +impl<D> Load for Signer<D> +where + D: DerivedSecretKeyGenerator + LoadWith<Account<D>>, +{ + type Path = D::Path; + + type LoadingKey = D::LoadingKey; + + type Error = <D as Load>::Error; + + #[inline] + fn load<P>(path: P, loading_key: &Self::LoadingKey) -> Result<Self, Self::Error> + where + P: AsRef<Self::Path>, + { + let (secret_key_source, account) = D::load_with(path, loading_key)?; + Ok(Self::with_account(secret_key_source, account)) + } +} + +impl<D> Save for Signer<D> +where + D: DerivedSecretKeyGenerator + SaveWith<Account<D>>, +{ + type Path = D::Path; + + type SavingKey = D::SavingKey; + + type Error = <D as Save>::Error; + + #[inline] + fn save<P>(self, path: P, saving_key: &Self::SavingKey) -> Result<(), Self::Error> + where + P: AsRef<Self::Path>, + { + self.secret_key_source + .save_with(self.account, path, saving_key) + } +} + +/// Signer Configuration +pub trait Configuration { + /// + type TransferConfiguration: transfer::Configuration; + + /// + type TransferProofSystemConfiguration: + transfer::ProofSystemConfiguration<Self::TransferConfiguration>; + + /// + type AccountKeyTable: AccountKeyTable<SecretKey = SecretKey<Self::TransferConfiguration>>; + + /// [`Utxo`] Accumulator Type + type UtxoSet: Accumulator< + Item = <Self::UtxoSetVerifier as Verifier>::Item, + Verifier = Self::UtxoSetVerifier, + > + ConstantCapacityAccumulator + + ExactSizeAccumulator + + OptimizedAccumulator + + Rollback; + + /// Asset Map Type + type AssetMap: AssetMap<Key = ??>; + + /// Random Number Generator Type + type Rng: CryptoRng + RngCore; +} + +/// Full Signer +pub struct Signer<C> +where + C: Configuration, +{ + /// Signer + signer: Signer<C::DerivedSecretKeyGenerator>, + + /// Commitment Scheme + commitment_scheme: C::CommitmentScheme, + + /// Proving Context + proving_context: ProvingContext<C>, + + /// UTXO Set + utxo_set: C::UtxoSet, + + /// Asset Distribution + assets: C::AssetMap, + + /// Pending Asset Distribution + pending_assets: PendingAssetMap<C::DerivedSecretKeyGenerator>, + + /// Random Number Generator + rng: C::Rng, +} + + +/// Internal Identity Error +/// +/// This `enum` is the error state for any construction of an [`InternalIdentity`] from a derived +/// secret key generator. +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "D::Error: Clone, IntegratedEncryptionSchemeError<C>: Clone"), + Copy(bound = "D::Error: Copy, IntegratedEncryptionSchemeError<C>: Copy"), + Debug(bound = "D::Error: Debug, IntegratedEncryptionSchemeError<C>: Debug"), + Eq(bound = "D::Error: Eq, IntegratedEncryptionSchemeError<C>: Eq"), + Hash(bound = "D::Error: Hash, IntegratedEncryptionSchemeError<C>: Hash"), + PartialEq(bound = "D::Error: PartialEq, IntegratedEncryptionSchemeError<C>: PartialEq") +)] +pub enum InternalIdentityError<D, C> +where + D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, +{ + /// Secret Key Generator Error + SecretKeyError(D::Error), + + /// Encryption Error + EncryptionError(IntegratedEncryptionSchemeError<C>), +} + +/// Transfer Accumulator +pub struct TransferAccumulator<D, C, const RECEIVERS: usize> +where + D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, +{ + /// Receivers + pub receivers: [Receiver<C>; RECEIVERS], + + /// Zero Balance Pre-Senders + pub zeroes: Vec<InternalKeyOwned<D, PreSender<C>>>, + + /// Accumulated Balance Pre-Sender + pub pre_sender: PreSender<C>, +} + +impl<D, C, const RECEIVERS: usize> TransferAccumulator<D, C, RECEIVERS> +where + D: DerivedSecretKeyGenerator, + C: transfer::Configuration<SecretKey = D::SecretKey>, +{ + /// Builds a new [`TransferAccumulator`] from `receivers`, `zeroes`, and `pre_sender`. + #[inline] + pub fn new( + receivers: [Receiver<C>; RECEIVERS], + zeroes: Vec<InternalKeyOwned<D, PreSender<C>>>, + pre_sender: PreSender<C>, + ) -> Self { + Self { + receivers, + zeroes, + pre_sender, + } + } +} +*/ diff --git a/manta-accounting/src/wallet2/state.rs b/manta-accounting/src/wallet2/state.rs new file mode 100644 index 000000000..14d1ac487 --- /dev/null +++ b/manta-accounting/src/wallet2/state.rs @@ -0,0 +1,377 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Full Wallet Implementation + +use crate::{ + asset::{Asset, AssetId, AssetValue}, + identity2::SecretKey, + key2::HierarchicalKeyTable, + transfer2::{ + canonical::{Transaction, TransactionKind}, + Configuration, ReceivingKey, + }, + wallet2::{ + ledger::{self, Checkpoint, PullResponse, PushResponse}, + signer::{self, SignResponse, SyncState}, + }, +}; +use alloc::{ + collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}, + vec::Vec, +}; +use core::marker::PhantomData; + +#[cfg(feature = "std")] +use std::{ + collections::hash_map::{Entry as HashMapEntry, HashMap, RandomState}, + hash::BuildHasher, +}; + +/// Balance State +pub trait BalanceState { + /// Returns the current balance associated with this `id`. + fn balance(&self, id: AssetId) -> AssetValue; + + /// Returns true if `self` contains at least `asset.value` of the asset of kind `asset.id`. + #[inline] + fn contains(&self, asset: Asset) -> bool { + self.balance(asset.id) >= asset.value + } + + /// Deposits `asset` into the balance state, increasing the balance of the asset stored at + /// `asset.id` by an amount equal to `asset.value`. + fn deposit(&mut self, asset: Asset); + + /// Deposits every asset in `assets` into the balance state. + #[inline] + fn deposit_all<I>(&mut self, assets: I) + where + I: IntoIterator<Item = Asset>, + { + assets.into_iter().for_each(move |a| self.deposit(a)); + } + + /// Withdraws `asset` from the balance state without checking if it would overdraw. + /// + /// # Panics + /// + /// This method does not check if withdrawing `asset` from the balance state would cause an + /// overdraw, but if it were to overdraw, this method must panic. + fn withdraw_unchecked(&mut self, asset: Asset); +} + +/// Performs an unchecked withdraw on `balance`, panicking on overflow. +#[inline] +fn withdraw_unchecked(balance: Option<&mut AssetValue>, withdraw: AssetValue) { + let balance = balance.expect("Trying to withdraw from a zero balance."); + *balance = balance + .checked_sub(withdraw) + .expect("Overdrawn balance state."); +} + +impl BalanceState for Vec<Asset> { + #[inline] + fn balance(&self, id: AssetId) -> AssetValue { + self.iter() + .find_map(move |a| a.value_of(id)) + .unwrap_or_default() + } + + #[inline] + fn deposit(&mut self, asset: Asset) { + self.push(asset); + } + + #[inline] + fn withdraw_unchecked(&mut self, asset: Asset) { + if !asset.is_zero() { + withdraw_unchecked( + self.iter_mut().find_map(move |a| a.value_of_mut(asset.id)), + asset.value, + ); + } + } +} + +/// Adds implementation of [`BalanceState`] for a map type with the given `$entry` type. +macro_rules! impl_balance_state_map_body { + ($entry:tt) => { + #[inline] + fn balance(&self, id: AssetId) -> AssetValue { + self.get(&id).copied().unwrap_or_default() + } + + #[inline] + fn deposit(&mut self, asset: Asset) { + match self.entry(asset.id) { + $entry::Vacant(entry) => { + entry.insert(asset.value); + } + $entry::Occupied(entry) => { + *entry.into_mut() += asset.value; + } + } + } + + #[inline] + fn withdraw_unchecked(&mut self, asset: Asset) { + if !asset.is_zero() { + withdraw_unchecked(self.get_mut(&asset.id), asset.value); + } + } + }; +} + +/// B-Tree Map [`BalanceState`] Implementation +pub type BTreeMapBalanceState = BTreeMap<AssetId, AssetValue>; + +impl BalanceState for BTreeMapBalanceState { + impl_balance_state_map_body! { BTreeMapEntry } +} + +/// Hash Map [`BalanceState`] Implementation +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +pub type HashMapBalanceState<S = RandomState> = HashMap<AssetId, AssetValue, S>; + +#[cfg(feature = "std")] +impl<S> BalanceState for HashMapBalanceState<S> +where + S: BuildHasher, +{ + impl_balance_state_map_body! { HashMapEntry } +} + +/// Wallet +pub struct Wallet<H, C, L, S, B = BTreeMapBalanceState> +where + H: HierarchicalKeyTable<SecretKey = SecretKey<C>>, + C: Configuration, + L: ledger::Connection<C>, + S: signer::Connection<H, C>, + B: BalanceState, +{ + /// Ledger Connection + ledger: L, + + /// Ledger Checkpoint + checkpoint: L::Checkpoint, + + /// Signer Connection + signer: S, + + /// Signer Synchronization State + sync_state: SyncState, + + /// Balance State + assets: B, + + /// Type Parameter Marker + __: PhantomData<(H, C)>, +} + +impl<H, C, L, S, B> Wallet<H, C, L, S, B> +where + H: HierarchicalKeyTable<SecretKey = SecretKey<C>>, + C: Configuration, + L: ledger::Connection<C>, + S: signer::Connection<H, C>, + B: BalanceState, +{ + /// Builds a new [`Wallet`]. + #[inline] + pub fn new( + signer: S, + sync_state: SyncState, + ledger: L, + checkpoint: L::Checkpoint, + assets: B, + ) -> Self { + Self { + signer, + sync_state, + ledger, + checkpoint, + assets, + __: PhantomData, + } + } + + /// Returns the current balance associated with this `id`. + #[inline] + pub fn balance(&self, id: AssetId) -> AssetValue { + self.assets.balance(id) + } + + /// Returns true if `self` contains at least `asset.value` of the asset of kind `asset.id`. + #[inline] + pub fn contains(&self, asset: Asset) -> bool { + self.assets.contains(asset) + } + + /// Returns the [`Checkpoint`](ledger::Connection::Checkpoint) representing the current state + /// of this wallet. + #[inline] + pub fn checkpoint(&self) -> &L::Checkpoint { + &self.checkpoint + } + + /// Pulls data from the `ledger`, synchronizing the wallet and balance state. + #[inline] + pub async fn sync(&mut self) -> Result<(), Error<H, C, L, S>> { + // TODO: How to recover from an `InconsistentSynchronization` error? Need some sort of + // recovery mode, like starting from the beginning of the state? + let PullResponse { + checkpoint, + receiver_data, + } = self + .ledger + .pull(&self.checkpoint) + .await + .map_err(Error::LedgerError)?; + self.assets.deposit_all( + self.signer + .sync( + self.sync_state, + self.checkpoint.receiver_index(), + receiver_data, + ) + .await? + .assets, + ); + self.sync_state = SyncState::Commit; + self.checkpoint = checkpoint; + Ok(()) + } + + /// Checks if `transaction` can be executed on the balance state of `self`, returning the + /// kind of update that should be performed on the balance state if the transaction is + /// successfully posted to the ledger. + /// + /// # Safety + /// + /// This method is already called by [`post`](Self::post), but can be used by custom + /// implementations to perform checks elsewhere. + #[inline] + pub fn check(&self, transaction: &Transaction<C>) -> Result<TransactionKind, Asset> { + transaction.check(move |a| self.contains(a)) + } + + /// Tries to commit to the current signer state. + #[inline] + async fn try_commit(&mut self) { + if self.signer.commit().await.is_err() { + self.sync_state = SyncState::Commit; + } + } + + /// Tries to rollback to the previous signer state. + #[inline] + async fn try_rollback(&mut self) { + if self.signer.rollback().await.is_err() { + self.sync_state = SyncState::Rollback; + } + } + + /// Posts a transaction to the ledger, returning `true` if the `transaction` was successfully + /// saved onto the ledger. This method automatically synchronizes with the ledger before + /// posting. To amortize the cost of future calls to [`post`](Self::post), the + /// [`sync`](Self::sync) method can be used to synchronize with the ledger. + /// + /// # Failure Conditions + /// + /// This method returns `false` when there were no errors in producing transfer data and + /// sending and receiving from the ledger, but instead the ledger just did not accept the + /// transaction as is. This could be caused by an external update to the ledger while the + /// signer was building the transaction that caused the wallet and the ledger to get out of + /// sync. In this case, [`post`](Self::post) can safely be called again, to retry the + /// transaction. + /// + /// This method returns an error in any other case. The internal state of the wallet is kept + /// consistent between calls and recoverable errors are returned for the caller to handle. + #[inline] + pub async fn post(&mut self, transaction: Transaction<C>) -> Result<bool, Error<H, C, L, S>> { + self.sync().await?; + let balance_update = self + .check(&transaction) + .map_err(Error::InsufficientBalance)?; + let SignResponse { posts } = self.signer.sign(transaction).await?; + match self.ledger.push(posts).await { + Ok(PushResponse { + checkpoint, + success: true, + }) => { + self.try_commit().await; + match balance_update { + TransactionKind::Deposit(asset) => self.assets.deposit(asset), + TransactionKind::Withdraw(asset) => self.assets.withdraw_unchecked(asset), + } + self.checkpoint = checkpoint; + Ok(true) + } + Ok(PushResponse { success: false, .. }) => { + // FIXME: What about the checkpoint returned in the response? + self.try_rollback().await; + Ok(false) + } + Err(err) => { + self.try_rollback().await; + Err(Error::LedgerError(err)) + } + } + } + + /// + #[inline] + pub async fn receiver(&mut self) -> Result<ReceivingKey<C>, signer::Error<H, C, S::Error>> { + self.signer.receiver().await + } +} + +/// Wallet Error +/// +/// This `enum` is the error state for [`Wallet`] methods. See [`sync`](Wallet::sync) and +/// [`post`](Wallet::post) for more. +pub enum Error<H, C, L, S> +where + H: HierarchicalKeyTable<SecretKey = SecretKey<C>>, + C: Configuration, + L: ledger::Connection<C>, + S: signer::Connection<H, C>, +{ + /// Insufficient Balance + InsufficientBalance(Asset), + + /// Ledger Error + LedgerError(L::Error), + + /// Signer Error + SignerError(signer::Error<H, C, S::Error>), +} + +impl<H, C, L, S> From<signer::Error<H, C, S::Error>> for Error<H, C, L, S> +where + H: HierarchicalKeyTable<SecretKey = SecretKey<C>>, + C: Configuration, + L: ledger::Connection<C>, + S: signer::Connection<H, C>, +{ + #[inline] + fn from(err: signer::Error<H, C, S::Error>) -> Self { + Self::SignerError(err) + } +} diff --git a/manta-accounting/src/wallet2/test.rs b/manta-accounting/src/wallet2/test.rs new file mode 100644 index 000000000..e032a146d --- /dev/null +++ b/manta-accounting/src/wallet2/test.rs @@ -0,0 +1,19 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Testing Framework + +// TODO: Add tests for the asynchronous wallet protocol. From 9aced27595a8401092c840017e1b365c2d0f8ac9 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 14 Nov 2021 23:20:28 -0500 Subject: [PATCH 124/275] wip: continue removing old implementations --- manta-accounting/src/asset.rs | 220 +- manta-accounting/src/identity.rs | 1817 ++++------------- manta-accounting/src/identity2.rs | 805 -------- manta-accounting/src/key.rs | 884 +------- manta-accounting/src/key2.rs | 130 -- manta-accounting/src/lib.rs | 12 +- manta-accounting/src/transfer.rs | 1539 ++------------ manta-accounting/src/transfer2.rs | 816 -------- manta-accounting/src/wallet/ledger.rs | 4 +- manta-accounting/src/wallet/signer.rs | 1582 +++++++------- manta-accounting/src/wallet/state.rs | 70 +- manta-accounting/src/wallet2/ledger.rs | 146 -- manta-accounting/src/wallet2/mod.rs | 28 - manta-accounting/src/wallet2/signer.rs | 1249 ----------- manta-accounting/src/wallet2/state.rs | 377 ---- manta-accounting/src/wallet2/test.rs | 19 - manta-crypto/src/accumulator.rs | 154 +- .../src/{encryption/mod.rs => encryption.rs} | 56 +- manta-crypto/src/encryption/ies.rs | 434 ---- manta-crypto/src/key.rs | 40 +- manta-crypto/src/lib.rs | 1 - manta-crypto/src/prf.rs | 32 - manta-pay/src/accounting/config.rs | 49 +- manta-pay/src/accounting/identity.rs | 91 +- manta-pay/src/accounting/key.rs | 54 +- manta-pay/src/accounting/mod.rs | 2 +- manta-pay/src/accounting/transfer.rs | 2 +- .../constraint/arkworks/constraint_system.rs | 2 +- .../arkworks/proof_systems/groth16.rs | 4 +- manta-pay/src/crypto/encryption.rs | 69 + manta-pay/src/crypto/ies.rs | 202 -- manta-pay/src/crypto/key.rs | 76 + manta-pay/src/crypto/merkle_tree.rs | 24 + manta-pay/src/crypto/mod.rs | 4 +- manta-pay/src/crypto/prf/blake2s.rs | 396 ---- manta-pay/src/crypto/prf/mod.rs | 19 - 36 files changed, 1904 insertions(+), 9505 deletions(-) delete mode 100644 manta-accounting/src/identity2.rs delete mode 100644 manta-accounting/src/key2.rs delete mode 100644 manta-accounting/src/transfer2.rs delete mode 100644 manta-accounting/src/wallet2/ledger.rs delete mode 100644 manta-accounting/src/wallet2/mod.rs delete mode 100644 manta-accounting/src/wallet2/signer.rs delete mode 100644 manta-accounting/src/wallet2/state.rs delete mode 100644 manta-accounting/src/wallet2/test.rs rename manta-crypto/src/{encryption/mod.rs => encryption.rs} (76%) delete mode 100644 manta-crypto/src/encryption/ies.rs delete mode 100644 manta-crypto/src/prf.rs create mode 100644 manta-pay/src/crypto/encryption.rs delete mode 100644 manta-pay/src/crypto/ies.rs create mode 100644 manta-pay/src/crypto/key.rs delete mode 100644 manta-pay/src/crypto/prf/blake2s.rs delete mode 100644 manta-pay/src/crypto/prf/mod.rs diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index e04f4c58d..290d91885 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -38,17 +38,12 @@ use derive_more::{ use manta_crypto::{ constraint::{ reflection::{unknown, HasAllocation, HasVariable, Var}, - Allocation, PublicOrSecret, Secret, Variable, + Allocation, PublicOrSecret, Secret, Variable, VariableSource, }, rand::{CryptoRng, Rand, RngCore, Sample, Standard}, }; use manta_util::{array_map, fallible_array_map, into_array_unchecked, Concat, ConcatAccumulator}; -pub(super) mod prelude { - #[doc(inline)] - pub use super::{Asset, AssetId, AssetValue, AssetValues}; -} - /// [`AssetId`] Base Type pub type AssetIdType = u32; @@ -263,9 +258,6 @@ impl<'a> Sum<&'a AssetValue> for AssetValue { } } -/// [`AssetValue`] Array Type -pub type AssetValues<const N: usize> = [AssetValue; N]; - /// Change Iterator /// /// An iterator over [`AssetValue`] change amounts. @@ -331,12 +323,20 @@ impl FusedIterator for Change {} /// Asset #[derive(Clone, Copy, Debug, Default, Display, Eq, From, Hash, PartialEq)] #[display(fmt = "{{id: {}, value: {}}}", id, value)] -pub struct Asset { +pub struct Asset<I = AssetId, V = AssetValue> { /// Asset Id - pub id: AssetId, + pub id: I, /// Asset Value - pub value: AssetValue, + pub value: V, +} + +impl<I, V> Asset<I, V> { + /// Builds a new [`Asset`] from an `id` and a `value`. + #[inline] + pub const fn new(id: I, value: V) -> Self { + Self { id, value } + } } impl Asset { @@ -346,12 +346,6 @@ impl Asset { /// The size of the data in this type in bytes. pub const SIZE: usize = (Self::BITS / 8) as usize; - /// Builds a new [`Asset`] from an `id` and a `value`. - #[inline] - pub const fn new(id: AssetId, value: AssetValue) -> Self { - Self { id, value } - } - /// Builds a new zero [`Asset`] with the given `id`. #[inline] pub const fn zero(id: AssetId) -> Self { @@ -408,25 +402,35 @@ impl Asset { } } -impl Add<AssetValue> for Asset { +impl<I, V> Add<V> for Asset<I, V> +where + V: AddAssign, +{ type Output = Self; #[inline] - fn add(mut self, rhs: AssetValue) -> Self::Output { + fn add(mut self, rhs: V) -> Self::Output { self += rhs; self } } -impl AddAssign<AssetValue> for Asset { +impl<I, V> AddAssign<V> for Asset<I, V> +where + V: AddAssign, +{ #[inline] - fn add_assign(&mut self, rhs: AssetValue) { + fn add_assign(&mut self, rhs: V) { self.value += rhs; } } -impl Concat for Asset { - type Item = u8; +impl<I, V> Concat for Asset<I, V> +where + I: Concat, + V: Concat<Item = I::Item>, +{ + type Item = I::Item; #[inline] fn concat<A>(&self, accumulator: &mut A) @@ -439,7 +443,11 @@ impl Concat for Asset { #[inline] fn size_hint(&self) -> Option<usize> { - Some(Self::SIZE) + if let (Some(id_size), Some(value_size)) = (self.id.size_hint(), self.value.size_hint()) { + Some(id_size + value_size) + } else { + None + } } } @@ -457,9 +465,9 @@ impl From<Asset> for [u8; Asset::SIZE] { } } -impl From<Asset> for (AssetId, AssetValue) { +impl<I, V> From<Asset<I, V>> for (I, V) { #[inline] - fn from(asset: Asset) -> Self { + fn from(asset: Asset<I, V>) -> Self { (asset.id, asset.value) } } @@ -475,81 +483,34 @@ impl Sample for Asset { } } -impl Sub<AssetValue> for Asset { +impl<I, V> Sub<V> for Asset<I, V> +where + V: SubAssign, +{ type Output = Self; #[inline] - fn sub(mut self, rhs: AssetValue) -> Self::Output { + fn sub(mut self, rhs: V) -> Self::Output { self -= rhs; self } } -impl SubAssign<AssetValue> for Asset { - #[inline] - fn sub_assign(&mut self, rhs: AssetValue) { - self.value -= rhs; - } -} - -/// Asset Id Variable -pub type AssetIdVar<C> = Var<AssetId, C>; - -/// Asset Value Variable -pub type AssetValueVar<C> = Var<AssetValue, C>; - -/// Asset Variable -pub struct AssetVar<C> -where - C: HasVariable<AssetId, Mode = PublicOrSecret> - + HasVariable<AssetValue, Mode = PublicOrSecret> - + ?Sized, -{ - /// Asset Id - pub id: AssetIdVar<C>, - - /// Asset Value - pub value: AssetValueVar<C>, -} - -impl<C> AssetVar<C> +impl<I, V> SubAssign<V> for Asset<I, V> where - C: HasVariable<AssetId, Mode = PublicOrSecret> - + HasVariable<AssetValue, Mode = PublicOrSecret> - + ?Sized, + V: SubAssign, { - /// Builds a new [`AssetVar`] from an `id` and a `value`. #[inline] - pub fn new(id: AssetIdVar<C>, value: AssetValueVar<C>) -> Self { - Self { id, value } - } -} - -impl<C> Concat for AssetVar<C> -where - C: HasVariable<AssetId, Mode = PublicOrSecret> - + HasVariable<AssetValue, Mode = PublicOrSecret> - + ?Sized, - AssetIdVar<C>: Concat, - AssetValueVar<C>: Concat<Item = <AssetIdVar<C> as Concat>::Item>, -{ - type Item = <AssetIdVar<C> as Concat>::Item; - - #[inline] - fn concat<A>(&self, accumulator: &mut A) - where - A: ConcatAccumulator<Self::Item> + ?Sized, - { - self.id.concat(accumulator); - self.value.concat(accumulator); + fn sub_assign(&mut self, rhs: V) { + self.value -= rhs; } } -impl<C> Variable<C> for AssetVar<C> +impl<C, I, V> Variable<C> for Asset<I, V> where - C: HasVariable<AssetId, Mode = PublicOrSecret> - + HasVariable<AssetValue, Mode = PublicOrSecret> - + ?Sized, + C: ?Sized, + I: Variable<C, Type = AssetId, Mode = PublicOrSecret>, + V: Variable<C, Type = AssetValue, Mode = PublicOrSecret>, { type Type = Asset; @@ -558,83 +519,12 @@ where #[inline] fn new(cs: &mut C, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { - Allocation::Known(this, mode) => Self::new( - cs.new_known_allocation(&this.id, mode), - cs.new_known_allocation(&this.value, mode), - ), - Allocation::Unknown(mode) => Self::new( - unknown::<AssetId, _>(cs, mode.into()), - unknown::<AssetValue, _>(cs, mode.into()), - ), - } - } -} - -impl<C> HasAllocation<C> for Asset -where - C: HasVariable<AssetId, Mode = PublicOrSecret> - + HasVariable<AssetValue, Mode = PublicOrSecret> - + ?Sized, -{ - type Variable = AssetVar<C>; - type Mode = Secret; -} - -/// Asset Collection -#[derive(Clone, Copy, Debug, Eq, From, Hash, Ord, PartialEq, PartialOrd)] -#[from(forward)] -pub struct AssetCollection<const N: usize> { - /// Asset Id - pub id: AssetId, - - /// Asset Values - pub values: [AssetValue; N], -} - -impl<const N: usize> AssetCollection<N> { - /// Generates a collection of assets with matching [`AssetId`]. - #[inline] - pub const fn new(id: AssetId, values: [AssetValue; N]) -> Self { - Self { id, values } - } -} - -impl<const N: usize> Default for AssetCollection<N> { - #[inline] - fn default() -> Self { - Self::new(Default::default(), [Default::default(); N]) - } -} - -impl<const N: usize> From<AssetCollection<N>> for [Asset; N] { - #[inline] - fn from(collection: AssetCollection<N>) -> Self { - array_map(collection.values, move |v| collection.id.with(v)) - } -} - -impl<const N: usize> TryFrom<[Asset; N]> for AssetCollection<N> { - type Error = usize; - - #[inline] - fn try_from(array: [Asset; N]) -> Result<Self, Self::Error> { - let mut counter: usize = 0; - let mut base_id = None; - let values = fallible_array_map(array, move |asset| { - let result = match base_id { - Some(id) if id == asset.id => Ok(asset.value), - Some(_) => Err(counter), - _ => { - base_id = Some(asset.id); - Ok(asset.value) - } - }; - counter += 1; - result - })?; - match base_id { - Some(id) => Ok(Self::new(id, values)), - _ => Err(0), + Allocation::Known(this, mode) => { + Self::new(this.id.as_known(cs, mode), this.value.as_known(cs, mode)) + } + Allocation::Unknown(mode) => { + Self::new(I::new_unknown(cs, mode), V::new_unknown(cs, mode)) + } } } } @@ -648,8 +538,6 @@ pub trait AssetMap: Default { /// Keys are used to access the underlying asset values. type Key; - // TODO: Turn `select` and `zeroes` back into iterator returning methods. - /// Selects asset keys which total up to at least `asset` in value. fn select(&self, asset: Asset) -> Selection<Self>; diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 1ced3ad54..cb831821f 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -14,847 +14,215 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Identities, Senders, and Receivers +//! Sender and Receiver Identities -// FIXME: Rename `AssetParameters`, since they are about identities not assets. -// FIXME: Check the secret key APIs. -// TODO: Get rid of [`Spend`] and [`OpenSpend`] if possible. They don't seem to be that useful. -// See `crate::wallet::signer`. - -use crate::asset::{Asset, AssetId, AssetValue, AssetVar}; -use core::{fmt::Debug, hash::Hash, marker::PhantomData}; +use core::marker::PhantomData; use manta_crypto::{ accumulator::{Accumulator, MembershipProof, Verifier}, commitment::{CommitmentScheme, Input as CommitmentInput}, - encryption::ies::{self, EncryptedMessage, IntegratedEncryptionScheme}, - prf::PseudorandomFunctionFamily, - rand::{CryptoRng, Rand, RngCore, Sample, SeedableRng, Standard, TrySample}, + encryption::{EncryptedMessage, HybridPublicKeyEncryptionScheme}, + key::KeyAgreementScheme, }; -pub(super) mod prelude { - #[doc(inline)] - pub use super::{Identity, Receiver, Sender, ShieldedIdentity, Spend, Utxo, VoidNumber}; -} - -/// [`Identity`] Configuration -pub trait Configuration { - /// Secret Key Type - type SecretKey: Clone; - - /// Pseudorandom Function Family Input Type - type PseudorandomFunctionFamilyInput: Sample; - - /// Pseudorandom Function Family Type - type PseudorandomFunctionFamily: PseudorandomFunctionFamily< - Seed = Self::SecretKey, - Input = Self::PseudorandomFunctionFamilyInput, - >; - - /// Commitment Scheme Randomness Type - type CommitmentSchemeRandomness: Sample; - - /// Commitment Scheme Type - type CommitmentScheme: CommitmentScheme<Randomness = Self::CommitmentSchemeRandomness> - + CommitmentInput<VoidNumberGenerator<Self>> - + CommitmentInput<Asset> - + CommitmentInput<VoidNumberCommitment<Self>>; - - /// Seedable Cryptographic Random Number Generator Type - type Rng: CryptoRng + RngCore + SeedableRng<Seed = Self::SecretKey>; -} - -/// [`PseudorandomFunctionFamily::Input`] Type -type PseudorandomFunctionFamilyInput<C> = - <<C as Configuration>::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Input; - -/// [`PseudorandomFunctionFamily::Output`] Type -type PseudorandomFunctionFamilyOutput<C> = - <<C as Configuration>::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Output; - -/// [`CommitmentScheme::Randomness`] Type -type CommitmentSchemeRandomness<C> = - <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Randomness; - -/// [`CommitmentScheme::Output`] Type -type CommitmentSchemeOutput<C> = - <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Output; - -/// Secret Key Type -pub type SecretKey<C> = <C as Configuration>::SecretKey; +use manta_crypto::constraint::{ConstraintSystem, Equal}; -/// Void Number Generator Type -pub type VoidNumberGenerator<C> = PseudorandomFunctionFamilyInput<C>; - -/// Void Number Type -pub type VoidNumber<C> = PseudorandomFunctionFamilyOutput<C>; - -/// Void Number Commitment Randomness Type -pub type VoidNumberCommitmentRandomness<C> = CommitmentSchemeRandomness<C>; - -/// Void Number Commitment Type -pub type VoidNumberCommitment<C> = CommitmentSchemeOutput<C>; - -/// UTXO Randomness Type -pub type UtxoRandomness<C> = CommitmentSchemeRandomness<C>; - -/// UTXO Type -pub type Utxo<C> = CommitmentSchemeOutput<C>; - -/// Generates a void number commitment from `void_number_generator` and -/// `void_number_commitment_randomness`. -#[inline] -pub fn generate_void_number_commitment<CS, VNG>( - commitment_scheme: &CS, - void_number_generator: &VNG, - void_number_commitment_randomness: &CS::Randomness, -) -> CS::Output +/// Viewing Key Table +pub trait ViewingKeyTable<K> where - CS: CommitmentScheme + CommitmentInput<VNG>, + K: KeyAgreementScheme, { - commitment_scheme.commit_one(void_number_generator, void_number_commitment_randomness) -} + /// Query Type + type Query: Default; -/// Generates a UTXO from `asset`, `void_number_commitment`, and `utxo_randomness`. -#[inline] -pub fn generate_utxo<CS, A, VNC>( - commitment_scheme: &CS, - asset: &A, - void_number_commitment: &VNC, - utxo_randomness: &CS::Randomness, -) -> CS::Output -where - CS: CommitmentScheme + CommitmentInput<A> + CommitmentInput<VNC>, -{ - commitment_scheme - .start() - .update(asset) - .update(void_number_commitment) - .commit(utxo_randomness) + /// Returns the key associated to `query`, generating a new key if `query` does not already + /// correspond to an existing key. + fn get_or_generate(&mut self, query: Self::Query) -> &K::SecretKey; } -/// Public Parameters for using an [`Asset`] -#[derive(derivative::Derivative)] -#[derivative( - Clone( - bound = "VoidNumberGenerator<C>: Clone, VoidNumberCommitmentRandomness<C>: Clone, UtxoRandomness<C>: Clone" - ), - Copy( - bound = "VoidNumberGenerator<C>: Copy, VoidNumberCommitmentRandomness<C>: Copy, UtxoRandomness<C>: Copy" - ), - Debug( - bound = "VoidNumberGenerator<C>: Debug, VoidNumberCommitmentRandomness<C>: Debug, UtxoRandomness<C>: Debug" - ), - Default( - bound = "VoidNumberGenerator<C>: Default, VoidNumberCommitmentRandomness<C>: Default, UtxoRandomness<C>: Default" - ), - Eq( - bound = "VoidNumberGenerator<C>: Eq, VoidNumberCommitmentRandomness<C>: Eq, UtxoRandomness<C>: Eq" - ), - Hash( - bound = "VoidNumberGenerator<C>: Hash, VoidNumberCommitmentRandomness<C>: Hash, UtxoRandomness<C>: Hash" - ), - PartialEq( - bound = "VoidNumberGenerator<C>: PartialEq, VoidNumberCommitmentRandomness<C>: PartialEq, UtxoRandomness<C>: PartialEq" - ) -)] -pub struct AssetParameters<C> +impl<K> ViewingKeyTable<K> for K::SecretKey where - C: Configuration + ?Sized, + K: KeyAgreementScheme, { - /// Void Number Generator - pub void_number_generator: VoidNumberGenerator<C>, - - /// Void Number Commitment Randomness - pub void_number_commitment_randomness: VoidNumberCommitmentRandomness<C>, - - /// UTXO Randomness - pub utxo_randomness: UtxoRandomness<C>, -} - -impl<C> AssetParameters<C> -where - C: Configuration + ?Sized, -{ - /// Builds a new [`AssetParameters`]. - #[inline] - pub fn new( - void_number_generator: VoidNumberGenerator<C>, - void_number_commitment_randomness: VoidNumberCommitmentRandomness<C>, - utxo_randomness: UtxoRandomness<C>, - ) -> Self { - Self { - void_number_generator, - void_number_commitment_randomness, - utxo_randomness, - } - } - - /// Generates a new void number commitment. - #[inline] - pub fn void_number_commitment( - &self, - commitment_scheme: &C::CommitmentScheme, - ) -> VoidNumberCommitment<C> { - generate_void_number_commitment( - commitment_scheme, - &self.void_number_generator, - &self.void_number_commitment_randomness, - ) - } + type Query = (); - /// Generates a [`Utxo`] from a given `asset` and `void_number_commitment`. #[inline] - pub fn utxo( - &self, - commitment_scheme: &C::CommitmentScheme, - asset: &Asset, - void_number_commitment: &VoidNumberCommitment<C>, - ) -> Utxo<C> { - generate_utxo( - commitment_scheme, - asset, - void_number_commitment, - &self.utxo_randomness, - ) + fn get_or_generate(&mut self, query: Self::Query) -> &K::SecretKey { + let _ = query; + self } } -impl<C> Sample for AssetParameters<C> +/// Spending Key +pub struct SpendingKey<K, V = <K as KeyAgreementScheme>::SecretKey> where - C: Configuration + ?Sized, + K: KeyAgreementScheme, + V: ViewingKeyTable<K>, { - #[inline] - fn sample<R>(distribution: Standard, rng: &mut R) -> Self - where - R: CryptoRng + RngCore + ?Sized, - { - let _ = distribution; - Self::new(rng.gen(), rng.gen(), rng.gen()) - } -} + /// Spending Key + spending_key: K::SecretKey, -/// Account Identity -pub struct Identity<C> -where - C: Configuration + ?Sized, -{ - /// Secret Key - secret_key: SecretKey<C>, + /// Viewing Key Table + viewing_key_table: V, } -impl<C> Identity<C> +impl<K, V> SpendingKey<K, V> where - C: Configuration + ?Sized, + K: KeyAgreementScheme, + V: ViewingKeyTable<K>, { - /// Builds a new [`Identity`] from a [`SecretKey`]. + /// Builds a new [`SpendingKey`] from `spending_key` and `viewing_key_table`. #[inline] - pub fn new(secret_key: SecretKey<C>) -> Self { - Self { secret_key } - } - - /// Generates the associated `C::Rng` and a `AssetParameters<C>` for this identity. - /// - /// # API Note - /// - /// This method is intentionally private so that internal random number generator is not part - /// of the public interface. See [`Self::parameters`] for access to the associated `parameters`. - /// - /// # Implementation Note - /// - /// Contributors should always use this method when generating an `rng` or a `parameters` in the - /// folowing ways: - /// - /// ```text - /// 1. [BOTH] let (mut rng, parameters) = self.rng_and_parameters(); - /// 2. [RNG] let (mut rng, _) = self.rng_and_parameters(); - /// 2. [PAIR] let parameters = self.parameters(); - /// ``` - /// - /// This is important because we need to preserve the order in which objects are randomly - /// generated across different methods. The `parameters` is always generated immediately after - /// creation of the random number generator. - #[inline] - fn rng_and_parameters(&self) -> (C::Rng, AssetParameters<C>) { - let mut rng = C::Rng::from_seed(self.secret_key.clone()); - let parameters = rng.gen(); - (rng, parameters) - } - - /// Generates [`AssetParameters`] for assets that are used by this identity. - #[inline] - fn parameters(&self) -> AssetParameters<C> { - let (_, parameters) = self.rng_and_parameters(); - parameters - } - - /// Generates the associated [`AssetParameters`] and asset [`PublicKey`](ies::PublicKey) for - /// this identity. - #[inline] - fn parameters_and_asset_public_key<I>(&self) -> (AssetParameters<C>, ies::PublicKey<I>) - where - I: IntegratedEncryptionScheme<Plaintext = Asset>, - { - let (mut rng, parameters) = self.rng_and_parameters(); - (parameters, I::generate_public_key(&mut rng)) - } - - /// Generates the associated [`AssetParameters`] and asset [`SecretKey`](ies::SecretKey) for - /// this identity. - #[inline] - fn parameters_and_asset_secret_key<I>(&self) -> (AssetParameters<C>, ies::SecretKey<I>) - where - I: IntegratedEncryptionScheme<Plaintext = Asset>, - { - let (mut rng, parameters) = self.rng_and_parameters(); - (parameters, I::generate_secret_key(&mut rng)) - } - - /// Generates a new void number using the `void_number_generator` parameter. - #[inline] - fn void_number(&self, void_number_generator: &VoidNumberGenerator<C>) -> VoidNumber<C> { - C::PseudorandomFunctionFamily::evaluate(&self.secret_key, void_number_generator) - } - - /// Generates a new void number commitment using the `void_number_generator` and - /// `void_number_commitment_randomness`. - #[inline] - fn void_number_commitment( - &self, - commitment_scheme: &C::CommitmentScheme, - parameters: &AssetParameters<C>, - ) -> VoidNumberCommitment<C> { - parameters.void_number_commitment(commitment_scheme) - } - - /// Returns the [`VoidNumberCommitment`], and [`Utxo`] for this identity. - #[inline] - fn construct_utxo( - &self, - commitment_scheme: &C::CommitmentScheme, - asset: &Asset, - parameters: &AssetParameters<C>, - ) -> (VoidNumberCommitment<C>, Utxo<C>) { - let void_number_commitment = parameters.void_number_commitment(commitment_scheme); - let utxo = parameters.utxo(commitment_scheme, asset, &void_number_commitment); - (void_number_commitment, utxo) - } - - /// Builds a new [`PreSender`] for the given `asset`. - #[inline] - pub fn into_pre_sender( - self, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - ) -> PreSender<C> { - let parameters = self.parameters(); - let (void_number_commitment, utxo) = - self.construct_utxo(commitment_scheme, &asset, &parameters); - PreSender { - void_number: self.void_number(&parameters.void_number_generator), - secret_key: self.secret_key, - asset, - parameters, - void_number_commitment, - utxo, + pub fn new(spending_key: K::SecretKey, viewing_key_table: V) -> Self { + Self { + spending_key, + viewing_key_table, } } - /// Builds a new [`Sender`] for the given `asset`. + /// Returns the receiving key for `self` with the viewing key located at the given `query` in + /// the viewing key table. #[inline] - pub fn into_sender<S>( - self, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - utxo_set: &S, - ) -> Option<Sender<C, S::Verifier>> - where - S: Accumulator<Item = Utxo<C>>, - { - let parameters = self.parameters(); - let (void_number_commitment, utxo) = - self.construct_utxo(commitment_scheme, &asset, &parameters); - Some(Sender { - utxo_membership_proof: utxo_set.prove(&utxo)?, - void_number: self.void_number(&parameters.void_number_generator), - secret_key: self.secret_key, - asset, - parameters, - void_number_commitment, - utxo, - }) - } - - /// Builds a new [`ShieldedIdentity`] from `commitment_scheme`, `parameters`, and - /// `asset_keypair`. - #[inline] - fn build_shielded_identity<I>( - &self, - commitment_scheme: &C::CommitmentScheme, - parameters: AssetParameters<C>, - asset_public_key: ies::PublicKey<I>, - ) -> ShieldedIdentity<C, I> - where - I: IntegratedEncryptionScheme<Plaintext = Asset>, - { - ShieldedIdentity { - void_number_commitment: self.void_number_commitment(commitment_scheme, &parameters), - utxo_randomness: parameters.utxo_randomness, - asset_public_key, + pub fn receiving_key(&mut self, query: V::Query) -> ReceivingKey<K> { + ReceivingKey { + spend: K::derive(&self.spending_key), + view: K::derive(self.viewing_key_table.get_or_generate(query)), } } - /// Builds a new [`ShieldedIdentity`] from this identity. - #[inline] - pub fn into_shielded<I>(self, commitment_scheme: &C::CommitmentScheme) -> ShieldedIdentity<C, I> - where - I: IntegratedEncryptionScheme<Plaintext = Asset>, - { - let (parameters, asset_public_key) = self.parameters_and_asset_public_key(); - self.build_shielded_identity(commitment_scheme, parameters, asset_public_key) - } - - /// Builds a new [`Spend`]. - #[inline] - pub fn into_spend<I>(self) -> Spend<C, I> - where - I: IntegratedEncryptionScheme<Plaintext = Asset>, - { - let (_, asset_secret_key) = self.parameters_and_asset_secret_key(); - Spend::new(self, asset_secret_key) - } - - /// Tries to open an `encrypted_asset`, returning an [`OpenSpend`] if successful. - #[inline] - pub fn try_open<I>( - self, - encrypted_asset: &EncryptedMessage<I>, - ) -> Result<OpenSpend<C>, I::Error> - where - I: IntegratedEncryptionScheme<Plaintext = Asset>, - { - self.into_spend().try_open(encrypted_asset) - } - - /// Builds a new [`Receiver`]. - #[inline] - pub fn into_receiver<I, R>( - self, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - rng: &mut R, - ) -> Result<Receiver<C, I>, I::Error> - where - I: IntegratedEncryptionScheme<Plaintext = Asset>, - R: CryptoRng + RngCore + ?Sized, - { - self.into_shielded(commitment_scheme) - .into_receiver(commitment_scheme, asset, rng) - } - - /// Builds a new [`InternalIdentity`]. + /// Prepares `self` for spending `asset` with the given `ephemeral_key`. #[inline] - pub fn into_internal<I, R>( - self, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - rng: &mut R, - ) -> Result<InternalIdentity<C, I>, I::Error> - where - I: IntegratedEncryptionScheme<Plaintext = Asset>, - R: CryptoRng + RngCore + ?Sized, - { - let (parameters, asset_public_key) = self.parameters_and_asset_public_key(); - Ok(InternalIdentity { - receiver: self - .build_shielded_identity(commitment_scheme, parameters, asset_public_key) - .into_receiver(commitment_scheme, asset, rng)?, - pre_sender: OpenSpend::new(self, asset).into_pre_sender(commitment_scheme), - }) - } -} - -impl<C, D> Sample<D> for Identity<C> -where - C: Configuration + ?Sized, - C::SecretKey: Sample<D>, -{ - #[inline] - fn sample<R>(distribution: D, rng: &mut R) -> Self - where - R: CryptoRng + RngCore + ?Sized, - { - Self::new(rng.sample(distribution)) - } -} - -impl<C, D> TrySample<D> for Identity<C> -where - C: Configuration + ?Sized, - C::SecretKey: TrySample<D>, -{ - type Error = <C::SecretKey as TrySample<D>>::Error; - - #[inline] - fn try_sample<R>(distribution: D, rng: &mut R) -> Result<Self, Self::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - Ok(Self::new(rng.try_sample(distribution)?)) - } -} - -/// Shielded Identity -pub struct ShieldedIdentity<C, I> -where - C: Configuration + ?Sized, - I: IntegratedEncryptionScheme<Plaintext = Asset>, -{ - /// UTXO Randomness - utxo_randomness: UtxoRandomness<C>, - - /// Void Number Commitment - void_number_commitment: VoidNumberCommitment<C>, - - /// Encrypted [`Asset`] Public Key - asset_public_key: ies::PublicKey<I>, -} - -impl<C, I> ShieldedIdentity<C, I> -where - C: Configuration + ?Sized, - I: IntegratedEncryptionScheme<Plaintext = Asset>, -{ - /// Builds a new [`ShieldedIdentity`] from `identity` and `commitment_scheme`. - #[inline] - pub fn from_identity(identity: Identity<C>, commitment_scheme: &C::CommitmentScheme) -> Self - where - I: IntegratedEncryptionScheme<Plaintext = Asset>, - { - identity.into_shielded(commitment_scheme) - } - - /// Generates a [`Receiver`] from a [`ShieldedIdentity`]. - #[inline] - pub fn into_receiver<R>( - self, + pub fn pre_sender<C>( + &self, + ephemeral_key: PublicKey<C>, + asset: C::Asset, commitment_scheme: &C::CommitmentScheme, - asset: Asset, - rng: &mut R, - ) -> Result<Receiver<C, I>, I::Error> + ) -> PreSender<C> where - R: CryptoRng + RngCore + ?Sized, + K::SecretKey: Clone, + C: Configuration<KeyAgreementScheme = K>, { - let Self { - utxo_randomness, - void_number_commitment, - asset_public_key, - } = self; - Ok(Receiver { - encrypted_asset: asset_public_key.encrypt(&asset, rng)?, - utxo: generate_utxo( - commitment_scheme, - &asset, - &void_number_commitment, - &utxo_randomness, - ), + PreSender::new( + self.spending_key.clone(), + ephemeral_key, asset, - utxo_randomness, - void_number_commitment, - }) - } -} - -/// Spend Error -/// -/// This `enum` is the error state for the [`into_sender`] method on [`Spend`]. -/// See its documentation for more. -/// -/// [`into_sender`]: Spend::into_sender -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "I::Error: Clone"), - Copy(bound = "I::Error: Copy"), - Debug(bound = "I::Error: Debug"), - Eq(bound = "I::Error: Eq"), - Hash(bound = "I::Error: Hash"), - PartialEq(bound = "I::Error: PartialEq") -)] -pub enum SpendError<I> -where - I: IntegratedEncryptionScheme<Plaintext = Asset>, -{ - /// Encryption Error - EncryptionError(I::Error), - - /// Missing UTXO Membership Proof - MissingUtxo, -} - -/// Spending Information -pub struct Spend<C, I> -where - C: Configuration + ?Sized, - I: IntegratedEncryptionScheme<Plaintext = Asset>, -{ - /// Spender Identity - identity: Identity<C>, - - /// Encrypted [`Asset`] Secret Key - asset_secret_key: ies::SecretKey<I>, -} - -impl<C, I> Spend<C, I> -where - C: Configuration + ?Sized, - I: IntegratedEncryptionScheme<Plaintext = Asset>, -{ - /// Builds a new `Spend` from an `Identity` and an `ies::SecretKey<I>`. - /// - /// # API Note - /// - /// This method is intentionally private so that `identity` and `asset_secret_key` are not part - /// of the public interface. - #[inline] - fn new(identity: Identity<C>, asset_secret_key: ies::SecretKey<I>) -> Self { - Self { - identity, - asset_secret_key, - } - } - - /// Builds a new [`Spend`] from an `identity`. - #[inline] - pub fn from_identity(identity: Identity<C>) -> Self { - identity.into_spend() - } - - /// Tries to open an `encrypted_asset`, returning an [`OpenSpend`] if successful. - #[inline] - pub fn try_open(self, encrypted_asset: &EncryptedMessage<I>) -> Result<OpenSpend<C>, I::Error> { - Ok(OpenSpend::new( - self.identity, - self.asset_secret_key.decrypt(encrypted_asset)?, - )) - } - - /// Builds a new [`PreSender`] for the given `encrypted_asset`. - #[inline] - pub fn into_pre_sender( - self, - commitment_scheme: &C::CommitmentScheme, - encrypted_asset: EncryptedMessage<I>, - ) -> Result<PreSender<C>, I::Error> { - Ok(self - .try_open(&encrypted_asset)? - .into_pre_sender(commitment_scheme)) - } - - /// Builds a new [`Sender`] for the given `encrypted_asset`. - #[inline] - pub fn into_sender<S>( - self, - commitment_scheme: &C::CommitmentScheme, - encrypted_asset: EncryptedMessage<I>, - utxo_set: &S, - ) -> Result<Sender<C, S::Verifier>, SpendError<I>> - where - S: Accumulator<Item = Utxo<C>>, - { - self.try_open(&encrypted_asset) - .map_err(SpendError::EncryptionError)? - .into_sender(commitment_scheme, utxo_set) - .ok_or(SpendError::MissingUtxo) - } -} - -impl<C, I> From<Identity<C>> for Spend<C, I> -where - C: Configuration + ?Sized, - I: IntegratedEncryptionScheme<Plaintext = Asset>, -{ - #[inline] - fn from(identity: Identity<C>) -> Self { - Self::from_identity(identity) + commitment_scheme, + ) } } -/// Open [`Spend`] -pub struct OpenSpend<C> +/// Receiving Key +pub struct ReceivingKey<K> where - C: Configuration + ?Sized, + K: KeyAgreementScheme, { - /// Spender Identity - identity: Identity<C>, + /// Spend Part of the Receiving Key + pub spend: K::PublicKey, - /// Unencrypted [`Asset`] - asset: Asset, + /// View Part of the Receiving Key + pub view: K::PublicKey, } -impl<C> OpenSpend<C> +impl<K> ReceivingKey<K> where - C: Configuration + ?Sized, + K: KeyAgreementScheme, { - /// Builds a new `OpenSpend` from an `Identity` and an `Asset`. - /// - /// # API Note - /// - /// This method is intentionally private so that `identity` and `asset` are not part of the - /// public interface. - #[inline] - fn new(identity: Identity<C>, asset: Asset) -> Self { - Self { identity, asset } - } - - /// Extracts decrypted asset from `self`. - #[inline] - pub fn into_asset(self) -> Asset { - self.asset - } - - /// Builds a new [`PreSender`] for `self`. - #[inline] - pub fn into_pre_sender(self, commitment_scheme: &C::CommitmentScheme) -> PreSender<C> { - self.identity.into_pre_sender(commitment_scheme, self.asset) - } - - /// Builds a new [`Sender`] for `self`. + /// Prepares `self` for receiving `asset` with the given `ephemeral_key`. #[inline] - pub fn into_sender<S>( + pub fn into_receiver<C>( self, + ephemeral_key: SecretKey<C>, + asset: C::Asset, commitment_scheme: &C::CommitmentScheme, - utxo_set: &S, - ) -> Option<Sender<C, S::Verifier>> + ) -> FullReceiver<C> where - S: Accumulator<Item = Utxo<C>>, + C: Configuration<KeyAgreementScheme = K>, { - self.identity - .into_sender(commitment_scheme, self.asset, utxo_set) + FullReceiver::new( + self.spend, + self.view, + ephemeral_key, + asset, + commitment_scheme, + ) } } -/// Internal Identity -pub struct InternalIdentity<C, I> -where - C: Configuration + ?Sized, - I: IntegratedEncryptionScheme<Plaintext = Asset>, -{ - /// Receiver - pub receiver: Receiver<C, I>, +/// Identity Configuration +pub trait Configuration { + /// Asset Type + type Asset; - /// Pre-Sender - pub pre_sender: PreSender<C>, -} + /// Key Agreement Scheme Type + type KeyAgreementScheme: KeyAgreementScheme; -impl<C, I> InternalIdentity<C, I> -where - C: Configuration + ?Sized, - I: IntegratedEncryptionScheme<Plaintext = Asset>, -{ - /// Builds an [`InternalIdentity`] from an [`Identity`] for the given `asset`. - #[inline] - pub fn from_identity<R>( - identity: Identity<C>, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - rng: &mut R, - ) -> Result<Self, I::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - identity.into_internal(commitment_scheme, asset, rng) - } -} - -impl<C, I> From<InternalIdentity<C, I>> for (Receiver<C, I>, PreSender<C>) -where - C: Configuration + ?Sized, - I: IntegratedEncryptionScheme<Plaintext = Asset>, -{ - #[inline] - fn from(internal: InternalIdentity<C, I>) -> Self { - (internal.receiver, internal.pre_sender) - } + /// Commitment Scheme Type + type CommitmentScheme: CommitmentScheme<Randomness = Trapdoor<Self>> + + CommitmentInput<Self::Asset> + + CommitmentInput<SecretKey<Self>>; } -/// Sender Proof -/// -/// This `struct` is created by the [`get_proof`](PreSender::get_proof) method on [`PreSender`]. -/// See its documentation for more. -pub struct SenderProof<C, V> -where - C: Configuration + ?Sized, - V: Verifier<Item = Utxo<C>> + ?Sized, -{ - /// UTXO Membership Proof - utxo_membership_proof: MembershipProof<V>, +/// Secret Key Type +pub type SecretKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::SecretKey; - /// Type Parameter Marker - __: PhantomData<C>, -} +/// Public Key Type +pub type PublicKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::PublicKey; -impl<C, V> SenderProof<C, V> -where - C: Configuration + ?Sized, - V: Verifier<Item = Utxo<C>> + ?Sized, -{ - /// Returns `true` if a [`PreSender`] could be upgraded using `self` given the `utxo_set`. - #[inline] - pub fn can_upgrade<S>(&self, utxo_set: &S) -> bool - where - S: Accumulator<Item = V::Item, Verifier = V>, - { - self.utxo_membership_proof.matching_output(utxo_set) - } +/// Trapdoor Type +pub type Trapdoor<C> = + <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret; - /// Upgrades the `pre_sender` to a [`Sender`] by attaching `self` to it. - /// - /// # Note - /// - /// When using this method, be sure to check that [`can_upgrade`](Self::can_upgrade) returns - /// `true`. Otherwise, using the sender returned here will most likely return an error when - /// posting to the ledger. - #[inline] - pub fn upgrade(self, pre_sender: PreSender<C>) -> Sender<C, V> { - pre_sender.upgrade(self) - } -} +/// Commitment Scheme Output Type +pub type CommitmentSchemeOutput<C> = + <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Output; + +/// Unspent Transaction Output Type +pub type Utxo<C> = CommitmentSchemeOutput<C>; + +/// Void Number Type +pub type VoidNumber<C> = CommitmentSchemeOutput<C>; /// Pre-Sender pub struct PreSender<C> where - C: Configuration + ?Sized, + C: Configuration, { - /// Secret Key - secret_key: SecretKey<C>, + /// Secret Spending Key + spending_key: SecretKey<C>, + + /// Ephemeral Public Key + ephemeral_key: PublicKey<C>, /// Asset - asset: Asset, + asset: C::Asset, - /// Asset Parameters - parameters: AssetParameters<C>, + /// Unspent Transaction Output + utxo: Utxo<C>, /// Void Number void_number: VoidNumber<C>, - - /// Void Number Commitment - void_number_commitment: VoidNumberCommitment<C>, - - /// Unspent Transaction Output - utxo: Utxo<C>, } impl<C> PreSender<C> where - C: Configuration + ?Sized, + C: Configuration, { - /// Builds a new [`PreSender`] for this `asset` from an `identity`. + /// Builds a new [`PreSender`] for `spending_key` to spend `asset` with + /// `ephemeral_key`. #[inline] - pub fn from_identity( - identity: Identity<C>, + pub fn new( + spending_key: SecretKey<C>, + ephemeral_key: PublicKey<C>, + asset: C::Asset, commitment_scheme: &C::CommitmentScheme, - asset: Asset, ) -> Self { - identity.into_pre_sender(commitment_scheme, asset) + let trapdoor = C::KeyAgreementScheme::agree(&spending_key, &ephemeral_key); + Self { + utxo: commitment_scheme.commit_one(&asset, &trapdoor), + void_number: commitment_scheme.commit_one(&spending_key, &trapdoor), + spending_key, + ephemeral_key, + asset, + } } /// Inserts the [`Utxo`] corresponding to `self` into the `utxo_set` with the intention of @@ -867,8 +235,8 @@ where utxo_set.insert(&self.utxo) } - /// Requests the membership proof of `self.utxo` from `utxo_set` so that we can turn `self` - /// into a [`Sender`]. + /// Requests the membership proof of the [`Utxo`] corresponding to `self` from `utxo_set` to + /// prepare the conversion from `self` into a [`Sender`]. #[inline] pub fn get_proof<S>(&self, utxo_set: &S) -> Option<SenderProof<C, S::Verifier>> where @@ -893,13 +261,11 @@ where V: Verifier<Item = Utxo<C>> + ?Sized, { Sender { - secret_key: self.secret_key, + spending_key: self.spending_key, + ephemeral_key: self.ephemeral_key, asset: self.asset, - parameters: self.parameters, - void_number: self.void_number, - void_number_commitment: self.void_number_commitment, - utxo: self.utxo, utxo_membership_proof: proof.utxo_membership_proof, + void_number: self.void_number, } } @@ -909,106 +275,130 @@ where where S: Accumulator<Item = Utxo<C>>, { - let proof = self.get_proof(utxo_set)?; - Some(self.upgrade(proof)) + Some(self.get_proof(utxo_set)?.upgrade(self)) } } -impl<C> Sample<&C::CommitmentScheme> for PreSender<C> +/// Sender Proof +/// +/// This `struct` is created by the [`get_proof`](PreSender::get_proof) method on [`PreSender`]. +/// See its documentation for more. +pub struct SenderProof<C, V> +where + C: Configuration, + V: Verifier<Item = Utxo<C>> + ?Sized, +{ + /// UTXO Membership Proof + utxo_membership_proof: MembershipProof<V>, + + /// Type Parameter Marker + __: PhantomData<C>, +} + +impl<C, V> SenderProof<C, V> where - C: Configuration + ?Sized, - C::SecretKey: Sample, + C: Configuration, + V: Verifier<Item = Utxo<C>> + ?Sized, { + /// Returns `true` if a [`PreSender`] could be upgraded using `self` given the `utxo_set`. #[inline] - fn sample<R>(distribution: &C::CommitmentScheme, rng: &mut R) -> Self + pub fn can_upgrade<S>(&self, utxo_set: &S) -> bool where - R: CryptoRng + RngCore + ?Sized, + S: Accumulator<Item = V::Item, Verifier = V>, { - Identity::gen(rng).into_pre_sender(distribution, rng.gen()) + self.utxo_membership_proof.matching_output(utxo_set) + } + + /// Upgrades the `pre_sender` to a [`Sender`] by attaching `self` to it. + /// + /// # Note + /// + /// When using this method, be sure to check that [`can_upgrade`](Self::can_upgrade) returns + /// `true`. Otherwise, using the sender returned here will most likely return an error when + /// posting to the ledger. + #[inline] + pub fn upgrade(self, pre_sender: PreSender<C>) -> Sender<C, V> { + pre_sender.upgrade(self) } } /// Sender pub struct Sender<C, V> where - C: Configuration + ?Sized, + C: Configuration, V: Verifier<Item = Utxo<C>> + ?Sized, { - /// Secret Key - secret_key: SecretKey<C>, + /// Secret Spend Key + spending_key: SecretKey<C>, + + /// Ephemeral Public Key + ephemeral_key: PublicKey<C>, /// Asset - asset: Asset, + asset: C::Asset, - /// Asset Parameters - parameters: AssetParameters<C>, + /// UTXO Membership Proof + utxo_membership_proof: MembershipProof<V>, /// Void Number void_number: VoidNumber<C>, - - /// Void Number Commitment - void_number_commitment: VoidNumberCommitment<C>, - - /// Unspent Transaction Output - utxo: Utxo<C>, - - /// UTXO Membership Proof - utxo_membership_proof: MembershipProof<V>, } impl<C, V> Sender<C, V> where - C: Configuration + ?Sized, - V: Verifier<Item = Utxo<C>> + ?Sized, + C: Configuration, + V: Verifier<Item = Utxo<C>>, { - /// Builds a new [`Sender`] for this `asset` from an `identity`. - #[inline] - pub fn from_identity<S>( - identity: Identity<C>, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - utxo_set: &S, - ) -> Option<Self> - where - S: Accumulator<Item = V::Item, Verifier = V>, - { - identity.into_sender(commitment_scheme, asset, utxo_set) - } - - /// Returns the asset id for this sender. - #[inline] - pub(super) fn asset_id(&self) -> AssetId { - self.asset.id - } - - /// Returns the asset value for this sender. - #[inline] - pub(super) fn asset_value(&self) -> AssetValue { - self.asset.value - } - /// Reverts `self` back into a [`PreSender`]. /// /// This method should be called if the [`Utxo`] membership proof attached to `self` was deemed /// invalid or had expired. #[inline] - pub fn downgrade(self) -> PreSender<C> { + pub fn downgrade(self, commitment_scheme: &C::CommitmentScheme) -> PreSender<C> { PreSender { - secret_key: self.secret_key, + utxo: commitment_scheme.commit_one( + &self.asset, + &C::KeyAgreementScheme::agree(&self.spending_key, &self.ephemeral_key), + ), + spending_key: self.spending_key, + ephemeral_key: self.ephemeral_key, asset: self.asset, - parameters: self.parameters, void_number: self.void_number, - void_number_commitment: self.void_number_commitment, - utxo: self.utxo, } } - /// Extracts ledger posting data for this sender. + /// Returns the asset for `self`, checking if `self` is well-formed in the given constraint + /// system `cs`. + #[inline] + pub fn get_well_formed_asset<CS>( + self, + commitment_scheme: &C::CommitmentScheme, + utxo_set_verifier: &V, + cs: &mut CS, + ) -> C::Asset + where + CS: ConstraintSystem, + CommitmentSchemeOutput<C>: Equal<CS>, + V: Verifier<Verification = CS::Bool>, + { + let trapdoor = C::KeyAgreementScheme::agree(&self.spending_key, &self.ephemeral_key); + cs.assert(self.utxo_membership_proof.verify( + &commitment_scheme.commit_one(&self.asset, &trapdoor), + utxo_set_verifier, + )); + cs.assert_eq( + &self.void_number, + &commitment_scheme.commit_one(&self.spending_key, &trapdoor), + ); + self.asset + } + + /// Extracts the ledger posting data from `self`. #[inline] pub fn into_post(self) -> SenderPost<C, V> { SenderPost { + utxo_accumulator_output: self.utxo_membership_proof.into_output(), void_number: self.void_number, - utxo_checkpoint: self.utxo_membership_proof.into_output(), } } } @@ -1016,8 +406,8 @@ where /// Sender Ledger pub trait SenderLedger<C, V> where - C: Configuration + ?Sized, - V: Verifier<Item = Utxo<C>> + ?Sized, + C: Configuration, + V: Verifier<Item = Utxo<C>>, { /// Valid [`VoidNumber`] Posting Key /// @@ -1026,20 +416,20 @@ where /// This type must be some wrapper around [`VoidNumber`] which can only be constructed by this /// implementation of [`SenderLedger`]. This is to prevent that [`spend`](Self::spend) is /// called before [`is_unspent`](Self::is_unspent) and - /// [`is_matching_checkpoint`](Self::is_matching_checkpoint). + /// [`has_matching_utxo_accumulator_output`](Self::has_matching_utxo_accumulator_output). type ValidVoidNumber; - /// Valid Utxo State Posting Key + /// Valid UTXO Accumulator Output Posting Key /// /// # Safety /// /// This type must be some wrapper around [`S::Output`] which can only be constructed by this /// implementation of [`SenderLedger`]. This is to prevent that [`spend`](Self::spend) is /// called before [`is_unspent`](Self::is_unspent) and - /// [`is_matching_checkpoint`](Self::is_matching_checkpoint). + /// [`has_matching_utxo_accumulator_output`](Self::has_matching_utxo_accumulator_output). /// /// [`S::Output`]: Verifier::Output - type ValidUtxoCheckpoint; + type ValidUtxoAccumulatorOutput; /// Super Posting Key /// @@ -1051,12 +441,15 @@ where /// Existence of such a void number could indicate a possible double-spend. fn is_unspent(&self, void_number: VoidNumber<C>) -> Option<Self::ValidVoidNumber>; - /// Checks if the `checkpoint` matches the current checkpoint of the UTXO set that is stored on + /// Checks if `output` matches the current accumulated value of the UTXO set that is stored on /// the ledger. /// /// Failure to match the ledger state means that the sender was constructed under an invalid or /// older state of the ledger. - fn is_matching_checkpoint(&self, checkpoint: V::Output) -> Option<Self::ValidUtxoCheckpoint>; + fn has_matching_utxo_accumulator_output( + &self, + output: V::Output, + ) -> Option<Self::ValidUtxoAccumulatorOutput>; /// Posts the `void_number` to the ledger, spending the asset. /// @@ -1066,8 +459,8 @@ where /// the ledger. See [`is_unspent`](Self::is_unspent). fn spend( &mut self, + utxo_accumulator_output: Self::ValidUtxoAccumulatorOutput, void_number: Self::ValidVoidNumber, - utxo_checkpoint: Self::ValidUtxoCheckpoint, super_key: &Self::SuperPostingKey, ); } @@ -1080,194 +473,208 @@ pub enum SenderPostError { /// The asset has already been spent. AssetSpent, - /// Invalid UTXO State Error + /// Invalid UTXO Accumulator Error /// /// The sender was not constructed under the current state of the UTXO set. - InvalidUtxoCheckpoint, + InvalidUtxoAccumulator, } /// Sender Post pub struct SenderPost<C, V> where - C: Configuration + ?Sized, - V: Verifier<Item = Utxo<C>> + ?Sized, + C: Configuration, + V: Verifier<Item = Utxo<C>>, { - /// Void Number - pub(super) void_number: VoidNumber<C>, + /// UTXO Accumulator Output + utxo_accumulator_output: V::Output, - /// UTXO Checkpoint - pub(super) utxo_checkpoint: V::Output, + /// Void Number + void_number: VoidNumber<C>, } impl<C, V> SenderPost<C, V> where - C: Configuration + ?Sized, - V: Verifier<Item = Utxo<C>> + ?Sized, + C: Configuration, + V: Verifier<Item = Utxo<C>>, { /// Validates `self` on the sender `ledger`. #[inline] pub fn validate<L>(self, ledger: &L) -> Result<SenderPostingKey<C, V, L>, SenderPostError> where - L: SenderLedger<C, V> + ?Sized, + L: SenderLedger<C, V>, { Ok(SenderPostingKey { + utxo_accumulator_output: ledger + .has_matching_utxo_accumulator_output(self.utxo_accumulator_output) + .ok_or(SenderPostError::InvalidUtxoAccumulator)?, void_number: ledger .is_unspent(self.void_number) .ok_or(SenderPostError::AssetSpent)?, - utxo_checkpoint: ledger - .is_matching_checkpoint(self.utxo_checkpoint) - .ok_or(SenderPostError::InvalidUtxoCheckpoint)?, }) } } -impl<C, V> From<Sender<C, V>> for SenderPost<C, V> -where - C: Configuration + ?Sized, - V: Verifier<Item = Utxo<C>> + ?Sized, -{ - #[inline] - fn from(sender: Sender<C, V>) -> Self { - sender.into_post() - } -} - /// Sender Posting Key pub struct SenderPostingKey<C, V, L> where - C: Configuration + ?Sized, - V: Verifier<Item = Utxo<C>> + ?Sized, - L: SenderLedger<C, V> + ?Sized, + C: Configuration, + V: Verifier<Item = Utxo<C>>, + L: SenderLedger<C, V>, { + /// UTXO Accumulator Output Posting Key + utxo_accumulator_output: L::ValidUtxoAccumulatorOutput, + /// Void Number Posting Key void_number: L::ValidVoidNumber, - - /// UTXO Checkpoint Posting Key - utxo_checkpoint: L::ValidUtxoCheckpoint, } impl<C, V, L> SenderPostingKey<C, V, L> where - C: Configuration + ?Sized, - V: Verifier<Item = Utxo<C>> + ?Sized, - L: SenderLedger<C, V> + ?Sized, + C: Configuration, + V: Verifier<Item = Utxo<C>>, + L: SenderLedger<C, V>, { /// Posts `self` to the sender `ledger`. #[inline] pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) { - ledger.spend(self.void_number, self.utxo_checkpoint, super_key); + ledger.spend(self.utxo_accumulator_output, self.void_number, super_key); } } /// Receiver -pub struct Receiver<C, I> +pub struct Receiver<C> where - C: Configuration + ?Sized, - I: IntegratedEncryptionScheme<Plaintext = Asset>, + C: Configuration, { - /// Asset - asset: Asset, + /// Public Spending Key + spending_key: PublicKey<C>, - /// UTXO Randomness - utxo_randomness: UtxoRandomness<C>, + /// Ephemeral Secret Key + ephemeral_key: SecretKey<C>, - /// Void Number Commitment - void_number_commitment: VoidNumberCommitment<C>, + /// Asset + asset: C::Asset, /// Unspent Transaction Output utxo: Utxo<C>, - - /// Encrypted [`Asset`] - encrypted_asset: EncryptedMessage<I>, } -impl<C, I> Receiver<C, I> +impl<C> Receiver<C> where - C: Configuration + ?Sized, - I: IntegratedEncryptionScheme<Plaintext = Asset>, + C: Configuration, { - /// Builds a [`Receiver`] from an [`Identity`] for the given `asset`. + /// Generates the [`Utxo`] for a [`Receiver`] with the given parameters. #[inline] - pub fn from_identity<R>( - identity: Identity<C>, + fn generate_utxo( + spending_key: &PublicKey<C>, + ephemeral_key: &SecretKey<C>, + asset: &C::Asset, commitment_scheme: &C::CommitmentScheme, - asset: Asset, - rng: &mut R, - ) -> Result<Self, I::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - identity.into_receiver(commitment_scheme, asset, rng) + ) -> Utxo<C> { + commitment_scheme.commit_one( + asset, + &C::KeyAgreementScheme::agree(ephemeral_key, spending_key), + ) } - /// Builds a [`Receiver`] from a [`ShieldedIdentity`] for the given `asset`. + /// Returns the asset for `self`, checking if `self` is well-formed in the given constraint + /// system `cs`. #[inline] - pub fn from_shielded<R>( - identity: ShieldedIdentity<C, I>, + pub fn get_well_formed_asset<CS>( + self, commitment_scheme: &C::CommitmentScheme, - asset: Asset, - rng: &mut R, - ) -> Result<Self, I::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - identity.into_receiver(commitment_scheme, asset, rng) + cs: &mut CS, + ) -> C::Asset + where + CS: ConstraintSystem, + Utxo<C>: Equal<CS>, + { + cs.assert_eq( + &self.utxo, + &Self::generate_utxo( + &self.spending_key, + &self.ephemeral_key, + &self.asset, + commitment_scheme, + ), + ); + self.asset } +} - /// Returns the asset id for this receiver. - #[inline] - pub(crate) fn asset_id(&self) -> AssetId { - self.asset.id - } +/// Full Receiver +pub struct FullReceiver<C> +where + C: Configuration, +{ + /// Public Spending Key + spending_key: PublicKey<C>, - /// Returns the asset value for this receiver. - #[inline] - pub(crate) fn asset_value(&self) -> AssetValue { - self.asset.value - } + /// Public Viewing Key + viewing_key: PublicKey<C>, - /// Inserts the [`Utxo`] corresponding to `self` into the `utxo_set` with the intention of - /// returning a proof later by a call to [`get_proof`](PreSender::get_proof). - #[inline] - pub fn insert_utxo<S>(&self, utxo_set: &mut S) -> bool - where - S: Accumulator<Item = Utxo<C>>, - { - utxo_set.insert(&self.utxo) - } + /// Ephemeral Secret Key + ephemeral_key: SecretKey<C>, - /// Extracts ledger posting data for this receiver. - #[inline] - pub fn into_post(self) -> ReceiverPost<C, I> { - ReceiverPost { - utxo: self.utxo, - encrypted_asset: self.encrypted_asset, - } - } + /// Asset + asset: C::Asset, + + /// Unspent Transaction Output + utxo: Utxo<C>, } -impl<C, I> TrySample<&C::CommitmentScheme> for Receiver<C, I> +impl<C> FullReceiver<C> where - C: Configuration + ?Sized, - C::SecretKey: Sample, - I: IntegratedEncryptionScheme<Plaintext = Asset>, + C: Configuration, { - type Error = I::Error; + /// Builds a new [`Receiver`] for `spending_key` to receive `asset` with + /// `ephemeral_key`. + #[inline] + pub fn new( + spending_key: PublicKey<C>, + viewing_key: PublicKey<C>, + ephemeral_key: SecretKey<C>, + asset: C::Asset, + commitment_scheme: &C::CommitmentScheme, + ) -> Self { + Self { + utxo: Receiver::<C>::generate_utxo( + &spending_key, + &ephemeral_key, + &asset, + commitment_scheme, + ), + spending_key, + viewing_key, + ephemeral_key, + asset, + } + } + /// Extracts the ledger posting data from `self`. #[inline] - fn try_sample<R>(distribution: &C::CommitmentScheme, rng: &mut R) -> Result<Self, Self::Error> + pub fn into_post<H>(self) -> ReceiverPost<C, H> where - R: CryptoRng + RngCore + ?Sized, + H: HybridPublicKeyEncryptionScheme< + Plaintext = C::Asset, + KeyAgreementScheme = C::KeyAgreementScheme, + >, { - Identity::gen(rng).into_receiver(distribution, rng.gen(), rng) + ReceiverPost { + utxo: self.utxo, + note: EncryptedMessage::new(&self.viewing_key, self.ephemeral_key, self.asset), + } } } /// Receiver Ledger -pub trait ReceiverLedger<C, I> +pub trait ReceiverLedger<C, H> where - C: Configuration + ?Sized, - I: IntegratedEncryptionScheme<Plaintext = Asset>, + C: Configuration, + H: HybridPublicKeyEncryptionScheme< + Plaintext = C::Asset, + KeyAgreementScheme = C::KeyAgreementScheme, + >, { /// Valid [`Utxo`] Posting Key /// @@ -1297,7 +704,7 @@ where fn register( &mut self, utxo: Self::ValidUtxo, - encrypted_asset: EncryptedMessage<I>, + note: EncryptedMessage<H>, super_key: &Self::SuperPostingKey, ); } @@ -1312,529 +719,73 @@ pub enum ReceiverPostError { } /// Receiver Post -pub struct ReceiverPost<C, I> +pub struct ReceiverPost<C, H> where - C: Configuration + ?Sized, - I: IntegratedEncryptionScheme<Plaintext = Asset>, + C: Configuration, + H: HybridPublicKeyEncryptionScheme< + Plaintext = C::Asset, + KeyAgreementScheme = C::KeyAgreementScheme, + >, { /// Unspent Transaction Output - pub(super) utxo: Utxo<C>, + utxo: Utxo<C>, - /// Encrypted [`Asset`] - pub(super) encrypted_asset: EncryptedMessage<I>, + /// Encrypted Note + note: EncryptedMessage<H>, } -impl<C, I> ReceiverPost<C, I> +impl<C, H> ReceiverPost<C, H> where - C: Configuration + ?Sized, - I: IntegratedEncryptionScheme<Plaintext = Asset>, + C: Configuration, + H: HybridPublicKeyEncryptionScheme< + Plaintext = C::Asset, + KeyAgreementScheme = C::KeyAgreementScheme, + >, { /// Validates `self` on the receiver `ledger`. #[inline] - pub fn validate<L>(self, ledger: &L) -> Result<ReceiverPostingKey<C, I, L>, ReceiverPostError> + pub fn validate<L>(self, ledger: &L) -> Result<ReceiverPostingKey<C, H, L>, ReceiverPostError> where - L: ReceiverLedger<C, I>, + L: ReceiverLedger<C, H>, { Ok(ReceiverPostingKey { utxo: ledger .is_not_registered(self.utxo) .ok_or(ReceiverPostError::AssetRegistered)?, - encrypted_asset: self.encrypted_asset, + note: self.note, }) } } -impl<C, I> From<Receiver<C, I>> for ReceiverPost<C, I> -where - C: Configuration + ?Sized, - I: IntegratedEncryptionScheme<Plaintext = Asset>, -{ - #[inline] - fn from(receiver: Receiver<C, I>) -> Self { - receiver.into_post() - } -} - /// Receiver Posting Key -pub struct ReceiverPostingKey<C, I, L> +pub struct ReceiverPostingKey<C, H, L> where - C: Configuration + ?Sized, - I: IntegratedEncryptionScheme<Plaintext = Asset>, - L: ReceiverLedger<C, I> + ?Sized, + C: Configuration, + H: HybridPublicKeyEncryptionScheme< + Plaintext = C::Asset, + KeyAgreementScheme = C::KeyAgreementScheme, + >, + L: ReceiverLedger<C, H>, { - /// Utxo Posting Key + /// UTXO Posting Key utxo: L::ValidUtxo, - /// Encrypted Asset - encrypted_asset: EncryptedMessage<I>, + /// Encrypted Note + note: EncryptedMessage<H>, } -impl<C, I, L> ReceiverPostingKey<C, I, L> +impl<C, H, L> ReceiverPostingKey<C, H, L> where - C: Configuration + ?Sized, - I: IntegratedEncryptionScheme<Plaintext = Asset>, - L: ReceiverLedger<C, I> + ?Sized, + C: Configuration, + H: HybridPublicKeyEncryptionScheme< + Plaintext = C::Asset, + KeyAgreementScheme = C::KeyAgreementScheme, + >, + L: ReceiverLedger<C, H>, { /// Posts `self` to the receiver `ledger`. #[inline] pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) { - ledger.register(self.utxo, self.encrypted_asset, super_key); - } -} - -/// Constraint System Gadgets for Identities -pub mod constraint { - use super::*; - use manta_crypto::{ - accumulator::constraint::{MembershipProofVar, VerifierVariable}, - constraint::{ - reflection::{HasAllocation, HasVariable}, - Allocation, Constant, ConstraintSystem, Derived, Equal, Input as ProofSystemInput, - ProofSystem, Public, PublicOrSecret, Secret, Variable, - }, - }; - - /// [`Identity`] Constraint System Configuration - pub trait Configuration: super::Configuration { - /// Constraint System - type ConstraintSystem: ConstraintSystem - + HasVariable<AssetId, Mode = PublicOrSecret> - + HasVariable<AssetValue, Mode = PublicOrSecret>; - - /// Secret Key Variable - type SecretKeyVar: Variable<Self::ConstraintSystem, Type = Self::SecretKey, Mode = Secret>; - - /// Pseudorandom Function Family Input Variable - type PseudorandomFunctionFamilyInputVar: Variable< - Self::ConstraintSystem, - Type = Self::PseudorandomFunctionFamilyInput, - Mode = Secret, - >; - - /// Pseudorandom Function Family Output Variable - type PseudorandomFunctionFamilyOutputVar: Variable< - Self::ConstraintSystem, - Type = <Self::PseudorandomFunctionFamily as PseudorandomFunctionFamily>::Output, - Mode = PublicOrSecret, - > + Equal<Self::ConstraintSystem>; - - /// Pseudorandom Function Family Variable - type PseudorandomFunctionFamilyVar: PseudorandomFunctionFamily< - Seed = Self::SecretKeyVar, - Input = Self::PseudorandomFunctionFamilyInputVar, - Output = Self::PseudorandomFunctionFamilyOutputVar, - > + Variable< - Self::ConstraintSystem, - Type = Self::PseudorandomFunctionFamily, - Mode = Constant, - >; - - /// Commitment Scheme Randomness Variable - type CommitmentSchemeRandomnessVar: Variable< - Self::ConstraintSystem, - Type = Self::CommitmentSchemeRandomness, - Mode = Secret, - >; - - /// Commitment Scheme Output Variable - type CommitmentSchemeOutputVar: Variable< - Self::ConstraintSystem, - Type = <Self::CommitmentScheme as CommitmentScheme>::Output, - Mode = PublicOrSecret, - > + Equal<Self::ConstraintSystem>; - - /// Commitment Scheme Variable - type CommitmentSchemeVar: CommitmentScheme< - Randomness = Self::CommitmentSchemeRandomnessVar, - Output = Self::CommitmentSchemeOutputVar, - > + CommitmentInput<VoidNumberGeneratorVar<Self>> - + CommitmentInput<AssetVar<Self::ConstraintSystem>> - + CommitmentInput<VoidNumberCommitmentVar<Self>> - + Variable<Self::ConstraintSystem, Type = Self::CommitmentScheme, Mode = Constant>; - } - - /// [`PseudorandomFunctionFamily::Input`] Variable Type - type PseudorandomFunctionFamilyInputVar<C> = - <C as Configuration>::PseudorandomFunctionFamilyInputVar; - - /// [`PseudorandomFunctionFamily::Output`] Variable Type - type PseudorandomFunctionFamilyOutputVar<C> = - <C as Configuration>::PseudorandomFunctionFamilyOutputVar; - - /// [`CommitmentScheme::Randomness`] Variable Type - type CommitmentSchemeRandomnessVar<C> = <C as Configuration>::CommitmentSchemeRandomnessVar; - - /// [`CommitmentScheme::Output`] Variable Type - type CommitmentSchemeOutputVar<C> = <C as Configuration>::CommitmentSchemeOutputVar; - - /// Secret Key Variable Type - pub type SecretKeyVar<C> = <C as Configuration>::SecretKeyVar; - - /// Void Number Generator Variable Type - pub type VoidNumberGeneratorVar<C> = PseudorandomFunctionFamilyInputVar<C>; - - /// Void Number Variable Type - pub type VoidNumberVar<C> = PseudorandomFunctionFamilyOutputVar<C>; - - /// Void Number Commitment Randomness Variable Type - pub type VoidNumberCommitmentRandomnessVar<C> = CommitmentSchemeRandomnessVar<C>; - - /// Void Number Commitment Variable Type - pub type VoidNumberCommitmentVar<C> = CommitmentSchemeOutputVar<C>; - - /// UTXO Randomness Variable Type - pub type UtxoRandomnessVar<C> = CommitmentSchemeRandomnessVar<C>; - - /// UTXO Variable Type - pub type UtxoVar<C> = CommitmentSchemeOutputVar<C>; - - /// UTXO Membership Proof Variable Type - pub type UtxoMembershipProofVar<C, V> = - MembershipProofVar<V, <C as Configuration>::ConstraintSystem>; - - /// Asset Parameters Variable - pub struct AssetParametersVar<C> - where - C: Configuration, - { - /// Void Number Generator - pub void_number_generator: VoidNumberGeneratorVar<C>, - - /// Void Number Commitment Randomness - pub void_number_commitment_randomness: VoidNumberCommitmentRandomnessVar<C>, - - /// UTXO Randomness - pub utxo_randomness: UtxoRandomnessVar<C>, - } - - impl<C> AssetParametersVar<C> - where - C: Configuration, - { - /// Builds a new [`AssetParametersVar`] from parameter variables. - #[inline] - pub fn new( - void_number_generator: VoidNumberGeneratorVar<C>, - void_number_commitment_randomness: VoidNumberCommitmentRandomnessVar<C>, - utxo_randomness: UtxoRandomnessVar<C>, - ) -> Self { - Self { - void_number_generator, - void_number_commitment_randomness, - utxo_randomness, - } - } - } - - impl<C> Variable<C::ConstraintSystem> for AssetParametersVar<C> - where - C: Configuration, - { - type Type = AssetParameters<C>; - - type Mode = Secret; - - #[inline] - fn new( - cs: &mut C::ConstraintSystem, - allocation: Allocation<Self::Type, Self::Mode>, - ) -> Self { - match allocation { - Allocation::Known(this, mode) => Self::new( - VoidNumberGeneratorVar::<C>::new_known(cs, &this.void_number_generator, mode), - VoidNumberCommitmentRandomnessVar::<C>::new_known( - cs, - &this.void_number_commitment_randomness, - mode, - ), - UtxoRandomnessVar::<C>::new_known(cs, &this.utxo_randomness, mode), - ), - Allocation::Unknown(mode) => Self::new( - VoidNumberGeneratorVar::<C>::new_unknown(cs, mode), - VoidNumberCommitmentRandomnessVar::<C>::new_unknown(cs, mode), - UtxoRandomnessVar::<C>::new_unknown(cs, mode), - ), - } - } - } - - impl<C> HasAllocation<C::ConstraintSystem> for AssetParameters<C> - where - C: Configuration, - { - type Variable = AssetParametersVar<C>; - type Mode = Secret; - } - - /// Sender Variable - pub struct SenderVar<C, V> - where - C: Configuration, - V: Verifier<Item = Utxo<C>> + ?Sized, - C::ConstraintSystem: HasVariable<V::Output> + HasVariable<V::Witness>, - { - /// Secret Key - secret_key: SecretKeyVar<C>, - - /// Asset - asset: AssetVar<C::ConstraintSystem>, - - /// Asset Parameters - parameters: AssetParametersVar<C>, - - /// Void Number - void_number: VoidNumberVar<C>, - - /// Void Number Commitment - void_number_commitment: VoidNumberCommitmentVar<C>, - - /// Unspent Transaction Output - utxo: UtxoVar<C>, - - /// UTXO Membership Proof - utxo_membership_proof: UtxoMembershipProofVar<C, V>, - } - - impl<C, V> SenderVar<C, V> - where - C: Configuration, - V: Verifier<Item = Utxo<C>> + ?Sized, - C::ConstraintSystem: HasVariable<V::Output> + HasVariable<V::Witness>, - { - /// Checks if `self` is a well-formed sender and returns its asset. - #[inline] - pub fn get_well_formed_asset<VV>( - self, - cs: &mut C::ConstraintSystem, - commitment_scheme: &C::CommitmentSchemeVar, - utxo_set_verifier: &VV, - ) -> AssetVar<C::ConstraintSystem> - where - VV: VerifierVariable<C::ConstraintSystem, ItemVar = UtxoVar<C>, Type = V>, - { - cs.assert_eq( - &self.void_number, - &C::PseudorandomFunctionFamilyVar::evaluate( - &self.secret_key, - &self.parameters.void_number_generator, - ), - ); - - // TODO: Prepare commitment input during allocation instead of here, could reduce - // constraint/variable count. - cs.assert_eq( - &self.void_number_commitment, - &generate_void_number_commitment( - commitment_scheme, - &self.parameters.void_number_generator, - &self.parameters.void_number_commitment_randomness, - ), - ); - - // TODO: Prepare commitment input during allocation instead of here, could reduce - // constraint/variable count. - cs.assert_eq( - &self.utxo, - &generate_utxo( - commitment_scheme, - &self.asset, - &self.void_number_commitment, - &self.parameters.utxo_randomness, - ), - ); - - self.utxo_membership_proof - .assert_validity(&self.utxo, utxo_set_verifier, cs); - self.asset - } - } - - impl<C, V> Variable<C::ConstraintSystem> for SenderVar<C, V> - where - C: Configuration, - V: Verifier<Item = Utxo<C>> + ?Sized, - C::ConstraintSystem: - HasVariable<V::Output, Mode = Public> + HasVariable<V::Witness, Mode = Secret>, - { - type Type = Sender<C, V>; - - type Mode = Derived; - - #[inline] - fn new( - cs: &mut C::ConstraintSystem, - allocation: Allocation<Self::Type, Self::Mode>, - ) -> Self { - match allocation { - Allocation::Known(this, mode) => Self { - secret_key: SecretKeyVar::<C>::new_known(cs, &this.secret_key, mode), - asset: this.asset.known(cs, mode), - parameters: this.parameters.known(cs, mode), - void_number: VoidNumberVar::<C>::new_known(cs, &this.void_number, Public), - void_number_commitment: VoidNumberCommitmentVar::<C>::new_known( - cs, - &this.void_number_commitment, - Secret, - ), - utxo: UtxoVar::<C>::new_known(cs, &this.utxo, Secret), - utxo_membership_proof: this.utxo_membership_proof.known(cs, mode), - }, - Allocation::Unknown(mode) => Self { - secret_key: SecretKeyVar::<C>::new_unknown(cs, mode), - asset: Asset::unknown(cs, mode), - parameters: AssetParameters::unknown(cs, mode), - void_number: VoidNumberVar::<C>::new_unknown(cs, Public), - void_number_commitment: VoidNumberCommitmentVar::<C>::new_unknown(cs, Secret), - utxo: UtxoVar::<C>::new_unknown(cs, Secret), - utxo_membership_proof: MembershipProof::<V>::unknown(cs, mode), - }, - } - } - } - - impl<C, V> HasAllocation<C::ConstraintSystem> for Sender<C, V> - where - C: Configuration, - V: Verifier<Item = Utxo<C>> + ?Sized, - C::ConstraintSystem: - HasVariable<V::Output, Mode = Public> + HasVariable<V::Witness, Mode = Secret>, - { - type Variable = SenderVar<C, V>; - type Mode = Derived; - } - - impl<C, V> SenderPost<C, V> - where - C: Configuration, - V: Verifier<Item = Utxo<C>> + ?Sized, - C::ConstraintSystem: HasVariable<V::Output, Mode = Public>, - { - /// Extends proof public input with `self`. - #[inline] - pub fn extend_input<P>(&self, input: &mut P::Input) - where - P: ProofSystem<ConstraintSystem = C::ConstraintSystem> - + ProofSystemInput<VoidNumber<C>> - + ProofSystemInput<V::Output>, - { - // TODO: Add a "public part" trait that extracts the public part of `Sender` (using - // `SenderVar` to determine the types), then generate this method automatically. - P::extend(input, &self.void_number); - P::extend(input, &self.utxo_checkpoint); - } - } - - /// Receiver Variable - pub struct ReceiverVar<C, I> - where - C: Configuration, - I: IntegratedEncryptionScheme<Plaintext = Asset>, - { - /// Asset - asset: AssetVar<C::ConstraintSystem>, - - /// UTXO Randomness - utxo_randomness: UtxoRandomnessVar<C>, - - /// Void Number Commitment - void_number_commitment: VoidNumberCommitmentVar<C>, - - /// Unspent Transaction Output - utxo: UtxoVar<C>, - - /// Type Parameter Marker - __: PhantomData<I>, - } - - impl<C, I> ReceiverVar<C, I> - where - C: Configuration, - I: IntegratedEncryptionScheme<Plaintext = Asset>, - { - /// Checks if `self` is a well-formed receiver and returns its asset. - #[inline] - pub fn get_well_formed_asset( - self, - cs: &mut C::ConstraintSystem, - commitment_scheme: &C::CommitmentSchemeVar, - ) -> AssetVar<C::ConstraintSystem> { - cs.assert_eq( - &self.utxo, - &generate_utxo( - commitment_scheme, - &self.asset, - &self.void_number_commitment, - &self.utxo_randomness, - ), - ); - self.asset - } - } - - impl<C, I> Variable<C::ConstraintSystem> for ReceiverVar<C, I> - where - C: Configuration, - I: IntegratedEncryptionScheme<Plaintext = Asset>, - { - type Type = Receiver<C, I>; - - type Mode = Derived; - - #[inline] - fn new( - cs: &mut C::ConstraintSystem, - allocation: Allocation<Self::Type, Self::Mode>, - ) -> Self { - match allocation { - Allocation::Known(this, mode) => Self { - asset: AssetVar::new_known(cs, &this.asset, mode), - utxo_randomness: UtxoRandomnessVar::<C>::new_known( - cs, - &this.utxo_randomness, - mode, - ), - void_number_commitment: VoidNumberCommitmentVar::<C>::new_known( - cs, - &this.void_number_commitment, - Secret, - ), - utxo: UtxoVar::<C>::new_known(cs, &this.utxo, Public), - __: PhantomData, - }, - Allocation::Unknown(mode) => Self { - asset: AssetVar::new_unknown(cs, mode), - utxo_randomness: UtxoRandomnessVar::<C>::new_unknown(cs, mode), - void_number_commitment: VoidNumberCommitmentVar::<C>::new_unknown(cs, Secret), - utxo: UtxoVar::<C>::new_unknown(cs, Public), - __: PhantomData, - }, - } - } - } - - impl<C, I> HasAllocation<C::ConstraintSystem> for Receiver<C, I> - where - C: Configuration, - I: IntegratedEncryptionScheme<Plaintext = Asset>, - { - type Variable = ReceiverVar<C, I>; - type Mode = Derived; - } - - impl<C, I> ReceiverPost<C, I> - where - C: Configuration, - I: IntegratedEncryptionScheme<Plaintext = Asset>, - { - /// Extends proof public input with `self`. - #[inline] - pub fn extend_input<P>(&self, input: &mut P::Input) - where - P: ProofSystem<ConstraintSystem = C::ConstraintSystem> + ProofSystemInput<Utxo<C>>, - { - // TODO: Add a "public part" trait that extracts the public part of `Receiver` (using - // `ReceiverVar` to determine the types), then generate this method automatically. - P::extend(input, &self.utxo); - } + ledger.register(self.utxo, self.note, super_key); } } diff --git a/manta-accounting/src/identity2.rs b/manta-accounting/src/identity2.rs deleted file mode 100644 index 214501a74..000000000 --- a/manta-accounting/src/identity2.rs +++ /dev/null @@ -1,805 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Sender and Receiver Identities - -use core::marker::PhantomData; -use manta_crypto::{ - accumulator::{Accumulator, MembershipProof, Verifier}, - commitment::{CommitmentScheme, Input as CommitmentInput}, - encryption::{EncryptedMessage, HybridPublicKeyEncryptionScheme}, - key::KeyAgreementScheme, -}; - -/// Viewing Key Table -pub trait ViewingKeyTable<K> -where - K: KeyAgreementScheme, -{ - /// Query Type - type Query; - - /// Returns the key associated to `query`, generating a new key if `query` does not already - /// correspond to an existing key. - fn get_or_generate(&mut self, query: Self::Query) -> &K::SecretKey; -} - -impl<K> ViewingKeyTable<K> for K::SecretKey -where - K: KeyAgreementScheme, -{ - type Query = (); - - #[inline] - fn get_or_generate(&mut self, query: Self::Query) -> &K::SecretKey { - self - } -} - -/// Spending Key -pub struct SpendingKey<K, V = <K as KeyAgreementScheme>::SecretKey> -where - K: KeyAgreementScheme, - V: ViewingKeyTable<K>, -{ - /// Spending Key - spending_key: K::SecretKey, - - /// Viewing Key Table - viewing_key_table: V, -} - -impl<K, V> SpendingKey<K, V> -where - K: KeyAgreementScheme, - V: ViewingKeyTable<K>, -{ - /// Builds a new [`SpendingKey`] from `spending_key` and `viewing_key_table`. - #[inline] - pub fn new(spending_key: K::SecretKey, viewing_key_table: V) -> Self { - Self { - spending_key, - viewing_key_table, - } - } - - /// Returns the receiving key for `self` with the viewing key located at the given `query` in - /// the viewing key table. - #[inline] - pub fn receiving_key(&mut self, query: V::Query) -> ReceivingKey<K> { - ReceivingKey { - spend: K::derive(&self.spending_key), - view: K::derive(self.viewing_key_table.get_or_generate(query)), - } - } - - /// Prepares `self` for spending `asset` with the given `ephemeral_key`. - #[inline] - pub fn pre_sender<C>( - &self, - ephemeral_key: PublicKey<C>, - asset: C::Asset, - commitment_scheme: &C::CommitmentScheme, - ) -> PreSender<C> - where - K::SecretKey: Clone, - C: Configuration<KeyAgreementScheme = K>, - { - PreSender::new( - self.spending_key.clone(), - ephemeral_key, - asset, - commitment_scheme, - ) - } -} - -/// Receiving Key -pub struct ReceivingKey<K> -where - K: KeyAgreementScheme, -{ - /// Spend Part of the Receiving Key - pub spend: K::PublicKey, - - /// View Part of the Receiving Key - pub view: K::PublicKey, -} - -impl<K> ReceivingKey<K> -where - K: KeyAgreementScheme, -{ - /// Prepares `self` for receiving `asset` with the given `ephemeral_key`. - #[inline] - pub fn into_receiver<C>( - self, - ephemeral_key: SecretKey<C>, - asset: C::Asset, - commitment_scheme: &C::CommitmentScheme, - ) -> FullReceiver<C> - where - C: Configuration<KeyAgreementScheme = K>, - { - FullReceiver::new( - self.spend, - self.view, - ephemeral_key, - asset, - commitment_scheme, - ) - } -} - -/// Identity Configuration -pub trait Configuration { - /// Asset Type - type Asset; - - /// Key Agreement Scheme Type - type KeyAgreementScheme: KeyAgreementScheme; - - /// Commitment Scheme Type - type CommitmentScheme: CommitmentScheme<Randomness = Trapdoor<Self>> - + CommitmentInput<Self::Asset> - + CommitmentInput<SecretKey<Self>>; -} - -/// Secret Key Type -pub type SecretKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::SecretKey; - -/// Public Key Type -pub type PublicKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::PublicKey; - -/// Trapdoor Type -pub type Trapdoor<C> = - <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret; - -/// Commitment Scheme Output Type -pub type CommitmentSchemeOutput<C> = - <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Output; - -/// Unspent Transaction Output Type -pub type Utxo<C> = CommitmentSchemeOutput<C>; - -/// Void Number Type -pub type VoidNumber<C> = CommitmentSchemeOutput<C>; - -/// Pre-Sender -pub struct PreSender<C> -where - C: Configuration, -{ - /// Secret Spending Key - spending_key: SecretKey<C>, - - /// Ephemeral Public Key - ephemeral_key: PublicKey<C>, - - /// Asset - asset: C::Asset, - - /// Unspent Transaction Output - utxo: Utxo<C>, - - /// Void Number - void_number: VoidNumber<C>, -} - -impl<C> PreSender<C> -where - C: Configuration, -{ - /// Builds a new [`PreSender`] for `spending_key` to spend `asset` with - /// `ephemeral_key`. - #[inline] - pub fn new( - spending_key: SecretKey<C>, - ephemeral_key: PublicKey<C>, - asset: C::Asset, - commitment_scheme: &C::CommitmentScheme, - ) -> Self { - let trapdoor = C::KeyAgreementScheme::agree(&spending_key, &ephemeral_key); - Self { - utxo: commitment_scheme.commit_one(&asset, &trapdoor), - void_number: commitment_scheme.commit_one(&spending_key, &trapdoor), - spending_key, - ephemeral_key, - asset, - } - } - - /// Inserts the [`Utxo`] corresponding to `self` into the `utxo_set` with the intention of - /// returning a proof later by a call to [`get_proof`](Self::get_proof). - #[inline] - pub fn insert_utxo<S>(&self, utxo_set: &mut S) -> bool - where - S: Accumulator<Item = Utxo<C>>, - { - utxo_set.insert(&self.utxo) - } - - /// Requests the membership proof of the [`Utxo`] corresponding to `self` from `utxo_set` to - /// prepare the conversion from `self` into a [`Sender`]. - #[inline] - pub fn get_proof<S>(&self, utxo_set: &S) -> Option<SenderProof<C, S::Verifier>> - where - S: Accumulator<Item = Utxo<C>>, - { - Some(SenderProof { - utxo_membership_proof: utxo_set.prove(&self.utxo)?, - __: PhantomData, - }) - } - - /// Converts `self` into a [`Sender`] by attaching `proof` to it. - /// - /// # Note - /// - /// When using this method, be sure to check that [`SenderProof::can_upgrade`] returns `true`. - /// Otherwise, using the sender returned here will most likely return an error when posting to - /// the ledger. - #[inline] - pub fn upgrade<V>(self, proof: SenderProof<C, V>) -> Sender<C, V> - where - V: Verifier<Item = Utxo<C>> + ?Sized, - { - Sender { - spending_key: self.spending_key, - ephemeral_key: self.ephemeral_key, - asset: self.asset, - utxo_membership_proof: proof.utxo_membership_proof, - void_number: self.void_number, - } - } - - /// Tries to convert `self` into a [`Sender`] by getting a proof from `utxo_set`. - #[inline] - pub fn try_upgrade<S>(self, utxo_set: &S) -> Option<Sender<C, S::Verifier>> - where - S: Accumulator<Item = Utxo<C>>, - { - Some(self.get_proof(utxo_set)?.upgrade(self)) - } -} - -/// Sender Proof -/// -/// This `struct` is created by the [`get_proof`](PreSender::get_proof) method on [`PreSender`]. -/// See its documentation for more. -pub struct SenderProof<C, V> -where - C: Configuration, - V: Verifier<Item = Utxo<C>> + ?Sized, -{ - /// UTXO Membership Proof - utxo_membership_proof: MembershipProof<V>, - - /// Type Parameter Marker - __: PhantomData<C>, -} - -impl<C, V> SenderProof<C, V> -where - C: Configuration, - V: Verifier<Item = Utxo<C>> + ?Sized, -{ - /// Returns `true` if a [`PreSender`] could be upgraded using `self` given the `utxo_set`. - #[inline] - pub fn can_upgrade<S>(&self, utxo_set: &S) -> bool - where - S: Accumulator<Item = V::Item, Verifier = V>, - { - self.utxo_membership_proof.matching_output(utxo_set) - } - - /// Upgrades the `pre_sender` to a [`Sender`] by attaching `self` to it. - /// - /// # Note - /// - /// When using this method, be sure to check that [`can_upgrade`](Self::can_upgrade) returns - /// `true`. Otherwise, using the sender returned here will most likely return an error when - /// posting to the ledger. - #[inline] - pub fn upgrade(self, pre_sender: PreSender<C>) -> Sender<C, V> { - pre_sender.upgrade(self) - } -} - -/// Sender -pub struct Sender<C, V> -where - C: Configuration, - V: Verifier<Item = Utxo<C>> + ?Sized, -{ - /// Secret Spend Key - spending_key: SecretKey<C>, - - /// Ephemeral Public Key - ephemeral_key: PublicKey<C>, - - /// Asset - asset: C::Asset, - - /// UTXO Membership Proof - utxo_membership_proof: MembershipProof<V>, - - /// Void Number - void_number: VoidNumber<C>, -} - -impl<C, V> Sender<C, V> -where - C: Configuration, - V: Verifier<Item = Utxo<C>>, -{ - /// Reverts `self` back into a [`PreSender`]. - /// - /// This method should be called if the [`Utxo`] membership proof attached to `self` was deemed - /// invalid or had expired. - #[inline] - pub fn downgrade(self, commitment_scheme: &C::CommitmentScheme) -> PreSender<C> { - PreSender { - utxo: commitment_scheme.commit_one( - &self.asset, - &C::KeyAgreementScheme::agree(&self.spending_key, &self.ephemeral_key), - ), - spending_key: self.spending_key, - ephemeral_key: self.ephemeral_key, - asset: self.asset, - void_number: self.void_number, - } - } - - /// Extracts the ledger posting data from `self`. - #[inline] - pub fn into_post(self) -> SenderPost<C, V> { - SenderPost { - utxo_accumulator_output: self.utxo_membership_proof.into_output(), - void_number: self.void_number, - } - } -} - -/// Sender Ledger -pub trait SenderLedger<C, V> -where - C: Configuration, - V: Verifier<Item = Utxo<C>>, -{ - /// Valid [`VoidNumber`] Posting Key - /// - /// # Safety - /// - /// This type must be some wrapper around [`VoidNumber`] which can only be constructed by this - /// implementation of [`SenderLedger`]. This is to prevent that [`spend`](Self::spend) is - /// called before [`is_unspent`](Self::is_unspent) and - /// [`has_matching_utxo_accumulator_output`](Self::has_matching_utxo_accumulator_output). - type ValidVoidNumber; - - /// Valid UTXO Accumulator Output Posting Key - /// - /// # Safety - /// - /// This type must be some wrapper around [`S::Output`] which can only be constructed by this - /// implementation of [`SenderLedger`]. This is to prevent that [`spend`](Self::spend) is - /// called before [`is_unspent`](Self::is_unspent) and - /// [`has_matching_utxo_accumulator_output`](Self::has_matching_utxo_accumulator_output). - /// - /// [`S::Output`]: Verifier::Output - type ValidUtxoAccumulatorOutput; - - /// Super Posting Key - /// - /// Type that allows super-traits of [`SenderLedger`] to customize posting key behavior. - type SuperPostingKey: Copy; - - /// Checks if the ledger already contains the `void_number` in its set of void numbers. - /// - /// Existence of such a void number could indicate a possible double-spend. - fn is_unspent(&self, void_number: VoidNumber<C>) -> Option<Self::ValidVoidNumber>; - - /// Checks if `output` matches the current accumulated value of the UTXO set that is stored on - /// the ledger. - /// - /// Failure to match the ledger state means that the sender was constructed under an invalid or - /// older state of the ledger. - fn has_matching_utxo_accumulator_output( - &self, - output: V::Output, - ) -> Option<Self::ValidUtxoAccumulatorOutput>; - - /// Posts the `void_number` to the ledger, spending the asset. - /// - /// # Safety - /// - /// This method can only be called once we check that `void_number` is not already stored on - /// the ledger. See [`is_unspent`](Self::is_unspent). - fn spend( - &mut self, - utxo_accumulator_output: Self::ValidUtxoAccumulatorOutput, - void_number: Self::ValidVoidNumber, - super_key: &Self::SuperPostingKey, - ); -} - -/// Sender Post Error -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum SenderPostError { - /// Asset Spent Error - /// - /// The asset has already been spent. - AssetSpent, - - /// Invalid UTXO Accumulator Error - /// - /// The sender was not constructed under the current state of the UTXO set. - InvalidUtxoAccumulator, -} - -/// Sender Post -pub struct SenderPost<C, V> -where - C: Configuration, - V: Verifier<Item = Utxo<C>>, -{ - /// UTXO Accumulator Output - utxo_accumulator_output: V::Output, - - /// Void Number - void_number: VoidNumber<C>, -} - -impl<C, V> SenderPost<C, V> -where - C: Configuration, - V: Verifier<Item = Utxo<C>>, -{ - /// Validates `self` on the sender `ledger`. - #[inline] - pub fn validate<L>(self, ledger: &L) -> Result<SenderPostingKey<C, V, L>, SenderPostError> - where - L: SenderLedger<C, V>, - { - Ok(SenderPostingKey { - utxo_accumulator_output: ledger - .has_matching_utxo_accumulator_output(self.utxo_accumulator_output) - .ok_or(SenderPostError::InvalidUtxoAccumulator)?, - void_number: ledger - .is_unspent(self.void_number) - .ok_or(SenderPostError::AssetSpent)?, - }) - } -} - -/// Sender Posting Key -pub struct SenderPostingKey<C, V, L> -where - C: Configuration, - V: Verifier<Item = Utxo<C>>, - L: SenderLedger<C, V>, -{ - /// UTXO Accumulator Output Posting Key - utxo_accumulator_output: L::ValidUtxoAccumulatorOutput, - - /// Void Number Posting Key - void_number: L::ValidVoidNumber, -} - -impl<C, V, L> SenderPostingKey<C, V, L> -where - C: Configuration, - V: Verifier<Item = Utxo<C>>, - L: SenderLedger<C, V>, -{ - /// Posts `self` to the sender `ledger`. - #[inline] - pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) { - ledger.spend(self.utxo_accumulator_output, self.void_number, super_key); - } -} - -/// Receiver -pub struct Receiver<C> -where - C: Configuration, -{ - /// Public Spending Key - spending_key: PublicKey<C>, - - /// Ephemeral Secret Key - ephemeral_key: SecretKey<C>, - - /// Asset - asset: C::Asset, - - /// Unspent Transaction Output - utxo: Utxo<C>, -} - -impl<C> Receiver<C> -where - C: Configuration, -{ - /// Generates the [`Utxo`] for a [`Receiver`] with the given parameters. - #[inline] - fn generate_utxo( - spending_key: &PublicKey<C>, - ephemeral_key: &SecretKey<C>, - asset: &C::Asset, - commitment_scheme: &C::CommitmentScheme, - ) -> Utxo<C> { - commitment_scheme.commit_one( - asset, - &C::KeyAgreementScheme::agree(ephemeral_key, spending_key), - ) - } -} - -/// Full Receiver -pub struct FullReceiver<C> -where - C: Configuration, -{ - /// Public Spending Key - spending_key: PublicKey<C>, - - /// Public Viewing Key - viewing_key: PublicKey<C>, - - /// Ephemeral Secret Key - ephemeral_key: SecretKey<C>, - - /// Asset - asset: C::Asset, - - /// Unspent Transaction Output - utxo: Utxo<C>, -} - -impl<C> FullReceiver<C> -where - C: Configuration, -{ - /// Builds a new [`Receiver`] for `spending_key` to receive `asset` with - /// `ephemeral_key`. - #[inline] - pub fn new( - spending_key: PublicKey<C>, - viewing_key: PublicKey<C>, - ephemeral_key: SecretKey<C>, - asset: C::Asset, - commitment_scheme: &C::CommitmentScheme, - ) -> Self { - Self { - utxo: Receiver::<C>::generate_utxo( - &spending_key, - &ephemeral_key, - &asset, - commitment_scheme, - ), - spending_key, - viewing_key, - ephemeral_key, - asset, - } - } - - /// Extracts the ledger posting data from `self`. - #[inline] - pub fn into_post<H>(self) -> ReceiverPost<C, H> - where - H: HybridPublicKeyEncryptionScheme< - Plaintext = C::Asset, - KeyAgreementScheme = C::KeyAgreementScheme, - >, - { - ReceiverPost { - utxo: self.utxo, - note: EncryptedMessage::new(&self.viewing_key, self.ephemeral_key, self.asset), - } - } -} - -/// Receiver Ledger -pub trait ReceiverLedger<C, H> -where - C: Configuration, - H: HybridPublicKeyEncryptionScheme< - Plaintext = C::Asset, - KeyAgreementScheme = C::KeyAgreementScheme, - >, -{ - /// Valid [`Utxo`] Posting Key - /// - /// # Safety - /// - /// This type must be some wrapper around [`Utxo`] which can only be constructed by this - /// implementation of [`ReceiverLedger`]. This is to prevent that [`register`](Self::register) - /// is called before [`is_not_registered`](Self::is_not_registered). - type ValidUtxo; - - /// Super Posting Key - /// - /// Type that allows super-traits of [`ReceiverLedger`] to customize posting key behavior. - type SuperPostingKey: Copy; - - /// Checks if the ledger already contains the `utxo` in its set of UTXOs. - /// - /// Existence of such a UTXO could indicate a possible double-spend. - fn is_not_registered(&self, utxo: Utxo<C>) -> Option<Self::ValidUtxo>; - - /// Posts the `utxo` and `encrypted_asset` to the ledger, registering the asset. - /// - /// # Safety - /// - /// This method can only be called once we check that `utxo` is not already stored on the - /// ledger. See [`is_not_registered`](Self::is_not_registered). - fn register( - &mut self, - utxo: Self::ValidUtxo, - note: EncryptedMessage<H>, - super_key: &Self::SuperPostingKey, - ); -} - -/// Receiver Post Error -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum ReceiverPostError { - /// Asset Registered Error - /// - /// The asset has already been registered with the ledger. - AssetRegistered, -} - -/// Receiver Post -pub struct ReceiverPost<C, H> -where - C: Configuration, - H: HybridPublicKeyEncryptionScheme< - Plaintext = C::Asset, - KeyAgreementScheme = C::KeyAgreementScheme, - >, -{ - /// Unspent Transaction Output - utxo: Utxo<C>, - - /// Encrypted Note - note: EncryptedMessage<H>, -} - -impl<C, H> ReceiverPost<C, H> -where - C: Configuration, - H: HybridPublicKeyEncryptionScheme< - Plaintext = C::Asset, - KeyAgreementScheme = C::KeyAgreementScheme, - >, -{ - /// Validates `self` on the receiver `ledger`. - #[inline] - pub fn validate<L>(self, ledger: &L) -> Result<ReceiverPostingKey<C, H, L>, ReceiverPostError> - where - L: ReceiverLedger<C, H>, - { - Ok(ReceiverPostingKey { - utxo: ledger - .is_not_registered(self.utxo) - .ok_or(ReceiverPostError::AssetRegistered)?, - note: self.note, - }) - } -} - -/// Receiver Posting Key -pub struct ReceiverPostingKey<C, H, L> -where - C: Configuration, - H: HybridPublicKeyEncryptionScheme< - Plaintext = C::Asset, - KeyAgreementScheme = C::KeyAgreementScheme, - >, - L: ReceiverLedger<C, H>, -{ - /// UTXO Posting Key - utxo: L::ValidUtxo, - - /// Encrypted Note - note: EncryptedMessage<H>, -} - -impl<C, H, L> ReceiverPostingKey<C, H, L> -where - C: Configuration, - H: HybridPublicKeyEncryptionScheme< - Plaintext = C::Asset, - KeyAgreementScheme = C::KeyAgreementScheme, - >, - L: ReceiverLedger<C, H>, -{ - /// Posts `self` to the receiver `ledger`. - #[inline] - pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) { - ledger.register(self.utxo, self.note, super_key); - } -} - -/// Constraint System Gadgets for Identities -pub mod constraint { - use super::*; - use manta_crypto::constraint::{ConstraintSystem, Equal}; - - impl<C, V> Sender<C, V> - where - C: Configuration, - V: Verifier<Item = Utxo<C>> + ?Sized, - { - /// Returns the asset for `self`, checking if `self` is well-formed in the given constraint - /// system `cs`. - #[inline] - pub fn get_well_formed_asset<CS>( - self, - commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &V, - cs: &mut CS, - ) -> C::Asset - where - CS: ConstraintSystem, - CommitmentSchemeOutput<C>: Equal<CS>, - V: Verifier<Verification = CS::Bool>, - { - let trapdoor = C::KeyAgreementScheme::agree(&self.spending_key, &self.ephemeral_key); - cs.assert(self.utxo_membership_proof.verify( - &commitment_scheme.commit_one(&self.asset, &trapdoor), - utxo_set_verifier, - )); - cs.assert_eq( - &self.void_number, - &commitment_scheme.commit_one(&self.spending_key, &trapdoor), - ); - self.asset - } - } - - impl<C> Receiver<C> - where - C: Configuration, - { - /// Returns the asset for `self`, checking if `self` is well-formed in the given constraint - /// system `cs`. - #[inline] - pub fn get_well_formed_asset<CS>( - self, - commitment_scheme: &C::CommitmentScheme, - cs: &mut CS, - ) -> C::Asset - where - CS: ConstraintSystem, - Utxo<C>: Equal<CS>, - { - cs.assert_eq( - &self.utxo, - &Self::generate_utxo( - &self.spending_key, - &self.ephemeral_key, - &self.asset, - commitment_scheme, - ), - ); - self.asset - } - } -} diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index 57ec37ea6..451eac9b6 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -14,889 +14,117 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Secret Key Generation Primitives +//! Hierarchical Key Tables -// NOTE: These interfaces are based on BIP-0044. See the specification here: -// https://raw.githubusercontent.com/bitcoin/bips/master/bip-0044.mediawiki +// TODO: Make [`Account`] more useful for managing accounts. -// TODO: Check to make sure we conform to the specification and then make a note about it in the -// module documentation, and add a link to the specification. -// TODO: Try to get rid of `KeyOwned` if possible, or at least minimize its use. +use core::{fmt::Debug, hash::Hash}; -use core::{convert::TryFrom, fmt::Debug, hash::Hash, ops::Range}; - -/// Derived Secret Key Parameter -pub trait DerivedSecretKeyParameter: Clone + Default + PartialOrd { +/// Hierarchical Key Table Parameter +pub trait HierarchicalKeyTableParameter: Clone + Default + PartialOrd { /// Increments the key parameter by one unit. fn increment(&mut self); } -/// Derived Secret Key Generator -pub trait DerivedSecretKeyGenerator { - /// Secret Key Type - type SecretKey; - +/// Hierarchical Key Table +pub trait HierarchicalKeyTable { /// Account Type - type Account: DerivedSecretKeyParameter; + type Account: HierarchicalKeyTableParameter; /// Index Type - type Index: DerivedSecretKeyParameter; + type Index: HierarchicalKeyTableParameter; - /// Key Generation Error - type Error; + /// Key Kind Type + type Kind; - /// Generates a new secret key determined by `kind` for the `account` with - /// the given `index`. - fn generate_key( - &self, - kind: KeyKind, - account: &Self::Account, - index: &Self::Index, - ) -> Result<Self::SecretKey, Self::Error>; + /// Secret Key Type + type SecretKey; - /// Generates a new external secret key for the `account` with the given `index`. - #[inline] - fn generate_external_key( - &self, - account: &Self::Account, - index: &Self::Index, - ) -> Result<Self::SecretKey, Self::Error> { - self.generate_key(KeyKind::External, account, index) - } + /// Key Access Error Type + type Error; - /// Generates a new internal secret key for the `account` with the given `index`. - #[inline] - fn generate_internal_key( + /// Returns the secret key associated to `account` and `index` of the given `kind`. + fn get( &self, account: &Self::Account, index: &Self::Index, - ) -> Result<Self::SecretKey, Self::Error> { - self.generate_key(KeyKind::Internal, account, index) - } + kind: &Self::Kind, + ) -> Result<Self::SecretKey, Self::Error>; } -impl<D> DerivedSecretKeyGenerator for &D +impl<H> HierarchicalKeyTable for &H where - D: DerivedSecretKeyGenerator, + H: HierarchicalKeyTable, { - type SecretKey = D::SecretKey; + type Account = H::Account; - type Account = D::Account; + type Index = H::Index; - type Index = D::Index; + type Kind = H::Kind; - type Error = D::Error; + type SecretKey = H::SecretKey; - #[inline] - fn generate_key( - &self, - kind: KeyKind, - account: &Self::Account, - index: &Self::Index, - ) -> Result<Self::SecretKey, Self::Error> { - (*self).generate_key(kind, account, index) - } - - #[inline] - fn generate_external_key( - &self, - account: &Self::Account, - index: &Self::Index, - ) -> Result<Self::SecretKey, Self::Error> { - (*self).generate_external_key(account, index) - } + type Error = H::Error; #[inline] - fn generate_internal_key( + fn get( &self, account: &Self::Account, index: &Self::Index, + kind: &Self::Kind, ) -> Result<Self::SecretKey, Self::Error> { - (*self).generate_internal_key(account, index) - } -} - -/// Key Kind -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum KeyKind { - /// External Key - External, - - /// Internal Key - Internal, -} - -impl KeyKind { - /// Returns `true` if `self` matches [`External`](Self::External). - #[inline] - pub const fn is_external(&self) -> bool { - matches!(self, Self::External) - } - - /// Returns `true` if `self` matches [`Internal`](Self::Internal). - #[inline] - pub const fn is_internal(&self) -> bool { - matches!(self, Self::Internal) + (*self).get(account, index, kind) } } -/// External Key Kind -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct External; - -impl From<External> for KeyKind { - #[inline] - fn from(kind: External) -> Self { - let _ = kind; - Self::External - } -} - -/// Internal Key Kind -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Internal; - -impl From<Internal> for KeyKind { - #[inline] - fn from(kind: Internal) -> Self { - let _ = kind; - Self::Internal - } -} - -/// External Secret Key Index Type -pub type ExternalIndex<D> = Index<D, External>; - -/// Internal Secret Key Index Type -pub type InternalIndex<D> = Index<D, Internal>; - -/// Secret Key Index +/// Account Index with Table #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "K: Clone"), - Copy(bound = "K: Copy, D::Index: Copy"), - Debug(bound = "K: Debug, D::Index: Debug"), - Eq(bound = "K: Eq, D::Index: Eq"), - Hash(bound = "K: Hash, D::Index: Hash"), - PartialEq(bound = "K: PartialEq") + Clone(bound = "H: Clone,"), + Copy(bound = "H: Copy, H::Account: Copy, H::Index: Copy"), + Debug(bound = "H: Debug, H::Account: Debug, H::Index: Debug"), + Default(bound = "H: Default"), + Eq(bound = "H: Eq, H::Account: Eq, H::Index: Eq"), + Hash(bound = "H: Hash, H::Account: Hash, H::Index: Hash"), + PartialEq(bound = "H: PartialEq") )] -pub struct Index<D, K = KeyKind> -where - D: DerivedSecretKeyGenerator, - K: Into<KeyKind>, -{ - /// Key Kind - pub kind: K, - - /// Key Index - pub index: D::Index, -} - -impl<D, K> Index<D, K> -where - D: DerivedSecretKeyGenerator, - K: Into<KeyKind>, -{ - /// Builds a new [`Index`] for `kind` and `index`. - #[inline] - pub fn new(kind: K, index: D::Index) -> Self { - Self { kind, index } - } - - /// Increments the internal `index`. - #[inline] - pub fn increment(&mut self) { - self.index.increment(); - } - - /// Reduces `self` into an [`Index`] with [`KeyKind`] as the key kind. - #[inline] - pub fn reduce(self) -> Index<D> { - Index::new(self.kind.into(), self.index) - } - - /// Wraps an `inner` value into a [`KeyOwned`] with `self` as the index. - #[inline] - pub fn wrap<T>(self, inner: T) -> KeyOwned<D, T, K> { - KeyOwned::new(inner, self) - } -} - -impl<D> Index<D> -where - D: DerivedSecretKeyGenerator, -{ - /// Builds a new [`Index`] for an external key with the given `index`. - #[inline] - pub fn new_external(index: D::Index) -> Self { - Self::new(KeyKind::External, index) - } - - /// Builds a new [`Index`] for an internal key with the given `index`. - #[inline] - pub fn new_internal(index: D::Index) -> Self { - Self::new(KeyKind::Internal, index) - } - - /// Returns `true` if `self` represents an external key. - #[inline] - pub fn is_external(&self) -> bool { - self.kind.is_external() - } - - /// Returns `true` if `self` represents an internal key. - #[inline] - pub fn is_internal(&self) -> bool { - self.kind.is_internal() - } - - /// Gets the underlying key for this `index` at the `account`. - #[inline] - pub fn key(&self, source: &D, account: &D::Account) -> Result<D::SecretKey, D::Error> { - source.generate_key(self.kind, account, &self.index) - } -} - -impl<D> ExternalIndex<D> -where - D: DerivedSecretKeyGenerator, -{ - /// Builds a new [`Index`] for an external key with the given `index`. - #[inline] - pub fn from(index: D::Index) -> Self { - Self::new(External, index) - } - - /// Gets the underlying key for this `index` at the `account`. - #[inline] - pub fn key(&self, source: &D, account: &D::Account) -> Result<D::SecretKey, D::Error> { - source.generate_external_key(account, &self.index) - } -} - -impl<D> InternalIndex<D> -where - D: DerivedSecretKeyGenerator, -{ - /// Builds a new [`Index`] for an internal key with the given `index`. - #[inline] - pub fn from(index: D::Index) -> Self { - Self::new(Internal, index) - } - - /// Gets the underlying key for this `index` at the `account`. - #[inline] - pub fn key(&self, source: &D, account: &D::Account) -> Result<D::SecretKey, D::Error> { - source.generate_internal_key(account, &self.index) - } -} - -/// Labelled Secret Key Type -pub type SecretKey<D, K = KeyKind> = KeyOwned<D, <D as DerivedSecretKeyGenerator>::SecretKey, K>; - -/// Labelled External Secret Key Type -pub type ExternalSecretKey<D> = SecretKey<D, External>; - -/// Labelled Internal Secret Key Type -pub type InternalSecretKey<D> = SecretKey<D, Internal>; - -/// External Key-Owned Value Type -pub type ExternalKeyOwned<D, T> = KeyOwned<D, T, External>; - -/// Internal Key-Owned Value Type -pub type InternalKeyOwned<D, T> = KeyOwned<D, T, Internal>; - -/// Key-Owned Value -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "T: Clone, K: Clone"), - Copy(bound = "D::Index: Copy, T: Copy, K: Copy"), - Debug(bound = "D::Index: Debug, T: Debug, K: Debug"), - Eq(bound = "D::Index: Eq, T: Eq, K: Eq"), - Hash(bound = "D::Index: Hash, T: Hash, K: Hash"), - PartialEq(bound = "T: PartialEq, K: PartialEq") -)] -pub struct KeyOwned<D, T, K = KeyKind> -where - D: DerivedSecretKeyGenerator, - K: Into<KeyKind>, -{ - /// Value Owned by the Key - pub inner: T, - - /// Key Index - pub index: Index<D, K>, -} - -impl<D, T, K> KeyOwned<D, T, K> -where - D: DerivedSecretKeyGenerator, - K: Into<KeyKind>, -{ - /// Builds a new [`KeyOwned`] for `inner` with `index` as the [`Index`]. - #[inline] - pub fn new(inner: T, index: Index<D, K>) -> Self { - Self { inner, index } - } - - /// Builds a new [`KeyOwned`] for `inner` with `kind` and `index` as the [`Index`]. - #[inline] - pub fn with_kind(inner: T, kind: K, index: D::Index) -> Self { - Self::new(inner, Index::new(kind, index)) - } - - /// Returns the inner [`self.inner`](Self::inner) dropping the [`self.index`](Self::index). - #[inline] - pub fn unwrap(self) -> T { - self.inner - } - - /// Reduces `self` into a [`KeyOwned`] with [`KeyKind`] as the key kind. - #[inline] - pub fn reduce(self) -> KeyOwned<D, T> { - KeyOwned::new(self.inner, self.index.reduce()) - } - - /// Maps the underlying value using `f`. - #[inline] - pub fn map<U, F>(self, f: F) -> KeyOwned<D, U, K> - where - F: FnOnce(T) -> U, - { - KeyOwned::new(f(self.inner), self.index) - } - - /// Maps the underlying value using `f` and then factors over the `Some` branch. - #[inline] - pub fn map_some<U, F>(self, f: F) -> Option<KeyOwned<D, U, K>> - where - F: FnOnce(T) -> Option<U>, - { - self.map(f).collect() - } - - /// Maps the underlying value using `f` and then factors over the `Ok` branch. - #[inline] - pub fn map_ok<U, E, F>(self, f: F) -> Result<KeyOwned<D, U, K>, E> - where - F: FnOnce(T) -> Result<U, E>, - { - self.map(f).collect() - } -} - -impl<D, T> KeyOwned<D, T> -where - D: DerivedSecretKeyGenerator, -{ - /// Builds a new [`KeyOwned`] for `inner` for an external key with `index`. - #[inline] - pub fn new_external(inner: T, index: D::Index) -> Self { - Self::new(inner, Index::new_external(index)) - } - - /// Builds a new [`KeyOwned`] for `inner` for an internal key with `index`. - #[inline] - pub fn new_internal(inner: T, index: D::Index) -> Self { - Self::new(inner, Index::new_internal(index)) - } - - /// Returns `true` if `self` represents a value owned by an external key. - #[inline] - pub fn is_external(&self) -> bool { - self.index.is_external() - } - - /// Returns `true` if `self` represents a value owned by an internal key. - #[inline] - pub fn is_internal(&self) -> bool { - self.index.is_internal() - } -} - -impl<D, T, K> AsRef<T> for KeyOwned<D, T, K> -where - D: DerivedSecretKeyGenerator, - K: Into<KeyKind>, -{ - #[inline] - fn as_ref(&self) -> &T { - &self.inner - } -} - -impl<D, T, K> AsMut<T> for KeyOwned<D, T, K> -where - D: DerivedSecretKeyGenerator, - K: Into<KeyKind>, -{ - #[inline] - fn as_mut(&mut self) -> &mut T { - &mut self.inner - } -} - -impl<D, T, K> From<KeyOwned<D, T, K>> for (T, Index<D, K>) -where - D: DerivedSecretKeyGenerator, - K: Into<KeyKind>, -{ - #[inline] - fn from(key_owned: KeyOwned<D, T, K>) -> Self { - (key_owned.inner, key_owned.index) - } -} - -impl<D, L, R, K> KeyOwned<D, (L, R), K> -where - D: DerivedSecretKeyGenerator, - K: Into<KeyKind>, -{ - /// Factors the key index over the left value in the pair. - #[inline] - pub fn left(self) -> (KeyOwned<D, L, K>, R) { - (KeyOwned::new(self.inner.0, self.index), self.inner.1) - } - - /// Factors the key index over the right value in the pair. - #[inline] - pub fn right(self) -> (L, KeyOwned<D, R, K>) { - (self.inner.0, KeyOwned::new(self.inner.1, self.index)) - } -} - -impl<D, T, K> KeyOwned<D, Option<T>, K> -where - D: DerivedSecretKeyGenerator, - K: Into<KeyKind>, -{ - /// Converts a `KeyOwned<D, Option<T>, K>` into an `Option<KeyOwned<D, T, K>>`. - #[inline] - pub fn collect(self) -> Option<KeyOwned<D, T, K>> { - Some(KeyOwned::new(self.inner?, self.index)) - } -} - -impl<D, T, K> From<KeyOwned<D, Option<T>, K>> for Option<KeyOwned<D, T, K>> -where - D: DerivedSecretKeyGenerator, - K: Into<KeyKind>, -{ - #[inline] - fn from(key_owned: KeyOwned<D, Option<T>, K>) -> Self { - key_owned.collect() - } -} - -impl<D, T, E, K> KeyOwned<D, Result<T, E>, K> -where - D: DerivedSecretKeyGenerator, - K: Into<KeyKind>, -{ - /// Converts a `KeyOwned<D, Result<T, E>, K>` into an `Result<KeyOwned<D, T, K>, E>`. - #[inline] - pub fn collect(self) -> Result<KeyOwned<D, T, K>, E> { - Ok(KeyOwned::new(self.inner?, self.index)) - } - - /// Converts a `KeyOwned<D, Result<T, E>, K>` into an `Option<KeyOwned<D, T, K>>`. - #[inline] - pub fn ok(self) -> Option<KeyOwned<D, T, K>> { - self.collect().ok() - } -} - -impl<D, T, E, K> From<KeyOwned<D, Result<T, E>, K>> for Result<KeyOwned<D, T, K>, E> -where - D: DerivedSecretKeyGenerator, - K: Into<KeyKind>, -{ - #[inline] - fn from(key_owned: KeyOwned<D, Result<T, E>, K>) -> Self { - key_owned.collect() - } -} - -impl<D, T, E, K> TryFrom<KeyOwned<D, Result<T, E>, K>> for KeyOwned<D, T, K> -where - D: DerivedSecretKeyGenerator, - K: Into<KeyKind>, -{ - type Error = E; - - #[inline] - fn try_from(key_owned: KeyOwned<D, Result<T, E>, K>) -> Result<Self, Self::Error> { - key_owned.collect() - } -} - -/// Generates an external or internal secret key according to the [`DerivedSecretKeyGenerator`] -/// protocol and increments the running `index`. -#[inline] -pub fn next_key<D>( - source: &D, - kind: KeyKind, - account: &D::Account, - index: &mut D::Index, -) -> Result<SecretKey<D>, D::Error> -where - D: DerivedSecretKeyGenerator, -{ - let secret_key = SecretKey::with_kind( - source.generate_key(kind, account, index)?, - kind, - index.clone(), - ); - index.increment(); - Ok(secret_key) -} - -/// Generates an external secret key according to the [`DerivedSecretKeyGenerator`] protocol -/// and increments the running `index`. -#[inline] -pub fn next_external<D>( - source: &D, - account: &D::Account, - index: &mut D::Index, -) -> Result<ExternalSecretKey<D>, D::Error> +pub struct Account<H> where - D: DerivedSecretKeyGenerator, + H: HierarchicalKeyTable, { - let secret_key = ExternalSecretKey::new( - source.generate_external_key(account, index)?, - Index::new(External, index.clone()), - ); - index.increment(); - Ok(secret_key) -} - -/// Generates an internal secret key according to the [`DerivedSecretKeyGenerator`] protocol -/// and increments the running `index`. -#[inline] -pub fn next_internal<D>( - source: &D, - account: &D::Account, - index: &mut D::Index, -) -> Result<InternalSecretKey<D>, D::Error> -where - D: DerivedSecretKeyGenerator, -{ - let secret_key = InternalSecretKey::new( - source.generate_internal_key(account, index)?, - Index::new(Internal, index.clone()), - ); - index.increment(); - Ok(secret_key) -} + /// Hierarchical Key Table + table: H, -/// Keys -struct Keys<'d, D> -where - D: DerivedSecretKeyGenerator, -{ - /// Derived Key Generator - derived_key_generator: &'d D, - - /// Key Account - account: &'d D::Account, - - /// Current Index - index: D::Index, -} - -impl<'d, D> Keys<'d, D> -where - D: DerivedSecretKeyGenerator, -{ - /// Builds a new [`Keys`] generator from a [`DerivedSecretKeyGenerator`] and an `account`. - #[inline] - fn new(derived_key_generator: &'d D, account: &'d D::Account) -> Self { - Self::from_index(derived_key_generator, account, Default::default()) - } - - /// Builds a new [`Keys`] generator from a [`DerivedSecretKeyGenerator`] and an `account`, - /// starting at `index`. - #[inline] - fn from_index(derived_key_generator: &'d D, account: &'d D::Account, index: D::Index) -> Self { - Self { - derived_key_generator, - account, - index, - } - } - - /// Generates an external secret key according to the [`DerivedSecretKeyGenerator`] protocol - /// and increments the running `self.index`. - #[inline] - fn generate_external_key(&mut self) -> Result<ExternalSecretKey<D>, D::Error> { - next_external(self.derived_key_generator, self.account, &mut self.index) - } - - /// Generates an internal secret key according to the [`DerivedSecretKeyGenerator`] protocol - /// and increments the running `self.index`. - #[inline] - fn generate_internal_key(&mut self) -> Result<InternalSecretKey<D>, D::Error> { - next_internal(self.derived_key_generator, self.account, &mut self.index) - } -} - -/// External Keys -pub struct ExternalKeys<'d, D>(Keys<'d, D>) -where - D: DerivedSecretKeyGenerator; - -impl<'d, D> ExternalKeys<'d, D> -where - D: DerivedSecretKeyGenerator, -{ - /// Builds a new [`ExternalKeys`] generator for `account` from a `source`. - #[inline] - pub fn new(source: &'d D, account: &'d D::Account) -> Self { - Self(Keys::new(source, account)) - } - - /// Builds a new [`ExternalKeys`] generator for `account` from a `source`, starting at `index`. - #[inline] - pub fn from_index(source: &'d D, account: &'d D::Account, index: D::Index) -> Self { - Self(Keys::from_index(source, account, index)) - } -} - -impl<'d, D> Iterator for ExternalKeys<'d, D> -where - D: DerivedSecretKeyGenerator, -{ - type Item = Result<ExternalSecretKey<D>, D::Error>; - - #[inline] - fn next(&mut self) -> Option<Self::Item> { - Some(self.0.generate_external_key()) - } -} - -/// Internal Keys -pub struct InternalKeys<'d, D>(Keys<'d, D>) -where - D: DerivedSecretKeyGenerator; - -impl<'d, D> InternalKeys<'d, D> -where - D: DerivedSecretKeyGenerator, -{ - /// Builds a new [`InternalKeys`] generator for `account` from a `source`. - #[inline] - pub fn new(source: &'d D, account: &'d D::Account) -> Self { - Self(Keys::new(source, account)) - } - - /// Builds a new [`InternalKeys`] generator for `account` from a `source`, starting at `index`. - #[inline] - pub fn from_index(source: &'d D, account: &'d D::Account, index: D::Index) -> Self { - Self(Keys::from_index(source, account, index)) - } -} - -impl<'d, D> Iterator for InternalKeys<'d, D> -where - D: DerivedSecretKeyGenerator, -{ - type Item = Result<InternalSecretKey<D>, D::Error>; - - #[inline] - fn next(&mut self) -> Option<Self::Item> { - Some(self.0.generate_internal_key()) - } -} - -/// Account Index Manager -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Debug(bound = "D::Account: Debug, D::Index: Debug"), - Default(bound = ""), - Eq(bound = "D::Account: Eq, D::Index: Eq"), - Hash(bound = "D::Account: Hash, D::Index: Hash"), - PartialEq(bound = "") -)] -pub struct Account<D> -where - D: DerivedSecretKeyGenerator, -{ /// Account Identifier - pub account: D::Account, - - /// External Transaction Index Range - pub external_indices: Range<D::Index>, + account: H::Account, - /// Internal Transaction Index Range - pub internal_indices: Range<D::Index>, + /// Latest Index + latest_index: H::Index, } -impl<D> Account<D> +impl<H> Account<H> where - D: DerivedSecretKeyGenerator, + H: HierarchicalKeyTable, { - /// Builds a new [`Account`] for the given `account` identifier. + /// Builds a new [`Account`] for `table` and the given `account` identifier. #[inline] - pub fn new(account: D::Account) -> Self { - Self::with_ranges(account, Default::default(), Default::default()) + pub fn new(table: H, account: H::Account) -> Self { + Self::with_index(table, account, Default::default()) } - /// Builds a new [`Account`] for the given `account` identifier with starting ranges - /// `external_indices` and `internal_indices`. + /// Builds a new [`Account`] for `table` and the given `account` identifier and `latest_index`. #[inline] - pub fn with_ranges( - account: D::Account, - external_indices: Range<D::Index>, - internal_indices: Range<D::Index>, - ) -> Self { + pub fn with_index(table: H, account: H::Account, latest_index: H::Index) -> Self { Self { + table, account, - external_indices, - internal_indices, - } - } - - /// Returns the next [`Account`] after `self`. - #[inline] - pub fn next(mut self) -> Self { - self.account.increment(); - Self::new(self.account) - } - - /// Resets the external and internal running indices to their default values. - #[inline] - pub fn reset(&mut self) -> &mut Self { - self.external_indices = Default::default(); - self.internal_indices = Default::default(); - self - } - - /// Generates a new key of the given `kind` for this account. - #[inline] - pub fn key(&self, source: &D, kind: KeyKind) -> Result<SecretKey<D>, D::Error> { - match kind { - KeyKind::External => Ok(self.external_key(source)?.reduce()), - KeyKind::Internal => Ok(self.internal_key(source)?.reduce()), + latest_index, } } - /// Generates a new external key for this account. - #[inline] - pub fn external_key(&self, source: &D) -> Result<ExternalSecretKey<D>, D::Error> { - Ok(SecretKey::new( - source.generate_external_key(&self.account, &self.external_indices.end)?, - Index::new(External, self.external_indices.end.clone()), - )) - } - - /// Generates a new internal key for this account. + /// Returns the key of the given `kind` for `self`. #[inline] - pub fn internal_key(&self, source: &D) -> Result<InternalSecretKey<D>, D::Error> { - Ok(SecretKey::new( - source.generate_internal_key(&self.account, &self.internal_indices.end)?, - Index::new(Internal, self.internal_indices.end.clone()), - )) - } - - /// Generates the next key of the given `kind` for this account, incrementing the - /// appropriate index. - #[inline] - pub fn next_key(&mut self, source: &D, kind: KeyKind) -> Result<SecretKey<D>, D::Error> { - match kind { - KeyKind::External => Ok(self.next_external_key(source)?.reduce()), - KeyKind::Internal => Ok(self.next_internal_key(source)?.reduce()), - } - } - - /// Generates the next external key for this account, incrementing the `external_index`. - #[inline] - pub fn next_external_key(&mut self, source: &D) -> Result<ExternalSecretKey<D>, D::Error> { - next_external(source, &self.account, &mut self.external_indices.end) - } - - /// Generates the next internal key for this account, incrementing the `internal_index`. - #[inline] - pub fn next_internal_key(&mut self, source: &D) -> Result<InternalSecretKey<D>, D::Error> { - next_internal(source, &self.account, &mut self.internal_indices.end) - } - - /// Returns an iterator over the external keys generated by the indices in - /// [`self.external_indices`](Self::external_indices). - #[inline] - pub fn external_keys<'s>(&'s self, source: &'s D) -> ExternalKeysRange<'s, D> { - ExternalKeysRange { - source, - account: &self.account, - range: self.external_indices.clone(), - } - } - - /// Increments the start of the external index range if `index` is equal to the start of the - /// range. - #[inline] - pub fn conditional_increment_external_range(&mut self, index: &D::Index) { - if &self.external_indices.start == index { - self.external_indices.start.increment(); - } - } - - /// Shifts the end of the internal key range to the start. - #[inline] - pub fn internal_range_shift_to_start(&mut self) { - self.internal_indices.end = self.internal_indices.start.clone(); - } - - /// Shifts the start of the internal key range to the end. - #[inline] - pub fn internal_range_shift_to_end(&mut self) { - self.internal_indices.start = self.internal_indices.end.clone(); - } -} - -impl<D> AsRef<D::Account> for Account<D> -where - D: DerivedSecretKeyGenerator, -{ - #[inline] - fn as_ref(&self) -> &D::Account { - &self.account - } -} - -/// External Keys Range Iterator -/// -/// This `struct` is created by the [`external_keys`](Account::external_keys) method on [`Account`]. -/// See its documentation for more. -pub struct ExternalKeysRange<'d, D> -where - D: DerivedSecretKeyGenerator, -{ - /// Derived Secret Key Generator - source: &'d D, - - /// Key Account - account: &'d D::Account, - - /// Index Range - range: Range<D::Index>, -} - -impl<'d, D> Iterator for ExternalKeysRange<'d, D> -where - D: DerivedSecretKeyGenerator, -{ - type Item = Result<ExternalSecretKey<D>, D::Error>; - - #[inline] - fn next(&mut self) -> Option<Self::Item> { - if self.range.is_empty() { - return None; - } - Some(next_external( - self.source, - self.account, - &mut self.range.end, - )) + pub fn key(&self, kind: &H::Kind) -> Result<H::SecretKey, H::Error> { + self.table.get(&self.account, &self.latest_index, kind) } } diff --git a/manta-accounting/src/key2.rs b/manta-accounting/src/key2.rs deleted file mode 100644 index 2d8e0ec8d..000000000 --- a/manta-accounting/src/key2.rs +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Hierarchical Key Tables - -// TODO: Make [`Account`] more useful for managing accounts. - -use core::{convert::TryFrom, fmt::Debug, hash::Hash}; - -/// Hierarchical Key Table Parameter -pub trait HierarchicalKeyTableParameter: Clone + Default + PartialOrd { - /// Increments the key parameter by one unit. - fn increment(&mut self); -} - -/// Hierarchical Key Table -pub trait HierarchicalKeyTable { - /// Account Type - type Account: HierarchicalKeyTableParameter; - - /// Index Type - type Index: HierarchicalKeyTableParameter; - - /// Key Kind Type - type Kind; - - /// Secret Key Type - type SecretKey; - - /// Key Access Error Type - type Error; - - /// Returns the secret key associated to `account` and `index` of the given `kind`. - fn get( - &self, - account: &Self::Account, - index: &Self::Index, - kind: &Self::Kind, - ) -> Result<Self::SecretKey, Self::Error>; -} - -impl<H> HierarchicalKeyTable for &H -where - H: HierarchicalKeyTable, -{ - type Account = H::Account; - - type Index = H::Index; - - type Kind = H::Kind; - - type SecretKey = H::SecretKey; - - type Error = H::Error; - - #[inline] - fn get( - &self, - account: &Self::Account, - index: &Self::Index, - kind: &Self::Kind, - ) -> Result<Self::SecretKey, Self::Error> { - (*self).get(account, index, kind) - } -} - -/// Account Index with Table -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "H: Clone,"), - Copy(bound = "H: Copy, H::Account: Copy, H::Index: Copy"), - Debug(bound = "H: Debug, H::Account: Debug, H::Index: Debug"), - Default(bound = "H: Default"), - Eq(bound = "H: Eq, H::Account: Eq, H::Index: Eq"), - Hash(bound = "H: Hash, H::Account: Hash, H::Index: Hash"), - PartialEq(bound = "H: PartialEq") -)] -pub struct Account<H> -where - H: HierarchicalKeyTable, -{ - /// Hierarchical Key Table - table: H, - - /// Account Identifier - account: H::Account, - - /// Latest Index - latest_index: H::Index, -} - -impl<H> Account<H> -where - H: HierarchicalKeyTable, -{ - /// Builds a new [`Account`] for `table` and the given `account` identifier. - #[inline] - pub fn new(table: H, account: H::Account) -> Self { - Self::with_index(table, account, Default::default()) - } - - /// Builds a new [`Account`] for `table` and the given `account` identifier and `latest_index`. - #[inline] - pub fn with_index(table: H, account: H::Account, latest_index: H::Index) -> Self { - Self { - table, - account, - latest_index, - } - } - - /// Returns the key of the given `kind` for `self`. - #[inline] - pub fn key(&self, kind: &H::Kind) -> Result<H::SecretKey, H::Error> { - self.table.get(&self.account, &self.latest_index, kind) - } -} diff --git a/manta-accounting/src/lib.rs b/manta-accounting/src/lib.rs index d7b2671fe..cd132509d 100644 --- a/manta-accounting/src/lib.rs +++ b/manta-accounting/src/lib.rs @@ -29,11 +29,7 @@ extern crate cocoon as cocoon_crate; pub mod asset; pub mod fs; -// TODO[remove]: pub mod identity; -pub mod identity2; -// TODO[remove]: pub mod key; -pub mod key2; -// TODO[remove]: pub mod transfer; -pub mod transfer2; -// TODO[remove]: pub mod wallet; -pub mod wallet2; +pub mod identity; +pub mod key; +pub mod transfer; +pub mod wallet; diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 55a6fcc06..fa1fdf602 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -16,36 +16,29 @@ //! Transfer Protocol -// FIXME: Make sure that either (a) no empty transfer can be built, or (b) empty transfers work -// properly i.e. do nothing. -// TODO: See if we can get rid of the `Copy` restriction on `ValidProof` and `SuperPostingKey`. -// TODO: See if we can get rid of `PublicTransfer` and `SecretTransfer` and just use `Transfer`. - use crate::{ - asset::{Asset, AssetId, AssetValue, AssetValues}, - identity::{ - self, constraint::UtxoVar, ReceiverLedger, ReceiverPostError, SenderLedger, - SenderPostError, Utxo, VoidNumber, - }, + asset::{Asset, AssetId, AssetValue}, + identity::{self, CommitmentSchemeOutput, Utxo}, }; use alloc::vec::Vec; -use core::{fmt::Debug, hash::Hash, ops::Add}; +use core::ops::Add; use manta_crypto::{ - accumulator::{constraint::VerifierVariable, Verifier}, + accumulator::Verifier, + commitment::CommitmentScheme, constraint::{ - self, - reflection::{HasAllocation, HasVariable}, - Allocation, Constant, ConstraintSystem as _, Derived, Equal, Input as ProofSystemInput, - ProofSystem, Public, PublicOrSecret, Secret, Variable, VariableSource, + reflection::{HasEqual, HasVariable, Var}, + Allocation, Constant, ConstraintSystem, Derived, Equal, ProofSystem, Public, + PublicOrSecret, Variable, VariableSource, }, - encryption::ies::{EncryptedMessage, IntegratedEncryptionScheme}, + encryption::{EncryptedMessage, HybridPublicKeyEncryptionScheme}, + key::KeyAgreementScheme, rand::{CryptoRng, RngCore}, }; -use manta_util::{create_seal, from_variant_impl, seal}; +use manta_util::create_seal; /// Returns `true` if the transfer with this shape would have no public participants. #[inline] -const fn has_no_public_participants( +pub const fn has_no_public_participants( sources: usize, senders: usize, receivers: usize, @@ -55,483 +48,139 @@ const fn has_no_public_participants( sources == 0 && sinks == 0 } -/// [`Transfer`] Configuration -pub trait Configuration: - identity::constraint::Configuration<ConstraintSystem = ConstraintSystem<Self>> -{ - /// Constraint System - type ConstraintSystem: constraint::ConstraintSystem - + HasVariable<AssetId, Variable = Self::AssetIdVar, Mode = PublicOrSecret> - + HasVariable<AssetValue, Variable = Self::AssetValueVar, Mode = PublicOrSecret> - + HasVariable<<Self::UtxoSetVerifier as Verifier>::Output, Mode = Public> - + HasVariable<<Self::UtxoSetVerifier as Verifier>::Witness, Mode = Secret>; - - /// Proof System - type ProofSystem: ProofSystem<ConstraintSystem = ConstraintSystem<Self>, Verification = bool> - + ProofSystemInput<AssetId> - + ProofSystemInput<AssetValue> - + ProofSystemInput<VoidNumber<Self>> - + ProofSystemInput<<Self::UtxoSetVerifier as Verifier>::Output> - + ProofSystemInput<Utxo<Self>>; - - /// Asset Id Variable - type AssetIdVar: Variable<ConstraintSystem<Self>, Mode = PublicOrSecret, Type = AssetId> - + Equal<ConstraintSystem<Self>>; - - /// Asset Value Variable - type AssetValueVar: Variable<ConstraintSystem<Self>, Mode = PublicOrSecret, Type = AssetValue> - + Equal<ConstraintSystem<Self>> - + Add<Output = Self::AssetValueVar>; - - /// Integrated Encryption Scheme for [`Asset`] - type IntegratedEncryptionScheme: IntegratedEncryptionScheme<Plaintext = Asset>; - - /// Accumulator Verifier for [`Utxo`] - type UtxoSetVerifier: Verifier<Item = Utxo<Self>>; - - /// Accumulator Verifier Variable for [`Utxo`] - type UtxoSetVerifierVar: VerifierVariable< - ConstraintSystem<Self>, - ItemVar = UtxoVar<Self>, - Type = Self::UtxoSetVerifier, - Mode = Constant, +/// Transfer Configuration +pub trait Configuration: identity::Configuration<Asset = Asset> { + /// Encryption Scheme Type + type EncryptionScheme: HybridPublicKeyEncryptionScheme< + Plaintext = Self::Asset, + KeyAgreementScheme = Self::KeyAgreementScheme, >; -} - -/// Transfer Shielded Identity Type -pub type ShieldedIdentity<C> = - identity::ShieldedIdentity<C, <C as Configuration>::IntegratedEncryptionScheme>; - -/// Transfer Internal Identity Type -pub type InternalIdentity<C> = - identity::InternalIdentity<C, <C as Configuration>::IntegratedEncryptionScheme>; - -/// Transfer Spend Type -pub type Spend<C> = identity::Spend<C, <C as Configuration>::IntegratedEncryptionScheme>; - -/// Transfer Sender Type -pub type Sender<C> = identity::Sender<C, <C as Configuration>::UtxoSetVerifier>; - -/// Transfer Sender Post Type -pub type SenderPost<C> = identity::SenderPost<C, <C as Configuration>::UtxoSetVerifier>; - -/// Transfer Sender Posting Key Type -pub type SenderPostingKey<C, L> = - identity::SenderPostingKey<C, <C as Configuration>::UtxoSetVerifier, L>; - -/// Transfer Receiver Type -pub type Receiver<C> = identity::Receiver<C, <C as Configuration>::IntegratedEncryptionScheme>; - -/// Transfer Receiver Post Type -pub type ReceiverPost<C> = - identity::ReceiverPost<C, <C as Configuration>::IntegratedEncryptionScheme>; - -/// Transfer Receiver Posting Key Type -pub type ReceiverPostingKey<C, L> = - identity::ReceiverPostingKey<C, <C as Configuration>::IntegratedEncryptionScheme, L>; - -/// Transfer Encrypted Asset Type -pub type EncryptedAsset<C> = EncryptedMessage<<C as Configuration>::IntegratedEncryptionScheme>; - -/// Transfer Integrated Encryption Scheme Error Type -pub type IntegratedEncryptionSchemeError<C> = - <<C as Configuration>::IntegratedEncryptionScheme as IntegratedEncryptionScheme>::Error; - -/// Transfer Constraint System Type -pub type ConstraintSystem<C> = <C as Configuration>::ConstraintSystem; - -/// Transfer Sender Variable Type -pub type SenderVar<C> = identity::constraint::SenderVar<C, <C as Configuration>::UtxoSetVerifier>; - -/// Transfer Receiver Type -pub type ReceiverVar<C> = - identity::constraint::ReceiverVar<C, <C as Configuration>::IntegratedEncryptionScheme>; - -/// Transfer Proving Context Type -pub type ProvingContext<C> = <<C as Configuration>::ProofSystem as ProofSystem>::ProvingContext; - -/// Transfer Verifying Context Type -pub type VerifyingContext<C> = <<C as Configuration>::ProofSystem as ProofSystem>::VerifyingContext; - -/// Transfer Proof Type -pub type Proof<C> = <<C as Configuration>::ProofSystem as ProofSystem>::Proof; - -/// Transfer Proof System Input Type -pub type ProofInput<C> = <<C as Configuration>::ProofSystem as ProofSystem>::Input; - -/// Transfer Proof System Error Type -pub type ProofSystemError<C> = <<C as Configuration>::ProofSystem as ProofSystem>::Error; -/// Transfer Source Posting Key Type -pub type SourcePostingKey<C, L> = <L as TransferLedger<C>>::ValidSourceBalance; - -/// Transfer Ledger Super Posting Key Type -pub type TransferLedgerSuperPostingKey<C, L> = <L as TransferLedger<C>>::SuperPostingKey; - -/// Transfer Ledger -/// -/// # Safety -/// -/// The [`TransferLedger`] interface describes the conditions for a valid ledger update but only for -/// the state that is described by a [`TransferPost`]. Independently of this trait, any ledger -/// implementation still needs to check that source and sink accounts are associated to a real -/// public address. However, checking public balances is done in the -/// [`check_source_balances`](Self::check_source_balances) method. -pub trait TransferLedger<C>: - SenderLedger< - C, - C::UtxoSetVerifier, - SuperPostingKey = (Self::ValidProof, TransferLedgerSuperPostingKey<C, Self>), - > + ReceiverLedger< - C, - C::IntegratedEncryptionScheme, - SuperPostingKey = (Self::ValidProof, TransferLedgerSuperPostingKey<C, Self>), - > -where - C: Configuration, -{ - /// Valid [`AssetValue`] for [`TransferPost`] source - /// - /// # Safety - /// - /// This type must be restricted so that it can only be constructed by this implementation of - /// [`TransferLedger`]. - type ValidSourceBalance; - - /// Valid [`Proof`] Posting Key - /// - /// # Safety - /// - /// This type must be restricted so that it can only be constructed by this implementation - /// of [`TransferLedger`]. This is to prevent that [`SenderPostingKey::post`] and - /// [`ReceiverPostingKey::post`] are called before [`SenderPost::validate`], - /// [`ReceiverPost::validate`], [`check_source_balances`](Self::check_source_balances), and - /// [`is_valid`](Self::is_valid). - type ValidProof: Copy; - - /// Super Posting Key - /// - /// Type that allows super-traits of [`TransferLedger`] to customize posting key behavior. - type SuperPostingKey: Copy; - - /// Checks that the balances associated to the source accounts are sufficient to withdraw the - /// amount given in `sources`. - fn check_source_balances( - &self, - sources: Vec<AssetValue>, - ) -> Result<Vec<Self::ValidSourceBalance>, InsufficientPublicBalance>; - - /// Checks that the transfer `proof` is valid. - fn is_valid( - &self, - asset_id: Option<AssetId>, - sources: &[Self::ValidSourceBalance], - senders: &[SenderPostingKey<C, Self>], - receivers: &[ReceiverPostingKey<C, Self>], - sinks: &[AssetValue], - proof: Proof<C>, - ) -> Option<Self::ValidProof>; - - /// Updates the public balances in the ledger, finishing the transaction. - /// - /// # Safety - /// - /// This method can only be called once we check that `proof` is a valid proof and that - /// `senders` and `receivers` are valid participants in the transaction. See - /// [`is_valid`](Self::is_valid) for more. - fn update_public_balances( - &mut self, - asset_id: AssetId, - sources: Vec<Self::ValidSourceBalance>, - sinks: Vec<AssetValue>, - proof: Self::ValidProof, - super_key: &TransferLedgerSuperPostingKey<C, Self>, - ); + /// UTXO Set Verifier Type + type UtxoSetVerifier: Verifier<Item = Utxo<Self>, Verification = bool>; + + /// Constraint System Type + type ConstraintSystem: ConstraintSystem + + HasVariable< + Self::KeyAgreementScheme, + Variable = KeyAgreementSchemeVar<Self>, + Mode = Constant, + > + HasVariable<Self::CommitmentScheme, Variable = CommitmentSchemeVar<Self>, Mode = Constant> + + HasVariable<Self::UtxoSetVerifier, Variable = UtxoSetVerifierVar<Self>, Mode = Constant> + + HasVariable<AssetId, Variable = AssetIdVar<Self>, Mode = PublicOrSecret> + + HasVariable<AssetValue, Variable = AssetValueVar<Self>, Mode = PublicOrSecret> + + HasVariable< + CommitmentSchemeOutput<Self>, + Variable = CommitmentSchemeOutput<Self::ConstraintConfiguration>, + Mode = PublicOrSecret, + > + HasEqual<CommitmentSchemeOutput<Self::ConstraintConfiguration>>; + + /// Constraint System Configuration + type ConstraintConfiguration: ConstraintConfiguration<Self::ConstraintSystem>; + + /// Proof System Type + type ProofSystem: ProofSystem<ConstraintSystem = Self::ConstraintSystem, Verification = bool>; } -/// Dynamic Transfer Shape -#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] -pub struct DynamicShape { - /// Number of Sources - pub sources: usize, - - /// Number of Senders - pub senders: usize, - - /// Number of Receivers - pub receivers: usize, - - /// Number of Sinks - pub sinks: usize, -} - -impl DynamicShape { - /// Builds a new [`DynamicShape`] from `sources`, `senders`, `receivers`, and `sinks`. - #[inline] - pub const fn new(sources: usize, senders: usize, receivers: usize, sinks: usize) -> Self { - Self { - sources, - senders, - receivers, - sinks, - } - } - - /// Builds a new [`DynamicShape`] using the static [`Shape`] given by `S`. - #[inline] - pub fn from_static<S>() -> Self - where - S: Shape, - { - Self::new(S::SOURCES, S::SENDERS, S::RECEIVERS, S::SINKS) - } - - /// Checks if `self` matches the static [`Shape`] given by `S`. - #[inline] - pub fn matches<S>(&self) -> bool - where - S: Shape, - { - S::SOURCES == self.sources - && S::SENDERS == self.senders - && S::RECEIVERS == self.receivers - && S::SINKS == self.sinks - } -} - -impl<S> From<S> for DynamicShape +/// Transfer Constraint System Configuration +pub trait ConstraintConfiguration<CS>: + identity::Configuration<Asset = Asset<Self::AssetId, Self::AssetValue>> where - S: Shape, + CS: ConstraintSystem, { - #[inline] - fn from(shape: S) -> Self { - let _ = shape; - Self::from_static::<S>() - } -} + /// Asset Id Variable Type + type AssetId: Variable<CS, Type = AssetId, Mode = PublicOrSecret> + Equal<CS>; -/// Public Transfer Protocol -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub struct PublicTransfer<const SOURCES: usize, const SINKS: usize> { - /// Asset Id - pub asset_id: Option<AssetId>, - - /// Public Asset Sources - pub sources: AssetValues<SOURCES>, + /// Asset Value Variable Type + type AssetValue: Variable<CS, Type = AssetValue, Mode = PublicOrSecret> + + Equal<CS> + + Add<Output = Self::AssetValue>; - /// Public Asset Sinks - pub sinks: AssetValues<SINKS>, + /// UTXO Set Verifier Variable Type + type UtxoSetVerifier: Verifier<Item = Utxo<Self>, Verification = CS::Bool>; } -impl<const SOURCES: usize, const SINKS: usize> PublicTransfer<SOURCES, SINKS> { - /// Builds a new [`PublicTransfer`]. - #[inline] - pub const fn new( - asset_id: AssetId, - sources: AssetValues<SOURCES>, - sinks: AssetValues<SINKS>, - ) -> Self { - Self::new_unchecked( - if has_no_public_participants(SOURCES, 0, 0, SINKS) { - None - } else { - Some(asset_id) - }, - sources, - sinks, - ) - } - - /// Builds a new [`PublicTransfer`] without checking if the asset id should be `None`. - #[inline] - const fn new_unchecked( - asset_id: Option<AssetId>, - sources: AssetValues<SOURCES>, - sinks: AssetValues<SINKS>, - ) -> Self { - Self { - asset_id, - sources, - sinks, - } - } - - /// Returns the shape of this public transfer. - #[inline] - pub fn shape(&self) -> DynamicShape { - DynamicShape::new(SOURCES, 0, 0, SINKS) - } +/// Spending Key Type +pub type SpendingKey<C> = identity::SpendingKey<<C as identity::Configuration>::KeyAgreementScheme>; - /// Returns the sum of the asset values of the sources in this transfer. - #[inline] - pub fn source_sum(&self) -> AssetValue { - self.sources.iter().sum() - } +/// Receiving Key Type +pub type ReceivingKey<C> = + identity::ReceivingKey<<C as identity::Configuration>::KeyAgreementScheme>; - /// Returns the sum of the asset values of the sinks in this transfer. - #[inline] - pub fn sink_sum(&self) -> AssetValue { - self.sinks.iter().sum() - } +/// Encrypted Note Type +pub type EncryptedNote<C> = EncryptedMessage<<C as Configuration>::EncryptionScheme>; - /// Validates the transaction by checking that the [`source_sum`](Self::source_sum) - /// equals the [`sink_sum`](Self::sink_sum). - #[inline] - pub fn is_valid(&self) -> bool { - self.source_sum() == self.sink_sum() - } -} +/// Constraint System Type +type ConstraintSystemType<C> = <C as Configuration>::ConstraintSystem; -#[allow(clippy::derivable_impls)] // NOTE: We only want default on the `<0, 0>` setting. -impl Default for PublicTransfer<0, 0> { - #[inline] - fn default() -> Self { - Self::new_unchecked(None, [], []) - } -} +/// Constraint Configuration Type +type ConstraintConfigurationType<C> = <C as Configuration>::ConstraintConfiguration; -/// Secret Transfer Protocol -pub struct SecretTransfer<C, const SENDERS: usize, const RECEIVERS: usize> -where - C: Configuration, -{ - /// Senders - pub senders: [Sender<C>; SENDERS], +/// Pre-Sender Type +pub type PreSender<C> = identity::PreSender<C>; - /// Receivers - pub receivers: [Receiver<C>; RECEIVERS], -} +/// Sender Type +pub type Sender<C> = identity::Sender<C, <C as Configuration>::UtxoSetVerifier>; -impl<C, const SENDERS: usize, const RECEIVERS: usize> SecretTransfer<C, SENDERS, RECEIVERS> -where - C: Configuration, -{ - /// Maximum Number of Senders - pub const MAXIMUM_SENDER_COUNT: usize = 16; +/// Sender Variable Type +type SenderVar<C> = identity::Sender< + ConstraintConfigurationType<C>, + <ConstraintConfigurationType<C> as ConstraintConfiguration<ConstraintSystemType<C>>>::UtxoSetVerifier, +>; - /// Maximum Number of Receivers - pub const MAXIMUM_RECEIVER_COUNT: usize = 16; +/// Sender Post Type +pub type SenderPost<C> = identity::SenderPost<C, <C as Configuration>::UtxoSetVerifier>; - /// Builds a new [`SecretTransfer`]. - #[inline] - pub fn new(senders: [Sender<C>; SENDERS], receivers: [Receiver<C>; RECEIVERS]) -> Self { - Self::check_shape(); - Self::new_unchecked(senders, receivers) - } +/// Receiver Type +pub type Receiver<C> = identity::Receiver<C>; - /// Checks that the [`SecretTransfer`] has a valid shape. - #[inline] - fn check_shape() { - Self::check_sender_shape(); - Self::check_receiver_shape(); - Self::check_size_overflow(); - } +/// Receiver Variable Type +type ReceiverVar<C> = identity::Receiver<<C as Configuration>::ConstraintConfiguration>; - /// Checks that the sender side is not empty. - #[inline] - fn check_sender_shape() { - assert_ne!(SENDERS, 0, "Not enough senders."); - } +/// Full Receiver Type +pub type FullReceiver<C> = identity::FullReceiver<C>; - /// Checks that the receiver side is not empty. - #[inline] - fn check_receiver_shape() { - assert_ne!(RECEIVERS, 0, "Not enough receivers."); - } +/// Receiver Post Type +pub type ReceiverPost<C> = identity::ReceiverPost<C, <C as Configuration>::EncryptionScheme>; - /// Checks that the number of senders and/or receivers does not exceed the allocation limit. - #[inline] - fn check_size_overflow() { - match ( - SENDERS > Self::MAXIMUM_SENDER_COUNT, - RECEIVERS > Self::MAXIMUM_RECEIVER_COUNT, - ) { - (true, true) => panic!("Allocated too many senders and receivers."), - (true, _) => panic!("Allocated too many senders."), - (_, true) => panic!("Allocated too many receivers."), - _ => {} - } - } +/// Asset Id Variable Type +pub type AssetIdVar<C> = + <ConstraintConfigurationType<C> as ConstraintConfiguration<ConstraintSystemType<C>>>::AssetId; - /// Builds a new [`SecretTransfer`] without checking the number of senders and receivers. - #[inline] - fn new_unchecked(senders: [Sender<C>; SENDERS], receivers: [Receiver<C>; RECEIVERS]) -> Self { - Self { senders, receivers } - } +/// Asset Value Variable Type +pub type AssetValueVar<C> = <ConstraintConfigurationType<C> as ConstraintConfiguration< + ConstraintSystemType<C>, +>>::AssetValue; - /// Returns the shape of this secret transfer. - #[inline] - pub fn shape(&self) -> DynamicShape { - DynamicShape::new(0, SENDERS, RECEIVERS, 0) - } +/// Key Agreement Scheme Variable Type +pub type KeyAgreementSchemeVar<C> = + <ConstraintConfigurationType<C> as identity::Configuration>::KeyAgreementScheme; - /// Returns an iterator over all the asset ids in this transfer. - #[inline] - fn asset_id_iter(&self) -> impl '_ + Iterator<Item = AssetId> { - self.senders - .iter() - .map(Sender::asset_id) - .chain(self.receivers.iter().map(Receiver::asset_id)) - } +/// Commitment Scheme Variable Type +pub type CommitmentSchemeVar<C> = + <ConstraintConfigurationType<C> as identity::Configuration>::CommitmentScheme; - /// Checks that the asset ids of all the senders and receivers matches. - #[inline] - pub fn has_unique_asset_id(&self) -> bool { - let mut asset_id = None; - self.asset_id_iter() - .all(move |i| asset_id.replace(i) == Some(i)) - } +/// UTXO Set Verifier Variable Type +pub type UtxoSetVerifierVar<C> = <ConstraintConfigurationType<C> as ConstraintConfiguration< + ConstraintSystemType<C>, +>>::UtxoSetVerifier; - /// Returns the sum of the asset values of the senders in this transfer. - #[inline] - pub fn sender_sum(&self) -> AssetValue { - self.senders.iter().map(Sender::asset_value).sum() - } +/// Transfer Proof System Type +type ProofSystemType<C> = <C as Configuration>::ProofSystem; - /// Returns the sum of the asset values of the receivers in this transfer. - #[inline] - pub fn receiver_sum(&self) -> AssetValue { - self.receivers.iter().map(Receiver::asset_value).sum() - } +/// Transfer Proof System Error Type +pub type ProofSystemError<C> = <ProofSystemType<C> as ProofSystem>::Error; - /// Checks that the [`sender_sum`](Self::sender_sum) equals the - /// [`receiver_sum`](Self::receiver_sum). - #[inline] - pub fn is_balanced(&self) -> bool { - self.sender_sum() == self.receiver_sum() - } +/// Transfer Proving Context Type +pub type ProvingContext<C> = <ProofSystemType<C> as ProofSystem>::ProvingContext; - /// Converts `self` into its ledger post. - #[inline] - pub fn into_post<R>( - self, - commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &C::UtxoSetVerifier, - context: &ProvingContext<C>, - rng: &mut R, - ) -> Result<TransferPost<C>, ProofSystemError<C>> - where - R: CryptoRng + RngCore + ?Sized, - { - Transfer::from(self).into_post(commitment_scheme, utxo_set_verifier, context, rng) - } -} +/// Transfer Verifying Context Type +pub type VerifyingContext<C> = <ProofSystemType<C> as ProofSystem>::VerifyingContext; -impl<C, const SENDERS: usize, const RECEIVERS: usize> From<SecretTransfer<C, SENDERS, RECEIVERS>> - for Transfer<C, 0, SENDERS, RECEIVERS, 0> -where - C: Configuration, -{ - #[inline] - fn from(transfer: SecretTransfer<C, SENDERS, RECEIVERS>) -> Self { - Self { - public: Default::default(), - secret: transfer, - } - } -} +/// Transfer Validity Proof Type +pub type Proof<C> = <ProofSystemType<C> as ProofSystem>::Proof; -/// Transfer Protocol +/// Transfer pub struct Transfer< C, const SOURCES: usize, @@ -541,11 +190,20 @@ pub struct Transfer< > where C: Configuration, { - /// Public Part of the Transfer - public: PublicTransfer<SOURCES, SINKS>, + /// Asset Id + asset_id: Option<AssetId>, - /// Secret Part of the Transfer - secret: SecretTransfer<C, SENDERS, RECEIVERS>, + /// Sources + sources: [AssetValue; SOURCES], + + /// Senders + senders: [Sender<C>; SENDERS], + + /// Receivers + receivers: [FullReceiver<C>; RECEIVERS], + + /// Sinks + sinks: [AssetValue; SINKS], } impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> @@ -553,30 +211,30 @@ impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, cons where C: Configuration, { - /// Builds a new universal [`Transfer`] from public and secret information. + /// Builds a new [`Transfer`] from public and secret information. #[inline] - pub fn new( - asset_id: AssetId, - sources: AssetValues<SOURCES>, + fn new( + asset_id: Option<AssetId>, + sources: [AssetValue; SOURCES], senders: [Sender<C>; SENDERS], - receivers: [Receiver<C>; RECEIVERS], - sinks: AssetValues<SINKS>, + receivers: [FullReceiver<C>; RECEIVERS], + sinks: [AssetValue; SINKS], ) -> Self { - Self::check_shape(); + Self::check_shape(asset_id.is_some()); Self::new_unchecked(asset_id, sources, senders, receivers, sinks) } /// Checks that the [`Transfer`] has a valid shape. #[inline] - fn check_shape() { - Self::check_input_shape(); - Self::check_output_shape(); - SecretTransfer::<C, SENDERS, RECEIVERS>::check_size_overflow(); + fn check_shape(has_visible_asset_id: bool) { + Self::has_nonempty_input_shape(); + Self::has_nonempty_output_shape(); + Self::has_visible_asset_id_when_required(has_visible_asset_id); } - /// Checks that the input side is not empty. + /// Checks that the input side of the transfer is not empty. #[inline] - fn check_input_shape() { + fn has_nonempty_input_shape() { assert_ne!( SOURCES + SENDERS, 0, @@ -584,9 +242,9 @@ where ); } - /// Checks that the output side is not empty. + /// Checks that the output side of the transfer is not empty. #[inline] - fn check_output_shape() { + fn has_nonempty_output_shape() { assert_ne!( RECEIVERS + SINKS, 0, @@ -594,134 +252,101 @@ where ); } + /// Checks that the given `asset_id` for [`Transfer`] building is visible exactly when required. + #[inline] + fn has_visible_asset_id_when_required(has_visible_asset_id: bool) { + if SOURCES > 0 || SINKS > 0 { + assert!( + has_visible_asset_id, + "Missing public asset id when required." + ); + } else { + assert!( + !has_visible_asset_id, + "Given public asset id when not required." + ); + } + } + /// Builds a new [`Transfer`] without checking the number of participants on the input and /// output sides. #[inline] fn new_unchecked( - asset_id: AssetId, - sources: AssetValues<SOURCES>, + asset_id: Option<AssetId>, + sources: [AssetValue; SOURCES], senders: [Sender<C>; SENDERS], - receivers: [Receiver<C>; RECEIVERS], - sinks: AssetValues<SINKS>, + receivers: [FullReceiver<C>; RECEIVERS], + sinks: [AssetValue; SINKS], ) -> Self { Self { - public: PublicTransfer::new(asset_id, sources, sinks), - secret: SecretTransfer::new_unchecked(senders, receivers), - } - } - - /// Returns the shape of this transfer. - #[inline] - pub fn shape(&self) -> DynamicShape { - DynamicShape::new(SOURCES, SENDERS, RECEIVERS, SINKS) - } - - /// Checks that there is one unique asset id for all participants in this transfer. - #[inline] - pub fn has_unique_asset_id(&self) -> bool { - if let Some(asset_id) = self.public.asset_id { - self.secret.asset_id_iter().all(move |i| asset_id == i) - } else { - self.secret.has_unique_asset_id() + asset_id, + sources, + senders, + receivers, + sinks, } } - /// Returns the sum of the asset values of the sources in this transfer. - #[inline] - pub fn source_sum(&self) -> AssetValue { - self.public.source_sum() - } - - /// Returns the sum of the asset values of the senders in this transfer. - #[inline] - pub fn sender_sum(&self) -> AssetValue { - self.secret.sender_sum() - } - - /// Returns the sum of the asset values of the receivers in this transfer. - #[inline] - pub fn receiver_sum(&self) -> AssetValue { - self.secret.receiver_sum() - } - - /// Returns the sum of the asset values of the sinks in this transfer. - #[inline] - pub fn sink_sum(&self) -> AssetValue { - self.public.sink_sum() - } - - /// Returns the sum of the asset values of the sources and senders in this transfer. - #[inline] - pub fn input_sum(&self) -> AssetValue { - self.source_sum() + self.sender_sum() - } - - /// Returns the sum of the asset values of the receivers and sinks in this transfer. - #[inline] - pub fn output_sum(&self) -> AssetValue { - self.receiver_sum() + self.sink_sum() - } - - /// Checks that the transaction is balanced. - #[inline] - pub fn is_balanced(&self) -> bool { - self.input_sum() == self.output_sum() - } - - /// Generates the unknown variables for the validity proof. + /// Generates the unknown variables for the transfer validity proof. #[inline] fn unknown_variables( commitment_scheme: &C::CommitmentScheme, utxo_set_verifier: &C::UtxoSetVerifier, - cs: &mut ConstraintSystem<C>, + cs: &mut C::ConstraintSystem, ) -> ( - Option<C::AssetIdVar>, + Option<AssetIdVar<C>>, TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS>, - C::CommitmentSchemeVar, - C::UtxoSetVerifierVar, + CommitmentSchemeVar<C>, + UtxoSetVerifierVar<C>, ) { let base_asset_id = if has_no_public_participants(SOURCES, SENDERS, RECEIVERS, SINKS) { None } else { - Some(C::AssetIdVar::new_unknown(cs, Public)) + Some(AssetIdVar::<C>::new_unknown(cs, Public)) }; + /* TODO: ( base_asset_id, TransferParticipantsVar::new_unknown(cs, Derived), commitment_scheme.as_known(cs, Public), utxo_set_verifier.as_known(cs, Public), ) + */ + todo!() } - /// Generates the known variables for the validity proof. + /// Generates the known variables for the transfer validity proof. #[inline] fn known_variables( &self, commitment_scheme: &C::CommitmentScheme, utxo_set_verifier: &C::UtxoSetVerifier, - cs: &mut ConstraintSystem<C>, + cs: &mut C::ConstraintSystem, ) -> ( - Option<C::AssetIdVar>, + Option<AssetIdVar<C>>, TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS>, - C::CommitmentSchemeVar, - C::UtxoSetVerifierVar, + CommitmentSchemeVar<C>, + UtxoSetVerifierVar<C>, ) { + /* TODO: ( self.public.asset_id.map(|id| id.as_known(cs, Public)), TransferParticipantsVar::new_known(cs, self, Derived), commitment_scheme.as_known(cs, Public), utxo_set_verifier.as_known(cs, Public), ) + */ + todo!() } - /// Builds constraints for transfer validity proof. + /// Builds constraints for the transfer validity proof. #[inline] fn build_constraints( - base_asset_id: Option<C::AssetIdVar>, + base_asset_id: Option<AssetIdVar<C>>, participants: TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS>, - commitment_scheme: C::CommitmentSchemeVar, - utxo_set_verifier: C::UtxoSetVerifierVar, - cs: &mut ConstraintSystem<C>, + commitment_scheme: CommitmentSchemeVar<C>, + utxo_set_verifier: UtxoSetVerifierVar<C>, + cs: &mut C::ConstraintSystem, ) { let mut secret_asset_ids = Vec::with_capacity(SENDERS + RECEIVERS); @@ -729,7 +354,7 @@ where .senders .into_iter() .map(|s| { - let asset = s.get_well_formed_asset(cs, &commitment_scheme, &utxo_set_verifier); + let asset = s.get_well_formed_asset(&commitment_scheme, &utxo_set_verifier, cs); secret_asset_ids.push(asset.id); asset.value }) @@ -741,7 +366,7 @@ where .receivers .into_iter() .map(|r| { - let asset = r.get_well_formed_asset(cs, &commitment_scheme); + let asset = r.get_well_formed_asset(&commitment_scheme, cs); secret_asset_ids.push(asset.id); asset.value }) @@ -762,7 +387,7 @@ where pub fn unknown_constraints( commitment_scheme: &C::CommitmentScheme, utxo_set_verifier: &C::UtxoSetVerifier, - ) -> ConstraintSystem<C> { + ) -> C::ConstraintSystem { let mut cs = C::ProofSystem::for_unknown(); let (base_asset_id, participants, commitment_scheme, utxo_set_verifier) = Self::unknown_variables(commitment_scheme, utxo_set_verifier, &mut cs); @@ -782,7 +407,7 @@ where &self, commitment_scheme: &C::CommitmentScheme, utxo_set_verifier: &C::UtxoSetVerifier, - ) -> ConstraintSystem<C> { + ) -> C::ConstraintSystem { let mut cs = C::ProofSystem::for_known(); let (base_asset_id, participants, commitment_scheme, utxo_set_verifier) = self.known_variables(commitment_scheme, utxo_set_verifier, &mut cs); @@ -840,15 +465,15 @@ where { Ok(TransferPost { validity_proof: self.is_valid(commitment_scheme, utxo_set_verifier, context, rng)?, - asset_id: self.public.asset_id, - sources: self.public.sources.into(), - sender_posts: IntoIterator::into_iter(self.secret.senders) + asset_id: self.asset_id, + sources: self.sources.into(), + sender_posts: IntoIterator::into_iter(self.senders) .map(Sender::into_post) .collect(), - receiver_posts: IntoIterator::into_iter(self.secret.receivers) - .map(Receiver::into_post) + receiver_posts: IntoIterator::into_iter(self.receivers) + .map(FullReceiver::into_post) .collect(), - sinks: self.public.sinks.into(), + sinks: self.sinks.into(), }) } } @@ -864,7 +489,7 @@ struct TransferParticipantsVar< C: Configuration, { /// Source Variables - sources: Vec<C::AssetValueVar>, + sources: Vec<AssetValueVar<C>>, /// Sender Variables senders: Vec<SenderVar<C>>, @@ -873,11 +498,11 @@ struct TransferParticipantsVar< receivers: Vec<ReceiverVar<C>>, /// Sink Variables - sinks: Vec<C::AssetValueVar>, + sinks: Vec<AssetValueVar<C>>, } impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> - Variable<ConstraintSystem<C>> for TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> + Variable<C::ConstraintSystem> for TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> where C: Configuration, { @@ -886,29 +511,31 @@ where type Mode = Derived; #[inline] - fn new(cs: &mut ConstraintSystem<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self { sources: this - .public .sources .iter() .map(|source| source.as_known(cs, Public)) .collect(), senders: this - .secret .senders .iter() - .map(|sender| sender.known(cs, mode)) + .map(|sender| { + // + todo!() + }) .collect(), receivers: this - .secret .receivers .iter() - .map(|receiver| receiver.known(cs, mode)) + .map(|receiver| { + // + todo!() + }) .collect(), sinks: this - .public .sinks .iter() .map(|sink| sink.as_known(cs, Public)) @@ -917,65 +544,31 @@ where Allocation::Unknown(mode) => Self { sources: (0..SOURCES) .into_iter() - .map(|_| C::AssetValueVar::new_unknown(cs, Public)) + .map(|_| AssetValueVar::<C>::new_unknown(cs, Public)) .collect(), senders: (0..SENDERS) .into_iter() - .map(|_| SenderVar::new_unknown(cs, mode)) + .map(|_| { + // + todo!() + }) .collect(), receivers: (0..RECEIVERS) .into_iter() - .map(|_| ReceiverVar::new_unknown(cs, mode)) + .map(|_| { + // + todo!() + }) .collect(), sinks: (0..SINKS) .into_iter() - .map(|_| C::AssetValueVar::new_unknown(cs, Public)) + .map(|_| AssetValueVar::<C>::new_unknown(cs, Public)) .collect(), }, } } } -/// Insufficient Public Balance Error -/// -/// This `enum` is the error state of the [`TransferLedger::check_source_balances`] method. See its -/// documentation for more. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub struct InsufficientPublicBalance { - /// Index of the Public Address - pub index: usize, - - /// Current Balance - pub balance: AssetValue, - - /// Amount Attempting to Withdraw - pub withdraw: AssetValue, -} - -/// Transfer Post Error -/// -/// This `enum` is the error state of the [`TransferPost::validate`] method. See its documentation -/// for more. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum TransferPostError { - /// Insufficient Public Balance - InsufficientPublicBalance(InsufficientPublicBalance), - - /// Sender Post Error - Sender(SenderPostError), - - /// Receiver Post Error - Receiver(ReceiverPostError), - - /// Invalid Transfer Proof Error - /// - /// Validity of the transfer could not be proved by the ledger. - InvalidProof, -} - -from_variant_impl!(TransferPostError, Sender, SenderPostError); -from_variant_impl!(TransferPostError, Receiver, ReceiverPostError); - /// Transfer Post pub struct TransferPost<C> where @@ -1000,156 +593,6 @@ where validity_proof: Proof<C>, } -impl<C> TransferPost<C> -where - C: Configuration, -{ - /// Returns the shape of the transfer which generated this post. - #[inline] - pub fn shape(&self) -> DynamicShape { - DynamicShape::new( - self.sources.len(), - self.sender_posts.len(), - self.receiver_posts.len(), - self.sinks.len(), - ) - } - - /// Generates the public input for the [`Transfer`] validation proof. - #[inline] - pub fn generate_proof_input(&self) -> ProofInput<C> { - // TODO: See comments in `crate::identity::constraint` about automatically deriving this - // method from possibly `TransferParticipantsVar`? - let mut input = Default::default(); - if let Some(asset_id) = self.asset_id { - C::ProofSystem::extend(&mut input, &asset_id); - } - self.sources - .iter() - .for_each(|source| C::ProofSystem::extend(&mut input, source)); - self.sender_posts - .iter() - .for_each(|post| post.extend_input::<C::ProofSystem>(&mut input)); - self.receiver_posts - .iter() - .for_each(|post| post.extend_input::<C::ProofSystem>(&mut input)); - self.sinks - .iter() - .for_each(|sink| C::ProofSystem::extend(&mut input, sink)); - input - } - - /// Validates `self` on the transfer `ledger`. - #[inline] - pub fn validate<L>(self, ledger: &L) -> Result<TransferPostingKey<C, L>, TransferPostError> - where - L: TransferLedger<C>, - { - // FIXME: The ledger needs to check whether or not senders and receivers are unique. Should - // this be done in this method or is it something the ledger can do "per-validation". - let source_posting_keys = ledger - .check_source_balances(self.sources) - .map_err(TransferPostError::InsufficientPublicBalance)?; - let sender_posting_keys = self - .sender_posts - .into_iter() - .map(move |s| s.validate(ledger)) - .collect::<Result<Vec<_>, _>>()?; - let receiver_posting_keys = self - .receiver_posts - .into_iter() - .map(move |r| r.validate(ledger)) - .collect::<Result<Vec<_>, _>>()?; - Ok(TransferPostingKey { - validity_proof: match ledger.is_valid( - self.asset_id, - &source_posting_keys, - &sender_posting_keys, - &receiver_posting_keys, - &self.sinks, - self.validity_proof, - ) { - Some(key) => key, - _ => return Err(TransferPostError::InvalidProof), - }, - asset_id: self.asset_id, - source_posting_keys, - sender_posting_keys, - receiver_posting_keys, - sinks: self.sinks, - }) - } -} - -/// Transfer Posting Key -pub struct TransferPostingKey<C, L> -where - C: Configuration, - L: TransferLedger<C>, -{ - /// Asset Id - asset_id: Option<AssetId>, - - /// Source Posting Keys - source_posting_keys: Vec<SourcePostingKey<C, L>>, - - /// Sender Posting Keys - sender_posting_keys: Vec<SenderPostingKey<C, L>>, - - /// Receiver Posting Keys - receiver_posting_keys: Vec<ReceiverPostingKey<C, L>>, - - /// Sinks - sinks: Vec<AssetValue>, - - /// Validity Proof Posting Key - validity_proof: L::ValidProof, -} - -impl<C, L> TransferPostingKey<C, L> -where - C: Configuration, - L: TransferLedger<C>, -{ - /// Returns the shape of the transfer which generated this posting key. - #[inline] - pub fn shape(&self) -> DynamicShape { - DynamicShape::new( - self.source_posting_keys.len(), - self.sender_posting_keys.len(), - self.receiver_posting_keys.len(), - self.sinks.len(), - ) - } - - /// Posts `self` to the transfer `ledger`. - /// - /// # Safety - /// - /// This method assumes that posting `self` to `ledger` is atomic and cannot fail. See - /// [`SenderLedger::spend`] and [`ReceiverLedger::register`] for more information on the - /// contract for this method. - #[inline] - pub fn post(self, super_key: &TransferLedgerSuperPostingKey<C, L>, ledger: &mut L) { - let proof = self.validity_proof; - for key in self.sender_posting_keys { - key.post(&(proof, *super_key), ledger); - } - for key in self.receiver_posting_keys { - key.post(&(proof, *super_key), ledger); - } - if let Some(asset_id) = self.asset_id { - ledger.update_public_balances( - asset_id, - self.source_posting_keys, - self.sinks, - proof, - super_key, - ); - } - } -} - create_seal! {} /// Transfer Shapes @@ -1174,7 +617,7 @@ pub trait Shape: sealed::Sealed { /// Canonical Transaction Types pub mod canonical { use super::*; - use crate::identity::{Identity, PreSender}; + use manta_util::seal; /// Implements [`Shape`] for a given shape type. macro_rules! impl_shape { @@ -1228,52 +671,15 @@ pub mod canonical { { /// Builds a [`Mint`] from `asset` and `receiver`. #[inline] - pub fn build(asset: Asset, receiver: Receiver<C>) -> Self { + pub fn build(asset: Asset, receiver: FullReceiver<C>) -> Self { Self::new( - asset.id, + Some(asset.id), [asset.value], Default::default(), [receiver], Default::default(), ) } - - /// Builds a [`Mint`] from an `identity` and an `asset`. - #[inline] - pub fn from_identity<R>( - identity: Identity<C>, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - rng: &mut R, - ) -> Result<Mint<C>, IntegratedEncryptionSchemeError<C>> - where - R: CryptoRng + RngCore + ?Sized, - { - Ok(Mint::build( - asset, - identity.into_receiver(commitment_scheme, asset, rng)?, - )) - } - - /// Builds a [`Mint`] from an `identity` for an [`Asset`] with the given `asset_id` but - /// zero value. - /// - /// This is particularly useful when constructing transactions accumulated from [`Transfer`] - /// objects and a zero slot on the input side needs to be filled. - #[inline] - pub fn zero<R>( - identity: Identity<C>, - commitment_scheme: &C::CommitmentScheme, - asset_id: AssetId, - rng: &mut R, - ) -> Result<(Mint<C>, PreSender<C>), IntegratedEncryptionSchemeError<C>> - where - R: CryptoRng + RngCore + ?Sized, - { - let asset = Asset::zero(asset_id); - let internal = identity.into_internal(commitment_scheme, asset, rng)?; - Ok((Mint::build(asset, internal.receiver), internal.pre_sender)) - } } /// Private Transfer Transaction Shape @@ -1297,7 +703,7 @@ pub mod canonical { #[inline] pub fn build( senders: [Sender<C>; PrivateTransferShape::SENDERS], - receivers: [Receiver<C>; PrivateTransferShape::RECEIVERS], + receivers: [FullReceiver<C>; PrivateTransferShape::RECEIVERS], ) -> Self { Self::new( Default::default(), @@ -1339,11 +745,11 @@ pub mod canonical { #[inline] pub fn build( senders: [Sender<C>; ReclaimShape::SENDERS], - receivers: [Receiver<C>; ReclaimShape::RECEIVERS], + receivers: [FullReceiver<C>; ReclaimShape::RECEIVERS], reclaim: Asset, ) -> Self { Self::new( - reclaim.id, + Some(reclaim.id), Default::default(), senders, receivers, @@ -1361,7 +767,7 @@ pub mod canonical { Mint(Asset), /// Private Transfer Asset to Receiver - PrivateTransfer(Asset, ShieldedIdentity<C>), + PrivateTransfer(Asset, ReceivingKey<C>), /// Reclaim Private Asset Reclaim(Asset), @@ -1406,496 +812,3 @@ pub mod canonical { Withdraw(Asset), } } - -/// Testing Framework -#[cfg(feature = "test")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] -pub mod test { - use super::*; - use crate::{asset::AssetValueType, identity::Identity}; - use manta_crypto::{ - accumulator::Accumulator, - rand::{Rand, Sample, Standard, TrySample}, - }; - use manta_util::{array_map, fallible_array_map, into_array_unchecked}; - - /// Test Sampling Distributions - pub mod distribution { - use super::*; - - /// [`PublicTransfer`](super::PublicTransfer) Sampling Distribution - pub type PublicTransfer = Standard; - - /// Fixed Asset [`PublicTransfer`](super::PublicTransfer) Sampling Distribution - #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] - pub struct FixedPublicTransfer(pub Asset); - - /// [`SecretTransfer`](super::SecretTransfer) Sampling Distribution - pub type SecretTransfer<'c, C, S> = Transfer<'c, C, S>; - - /// Fixed Asset [`SecretTransfer`](super::SecretTransfer) Sampling Distribution - pub struct FixedSecretTransfer<'c, C, S> - where - C: Configuration, - S: Accumulator< - Item = <C::UtxoSetVerifier as Verifier>::Item, - Verifier = C::UtxoSetVerifier, - >, - { - /// Asset - pub asset: Asset, - - /// Base Distribution - pub base: SecretTransfer<'c, C, S>, - } - - impl<'c, C, S> FixedSecretTransfer<'c, C, S> - where - C: Configuration, - C::SecretKey: Sample, - S: Accumulator< - Item = <C::UtxoSetVerifier as Verifier>::Item, - Verifier = C::UtxoSetVerifier, - >, - { - /// Tries to sample a [`super::SecretTransfer`] using custom sender and receiver asset - /// totals. - /// - /// # Safety - /// - /// This method does not check the input/output size constraints found in the - /// [`super::SecretTransfer::check_shape`] method. - #[inline] - pub(super) fn try_sample_custom_totals< - R, - const SENDERS: usize, - const RECEIVERS: usize, - >( - asset_id: AssetId, - sender_total: AssetValue, - receiver_total: AssetValue, - commitment_scheme: &C::CommitmentScheme, - utxo_set: &mut S, - rng: &mut R, - ) -> Result< - super::SecretTransfer<C, SENDERS, RECEIVERS>, - IntegratedEncryptionSchemeError<C>, - > - where - R: CryptoRng + RngCore + ?Sized, - { - FixedSecretTransfer::<C, S>::try_sample_custom_distribution( - asset_id, - sample_asset_values::<_, SENDERS>(sender_total, rng), - sample_asset_values::<_, RECEIVERS>(receiver_total, rng), - commitment_scheme, - utxo_set, - rng, - ) - } - - /// Tries to sample a [`super::SecretTransfer`] with custom sender and receiver asset - /// value distributions. - /// - /// # Safety - /// - /// This method does not check the input/output size constraints found in the - /// [`super::SecretTransfer::check_shape`] method. - #[inline] - pub(super) fn try_sample_custom_distribution< - R, - const SENDERS: usize, - const RECEIVERS: usize, - >( - asset_id: AssetId, - senders: AssetValues<SENDERS>, - receivers: AssetValues<RECEIVERS>, - commitment_scheme: &C::CommitmentScheme, - utxo_set: &mut S, - rng: &mut R, - ) -> Result< - super::SecretTransfer<C, SENDERS, RECEIVERS>, - IntegratedEncryptionSchemeError<C>, - > - where - R: CryptoRng + RngCore + ?Sized, - { - Ok(super::SecretTransfer::new_unchecked( - array_map(senders, |v| { - let pre_sender = - Identity::gen(rng).into_pre_sender(commitment_scheme, asset_id.with(v)); - pre_sender.insert_utxo(utxo_set); - pre_sender.try_upgrade(utxo_set).unwrap() - }), - fallible_array_map(receivers, |v| { - Identity::gen(rng).into_receiver(commitment_scheme, asset_id.with(v), rng) - })?, - )) - } - - /// Tries to sample a [`super::SecretTransfer`]. - #[inline] - pub(super) fn try_sample<R, const SENDERS: usize, const RECEIVERS: usize>( - self, - rng: &mut R, - ) -> Result< - super::SecretTransfer<C, SENDERS, RECEIVERS>, - IntegratedEncryptionSchemeError<C>, - > - where - R: CryptoRng + RngCore + ?Sized, - { - super::SecretTransfer::<C, SENDERS, RECEIVERS>::check_shape(); - Self::try_sample_custom_totals( - self.asset.id, - self.asset.value, - self.asset.value, - self.base.commitment_scheme, - self.base.utxo_set, - rng, - ) - } - } - - /// [`Transfer`](super::Transfer) Sampling Distribution - pub struct Transfer<'c, C, S> - where - C: Configuration, - S: Accumulator< - Item = <C::UtxoSetVerifier as Verifier>::Item, - Verifier = C::UtxoSetVerifier, - >, - { - /// Commitment Scheme - pub commitment_scheme: &'c C::CommitmentScheme, - - /// UTXO Set - pub utxo_set: &'c mut S, - } - - /// Fixed Asset [`Transfer`](super::Transfer) Sampling Distribution - pub struct FixedTransfer<'c, C, S> - where - C: Configuration, - S: Accumulator< - Item = <C::UtxoSetVerifier as Verifier>::Item, - Verifier = C::UtxoSetVerifier, - >, - { - /// Asset - pub asset: Asset, - - /// Base Distribution - pub base: Transfer<'c, C, S>, - } - } - - /// Samples a distribution over `count`-many values summing to `total`. - /// - /// # Warning - /// - /// This is a naive algorithm and should only be used for testing purposes. - #[inline] - pub fn value_distribution<R>(count: usize, total: AssetValue, rng: &mut R) -> Vec<AssetValue> - where - R: CryptoRng + RngCore + ?Sized, - { - if count == 0 { - return Default::default(); - } - let mut result = Vec::with_capacity(count + 1); - result.push(AssetValue(0)); - for _ in 1..count { - result.push(AssetValue(AssetValueType::gen(rng) % total.0)); - } - result.push(total); - result.sort_unstable(); - for i in 0..count { - result[i] = result[i + 1] - result[i]; - } - result.pop().unwrap(); - result - } - - /// Samples asset values from `rng`. - /// - /// # Warning - /// - /// This is a naive algorithm and should only be used for testing purposes. - #[inline] - pub fn sample_asset_values<R, const N: usize>(total: AssetValue, rng: &mut R) -> AssetValues<N> - where - R: CryptoRng + RngCore + ?Sized, - { - into_array_unchecked(value_distribution(N, total, rng)) - } - - impl<const SOURCES: usize, const SINKS: usize> Sample<distribution::PublicTransfer> - for PublicTransfer<SOURCES, SINKS> - { - #[inline] - fn sample<R>(distribution: distribution::PublicTransfer, rng: &mut R) -> Self - where - R: CryptoRng + RngCore + ?Sized, - { - let _ = distribution; - Self::sample(distribution::FixedPublicTransfer(rng.gen()), rng) - } - } - - impl<const SOURCES: usize, const SINKS: usize> Sample<distribution::FixedPublicTransfer> - for PublicTransfer<SOURCES, SINKS> - { - #[inline] - fn sample<R>(distribution: distribution::FixedPublicTransfer, rng: &mut R) -> Self - where - R: CryptoRng + RngCore + ?Sized, - { - Self::new( - distribution.0.id, - sample_asset_values(distribution.0.value, rng), - sample_asset_values(distribution.0.value, rng), - ) - } - } - - impl<C, S, const SENDERS: usize, const RECEIVERS: usize> - TrySample<distribution::SecretTransfer<'_, C, S>> for SecretTransfer<C, SENDERS, RECEIVERS> - where - C: Configuration, - C::SecretKey: Sample, - S: Accumulator< - Item = <C::UtxoSetVerifier as Verifier>::Item, - Verifier = C::UtxoSetVerifier, - >, - { - type Error = IntegratedEncryptionSchemeError<C>; - - #[inline] - fn try_sample<R>( - distribution: distribution::SecretTransfer<C, S>, - rng: &mut R, - ) -> Result<Self, Self::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - Self::try_sample( - distribution::FixedSecretTransfer { - asset: rng.gen(), - base: distribution, - }, - rng, - ) - } - } - - impl<C, S, const SENDERS: usize, const RECEIVERS: usize> - TrySample<distribution::FixedSecretTransfer<'_, C, S>> - for SecretTransfer<C, SENDERS, RECEIVERS> - where - C: Configuration, - C::SecretKey: Sample, - S: Accumulator< - Item = <C::UtxoSetVerifier as Verifier>::Item, - Verifier = C::UtxoSetVerifier, - >, - { - type Error = IntegratedEncryptionSchemeError<C>; - - #[inline] - fn try_sample<R>( - distribution: distribution::FixedSecretTransfer<C, S>, - rng: &mut R, - ) -> Result<Self, Self::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - distribution.try_sample(rng) - } - } - - impl< - C, - S, - const SOURCES: usize, - const SENDERS: usize, - const RECEIVERS: usize, - const SINKS: usize, - > TrySample<distribution::Transfer<'_, C, S>> - for Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS> - where - C: Configuration, - C::SecretKey: Sample, - S: Accumulator< - Item = <C::UtxoSetVerifier as Verifier>::Item, - Verifier = C::UtxoSetVerifier, - >, - { - type Error = IntegratedEncryptionSchemeError<C>; - - #[inline] - fn try_sample<R>( - distribution: distribution::Transfer<C, S>, - rng: &mut R, - ) -> Result<Self, Self::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - Self::try_sample( - distribution::FixedTransfer { - asset: rng.gen(), - base: distribution, - }, - rng, - ) - } - } - - impl< - C, - S, - const SOURCES: usize, - const SENDERS: usize, - const RECEIVERS: usize, - const SINKS: usize, - > TrySample<distribution::FixedTransfer<'_, C, S>> - for Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS> - where - C: Configuration, - C::SecretKey: Sample, - S: Accumulator< - Item = <C::UtxoSetVerifier as Verifier>::Item, - Verifier = C::UtxoSetVerifier, - >, - { - type Error = IntegratedEncryptionSchemeError<C>; - - #[inline] - fn try_sample<R>( - distribution: distribution::FixedTransfer<C, S>, - rng: &mut R, - ) -> Result<Self, Self::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - Self::check_shape(); - - let asset = distribution.asset; - let mut input = value_distribution(SOURCES + SENDERS, asset.value, rng); - let mut output = value_distribution(RECEIVERS + SINKS, asset.value, rng); - let secret_input = input.split_off(SOURCES); - let public_output = output.split_off(RECEIVERS); - - Ok(Self { - public: PublicTransfer::new( - asset.id, - into_array_unchecked(input), - into_array_unchecked(public_output), - ), - secret: distribution::FixedSecretTransfer::try_sample_custom_distribution( - asset.id, - into_array_unchecked(secret_input), - into_array_unchecked(output), - distribution.base.commitment_scheme, - distribution.base.utxo_set, - rng, - )?, - }) - } - } - - impl< - C, - const SOURCES: usize, - const SENDERS: usize, - const RECEIVERS: usize, - const SINKS: usize, - > Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS> - where - C: Configuration, - { - /// Samples constraint system for unknown transfer from `rng`. - #[inline] - pub fn sample_unknown_constraints<CD, VD, R>(rng: &mut R) -> ConstraintSystem<C> - where - CD: Default, - VD: Default, - C::CommitmentScheme: Sample<CD>, - C::UtxoSetVerifier: Sample<VD>, - R: CryptoRng + RngCore + ?Sized, - { - Self::unknown_constraints(&rng.gen(), &rng.gen()) - } - - /// Samples proving and verifying contexts from `rng`. - #[inline] - pub fn sample_context<CD, VD, R>( - rng: &mut R, - ) -> Result<(ProvingContext<C>, VerifyingContext<C>), ProofSystemError<C>> - where - CD: Default, - VD: Default, - C::CommitmentScheme: Sample<CD>, - C::UtxoSetVerifier: Sample<VD>, - R: CryptoRng + RngCore + ?Sized, - { - Self::generate_context(&rng.gen(), &rng.gen(), rng) - } - - /// Samples a [`Transfer`] and checks whether its internal proof is valid relative to the - /// given `commitment_scheme` and `utxo_set`. - #[inline] - pub fn sample_and_check_proof<S, R>( - commitment_scheme: &C::CommitmentScheme, - utxo_set: &mut S, - rng: &mut R, - ) -> Result<bool, ProofSystemError<C>> - where - C::SecretKey: Sample, - S: Accumulator< - Item = <C::UtxoSetVerifier as Verifier>::Item, - Verifier = C::UtxoSetVerifier, - >, - R: CryptoRng + RngCore + ?Sized, - { - let transfer = Self::try_sample( - distribution::Transfer { - commitment_scheme, - utxo_set, - }, - rng, - ) - .ok() - .expect("This test is not checking whether sampling works properly."); - - let (proving_key, verifying_key) = - Self::generate_context(commitment_scheme, utxo_set.verifier(), rng)?; - - has_valid_proof( - &transfer.into_post(commitment_scheme, utxo_set.verifier(), &proving_key, rng)?, - &verifying_key, - ) - } - } - - /// Checks if `post` has a valid internal proof, verifying with the given `context`. - #[inline] - pub fn has_valid_proof<C>( - post: &TransferPost<C>, - context: &VerifyingContext<C>, - ) -> Result<bool, ProofSystemError<C>> - where - C: Configuration, - { - C::ProofSystem::verify(&post.generate_proof_input(), &post.validity_proof, context) - } - - /// Asserts that `post` has a valid internal proof, verifying with the given `context`. - #[inline] - pub fn assert_valid_proof<C>(post: &TransferPost<C>, context: &VerifyingContext<C>) - where - C: Configuration, - { - assert!(matches!(has_valid_proof(post, context), Ok(true))); - } -} diff --git a/manta-accounting/src/transfer2.rs b/manta-accounting/src/transfer2.rs deleted file mode 100644 index 5a0553462..000000000 --- a/manta-accounting/src/transfer2.rs +++ /dev/null @@ -1,816 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Transfer Protocol - -use crate::{ - asset::{Asset, AssetId, AssetValue, AssetVar}, - identity2::{self, CommitmentSchemeOutput, Utxo}, -}; -use alloc::vec::Vec; -use core::ops::Add; -use manta_crypto::{ - accumulator::Verifier, - commitment::CommitmentScheme, - constraint::{ - reflection::{HasEqual, HasVariable, Var}, - Allocation, Constant, ConstraintSystem, Derived, Equal, ProofSystem, Public, - PublicOrSecret, Variable, VariableSource, - }, - encryption::{EncryptedMessage, HybridPublicKeyEncryptionScheme}, - key::KeyAgreementScheme, - rand::{CryptoRng, RngCore}, -}; -use manta_util::create_seal; - -/// Returns `true` if the transfer with this shape would have no public participants. -#[inline] -pub const fn has_no_public_participants( - sources: usize, - senders: usize, - receivers: usize, - sinks: usize, -) -> bool { - let _ = (senders, receivers); - sources == 0 && sinks == 0 -} - -/// Transfer Configuration -pub trait Configuration: identity2::Configuration<Asset = Asset> { - /// Encryption Scheme Type - type EncryptionScheme: HybridPublicKeyEncryptionScheme< - Plaintext = Self::Asset, - KeyAgreementScheme = Self::KeyAgreementScheme, - >; - - /// UTXO Set Verifier Type - type UtxoSetVerifier: Verifier<Item = Utxo<Self>, Verification = bool>; - - /// Constraint System Type - type ConstraintSystem: ConstraintSystem - + HasVariable< - Self::KeyAgreementScheme, - Variable = KeyAgreementSchemeVar<Self>, - Mode = Constant, - > + HasVariable<Self::CommitmentScheme, Variable = CommitmentSchemeVar<Self>, Mode = Constant> - + HasVariable<Self::UtxoSetVerifier, Variable = UtxoSetVerifierVar<Self>, Mode = Constant> - + HasVariable<AssetId, Variable = AssetIdVar<Self>, Mode = PublicOrSecret> - + HasVariable<AssetValue, Variable = AssetValueVar<Self>, Mode = PublicOrSecret> - + HasVariable< - CommitmentSchemeOutput<Self>, - Variable = CommitmentSchemeOutput<Self::ConstraintConfiguration>, - Mode = PublicOrSecret, - > + HasEqual<CommitmentSchemeOutput<Self::ConstraintConfiguration>>; - - /// Constraint System Configuration - type ConstraintConfiguration: ConstraintConfiguration<Self::ConstraintSystem>; - - /// Proof System Type - type ProofSystem: ProofSystem<ConstraintSystem = Self::ConstraintSystem, Verification = bool>; -} - -/// Transfer Constraint System Configuration -pub trait ConstraintConfiguration<CS>: identity2::Configuration<Asset = AssetVar<CS>> -where - CS: ConstraintSystem - + HasVariable<AssetId, Variable = Self::AssetId, Mode = PublicOrSecret> - + HasVariable<AssetValue, Variable = Self::AssetValue, Mode = PublicOrSecret>, -{ - /// Asset Id Variable Type - type AssetId: Variable<CS, Type = AssetId, Mode = PublicOrSecret> + Equal<CS>; - - /// Asset Value Variable Type - type AssetValue: Variable<CS, Type = AssetValue, Mode = PublicOrSecret> - + Equal<CS> - + Add<Output = Self::AssetValue>; - - /// UTXO Set Verifier Variable Type - type UtxoSetVerifier: Verifier<Item = Utxo<Self>, Verification = CS::Bool>; -} - -/// Spending Key Type -pub type SpendingKey<C> = - identity2::SpendingKey<<C as identity2::Configuration>::KeyAgreementScheme>; - -/// Receiving Key Type -pub type ReceivingKey<C> = - identity2::ReceivingKey<<C as identity2::Configuration>::KeyAgreementScheme>; - -/// Encrypted Note Type -pub type EncryptedNote<C> = EncryptedMessage<<C as Configuration>::EncryptionScheme>; - -/// Constraint System Type -type ConstraintSystemType<C> = <C as Configuration>::ConstraintSystem; - -/// Constraint Configuration Type -type ConstraintConfigurationType<C> = <C as Configuration>::ConstraintConfiguration; - -/// Pre-Sender Type -pub type PreSender<C> = identity2::PreSender<C>; - -/// Sender Type -pub type Sender<C> = identity2::Sender<C, <C as Configuration>::UtxoSetVerifier>; - -/// Sender Variable Type -type SenderVar<C> = identity2::Sender< - ConstraintConfigurationType<C>, - <ConstraintConfigurationType<C> as ConstraintConfiguration<ConstraintSystemType<C>>>::UtxoSetVerifier, ->; - -/// Sender Post Type -pub type SenderPost<C> = identity2::SenderPost<C, <C as Configuration>::UtxoSetVerifier>; - -/// Receiver Type -pub type Receiver<C> = identity2::Receiver<C>; - -/// Receiver Variable Type -type ReceiverVar<C> = identity2::Receiver<<C as Configuration>::ConstraintConfiguration>; - -/// Full Receiver Type -pub type FullReceiver<C> = identity2::FullReceiver<C>; - -/// Receiver Post Type -pub type ReceiverPost<C> = identity2::ReceiverPost<C, <C as Configuration>::EncryptionScheme>; - -/// Asset Id Variable Type -pub type AssetIdVar<C> = - <ConstraintConfigurationType<C> as ConstraintConfiguration<ConstraintSystemType<C>>>::AssetId; - -/// Asset Value Variable Type -pub type AssetValueVar<C> = <ConstraintConfigurationType<C> as ConstraintConfiguration< - ConstraintSystemType<C>, ->>::AssetValue; - -/// Key Agreement Scheme Variable Type -pub type KeyAgreementSchemeVar<C> = - <ConstraintConfigurationType<C> as identity2::Configuration>::KeyAgreementScheme; - -/// Commitment Scheme Variable Type -pub type CommitmentSchemeVar<C> = - <ConstraintConfigurationType<C> as identity2::Configuration>::CommitmentScheme; - -/// UTXO Set Verifier Variable Type -pub type UtxoSetVerifierVar<C> = <ConstraintConfigurationType<C> as ConstraintConfiguration< - ConstraintSystemType<C>, ->>::UtxoSetVerifier; - -/// Transfer Proof System Type -type ProofSystemType<C> = <C as Configuration>::ProofSystem; - -/// Transfer Proof System Error Type -pub type ProofSystemError<C> = <ProofSystemType<C> as ProofSystem>::Error; - -/// Transfer Proving Context Type -pub type ProvingContext<C> = <ProofSystemType<C> as ProofSystem>::ProvingContext; - -/// Transfer Verifying Context Type -pub type VerifyingContext<C> = <ProofSystemType<C> as ProofSystem>::VerifyingContext; - -/// Transfer Validity Proof Type -pub type Proof<C> = <ProofSystemType<C> as ProofSystem>::Proof; - -/// Transfer -pub struct Transfer< - C, - const SOURCES: usize, - const SENDERS: usize, - const RECEIVERS: usize, - const SINKS: usize, -> where - C: Configuration, -{ - /// Asset Id - asset_id: Option<AssetId>, - - /// Sources - sources: [AssetValue; SOURCES], - - /// Senders - senders: [Sender<C>; SENDERS], - - /// Receivers - receivers: [FullReceiver<C>; RECEIVERS], - - /// Sinks - sinks: [AssetValue; SINKS], -} - -impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> - Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS> -where - C: Configuration, -{ - /// Builds a new [`Transfer`] from public and secret information. - #[inline] - fn new( - asset_id: Option<AssetId>, - sources: [AssetValue; SOURCES], - senders: [Sender<C>; SENDERS], - receivers: [FullReceiver<C>; RECEIVERS], - sinks: [AssetValue; SINKS], - ) -> Self { - Self::check_shape(asset_id.is_some()); - Self::new_unchecked(asset_id, sources, senders, receivers, sinks) - } - - /// Checks that the [`Transfer`] has a valid shape. - #[inline] - fn check_shape(has_visible_asset_id: bool) { - Self::has_nonempty_input_shape(); - Self::has_nonempty_output_shape(); - Self::has_visible_asset_id_when_required(has_visible_asset_id); - } - - /// Checks that the input side of the transfer is not empty. - #[inline] - fn has_nonempty_input_shape() { - assert_ne!( - SOURCES + SENDERS, - 0, - "Not enough participants on the input side." - ); - } - - /// Checks that the output side of the transfer is not empty. - #[inline] - fn has_nonempty_output_shape() { - assert_ne!( - RECEIVERS + SINKS, - 0, - "Not enough participants on the output side." - ); - } - - /// Checks that the given `asset_id` for [`Transfer`] building is visible exactly when required. - #[inline] - fn has_visible_asset_id_when_required(has_visible_asset_id: bool) { - if SOURCES > 0 || SINKS > 0 { - assert!( - has_visible_asset_id, - "Missing public asset id when required." - ); - } else { - assert!( - !has_visible_asset_id, - "Given public asset id when not required." - ); - } - } - - /// Builds a new [`Transfer`] without checking the number of participants on the input and - /// output sides. - #[inline] - fn new_unchecked( - asset_id: Option<AssetId>, - sources: [AssetValue; SOURCES], - senders: [Sender<C>; SENDERS], - receivers: [FullReceiver<C>; RECEIVERS], - sinks: [AssetValue; SINKS], - ) -> Self { - Self { - asset_id, - sources, - senders, - receivers, - sinks, - } - } - - /// Generates the unknown variables for the transfer validity proof. - #[inline] - fn unknown_variables( - commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &C::UtxoSetVerifier, - cs: &mut C::ConstraintSystem, - ) -> ( - Option<AssetIdVar<C>>, - TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS>, - CommitmentSchemeVar<C>, - UtxoSetVerifierVar<C>, - ) { - let base_asset_id = if has_no_public_participants(SOURCES, SENDERS, RECEIVERS, SINKS) { - None - } else { - Some(AssetIdVar::<C>::new_unknown(cs, Public)) - }; - /* TODO: - ( - base_asset_id, - TransferParticipantsVar::new_unknown(cs, Derived), - commitment_scheme.as_known(cs, Public), - utxo_set_verifier.as_known(cs, Public), - ) - */ - todo!() - } - - /// Generates the known variables for the transfer validity proof. - #[inline] - fn known_variables( - &self, - commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &C::UtxoSetVerifier, - cs: &mut C::ConstraintSystem, - ) -> ( - Option<AssetIdVar<C>>, - TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS>, - CommitmentSchemeVar<C>, - UtxoSetVerifierVar<C>, - ) { - /* TODO: - ( - self.public.asset_id.map(|id| id.as_known(cs, Public)), - TransferParticipantsVar::new_known(cs, self, Derived), - commitment_scheme.as_known(cs, Public), - utxo_set_verifier.as_known(cs, Public), - ) - */ - todo!() - } - - /// Builds constraints for the transfer validity proof. - #[inline] - fn build_constraints( - base_asset_id: Option<AssetIdVar<C>>, - participants: TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS>, - commitment_scheme: CommitmentSchemeVar<C>, - utxo_set_verifier: UtxoSetVerifierVar<C>, - cs: &mut C::ConstraintSystem, - ) { - let mut secret_asset_ids = Vec::with_capacity(SENDERS + RECEIVERS); - - let input_sum = participants - .senders - .into_iter() - .map(|s| { - let asset = s.get_well_formed_asset(&commitment_scheme, &utxo_set_verifier, cs); - secret_asset_ids.push(asset.id); - asset.value - }) - .chain(participants.sources) - .reduce(Add::add) - .unwrap(); - - let output_sum = participants - .receivers - .into_iter() - .map(|r| { - let asset = r.get_well_formed_asset(&commitment_scheme, cs); - secret_asset_ids.push(asset.id); - asset.value - }) - .chain(participants.sinks) - .reduce(Add::add) - .unwrap(); - - cs.assert_eq(&input_sum, &output_sum); - - match base_asset_id { - Some(asset_id) => cs.assert_all_eq_to_base(&asset_id, secret_asset_ids.iter()), - _ => cs.assert_all_eq(secret_asset_ids.iter()), - } - } - - /// Generates the constraint system for an unknown transfer. - #[inline] - pub fn unknown_constraints( - commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &C::UtxoSetVerifier, - ) -> C::ConstraintSystem { - let mut cs = C::ProofSystem::for_unknown(); - let (base_asset_id, participants, commitment_scheme, utxo_set_verifier) = - Self::unknown_variables(commitment_scheme, utxo_set_verifier, &mut cs); - Self::build_constraints( - base_asset_id, - participants, - commitment_scheme, - utxo_set_verifier, - &mut cs, - ); - cs - } - - /// Generates the constraint system for a known transfer. - #[inline] - pub fn known_constraints( - &self, - commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &C::UtxoSetVerifier, - ) -> C::ConstraintSystem { - let mut cs = C::ProofSystem::for_known(); - let (base_asset_id, participants, commitment_scheme, utxo_set_verifier) = - self.known_variables(commitment_scheme, utxo_set_verifier, &mut cs); - Self::build_constraints( - base_asset_id, - participants, - commitment_scheme, - utxo_set_verifier, - &mut cs, - ); - cs - } - - /// Generates a proving and verifying context for this transfer shape. - #[inline] - pub fn generate_context<R>( - commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &C::UtxoSetVerifier, - rng: &mut R, - ) -> Result<(ProvingContext<C>, VerifyingContext<C>), ProofSystemError<C>> - where - R: CryptoRng + RngCore + ?Sized, - { - Self::unknown_constraints(commitment_scheme, utxo_set_verifier) - .generate_context::<C::ProofSystem, _>(rng) - } - - /// Generates a validity proof for this transfer. - #[inline] - pub fn is_valid<R>( - &self, - commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &C::UtxoSetVerifier, - context: &ProvingContext<C>, - rng: &mut R, - ) -> Result<Proof<C>, ProofSystemError<C>> - where - R: CryptoRng + RngCore + ?Sized, - { - self.known_constraints(commitment_scheme, utxo_set_verifier) - .prove::<C::ProofSystem, _>(context, rng) - } - - /// Converts `self` into its ledger post. - #[inline] - pub fn into_post<R>( - self, - commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &C::UtxoSetVerifier, - context: &ProvingContext<C>, - rng: &mut R, - ) -> Result<TransferPost<C>, ProofSystemError<C>> - where - R: CryptoRng + RngCore + ?Sized, - { - Ok(TransferPost { - validity_proof: self.is_valid(commitment_scheme, utxo_set_verifier, context, rng)?, - asset_id: self.asset_id, - sources: self.sources.into(), - sender_posts: IntoIterator::into_iter(self.senders) - .map(Sender::into_post) - .collect(), - receiver_posts: IntoIterator::into_iter(self.receivers) - .map(FullReceiver::into_post) - .collect(), - sinks: self.sinks.into(), - }) - } -} - -/// Transfer Participants Variable -struct TransferParticipantsVar< - C, - const SOURCES: usize, - const SENDERS: usize, - const RECEIVERS: usize, - const SINKS: usize, -> where - C: Configuration, -{ - /// Source Variables - sources: Vec<AssetValueVar<C>>, - - /// Sender Variables - senders: Vec<SenderVar<C>>, - - /// Receiver Variables - receivers: Vec<ReceiverVar<C>>, - - /// Sink Variables - sinks: Vec<AssetValueVar<C>>, -} - -impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> - Variable<C::ConstraintSystem> for TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> -where - C: Configuration, -{ - type Type = Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>; - - type Mode = Derived; - - #[inline] - fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - match allocation { - Allocation::Known(this, mode) => Self { - sources: this - .sources - .iter() - .map(|source| source.as_known(cs, Public)) - .collect(), - senders: this - .senders - .iter() - .map(|sender| { - // - todo!() - }) - .collect(), - receivers: this - .receivers - .iter() - .map(|receiver| { - // - todo!() - }) - .collect(), - sinks: this - .sinks - .iter() - .map(|sink| sink.as_known(cs, Public)) - .collect(), - }, - Allocation::Unknown(mode) => Self { - sources: (0..SOURCES) - .into_iter() - .map(|_| AssetValueVar::<C>::new_unknown(cs, Public)) - .collect(), - senders: (0..SENDERS) - .into_iter() - .map(|_| { - // - todo!() - }) - .collect(), - receivers: (0..RECEIVERS) - .into_iter() - .map(|_| { - // - todo!() - }) - .collect(), - sinks: (0..SINKS) - .into_iter() - .map(|_| AssetValueVar::<C>::new_unknown(cs, Public)) - .collect(), - }, - } - } -} - -/// Transfer Post -pub struct TransferPost<C> -where - C: Configuration, -{ - /// Asset Id - asset_id: Option<AssetId>, - - /// Sources - sources: Vec<AssetValue>, - - /// Sender Posts - sender_posts: Vec<SenderPost<C>>, - - /// Receiver Posts - receiver_posts: Vec<ReceiverPost<C>>, - - /// Sinks - sinks: Vec<AssetValue>, - - /// Validity Proof - validity_proof: Proof<C>, -} - -create_seal! {} - -/// Transfer Shapes -/// -/// This trait identifies a transfer shape, i.e. the number and type of participants on the input -/// and output sides of the transaction. This trait is sealed and can only be used with the -/// [existing canonical implementations](canonical). -pub trait Shape: sealed::Sealed { - /// Number of Sources - const SOURCES: usize; - - /// Number of Senders - const SENDERS: usize; - - /// Number of Receivers - const RECEIVERS: usize; - - /// Number of Sinks - const SINKS: usize; -} - -/// Canonical Transaction Types -pub mod canonical { - use super::*; - use manta_util::seal; - - /// Implements [`Shape`] for a given shape type. - macro_rules! impl_shape { - ($shape:tt, $sources:expr, $senders:expr, $receivers:expr, $sinks:expr) => { - seal!($shape); - impl Shape for $shape { - const SOURCES: usize = $sources; - const SENDERS: usize = $senders; - const RECEIVERS: usize = $receivers; - const SINKS: usize = $sinks; - } - }; - } - - /// Builds a new alias using the given shape type. - macro_rules! alias_type { - ($type:tt, $t:ident, $shape:tt) => { - $type< - $t, - { $shape::SOURCES }, - { $shape::SENDERS }, - { $shape::RECEIVERS }, - { $shape::SINKS }, - > - } - } - - /// Builds a new [`Transfer`] alias using the given shape type. - macro_rules! transfer_alias { - ($t:ident, $shape:tt) => { - alias_type!(Transfer, $t, $shape) - }; - } - - /// Mint Transaction Shape - /// - /// ```text - /// <1, 0, 1, 0> - /// ``` - #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd)] - pub struct MintShape; - - impl_shape!(MintShape, 1, 0, 1, 0); - - /// Mint Transaction - pub type Mint<C> = transfer_alias!(C, MintShape); - - impl<C> Mint<C> - where - C: Configuration, - { - /// Builds a [`Mint`] from `asset` and `receiver`. - #[inline] - pub fn build(asset: Asset, receiver: FullReceiver<C>) -> Self { - Self::new( - Some(asset.id), - [asset.value], - Default::default(), - [receiver], - Default::default(), - ) - } - } - - /// Private Transfer Transaction Shape - /// - /// ```text - /// <0, 2, 2, 0> - /// ``` - #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd)] - pub struct PrivateTransferShape; - - impl_shape!(PrivateTransferShape, 0, 2, 2, 0); - - /// Private Transfer Transaction - pub type PrivateTransfer<C> = transfer_alias!(C, PrivateTransferShape); - - impl<C> PrivateTransfer<C> - where - C: Configuration, - { - /// Builds a [`PrivateTransfer`] from `senders` and `receivers`. - #[inline] - pub fn build( - senders: [Sender<C>; PrivateTransferShape::SENDERS], - receivers: [FullReceiver<C>; PrivateTransferShape::RECEIVERS], - ) -> Self { - Self::new( - Default::default(), - Default::default(), - senders, - receivers, - Default::default(), - ) - } - } - - /// Reclaim Transaction Shape - /// - /// ```text - /// <0, 2, 1, 1> - /// ``` - /// - /// The [`ReclaimShape`] is defined in terms of the [`PrivateTransferShape`]. It is defined to - /// have the same number of senders and one secret receiver turned into a public sink. - #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd)] - pub struct ReclaimShape; - - impl_shape!( - ReclaimShape, - 0, - PrivateTransferShape::SENDERS, - PrivateTransferShape::RECEIVERS - 1, - 1 - ); - - /// Reclaim Transaction - pub type Reclaim<C> = transfer_alias!(C, ReclaimShape); - - impl<C> Reclaim<C> - where - C: Configuration, - { - /// Builds a [`Reclaim`] from `senders`, `receivers`, and `reclaim`. - #[inline] - pub fn build( - senders: [Sender<C>; ReclaimShape::SENDERS], - receivers: [FullReceiver<C>; ReclaimShape::RECEIVERS], - reclaim: Asset, - ) -> Self { - Self::new( - Some(reclaim.id), - Default::default(), - senders, - receivers, - [reclaim.value], - ) - } - } - - /// Canonical Transaction Type - pub enum Transaction<C> - where - C: Configuration, - { - /// Mint Private Asset - Mint(Asset), - - /// Private Transfer Asset to Receiver - PrivateTransfer(Asset, ReceivingKey<C>), - - /// Reclaim Private Asset - Reclaim(Asset), - } - - impl<C> Transaction<C> - where - C: Configuration, - { - /// Checks that `self` can be executed for a given `balance` state, returning the - /// transaction kind if successful, and returning the asset back if the balance was - /// insufficient. - #[inline] - pub fn check<F>(&self, balance: F) -> Result<TransactionKind, Asset> - where - F: FnOnce(Asset) -> bool, - { - match self { - Self::Mint(asset) => Ok(TransactionKind::Deposit(*asset)), - Self::PrivateTransfer(asset, _) | Self::Reclaim(asset) => { - if balance(*asset) { - Ok(TransactionKind::Withdraw(*asset)) - } else { - Err(*asset) - } - } - } - } - } - - /// Transaction Kind - #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] - pub enum TransactionKind { - /// Deposit Transaction - /// - /// A transaction of this kind will result in a deposit of `asset`. - Deposit(Asset), - - /// Withdraw Transaction - /// - /// A transaction of this kind will result in a withdraw of `asset`. - Withdraw(Asset), - } -} diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs index 81d5da960..58d53d835 100644 --- a/manta-accounting/src/wallet/ledger.rs +++ b/manta-accounting/src/wallet/ledger.rs @@ -21,7 +21,7 @@ use crate::{ identity::{Utxo, VoidNumber}, - transfer::{Configuration, EncryptedAsset, TransferPost}, + transfer::{Configuration, EncryptedNote, TransferPost}, }; use alloc::vec::Vec; use core::future::Future; @@ -44,7 +44,7 @@ where type Checkpoint: Checkpoint; /// Receiver Data Iterator Type - type ReceiverData: IntoIterator<Item = (Utxo<C>, EncryptedAsset<C>)>; + type ReceiverData: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>; /// Sender Data Iterator Type type SenderData: IntoIterator<Item = VoidNumber<C>>; diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 506be4a05..e638b90ae 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -30,17 +30,13 @@ use crate::{ asset::{Asset, AssetId, AssetMap, AssetValue}, fs::{Load, LoadWith, Save, SaveWith}, - identity::{self, Identity, PreSender, Utxo}, - key::{ - Account, DerivedSecretKeyGenerator, ExternalKeyOwned, ExternalSecretKey, Index, - InternalIndex, InternalKeyOwned, KeyKind, KeyOwned, - }, + identity::{self, PreSender, PublicKey, SecretKey, Utxo}, + key::{Account, HierarchicalKeyTable}, transfer::{ self, canonical::{Mint, PrivateTransfer, PrivateTransferShape, Reclaim, Transaction}, - EncryptedAsset, IntegratedEncryptionSchemeError, InternalIdentity, ProofSystemError, - ProvingContext, Receiver, SecretTransfer, Sender, Shape, ShieldedIdentity, Transfer, - TransferPost, + EncryptedNote, FullReceiver, ProofSystemError, ProvingContext, Receiver, ReceivingKey, + Sender, Shape, Transfer, TransferPost, }, }; use alloc::{vec, vec::Vec}; @@ -57,6 +53,7 @@ use manta_crypto::{ Accumulator, ConstantCapacityAccumulator, ExactSizeAccumulator, OptimizedAccumulator, Verifier, }, + key::KeyAgreementScheme, rand::{CryptoRng, RngCore}, }; use manta_util::{fallible_array_map, into_array_unchecked, iter::IteratorExt}; @@ -83,20 +80,20 @@ pub trait Rollback { } /// Signer Connection -pub trait Connection<D, C> +pub trait Connection<H, C> where - D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, + H: HierarchicalKeyTable<SecretKey = SecretKey<C>>, + C: transfer::Configuration, { /// Sync Future Type /// /// Future for the [`sync`](Self::sync) method. - type SyncFuture: Future<Output = SyncResult<D, C, Self>>; + type SyncFuture: Future<Output = SyncResult<H, C, Self>>; /// Sign Future Type /// /// Future for the [`sign`](Self::sign) method. - type SignFuture: Future<Output = SignResult<D, C, Self>>; + type SignFuture: Future<Output = SignResult<H, C, Self>>; /// Sign Commit Future Type /// @@ -108,10 +105,10 @@ where /// Future for the [`rollback`](Self::rollback) method. type RollbackFuture: Future<Output = Result<(), Self::Error>>; - /// External Receiver Future Type + /// Receiver Future Type /// - /// Future for the [`external_receiver`](Self::external_receiver) method. - type ExternalReceiverFuture: Future<Output = ExternalReceiverResult<D, C, Self>>; + /// Future for the [`receiver`](Self::receiver) method. + type ReceiverFuture: Future<Output = ReceiverResult<H, C, Self>>; /// Error Type type Error; @@ -126,7 +123,7 @@ where updates: I, ) -> Self::SyncFuture where - I: IntoIterator<Item = (Utxo<C>, EncryptedAsset<C>)>; + I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>; /// Signs a `transaction` and returns the ledger transfer posts if successful. /// @@ -151,14 +148,14 @@ where /// See the [`Rollback`] trait for expectations on the behavior of [`rollback`](Self::rollback). fn rollback(&mut self) -> Self::RollbackFuture; - /// Generates a new [`ShieldedIdentity`] for `self` to receive assets. + /// Returns a [`ReceivingKey`] for `self` to receive assets. /// /// # Note /// /// This method does not interact with the other methods on [`Connection`] so it can be called /// at any point in between calls to [`sync`](Self::sync), [`sign`](Self::sign), and other /// rollback related methods. - fn external_receiver(&mut self) -> Self::ExternalReceiverFuture; + fn receiver(&mut self) -> Self::ReceiverFuture; } /// Synchronization State @@ -177,19 +174,18 @@ pub enum SyncState { /// Synchronization Result /// /// See the [`sync`](Connection::sync) method on [`Connection`] for more information. -pub type SyncResult<D, C, S> = Result<SyncResponse, Error<D, C, <S as Connection<D, C>>::Error>>; +pub type SyncResult<H, C, S> = Result<SyncResponse, Error<H, C, <S as Connection<H, C>>::Error>>; /// Signing Result /// /// See the [`sign`](Connection::sign) method on [`Connection`] for more information. -pub type SignResult<D, C, S> = Result<SignResponse<C>, Error<D, C, <S as Connection<D, C>>::Error>>; +pub type SignResult<H, C, S> = Result<SignResponse<C>, Error<H, C, <S as Connection<H, C>>::Error>>; -/// External Receiver Generation Result +/// Receiver Result /// -/// See the [`external_receiver`](Connection::external_receiver) method on [`Connection`] for more -/// information. -pub type ExternalReceiverResult<D, C, S> = - Result<ShieldedIdentity<C>, Error<D, C, <S as Connection<D, C>>::Error>>; +/// See the [`receiver`](Connection::receiver) method on [`Connection`] for more information. +pub type ReceiverResult<H, C, S> = + Result<ReceivingKey<C>, Error<H, C, <S as Connection<H, C>>::Error>>; /// Signer Synchronization Response /// @@ -232,16 +228,13 @@ where } /// Signer Error -pub enum Error<D, C, CE = Infallible> +pub enum Error<H, C, CE = Infallible> where - D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, + H: HierarchicalKeyTable<SecretKey = SecretKey<C>>, + C: transfer::Configuration, { - /// Secret Key Generation Error - SecretKeyError(D::Error), - - /// Encryption Error - EncryptionError(IntegratedEncryptionSchemeError<C>), + /// Hierarchical Key Table Error + HierarchicalKeyTableError(H::Error), /// Missing [`Utxo`] Membership Proof MissingUtxoMembershipProof, @@ -259,588 +252,245 @@ where ConnectionError(CE), } -impl<D, C, CE> From<InternalIdentityError<D, C>> for Error<D, C, CE> +/// Signer Configuration +pub trait Configuration: transfer::Configuration { + /// Hierarchical Key Table + type HierarchicalKeyTable: HierarchicalKeyTable<SecretKey = SecretKey<Self>>; + + /// [`Utxo`] Accumulator Type + type UtxoSet: Accumulator< + Item = <Self::UtxoSetVerifier as Verifier>::Item, + Verifier = Self::UtxoSetVerifier, + > + ConstantCapacityAccumulator + + ExactSizeAccumulator + + OptimizedAccumulator + + Rollback; + + /// Asset Map Type + type AssetMap: AssetMap<Key = PublicKey<Self>>; + + /// Random Number Generator Type + type Rng: CryptoRng + RngCore; +} + +/// Pending Asset Map +#[derive(derivative::Derivative)] +#[derivative(Default(bound = ""))] +struct PendingAssetMap<C> where - D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, + C: Configuration, +{ + /// Pending Insert Data + insert: Option<(PublicKey<C>, Asset)>, + + /// Pending Insert Zeroes Data + insert_zeroes: Option<(AssetId, Vec<PublicKey<C>>)>, + + /// Pending Remove Data + remove: Vec<PublicKey<C>>, +} + +impl<C> PendingAssetMap<C> +where + C: Configuration, { + /// Commits the pending asset map data to `assets`. #[inline] - fn from(err: InternalIdentityError<D, C>) -> Self { - match err { - InternalIdentityError::SecretKeyError(err) => Self::SecretKeyError(err), - InternalIdentityError::EncryptionError(err) => Self::EncryptionError(err), + fn commit<M>(&mut self, assets: &mut M) + where + M: AssetMap<Key = PublicKey<C>>, + { + if let Some((key, asset)) = self.insert.take() { + assets.insert(key, asset); + } + if let Some((asset_id, zeroes)) = self.insert_zeroes.take() { + assets.insert_zeroes(asset_id, zeroes); } + assets.remove_all(mem::take(&mut self.remove)); + } + + /// Clears the pending asset map. + #[inline] + fn rollback(&mut self) { + *self = Default::default(); } } /// Signer -pub struct Signer<D> +pub struct Signer<C> where - D: DerivedSecretKeyGenerator, + C: Configuration, { - /// Secret Key Source - secret_key_source: D, + /// Account Keys + account: Account<C::HierarchicalKeyTable>, + + /// Commitment Scheme + commitment_scheme: C::CommitmentScheme, + + /// Proving Context + proving_context: ProvingContext<C>, + + /// UTXO Set + utxo_set: C::UtxoSet, + + /// Asset Distribution + assets: C::AssetMap, - /// Signer Account - account: Account<D>, + /// Pending Asset Distribution + pending_assets: PendingAssetMap<C>, + + /// Random Number Generator + rng: C::Rng, } -impl<D> Signer<D> +impl<C> Signer<C> where - D: DerivedSecretKeyGenerator, + C: Configuration, { - /// Builds a new [`Signer`] for `account` from a `secret_key_source`. - #[inline] - pub fn new(secret_key_source: D, account: D::Account) -> Self { - Self::with_account(secret_key_source, Account::new(account)) - } - - /// Builds a new [`Signer`] for `account` from a `secret_key_source`. + /// #[inline] - pub fn with_account(secret_key_source: D, account: Account<D>) -> Self { + fn new_inner( + account: Account<C::HierarchicalKeyTable>, + commitment_scheme: C::CommitmentScheme, + proving_context: ProvingContext<C>, + utxo_set: C::UtxoSet, + assets: C::AssetMap, + pending_assets: PendingAssetMap<C>, + rng: C::Rng, + ) -> Self { Self { - secret_key_source, account, + commitment_scheme, + proving_context, + utxo_set, + assets, + pending_assets, + rng, } } - /// Builds a new [`Signer`] for `account` from a `secret_key_source` with starting ranges - /// `external_indices` and `internal_indices`. - #[inline] - pub fn with_ranges( - secret_key_source: D, - account: D::Account, - external_indices: Range<D::Index>, - internal_indices: Range<D::Index>, - ) -> Self { - Self::with_account( - secret_key_source, - Account::with_ranges(account, external_indices, internal_indices), - ) - } - - /// Returns the next [`Signer`] after `self`, incrementing the account number. - #[inline] - pub fn next(self) -> Self { - Self::with_account(self.secret_key_source, self.account.next()) - } - - /// Returns the identity for a key of the given `index`. + /// #[inline] - pub fn get<C, K>(&self, index: &Index<D, K>) -> Result<Identity<C>, D::Error> + pub fn new( + account: Account<C::HierarchicalKeyTable>, + commitment_scheme: C::CommitmentScheme, + proving_context: ProvingContext<C>, + rng: C::Rng, + ) -> Self where - C: identity::Configuration<SecretKey = D::SecretKey>, - K: Clone + Into<KeyKind>, + C::UtxoSet: Default, { - self.secret_key_source - .generate_key( - index.kind.clone().into(), - self.account.as_ref(), - &index.index, - ) - .map(Identity::new) + Self::new_inner( + account, + commitment_scheme, + proving_context, + Default::default(), + Default::default(), + Default::default(), + rng, + ) } - /// Returns a [`PreSender`] for the key at the given `index`. + /// Updates the internal ledger state, returning the new asset distribution. #[inline] - pub fn get_pre_sender<C>( - &self, - index: Index<D>, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - ) -> Result<PreSender<C>, D::Error> + fn sync_inner<I>(&mut self, updates: I) -> SyncResult<C::HierarchicalKeyTable, C, Self> where - C: transfer::Configuration<SecretKey = D::SecretKey>, + I: Iterator<Item = (Utxo<C>, EncryptedNote<C>)>, { - Ok(self.get(&index)?.into_pre_sender(commitment_scheme, asset)) + /* TODO: + let mut assets = Vec::new(); + for (utxo, encrypted_asset) in updates { + if let Some(KeyOwned { inner, index }) = + self.signer.find_external_asset::<C>(&encrypted_asset) + { + // FIXME: We need to actually check at this point whether the `utxo` is valid, by + // computing the UTXO that lives at the `Sender` of `index`, this way, a + // future call to `try_upgrade` will never fail. If the call to `try_upgrade` + // fails, we need to mark the coin as burnt, or it will show up again in + // later calls to the signer (during coin selection). Currently, if the + // `utxo` does not match it should be stored in the verified set as + // non-provable and the asset should not be added to the asset map, since the + // asset is effectively burnt. + assets.push(inner); + self.assets.insert(index.reduce(), inner); + self.utxo_set.insert(&utxo); + } else { + self.utxo_set.insert_nonprovable(&utxo); + } + } + Ok(SyncResponse::new(assets)) + */ + todo!() } - /// Generates the next external identity for this signer. + /// Updates the internal ledger state, returning the new asset distribution. #[inline] - fn next_external_identity<C>(&mut self) -> Result<Identity<C>, D::Error> + pub fn sync<I>( + &mut self, + sync_state: SyncState, + starting_index: usize, + updates: I, + ) -> SyncResult<C::HierarchicalKeyTable, C, Self> where - C: identity::Configuration<SecretKey = D::SecretKey>, + I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, { - Ok(self - .account - .next_external_key(&self.secret_key_source)? - .map(Identity::new) - .unwrap()) + /* TODO: + self.start_sync(sync_state); + + // FIXME: Do a capacity check on the current UTXO set. + match self.utxo_set.len().checked_sub(starting_index) { + Some(diff) => self.sync_inner(updates.into_iter().skip(diff)), + _ => Err(Error::InconsistentSynchronization), + } + */ + todo!() } - /// Generates the next internal identity for this signer. + /// Selects the pre-senders which collectively own at least `asset`, returning any change. #[inline] - fn next_internal_identity<C>(&mut self) -> Result<InternalKeyOwned<D, Identity<C>>, D::Error> - where - C: identity::Configuration<SecretKey = D::SecretKey>, - { - Ok(self - .account - .next_internal_key(&self.secret_key_source)? - .map(Identity::new)) + fn select(&mut self, asset: Asset) -> Result<Selection<C>, Error<C::HierarchicalKeyTable, C>> { + /* TODO: + let selection = self.assets.select(asset); + if selection.is_empty() { + return Err(Error::InsufficientBalance(asset)); + } + self.pending_assets.remove = selection.keys().cloned().collect(); + let pre_senders = selection + .values + .into_iter() + .map(move |(k, v)| self.get_pre_sender(k, asset.id.with(v))) + .collect::<Result<_, _>>()?; + Ok(Selection::new(selection.change, pre_senders)) + */ + todo!() } - /// Generates a new [`ShieldedIdentity`] to receive assets to this account via an external - /// transaction. + /// Builds a [`TransferPost`] for the given `transfer`. #[inline] - pub fn next_shielded<C>( + fn build_post< + const SOURCES: usize, + const SENDERS: usize, + const RECEIVERS: usize, + const SINKS: usize, + >( &mut self, - commitment_scheme: &C::CommitmentScheme, - ) -> Result<ShieldedIdentity<C>, D::Error> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - { - Ok(self - .next_external_identity()? - .into_shielded(commitment_scheme)) + transfer: impl Into<Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>>, + ) -> Result<TransferPost<C>, Error<C::HierarchicalKeyTable, C>> { + transfer + .into() + .into_post( + &self.commitment_scheme, + self.utxo_set.verifier(), + &self.proving_context, + &mut self.rng, + ) + .map_err(Error::ProofSystemError) } - /// Generates a new [`InternalIdentity`] to receive assets in this account via an internal - /// transaction. + /* TODO: + /// Accumulate transfers using the `SENDERS -> RECEIVERS` shape. #[inline] - pub fn next_internal<C, R>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - rng: &mut R, - ) -> Result<InternalKeyOwned<D, InternalIdentity<C>>, InternalIdentityError<D, C>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - R: CryptoRng + RngCore + ?Sized, - { - self.next_internal_identity() - .map_err(InternalIdentityError::SecretKeyError)? - .map_ok(move |identity| { - identity - .into_internal(commitment_scheme, asset, rng) - .map_err(InternalIdentityError::EncryptionError) - }) - } - - /// Builds the next transfer accumulator. - #[inline] - pub fn next_accumulator<C, R, const RECEIVERS: usize>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - asset_id: AssetId, - sender_sum: AssetValue, - rng: &mut R, - ) -> Result<TransferAccumulator<D, C, RECEIVERS>, InternalIdentityError<D, C>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - R: CryptoRng + RngCore + ?Sized, - { - let mut receivers = Vec::with_capacity(RECEIVERS); - let mut zero_pre_senders = Vec::with_capacity(RECEIVERS - 1); - - for _ in 0..RECEIVERS - 1 { - let (internal, index) = self - .next_internal(commitment_scheme, Asset::zero(asset_id), rng)? - .into(); - receivers.push(internal.receiver); - zero_pre_senders.push(KeyOwned::new(internal.pre_sender, index)); - } - - let internal = self - .next_internal(commitment_scheme, asset_id.with(sender_sum), rng)? - .unwrap(); - - receivers.push(internal.receiver); - - Ok(TransferAccumulator::new( - into_array_unchecked(receivers), - zero_pre_senders, - internal.pre_sender, - )) - } - - /// Builds the change receiver for the end of a transaction. - #[inline] - pub fn next_change<C, R>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - rng: &mut R, - ) -> Result<InternalKeyOwned<D, Receiver<C>>, InternalIdentityError<D, C>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - R: CryptoRng + RngCore + ?Sized, - { - self.next_internal_identity() - .map_err(InternalIdentityError::SecretKeyError)? - .map_ok(move |identity| identity.into_receiver(commitment_scheme, asset, rng)) - .map_err(InternalIdentityError::EncryptionError) - } - - /// Builds a [`Mint`] transaction to mint `asset` and returns the index for that asset. - #[inline] - pub fn mint<C, R>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - rng: &mut R, - ) -> Result<InternalKeyOwned<D, Mint<C>>, InternalIdentityError<D, C>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - R: CryptoRng + RngCore + ?Sized, - { - self.next_internal_identity() - .map_err(InternalIdentityError::SecretKeyError)? - .map_ok(|identity| { - Mint::from_identity(identity, commitment_scheme, asset, rng) - .map_err(InternalIdentityError::EncryptionError) - }) - } - - /// Builds a [`Mint`] transaction to mint a zero asset with the given `asset_id`, returning a - /// [`PreSender`] for that asset. - #[inline] - pub fn mint_zero<C, R>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - asset_id: AssetId, - rng: &mut R, - ) -> Result<(Mint<C>, PreSender<C>), InternalIdentityError<D, C>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - R: CryptoRng + RngCore + ?Sized, - { - Mint::zero( - self.next_internal_identity() - .map_err(InternalIdentityError::SecretKeyError)? - .unwrap(), - commitment_scheme, - asset_id, - rng, - ) - .map_err(InternalIdentityError::EncryptionError) - } - - /// Tries to decrypt `encrypted_asset` using the `secret_key`. - #[inline] - fn try_open_asset<C>( - secret_key: Result<ExternalSecretKey<D>, D::Error>, - encrypted_asset: &EncryptedAsset<C>, - ) -> Option<ExternalKeyOwned<D, Asset>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - { - let KeyOwned { inner, index } = secret_key.ok()?; - Some( - index.wrap( - Identity::<C>::new(inner) - .try_open(encrypted_asset) - .ok()? - .into_asset(), - ), - ) - } - - /// Looks for an index that can decrypt the given `encrypted_asset`. - #[inline] - pub fn find_external_asset<C>( - &mut self, - encrypted_asset: &EncryptedAsset<C>, - ) -> Option<ExternalKeyOwned<D, Asset>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - { - let asset = self - .account - .external_keys(&self.secret_key_source) - .find_map(move |k| Self::try_open_asset::<C>(k, encrypted_asset))?; - self.account - .conditional_increment_external_range(&asset.index.index); - Some(asset) - } -} - -impl<D> Load for Signer<D> -where - D: DerivedSecretKeyGenerator + LoadWith<Account<D>>, -{ - type Path = D::Path; - - type LoadingKey = D::LoadingKey; - - type Error = <D as Load>::Error; - - #[inline] - fn load<P>(path: P, loading_key: &Self::LoadingKey) -> Result<Self, Self::Error> - where - P: AsRef<Self::Path>, - { - let (secret_key_source, account) = D::load_with(path, loading_key)?; - Ok(Self::with_account(secret_key_source, account)) - } -} - -impl<D> Save for Signer<D> -where - D: DerivedSecretKeyGenerator + SaveWith<Account<D>>, -{ - type Path = D::Path; - - type SavingKey = D::SavingKey; - - type Error = <D as Save>::Error; - - #[inline] - fn save<P>(self, path: P, saving_key: &Self::SavingKey) -> Result<(), Self::Error> - where - P: AsRef<Self::Path>, - { - self.secret_key_source - .save_with(self.account, path, saving_key) - } -} - -/// Pending Asset Map -#[derive(derivative::Derivative)] -#[derivative(Default(bound = ""))] -struct PendingAssetMap<D> -where - D: DerivedSecretKeyGenerator, -{ - /// Pending Insert Data - insert: Option<(InternalIndex<D>, Asset)>, - - /// Pending Insert Zeroes Data - insert_zeroes: Option<(AssetId, Vec<InternalIndex<D>>)>, - - /// Pending Remove Data - remove: Vec<Index<D>>, -} - -impl<D> PendingAssetMap<D> -where - D: DerivedSecretKeyGenerator, -{ - /// Commits the pending asset map data to `assets`. - #[inline] - fn commit<M>(&mut self, assets: &mut M) - where - M: AssetMap<Key = Index<D>> + ?Sized, - { - if let Some((key, asset)) = self.insert.take() { - assets.insert(key.reduce(), asset); - } - if let Some((asset_id, zeroes)) = self.insert_zeroes.take() { - assets.insert_zeroes(asset_id, zeroes.into_iter().map(Index::reduce)); - } - assets.remove_all(mem::take(&mut self.remove)); - } - - /// Clears the pending asset map. - #[inline] - fn rollback(&mut self) { - *self = Default::default(); - } -} - -/// Signer Configuration -pub trait Configuration: transfer::Configuration { - /// Derived Secret Key Generator Type - type DerivedSecretKeyGenerator: DerivedSecretKeyGenerator<SecretKey = Self::SecretKey>; - - /// [`Utxo`] Accumulator Type - type UtxoSet: Accumulator< - Item = <Self::UtxoSetVerifier as Verifier>::Item, - Verifier = Self::UtxoSetVerifier, - > + ConstantCapacityAccumulator - + ExactSizeAccumulator - + OptimizedAccumulator - + Rollback; - - /// Asset Map Type - type AssetMap: AssetMap<Key = Index<Self::DerivedSecretKeyGenerator>>; - - /// Random Number Generator Type - type EntropySource: CryptoRng + RngCore; -} - -/// Full Signer -pub struct FullSigner<C> -where - C: Configuration, -{ - /// Signer - signer: Signer<C::DerivedSecretKeyGenerator>, - - /// Commitment Scheme - commitment_scheme: C::CommitmentScheme, - - /// Proving Context - proving_context: ProvingContext<C>, - - /// UTXO Set - utxo_set: C::UtxoSet, - - /// Asset Distribution - assets: C::AssetMap, - - /// Pending Asset Distribution - pending_assets: PendingAssetMap<C::DerivedSecretKeyGenerator>, - - /// Random Number Generator - rng: C::EntropySource, -} - -impl<C> FullSigner<C> -where - C: Configuration, -{ - /// Builds a new [`FullSigner`]. - #[inline] - fn new_inner( - signer: Signer<C::DerivedSecretKeyGenerator>, - commitment_scheme: C::CommitmentScheme, - proving_context: ProvingContext<C>, - utxo_set: C::UtxoSet, - assets: C::AssetMap, - pending_assets: PendingAssetMap<C::DerivedSecretKeyGenerator>, - rng: C::EntropySource, - ) -> Self { - Self { - signer, - commitment_scheme, - proving_context, - utxo_set, - assets, - pending_assets, - rng, - } - } - - /// Builds a new [`FullSigner`] from `secret_key_source`, `account`, `commitment_scheme`, - /// `proving_context`, and `rng`, using a default [`Utxo`] set and asset distribution. - #[inline] - pub fn new( - secret_key_source: C::DerivedSecretKeyGenerator, - account: <C::DerivedSecretKeyGenerator as DerivedSecretKeyGenerator>::Account, - commitment_scheme: C::CommitmentScheme, - proving_context: ProvingContext<C>, - rng: C::EntropySource, - ) -> Self - where - C::UtxoSet: Default, - { - Self::new_inner( - Signer::new(secret_key_source, account), - commitment_scheme, - proving_context, - Default::default(), - Default::default(), - Default::default(), - rng, - ) - } - - /// Updates the internal ledger state, returning the new asset distribution. - #[inline] - fn sync_inner<I>(&mut self, updates: I) -> SyncResult<C::DerivedSecretKeyGenerator, C, Self> - where - I: Iterator<Item = (Utxo<C>, EncryptedAsset<C>)>, - { - let mut assets = Vec::new(); - for (utxo, encrypted_asset) in updates { - if let Some(KeyOwned { inner, index }) = - self.signer.find_external_asset::<C>(&encrypted_asset) - { - // FIXME: We need to actually check at this point whether the `utxo` is valid, by - // computing the UTXO that lives at the `Sender` of `index`, this way, a - // future call to `try_upgrade` will never fail. If the call to `try_upgrade` - // fails, we need to mark the coin as burnt, or it will show up again in - // later calls to the signer (during coin selection). Currently, if the - // `utxo` does not match it should be stored in the verified set as - // non-provable and the asset should not be added to the asset map, since the - // asset is effectively burnt. - assets.push(inner); - self.assets.insert(index.reduce(), inner); - self.utxo_set.insert(&utxo); - } else { - self.utxo_set.insert_nonprovable(&utxo); - } - } - Ok(SyncResponse::new(assets)) - } - - /// Updates the internal ledger state, returning the new asset distribution. - #[inline] - pub fn sync<I>( - &mut self, - sync_state: SyncState, - starting_index: usize, - updates: I, - ) -> SyncResult<C::DerivedSecretKeyGenerator, C, Self> - where - I: IntoIterator<Item = (Utxo<C>, EncryptedAsset<C>)>, - { - self.start_sync(sync_state); - - // FIXME: Do a capacity check on the current UTXO set. - match self.utxo_set.len().checked_sub(starting_index) { - Some(diff) => self.sync_inner(updates.into_iter().skip(diff)), - _ => Err(Error::InconsistentSynchronization), - } - } - - /// Returns a [`PreSender`] for the key at the given `index`. - #[inline] - fn get_pre_sender( - &self, - index: Index<C::DerivedSecretKeyGenerator>, - asset: Asset, - ) -> Result<PreSender<C>, Error<C::DerivedSecretKeyGenerator, C>> { - self.signer - .get_pre_sender(index, &self.commitment_scheme, asset) - .map_err(Error::SecretKeyError) - } - - /// Selects the pre-senders which collectively own at least `asset`, returning any change. - #[inline] - fn select( - &mut self, - asset: Asset, - ) -> Result<Selection<C>, Error<C::DerivedSecretKeyGenerator, C>> { - let selection = self.assets.select(asset); - if selection.is_empty() { - return Err(Error::InsufficientBalance(asset)); - } - self.pending_assets.remove = selection.keys().cloned().collect(); - let pre_senders = selection - .values - .into_iter() - .map(move |(k, v)| self.get_pre_sender(k, asset.id.with(v))) - .collect::<Result<_, _>>()?; - Ok(Selection::new(selection.change, pre_senders)) - } - - /// Builds a [`TransferPost`] for the given `transfer`. - #[inline] - fn build_post< - const SOURCES: usize, - const SENDERS: usize, - const RECEIVERS: usize, - const SINKS: usize, - >( - &mut self, - transfer: impl Into<Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>>, - ) -> Result<TransferPost<C>, Error<C::DerivedSecretKeyGenerator, C>> { - transfer - .into() - .into_post( - &self.commitment_scheme, - self.utxo_set.verifier(), - &self.proving_context, - &mut self.rng, - ) - .map_err(Error::ProofSystemError) - } - - /// Accumulate transfers using the `SENDERS -> RECEIVERS` shape. - #[inline] - fn accumulate_transfers<const SENDERS: usize, const RECEIVERS: usize>( + fn accumulate_transfers<const SENDERS: usize, const RECEIVERS: usize>( &mut self, asset_id: AssetId, mut pre_senders: Vec<PreSender<C>>, @@ -888,263 +538,654 @@ where pre_senders = accumulators; } - self.prepare_final_pre_senders::<SENDERS>(asset_id, new_zeroes, &mut pre_senders, posts)?; + self.prepare_final_pre_senders::<SENDERS>(asset_id, new_zeroes, &mut pre_senders, posts)?; + + Ok(into_array_unchecked( + pre_senders + .into_iter() + .map(move |ps| ps.try_upgrade(&self.utxo_set)) + .collect::<Option<Vec<_>>>() + .ok_or(Error::MissingUtxoMembershipProof)?, + )) + } + + /// Prepare final pre-senders for the transaction. + #[inline] + fn prepare_final_pre_senders<const SENDERS: usize>( + &mut self, + asset_id: AssetId, + mut new_zeroes: Vec<InternalKeyOwned<C::DerivedSecretKeyGenerator, PreSender<C>>>, + pre_senders: &mut Vec<PreSender<C>>, + posts: &mut Vec<TransferPost<C>>, + ) -> Result<(), Error<C::DerivedSecretKeyGenerator, C>> { + let mut needed_zeroes = SENDERS - pre_senders.len(); + if needed_zeroes == 0 { + return Ok(()); + } + + let zeroes = self.assets.zeroes(needed_zeroes, asset_id); + needed_zeroes -= zeroes.len(); + + for zero in zeroes { + pre_senders.push(self.get_pre_sender(zero, Asset::zero(asset_id))?); + } + + if needed_zeroes == 0 { + return Ok(()); + } + + let needed_mints = needed_zeroes.saturating_sub(new_zeroes.len()); + + for _ in 0..needed_zeroes { + match new_zeroes.pop() { + Some(zero) => pre_senders.push(zero.unwrap()), + _ => break, + } + } + + self.pending_assets.insert_zeroes = Some(( + asset_id, + new_zeroes.into_iter().map(move |z| z.index).collect(), + )); + + if needed_mints == 0 { + return Ok(()); + } + + for _ in 0..needed_mints { + let (mint, pre_sender) = + self.signer + .mint_zero(&self.commitment_scheme, asset_id, &mut self.rng)?; + pre_senders.push(pre_sender); + posts.push(self.build_post(mint)?); + } + + Ok(()) + } + + /// Returns the next change receiver for `asset`. + #[inline] + fn next_change( + &mut self, + asset_id: AssetId, + change: AssetValue, + ) -> Result<Receiver<C>, Error<C::DerivedSecretKeyGenerator, C>> { + let asset = asset_id.with(change); + let (receiver, index) = self + .signer + .next_change(&self.commitment_scheme, asset, &mut self.rng)? + .into(); + self.pending_assets.insert = Some((index, asset)); + Ok(receiver) + } + + /// Prepares a given [`ShieldedIdentity`] for receiving `asset`. + #[inline] + pub fn prepare_receiver( + &mut self, + asset: Asset, + receiver: ShieldedIdentity<C>, + ) -> Result<Receiver<C>, Error<C::DerivedSecretKeyGenerator, C>> { + receiver + .into_receiver(&self.commitment_scheme, asset, &mut self.rng) + .map_err(Error::EncryptionError) + } + */ + + /// Signs a withdraw transaction without resetting on error. + #[inline] + fn sign_withdraw_inner( + &mut self, + asset: Asset, + receiver: Option<ReceivingKey<C>>, + ) -> SignResult<C::HierarchicalKeyTable, C, Self> { + /* TODO: + const SENDERS: usize = PrivateTransferShape::SENDERS; + const RECEIVERS: usize = PrivateTransferShape::RECEIVERS; + + let selection = self.select(asset)?; + + let mut posts = Vec::new(); + let senders = self.accumulate_transfers::<SENDERS, RECEIVERS>( + asset.id, + selection.pre_senders, + &mut posts, + )?; + + let change = self.next_change(asset.id, selection.change)?; + let final_post = match receiver { + Some(receiver) => { + let receiver = self.prepare_receiver(asset, receiver)?; + self.build_post(PrivateTransfer::build(senders, [change, receiver]))? + } + _ => self.build_post(Reclaim::build(senders, [change], asset))?, + }; + + posts.push(final_post); + + Ok(SignResponse::new(posts)) + */ + todo!() + } + + /// Signs a withdraw transaction, resetting the internal state on an error. + #[inline] + fn sign_withdraw( + &mut self, + asset: Asset, + receiver: Option<ReceivingKey<C>>, + ) -> SignResult<C::HierarchicalKeyTable, C, Self> { + let result = self.sign_withdraw_inner(asset, receiver); + if result.is_err() { + self.rollback(); + } + result + } + + /// Signs the `transaction`, generating transfer posts. + #[inline] + pub fn sign( + &mut self, + transaction: Transaction<C>, + ) -> SignResult<C::HierarchicalKeyTable, C, Self> { + self.commit(); + match transaction { + Transaction::Mint(asset) => { + /* TODO: + let (mint, owner) = self + .signer + .mint(&self.commitment_scheme, asset, &mut self.rng)? + .into(); + let mint_post = self.build_post(mint)?; + self.pending_assets.insert = Some((owner, asset)); + Ok(SignResponse::new(vec![mint_post])) + */ + todo!() + } + Transaction::PrivateTransfer(asset, receiver) => { + self.sign_withdraw(asset, Some(receiver)) + } + Transaction::Reclaim(asset) => self.sign_withdraw(asset, None), + } + } + + /// Commits to the state after the last call to [`sign`](Self::sign). + #[inline] + pub fn commit(&mut self) { + /* TODO: + self.signer.account.internal_range_shift_to_end(); + self.utxo_set.commit(); + self.pending_assets.commit(&mut self.assets); + */ + todo!() + } + + /// Rolls back to the state before the last call to [`sign`](Self::sign). + #[inline] + pub fn rollback(&mut self) { + /* TODO: + self.signer.account.internal_range_shift_to_start(); + self.utxo_set.rollback(); + self.pending_assets.rollback(); + */ + todo!() + } + + /// Commits or rolls back the state depending on the value of `sync_state`. + #[inline] + pub fn start_sync(&mut self, sync_state: SyncState) { + match sync_state { + SyncState::Commit => self.commit(), + SyncState::Rollback => self.rollback(), + } + } + + /// Generates a new [`ReceivingKey`] for `self` to receive assets. + #[inline] + pub fn receiver(&mut self) -> ReceiverResult<C::HierarchicalKeyTable, C, Self> { + /* TODO: + self.signer + .next_shielded(&self.commitment_scheme) + .map_err(Error::SecretKeyError) + */ + todo!() + } +} + +impl<C> Connection<C::HierarchicalKeyTable, C> for Signer<C> +where + C: Configuration, +{ + type SyncFuture = Ready<SyncResult<C::HierarchicalKeyTable, C, Self>>; + + type SignFuture = Ready<SignResult<C::HierarchicalKeyTable, C, Self>>; + + type CommitFuture = Ready<Result<(), Self::Error>>; + + type RollbackFuture = Ready<Result<(), Self::Error>>; + + type ReceiverFuture = Ready<ReceiverResult<C::HierarchicalKeyTable, C, Self>>; + + type Error = Infallible; + + #[inline] + fn sync<I>( + &mut self, + sync_state: SyncState, + starting_index: usize, + updates: I, + ) -> Self::SyncFuture + where + I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, + { + future::ready(self.sync(sync_state, starting_index, updates)) + } + + #[inline] + fn sign(&mut self, transaction: Transaction<C>) -> Self::SignFuture { + future::ready(self.sign(transaction)) + } + + #[inline] + fn commit(&mut self) -> Self::CommitFuture { + future::ready({ + self.commit(); + Ok(()) + }) + } - Ok(into_array_unchecked( - pre_senders - .into_iter() - .map(move |ps| ps.try_upgrade(&self.utxo_set)) - .collect::<Option<Vec<_>>>() - .ok_or(Error::MissingUtxoMembershipProof)?, - )) + #[inline] + fn rollback(&mut self) -> Self::RollbackFuture { + future::ready({ + self.rollback(); + Ok(()) + }) } - /// Prepare final pre-senders for the transaction. #[inline] - fn prepare_final_pre_senders<const SENDERS: usize>( - &mut self, - asset_id: AssetId, - mut new_zeroes: Vec<InternalKeyOwned<C::DerivedSecretKeyGenerator, PreSender<C>>>, - pre_senders: &mut Vec<PreSender<C>>, - posts: &mut Vec<TransferPost<C>>, - ) -> Result<(), Error<C::DerivedSecretKeyGenerator, C>> { - let mut needed_zeroes = SENDERS - pre_senders.len(); - if needed_zeroes == 0 { - return Ok(()); - } + fn receiver(&mut self) -> Self::ReceiverFuture { + future::ready(self.receiver()) + } +} - let zeroes = self.assets.zeroes(needed_zeroes, asset_id); - needed_zeroes -= zeroes.len(); +/// Pre-Sender Selection +struct Selection<C> +where + C: transfer::Configuration, +{ + /// Selection Change + pub change: AssetValue, - for zero in zeroes { - pre_senders.push(self.get_pre_sender(zero, Asset::zero(asset_id))?); - } + /// Selection Pre-Senders + pub pre_senders: Vec<PreSender<C>>, +} - if needed_zeroes == 0 { - return Ok(()); +impl<C> Selection<C> +where + C: transfer::Configuration, +{ + /// Builds a new [`Selection`] from `change` and `pre_senders`. + #[inline] + pub fn new(change: AssetValue, pre_senders: Vec<PreSender<C>>) -> Self { + Self { + change, + pre_senders, } + } +} - let needed_mints = needed_zeroes.saturating_sub(new_zeroes.len()); +/* TODO: +impl<D> Signer<D> +where + D: DerivedSecretKeyGenerator, +{ + /// Builds a new [`Signer`] for `account` from a `secret_key_source`. + #[inline] + pub fn new(secret_key_source: D, account: D::Account) -> Self { + Self::with_account(secret_key_source, Account::new(account)) + } - for _ in 0..needed_zeroes { - match new_zeroes.pop() { - Some(zero) => pre_senders.push(zero.unwrap()), - _ => break, - } + /// Builds a new [`Signer`] for `account` from a `secret_key_source`. + #[inline] + pub fn with_account(secret_key_source: D, account: Account<D>) -> Self { + Self { + secret_key_source, + account, } + } - self.pending_assets.insert_zeroes = Some(( - asset_id, - new_zeroes.into_iter().map(move |z| z.index).collect(), - )); + /// Builds a new [`Signer`] for `account` from a `secret_key_source` with starting ranges + /// `external_indices` and `internal_indices`. + #[inline] + pub fn with_ranges( + secret_key_source: D, + account: D::Account, + external_indices: Range<D::Index>, + internal_indices: Range<D::Index>, + ) -> Self { + Self::with_account( + secret_key_source, + Account::with_ranges(account, external_indices, internal_indices), + ) + } - if needed_mints == 0 { - return Ok(()); - } + /// Returns the next [`Signer`] after `self`, incrementing the account number. + #[inline] + pub fn next(self) -> Self { + Self::with_account(self.secret_key_source, self.account.next()) + } - for _ in 0..needed_mints { - let (mint, pre_sender) = - self.signer - .mint_zero(&self.commitment_scheme, asset_id, &mut self.rng)?; - pre_senders.push(pre_sender); - posts.push(self.build_post(mint)?); - } - Ok(()) + /// Returns a [`PreSender`] for the key at the given `index`. + #[inline] + pub fn get_pre_sender<C>( + &self, + index: Index<D>, + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + ) -> Result<PreSender<C>, D::Error> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + { + Ok(self.get(&index)?.into_pre_sender(commitment_scheme, asset)) } - /// Returns the next change receiver for `asset`. + /// Generates the next external identity for this signer. #[inline] - fn next_change( - &mut self, - asset_id: AssetId, - change: AssetValue, - ) -> Result<Receiver<C>, Error<C::DerivedSecretKeyGenerator, C>> { - let asset = asset_id.with(change); - let (receiver, index) = self - .signer - .next_change(&self.commitment_scheme, asset, &mut self.rng)? - .into(); - self.pending_assets.insert = Some((index, asset)); - Ok(receiver) + fn next_external_identity<C>(&mut self) -> Result<Identity<C>, D::Error> + where + C: identity::Configuration<SecretKey = D::SecretKey>, + { + Ok(self + .account + .next_external_key(&self.secret_key_source)? + .map(Identity::new) + .unwrap()) } - /// Prepares a given [`ShieldedIdentity`] for receiving `asset`. + /// Generates the next internal identity for this signer. #[inline] - pub fn prepare_receiver( + fn next_internal_identity<C>(&mut self) -> Result<InternalKeyOwned<D, Identity<C>>, D::Error> + where + C: identity::Configuration<SecretKey = D::SecretKey>, + { + Ok(self + .account + .next_internal_key(&self.secret_key_source)? + .map(Identity::new)) + } + + /// Generates a new [`ShieldedIdentity`] to receive assets to this account via an external + /// transaction. + #[inline] + pub fn next_shielded<C>( &mut self, - asset: Asset, - receiver: ShieldedIdentity<C>, - ) -> Result<Receiver<C>, Error<C::DerivedSecretKeyGenerator, C>> { - receiver - .into_receiver(&self.commitment_scheme, asset, &mut self.rng) - .map_err(Error::EncryptionError) + commitment_scheme: &C::CommitmentScheme, + ) -> Result<ShieldedIdentity<C>, D::Error> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + { + Ok(self + .next_external_identity()? + .into_shielded(commitment_scheme)) } - /// Signs a withdraw transaction without resetting on error. + /// Generates a new [`InternalIdentity`] to receive assets in this account via an internal + /// transaction. #[inline] - fn sign_withdraw_inner( + pub fn next_internal<C, R>( &mut self, + commitment_scheme: &C::CommitmentScheme, asset: Asset, - receiver: Option<ShieldedIdentity<C>>, - ) -> SignResult<C::DerivedSecretKeyGenerator, C, Self> { - const SENDERS: usize = PrivateTransferShape::SENDERS; - const RECEIVERS: usize = PrivateTransferShape::RECEIVERS; + rng: &mut R, + ) -> Result<InternalKeyOwned<D, InternalIdentity<C>>, InternalIdentityError<D, C>> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + R: CryptoRng + RngCore + ?Sized, + { + self.next_internal_identity() + .map_err(InternalIdentityError::SecretKeyError)? + .map_ok(move |identity| { + identity + .into_internal(commitment_scheme, asset, rng) + .map_err(InternalIdentityError::EncryptionError) + }) + } - let selection = self.select(asset)?; + /// Builds the next transfer accumulator. + #[inline] + pub fn next_accumulator<C, R, const RECEIVERS: usize>( + &mut self, + commitment_scheme: &C::CommitmentScheme, + asset_id: AssetId, + sender_sum: AssetValue, + rng: &mut R, + ) -> Result<TransferAccumulator<D, C, RECEIVERS>, InternalIdentityError<D, C>> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + R: CryptoRng + RngCore + ?Sized, + { + let mut receivers = Vec::with_capacity(RECEIVERS); + let mut zero_pre_senders = Vec::with_capacity(RECEIVERS - 1); - let mut posts = Vec::new(); - let senders = self.accumulate_transfers::<SENDERS, RECEIVERS>( - asset.id, - selection.pre_senders, - &mut posts, - )?; + for _ in 0..RECEIVERS - 1 { + let (internal, index) = self + .next_internal(commitment_scheme, Asset::zero(asset_id), rng)? + .into(); + receivers.push(internal.receiver); + zero_pre_senders.push(KeyOwned::new(internal.pre_sender, index)); + } - let change = self.next_change(asset.id, selection.change)?; - let final_post = match receiver { - Some(receiver) => { - let receiver = self.prepare_receiver(asset, receiver)?; - self.build_post(PrivateTransfer::build(senders, [change, receiver]))? - } - _ => self.build_post(Reclaim::build(senders, [change], asset))?, - }; + let internal = self + .next_internal(commitment_scheme, asset_id.with(sender_sum), rng)? + .unwrap(); - posts.push(final_post); + receivers.push(internal.receiver); - Ok(SignResponse::new(posts)) + Ok(TransferAccumulator::new( + into_array_unchecked(receivers), + zero_pre_senders, + internal.pre_sender, + )) } - /// Signs a withdraw transaction, resetting the internal state on an error. + /// Builds the change receiver for the end of a transaction. #[inline] - fn sign_withdraw( + pub fn next_change<C, R>( &mut self, + commitment_scheme: &C::CommitmentScheme, asset: Asset, - receiver: Option<ShieldedIdentity<C>>, - ) -> SignResult<C::DerivedSecretKeyGenerator, C, Self> { - let result = self.sign_withdraw_inner(asset, receiver); - if result.is_err() { - self.rollback(); - } - result + rng: &mut R, + ) -> Result<InternalKeyOwned<D, Receiver<C>>, InternalIdentityError<D, C>> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + R: CryptoRng + RngCore + ?Sized, + { + self.next_internal_identity() + .map_err(InternalIdentityError::SecretKeyError)? + .map_ok(move |identity| identity.into_receiver(commitment_scheme, asset, rng)) + .map_err(InternalIdentityError::EncryptionError) } - /// Signs the `transaction`, generating transfer posts. + /// Builds a [`Mint`] transaction to mint `asset` and returns the index for that asset. #[inline] - pub fn sign( + pub fn mint<C, R>( &mut self, - transaction: Transaction<C>, - ) -> SignResult<C::DerivedSecretKeyGenerator, C, Self> { - self.commit(); - match transaction { - Transaction::Mint(asset) => { - let (mint, owner) = self - .signer - .mint(&self.commitment_scheme, asset, &mut self.rng)? - .into(); - let mint_post = self.build_post(mint)?; - self.pending_assets.insert = Some((owner, asset)); - Ok(SignResponse::new(vec![mint_post])) - } - Transaction::PrivateTransfer(asset, receiver) => { - self.sign_withdraw(asset, Some(receiver)) - } - Transaction::Reclaim(asset) => self.sign_withdraw(asset, None), - } - } - - /// Commits to the state after the last call to [`sign`](Self::sign). - #[inline] - pub fn commit(&mut self) { - self.signer.account.internal_range_shift_to_end(); - self.utxo_set.commit(); - self.pending_assets.commit(&mut self.assets); + commitment_scheme: &C::CommitmentScheme, + asset: Asset, + rng: &mut R, + ) -> Result<InternalKeyOwned<D, Mint<C>>, InternalIdentityError<D, C>> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + R: CryptoRng + RngCore + ?Sized, + { + self.next_internal_identity() + .map_err(InternalIdentityError::SecretKeyError)? + .map_ok(|identity| { + Mint::from_identity(identity, commitment_scheme, asset, rng) + .map_err(InternalIdentityError::EncryptionError) + }) } - /// Rolls back to the state before the last call to [`sign`](Self::sign). + /// Builds a [`Mint`] transaction to mint a zero asset with the given `asset_id`, returning a + /// [`PreSender`] for that asset. #[inline] - pub fn rollback(&mut self) { - self.signer.account.internal_range_shift_to_start(); - self.utxo_set.rollback(); - self.pending_assets.rollback(); + pub fn mint_zero<C, R>( + &mut self, + commitment_scheme: &C::CommitmentScheme, + asset_id: AssetId, + rng: &mut R, + ) -> Result<(Mint<C>, PreSender<C>), InternalIdentityError<D, C>> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + R: CryptoRng + RngCore + ?Sized, + { + Mint::zero( + self.next_internal_identity() + .map_err(InternalIdentityError::SecretKeyError)? + .unwrap(), + commitment_scheme, + asset_id, + rng, + ) + .map_err(InternalIdentityError::EncryptionError) } - /// Commits or rolls back the state depending on the value of `sync_state`. + /// Tries to decrypt `encrypted_asset` using the `secret_key`. #[inline] - pub fn start_sync(&mut self, sync_state: SyncState) { - match sync_state { - SyncState::Commit => self.commit(), - SyncState::Rollback => self.rollback(), - } + fn try_open_asset<C>( + secret_key: Result<ExternalSecretKey<D>, D::Error>, + encrypted_asset: &EncryptedAsset<C>, + ) -> Option<ExternalKeyOwned<D, Asset>> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + { + let KeyOwned { inner, index } = secret_key.ok()?; + Some( + index.wrap( + Identity::<C>::new(inner) + .try_open(encrypted_asset) + .ok()? + .into_asset(), + ), + ) } - /// Generates a new [`ShieldedIdentity`] for `self` to receive assets. + /// Looks for an index that can decrypt the given `encrypted_asset`. #[inline] - pub fn external_receiver( + pub fn find_external_asset<C>( &mut self, - ) -> ExternalReceiverResult<C::DerivedSecretKeyGenerator, C, Self> { - self.signer - .next_shielded(&self.commitment_scheme) - .map_err(Error::SecretKeyError) + encrypted_asset: &EncryptedAsset<C>, + ) -> Option<ExternalKeyOwned<D, Asset>> + where + C: transfer::Configuration<SecretKey = D::SecretKey>, + { + let asset = self + .account + .external_keys(&self.secret_key_source) + .find_map(move |k| Self::try_open_asset::<C>(k, encrypted_asset))?; + self.account + .conditional_increment_external_range(&asset.index.index); + Some(asset) } } -impl<C> Connection<C::DerivedSecretKeyGenerator, C> for FullSigner<C> +impl<D> Load for Signer<D> where - C: Configuration, + D: DerivedSecretKeyGenerator + LoadWith<Account<D>>, { - type SyncFuture = Ready<SyncResult<C::DerivedSecretKeyGenerator, C, Self>>; + type Path = D::Path; - type SignFuture = Ready<SignResult<C::DerivedSecretKeyGenerator, C, Self>>; + type LoadingKey = D::LoadingKey; - type CommitFuture = Ready<Result<(), Self::Error>>; + type Error = <D as Load>::Error; - type RollbackFuture = Ready<Result<(), Self::Error>>; + #[inline] + fn load<P>(path: P, loading_key: &Self::LoadingKey) -> Result<Self, Self::Error> + where + P: AsRef<Self::Path>, + { + let (secret_key_source, account) = D::load_with(path, loading_key)?; + Ok(Self::with_account(secret_key_source, account)) + } +} + +impl<D> Save for Signer<D> +where + D: DerivedSecretKeyGenerator + SaveWith<Account<D>>, +{ + type Path = D::Path; - type ExternalReceiverFuture = - Ready<ExternalReceiverResult<C::DerivedSecretKeyGenerator, C, Self>>; + type SavingKey = D::SavingKey; - type Error = Infallible; + type Error = <D as Save>::Error; #[inline] - fn sync<I>( - &mut self, - sync_state: SyncState, - starting_index: usize, - updates: I, - ) -> Self::SyncFuture + fn save<P>(self, path: P, saving_key: &Self::SavingKey) -> Result<(), Self::Error> where - I: IntoIterator<Item = (Utxo<C>, EncryptedAsset<C>)>, + P: AsRef<Self::Path>, { - future::ready(self.sync(sync_state, starting_index, updates)) + self.secret_key_source + .save_with(self.account, path, saving_key) } +} - #[inline] - fn sign(&mut self, transaction: Transaction<C>) -> Self::SignFuture { - future::ready(self.sign(transaction)) - } +/// Signer Configuration +pub trait Configuration { + /// + type TransferConfiguration: transfer::Configuration; - #[inline] - fn commit(&mut self) -> Self::CommitFuture { - future::ready({ - self.commit(); - Ok(()) - }) - } + /// + type TransferProofSystemConfiguration: + transfer::ProofSystemConfiguration<Self::TransferConfiguration>; - #[inline] - fn rollback(&mut self) -> Self::RollbackFuture { - future::ready({ - self.rollback(); - Ok(()) - }) - } + /// + type AccountKeyTable: AccountKeyTable<SecretKey = SecretKey<Self::TransferConfiguration>>; - #[inline] - fn external_receiver(&mut self) -> Self::ExternalReceiverFuture { - future::ready(self.external_receiver()) - } + /// [`Utxo`] Accumulator Type + type UtxoSet: Accumulator< + Item = <Self::UtxoSetVerifier as Verifier>::Item, + Verifier = Self::UtxoSetVerifier, + > + ConstantCapacityAccumulator + + ExactSizeAccumulator + + OptimizedAccumulator + + Rollback; + + /// Asset Map Type + type AssetMap: AssetMap<Key = ??>; + + /// Random Number Generator Type + type Rng: CryptoRng + RngCore; +} + +/// Full Signer +pub struct Signer<C> +where + C: Configuration, +{ + /// Signer + signer: Signer<C::DerivedSecretKeyGenerator>, + + /// Commitment Scheme + commitment_scheme: C::CommitmentScheme, + + /// Proving Context + proving_context: ProvingContext<C>, + + /// UTXO Set + utxo_set: C::UtxoSet, + + /// Asset Distribution + assets: C::AssetMap, + + /// Pending Asset Distribution + pending_assets: PendingAssetMap<C::DerivedSecretKeyGenerator>, + + /// Random Number Generator + rng: C::Rng, } + /// Internal Identity Error /// /// This `enum` is the error state for any construction of an [`InternalIdentity`] from a derived @@ -1205,29 +1246,4 @@ where } } } - -/// Pre-Sender Selection -struct Selection<C> -where - C: transfer::Configuration, -{ - /// Selection Change - pub change: AssetValue, - - /// Selection Pre-Senders - pub pre_senders: Vec<PreSender<C>>, -} - -impl<C> Selection<C> -where - C: transfer::Configuration, -{ - /// Builds a new [`Selection`] from `change` and `pre_senders`. - #[inline] - pub fn new(change: AssetValue, pre_senders: Vec<PreSender<C>>) -> Self { - Self { - change, - pre_senders, - } - } -} +*/ diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index f1369d67d..2351d095c 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -18,10 +18,11 @@ use crate::{ asset::{Asset, AssetId, AssetValue}, - key::DerivedSecretKeyGenerator, + identity::SecretKey, + key::HierarchicalKeyTable, transfer::{ canonical::{Transaction, TransactionKind}, - Configuration, ShieldedIdentity, + Configuration, ReceivingKey, }, wallet::{ ledger::{self, Checkpoint, PullResponse, PushResponse}, @@ -156,12 +157,12 @@ where } /// Wallet -pub struct Wallet<D, C, L, S, B = BTreeMapBalanceState> +pub struct Wallet<H, C, L, S, B = BTreeMapBalanceState> where - D: DerivedSecretKeyGenerator, - C: Configuration<SecretKey = D::SecretKey>, + H: HierarchicalKeyTable<SecretKey = SecretKey<C>>, + C: Configuration, L: ledger::Connection<C>, - S: signer::Connection<D, C>, + S: signer::Connection<H, C>, B: BalanceState, { /// Ledger Connection @@ -180,15 +181,15 @@ where assets: B, /// Type Parameter Marker - __: PhantomData<(D, C)>, + __: PhantomData<(H, C)>, } -impl<D, C, L, S, B> Wallet<D, C, L, S, B> +impl<H, C, L, S, B> Wallet<H, C, L, S, B> where - D: DerivedSecretKeyGenerator, - C: Configuration<SecretKey = D::SecretKey>, + H: HierarchicalKeyTable<SecretKey = SecretKey<C>>, + C: Configuration, L: ledger::Connection<C>, - S: signer::Connection<D, C>, + S: signer::Connection<H, C>, B: BalanceState, { /// Builds a new [`Wallet`]. @@ -231,7 +232,7 @@ where /// Pulls data from the `ledger`, synchronizing the wallet and balance state. #[inline] - pub async fn sync(&mut self) -> Result<(), Error<D, C, L, S>> { + pub async fn sync(&mut self) -> Result<(), Error<H, C, L, S>> { // TODO: How to recover from an `InconsistentSynchronization` error? Need some sort of // recovery mode, like starting from the beginning of the state? let PullResponse { @@ -303,7 +304,7 @@ where /// This method returns an error in any other case. The internal state of the wallet is kept /// consistent between calls and recoverable errors are returned for the caller to handle. #[inline] - pub async fn post(&mut self, transaction: Transaction<C>) -> Result<bool, Error<D, C, L, S>> { + pub async fn post(&mut self, transaction: Transaction<C>) -> Result<bool, Error<H, C, L, S>> { self.sync().await?; let balance_update = self .check(&transaction) @@ -323,7 +324,7 @@ where Ok(true) } Ok(PushResponse { success: false, .. }) => { - // FIXME: What about the checkpoint? + // FIXME: What about the checkpoint returned in the response? self.try_rollback().await; Ok(false) } @@ -334,12 +335,10 @@ where } } - /// Returns a new shielded identity to receive external assets at this wallet. + /// #[inline] - pub async fn external_receiver( - &mut self, - ) -> Result<ShieldedIdentity<C>, signer::Error<D, C, S::Error>> { - self.signer.external_receiver().await + pub async fn receiver(&mut self) -> Result<ReceivingKey<C>, signer::Error<H, C, S::Error>> { + self.signer.receiver().await } } @@ -347,12 +346,12 @@ where /// /// This `enum` is the error state for [`Wallet`] methods. See [`sync`](Wallet::sync) and /// [`post`](Wallet::post) for more. -pub enum Error<D, C, L, S> +pub enum Error<H, C, L, S> where - D: DerivedSecretKeyGenerator, - C: Configuration<SecretKey = D::SecretKey>, + H: HierarchicalKeyTable<SecretKey = SecretKey<C>>, + C: Configuration, L: ledger::Connection<C>, - S: signer::Connection<D, C>, + S: signer::Connection<H, C>, { /// Insufficient Balance InsufficientBalance(Asset), @@ -361,31 +360,18 @@ where LedgerError(L::Error), /// Signer Error - SignerError(signer::Error<D, C, S::Error>), + SignerError(signer::Error<H, C, S::Error>), } -impl<D, C, L, S> From<signer::Error<D, C, S::Error>> for Error<D, C, L, S> +impl<H, C, L, S> From<signer::Error<H, C, S::Error>> for Error<H, C, L, S> where - D: DerivedSecretKeyGenerator, - C: Configuration<SecretKey = D::SecretKey>, + H: HierarchicalKeyTable<SecretKey = SecretKey<C>>, + C: Configuration, L: ledger::Connection<C>, - S: signer::Connection<D, C>, + S: signer::Connection<H, C>, { #[inline] - fn from(err: signer::Error<D, C, S::Error>) -> Self { + fn from(err: signer::Error<H, C, S::Error>) -> Self { Self::SignerError(err) } } - -impl<D, C, L, S> From<signer::InternalIdentityError<D, C>> for Error<D, C, L, S> -where - D: DerivedSecretKeyGenerator, - C: Configuration<SecretKey = D::SecretKey>, - L: ledger::Connection<C>, - S: signer::Connection<D, C>, -{ - #[inline] - fn from(err: signer::InternalIdentityError<D, C>) -> Self { - Self::SignerError(err.into()) - } -} diff --git a/manta-accounting/src/wallet2/ledger.rs b/manta-accounting/src/wallet2/ledger.rs deleted file mode 100644 index 30ea0d3c8..000000000 --- a/manta-accounting/src/wallet2/ledger.rs +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Ledger Source - -// TODO: Move to asynchronous streaming model so we can process some of the data as it is incoming. -// TODO: Add non-atomic transactions. See similar comment in `crate::wallet::signer`. - -use crate::{ - identity2::{Utxo, VoidNumber}, - transfer2::{Configuration, EncryptedNote, TransferPost}, -}; -use alloc::vec::Vec; -use core::future::Future; - -/// Ledger Checkpoint -pub trait Checkpoint: Default + PartialOrd { - /// Returns the number of receivers that have participated in transactions on the ledger so far. - fn receiver_index(&self) -> usize; - - /// Returns the number of senders that have participated in transactions on the ledger so far. - fn sender_index(&self) -> usize; -} - -/// Ledger Source Connection -pub trait Connection<C> -where - C: Configuration, -{ - /// Ledger State Checkpoint Type - type Checkpoint: Checkpoint; - - /// Receiver Data Iterator Type - type ReceiverData: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>; - - /// Sender Data Iterator Type - type SenderData: IntoIterator<Item = VoidNumber<C>>; - - /// Pull Future Type - /// - /// Future for the [`pull`](Self::pull) method. - type PullFuture: Future<Output = PullResult<C, Self>>; - - /// Pull All Future Type - /// - /// Future for the [`pull_all`](Self::pull_all) method. - type PullAllFuture: Future<Output = PullAllResult<C, Self>>; - - /// Push Future Type - /// - /// Future for the [`push`](Self::push) method. - type PushFuture: Future<Output = PushResult<C, Self>>; - - /// Error Type - type Error; - - /// Pulls receiver data from the ledger starting from `checkpoint`, returning the current - /// [`Checkpoint`](Self::Checkpoint). - fn pull(&self, checkpoint: &Self::Checkpoint) -> Self::PullFuture; - - /// Pulls all data from the ledger, returning the current [`Checkpoint`](Self::Checkpoint). - fn pull_all(&self) -> Self::PullAllFuture; - - /// Sends `posts` to the ledger to be appended atomically, returning `false` if the posts were - /// invalid. - fn push(&self, posts: Vec<TransferPost<C>>) -> Self::PushFuture; -} - -/// Ledger Source Pull Result -/// -/// See the [`pull`](Connection::pull) method on [`Connection`] for more information. -pub type PullResult<C, L> = Result<PullResponse<C, L>, <L as Connection<C>>::Error>; - -/// Ledger Source Pull All Result -/// -/// See the [`pull_all`](Connection::pull_all) method on [`Connection`] for more information. -pub type PullAllResult<C, L> = Result<PullAllResponse<C, L>, <L as Connection<C>>::Error>; - -/// Ledger Source Push Result -/// -/// See the [`push`](Connection::push) method on [`Connection`] for more information. -pub type PushResult<C, L> = Result<PushResponse<C, L>, <L as Connection<C>>::Error>; - -/// Ledger Source Pull Response -/// -/// This `struct` is created by the [`pull`](Connection::pull) method on [`Connection`]. -/// See its documentation for more. -pub struct PullResponse<C, L> -where - C: Configuration, - L: Connection<C> + ?Sized, -{ - /// Current Ledger Checkpoint - pub checkpoint: L::Checkpoint, - - /// Ledger Receiver Data - pub receiver_data: L::ReceiverData, -} - -/// Ledger Source Pull All Response -/// -/// This `struct` is created by the [`pull_all`](Connection::pull_all) method on [`Connection`]. -/// See its documentation for more. -pub struct PullAllResponse<C, L> -where - C: Configuration, - L: Connection<C> + ?Sized, -{ - /// Current Ledger Checkpoint - pub checkpoint: L::Checkpoint, - - /// Ledger Receiver Data - pub receiver_data: L::ReceiverData, - - /// Ledger Sender Data - pub sender_data: L::SenderData, -} - -/// Ledger Source Push Response -/// -/// This `struct` is created by the [`push`](Connection::push) method on [`Connection`]. -/// See its documentation for more. -pub struct PushResponse<C, L> -where - C: Configuration, - L: Connection<C> + ?Sized, -{ - /// Current Ledger Checkpoint - pub checkpoint: L::Checkpoint, - - /// Successful Push - pub success: bool, -} diff --git a/manta-accounting/src/wallet2/mod.rs b/manta-accounting/src/wallet2/mod.rs deleted file mode 100644 index e38a5adc6..000000000 --- a/manta-accounting/src/wallet2/mod.rs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Wallet Abstractions - -mod state; - -pub mod ledger; -pub mod signer; - -#[cfg(feature = "test")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] -pub mod test; - -pub use state::*; diff --git a/manta-accounting/src/wallet2/signer.rs b/manta-accounting/src/wallet2/signer.rs deleted file mode 100644 index 1e89958e7..000000000 --- a/manta-accounting/src/wallet2/signer.rs +++ /dev/null @@ -1,1249 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Wallet Signer - -// FIXME: Change the name of `TransferAccumulator`, its not an `Accumulator`. -// TODO: Add wallet recovery i.e. remove the assumption that a new signer represents a completely -// new derived secret key generator. -// TODO: Allow for non-atomic signing, i.e. rollback state to something in-between two calls to -// sign`. Will have to upgrade `Rollback` and `manta_crypto::merkle_tree::fork` as well. -// TODO: Add checkpointing/garbage-collection in `utxo_set` so we can remove old UTXOs once they -// are irrelevant. Once we create a sender and its transaction succeeds we can drop the UTXO. -// TODO: Should have a mode on the signer where we return a generic error which reveals no detail -// about what went wrong during signing. The kind of error returned from a signing could -// reveal information about the internal state (privacy leak, not a secrecy leak). - -use crate::{ - asset::{Asset, AssetId, AssetMap, AssetValue}, - fs::{Load, LoadWith, Save, SaveWith}, - identity2::{self, PreSender, PublicKey, SecretKey, Utxo}, - key2::{Account, HierarchicalKeyTable}, - transfer2::{ - self, - canonical::{Mint, PrivateTransfer, PrivateTransferShape, Reclaim, Transaction}, - EncryptedNote, FullReceiver, ProofSystemError, ProvingContext, Receiver, ReceivingKey, - Sender, Shape, Transfer, TransferPost, - }, -}; -use alloc::{vec, vec::Vec}; -use core::{ - convert::Infallible, - fmt::Debug, - future::{self, Future, Ready}, - hash::Hash, - mem, - ops::Range, -}; -use manta_crypto::{ - accumulator::{ - Accumulator, ConstantCapacityAccumulator, ExactSizeAccumulator, OptimizedAccumulator, - Verifier, - }, - key::KeyAgreementScheme, - rand::{CryptoRng, RngCore}, -}; -use manta_util::{fallible_array_map, into_array_unchecked, iter::IteratorExt}; - -/// Rollback Trait -pub trait Rollback { - /// Commits `self` to the current state. - /// - /// # Implementation Note - /// - /// Commiting to the current state must be idempotent. Calling [`rollback`](Self::rollback) - /// after [`commit`](Self::commit) must not change the state after the call to - /// [`commit`](Self::commit). - fn commit(&mut self); - - /// Rolls back `self` to the previous state. - /// - /// # Implementation Note - /// - /// Rolling back to the previous state must be idempotent. Calling [`commit`](Self::commit) - /// after [`rollback`](Self::rollback) must not change the state after the call to - /// [`rollback`](Self::rollback). - fn rollback(&mut self); -} - -/// Signer Connection -pub trait Connection<H, C> -where - H: HierarchicalKeyTable<SecretKey = SecretKey<C>>, - C: transfer2::Configuration, -{ - /// Sync Future Type - /// - /// Future for the [`sync`](Self::sync) method. - type SyncFuture: Future<Output = SyncResult<H, C, Self>>; - - /// Sign Future Type - /// - /// Future for the [`sign`](Self::sign) method. - type SignFuture: Future<Output = SignResult<H, C, Self>>; - - /// Sign Commit Future Type - /// - /// Future for the [`commit`](Self::commit) method. - type CommitFuture: Future<Output = Result<(), Self::Error>>; - - /// Sign Rollback Future Type - /// - /// Future for the [`rollback`](Self::rollback) method. - type RollbackFuture: Future<Output = Result<(), Self::Error>>; - - /// Receiver Future Type - /// - /// Future for the [`receiver`](Self::receiver) method. - type ReceiverFuture: Future<Output = ReceiverResult<H, C, Self>>; - - /// Error Type - type Error; - - /// Pushes updates from the ledger to the wallet, synchronizing it with the ledger state and - /// returning an updated asset distribution. Depending on the `sync_state`, the signer will - /// either commit to the current state before synchronizing or rollback to the previous state. - fn sync<I>( - &mut self, - sync_state: SyncState, - starting_index: usize, - updates: I, - ) -> Self::SyncFuture - where - I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>; - - /// Signs a `transaction` and returns the ledger transfer posts if successful. - /// - /// # Safety - /// - /// To preserve consistency, calls to [`sign`](Self::sign) should be followed by a call to - /// either [`commit`](Self::commit), [`rollback`](Self::rollback), or [`sync`](Self::sync) with - /// the appropriate [`SyncState`]. Repeated calls to [`sign`](Self::sign) should automatically - /// commit the current state before signing. - /// - /// See the [`Rollback`] trait for expectations on the behavior of [`commit`](Self::commit) - /// and [`rollback`](Self::rollback). - fn sign(&mut self, transaction: Transaction<C>) -> Self::SignFuture; - - /// Commits to the state after the last call to [`sign`](Self::sign). - /// - /// See the [`Rollback`] trait for expectations on the behavior of [`commit`](Self::commit). - fn commit(&mut self) -> Self::CommitFuture; - - /// Rolls back to the state before the last call to [`sign`](Self::sign). - /// - /// See the [`Rollback`] trait for expectations on the behavior of [`rollback`](Self::rollback). - fn rollback(&mut self) -> Self::RollbackFuture; - - /// Returns a [`ReceivingKey`] for `self` to receive assets. - /// - /// # Note - /// - /// This method does not interact with the other methods on [`Connection`] so it can be called - /// at any point in between calls to [`sync`](Self::sync), [`sign`](Self::sign), and other - /// rollback related methods. - fn receiver(&mut self) -> Self::ReceiverFuture; -} - -/// Synchronization State -/// -/// This `enum` is used by the [`sync`](Connection::sync) method on [`Connection`]. See its -/// documentation for more. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum SyncState { - /// Should commit the current state before synchronizing - Commit, - - /// Should rollback to the previous state before synchronizing - Rollback, -} - -/// Synchronization Result -/// -/// See the [`sync`](Connection::sync) method on [`Connection`] for more information. -pub type SyncResult<H, C, S> = Result<SyncResponse, Error<H, C, <S as Connection<H, C>>::Error>>; - -/// Signing Result -/// -/// See the [`sign`](Connection::sign) method on [`Connection`] for more information. -pub type SignResult<H, C, S> = Result<SignResponse<C>, Error<H, C, <S as Connection<H, C>>::Error>>; - -/// Receiver Result -/// -/// See the [`receiver`](Connection::receiver) method on [`Connection`] for more information. -pub type ReceiverResult<H, C, S> = - Result<ReceivingKey<C>, Error<H, C, <S as Connection<H, C>>::Error>>; - -/// Signer Synchronization Response -/// -/// This `struct` is created by the [`sync`](Connection::sync) method on [`Connection`]. -/// See its documentation for more. -pub struct SyncResponse { - /// Updates to the Asset Distribution - pub assets: Vec<Asset>, -} - -impl SyncResponse { - /// Builds a new [`SyncResponse`] from `assets`. - #[inline] - pub fn new(assets: Vec<Asset>) -> Self { - Self { assets } - } -} - -/// Signer Signing Response -/// -/// This `struct` is created by the [`sign`](Connection::sign) method on [`Connection`]. -/// See its documentation for more. -pub struct SignResponse<C> -where - C: transfer2::Configuration, -{ - /// Transfer Posts - pub posts: Vec<TransferPost<C>>, -} - -impl<C> SignResponse<C> -where - C: transfer2::Configuration, -{ - /// Builds a new [`SignResponse`] from `posts`. - #[inline] - pub fn new(posts: Vec<TransferPost<C>>) -> Self { - Self { posts } - } -} - -/// Signer Error -pub enum Error<H, C, CE = Infallible> -where - H: HierarchicalKeyTable<SecretKey = SecretKey<C>>, - C: transfer2::Configuration, -{ - /// Hierarchical Key Table Error - HierarchicalKeyTableError(H::Error), - - /// Missing [`Utxo`] Membership Proof - MissingUtxoMembershipProof, - - /// Insufficient Balance - InsufficientBalance(Asset), - - /// Proof System Error - ProofSystemError(ProofSystemError<C>), - - /// Inconsistent Synchronization State - InconsistentSynchronization, - - /// Signer Connection Error - ConnectionError(CE), -} - -/// Signer Configuration -pub trait Configuration: transfer2::Configuration { - /// Hierarchical Key Table - type HierarchicalKeyTable: HierarchicalKeyTable<SecretKey = SecretKey<Self>>; - - /// [`Utxo`] Accumulator Type - type UtxoSet: Accumulator< - Item = <Self::UtxoSetVerifier as Verifier>::Item, - Verifier = Self::UtxoSetVerifier, - > + ConstantCapacityAccumulator - + ExactSizeAccumulator - + OptimizedAccumulator - + Rollback; - - /// Asset Map Type - type AssetMap: AssetMap<Key = PublicKey<Self>>; - - /// Random Number Generator Type - type Rng: CryptoRng + RngCore; -} - -/// Pending Asset Map -#[derive(derivative::Derivative)] -#[derivative(Default(bound = ""))] -struct PendingAssetMap<C> -where - C: Configuration, -{ - /// Pending Insert Data - insert: Option<(PublicKey<C>, Asset)>, - - /// Pending Insert Zeroes Data - insert_zeroes: Option<(AssetId, Vec<PublicKey<C>>)>, - - /// Pending Remove Data - remove: Vec<PublicKey<C>>, -} - -impl<C> PendingAssetMap<C> -where - C: Configuration, -{ - /// Commits the pending asset map data to `assets`. - #[inline] - fn commit<M>(&mut self, assets: &mut M) - where - M: AssetMap<Key = PublicKey<C>>, - { - if let Some((key, asset)) = self.insert.take() { - assets.insert(key, asset); - } - if let Some((asset_id, zeroes)) = self.insert_zeroes.take() { - assets.insert_zeroes(asset_id, zeroes); - } - assets.remove_all(mem::take(&mut self.remove)); - } - - /// Clears the pending asset map. - #[inline] - fn rollback(&mut self) { - *self = Default::default(); - } -} - -/// Signer -pub struct Signer<C> -where - C: Configuration, -{ - /// Account Keys - account: Account<C::HierarchicalKeyTable>, - - /// Commitment Scheme - commitment_scheme: C::CommitmentScheme, - - /// Proving Context - proving_context: ProvingContext<C>, - - /// UTXO Set - utxo_set: C::UtxoSet, - - /// Asset Distribution - assets: C::AssetMap, - - /// Pending Asset Distribution - pending_assets: PendingAssetMap<C>, - - /// Random Number Generator - rng: C::Rng, -} - -impl<C> Signer<C> -where - C: Configuration, -{ - /// - #[inline] - fn new_inner( - account: Account<C::HierarchicalKeyTable>, - commitment_scheme: C::CommitmentScheme, - proving_context: ProvingContext<C>, - utxo_set: C::UtxoSet, - assets: C::AssetMap, - pending_assets: PendingAssetMap<C>, - rng: C::Rng, - ) -> Self { - Self { - account, - commitment_scheme, - proving_context, - utxo_set, - assets, - pending_assets, - rng, - } - } - - /// - #[inline] - pub fn new( - account: Account<C::HierarchicalKeyTable>, - commitment_scheme: C::CommitmentScheme, - proving_context: ProvingContext<C>, - rng: C::Rng, - ) -> Self - where - C::UtxoSet: Default, - { - Self::new_inner( - account, - commitment_scheme, - proving_context, - Default::default(), - Default::default(), - Default::default(), - rng, - ) - } - - /// Updates the internal ledger state, returning the new asset distribution. - #[inline] - fn sync_inner<I>(&mut self, updates: I) -> SyncResult<C::HierarchicalKeyTable, C, Self> - where - I: Iterator<Item = (Utxo<C>, EncryptedNote<C>)>, - { - /* TODO: - let mut assets = Vec::new(); - for (utxo, encrypted_asset) in updates { - if let Some(KeyOwned { inner, index }) = - self.signer.find_external_asset::<C>(&encrypted_asset) - { - // FIXME: We need to actually check at this point whether the `utxo` is valid, by - // computing the UTXO that lives at the `Sender` of `index`, this way, a - // future call to `try_upgrade` will never fail. If the call to `try_upgrade` - // fails, we need to mark the coin as burnt, or it will show up again in - // later calls to the signer (during coin selection). Currently, if the - // `utxo` does not match it should be stored in the verified set as - // non-provable and the asset should not be added to the asset map, since the - // asset is effectively burnt. - assets.push(inner); - self.assets.insert(index.reduce(), inner); - self.utxo_set.insert(&utxo); - } else { - self.utxo_set.insert_nonprovable(&utxo); - } - } - Ok(SyncResponse::new(assets)) - */ - todo!() - } - - /// Updates the internal ledger state, returning the new asset distribution. - #[inline] - pub fn sync<I>( - &mut self, - sync_state: SyncState, - starting_index: usize, - updates: I, - ) -> SyncResult<C::HierarchicalKeyTable, C, Self> - where - I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, - { - /* TODO: - self.start_sync(sync_state); - - // FIXME: Do a capacity check on the current UTXO set. - match self.utxo_set.len().checked_sub(starting_index) { - Some(diff) => self.sync_inner(updates.into_iter().skip(diff)), - _ => Err(Error::InconsistentSynchronization), - } - */ - todo!() - } - - /// Selects the pre-senders which collectively own at least `asset`, returning any change. - #[inline] - fn select(&mut self, asset: Asset) -> Result<Selection<C>, Error<C::HierarchicalKeyTable, C>> { - /* TODO: - let selection = self.assets.select(asset); - if selection.is_empty() { - return Err(Error::InsufficientBalance(asset)); - } - self.pending_assets.remove = selection.keys().cloned().collect(); - let pre_senders = selection - .values - .into_iter() - .map(move |(k, v)| self.get_pre_sender(k, asset.id.with(v))) - .collect::<Result<_, _>>()?; - Ok(Selection::new(selection.change, pre_senders)) - */ - todo!() - } - - /// Builds a [`TransferPost`] for the given `transfer`. - #[inline] - fn build_post< - const SOURCES: usize, - const SENDERS: usize, - const RECEIVERS: usize, - const SINKS: usize, - >( - &mut self, - transfer: impl Into<Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>>, - ) -> Result<TransferPost<C>, Error<C::HierarchicalKeyTable, C>> { - transfer - .into() - .into_post( - &self.commitment_scheme, - self.utxo_set.verifier(), - &self.proving_context, - &mut self.rng, - ) - .map_err(Error::ProofSystemError) - } - - /* TODO: - /// Accumulate transfers using the `SENDERS -> RECEIVERS` shape. - #[inline] - fn accumulate_transfers<const SENDERS: usize, const RECEIVERS: usize>( - &mut self, - asset_id: AssetId, - mut pre_senders: Vec<PreSender<C>>, - posts: &mut Vec<TransferPost<C>>, - ) -> Result<[Sender<C>; SENDERS], Error<C::DerivedSecretKeyGenerator, C>> { - assert!( - (SENDERS > 1) && (RECEIVERS > 1), - "The transfer shape must include at least two senders and two receivers." - ); - assert!( - !pre_senders.is_empty(), - "The set of initial senders cannot be empty." - ); - - let mut new_zeroes = Vec::new(); - - while pre_senders.len() > SENDERS { - let mut accumulators = Vec::new(); - let mut iter = pre_senders.into_iter().chunk_by::<SENDERS>(); - for chunk in &mut iter { - let senders = fallible_array_map(chunk, |ps| { - ps.try_upgrade(&self.utxo_set) - .ok_or(Error::MissingUtxoMembershipProof) - })?; - - let mut accumulator = self.signer.next_accumulator::<_, _, RECEIVERS>( - &self.commitment_scheme, - asset_id, - senders.iter().map(Sender::asset_value).sum(), - &mut self.rng, - )?; - - posts.push(self.build_post(SecretTransfer::new(senders, accumulator.receivers))?); - - for zero in &accumulator.zeroes { - zero.as_ref().insert_utxo(&mut self.utxo_set); - } - accumulator.pre_sender.insert_utxo(&mut self.utxo_set); - - new_zeroes.append(&mut accumulator.zeroes); - accumulators.push(accumulator.pre_sender); - } - - accumulators.append(&mut iter.remainder()); - pre_senders = accumulators; - } - - self.prepare_final_pre_senders::<SENDERS>(asset_id, new_zeroes, &mut pre_senders, posts)?; - - Ok(into_array_unchecked( - pre_senders - .into_iter() - .map(move |ps| ps.try_upgrade(&self.utxo_set)) - .collect::<Option<Vec<_>>>() - .ok_or(Error::MissingUtxoMembershipProof)?, - )) - } - - /// Prepare final pre-senders for the transaction. - #[inline] - fn prepare_final_pre_senders<const SENDERS: usize>( - &mut self, - asset_id: AssetId, - mut new_zeroes: Vec<InternalKeyOwned<C::DerivedSecretKeyGenerator, PreSender<C>>>, - pre_senders: &mut Vec<PreSender<C>>, - posts: &mut Vec<TransferPost<C>>, - ) -> Result<(), Error<C::DerivedSecretKeyGenerator, C>> { - let mut needed_zeroes = SENDERS - pre_senders.len(); - if needed_zeroes == 0 { - return Ok(()); - } - - let zeroes = self.assets.zeroes(needed_zeroes, asset_id); - needed_zeroes -= zeroes.len(); - - for zero in zeroes { - pre_senders.push(self.get_pre_sender(zero, Asset::zero(asset_id))?); - } - - if needed_zeroes == 0 { - return Ok(()); - } - - let needed_mints = needed_zeroes.saturating_sub(new_zeroes.len()); - - for _ in 0..needed_zeroes { - match new_zeroes.pop() { - Some(zero) => pre_senders.push(zero.unwrap()), - _ => break, - } - } - - self.pending_assets.insert_zeroes = Some(( - asset_id, - new_zeroes.into_iter().map(move |z| z.index).collect(), - )); - - if needed_mints == 0 { - return Ok(()); - } - - for _ in 0..needed_mints { - let (mint, pre_sender) = - self.signer - .mint_zero(&self.commitment_scheme, asset_id, &mut self.rng)?; - pre_senders.push(pre_sender); - posts.push(self.build_post(mint)?); - } - - Ok(()) - } - - /// Returns the next change receiver for `asset`. - #[inline] - fn next_change( - &mut self, - asset_id: AssetId, - change: AssetValue, - ) -> Result<Receiver<C>, Error<C::DerivedSecretKeyGenerator, C>> { - let asset = asset_id.with(change); - let (receiver, index) = self - .signer - .next_change(&self.commitment_scheme, asset, &mut self.rng)? - .into(); - self.pending_assets.insert = Some((index, asset)); - Ok(receiver) - } - - /// Prepares a given [`ShieldedIdentity`] for receiving `asset`. - #[inline] - pub fn prepare_receiver( - &mut self, - asset: Asset, - receiver: ShieldedIdentity<C>, - ) -> Result<Receiver<C>, Error<C::DerivedSecretKeyGenerator, C>> { - receiver - .into_receiver(&self.commitment_scheme, asset, &mut self.rng) - .map_err(Error::EncryptionError) - } - */ - - /// Signs a withdraw transaction without resetting on error. - #[inline] - fn sign_withdraw_inner( - &mut self, - asset: Asset, - receiver: Option<ReceivingKey<C>>, - ) -> SignResult<C::HierarchicalKeyTable, C, Self> { - /* TODO: - const SENDERS: usize = PrivateTransferShape::SENDERS; - const RECEIVERS: usize = PrivateTransferShape::RECEIVERS; - - let selection = self.select(asset)?; - - let mut posts = Vec::new(); - let senders = self.accumulate_transfers::<SENDERS, RECEIVERS>( - asset.id, - selection.pre_senders, - &mut posts, - )?; - - let change = self.next_change(asset.id, selection.change)?; - let final_post = match receiver { - Some(receiver) => { - let receiver = self.prepare_receiver(asset, receiver)?; - self.build_post(PrivateTransfer::build(senders, [change, receiver]))? - } - _ => self.build_post(Reclaim::build(senders, [change], asset))?, - }; - - posts.push(final_post); - - Ok(SignResponse::new(posts)) - */ - todo!() - } - - /// Signs a withdraw transaction, resetting the internal state on an error. - #[inline] - fn sign_withdraw( - &mut self, - asset: Asset, - receiver: Option<ReceivingKey<C>>, - ) -> SignResult<C::HierarchicalKeyTable, C, Self> { - let result = self.sign_withdraw_inner(asset, receiver); - if result.is_err() { - self.rollback(); - } - result - } - - /// Signs the `transaction`, generating transfer posts. - #[inline] - pub fn sign( - &mut self, - transaction: Transaction<C>, - ) -> SignResult<C::HierarchicalKeyTable, C, Self> { - self.commit(); - match transaction { - Transaction::Mint(asset) => { - /* TODO: - let (mint, owner) = self - .signer - .mint(&self.commitment_scheme, asset, &mut self.rng)? - .into(); - let mint_post = self.build_post(mint)?; - self.pending_assets.insert = Some((owner, asset)); - Ok(SignResponse::new(vec![mint_post])) - */ - todo!() - } - Transaction::PrivateTransfer(asset, receiver) => { - self.sign_withdraw(asset, Some(receiver)) - } - Transaction::Reclaim(asset) => self.sign_withdraw(asset, None), - } - } - - /// Commits to the state after the last call to [`sign`](Self::sign). - #[inline] - pub fn commit(&mut self) { - /* TODO: - self.signer.account.internal_range_shift_to_end(); - self.utxo_set.commit(); - self.pending_assets.commit(&mut self.assets); - */ - todo!() - } - - /// Rolls back to the state before the last call to [`sign`](Self::sign). - #[inline] - pub fn rollback(&mut self) { - /* TODO: - self.signer.account.internal_range_shift_to_start(); - self.utxo_set.rollback(); - self.pending_assets.rollback(); - */ - todo!() - } - - /// Commits or rolls back the state depending on the value of `sync_state`. - #[inline] - pub fn start_sync(&mut self, sync_state: SyncState) { - match sync_state { - SyncState::Commit => self.commit(), - SyncState::Rollback => self.rollback(), - } - } - - /// Generates a new [`ReceivingKey`] for `self` to receive assets. - #[inline] - pub fn receiver(&mut self) -> ReceiverResult<C::HierarchicalKeyTable, C, Self> { - /* TODO: - self.signer - .next_shielded(&self.commitment_scheme) - .map_err(Error::SecretKeyError) - */ - todo!() - } -} - -impl<C> Connection<C::HierarchicalKeyTable, C> for Signer<C> -where - C: Configuration, -{ - type SyncFuture = Ready<SyncResult<C::HierarchicalKeyTable, C, Self>>; - - type SignFuture = Ready<SignResult<C::HierarchicalKeyTable, C, Self>>; - - type CommitFuture = Ready<Result<(), Self::Error>>; - - type RollbackFuture = Ready<Result<(), Self::Error>>; - - type ReceiverFuture = Ready<ReceiverResult<C::HierarchicalKeyTable, C, Self>>; - - type Error = Infallible; - - #[inline] - fn sync<I>( - &mut self, - sync_state: SyncState, - starting_index: usize, - updates: I, - ) -> Self::SyncFuture - where - I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, - { - future::ready(self.sync(sync_state, starting_index, updates)) - } - - #[inline] - fn sign(&mut self, transaction: Transaction<C>) -> Self::SignFuture { - future::ready(self.sign(transaction)) - } - - #[inline] - fn commit(&mut self) -> Self::CommitFuture { - future::ready({ - self.commit(); - Ok(()) - }) - } - - #[inline] - fn rollback(&mut self) -> Self::RollbackFuture { - future::ready({ - self.rollback(); - Ok(()) - }) - } - - #[inline] - fn receiver(&mut self) -> Self::ReceiverFuture { - future::ready(self.receiver()) - } -} - -/// Pre-Sender Selection -struct Selection<C> -where - C: transfer2::Configuration, -{ - /// Selection Change - pub change: AssetValue, - - /// Selection Pre-Senders - pub pre_senders: Vec<PreSender<C>>, -} - -impl<C> Selection<C> -where - C: transfer2::Configuration, -{ - /// Builds a new [`Selection`] from `change` and `pre_senders`. - #[inline] - pub fn new(change: AssetValue, pre_senders: Vec<PreSender<C>>) -> Self { - Self { - change, - pre_senders, - } - } -} - -/* TODO: -impl<D> Signer<D> -where - D: DerivedSecretKeyGenerator, -{ - /// Builds a new [`Signer`] for `account` from a `secret_key_source`. - #[inline] - pub fn new(secret_key_source: D, account: D::Account) -> Self { - Self::with_account(secret_key_source, Account::new(account)) - } - - /// Builds a new [`Signer`] for `account` from a `secret_key_source`. - #[inline] - pub fn with_account(secret_key_source: D, account: Account<D>) -> Self { - Self { - secret_key_source, - account, - } - } - - /// Builds a new [`Signer`] for `account` from a `secret_key_source` with starting ranges - /// `external_indices` and `internal_indices`. - #[inline] - pub fn with_ranges( - secret_key_source: D, - account: D::Account, - external_indices: Range<D::Index>, - internal_indices: Range<D::Index>, - ) -> Self { - Self::with_account( - secret_key_source, - Account::with_ranges(account, external_indices, internal_indices), - ) - } - - /// Returns the next [`Signer`] after `self`, incrementing the account number. - #[inline] - pub fn next(self) -> Self { - Self::with_account(self.secret_key_source, self.account.next()) - } - - - /// Returns a [`PreSender`] for the key at the given `index`. - #[inline] - pub fn get_pre_sender<C>( - &self, - index: Index<D>, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - ) -> Result<PreSender<C>, D::Error> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - { - Ok(self.get(&index)?.into_pre_sender(commitment_scheme, asset)) - } - - /// Generates the next external identity for this signer. - #[inline] - fn next_external_identity<C>(&mut self) -> Result<Identity<C>, D::Error> - where - C: identity::Configuration<SecretKey = D::SecretKey>, - { - Ok(self - .account - .next_external_key(&self.secret_key_source)? - .map(Identity::new) - .unwrap()) - } - - /// Generates the next internal identity for this signer. - #[inline] - fn next_internal_identity<C>(&mut self) -> Result<InternalKeyOwned<D, Identity<C>>, D::Error> - where - C: identity::Configuration<SecretKey = D::SecretKey>, - { - Ok(self - .account - .next_internal_key(&self.secret_key_source)? - .map(Identity::new)) - } - - /// Generates a new [`ShieldedIdentity`] to receive assets to this account via an external - /// transaction. - #[inline] - pub fn next_shielded<C>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - ) -> Result<ShieldedIdentity<C>, D::Error> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - { - Ok(self - .next_external_identity()? - .into_shielded(commitment_scheme)) - } - - /// Generates a new [`InternalIdentity`] to receive assets in this account via an internal - /// transaction. - #[inline] - pub fn next_internal<C, R>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - rng: &mut R, - ) -> Result<InternalKeyOwned<D, InternalIdentity<C>>, InternalIdentityError<D, C>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - R: CryptoRng + RngCore + ?Sized, - { - self.next_internal_identity() - .map_err(InternalIdentityError::SecretKeyError)? - .map_ok(move |identity| { - identity - .into_internal(commitment_scheme, asset, rng) - .map_err(InternalIdentityError::EncryptionError) - }) - } - - /// Builds the next transfer accumulator. - #[inline] - pub fn next_accumulator<C, R, const RECEIVERS: usize>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - asset_id: AssetId, - sender_sum: AssetValue, - rng: &mut R, - ) -> Result<TransferAccumulator<D, C, RECEIVERS>, InternalIdentityError<D, C>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - R: CryptoRng + RngCore + ?Sized, - { - let mut receivers = Vec::with_capacity(RECEIVERS); - let mut zero_pre_senders = Vec::with_capacity(RECEIVERS - 1); - - for _ in 0..RECEIVERS - 1 { - let (internal, index) = self - .next_internal(commitment_scheme, Asset::zero(asset_id), rng)? - .into(); - receivers.push(internal.receiver); - zero_pre_senders.push(KeyOwned::new(internal.pre_sender, index)); - } - - let internal = self - .next_internal(commitment_scheme, asset_id.with(sender_sum), rng)? - .unwrap(); - - receivers.push(internal.receiver); - - Ok(TransferAccumulator::new( - into_array_unchecked(receivers), - zero_pre_senders, - internal.pre_sender, - )) - } - - /// Builds the change receiver for the end of a transaction. - #[inline] - pub fn next_change<C, R>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - rng: &mut R, - ) -> Result<InternalKeyOwned<D, Receiver<C>>, InternalIdentityError<D, C>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - R: CryptoRng + RngCore + ?Sized, - { - self.next_internal_identity() - .map_err(InternalIdentityError::SecretKeyError)? - .map_ok(move |identity| identity.into_receiver(commitment_scheme, asset, rng)) - .map_err(InternalIdentityError::EncryptionError) - } - - /// Builds a [`Mint`] transaction to mint `asset` and returns the index for that asset. - #[inline] - pub fn mint<C, R>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - rng: &mut R, - ) -> Result<InternalKeyOwned<D, Mint<C>>, InternalIdentityError<D, C>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - R: CryptoRng + RngCore + ?Sized, - { - self.next_internal_identity() - .map_err(InternalIdentityError::SecretKeyError)? - .map_ok(|identity| { - Mint::from_identity(identity, commitment_scheme, asset, rng) - .map_err(InternalIdentityError::EncryptionError) - }) - } - - /// Builds a [`Mint`] transaction to mint a zero asset with the given `asset_id`, returning a - /// [`PreSender`] for that asset. - #[inline] - pub fn mint_zero<C, R>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - asset_id: AssetId, - rng: &mut R, - ) -> Result<(Mint<C>, PreSender<C>), InternalIdentityError<D, C>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - R: CryptoRng + RngCore + ?Sized, - { - Mint::zero( - self.next_internal_identity() - .map_err(InternalIdentityError::SecretKeyError)? - .unwrap(), - commitment_scheme, - asset_id, - rng, - ) - .map_err(InternalIdentityError::EncryptionError) - } - - /// Tries to decrypt `encrypted_asset` using the `secret_key`. - #[inline] - fn try_open_asset<C>( - secret_key: Result<ExternalSecretKey<D>, D::Error>, - encrypted_asset: &EncryptedAsset<C>, - ) -> Option<ExternalKeyOwned<D, Asset>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - { - let KeyOwned { inner, index } = secret_key.ok()?; - Some( - index.wrap( - Identity::<C>::new(inner) - .try_open(encrypted_asset) - .ok()? - .into_asset(), - ), - ) - } - - /// Looks for an index that can decrypt the given `encrypted_asset`. - #[inline] - pub fn find_external_asset<C>( - &mut self, - encrypted_asset: &EncryptedAsset<C>, - ) -> Option<ExternalKeyOwned<D, Asset>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - { - let asset = self - .account - .external_keys(&self.secret_key_source) - .find_map(move |k| Self::try_open_asset::<C>(k, encrypted_asset))?; - self.account - .conditional_increment_external_range(&asset.index.index); - Some(asset) - } -} - -impl<D> Load for Signer<D> -where - D: DerivedSecretKeyGenerator + LoadWith<Account<D>>, -{ - type Path = D::Path; - - type LoadingKey = D::LoadingKey; - - type Error = <D as Load>::Error; - - #[inline] - fn load<P>(path: P, loading_key: &Self::LoadingKey) -> Result<Self, Self::Error> - where - P: AsRef<Self::Path>, - { - let (secret_key_source, account) = D::load_with(path, loading_key)?; - Ok(Self::with_account(secret_key_source, account)) - } -} - -impl<D> Save for Signer<D> -where - D: DerivedSecretKeyGenerator + SaveWith<Account<D>>, -{ - type Path = D::Path; - - type SavingKey = D::SavingKey; - - type Error = <D as Save>::Error; - - #[inline] - fn save<P>(self, path: P, saving_key: &Self::SavingKey) -> Result<(), Self::Error> - where - P: AsRef<Self::Path>, - { - self.secret_key_source - .save_with(self.account, path, saving_key) - } -} - -/// Signer Configuration -pub trait Configuration { - /// - type TransferConfiguration: transfer::Configuration; - - /// - type TransferProofSystemConfiguration: - transfer::ProofSystemConfiguration<Self::TransferConfiguration>; - - /// - type AccountKeyTable: AccountKeyTable<SecretKey = SecretKey<Self::TransferConfiguration>>; - - /// [`Utxo`] Accumulator Type - type UtxoSet: Accumulator< - Item = <Self::UtxoSetVerifier as Verifier>::Item, - Verifier = Self::UtxoSetVerifier, - > + ConstantCapacityAccumulator - + ExactSizeAccumulator - + OptimizedAccumulator - + Rollback; - - /// Asset Map Type - type AssetMap: AssetMap<Key = ??>; - - /// Random Number Generator Type - type Rng: CryptoRng + RngCore; -} - -/// Full Signer -pub struct Signer<C> -where - C: Configuration, -{ - /// Signer - signer: Signer<C::DerivedSecretKeyGenerator>, - - /// Commitment Scheme - commitment_scheme: C::CommitmentScheme, - - /// Proving Context - proving_context: ProvingContext<C>, - - /// UTXO Set - utxo_set: C::UtxoSet, - - /// Asset Distribution - assets: C::AssetMap, - - /// Pending Asset Distribution - pending_assets: PendingAssetMap<C::DerivedSecretKeyGenerator>, - - /// Random Number Generator - rng: C::Rng, -} - - -/// Internal Identity Error -/// -/// This `enum` is the error state for any construction of an [`InternalIdentity`] from a derived -/// secret key generator. -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "D::Error: Clone, IntegratedEncryptionSchemeError<C>: Clone"), - Copy(bound = "D::Error: Copy, IntegratedEncryptionSchemeError<C>: Copy"), - Debug(bound = "D::Error: Debug, IntegratedEncryptionSchemeError<C>: Debug"), - Eq(bound = "D::Error: Eq, IntegratedEncryptionSchemeError<C>: Eq"), - Hash(bound = "D::Error: Hash, IntegratedEncryptionSchemeError<C>: Hash"), - PartialEq(bound = "D::Error: PartialEq, IntegratedEncryptionSchemeError<C>: PartialEq") -)] -pub enum InternalIdentityError<D, C> -where - D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, -{ - /// Secret Key Generator Error - SecretKeyError(D::Error), - - /// Encryption Error - EncryptionError(IntegratedEncryptionSchemeError<C>), -} - -/// Transfer Accumulator -pub struct TransferAccumulator<D, C, const RECEIVERS: usize> -where - D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, -{ - /// Receivers - pub receivers: [Receiver<C>; RECEIVERS], - - /// Zero Balance Pre-Senders - pub zeroes: Vec<InternalKeyOwned<D, PreSender<C>>>, - - /// Accumulated Balance Pre-Sender - pub pre_sender: PreSender<C>, -} - -impl<D, C, const RECEIVERS: usize> TransferAccumulator<D, C, RECEIVERS> -where - D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, -{ - /// Builds a new [`TransferAccumulator`] from `receivers`, `zeroes`, and `pre_sender`. - #[inline] - pub fn new( - receivers: [Receiver<C>; RECEIVERS], - zeroes: Vec<InternalKeyOwned<D, PreSender<C>>>, - pre_sender: PreSender<C>, - ) -> Self { - Self { - receivers, - zeroes, - pre_sender, - } - } -} -*/ diff --git a/manta-accounting/src/wallet2/state.rs b/manta-accounting/src/wallet2/state.rs deleted file mode 100644 index 14d1ac487..000000000 --- a/manta-accounting/src/wallet2/state.rs +++ /dev/null @@ -1,377 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Full Wallet Implementation - -use crate::{ - asset::{Asset, AssetId, AssetValue}, - identity2::SecretKey, - key2::HierarchicalKeyTable, - transfer2::{ - canonical::{Transaction, TransactionKind}, - Configuration, ReceivingKey, - }, - wallet2::{ - ledger::{self, Checkpoint, PullResponse, PushResponse}, - signer::{self, SignResponse, SyncState}, - }, -}; -use alloc::{ - collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}, - vec::Vec, -}; -use core::marker::PhantomData; - -#[cfg(feature = "std")] -use std::{ - collections::hash_map::{Entry as HashMapEntry, HashMap, RandomState}, - hash::BuildHasher, -}; - -/// Balance State -pub trait BalanceState { - /// Returns the current balance associated with this `id`. - fn balance(&self, id: AssetId) -> AssetValue; - - /// Returns true if `self` contains at least `asset.value` of the asset of kind `asset.id`. - #[inline] - fn contains(&self, asset: Asset) -> bool { - self.balance(asset.id) >= asset.value - } - - /// Deposits `asset` into the balance state, increasing the balance of the asset stored at - /// `asset.id` by an amount equal to `asset.value`. - fn deposit(&mut self, asset: Asset); - - /// Deposits every asset in `assets` into the balance state. - #[inline] - fn deposit_all<I>(&mut self, assets: I) - where - I: IntoIterator<Item = Asset>, - { - assets.into_iter().for_each(move |a| self.deposit(a)); - } - - /// Withdraws `asset` from the balance state without checking if it would overdraw. - /// - /// # Panics - /// - /// This method does not check if withdrawing `asset` from the balance state would cause an - /// overdraw, but if it were to overdraw, this method must panic. - fn withdraw_unchecked(&mut self, asset: Asset); -} - -/// Performs an unchecked withdraw on `balance`, panicking on overflow. -#[inline] -fn withdraw_unchecked(balance: Option<&mut AssetValue>, withdraw: AssetValue) { - let balance = balance.expect("Trying to withdraw from a zero balance."); - *balance = balance - .checked_sub(withdraw) - .expect("Overdrawn balance state."); -} - -impl BalanceState for Vec<Asset> { - #[inline] - fn balance(&self, id: AssetId) -> AssetValue { - self.iter() - .find_map(move |a| a.value_of(id)) - .unwrap_or_default() - } - - #[inline] - fn deposit(&mut self, asset: Asset) { - self.push(asset); - } - - #[inline] - fn withdraw_unchecked(&mut self, asset: Asset) { - if !asset.is_zero() { - withdraw_unchecked( - self.iter_mut().find_map(move |a| a.value_of_mut(asset.id)), - asset.value, - ); - } - } -} - -/// Adds implementation of [`BalanceState`] for a map type with the given `$entry` type. -macro_rules! impl_balance_state_map_body { - ($entry:tt) => { - #[inline] - fn balance(&self, id: AssetId) -> AssetValue { - self.get(&id).copied().unwrap_or_default() - } - - #[inline] - fn deposit(&mut self, asset: Asset) { - match self.entry(asset.id) { - $entry::Vacant(entry) => { - entry.insert(asset.value); - } - $entry::Occupied(entry) => { - *entry.into_mut() += asset.value; - } - } - } - - #[inline] - fn withdraw_unchecked(&mut self, asset: Asset) { - if !asset.is_zero() { - withdraw_unchecked(self.get_mut(&asset.id), asset.value); - } - } - }; -} - -/// B-Tree Map [`BalanceState`] Implementation -pub type BTreeMapBalanceState = BTreeMap<AssetId, AssetValue>; - -impl BalanceState for BTreeMapBalanceState { - impl_balance_state_map_body! { BTreeMapEntry } -} - -/// Hash Map [`BalanceState`] Implementation -#[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] -pub type HashMapBalanceState<S = RandomState> = HashMap<AssetId, AssetValue, S>; - -#[cfg(feature = "std")] -impl<S> BalanceState for HashMapBalanceState<S> -where - S: BuildHasher, -{ - impl_balance_state_map_body! { HashMapEntry } -} - -/// Wallet -pub struct Wallet<H, C, L, S, B = BTreeMapBalanceState> -where - H: HierarchicalKeyTable<SecretKey = SecretKey<C>>, - C: Configuration, - L: ledger::Connection<C>, - S: signer::Connection<H, C>, - B: BalanceState, -{ - /// Ledger Connection - ledger: L, - - /// Ledger Checkpoint - checkpoint: L::Checkpoint, - - /// Signer Connection - signer: S, - - /// Signer Synchronization State - sync_state: SyncState, - - /// Balance State - assets: B, - - /// Type Parameter Marker - __: PhantomData<(H, C)>, -} - -impl<H, C, L, S, B> Wallet<H, C, L, S, B> -where - H: HierarchicalKeyTable<SecretKey = SecretKey<C>>, - C: Configuration, - L: ledger::Connection<C>, - S: signer::Connection<H, C>, - B: BalanceState, -{ - /// Builds a new [`Wallet`]. - #[inline] - pub fn new( - signer: S, - sync_state: SyncState, - ledger: L, - checkpoint: L::Checkpoint, - assets: B, - ) -> Self { - Self { - signer, - sync_state, - ledger, - checkpoint, - assets, - __: PhantomData, - } - } - - /// Returns the current balance associated with this `id`. - #[inline] - pub fn balance(&self, id: AssetId) -> AssetValue { - self.assets.balance(id) - } - - /// Returns true if `self` contains at least `asset.value` of the asset of kind `asset.id`. - #[inline] - pub fn contains(&self, asset: Asset) -> bool { - self.assets.contains(asset) - } - - /// Returns the [`Checkpoint`](ledger::Connection::Checkpoint) representing the current state - /// of this wallet. - #[inline] - pub fn checkpoint(&self) -> &L::Checkpoint { - &self.checkpoint - } - - /// Pulls data from the `ledger`, synchronizing the wallet and balance state. - #[inline] - pub async fn sync(&mut self) -> Result<(), Error<H, C, L, S>> { - // TODO: How to recover from an `InconsistentSynchronization` error? Need some sort of - // recovery mode, like starting from the beginning of the state? - let PullResponse { - checkpoint, - receiver_data, - } = self - .ledger - .pull(&self.checkpoint) - .await - .map_err(Error::LedgerError)?; - self.assets.deposit_all( - self.signer - .sync( - self.sync_state, - self.checkpoint.receiver_index(), - receiver_data, - ) - .await? - .assets, - ); - self.sync_state = SyncState::Commit; - self.checkpoint = checkpoint; - Ok(()) - } - - /// Checks if `transaction` can be executed on the balance state of `self`, returning the - /// kind of update that should be performed on the balance state if the transaction is - /// successfully posted to the ledger. - /// - /// # Safety - /// - /// This method is already called by [`post`](Self::post), but can be used by custom - /// implementations to perform checks elsewhere. - #[inline] - pub fn check(&self, transaction: &Transaction<C>) -> Result<TransactionKind, Asset> { - transaction.check(move |a| self.contains(a)) - } - - /// Tries to commit to the current signer state. - #[inline] - async fn try_commit(&mut self) { - if self.signer.commit().await.is_err() { - self.sync_state = SyncState::Commit; - } - } - - /// Tries to rollback to the previous signer state. - #[inline] - async fn try_rollback(&mut self) { - if self.signer.rollback().await.is_err() { - self.sync_state = SyncState::Rollback; - } - } - - /// Posts a transaction to the ledger, returning `true` if the `transaction` was successfully - /// saved onto the ledger. This method automatically synchronizes with the ledger before - /// posting. To amortize the cost of future calls to [`post`](Self::post), the - /// [`sync`](Self::sync) method can be used to synchronize with the ledger. - /// - /// # Failure Conditions - /// - /// This method returns `false` when there were no errors in producing transfer data and - /// sending and receiving from the ledger, but instead the ledger just did not accept the - /// transaction as is. This could be caused by an external update to the ledger while the - /// signer was building the transaction that caused the wallet and the ledger to get out of - /// sync. In this case, [`post`](Self::post) can safely be called again, to retry the - /// transaction. - /// - /// This method returns an error in any other case. The internal state of the wallet is kept - /// consistent between calls and recoverable errors are returned for the caller to handle. - #[inline] - pub async fn post(&mut self, transaction: Transaction<C>) -> Result<bool, Error<H, C, L, S>> { - self.sync().await?; - let balance_update = self - .check(&transaction) - .map_err(Error::InsufficientBalance)?; - let SignResponse { posts } = self.signer.sign(transaction).await?; - match self.ledger.push(posts).await { - Ok(PushResponse { - checkpoint, - success: true, - }) => { - self.try_commit().await; - match balance_update { - TransactionKind::Deposit(asset) => self.assets.deposit(asset), - TransactionKind::Withdraw(asset) => self.assets.withdraw_unchecked(asset), - } - self.checkpoint = checkpoint; - Ok(true) - } - Ok(PushResponse { success: false, .. }) => { - // FIXME: What about the checkpoint returned in the response? - self.try_rollback().await; - Ok(false) - } - Err(err) => { - self.try_rollback().await; - Err(Error::LedgerError(err)) - } - } - } - - /// - #[inline] - pub async fn receiver(&mut self) -> Result<ReceivingKey<C>, signer::Error<H, C, S::Error>> { - self.signer.receiver().await - } -} - -/// Wallet Error -/// -/// This `enum` is the error state for [`Wallet`] methods. See [`sync`](Wallet::sync) and -/// [`post`](Wallet::post) for more. -pub enum Error<H, C, L, S> -where - H: HierarchicalKeyTable<SecretKey = SecretKey<C>>, - C: Configuration, - L: ledger::Connection<C>, - S: signer::Connection<H, C>, -{ - /// Insufficient Balance - InsufficientBalance(Asset), - - /// Ledger Error - LedgerError(L::Error), - - /// Signer Error - SignerError(signer::Error<H, C, S::Error>), -} - -impl<H, C, L, S> From<signer::Error<H, C, S::Error>> for Error<H, C, L, S> -where - H: HierarchicalKeyTable<SecretKey = SecretKey<C>>, - C: Configuration, - L: ledger::Connection<C>, - S: signer::Connection<H, C>, -{ - #[inline] - fn from(err: signer::Error<H, C, S::Error>) -> Self { - Self::SignerError(err) - } -} diff --git a/manta-accounting/src/wallet2/test.rs b/manta-accounting/src/wallet2/test.rs deleted file mode 100644 index e032a146d..000000000 --- a/manta-accounting/src/wallet2/test.rs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Testing Framework - -// TODO: Add tests for the asynchronous wallet protocol. diff --git a/manta-crypto/src/accumulator.rs b/manta-crypto/src/accumulator.rs index 490fe657a..e52c94af2 100644 --- a/manta-crypto/src/accumulator.rs +++ b/manta-crypto/src/accumulator.rs @@ -274,44 +274,36 @@ where } /// Constraint System Gadgets for Accumulators -// TODO[remove]: #[cfg(feature = "constraint")] #[cfg_attr(doc_cfg, doc(cfg(feature = "constraint")))] pub mod constraint { use super::*; - use crate::constraint::{ - reflection::{unknown, HasAllocation, HasVariable, Mode, Var}, - Allocation, AllocationMode, AllocationSystem, ConstraintSystem, Derived, Variable, - }; + use crate::constraint::{Allocation, AllocationMode, Derived, Variable, VariableSource}; use core::marker::PhantomData; /// Membership Proof Allocation Mode Entry #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] - pub struct MembershipProofModeEntry<CheckpointMode, WitnessMode> { - /// Public Checkpoint Allocation Mode - pub checkpoint: CheckpointMode, - + pub struct MembershipProofModeEntry<WitnessMode, OutputMode> { /// Secret Witness Allocation Mode pub witness: WitnessMode, + + /// Accumulated Value Allocation Mode + pub output: OutputMode, } - impl<CheckpointMode, WitnessMode> MembershipProofModeEntry<CheckpointMode, WitnessMode> { - /// Builds a new [`MembershipProofModeEntry`] from a `checkpoint` mode and a `witness` mode. + impl<WitnessMode, OutputMode> MembershipProofModeEntry<WitnessMode, OutputMode> { + /// Builds a new [`MembershipProofModeEntry`] from a witness` mode and an `output` mode. #[inline] - pub fn new(checkpoint: CheckpointMode, witness: WitnessMode) -> Self { - Self { - checkpoint, - witness, - } + pub fn new(witness: WitnessMode, output: OutputMode) -> Self { + Self { witness, output } } } - impl<CheckpointMode, WitnessMode> From<Derived> - for MembershipProofModeEntry<CheckpointMode, WitnessMode> + impl<WitnessMode, OutputMode> From<Derived> for MembershipProofModeEntry<WitnessMode, OutputMode> where - CheckpointMode: From<Derived>, WitnessMode: From<Derived>, + OutputMode: From<Derived>, { #[inline] fn from(d: Derived) -> Self { @@ -322,129 +314,49 @@ pub mod constraint { /// Membership Proof Allocation Mode #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct MembershipProofMode<CheckpointMode, WitnessMode>( - PhantomData<(CheckpointMode, WitnessMode)>, - ) - where - CheckpointMode: AllocationMode, - WitnessMode: AllocationMode; - - impl<CheckpointMode, WitnessMode> AllocationMode - for MembershipProofMode<CheckpointMode, WitnessMode> + pub struct MembershipProofMode<WitnessMode, OutputMode>(PhantomData<(WitnessMode, OutputMode)>) where - CheckpointMode: AllocationMode, WitnessMode: AllocationMode, - { - type Known = MembershipProofModeEntry<CheckpointMode::Known, WitnessMode::Known>; - type Unknown = MembershipProofModeEntry<CheckpointMode::Unknown, WitnessMode::Unknown>; - } - - /// Membership Proof Variable - pub struct MembershipProofVar<V, C> - where - V: Verifier + ?Sized, - C: HasVariable<V::Output> + HasVariable<V::Witness> + ?Sized, - { - /// Public Checkpoint Variable - checkpoint: Var<V::Output, C>, - - /// Secret Witness Variable - witness: Var<V::Witness, C>, - } + OutputMode: AllocationMode; - impl<V, C> MembershipProofVar<V, C> + impl<WitnessMode, OutputMode> AllocationMode for MembershipProofMode<WitnessMode, OutputMode> where - V: Verifier + ?Sized, - C: HasVariable<V::Output> + HasVariable<V::Witness> + ?Sized, + WitnessMode: AllocationMode, + OutputMode: AllocationMode, { - /// Builds a new [`MembershipProofVar`] from `checkpoint` and `witness` variables. - #[inline] - pub fn new(checkpoint: Var<V::Output, C>, witness: Var<V::Witness, C>) -> Self { - Self { - checkpoint, - witness, - } - } - - /// Asserts that `self` is a valid proof to the fact that `item` is stored in some known - /// accumulator. - #[inline] - pub fn assert_validity<VV>(&self, item: &VV::ItemVar, verifier: &VV, cs: &mut C) - where - C: ConstraintSystem, - VV: VerifierVariable<C, Type = V>, - { - verifier.assert_valid_membership_proof(item, &self.checkpoint, &self.witness, cs); - } + type Known = MembershipProofModeEntry<WitnessMode::Known, OutputMode::Known>; + type Unknown = MembershipProofModeEntry<WitnessMode::Unknown, OutputMode::Unknown>; } - impl<V, C> Variable<C> for MembershipProofVar<V, C> + impl<C, V> Variable<C> for MembershipProof<V> where - V: Verifier + ?Sized, - C: HasVariable<V::Output> + HasVariable<V::Witness> + ?Sized, + C: ?Sized, + V: Variable<C> + Verifier + ?Sized, + V::Type: Verifier, + V::Witness: Variable<C, Type = <V::Type as Verifier>::Witness>, + V::Output: Variable<C, Type = <V::Type as Verifier>::Output>, { - type Type = MembershipProof<V>; + type Type = MembershipProof<V::Type>; - type Mode = MembershipProofMode<Mode<V::Output, C>, Mode<V::Witness, C>>; + type Mode = MembershipProofMode< + <V::Witness as Variable<C>>::Mode, + <V::Output as Variable<C>>::Mode, + >; #[inline] fn new(cs: &mut C, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self::new( - cs.allocate_known(&this.output, mode.checkpoint), - cs.allocate_known(&this.witness, mode.witness), + this.witness.as_known(cs, mode.witness), + this.output.as_known(cs, mode.output), ), Allocation::Unknown(mode) => Self::new( - unknown::<V::Output, _>(cs, mode.checkpoint), - unknown::<V::Witness, _>(cs, mode.witness), + V::Witness::new_unknown(cs, mode.witness), + V::Output::new_unknown(cs, mode.output), ), } } } - - impl<V, C> HasAllocation<C> for MembershipProof<V> - where - V: Verifier + ?Sized, - C: HasVariable<V::Output> + HasVariable<V::Witness> + ?Sized, - { - type Variable = MembershipProofVar<V, C>; - type Mode = MembershipProofMode<Mode<V::Output, C>, Mode<V::Witness, C>>; - } - - /// Public Checkpoint Type for [`VerifierVariable`] - pub type CheckpointType<V, C> = <<V as Variable<C>>::Type as Verifier>::Output; - - /// Secret Witness Type for [`VerifierVariable`] - pub type WitnessType<V, C> = <<V as Variable<C>>::Type as Verifier>::Witness; - - /// Public Checkpoint Variable Type for [`VerifierVariable`] - pub type CheckpointVar<V, C> = Var<CheckpointType<V, C>, C>; - - /// Secret Witness Variable Type for [`VerifierVariable`] - pub type WitnessVar<V, C> = Var<WitnessType<V, C>, C>; - - /// Verified Set Variable - pub trait VerifierVariable<C>: Variable<C> - where - C: ConstraintSystem - + HasVariable<CheckpointType<Self, C>> - + HasVariable<WitnessType<Self, C>> - + ?Sized, - Self::Type: Verifier, - { - /// Item Variable - type ItemVar: Variable<C, Type = <Self::Type as Verifier>::Item>; - - /// Asserts that `checkpoint` and `witness` form a proof to the fact that `item` is stored - /// in some known accumulator. - fn assert_valid_membership_proof( - &self, - item: &Self::ItemVar, - checkpoint: &CheckpointVar<Self, C>, - witness: &WitnessVar<Self, C>, - cs: &mut C, - ); - } } /// Testing Framework diff --git a/manta-crypto/src/encryption/mod.rs b/manta-crypto/src/encryption.rs similarity index 76% rename from manta-crypto/src/encryption/mod.rs rename to manta-crypto/src/encryption.rs index 9eec2eab5..46a40a9a7 100644 --- a/manta-crypto/src/encryption/mod.rs +++ b/manta-crypto/src/encryption.rs @@ -16,12 +16,10 @@ //! Encryption Primitives -// TODO: remove -pub mod ies; - use crate::key::{KeyAgreementScheme, KeyDerivationFunction}; +use core::marker::PhantomData; -/// Symmetric-Key Encryption Scheme +/// Symmetric Key Encryption Scheme /// /// # Specification /// @@ -58,8 +56,8 @@ pub trait HybridPublicKeyEncryptionScheme: SymmetricKeyEncryptionScheme { /// Key Derivation Function Type type KeyDerivationFunction: KeyDerivationFunction< - KeyAgreementScheme = Self::KeyAgreementScheme, - Key = <Self as SymmetricKeyEncryptionScheme>::Key, + <Self::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret, + Self::Key, >; } @@ -71,6 +69,52 @@ pub type SecretKey<H> = pub type PublicKey<H> = <<H as HybridPublicKeyEncryptionScheme>::KeyAgreementScheme as KeyAgreementScheme>::PublicKey; +/// Hybrid Public Key Encryption Scheme +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, PartialOrd)] +pub struct Hybrid<K, S, F> +where + K: KeyAgreementScheme, + S: SymmetricKeyEncryptionScheme, + F: KeyDerivationFunction<K::SharedSecret, S::Key>, +{ + /// Type Parameter Marker + __: PhantomData<(K, S, F)>, +} + +impl<K, S, F> SymmetricKeyEncryptionScheme for Hybrid<K, S, F> +where + K: KeyAgreementScheme, + S: SymmetricKeyEncryptionScheme, + F: KeyDerivationFunction<K::SharedSecret, S::Key>, +{ + type Key = S::Key; + + type Plaintext = S::Plaintext; + + type Ciphertext = S::Ciphertext; + + #[inline] + fn encrypt(key: Self::Key, plaintext: Self::Plaintext) -> Self::Ciphertext { + S::encrypt(key, plaintext) + } + + #[inline] + fn decrypt(key: Self::Key, ciphertext: &Self::Ciphertext) -> Option<Self::Plaintext> { + S::decrypt(key, ciphertext) + } +} + +impl<K, S, F> HybridPublicKeyEncryptionScheme for Hybrid<K, S, F> +where + K: KeyAgreementScheme, + S: SymmetricKeyEncryptionScheme, + F: KeyDerivationFunction<K::SharedSecret, S::Key>, +{ + type KeyAgreementScheme = K; + type KeyDerivationFunction = F; +} + /// Encrypted Message pub struct EncryptedMessage<H> where diff --git a/manta-crypto/src/encryption/ies.rs b/manta-crypto/src/encryption/ies.rs deleted file mode 100644 index 1ac034318..000000000 --- a/manta-crypto/src/encryption/ies.rs +++ /dev/null @@ -1,434 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Integrated Encryption Schemes and Encrypted Messages - -// FIXME: add zeroize for secret keys - -use core::{fmt::Debug, hash::Hash}; -use rand_core::{CryptoRng, RngCore}; - -/// Integrated Encryption Scheme Trait -pub trait IntegratedEncryptionScheme { - /// Public Key Type - type PublicKey; - - /// Secret Key Type - type SecretKey; - - /// Plaintext Type - type Plaintext; - - /// Ciphertext Type - type Ciphertext; - - /// Encryption/Decryption Error Type - type Error; - - /// Generates a public/secret keypair. - fn generate_keys<R>(rng: &mut R) -> KeyPair<Self> - where - R: CryptoRng + RngCore + ?Sized; - - /// Generates a public key. - /// - /// This enables an optimization path whenever decryption is not necessary. - #[inline] - fn generate_public_key<R>(rng: &mut R) -> PublicKey<Self> - where - R: CryptoRng + RngCore + ?Sized, - { - Self::generate_keys(rng).into_public() - } - - /// Generates a secret key. - /// - /// This enables an optimization path whenever encryption is not necessary. - #[inline] - fn generate_secret_key<R>(rng: &mut R) -> SecretKey<Self> - where - R: CryptoRng + RngCore + ?Sized, - { - Self::generate_keys(rng).into_secret() - } - - /// Encrypts the `plaintext` with the `public_key`, generating an [`EncryptedMessage`]. - fn encrypt<R>( - plaintext: &Self::Plaintext, - public_key: Self::PublicKey, - rng: &mut R, - ) -> Result<EncryptedMessage<Self>, Self::Error> - where - R: CryptoRng + RngCore + ?Sized; - - /// Generates a public/secret keypair and then encrypts the `plaintext` with the generated - /// public key, returning an [`EncryptedMessage`] and a [`SecretKey`]. - #[inline] - fn generate_keys_and_encrypt<R>( - plaintext: &Self::Plaintext, - rng: &mut R, - ) -> Result<(EncryptedMessage<Self>, SecretKey<Self>), Self::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - Self::generate_keys(rng).encrypt(plaintext, rng) - } - - /// Generates a public key and then encrypts the `plaintext` with the generated public key, - /// returning an [`EncryptedMessage`]. - /// - /// This enables an optimization path whenever decryption is not necessary. - #[inline] - fn generate_public_key_and_encrypt<R>( - plaintext: &Self::Plaintext, - rng: &mut R, - ) -> Result<EncryptedMessage<Self>, Self::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - Self::generate_public_key(rng).encrypt(plaintext, rng) - } - - /// Decrypts the `ciphertext` with the `secret_key`, returning the - /// [`Plaintext`](Self::Plaintext). - fn decrypt( - ciphertext: &Self::Ciphertext, - secret_key: Self::SecretKey, - ) -> Result<Self::Plaintext, Self::Error>; - - /// Generates a secret key and then decrypts the `ciphertext` with the generated secret key, - /// returing the [`Plaintext`](Self::Plaintext). - /// - /// This enables an optimization path whenever encryption is not necessary. - #[inline] - fn generate_secret_key_and_decrypt<R>( - ciphertext: &Self::Ciphertext, - rng: &mut R, - ) -> Result<Self::Plaintext, Self::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - Self::decrypt(ciphertext, Self::generate_secret_key(rng).secret_key) - } -} - -/// [`IntegratedEncryptionScheme`] Public Key -pub struct PublicKey<I> -where - I: IntegratedEncryptionScheme + ?Sized, -{ - /// Public Key - public_key: I::PublicKey, -} - -impl<I> PublicKey<I> -where - I: IntegratedEncryptionScheme + ?Sized, -{ - /// Builds a new [`PublicKey`] from `I::PublicKey`. - #[inline] - pub fn new(public_key: I::PublicKey) -> Self { - Self { public_key } - } - - /// Generates a public key. - /// - /// This enables an optimization path whenever decryption is not necessary. - #[inline] - pub fn generate<R>(rng: &mut R) -> Self - where - R: CryptoRng + RngCore + ?Sized, - { - I::generate_public_key(rng) - } - - /// Encrypts the `plaintext` with `self`, returning an [`EncryptedMessage`]. - #[inline] - pub fn encrypt<R>( - self, - plaintext: &I::Plaintext, - rng: &mut R, - ) -> Result<EncryptedMessage<I>, I::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - I::encrypt(plaintext, self.public_key, rng) - } - - /// Generates a public key and then encrypts the `plaintext` with the generated public key, - /// returning an [`EncryptedMessage`]. - /// - /// This enables an optimization path whenever decryption is not necessary. - #[inline] - pub fn generate_and_encrypt<R>( - plaintext: &I::Plaintext, - rng: &mut R, - ) -> Result<EncryptedMessage<I>, I::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - I::generate_public_key_and_encrypt(plaintext, rng) - } -} - -/// [`IntegratedEncryptionScheme`] Secret Key -pub struct SecretKey<I> -where - I: IntegratedEncryptionScheme + ?Sized, -{ - /// Secret Key - secret_key: I::SecretKey, -} - -impl<I> SecretKey<I> -where - I: IntegratedEncryptionScheme + ?Sized, -{ - /// Builds a new [`SecretKey`] from `I::SecretKey`. - #[inline] - pub fn new(secret_key: I::SecretKey) -> Self { - Self { secret_key } - } - - /// Generates a secret key. - /// - /// This enables an optimization path whenever encryption is not necessary. - #[inline] - pub fn generate<R>(rng: &mut R) -> Self - where - R: CryptoRng + RngCore + ?Sized, - { - I::generate_secret_key(rng) - } - - /// Decrypts the `message` with `self` returning the - /// [`Plaintext`](IntegratedEncryptionScheme::Plaintext). - #[inline] - pub fn decrypt(self, message: &EncryptedMessage<I>) -> Result<I::Plaintext, I::Error> { - message.decrypt(self) - } - - /// Generates a secret key and then decrypts the `message` with the generated secret key, - /// returing the [`Plaintext`](IntegratedEncryptionScheme::Plaintext). - /// - /// This enables an optimization path whenever encryption is not necessary. - #[inline] - pub fn generate_and_decrypt<R>( - message: &EncryptedMessage<I>, - rng: &mut R, - ) -> Result<I::Plaintext, I::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - I::generate_secret_key(rng).decrypt(message) - } -} - -/// [`IntegratedEncryptionScheme`] Key Pair -pub struct KeyPair<I> -where - I: IntegratedEncryptionScheme + ?Sized, -{ - /// Public Key - public_key: I::PublicKey, - - /// Secret Key - secret_key: I::SecretKey, -} - -impl<I> KeyPair<I> -where - I: IntegratedEncryptionScheme + ?Sized, -{ - /// Builds a new [`KeyPair`] from a `public_key` and a `secret_key`. - #[inline] - pub fn new(public_key: I::PublicKey, secret_key: I::SecretKey) -> Self { - Self { - public_key, - secret_key, - } - } - - /// Generates a public/secret keypair. - #[inline] - pub fn generate<R>(rng: &mut R) -> Self - where - R: CryptoRng + RngCore + ?Sized, - { - I::generate_keys(rng) - } - - /// Returns the public side of the key pair. - #[inline] - fn into_public(self) -> PublicKey<I> { - PublicKey::new(self.public_key) - } - - /// Returns the secret side of the key pair. - #[inline] - fn into_secret(self) -> SecretKey<I> { - SecretKey::new(self.secret_key) - } - - /// Encrypts the `plaintext` with `self`, returning an [`EncryptedMessage`]. - #[inline] - pub fn encrypt<R>( - self, - plaintext: &I::Plaintext, - rng: &mut R, - ) -> Result<(EncryptedMessage<I>, SecretKey<I>), I::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - let (public_key, secret_key) = self.into(); - Ok((public_key.encrypt(plaintext, rng)?, secret_key)) - } -} - -impl<I> From<KeyPair<I>> for (PublicKey<I>, SecretKey<I>) -where - I: IntegratedEncryptionScheme + ?Sized, -{ - #[inline] - fn from(keypair: KeyPair<I>) -> Self { - ( - PublicKey::new(keypair.public_key), - SecretKey::new(keypair.secret_key), - ) - } -} - -/// Encrypted Message -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "I::Ciphertext: Clone"), - Copy(bound = "I::Ciphertext: Copy"), - Debug(bound = "I::Ciphertext: Debug"), - Default(bound = "I::Ciphertext: Default"), - Eq(bound = "I::Ciphertext: Eq"), - Hash(bound = "I::Ciphertext: Hash"), - PartialEq(bound = "I::Ciphertext: PartialEq") -)] -pub struct EncryptedMessage<I> -where - I: IntegratedEncryptionScheme + ?Sized, -{ - /// Message Ciphertext - ciphertext: I::Ciphertext, -} - -impl<I> EncryptedMessage<I> -where - I: IntegratedEncryptionScheme + ?Sized, -{ - /// Builds a new [`EncryptedMessage`] from - /// [`I::Ciphertext`](IntegratedEncryptionScheme::Ciphertext). - #[inline] - pub fn new(ciphertext: I::Ciphertext) -> Self { - Self { ciphertext } - } - - /// Encrypts the `plaintext` with the `public_key`, returning an [`EncryptedMessage`]. - #[inline] - pub fn encrypt<R>( - plaintext: &I::Plaintext, - public_key: I::PublicKey, - rng: &mut R, - ) -> Result<Self, I::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - I::encrypt(plaintext, public_key, rng) - } - - /// Generates a public/secret keypair and then encrypts the `plaintext` with the generated - /// public key, returning an [`EncryptedMessage`] and a [`SecretKey`]. - #[inline] - pub fn generate_keys_and_encrypt<R>( - plaintext: &I::Plaintext, - rng: &mut R, - ) -> Result<(Self, SecretKey<I>), I::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - I::generate_keys_and_encrypt(plaintext, rng) - } - - /// Generates a public key and then encrypts the `plaintext` with the generated public key, - /// returning an [`EncryptedMessage`]. - /// - /// This enables an optimization path whenever decryption is not necessary. - #[inline] - pub fn generate_public_key_and_encrypt<R>( - plaintext: &I::Plaintext, - rng: &mut R, - ) -> Result<Self, I::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - I::generate_public_key_and_encrypt(plaintext, rng) - } - - /// Decrypts `self` with the `secret_key` returning the - /// [`Plaintext`](IntegratedEncryptionScheme::Plaintext). - #[inline] - pub fn decrypt(&self, secret_key: SecretKey<I>) -> Result<I::Plaintext, I::Error> { - I::decrypt(&self.ciphertext, secret_key.secret_key) - } - - /// Generates a secret key and then decrypts `self` with the generated secret key, - /// returing the [`Plaintext`](IntegratedEncryptionScheme::Plaintext). - /// - /// This enables an optimization path whenever encryption is not necessary. - #[inline] - pub fn generate_secret_key_and_decrypt<R>(&self, rng: &mut R) -> Result<I::Plaintext, I::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - I::generate_secret_key_and_decrypt(&self.ciphertext, rng) - } -} - -/// Testing Framework -#[cfg(feature = "test")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] -pub mod test { - use super::*; - use core::fmt::Debug; - - /// Tests encryption/decryption of a sample `plaintext`. - #[inline] - pub fn assert_decryption_of_encryption<I, R>(plaintext: &I::Plaintext, rng: &mut R) - where - I: IntegratedEncryptionScheme, - I::Plaintext: Debug + PartialEq, - I::Error: Debug, - R: CryptoRng + RngCore + ?Sized, - { - let (public_key, secret_key) = I::generate_keys(rng).into(); - let reconstructed_plaintext = secret_key - .decrypt( - &public_key - .encrypt(plaintext, rng) - .expect("Unable to encrypt plaintext."), - ) - .expect("Unable to decrypt plaintext."); - assert_eq!( - plaintext, &reconstructed_plaintext, - "Plaintext didn't match decrypted ciphertext." - ); - } -} diff --git a/manta-crypto/src/key.rs b/manta-crypto/src/key.rs index 42a31ad6d..9195b5aad 100644 --- a/manta-crypto/src/key.rs +++ b/manta-crypto/src/key.rs @@ -14,9 +14,9 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Key Primitives +//! Cryptographic Key Primitives -/// Key-Agreement Scheme +/// Key Agreement Scheme /// /// # Specification /// @@ -50,9 +50,9 @@ pub trait KeyAgreementScheme { /// /// # Implementation Note /// - /// This method is an optimization path for [`derive`] when we own the `secret_key` value, and - /// by default, uses [`derive`] as its implementation. This method must return the same value - /// as [`derive`] on the same input. + /// This method is an optimization path for [`derive`] when the `secret_key` value is owned, + /// and by default, [`derive`] is used as its implementation. This method must return the same + /// value as [`derive`] on the same input. /// /// [`derive`]: Self::derive #[inline] @@ -62,18 +62,24 @@ pub trait KeyAgreementScheme { /// Computes the shared secret given the known `secret_key` and the given `public_key`. fn agree(secret_key: &Self::SecretKey, public_key: &Self::PublicKey) -> Self::SharedSecret; -} - -/// Key-Derivation Function -pub trait KeyDerivationFunction { - /// Key Agreement Scheme Type - type KeyAgreementScheme: KeyAgreementScheme; - /// Output Key Type - type Key; + /// Computes the shared secret given the known `secret_key` and the given `public_key`. + /// + /// # Implementation Note + /// + /// This method is an optimization path for [`agree`] when the `secret_key` value and + /// `public_key` value are owned, and by default, [`agree`] is used as its implementation. This + /// method must return the same value as [`agree`] on the same input. + /// + /// [`agree`]: Self::agree + #[inline] + fn agree_owned(secret_key: Self::SecretKey, public_key: Self::PublicKey) -> Self::SharedSecret { + Self::agree(&secret_key, &public_key) + } +} - /// Derives an output key from `shared_secret` computed from a key-agreement scheme. - fn derive( - shared_secret: <Self::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret, - ) -> Self::Key; +/// Key Derivation Function +pub trait KeyDerivationFunction<A, B> { + /// Derives an output key from `secret` computed from a key-agreement scheme. + fn derive(secret: A) -> B; } diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index 5cf16172b..6ea563c9b 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -28,7 +28,6 @@ pub mod commitment; pub mod encryption; pub mod key; pub mod merkle_tree; -pub mod prf; pub mod rand; #[cfg(feature = "constraint")] diff --git a/manta-crypto/src/prf.rs b/manta-crypto/src/prf.rs deleted file mode 100644 index 0e69de3a9..000000000 --- a/manta-crypto/src/prf.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Pseudorandom Function Families - -/// Pseudorandom Function Families (PRF) Trait -pub trait PseudorandomFunctionFamily { - /// PRF Seed Type - type Seed: ?Sized; - - /// PRF Input Type - type Input: ?Sized; - - /// PRF Output Type - type Output; - - /// Evaluates the PRF at the `seed` and `input`. - fn evaluate(seed: &Self::Seed, input: &Self::Input) -> Self::Output; -} diff --git a/manta-pay/src/accounting/config.rs b/manta-pay/src/accounting/config.rs index ae96e791d..8926f1df8 100644 --- a/manta-pay/src/accounting/config.rs +++ b/manta-pay/src/accounting/config.rs @@ -20,27 +20,24 @@ // enabled when using whichever backend. use crate::{ - accounting::identity::{constraint::UtxoSetVerifierVar, Parameters, Utxo}, + accounting::identity::{Parameters, Utxo}, crypto::{ commitment::pedersen::{self, PedersenWindow}, constraint::arkworks::{ proof_systems::groth16::Groth16, ArkConstraintSystem, AssetIdVar, AssetValueVar, }, - ies::IES, + key::EllipticCurveDiffieHellman, merkle_tree::{ constraint as merkle_tree_constraint, ConfigConverter as ArkMerkleTreeConfigConverter, Configuration as ArkMerkleTreeConfiguration, }, - prf::blake2s::{constraint::Blake2sVar, Blake2s}, }, }; use ark_bls12_381::Bls12_381; use ark_crypto_primitives::crh::pedersen::{constraints::CRHGadget, CRH}; use ark_ed_on_bls12_381::{constraints::EdwardsVar, EdwardsProjective, Fq}; -use manta_accounting::{identity, transfer}; -use manta_crypto::{ - commitment::CommitmentScheme, merkle_tree, prf::PseudorandomFunctionFamily, rand::SeedIntoRng, -}; +use manta_accounting::{asset::Asset, identity, transfer}; +use manta_crypto::{commitment::CommitmentScheme, merkle_tree, rand::SeedIntoRng}; use rand_chacha::ChaCha20Rng; /// Pedersen Window Parameters @@ -128,33 +125,29 @@ impl merkle_tree_constraint::Configuration for Configuration { } impl identity::Configuration for Configuration { - type SecretKey = <Blake2s as PseudorandomFunctionFamily>::Seed; - type PseudorandomFunctionFamilyInput = <Blake2s as PseudorandomFunctionFamily>::Input; - type PseudorandomFunctionFamily = Blake2s; - type CommitmentSchemeRandomness = <PedersenCommitment as CommitmentScheme>::Randomness; + type Asset = Asset; + type KeyAgreementScheme = EllipticCurveDiffieHellman<PedersenCommitmentProjectiveCurve>; type CommitmentScheme = PedersenCommitment; - type Rng = SeedIntoRng<Self::SecretKey, ChaCha20Rng>; } -impl identity::constraint::Configuration for Configuration { - type ConstraintSystem = ConstraintSystem; - type SecretKeyVar = <Blake2sVar<ConstraintField> as PseudorandomFunctionFamily>::Seed; - type PseudorandomFunctionFamilyInputVar = - <Blake2sVar<ConstraintField> as PseudorandomFunctionFamily>::Input; - type PseudorandomFunctionFamilyOutputVar = - <Blake2sVar<ConstraintField> as PseudorandomFunctionFamily>::Output; - type PseudorandomFunctionFamilyVar = Blake2sVar<ConstraintField>; - type CommitmentSchemeRandomnessVar = <PedersenCommitmentVar as CommitmentScheme>::Randomness; - type CommitmentSchemeOutputVar = <PedersenCommitmentVar as CommitmentScheme>::Output; - type CommitmentSchemeVar = PedersenCommitmentVar; +/* +/// Transfer Constraint Configuration Structure +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct TransferConstraintConfiguration; + +impl identity::Configuration for TransferConstraintConfiguration { + type Asset = AssetVar; + type KeyAgreementScheme = (); + type CommitmentScheme = (); } +impl transfer::ConstraintConfiguration<ConstraintSystem> for TransferConstraintConfiguration {} + impl transfer::Configuration for Configuration { + type EncryptionScheme = (); + type UtxoSetVerifier = (); type ConstraintSystem = ConstraintSystem; + type ConstraintConfiguration = TransferConstraintConfiguration; type ProofSystem = ProofSystem; - type AssetIdVar = AssetIdVar<ConstraintField>; - type AssetValueVar = AssetValueVar<ConstraintField>; - type IntegratedEncryptionScheme = IES; - type UtxoSetVerifier = Parameters; - type UtxoSetVerifierVar = UtxoSetVerifierVar; } +*/ diff --git a/manta-pay/src/accounting/identity.rs b/manta-pay/src/accounting/identity.rs index ce0adcf9a..fb01d24b7 100644 --- a/manta-pay/src/accounting/identity.rs +++ b/manta-pay/src/accounting/identity.rs @@ -20,52 +20,12 @@ use crate::{accounting::config::Configuration, crypto::merkle_tree::ConfigConver use manta_accounting::{identity, transfer}; use manta_crypto::merkle_tree::{self, full::Full}; -/// Asset Parameters -pub type AssetParameters = identity::AssetParameters<Configuration>; - /// Unspent Transaction Output pub type Utxo = identity::Utxo<Configuration>; /// Void Number pub type VoidNumber = identity::VoidNumber<Configuration>; -/// Identity Type -pub type Identity = identity::Identity<Configuration>; - -/// Sender Type -pub type Sender = - identity::Sender<Configuration, <Configuration as transfer::Configuration>::UtxoSetVerifier>; - -/// Receiver Type -pub type Receiver = identity::Receiver< - Configuration, - <Configuration as transfer::Configuration>::IntegratedEncryptionScheme, ->; - -/// Shielded Identity Type -pub type ShieldedIdentity = identity::ShieldedIdentity< - Configuration, - <Configuration as transfer::Configuration>::IntegratedEncryptionScheme, ->; - -/// Spend Type -pub type Spend = identity::Spend< - Configuration, - <Configuration as transfer::Configuration>::IntegratedEncryptionScheme, ->; - -/// Sender Post Type -pub type SenderPost = identity::SenderPost< - Configuration, - <Configuration as transfer::Configuration>::UtxoSetVerifier, ->; - -/// Receiver Post Type -pub type ReceiverPost = identity::ReceiverPost< - Configuration, - <Configuration as transfer::Configuration>::IntegratedEncryptionScheme, ->; - /// UTXO Set Parameters pub type Parameters = merkle_tree::Parameters<ConfigConverter<Configuration>>; @@ -75,11 +35,6 @@ pub type Root = merkle_tree::Root<ConfigConverter<Configuration>>; /// UTXO Set Path pub type Path = merkle_tree::Path<ConfigConverter<Configuration>>; -/// UTXO Set -// FIXME: Change this to sharded merkle tree. -pub type UtxoSet = - merkle_tree::MerkleTree<ConfigConverter<Configuration>, Full<ConfigConverter<Configuration>>>; - /// Identity Constraint System Variables pub mod constraint { use super::*; @@ -88,26 +43,11 @@ pub mod constraint { crypto::merkle_tree::constraint as merkle_tree_constraint, }; use manta_crypto::{ - accumulator::constraint::VerifierVariable, + accumulator::Verifier, constraint::{reflection::HasAllocation, Allocation, Constant, Variable}, }; use manta_util::concatenate; - /// Sender Variable Type - pub type SenderVar = identity::constraint::SenderVar< - Configuration, - <Configuration as transfer::Configuration>::UtxoSetVerifier, - >; - - /// Receiver Variable Type - pub type ReceiverVar = identity::constraint::ReceiverVar< - Configuration, - <Configuration as transfer::Configuration>::IntegratedEncryptionScheme, - >; - - /// UTXO Variable - pub type UtxoVar = identity::constraint::UtxoVar<Configuration>; - /// UTXO Set Parameters Variable pub type ParametersVar = merkle_tree_constraint::ParametersVar<Configuration>; @@ -119,9 +59,9 @@ pub mod constraint { /// UTXO Set Verifier Variable #[derive(Clone)] - pub struct UtxoSetVerifierVar(ParametersVar); + pub struct UtxoSetVerifier(ParametersVar); - impl Variable<ConstraintSystem> for UtxoSetVerifierVar { + impl Variable<ConstraintSystem> for UtxoSetVerifier { type Type = Parameters; type Mode = Constant; @@ -133,20 +73,23 @@ pub mod constraint { } } - impl VerifierVariable<ConstraintSystem> for UtxoSetVerifierVar { - type ItemVar = UtxoVar; + impl Verifier for UtxoSetVerifier { + type Item = UtxoVar; + + type Witness = <ParametersVar as Verifier>::Witness; + + type Output = <ParametersVar as Verifier>::Output; + + type Verification = <ParametersVar as Verifier>::Verification; #[inline] - fn assert_valid_membership_proof( + fn verify( &self, - item: &UtxoVar, - checkpoint: &RootVar, - witness: &PathVar, - cs: &mut ConstraintSystem, - ) { - let _ = cs; - self.0 - .assert_verified(checkpoint, witness, &concatenate!(item)); + item: &Self::Item, + witness: &Self::Witness, + output: &Self::Output, + ) -> Self::Verification { + self.0.verify(output, witness, &concatenate!(item)) } } } diff --git a/manta-pay/src/accounting/key.rs b/manta-pay/src/accounting/key.rs index dd01222cf..9f2f0c987 100644 --- a/manta-pay/src/accounting/key.rs +++ b/manta-pay/src/accounting/key.rs @@ -16,8 +16,9 @@ //! Secret Key Generation //! -//! This module contains [`DerivedKeySecret`] which implements the [`BIP-0044`] specification. We -//! may implement other kinds of key generation schemes in the future. +//! This module contains [`KeySecret`] which implements a hierarchical deterministic key generation +//! scheme based on the [`BIP-0044`] specification. We may implement other kinds of key generation +//! schemes in the future. //! //! See [`CoinType`] for the coins which this key generation scheme can control. //! @@ -26,7 +27,7 @@ use alloc::{format, string::String}; use bip32::{Seed, XPrv}; use core::{marker::PhantomData, num::ParseIntError, str::FromStr}; -use manta_accounting::key::{DerivedSecretKeyGenerator, DerivedSecretKeyParameter, KeyKind}; +use manta_accounting::key::{HierarchicalKeyTable, HierarchicalKeyTableParameter}; use manta_util::{create_seal, seal}; pub use bip32::{Error, Mnemonic}; @@ -38,8 +39,8 @@ pub type CoinTypeId = u128; /// Coin Type Marker Trait /// -/// This trait identifies a coin type and its id for the [`BIP-0044`] specification. This trait is -/// sealed and can only be used with the existing implementations. +/// This trait identifies a coin type and its identifier for the [`BIP-0044`] specification. This +/// trait is sealed and can only be used with the existing implementations. /// /// [`BIP-0044`]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki pub trait CoinType: sealed::Sealed { @@ -102,10 +103,10 @@ macro_rules! impl_from_for_parameter { } } -/// Implements the [`DerivedSecretKeyParameter`] trait for `$name`. +/// Implements the [`HierarchicalKeyTableParameter`] trait for `$name`. macro_rules! impl_parameter { ($name:ty) => { - impl DerivedSecretKeyParameter for $name { + impl HierarchicalKeyTableParameter for $name { #[inline] fn increment(&mut self) { self.0 += 1; @@ -140,32 +141,32 @@ pub struct IndexParameter(ParameterType); impl_parameter!(IndexParameter); -/// [`Testnet`] [`DerivedKeySecret`] Alias Type -pub type TestnetDerivedKeySecret = DerivedKeySecret<Testnet>; +/// Testnet [`KeySecret`] Type +pub type TestnetKeySecret = KeySecret<Testnet>; -/// [`Manta`] [`DerivedKeySecret`] Alias Type -pub type MantaDerivedKeySecret = DerivedKeySecret<Manta>; +/// Manta [`KeySecret`] Type +pub type MantaKeySecret = KeySecret<Manta>; -/// [`Calamari`] [`DerivedKeySecret`] Alias Type -pub type CalamariDerivedKeySecret = DerivedKeySecret<Calamari>; +/// Calamari [`KeySecret`] Type +pub type CalamariKeySecret = KeySecret<Calamari>; -/// Derived Key Secret -pub struct DerivedKeySecret<C> +/// Key Secret +pub struct KeySecret<C> where C: CoinType, { - /// Derived Key Seed + /// Key Seed seed: Seed, /// Type Parameter Marker __: PhantomData<C>, } -impl<C> DerivedKeySecret<C> +impl<C> KeySecret<C> where C: CoinType, { - /// Converts a `mnemonic` phrase into a [`DerivedKeySecret`], locking it with `password`. + /// Converts a `mnemonic` phrase into a [`KeySecret`], locking it with `password`. #[must_use] #[inline] pub fn new(mnemonic: Mnemonic, password: &str) -> Self { @@ -181,7 +182,7 @@ where /// [`BIP-0044`]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki #[must_use] #[inline] -pub fn path_string<C>(kind: KeyKind, account: &AccountParameter, index: &IndexParameter) -> String +pub fn path_string<C>(account: &AccountParameter, index: &IndexParameter, kind: u32) -> String where C: CoinType, { @@ -191,12 +192,12 @@ where BIP_44_PURPOSE_ID, C::COIN_TYPE_ID, account.0, - if kind.is_external() { 0 } else { 1 }, + kind, index.0 ) } -impl<C> DerivedSecretKeyGenerator for DerivedKeySecret<C> +impl<C> HierarchicalKeyTable for KeySecret<C> where C: CoinType, { @@ -206,15 +207,20 @@ where type Index = IndexParameter; + type Kind = u32; + type Error = Error; #[inline] - fn generate_key( + fn get( &self, - kind: KeyKind, account: &Self::Account, index: &Self::Index, + kind: &Self::Kind, ) -> Result<Self::SecretKey, Self::Error> { - XPrv::derive_from_path(&self.seed, &path_string::<C>(kind, account, index).parse()?) + XPrv::derive_from_path( + &self.seed, + &path_string::<C>(account, index, *kind).parse()?, + ) } } diff --git a/manta-pay/src/accounting/mod.rs b/manta-pay/src/accounting/mod.rs index 8fe3d4ae7..22a1bf290 100644 --- a/manta-pay/src/accounting/mod.rs +++ b/manta-pay/src/accounting/mod.rs @@ -19,5 +19,5 @@ pub mod config; pub mod identity; pub mod key; -pub mod ledger; +// TODO: pub mod ledger; pub mod transfer; diff --git a/manta-pay/src/accounting/transfer.rs b/manta-pay/src/accounting/transfer.rs index 96e5c0033..91abb62b0 100644 --- a/manta-pay/src/accounting/transfer.rs +++ b/manta-pay/src/accounting/transfer.rs @@ -17,7 +17,7 @@ //! Transfer Implementations use crate::accounting::config::Configuration; -use manta_accounting::transfer::{self, canonical}; +use manta_accounting::transfer::{self as transfer, canonical}; /// Mint Transaction Type pub type Mint = canonical::Mint<Configuration>; diff --git a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs index 387ac4921..864043459 100644 --- a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs @@ -26,7 +26,7 @@ use core::{ borrow::Borrow, ops::{Add, AddAssign}, }; -use manta_accounting::{AssetId, AssetValue}; +use manta_accounting::asset::{AssetId, AssetValue}; use manta_crypto::constraint::{ measure::Measure, reflection::HasAllocation, types::Bool, Allocation, AllocationMode, ConstraintSystem, Equal, Public, PublicOrSecret, Secret, Variable, VariableSource, diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs index 3fb7959d4..938007875 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs @@ -136,6 +136,7 @@ where } } +/* TODO: impl<E> Input<AssetId> for Groth16<E> where E: PairingEngine, @@ -180,10 +181,11 @@ where impl<E> Input<Utxo> for Groth16<E> where - E: PairingEngine<Fr = ark_ff::Fp256<ark_bls12_381::FrParameters>>, + E: PairingEngine, { #[inline] fn extend(input: &mut Self::Input, next: &Utxo) { next.extend_input(input); } } +*/ diff --git a/manta-pay/src/crypto/encryption.rs b/manta-pay/src/crypto/encryption.rs new file mode 100644 index 000000000..c2a916e64 --- /dev/null +++ b/manta-pay/src/crypto/encryption.rs @@ -0,0 +1,69 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Encryption Implementations + +// FIXME: Don't use raw bytes as secret and public key. +// FIXME: Make sure secret keys are protected. + +use aes_gcm::{ + aead::{Aead, NewAead}, + Aes256Gcm, Nonce, +}; +use core::marker::PhantomData; +use generic_array::GenericArray; +use manta_crypto::encryption::SymmetricKeyEncryptionScheme; + +/// AES Galois Counter Mode +pub struct AesGcm<T>(PhantomData<T>) +where + T: AsRef<[u8]> + From<Vec<u8>>; + +impl<T> AesGcm<T> +where + T: AsRef<[u8]> + From<Vec<u8>>, +{ + /// Encryption/Decryption Nonce + const NONCE: &'static [u8] = b"manta rocks!"; +} + +impl<T> SymmetricKeyEncryptionScheme for AesGcm<T> +where + T: AsRef<[u8]> + From<Vec<u8>>, +{ + type Key = [u8; 32]; + + type Plaintext = T; + + type Ciphertext = Vec<u8>; + + #[inline] + fn encrypt(key: Self::Key, plaintext: Self::Plaintext) -> Self::Ciphertext { + // SAFETY: Using a deterministic nonce is ok since we never reuse keys. + Aes256Gcm::new(GenericArray::from_slice(&key)) + .encrypt(Nonce::from_slice(Self::NONCE), plaintext.as_ref()) + .expect("Symmetric encryption is not allowed to fail.") + } + + #[inline] + fn decrypt(key: Self::Key, ciphertext: &Self::Ciphertext) -> Option<Self::Plaintext> { + // SAFETY: Using a deterministic nonce is ok since we never reuse keys. + Aes256Gcm::new(GenericArray::from_slice(&key)) + .decrypt(Nonce::from_slice(Self::NONCE), ciphertext.as_ref()) + .ok() + .map(Into::into) + } +} diff --git a/manta-pay/src/crypto/ies.rs b/manta-pay/src/crypto/ies.rs deleted file mode 100644 index 6bbfec7ad..000000000 --- a/manta-pay/src/crypto/ies.rs +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! IES Implementation - -// FIXME: Don't use raw bytes as secret and public key. -// FIXME: Make sure secret keys are protected. - -use aes_gcm::{ - aead::{Aead, NewAead}, - Aes256Gcm, Nonce, -}; -use blake2::{Blake2s, Digest}; -use generic_array::GenericArray; -use manta_accounting::Asset; -use manta_crypto::{ - encryption::ies::{self, IntegratedEncryptionScheme, KeyPair}, - rand::{CryptoRng, RngCore}, -}; -use manta_util::into_array_unchecked; -use x25519_dalek::{EphemeralSecret, PublicKey as PubKey, StaticSecret}; - -/// Public Key Type -pub type PublicKey = [u8; 32]; - -/// Secret Key Type -pub type SecretKey = [u8; 32]; - -/// `GCM` Tag Size -#[allow(clippy::cast_possible_truncation)] // NOTE: GCM Tag Size should be smaller than `2^32`. -const GCM_TAG_SIZE: usize = (aes_gcm::C_MAX - aes_gcm::P_MAX) as usize; - -/// Asset Ciphertext Type -pub type AssetCiphertext = [u8; Asset::SIZE + GCM_TAG_SIZE]; - -/// Ephemeral Public Key Type -pub type EphemeralPublicKey = PublicKey; - -/// Augmented Asset Ciphertext -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub struct AugmentedAssetCiphertext { - /// Asset Ciphertext - pub asset_ciphertext: AssetCiphertext, - - /// Ephemeral Public Key - pub ephemeral_public_key: EphemeralPublicKey, -} - -impl AugmentedAssetCiphertext { - /// Builds a new [`AugmentedAssetCiphertext`] from `asset_ciphertext` - /// and `ephemeral_public_key`. - #[inline] - pub const fn new( - asset_ciphertext: AssetCiphertext, - ephemeral_public_key: EphemeralPublicKey, - ) -> Self { - Self { - asset_ciphertext, - ephemeral_public_key, - } - } -} - -/// Encrypted Message for [`IES`] -pub type EncryptedAsset = ies::EncryptedMessage<IES>; - -/// Implementation of [`IntegratedEncryptionScheme`] -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct IES; - -impl IES { - /// Encryption/Decryption Nonce - const NONCE: &'static [u8] = b"manta rocks!"; - - /// KDF Salt - const KDF_SALT: &'static [u8] = b"manta kdf instantiated with blake2s hash function"; - - /// Runs `blake2s::hkdf_extract(salt, seed)` with a fixed salt. - #[inline] - fn blake2s_kdf(input: &[u8]) -> [u8; 32] { - let mut hasher = Blake2s::new(); - hasher.update(input); - hasher.update(Self::KDF_SALT); - into_array_unchecked(hasher.finalize()) - } -} - -impl IntegratedEncryptionScheme for IES { - type PublicKey = PublicKey; - - type SecretKey = SecretKey; - - type Plaintext = Asset; - - type Ciphertext = AugmentedAssetCiphertext; - - type Error = aes_gcm::Error; - - #[inline] - fn generate_keys<R>(rng: &mut R) -> KeyPair<Self> - where - R: CryptoRng + RngCore + ?Sized, - { - let sk = StaticSecret::new(rng); - let pk = PubKey::from(&sk); - KeyPair::new(pk.to_bytes(), sk.to_bytes()) - } - - #[inline] - fn generate_public_key<R>(rng: &mut R) -> ies::PublicKey<Self> - where - R: CryptoRng + RngCore + ?Sized, - { - // TODO: Is there an even more efficient way to do this? - ies::PublicKey::new(PubKey::from(&StaticSecret::new(rng)).to_bytes()) - } - - #[inline] - fn generate_secret_key<R>(rng: &mut R) -> ies::SecretKey<Self> - where - R: CryptoRng + RngCore + ?Sized, - { - ies::SecretKey::new(StaticSecret::new(rng).to_bytes()) - } - - fn encrypt<R>( - plaintext: &Self::Plaintext, - public_key: Self::PublicKey, - rng: &mut R, - ) -> Result<EncryptedAsset, Self::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - let ephemeral_secret_key = EphemeralSecret::new(rng); - let ephemeral_public_key = PubKey::from(&ephemeral_secret_key); - let shared_secret = Self::blake2s_kdf( - &ephemeral_secret_key - .diffie_hellman(&public_key.into()) - .to_bytes(), - ); - - // SAFETY: Using a deterministic nonce is ok since we never reuse keys. - let asset_ciphertext = Aes256Gcm::new(GenericArray::from_slice(&shared_secret)).encrypt( - Nonce::from_slice(Self::NONCE), - plaintext.into_bytes().as_ref(), - )?; - - Ok(EncryptedAsset::new(AugmentedAssetCiphertext::new( - into_array_unchecked(asset_ciphertext), - ephemeral_public_key.to_bytes(), - ))) - } - - fn decrypt( - ciphertext: &Self::Ciphertext, - secret_key: Self::SecretKey, - ) -> Result<Self::Plaintext, Self::Error> { - let shared_secret = Self::blake2s_kdf( - &StaticSecret::from(secret_key) - .diffie_hellman(&ciphertext.ephemeral_public_key.into()) - .to_bytes(), - ); - - // SAFETY: Using a deterministic nonce is ok since we never reuse keys. - let plaintext = Aes256Gcm::new(GenericArray::from_slice(&shared_secret)).decrypt( - Nonce::from_slice(Self::NONCE), - ciphertext.asset_ciphertext.as_ref(), - )?; - - Ok(Asset::from_bytes(into_array_unchecked( - &plaintext[..Asset::SIZE], - ))) - } -} - -/// Testing Suite -#[cfg(test)] -mod test { - use super::*; - use manta_crypto::{encryption::ies::test as ies_test, rand::Rand}; - use rand::thread_rng; - - /// Tests encryption/decryption of a random asset. - #[test] - fn encryption_decryption() { - let mut rng = thread_rng(); - ies_test::assert_decryption_of_encryption::<IES, _>(&rng.gen(), &mut rng); - } -} diff --git a/manta-pay/src/crypto/key.rs b/manta-pay/src/crypto/key.rs new file mode 100644 index 000000000..bc7c8d0d0 --- /dev/null +++ b/manta-pay/src/crypto/key.rs @@ -0,0 +1,76 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Cryptographic Key Primitive Implementations + +use ark_ec::{AffineCurve, ProjectiveCurve}; +use blake2::{Blake2s, Digest}; +use core::marker::PhantomData; +use manta_crypto::key::{KeyAgreementScheme, KeyDerivationFunction}; +use manta_util::into_array_unchecked; + +/// Elliptic Curve Diffie Hellman Protocol +pub struct EllipticCurveDiffieHellman<C>(PhantomData<C>) +where + C: ProjectiveCurve; + +impl<C> KeyAgreementScheme for EllipticCurveDiffieHellman<C> +where + C: ProjectiveCurve, +{ + type SecretKey = C::ScalarField; + + type PublicKey = C; + + type SharedSecret = C; + + #[inline] + fn derive(secret_key: &Self::SecretKey) -> Self::PublicKey { + Self::derive_owned(*secret_key) + } + + #[inline] + fn derive_owned(secret_key: Self::SecretKey) -> Self::PublicKey { + C::Affine::prime_subgroup_generator().mul(secret_key) + } + + #[inline] + fn agree(secret_key: &Self::SecretKey, public_key: &Self::PublicKey) -> Self::SharedSecret { + Self::agree_owned(*secret_key, *public_key) + } + + #[inline] + fn agree_owned(secret_key: Self::SecretKey, public_key: Self::PublicKey) -> Self::SharedSecret { + public_key *= secret_key; + public_key + } +} + +/// Blake2s KDF +pub struct Blake2sKdf; + +impl<T> KeyDerivationFunction<T, [u8; 32]> for Blake2sKdf +where + T: AsRef<[u8]>, +{ + #[inline] + fn derive(secret: T) -> [u8; 32] { + let mut hasher = Blake2s::new(); + hasher.update(secret.as_ref()); + hasher.update(b"manta kdf instantiated with blake2s hash function"); + into_array_unchecked(hasher.finalize()) + } +} diff --git a/manta-pay/src/crypto/merkle_tree.rs b/manta-pay/src/crypto/merkle_tree.rs index 01897b5fa..b7d6d2d8a 100644 --- a/manta-pay/src/crypto/merkle_tree.rs +++ b/manta-pay/src/crypto/merkle_tree.rs @@ -260,6 +260,7 @@ pub mod constraint { use ark_r1cs_std::{alloc::AllocVar, boolean::Boolean, eq::EqGadget, uint8::UInt8}; use ark_relations::ns; use manta_crypto::{ + accumulator::Verifier, constraint::{reflection::HasAllocation, Allocation, Constant, Public, Secret, Variable}, merkle_tree::{Parameters, Path, Root}, }; @@ -353,6 +354,29 @@ pub mod constraint { } } + impl<C> Verifier for ParametersVar<C> + where + C: Configuration, + { + type Item = [UInt8<ConstraintField<C>>]; + + type Witness = PathVar<C>; + + type Output = RootVar<C>; + + type Verification = Boolean<ConstraintField<C>>; + + #[inline] + fn verify( + &self, + item: &Self::Item, + witness: &Self::Witness, + output: &Self::Output, + ) -> Self::Verification { + self.verify(output, witness, item) + } + } + impl<C> Variable<ContraintSystem<C>> for ParametersVar<C> where C: Configuration, diff --git a/manta-pay/src/crypto/mod.rs b/manta-pay/src/crypto/mod.rs index 603696d1c..b03b90657 100644 --- a/manta-pay/src/crypto/mod.rs +++ b/manta-pay/src/crypto/mod.rs @@ -18,6 +18,6 @@ pub mod commitment; pub mod constraint; -pub mod ies; +pub mod encryption; +pub mod key; pub mod merkle_tree; -pub mod prf; diff --git a/manta-pay/src/crypto/prf/blake2s.rs b/manta-pay/src/crypto/prf/blake2s.rs deleted file mode 100644 index 00d4727d6..000000000 --- a/manta-pay/src/crypto/prf/blake2s.rs +++ /dev/null @@ -1,396 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Blake2s PRF Implementation - -use alloc::vec::Vec; -use ark_crypto_primitives::prf::{Blake2s as ArkBlake2s, PRF}; -use manta_crypto::{ - prf::PseudorandomFunctionFamily, - rand::{CryptoRng, Rand, RngCore, Sample, Standard}, -}; -use manta_util::{Concat, ConcatAccumulator}; - -/// Blake2s Pseudorandom Function Family -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Blake2s; - -/// Blake2s Pseudorandom Function Family Seed -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Blake2sSeed(<ArkBlake2s as PRF>::Seed); - -impl AsMut<[u8]> for Blake2sSeed { - #[inline] - fn as_mut(&mut self) -> &mut [u8] { - self.0.as_mut() - } -} - -impl Concat for Blake2sSeed { - type Item = u8; - - #[inline] - fn concat<A>(&self, accumulator: &mut A) - where - A: ConcatAccumulator<Self::Item> + ?Sized, - { - self.0.concat(accumulator); - } -} - -impl From<Blake2sSeed> for [u8; 32] { - #[inline] - fn from(seed: Blake2sSeed) -> Self { - seed.0 - } -} - -impl Sample for Blake2sSeed { - #[inline] - fn sample<R>(distribution: Standard, rng: &mut R) -> Self - where - R: CryptoRng + RngCore + ?Sized, - { - let _ = distribution; - Self(rng.gen()) - } -} - -/// Blake2s Pseudorandom Function Family Input -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Blake2sInput(<ArkBlake2s as PRF>::Input); - -impl Concat for Blake2sInput { - type Item = u8; - - #[inline] - fn concat<A>(&self, accumulator: &mut A) - where - A: ConcatAccumulator<Self::Item> + ?Sized, - { - self.0.concat(accumulator); - } -} - -impl Sample for Blake2sInput { - #[inline] - fn sample<R>(distribution: Standard, rng: &mut R) -> Self - where - R: CryptoRng + RngCore + ?Sized, - { - let _ = distribution; - Self(rng.gen()) - } -} - -/// Blake2s Pseudorandom Function Family Output -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Blake2sOutput(<ArkBlake2s as PRF>::Output); - -impl Concat for Blake2sOutput { - type Item = u8; - - #[inline] - fn concat<A>(&self, accumulator: &mut A) - where - A: ConcatAccumulator<Self::Item> + ?Sized, - { - self.0.concat(accumulator); - } -} - -impl PseudorandomFunctionFamily for Blake2s { - type Seed = Blake2sSeed; - - type Input = Blake2sInput; - - type Output = Blake2sOutput; - - #[inline] - fn evaluate(seed: &Self::Seed, input: &Self::Input) -> Self::Output { - Blake2sOutput( - ArkBlake2s::evaluate(&seed.0, &input.0) - .expect("As of arkworks 0.3.0, this never fails."), - ) - } -} - -/// Blake2s PRF Constraint System Implementations -pub mod constraint { - use super::*; - use crate::crypto::constraint::arkworks::{empty, full, ByteArrayVar}; - use ark_crypto_primitives::{ - prf::blake2s::constraints::Blake2sGadget as ArkBlake2sVar, PRFGadget, - }; - use ark_ff::{PrimeField, ToConstraintField}; - use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget, uint8::UInt8, ToBytesGadget}; - use ark_relations::ns; - use core::marker::PhantomData; - use manta_crypto::constraint::{ - reflection::HasAllocation, types::Bool, Allocation, Constant, Equal, PublicOrSecret, - Secret, Variable, - }; - - /// Constraint System Type - pub use crate::crypto::constraint::arkworks::ArkConstraintSystem as ConstraintSystem; - - /// Blake2s Pseudorandom Function Family Seed Variable - #[derive(derivative::Derivative)] - #[derivative(Clone)] - pub struct Blake2sSeedVar<F>(ByteArrayVar<F, 32>) - where - F: PrimeField; - - impl<F> Concat for Blake2sSeedVar<F> - where - F: PrimeField, - { - type Item = UInt8<F>; - - #[inline] - fn concat<A>(&self, accumulator: &mut A) - where - A: ConcatAccumulator<Self::Item> + ?Sized, - { - self.0.concat(accumulator); - } - } - - impl<F> Variable<ConstraintSystem<F>> for Blake2sSeedVar<F> - where - F: PrimeField, - { - type Type = Blake2sSeed; - - type Mode = Secret; - - #[inline] - fn new( - cs: &mut ConstraintSystem<F>, - allocation: Allocation<Self::Type, Self::Mode>, - ) -> Self { - Self(allocation.map_allocate(cs, move |this| this.0)) - } - } - - impl<F> HasAllocation<ConstraintSystem<F>> for Blake2sSeed - where - F: PrimeField, - { - type Variable = Blake2sSeedVar<F>; - type Mode = Secret; - } - - /// Blake2s Pseudorandom Function Family Input Variable - #[derive(derivative::Derivative)] - #[derivative(Clone)] - pub struct Blake2sInputVar<F>(ByteArrayVar<F, 32>) - where - F: PrimeField; - - impl<F> Concat for Blake2sInputVar<F> - where - F: PrimeField, - { - type Item = UInt8<F>; - - #[inline] - fn concat<A>(&self, accumulator: &mut A) - where - A: ConcatAccumulator<Self::Item> + ?Sized, - { - self.0.concat(accumulator); - } - } - - impl<F> Variable<ConstraintSystem<F>> for Blake2sInputVar<F> - where - F: PrimeField, - { - type Type = Blake2sInput; - - type Mode = Secret; - - #[inline] - fn new( - cs: &mut ConstraintSystem<F>, - allocation: Allocation<Self::Type, Self::Mode>, - ) -> Self { - Self(allocation.map_allocate(cs, move |this| this.0)) - } - } - - impl<F> HasAllocation<ConstraintSystem<F>> for Blake2sInput - where - F: PrimeField, - { - type Variable = Blake2sInputVar<F>; - type Mode = Secret; - } - - /// Blake2s Pseudorandom Function Family Output Variable Inner Type - type Blake2sOutputVarInnerType<F> = <ArkBlake2sVar as PRFGadget<ArkBlake2s, F>>::OutputVar; - - /// Blake2s Pseudorandom Function Family Output Variable - #[derive(derivative::Derivative)] - #[derivative(Clone)] - pub struct Blake2sOutputVar<F>(Blake2sOutputVarInnerType<F>) - where - F: PrimeField; - - impl<F> Concat for Blake2sOutputVar<F> - where - F: PrimeField, - { - type Item = UInt8<F>; - - #[inline] - fn concat<A>(&self, accumulator: &mut A) - where - A: ConcatAccumulator<Self::Item> + ?Sized, - { - accumulator.extend(&self.0.to_bytes().expect("This is not allowed to fail.")); - } - } - - impl<F> Variable<ConstraintSystem<F>> for Blake2sOutputVar<F> - where - F: PrimeField, - { - type Type = Blake2sOutput; - - type Mode = PublicOrSecret; - - #[inline] - fn new( - cs: &mut ConstraintSystem<F>, - allocation: Allocation<Self::Type, Self::Mode>, - ) -> Self { - // SAFETY: We can use `empty` here because `Blake2sOutputVarInnerType` has an internal - // default and so its allocation never fails. - Self( - match allocation { - Allocation::Known(this, mode) => match mode { - PublicOrSecret::Public => Blake2sOutputVarInnerType::new_input( - ns!(cs.cs, "blake2s output public input"), - full(this.0), - ), - PublicOrSecret::Secret => Blake2sOutputVarInnerType::new_witness( - ns!(cs.cs, "blake2s output secret witness"), - full(this.0), - ), - }, - Allocation::Unknown(mode) => match mode { - PublicOrSecret::Public => Blake2sOutputVarInnerType::new_input( - ns!(cs.cs, "blake2s output public input"), - empty::<<ArkBlake2s as PRF>::Output>, - ), - PublicOrSecret::Secret => Blake2sOutputVarInnerType::new_witness( - ns!(cs.cs, "blake2s output secret witness"), - empty::<<ArkBlake2s as PRF>::Output>, - ), - }, - } - .expect("Variable allocation is not allowed to fail."), - ) - } - } - - impl<F> HasAllocation<ConstraintSystem<F>> for Blake2sOutput - where - F: PrimeField, - { - type Variable = Blake2sOutputVar<F>; - type Mode = PublicOrSecret; - } - - impl<F> Equal<ConstraintSystem<F>> for Blake2sOutputVar<F> - where - F: PrimeField, - { - #[inline] - fn eq(cs: &mut ConstraintSystem<F>, lhs: &Self, rhs: &Self) -> Bool<ConstraintSystem<F>> { - let _ = cs; - lhs.0 - .is_eq(&rhs.0) - .expect("Equality checking is not allowed to fail.") - } - } - - impl Blake2sOutput { - /// Extends the `input` vector by constraint field elements that make up `self`. - #[inline] - pub fn extend_input<F>(&self, input: &mut Vec<F>) - where - F: PrimeField, - { - input.append( - &mut ToConstraintField::to_field_elements(&self.0) - .expect("Conversion to constraint field elements is not allowed to fail."), - ); - } - } - - /// Blake2s Pseudorandom Function Family Variable - #[derive(derivative::Derivative)] - #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct Blake2sVar<F>(PhantomData<F>) - where - F: PrimeField; - - impl<F> Variable<ConstraintSystem<F>> for Blake2sVar<F> - where - F: PrimeField, - { - type Type = Blake2s; - - type Mode = Constant; - - #[inline] - fn new( - cs: &mut ConstraintSystem<F>, - allocation: Allocation<Self::Type, Self::Mode>, - ) -> Self { - let _ = (cs, allocation); - Default::default() - } - } - - impl<F> HasAllocation<ConstraintSystem<F>> for Blake2s - where - F: PrimeField, - { - type Variable = Blake2sVar<F>; - type Mode = Constant; - } - - impl<F> PseudorandomFunctionFamily for Blake2sVar<F> - where - F: PrimeField, - { - type Seed = Blake2sSeedVar<F>; - type Input = Blake2sInputVar<F>; - type Output = Blake2sOutputVar<F>; - - #[inline] - fn evaluate(seed: &Self::Seed, input: &Self::Input) -> Self::Output { - Blake2sOutputVar( - ArkBlake2sVar::evaluate(seed.0.as_ref(), input.0.as_ref()) - .expect("Failure outcomes are not accepted."), - ) - } - } -} diff --git a/manta-pay/src/crypto/prf/mod.rs b/manta-pay/src/crypto/prf/mod.rs deleted file mode 100644 index 2ff44a2ea..000000000 --- a/manta-pay/src/crypto/prf/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Pseudorandom Function Family Implementations - -pub mod blake2s; From b5dd8b5cae6f88faa04670ad5d57ddd95273deec Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 15 Nov 2021 00:56:14 -0500 Subject: [PATCH 125/275] wip: optimize some of the transfer interfaces --- manta-accounting/src/asset.rs | 8 +- manta-accounting/src/identity.rs | 217 ++++++++++++++++++-------- manta-accounting/src/transfer.rs | 43 ++--- manta-accounting/src/wallet/signer.rs | 103 ++++++------ manta-crypto/src/encryption.rs | 6 + 5 files changed, 235 insertions(+), 142 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 290d91885..d71ab5fc7 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -24,7 +24,6 @@ use alloc::vec::Vec; use core::{ - convert::TryFrom, fmt::Debug, hash::Hash, iter, @@ -36,13 +35,10 @@ use derive_more::{ Add, AddAssign, Display, Div, DivAssign, From, Mul, MulAssign, Product, Sub, SubAssign, Sum, }; use manta_crypto::{ - constraint::{ - reflection::{unknown, HasAllocation, HasVariable, Var}, - Allocation, PublicOrSecret, Secret, Variable, VariableSource, - }, + constraint::{Allocation, PublicOrSecret, Secret, Variable, VariableSource}, rand::{CryptoRng, Rand, RngCore, Sample, Standard}, }; -use manta_util::{array_map, fallible_array_map, into_array_unchecked, Concat, ConcatAccumulator}; +use manta_util::{into_array_unchecked, Concat, ConcatAccumulator}; /// [`AssetId`] Base Type pub type AssetIdType = u32; diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index cb831821f..5bd47b9fe 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -20,78 +20,76 @@ use core::marker::PhantomData; use manta_crypto::{ accumulator::{Accumulator, MembershipProof, Verifier}, commitment::{CommitmentScheme, Input as CommitmentInput}, - encryption::{EncryptedMessage, HybridPublicKeyEncryptionScheme}, + constraint::{ + Allocation, ConstraintSystem, Derived, Equal, Public, PublicOrSecret, Secret, Variable, + VariableSource, + }, + encryption::{DecryptedMessage, EncryptedMessage, HybridPublicKeyEncryptionScheme}, key::KeyAgreementScheme, }; -use manta_crypto::constraint::{ConstraintSystem, Equal}; - -/// Viewing Key Table -pub trait ViewingKeyTable<K> +/// Spending Key +pub struct SpendingKey<K> where K: KeyAgreementScheme, { - /// Query Type - type Query: Default; + /// Spend Part of the Spending Key + spend: K::SecretKey, - /// Returns the key associated to `query`, generating a new key if `query` does not already - /// correspond to an existing key. - fn get_or_generate(&mut self, query: Self::Query) -> &K::SecretKey; + /// View Part of the Spending Key + view: K::SecretKey, } -impl<K> ViewingKeyTable<K> for K::SecretKey +impl<K> SpendingKey<K> where K: KeyAgreementScheme, { - type Query = (); - + /// Builds a new [`SpendingKey`] from `spend` and `view`. #[inline] - fn get_or_generate(&mut self, query: Self::Query) -> &K::SecretKey { - let _ = query; - self + pub fn new(spend: K::SecretKey, view: K::SecretKey) -> Self { + Self { spend, view } } -} -/// Spending Key -pub struct SpendingKey<K, V = <K as KeyAgreementScheme>::SecretKey> -where - K: KeyAgreementScheme, - V: ViewingKeyTable<K>, -{ - /// Spending Key - spending_key: K::SecretKey, - - /// Viewing Key Table - viewing_key_table: V, -} - -impl<K, V> SpendingKey<K, V> -where - K: KeyAgreementScheme, - V: ViewingKeyTable<K>, -{ - /// Builds a new [`SpendingKey`] from `spending_key` and `viewing_key_table`. + /// Derives the receiving key for `self`. #[inline] - pub fn new(spending_key: K::SecretKey, viewing_key_table: V) -> Self { - Self { - spending_key, - viewing_key_table, + pub fn derive(&self) -> ReceivingKey<K> { + ReceivingKey { + spend: K::derive(&self.spend), + view: K::derive(&self.view), } } - /// Returns the receiving key for `self` with the viewing key located at the given `query` in - /// the viewing key table. + /// Tries to decrypt `encrypted_note` with the viewing key associated to `self`. #[inline] - pub fn receiving_key(&mut self, query: V::Query) -> ReceivingKey<K> { - ReceivingKey { - spend: K::derive(&self.spending_key), - view: K::derive(self.viewing_key_table.get_or_generate(query)), - } + pub fn decrypt<H>( + &self, + encrypted_note: EncryptedMessage<H>, + ) -> Result<DecryptedMessage<H>, EncryptedMessage<H>> + where + H: HybridPublicKeyEncryptionScheme<KeyAgreementScheme = K>, + { + encrypted_note.decrypt(&self.view) + } + + /// Validates the `utxo` against `self` and the given `ephemeral_key` and `asset`. + #[inline] + pub fn validate_utxo<C>( + &self, + ephemeral_key: &PublicKey<C>, + asset: &C::Asset, + utxo: &Utxo<C>, + commitment_scheme: &C::CommitmentScheme, + ) -> bool + where + C: Configuration<KeyAgreementScheme = K>, + Utxo<C>: PartialEq, + { + &commitment_scheme.commit_one(asset, &K::agree(&self.spend, ephemeral_key)) == utxo } /// Prepares `self` for spending `asset` with the given `ephemeral_key`. #[inline] - pub fn pre_sender<C>( + pub fn sender<C>( &self, ephemeral_key: PublicKey<C>, asset: C::Asset, @@ -101,12 +99,16 @@ where K::SecretKey: Clone, C: Configuration<KeyAgreementScheme = K>, { - PreSender::new( - self.spending_key.clone(), - ephemeral_key, - asset, - commitment_scheme, - ) + PreSender::new(self.spend.clone(), ephemeral_key, asset, commitment_scheme) + } + + /// Prepares `self` for receiving `asset`. + #[inline] + pub fn receiver<C>(&self, asset: C::Asset) -> PreReceiver<C> + where + C: Configuration<KeyAgreementScheme = K>, + { + self.derive().into_receiver(asset) } } @@ -126,24 +128,13 @@ impl<K> ReceivingKey<K> where K: KeyAgreementScheme, { - /// Prepares `self` for receiving `asset` with the given `ephemeral_key`. + /// Prepares `self` for receiving `asset`. #[inline] - pub fn into_receiver<C>( - self, - ephemeral_key: SecretKey<C>, - asset: C::Asset, - commitment_scheme: &C::CommitmentScheme, - ) -> FullReceiver<C> + pub fn into_receiver<C>(self, asset: C::Asset) -> PreReceiver<C> where C: Configuration<KeyAgreementScheme = K>, { - FullReceiver::new( - self.spend, - self.view, - ephemeral_key, - asset, - commitment_scheme, - ) + PreReceiver::new(self.spend, self.view, asset) } } @@ -541,6 +532,52 @@ where } } +/// Pre-Receiver +pub struct PreReceiver<C> +where + C: Configuration, +{ + /// Public Spending Key + spending_key: PublicKey<C>, + + /// Public Viewing Key + viewing_key: PublicKey<C>, + + /// Asset + asset: C::Asset, +} + +impl<C> PreReceiver<C> +where + C: Configuration, +{ + /// Builds a new [`PreReceiver`] for `spending_key` to receive `asset`. + #[inline] + pub fn new(spending_key: PublicKey<C>, viewing_key: PublicKey<C>, asset: C::Asset) -> Self { + Self { + spending_key, + viewing_key, + asset, + } + } + + /// Upgrades `self` into a [`FullReceiver`] with the designated `ephemeral_key`. + #[inline] + pub fn upgrade( + self, + ephemeral_key: SecretKey<C>, + commitment_scheme: &C::CommitmentScheme, + ) -> FullReceiver<C> { + FullReceiver::new( + self.spending_key, + self.viewing_key, + ephemeral_key, + self.asset, + commitment_scheme, + ) + } +} + /// Receiver pub struct Receiver<C> where @@ -602,6 +639,38 @@ where } } +impl<CS, C> Variable<CS> for Receiver<C> +where + C: Configuration + Variable<CS>, + C::Type: Configuration, + PublicKey<C>: Variable<CS, Type = PublicKey<C::Type>, Mode = Secret>, + SecretKey<C>: Variable<CS, Type = SecretKey<C::Type>, Mode = Secret>, + C::Asset: Variable<CS, Type = <C::Type as Configuration>::Asset, Mode = Secret>, + Utxo<C>: Variable<CS, Type = Utxo<C::Type>, Mode = PublicOrSecret>, +{ + type Type = Receiver<C::Type>; + + type Mode = Derived; + + #[inline] + fn new(cs: &mut CS, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + match allocation { + Allocation::Known(this, mode) => Self { + spending_key: this.spending_key.as_known(cs, mode), + ephemeral_key: this.ephemeral_key.as_known(cs, mode), + asset: this.asset.as_known(cs, mode), + utxo: this.utxo.as_known(cs, Public), + }, + Allocation::Unknown(mode) => Self { + spending_key: PublicKey::<C>::new_unknown(cs, mode), + ephemeral_key: SecretKey::<C>::new_unknown(cs, mode), + asset: C::Asset::new_unknown(cs, mode), + utxo: Utxo::<C>::new_unknown(cs, Public), + }, + } + } +} + /// Full Receiver pub struct FullReceiver<C> where @@ -651,6 +720,12 @@ where } } + /// Converts `self` into its [`PreReceiver`], dropping the ephemeral key. + #[inline] + pub fn downgrade(self) -> PreReceiver<C> { + PreReceiver::new(self.spending_key, self.viewing_key, self.asset) + } + /// Extracts the ledger posting data from `self`. #[inline] pub fn into_post<H>(self) -> ReceiverPost<C, H> @@ -742,6 +817,12 @@ where KeyAgreementScheme = C::KeyAgreementScheme, >, { + /// Returns the ephemeral key associated to `self`. + #[inline] + pub fn ephemeral_key(&self) -> &PublicKey<C> { + self.note.ephemeral_public_key() + } + /// Validates `self` on the receiver `ledger`. #[inline] pub fn validate<L>(self, ledger: &L) -> Result<ReceiverPostingKey<C, H, L>, ReceiverPostError> diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index fa1fdf602..78350371b 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -18,7 +18,7 @@ use crate::{ asset::{Asset, AssetId, AssetValue}, - identity::{self, CommitmentSchemeOutput, Utxo}, + identity::{self, CommitmentSchemeOutput, PreReceiver, Utxo}, }; use alloc::vec::Vec; use core::ops::Add; @@ -197,10 +197,10 @@ pub struct Transfer< sources: [AssetValue; SOURCES], /// Senders - senders: [Sender<C>; SENDERS], + senders: [PreSender<C>; SENDERS], /// Receivers - receivers: [FullReceiver<C>; RECEIVERS], + receivers: [PreReceiver<C>; RECEIVERS], /// Sinks sinks: [AssetValue; SINKS], @@ -211,13 +211,13 @@ impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, cons where C: Configuration, { - /// Builds a new [`Transfer`] from public and secret information. + /// Builds a new [`Transfer`]. #[inline] fn new( asset_id: Option<AssetId>, sources: [AssetValue; SOURCES], - senders: [Sender<C>; SENDERS], - receivers: [FullReceiver<C>; RECEIVERS], + senders: [PreSender<C>; SENDERS], + receivers: [PreReceiver<C>; RECEIVERS], sinks: [AssetValue; SINKS], ) -> Self { Self::check_shape(asset_id.is_some()); @@ -274,8 +274,8 @@ where fn new_unchecked( asset_id: Option<AssetId>, sources: [AssetValue; SOURCES], - senders: [Sender<C>; SENDERS], - receivers: [FullReceiver<C>; RECEIVERS], + senders: [PreSender<C>; SENDERS], + receivers: [PreReceiver<C>; RECEIVERS], sinks: [AssetValue; SINKS], ) -> Self { Self { @@ -348,6 +348,8 @@ where utxo_set_verifier: UtxoSetVerifierVar<C>, cs: &mut C::ConstraintSystem, ) { + // FIXME: Add fair randomness constraint. + let mut secret_asset_ids = Vec::with_capacity(SENDERS + RECEIVERS); let input_sum = participants @@ -463,6 +465,7 @@ where where R: CryptoRng + RngCore + ?Sized, { + /* TODO: Ok(TransferPost { validity_proof: self.is_valid(commitment_scheme, utxo_set_verifier, context, rng)?, asset_id: self.asset_id, @@ -475,6 +478,8 @@ where .collect(), sinks: self.sinks.into(), }) + */ + todo!() } } @@ -575,22 +580,22 @@ where C: Configuration, { /// Asset Id - asset_id: Option<AssetId>, + pub asset_id: Option<AssetId>, /// Sources - sources: Vec<AssetValue>, + pub sources: Vec<AssetValue>, /// Sender Posts - sender_posts: Vec<SenderPost<C>>, + pub sender_posts: Vec<SenderPost<C>>, /// Receiver Posts - receiver_posts: Vec<ReceiverPost<C>>, + pub receiver_posts: Vec<ReceiverPost<C>>, /// Sinks - sinks: Vec<AssetValue>, + pub sinks: Vec<AssetValue>, /// Validity Proof - validity_proof: Proof<C>, + pub validity_proof: Proof<C>, } create_seal! {} @@ -671,7 +676,7 @@ pub mod canonical { { /// Builds a [`Mint`] from `asset` and `receiver`. #[inline] - pub fn build(asset: Asset, receiver: FullReceiver<C>) -> Self { + pub fn build(asset: Asset, receiver: PreReceiver<C>) -> Self { Self::new( Some(asset.id), [asset.value], @@ -702,8 +707,8 @@ pub mod canonical { /// Builds a [`PrivateTransfer`] from `senders` and `receivers`. #[inline] pub fn build( - senders: [Sender<C>; PrivateTransferShape::SENDERS], - receivers: [FullReceiver<C>; PrivateTransferShape::RECEIVERS], + senders: [PreSender<C>; PrivateTransferShape::SENDERS], + receivers: [PreReceiver<C>; PrivateTransferShape::RECEIVERS], ) -> Self { Self::new( Default::default(), @@ -744,8 +749,8 @@ pub mod canonical { /// Builds a [`Reclaim`] from `senders`, `receivers`, and `reclaim`. #[inline] pub fn build( - senders: [Sender<C>; ReclaimShape::SENDERS], - receivers: [FullReceiver<C>; ReclaimShape::RECEIVERS], + senders: [PreSender<C>; ReclaimShape::SENDERS], + receivers: [PreReceiver<C>; ReclaimShape::RECEIVERS], reclaim: Asset, ) -> Self { Self::new( diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index e638b90ae..4489fcbc1 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -29,14 +29,13 @@ use crate::{ asset::{Asset, AssetId, AssetMap, AssetValue}, - fs::{Load, LoadWith, Save, SaveWith}, identity::{self, PreSender, PublicKey, SecretKey, Utxo}, key::{Account, HierarchicalKeyTable}, transfer::{ self, canonical::{Mint, PrivateTransfer, PrivateTransferShape, Reclaim, Transaction}, EncryptedNote, FullReceiver, ProofSystemError, ProvingContext, Receiver, ReceivingKey, - Sender, Shape, Transfer, TransferPost, + Sender, Shape, SpendingKey, Transfer, TransferPost, }, }; use alloc::{vec, vec::Vec}; @@ -53,6 +52,7 @@ use manta_crypto::{ Accumulator, ConstantCapacityAccumulator, ExactSizeAccumulator, OptimizedAccumulator, Verifier, }, + encryption::DecryptedMessage, key::KeyAgreementScheme, rand::{CryptoRng, RngCore}, }; @@ -321,8 +321,12 @@ pub struct Signer<C> where C: Configuration, { + /* TODO: /// Account Keys account: Account<C::HierarchicalKeyTable>, + */ + /// Spending Key + spending_key: SpendingKey<C>, /// Commitment Scheme commitment_scheme: C::CommitmentScheme, @@ -350,7 +354,8 @@ where /// #[inline] fn new_inner( - account: Account<C::HierarchicalKeyTable>, + // TODO: account: Account<C::HierarchicalKeyTable>, + spending_key: SpendingKey<C>, commitment_scheme: C::CommitmentScheme, proving_context: ProvingContext<C>, utxo_set: C::UtxoSet, @@ -359,7 +364,8 @@ where rng: C::Rng, ) -> Self { Self { - account, + // TODO: account, + spending_key, commitment_scheme, proving_context, utxo_set, @@ -372,7 +378,8 @@ where /// #[inline] pub fn new( - account: Account<C::HierarchicalKeyTable>, + // TODO: account: Account<C::HierarchicalKeyTable>, + spending_key: SpendingKey<C>, commitment_scheme: C::CommitmentScheme, proving_context: ProvingContext<C>, rng: C::Rng, @@ -381,7 +388,8 @@ where C::UtxoSet: Default, { Self::new_inner( - account, + // TODO: account, + spending_key, commitment_scheme, proving_context, Default::default(), @@ -397,30 +405,33 @@ where where I: Iterator<Item = (Utxo<C>, EncryptedNote<C>)>, { - /* TODO: let mut assets = Vec::new(); - for (utxo, encrypted_asset) in updates { - if let Some(KeyOwned { inner, index }) = - self.signer.find_external_asset::<C>(&encrypted_asset) + for (utxo, encrypted_note) in updates { + if let Ok(DecryptedMessage { + plaintext: asset, + ephemeral_public_key, + }) = self.spending_key.decrypt(encrypted_note) { - // FIXME: We need to actually check at this point whether the `utxo` is valid, by - // computing the UTXO that lives at the `Sender` of `index`, this way, a - // future call to `try_upgrade` will never fail. If the call to `try_upgrade` - // fails, we need to mark the coin as burnt, or it will show up again in - // later calls to the signer (during coin selection). Currently, if the - // `utxo` does not match it should be stored in the verified set as - // non-provable and the asset should not be added to the asset map, since the - // asset is effectively burnt. - assets.push(inner); - self.assets.insert(index.reduce(), inner); + /* FIXME: + if self.spending_key.validate_utxo::<C>( + &ephemeral_public_key, + &asset, + &utxo, + &self.commitment_scheme, + ) { + assets.push(asset); + self.assets.insert(ephemeral_public_key, asset); + self.utxo_set.insert(&utxo); + } + */ + assets.push(asset); + self.assets.insert(ephemeral_public_key, asset); self.utxo_set.insert(&utxo); } else { self.utxo_set.insert_nonprovable(&utxo); } } Ok(SyncResponse::new(assets)) - */ - todo!() } /// Updates the internal ledger state, returning the new asset distribution. @@ -434,7 +445,6 @@ where where I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, { - /* TODO: self.start_sync(sync_state); // FIXME: Do a capacity check on the current UTXO set. @@ -442,6 +452,14 @@ where Some(diff) => self.sync_inner(updates.into_iter().skip(diff)), _ => Err(Error::InconsistentSynchronization), } + } + + /// Builds the pre-sender associated to `ephemeral_key` and `asset`. + #[inline] + fn build_pre_sender(&self, ephemeral_key: PublicKey<C>, asset: Asset) -> PreSender<C> { + /* TODO: + self.spending_key + .sender(ephemeral_key, asset, &self.commitment_scheme) */ todo!() } @@ -455,12 +473,14 @@ where return Err(Error::InsufficientBalance(asset)); } self.pending_assets.remove = selection.keys().cloned().collect(); - let pre_senders = selection - .values - .into_iter() - .map(move |(k, v)| self.get_pre_sender(k, asset.id.with(v))) - .collect::<Result<_, _>>()?; - Ok(Selection::new(selection.change, pre_senders)) + Ok(Selection::new( + selection.change, + selection + .values + .into_iter() + .map(move |(k, v)| self.build_pre_sender(k, asset.id.with(v))) + .collect(), + )) */ todo!() } @@ -692,12 +712,10 @@ where match transaction { Transaction::Mint(asset) => { /* TODO: - let (mint, owner) = self - .signer - .mint(&self.commitment_scheme, asset, &mut self.rng)? - .into(); - let mint_post = self.build_post(mint)?; - self.pending_assets.insert = Some((owner, asset)); + let mint_post = + self.build_post(Mint::build(asset, self.spending_key.receiver(asset)))?; + self.pending_assets.insert = + Some((mint_post.receiver_posts[0].ephemeral_key().clone(), asset)); Ok(SignResponse::new(vec![mint_post])) */ todo!() @@ -712,23 +730,15 @@ where /// Commits to the state after the last call to [`sign`](Self::sign). #[inline] pub fn commit(&mut self) { - /* TODO: - self.signer.account.internal_range_shift_to_end(); self.utxo_set.commit(); self.pending_assets.commit(&mut self.assets); - */ - todo!() } /// Rolls back to the state before the last call to [`sign`](Self::sign). #[inline] pub fn rollback(&mut self) { - /* TODO: - self.signer.account.internal_range_shift_to_start(); self.utxo_set.rollback(); self.pending_assets.rollback(); - */ - todo!() } /// Commits or rolls back the state depending on the value of `sync_state`. @@ -743,12 +753,7 @@ where /// Generates a new [`ReceivingKey`] for `self` to receive assets. #[inline] pub fn receiver(&mut self) -> ReceiverResult<C::HierarchicalKeyTable, C, Self> { - /* TODO: - self.signer - .next_shielded(&self.commitment_scheme) - .map_err(Error::SecretKeyError) - */ - todo!() + Ok(self.spending_key.derive()) } } diff --git a/manta-crypto/src/encryption.rs b/manta-crypto/src/encryption.rs index 46a40a9a7..6297f4792 100644 --- a/manta-crypto/src/encryption.rs +++ b/manta-crypto/src/encryption.rs @@ -151,6 +151,12 @@ where } } + /// Returns the ephemeral public key associated to `self`. + #[inline] + pub fn ephemeral_public_key(&self) -> &PublicKey<H> { + &self.ephemeral_public_key + } + /// Tries to decrypt `self` using `secret_key`, returning back `Err(self)` if the `secret_key` /// was unable to decrypt the message. #[inline] From 2a2081d8c81180364cf306d3e92b1dc6649be4c4 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 15 Nov 2021 12:26:38 -0500 Subject: [PATCH 126/275] wip: start rewriting signer --- manta-accounting/src/identity.rs | 42 ++++-- manta-accounting/src/key.rs | 185 ++++++++++++++++++++++---- manta-accounting/src/transfer.rs | 74 +++++++++-- manta-accounting/src/wallet/signer.rs | 38 ++---- manta-crypto/src/accumulator.rs | 17 +++ manta-crypto/src/commitment.rs | 33 ++--- manta-crypto/src/key.rs | 4 +- 7 files changed, 291 insertions(+), 102 deletions(-) diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs index 5bd47b9fe..8799d29af 100644 --- a/manta-accounting/src/identity.rs +++ b/manta-accounting/src/identity.rs @@ -84,7 +84,10 @@ where C: Configuration<KeyAgreementScheme = K>, Utxo<C>: PartialEq, { - &commitment_scheme.commit_one(asset, &K::agree(&self.spend, ephemeral_key)) == utxo + &commitment_scheme.commit_one( + asset, + &C::into_trapdoor(K::agree(&self.spend, ephemeral_key)), + ) == utxo } /// Prepares `self` for spending `asset` with the given `ephemeral_key`. @@ -147,9 +150,13 @@ pub trait Configuration { type KeyAgreementScheme: KeyAgreementScheme; /// Commitment Scheme Type - type CommitmentScheme: CommitmentScheme<Randomness = Trapdoor<Self>> + type CommitmentScheme: CommitmentScheme + CommitmentInput<Self::Asset> + CommitmentInput<SecretKey<Self>>; + + /// Converts the `shared_secret` returned by the key agreement scheme into a trapdoor for the + /// commitment scheme. + fn into_trapdoor(shared_secret: SharedSecret<Self>) -> Trapdoor<Self>; } /// Secret Key Type @@ -158,19 +165,21 @@ pub type SecretKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreemen /// Public Key Type pub type PublicKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::PublicKey; -/// Trapdoor Type -pub type Trapdoor<C> = +/// Shared Secret Type +pub type SharedSecret<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret; +/// Trapdoor Type +pub type Trapdoor<C> = <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Trapdoor; + /// Commitment Scheme Output Type -pub type CommitmentSchemeOutput<C> = - <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Output; +pub type Commitment<C> = <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Output; /// Unspent Transaction Output Type -pub type Utxo<C> = CommitmentSchemeOutput<C>; +pub type Utxo<C> = Commitment<C>; /// Void Number Type -pub type VoidNumber<C> = CommitmentSchemeOutput<C>; +pub type VoidNumber<C> = Commitment<C>; /// Pre-Sender pub struct PreSender<C> @@ -206,7 +215,8 @@ where asset: C::Asset, commitment_scheme: &C::CommitmentScheme, ) -> Self { - let trapdoor = C::KeyAgreementScheme::agree(&spending_key, &ephemeral_key); + let trapdoor = + C::into_trapdoor(C::KeyAgreementScheme::agree(&spending_key, &ephemeral_key)); Self { utxo: commitment_scheme.commit_one(&asset, &trapdoor), void_number: commitment_scheme.commit_one(&spending_key, &trapdoor), @@ -349,7 +359,10 @@ where PreSender { utxo: commitment_scheme.commit_one( &self.asset, - &C::KeyAgreementScheme::agree(&self.spending_key, &self.ephemeral_key), + &C::into_trapdoor(C::KeyAgreementScheme::agree( + &self.spending_key, + &self.ephemeral_key, + )), ), spending_key: self.spending_key, ephemeral_key: self.ephemeral_key, @@ -369,10 +382,13 @@ where ) -> C::Asset where CS: ConstraintSystem, - CommitmentSchemeOutput<C>: Equal<CS>, + Commitment<C>: Equal<CS>, V: Verifier<Verification = CS::Bool>, { - let trapdoor = C::KeyAgreementScheme::agree(&self.spending_key, &self.ephemeral_key); + let trapdoor = C::into_trapdoor(C::KeyAgreementScheme::agree( + &self.spending_key, + &self.ephemeral_key, + )); cs.assert(self.utxo_membership_proof.verify( &commitment_scheme.commit_one(&self.asset, &trapdoor), utxo_set_verifier, @@ -610,7 +626,7 @@ where ) -> Utxo<C> { commitment_scheme.commit_one( asset, - &C::KeyAgreementScheme::agree(ephemeral_key, spending_key), + &C::into_trapdoor(C::KeyAgreementScheme::agree(ephemeral_key, spending_key)), ) } diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index 451eac9b6..cd7c64d0d 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -16,12 +16,13 @@ //! Hierarchical Key Tables -// TODO: Make [`Account`] more useful for managing accounts. - +use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; /// Hierarchical Key Table Parameter -pub trait HierarchicalKeyTableParameter: Clone + Default + PartialOrd { +pub trait HierarchicalKeyTableParameter: + Clone + Copy + Default + PartialOrd + From<usize> + Into<usize> +{ /// Increments the key parameter by one unit. fn increment(&mut self); } @@ -46,8 +47,8 @@ pub trait HierarchicalKeyTable { /// Returns the secret key associated to `account` and `index` of the given `kind`. fn get( &self, - account: &Self::Account, - index: &Self::Index, + account: Self::Account, + index: Self::Index, kind: &Self::Kind, ) -> Result<Self::SecretKey, Self::Error>; } @@ -69,62 +70,188 @@ where #[inline] fn get( &self, - account: &Self::Account, - index: &Self::Index, + account: Self::Account, + index: Self::Index, kind: &Self::Kind, ) -> Result<Self::SecretKey, Self::Error> { (*self).get(account, index, kind) } } -/// Account Index with Table +/// Account Map +pub trait AccountMap<H> +where + H: HierarchicalKeyTable, +{ + /// Returns the maximum index associated to `account`. + fn max_index(&self, account: H::Account) -> Option<H::Index>; + + /// Creates a new account, returning the new account parameter. + fn create_account(&mut self) -> H::Account; + + /// Increments the index on the existing account, returning the new index parameter. + fn increment_index(&mut self, account: H::Account) -> Option<H::Index>; +} + +/// [`Vec`] Account Map Type +pub type VecAccountMap<H> = Vec<<H as HierarchicalKeyTable>::Index>; + +impl<H> AccountMap<H> for VecAccountMap<H> +where + H: HierarchicalKeyTable, +{ + #[inline] + fn max_index(&self, account: H::Account) -> Option<H::Index> { + self.get(account.into()).copied() + } + + #[inline] + fn create_account(&mut self) -> H::Account { + self.push(0.into()); + (self.len() - 1).into() + } + + #[inline] + fn increment_index(&mut self, account: H::Account) -> Option<H::Index> { + self.get_mut(account.into()).map(move |index| { + index.increment(); + *index + }) + } +} + +/// Account Table +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "H: Clone, M: Clone"), + Copy(bound = "H: Copy, M: Copy"), + Debug(bound = "H: Debug, M: Debug"), + Default(bound = "H: Default, M: Default"), + Eq(bound = "H: Eq, M: Eq"), + Hash(bound = "H: Hash, M: Hash"), + PartialEq(bound = "H: PartialEq, M: PartialEq") +)] +pub struct AccountTable<H, M = VecAccountMap<H>> +where + H: HierarchicalKeyTable, + M: AccountMap<H>, +{ + /// Hierarchical Key Table + table: H, + + /// Account Map + accounts: M, +} + +impl<H, M> AccountTable<H, M> +where + H: HierarchicalKeyTable, + M: AccountMap<H>, +{ + /// Builds a new [`AccountTable`] from a hierarchical key `table`. + #[inline] + pub fn new(table: H) -> Self + where + M: Default, + { + Self::with_accounts(table, Default::default()) + } + + /// Builds a new [`AccountTable`] from `table` and `accounts`. + #[inline] + pub fn with_accounts(table: H, accounts: M) -> Self { + Self { table, accounts } + } + + /// Returns the key associated to `account`, `index`, and `kind`. + #[inline] + pub fn key( + &self, + account: H::Account, + index: H::Index, + kind: &H::Kind, + ) -> Option<Result<H::SecretKey, H::Error>> { + self.subtable(account, index).map(move |st| st.key(kind)) + } + + /// Returns a subtable of `self` with fixed `account` and `index` parameters. + #[inline] + pub fn subtable(&self, account: H::Account, index: H::Index) -> Option<AccountSubTable<H>> { + match self.accounts.max_index(account) { + Some(max_index) if index <= max_index => { + Some(AccountSubTable::new(&self.table, account, index)) + } + _ => None, + } + } + + /// Creates a new account, returning the new account parameter. + #[inline] + pub fn create_account(&mut self) -> H::Account { + self.accounts.create_account() + } + + /// Increments the index on the existing account, returning the new index parameter. + #[inline] + pub fn increment_index(&mut self, account: H::Account) -> Option<H::Index> { + self.accounts.increment_index(account) + } +} + +/// Account Sub-Table #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "H: Clone,"), - Copy(bound = "H: Copy, H::Account: Copy, H::Index: Copy"), + Clone(bound = ""), + Copy(bound = ""), Debug(bound = "H: Debug, H::Account: Debug, H::Index: Debug"), - Default(bound = "H: Default"), Eq(bound = "H: Eq, H::Account: Eq, H::Index: Eq"), Hash(bound = "H: Hash, H::Account: Hash, H::Index: Hash"), - PartialEq(bound = "H: PartialEq") + PartialEq(bound = "H: PartialEq, H::Account: PartialEq, H::Index: PartialEq") )] -pub struct Account<H> +pub struct AccountSubTable<'t, H> where H: HierarchicalKeyTable, { /// Hierarchical Key Table - table: H, + table: &'t H, - /// Account Identifier + /// Account Parameter account: H::Account, - /// Latest Index - latest_index: H::Index, + /// Index Parameter + index: H::Index, } -impl<H> Account<H> +impl<'t, H> AccountSubTable<'t, H> where H: HierarchicalKeyTable, { - /// Builds a new [`Account`] for `table` and the given `account` identifier. + /// Builds a new [`AccountSubTable`] for `table`, `account`, and `index`. #[inline] - pub fn new(table: H, account: H::Account) -> Self { - Self::with_index(table, account, Default::default()) - } - - /// Builds a new [`Account`] for `table` and the given `account` identifier and `latest_index`. - #[inline] - pub fn with_index(table: H, account: H::Account, latest_index: H::Index) -> Self { + fn new(table: &'t H, account: H::Account, index: H::Index) -> Self { Self { table, account, - latest_index, + index, } } - /// Returns the key of the given `kind` for `self`. + /// Returns the inner account parameter of this subtable. + #[inline] + pub fn account(&self) -> H::Account { + self.account + } + + /// Returns the inner index parameter of this subtable. + #[inline] + pub fn index(&self) -> H::Index { + self.index + } + + /// Returns the key of the given `kind` from the hierarchical key table with a fixed account + /// and index. #[inline] pub fn key(&self, kind: &H::Kind) -> Result<H::SecretKey, H::Error> { - self.table.get(&self.account, &self.latest_index, kind) + self.table.get(self.account, self.index, kind) } } diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 78350371b..8f6eb8834 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -18,7 +18,7 @@ use crate::{ asset::{Asset, AssetId, AssetValue}, - identity::{self, CommitmentSchemeOutput, PreReceiver, Utxo}, + identity::{self, Commitment, PreReceiver, PublicKey, Utxo}, }; use alloc::vec::Vec; use core::ops::Add; @@ -70,10 +70,10 @@ pub trait Configuration: identity::Configuration<Asset = Asset> { + HasVariable<AssetId, Variable = AssetIdVar<Self>, Mode = PublicOrSecret> + HasVariable<AssetValue, Variable = AssetValueVar<Self>, Mode = PublicOrSecret> + HasVariable< - CommitmentSchemeOutput<Self>, - Variable = CommitmentSchemeOutput<Self::ConstraintConfiguration>, + Commitment<Self>, + Variable = Commitment<Self::ConstraintConfiguration>, Mode = PublicOrSecret, - > + HasEqual<CommitmentSchemeOutput<Self::ConstraintConfiguration>>; + > + HasEqual<Commitment<Self::ConstraintConfiguration>>; /// Constraint System Configuration type ConstraintConfiguration: ConstraintConfiguration<Self::ConstraintSystem>; @@ -180,6 +180,40 @@ pub type VerifyingContext<C> = <ProofSystemType<C> as ProofSystem>::VerifyingCon /// Transfer Validity Proof Type pub type Proof<C> = <ProofSystemType<C> as ProofSystem>::Proof; +/* TODO: +/// +pub struct Transfer2< + C, + const SOURCES: usize, + const SENDERS: usize, + const RECEIVERS: usize, + const SINKS: usize, +> where + C: Configuration, +{ + /// Asset Id + asset_id: Option<AssetId>, + + /// Sources + sources: [AssetValue; SOURCES], + + /// Senders + senders: [Sender<C>; SENDERS], + + /// Receivers + receivers: [FullReceiver<C>; RECEIVERS], + + /// Sinks + sinks: [AssetValue; SINKS], + + /// Fairness Constant Trapdoor + fair_trapdoor: Trapdoor<C>, + + /// Fairness Constant + fair: Commitment<C>, +} +*/ + /// Transfer pub struct Transfer< C, @@ -386,7 +420,7 @@ where /// Generates the constraint system for an unknown transfer. #[inline] - pub fn unknown_constraints( + fn unknown_constraints( commitment_scheme: &C::CommitmentScheme, utxo_set_verifier: &C::UtxoSetVerifier, ) -> C::ConstraintSystem { @@ -405,7 +439,7 @@ where /// Generates the constraint system for a known transfer. #[inline] - pub fn known_constraints( + fn known_constraints( &self, commitment_scheme: &C::CommitmentScheme, utxo_set_verifier: &C::UtxoSetVerifier, @@ -439,7 +473,7 @@ where /// Generates a validity proof for this transfer. #[inline] - pub fn is_valid<R>( + fn is_valid<R>( &self, commitment_scheme: &C::CommitmentScheme, utxo_set_verifier: &C::UtxoSetVerifier, @@ -580,22 +614,36 @@ where C: Configuration, { /// Asset Id - pub asset_id: Option<AssetId>, + asset_id: Option<AssetId>, /// Sources - pub sources: Vec<AssetValue>, + sources: Vec<AssetValue>, /// Sender Posts - pub sender_posts: Vec<SenderPost<C>>, + sender_posts: Vec<SenderPost<C>>, /// Receiver Posts - pub receiver_posts: Vec<ReceiverPost<C>>, + receiver_posts: Vec<ReceiverPost<C>>, /// Sinks - pub sinks: Vec<AssetValue>, + sinks: Vec<AssetValue>, /// Validity Proof - pub validity_proof: Proof<C>, + validity_proof: Proof<C>, +} + +impl<C> TransferPost<C> +where + C: Configuration, +{ + /// Returns the ephemeral keys associated to the receiver posts of `self`. + #[inline] + pub fn receiver_ephemeral_keys(&self) -> Vec<&PublicKey<C>> { + self.receiver_posts + .iter() + .map(ReceiverPost::ephemeral_key) + .collect() + } } create_seal! {} diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 4489fcbc1..ecf4f6ae6 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -23,14 +23,16 @@ // sign`. Will have to upgrade `Rollback` and `manta_crypto::merkle_tree::fork` as well. // TODO: Add checkpointing/garbage-collection in `utxo_set` so we can remove old UTXOs once they // are irrelevant. Once we create a sender and its transaction succeeds we can drop the UTXO. +// See `OptimizedAccumulator::remove_proof`. // TODO: Should have a mode on the signer where we return a generic error which reveals no detail // about what went wrong during signing. The kind of error returned from a signing could // reveal information about the internal state (privacy leak, not a secrecy leak). +// TODO: Setup multi-account wallets using `crate::key::AccountTable`. use crate::{ asset::{Asset, AssetId, AssetMap, AssetValue}, identity::{self, PreSender, PublicKey, SecretKey, Utxo}, - key::{Account, HierarchicalKeyTable}, + key::HierarchicalKeyTable, transfer::{ self, canonical::{Mint, PrivateTransfer, PrivateTransferShape, Reclaim, Transaction}, @@ -262,6 +264,7 @@ pub trait Configuration: transfer::Configuration { Item = <Self::UtxoSetVerifier as Verifier>::Item, Verifier = Self::UtxoSetVerifier, > + ConstantCapacityAccumulator + + Default + ExactSizeAccumulator + OptimizedAccumulator + Rollback; @@ -321,10 +324,6 @@ pub struct Signer<C> where C: Configuration, { - /* TODO: - /// Account Keys - account: Account<C::HierarchicalKeyTable>, - */ /// Spending Key spending_key: SpendingKey<C>, @@ -351,10 +350,9 @@ impl<C> Signer<C> where C: Configuration, { - /// + /// Builds a new [`Signer`]. #[inline] fn new_inner( - // TODO: account: Account<C::HierarchicalKeyTable>, spending_key: SpendingKey<C>, commitment_scheme: C::CommitmentScheme, proving_context: ProvingContext<C>, @@ -364,7 +362,6 @@ where rng: C::Rng, ) -> Self { Self { - // TODO: account, spending_key, commitment_scheme, proving_context, @@ -375,20 +372,20 @@ where } } + /// Builds a new [`Signer`] from a fresh `spending_key`. + /// + /// # Warning /// + /// This method assumes that `spending_key` has never been used before, and does not attempt to + /// perform wallet recovery on this key. #[inline] pub fn new( - // TODO: account: Account<C::HierarchicalKeyTable>, spending_key: SpendingKey<C>, commitment_scheme: C::CommitmentScheme, proving_context: ProvingContext<C>, rng: C::Rng, - ) -> Self - where - C::UtxoSet: Default, - { + ) -> Self { Self::new_inner( - // TODO: account, spending_key, commitment_scheme, proving_context, @@ -412,7 +409,7 @@ where ephemeral_public_key, }) = self.spending_key.decrypt(encrypted_note) { - /* FIXME: + /* FIXME: Add UTXO validation check. Is this necessary? if self.spending_key.validate_utxo::<C>( &ephemeral_public_key, &asset, @@ -457,17 +454,13 @@ where /// Builds the pre-sender associated to `ephemeral_key` and `asset`. #[inline] fn build_pre_sender(&self, ephemeral_key: PublicKey<C>, asset: Asset) -> PreSender<C> { - /* TODO: self.spending_key .sender(ephemeral_key, asset, &self.commitment_scheme) - */ - todo!() } /// Selects the pre-senders which collectively own at least `asset`, returning any change. #[inline] fn select(&mut self, asset: Asset) -> Result<Selection<C>, Error<C::HierarchicalKeyTable, C>> { - /* TODO: let selection = self.assets.select(asset); if selection.is_empty() { return Err(Error::InsufficientBalance(asset)); @@ -481,8 +474,6 @@ where .map(move |(k, v)| self.build_pre_sender(k, asset.id.with(v))) .collect(), )) - */ - todo!() } /// Builds a [`TransferPost`] for the given `transfer`. @@ -711,14 +702,11 @@ where self.commit(); match transaction { Transaction::Mint(asset) => { - /* TODO: let mint_post = self.build_post(Mint::build(asset, self.spending_key.receiver(asset)))?; self.pending_assets.insert = - Some((mint_post.receiver_posts[0].ephemeral_key().clone(), asset)); + Some((mint_post.receiver_ephemeral_keys()[0].clone(), asset)); Ok(SignResponse::new(vec![mint_post])) - */ - todo!() } Transaction::PrivateTransfer(asset, receiver) => { self.sign_withdraw(asset, Some(receiver)) diff --git a/manta-crypto/src/accumulator.rs b/manta-crypto/src/accumulator.rs index e52c94af2..b584915a2 100644 --- a/manta-crypto/src/accumulator.rs +++ b/manta-crypto/src/accumulator.rs @@ -216,6 +216,23 @@ pub trait OptimizedAccumulator: Accumulator { fn insert_nonprovable(&mut self, item: &<Self::Verifier as Verifier>::Item) -> bool { self.insert(item) } + + /// Removes the witnesses to the membership of `item` in `self`. The resulting state of the + /// accumulator after removing a proof should be the same as if the item had been inserted into + /// the accumulator with [`insert_nonprovable`](Self::insert_nonprovable). This method returns + /// `true` if the item was successfully demoted to non-provable. + /// + /// # Implementation Note + /// + /// By default, this method does nothing and returns `false`. Implementations of this method may + /// fail arbitrarily, and should only successfully remove a proof if the implementation is + /// efficient enough. Space and time tradeoffs should be studied to determine the usefulness of + /// this method. + #[inline] + fn remove_proof(&mut self, item: &<Self::Verifier as Verifier>::Item) -> bool { + let _ = item; + false + } } /// Accumulator Membership Proof diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index f9e197444..6edf2f9a3 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -18,7 +18,6 @@ // FIXME: Change this so that commiting one value is the default, and commiting a "concatenation" // of values is the special case. -// TODO: Change `Randomness` to `Trapdoor` use core::{fmt::Debug, hash::Hash}; use manta_util::{Concat, ConcatAccumulator}; @@ -28,8 +27,8 @@ pub trait CommitmentScheme { /// Commitment Input Type type Input: Default; - /// Commitment Randomness Parameter Type - type Randomness; + /// Commitment Trapdoor Parameter Type + type Trapdoor; /// Commitment Output Type type Output; @@ -40,23 +39,17 @@ pub trait CommitmentScheme { Builder::new(self) } - /// Commits the `input` with the given `randomness` parameter. - fn commit(&self, input: Self::Input, randomness: &Self::Randomness) -> Self::Output; + /// Commits the `input` with the given `trapdoor` parameter. + fn commit(&self, input: Self::Input, trapdoor: &Self::Trapdoor) -> Self::Output; - /// Commits with an empty input using the given `randomness` parameter. + /// Commits the single `input` value with the given `trapdoor` parameter. #[inline] - fn commit_none(&self, randomness: &Self::Randomness) -> Self::Output { - self.start().commit(randomness) - } - - /// Commits the single `input` value with the given `randomness` parameter. - #[inline] - fn commit_one<T>(&self, input: &T, randomness: &Self::Randomness) -> Self::Output + fn commit_one<T>(&self, input: &T, trapdoor: &Self::Trapdoor) -> Self::Output where T: ?Sized, Self: Input<T>, { - self.start().update(input).commit(randomness) + self.start().update(input).commit(trapdoor) } } @@ -126,10 +119,10 @@ where self } - /// Commits to the input stored in the builder with the given `randomness`. + /// Commits to the input stored in the builder with the given `trapdoor`. #[inline] - pub fn commit(self, randomness: &C::Randomness) -> C::Output { - self.commitment_scheme.commit(self.input, randomness) + pub fn commit(self, trapdoor: &C::Trapdoor) -> C::Output { + self.commitment_scheme.commit(self.input, trapdoor) } } @@ -140,19 +133,19 @@ pub mod test { use super::*; use core::fmt::Debug; - /// Asserts that the given commitment `output` is equal to commiting `input` with `randomness` + /// Asserts that the given commitment `output` is equal to commiting `input` with `trapdoor` /// using the `commitment_scheme`. #[inline] pub fn assert_commitment_matches<T, C>( commitment_scheme: &C, input: &T, - randomness: &C::Randomness, + trapdoor: &C::Trapdoor, output: &C::Output, ) where T: ?Sized, C: CommitmentScheme + Input<T> + ?Sized, C::Output: Debug + PartialEq, { - assert_eq!(&commitment_scheme.commit_one(input, randomness), output); + assert_eq!(&commitment_scheme.commit_one(input, trapdoor), output); } } diff --git a/manta-crypto/src/key.rs b/manta-crypto/src/key.rs index 9195b5aad..0d826013a 100644 --- a/manta-crypto/src/key.rs +++ b/manta-crypto/src/key.rs @@ -33,10 +33,10 @@ /// about the value of the [`SharedSecret`](Self::SharedSecret). pub trait KeyAgreementScheme { /// Secret Key Type - type SecretKey; + type SecretKey: Clone; /// Public Key Type - type PublicKey; + type PublicKey: Clone; /// Shared Secret Type type SharedSecret; From 2eea01312b30983e1ba4d82d1b56edaccd37b9e0 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 15 Nov 2021 17:18:03 -0500 Subject: [PATCH 127/275] wip: merge identity module into transfer module --- manta-accounting/src/lib.rs | 4 +- manta-accounting/src/transfer.rs | 1229 +++++++++++++++++++------ manta-accounting/src/wallet/ledger.rs | 5 +- manta-accounting/src/wallet/signer.rs | 6 +- manta-accounting/src/wallet/state.rs | 3 +- manta-crypto/src/key.rs | 4 +- 6 files changed, 962 insertions(+), 289 deletions(-) diff --git a/manta-accounting/src/lib.rs b/manta-accounting/src/lib.rs index cd132509d..826dc07bb 100644 --- a/manta-accounting/src/lib.rs +++ b/manta-accounting/src/lib.rs @@ -29,7 +29,7 @@ extern crate cocoon as cocoon_crate; pub mod asset; pub mod fs; -pub mod identity; +// TODO[remove]: pub mod identity; pub mod key; pub mod transfer; -pub mod wallet; +// pub mod wallet; diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 8f6eb8834..60606877a 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -16,23 +16,20 @@ //! Transfer Protocol -use crate::{ - asset::{Asset, AssetId, AssetValue}, - identity::{self, Commitment, PreReceiver, PublicKey, Utxo}, -}; +use crate::asset::{Asset, AssetId, AssetValue}; use alloc::vec::Vec; -use core::ops::Add; +use core::{marker::PhantomData, ops::Add}; use manta_crypto::{ - accumulator::Verifier, - commitment::CommitmentScheme, + accumulator::{Accumulator, MembershipProof, Verifier}, + commitment::{CommitmentScheme, Input as CommitmentInput}, constraint::{ reflection::{HasEqual, HasVariable, Var}, Allocation, Constant, ConstraintSystem, Derived, Equal, ProofSystem, Public, - PublicOrSecret, Variable, VariableSource, + PublicOrSecret, Secret, Variable, VariableSource, }, - encryption::{EncryptedMessage, HybridPublicKeyEncryptionScheme}, + encryption::{self, DecryptedMessage, EncryptedMessage, HybridPublicKeyEncryptionScheme}, key::KeyAgreementScheme, - rand::{CryptoRng, RngCore}, + rand::{CryptoRng, Rand, RngCore}, }; use manta_util::create_seal; @@ -49,121 +46,128 @@ pub const fn has_no_public_participants( } /// Transfer Configuration -pub trait Configuration: identity::Configuration<Asset = Asset> { - /// Encryption Scheme Type - type EncryptionScheme: HybridPublicKeyEncryptionScheme< - Plaintext = Self::Asset, - KeyAgreementScheme = Self::KeyAgreementScheme, - >; +pub trait Configuration { + /// Key Agreement Scheme + type KeyAgreementScheme: KeyAgreementScheme; + + /// Secret Key Variable + type SecretKeyVar: Variable<Self::ConstraintSystem, Type = SecretKey<Self>, Mode = Secret>; + + /// Public Key Variable + type PublicKeyVar: Variable<Self::ConstraintSystem, Type = PublicKey<Self>, Mode = Secret>; + + /// Key Agreement Scheme Variable + type KeyAgreementSchemeVar: KeyAgreementScheme<SecretKey = Self::SecretKeyVar, PublicKey = Self::PublicKeyVar> + + Variable<Self::ConstraintSystem, Type = Self::KeyAgreementScheme, Mode = Constant>; + + /// Commitment Scheme Output + type CommitmentSchemeOutput: PartialEq; + + /// Commitment Scheme Output Variable + type CommitmentSchemeOutputVar: Variable<Self::ConstraintSystem, Type = Self::CommitmentSchemeOutput, Mode = PublicOrSecret> + + Equal<Self::ConstraintSystem>; + + /// Commitment Scheme + type CommitmentScheme: CommitmentScheme<Trapdoor = SharedSecret<Self>, Output = Self::CommitmentSchemeOutput> + + CommitmentInput<Asset> + + CommitmentInput<SecretKey<Self>>; + + /// Commitment Scheme Variable + type CommitmentSchemeVar: CommitmentScheme<Trapdoor = SharedSecretVar<Self>, Output = Self::CommitmentSchemeOutputVar> + + CommitmentInput<AssetVar<Self>> + + CommitmentInput<Self::SecretKeyVar> + + Variable<Self::ConstraintSystem, Type = Self::CommitmentScheme, Mode = Constant>; - /// UTXO Set Verifier Type + /// UTXO Set Verifier type UtxoSetVerifier: Verifier<Item = Utxo<Self>, Verification = bool>; + /// UTXO Set Verifier Variable + type UtxoSetVerifierVar: Verifier< + Item = UtxoVar<Self>, + Verification = <Self::ConstraintSystem as ConstraintSystem>::Bool, + > + Variable<Self::ConstraintSystem, Type = Self::UtxoSetVerifier, Mode = Constant>; + + /// Asset Id Variable + type AssetIdVar: Variable<Self::ConstraintSystem, Type = AssetId, Mode = PublicOrSecret> + + Equal<Self::ConstraintSystem>; + + /// Asset Value Variable + type AssetValueVar: Variable<Self::ConstraintSystem, Type = AssetValue, Mode = PublicOrSecret> + + Equal<Self::ConstraintSystem> + + Add<Output = Self::AssetValueVar>; + /// Constraint System Type - type ConstraintSystem: ConstraintSystem - + HasVariable< - Self::KeyAgreementScheme, - Variable = KeyAgreementSchemeVar<Self>, - Mode = Constant, - > + HasVariable<Self::CommitmentScheme, Variable = CommitmentSchemeVar<Self>, Mode = Constant> - + HasVariable<Self::UtxoSetVerifier, Variable = UtxoSetVerifierVar<Self>, Mode = Constant> - + HasVariable<AssetId, Variable = AssetIdVar<Self>, Mode = PublicOrSecret> - + HasVariable<AssetValue, Variable = AssetValueVar<Self>, Mode = PublicOrSecret> - + HasVariable< - Commitment<Self>, - Variable = Commitment<Self::ConstraintConfiguration>, - Mode = PublicOrSecret, - > + HasEqual<Commitment<Self::ConstraintConfiguration>>; - - /// Constraint System Configuration - type ConstraintConfiguration: ConstraintConfiguration<Self::ConstraintSystem>; + type ConstraintSystem: ConstraintSystem; /// Proof System Type type ProofSystem: ProofSystem<ConstraintSystem = Self::ConstraintSystem, Verification = bool>; -} - -/// Transfer Constraint System Configuration -pub trait ConstraintConfiguration<CS>: - identity::Configuration<Asset = Asset<Self::AssetId, Self::AssetValue>> -where - CS: ConstraintSystem, -{ - /// Asset Id Variable Type - type AssetId: Variable<CS, Type = AssetId, Mode = PublicOrSecret> + Equal<CS>; - - /// Asset Value Variable Type - type AssetValue: Variable<CS, Type = AssetValue, Mode = PublicOrSecret> - + Equal<CS> - + Add<Output = Self::AssetValue>; - /// UTXO Set Verifier Variable Type - type UtxoSetVerifier: Verifier<Item = Utxo<Self>, Verification = CS::Bool>; + /// Note Encryption Scheme Type + type NoteEncryptionScheme: HybridPublicKeyEncryptionScheme< + Plaintext = Asset, + KeyAgreementScheme = Self::KeyAgreementScheme, + >; } -/// Spending Key Type -pub type SpendingKey<C> = identity::SpendingKey<<C as identity::Configuration>::KeyAgreementScheme>; +/// +pub type AssetVar<C> = Asset<<C as Configuration>::AssetIdVar, <C as Configuration>::AssetValueVar>; -/// Receiving Key Type -pub type ReceivingKey<C> = - identity::ReceivingKey<<C as identity::Configuration>::KeyAgreementScheme>; +/// +pub type SecretKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::SecretKey; -/// Encrypted Note Type -pub type EncryptedNote<C> = EncryptedMessage<<C as Configuration>::EncryptionScheme>; +/// +pub type PublicKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::PublicKey; + +/// +pub type SharedSecret<C> = + <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret; -/// Constraint System Type -type ConstraintSystemType<C> = <C as Configuration>::ConstraintSystem; +/// +pub type SharedSecretVar<C> = + <<C as Configuration>::KeyAgreementSchemeVar as KeyAgreementScheme>::SharedSecret; -/// Constraint Configuration Type -type ConstraintConfigurationType<C> = <C as Configuration>::ConstraintConfiguration; +/// +pub type Trapdoor<C> = <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Trapdoor; -/// Pre-Sender Type -pub type PreSender<C> = identity::PreSender<C>; +/// +pub type TrapdoorVar<C> = <<C as Configuration>::CommitmentSchemeVar as CommitmentScheme>::Trapdoor; -/// Sender Type -pub type Sender<C> = identity::Sender<C, <C as Configuration>::UtxoSetVerifier>; +/// +pub type CommitmentSchemeOutput<C> = <C as Configuration>::CommitmentSchemeOutput; -/// Sender Variable Type -type SenderVar<C> = identity::Sender< - ConstraintConfigurationType<C>, - <ConstraintConfigurationType<C> as ConstraintConfiguration<ConstraintSystemType<C>>>::UtxoSetVerifier, ->; +/// +pub type CommitmentSchemeOutputVar<C> = <C as Configuration>::CommitmentSchemeOutputVar; -/// Sender Post Type -pub type SenderPost<C> = identity::SenderPost<C, <C as Configuration>::UtxoSetVerifier>; +/// +pub type Utxo<C> = CommitmentSchemeOutput<C>; -/// Receiver Type -pub type Receiver<C> = identity::Receiver<C>; +/// +pub type UtxoVar<C> = CommitmentSchemeOutputVar<C>; -/// Receiver Variable Type -type ReceiverVar<C> = identity::Receiver<<C as Configuration>::ConstraintConfiguration>; +/// +pub type UtxoAccumulatorOutput<C> = <<C as Configuration>::UtxoSetVerifier as Verifier>::Output; -/// Full Receiver Type -pub type FullReceiver<C> = identity::FullReceiver<C>; +/// +pub type UtxoAccumulatorOutputVar<C> = + <<C as Configuration>::UtxoSetVerifierVar as Verifier>::Output; -/// Receiver Post Type -pub type ReceiverPost<C> = identity::ReceiverPost<C, <C as Configuration>::EncryptionScheme>; +/// +pub type UtxoMembershipProof<C> = MembershipProof<<C as Configuration>::UtxoSetVerifier>; -/// Asset Id Variable Type -pub type AssetIdVar<C> = - <ConstraintConfigurationType<C> as ConstraintConfiguration<ConstraintSystemType<C>>>::AssetId; +/// +pub type UtxoMembershipProofVar<C> = MembershipProof<<C as Configuration>::UtxoSetVerifierVar>; -/// Asset Value Variable Type -pub type AssetValueVar<C> = <ConstraintConfigurationType<C> as ConstraintConfiguration< - ConstraintSystemType<C>, ->>::AssetValue; +/// +pub type VoidNumber<C> = CommitmentSchemeOutput<C>; -/// Key Agreement Scheme Variable Type -pub type KeyAgreementSchemeVar<C> = - <ConstraintConfigurationType<C> as identity::Configuration>::KeyAgreementScheme; +/// +pub type VoidNumberVar<C> = CommitmentSchemeOutputVar<C>; -/// Commitment Scheme Variable Type -pub type CommitmentSchemeVar<C> = - <ConstraintConfigurationType<C> as identity::Configuration>::CommitmentScheme; +/// +pub type EncryptedNote<C> = EncryptedMessage<<C as Configuration>::NoteEncryptionScheme>; -/// UTXO Set Verifier Variable Type -pub type UtxoSetVerifierVar<C> = <ConstraintConfigurationType<C> as ConstraintConfiguration< - ConstraintSystemType<C>, ->>::UtxoSetVerifier; +/// +pub type Note<C> = DecryptedMessage<<C as Configuration>::NoteEncryptionScheme>; /// Transfer Proof System Type type ProofSystemType<C> = <C as Configuration>::ProofSystem; @@ -180,39 +184,689 @@ pub type VerifyingContext<C> = <ProofSystemType<C> as ProofSystem>::VerifyingCon /// Transfer Validity Proof Type pub type Proof<C> = <ProofSystemType<C> as ProofSystem>::Proof; -/* TODO: +/// Spending Key +pub struct SpendingKey<C> +where + C: Configuration, +{ + /// Spend Part of the Spending Key + spend: SecretKey<C>, + + /// View Part of the Spending Key + view: SecretKey<C>, +} + +impl<C> SpendingKey<C> +where + C: Configuration, +{ + /// Builds a new [`SpendingKey`] from `spend` and `view`. + #[inline] + pub fn new(spend: SecretKey<C>, view: SecretKey<C>) -> Self { + Self { spend, view } + } + + /// Derives the receiving key for `self`. + #[inline] + pub fn derive(&self) -> ReceivingKey<C> { + ReceivingKey { + spend: C::KeyAgreementScheme::derive(&self.spend), + view: C::KeyAgreementScheme::derive(&self.view), + } + } + + /// Tries to decrypt `encrypted_note` with the viewing key associated to `self`. + #[inline] + pub fn decrypt(&self, encrypted_note: EncryptedNote<C>) -> Result<Note<C>, EncryptedNote<C>> { + encrypted_note.decrypt(&self.view) + } + + /// Validates the `utxo` against `self` and the given `ephemeral_key` and `asset`. + #[inline] + pub fn validate_utxo( + &self, + ephemeral_key: &PublicKey<C>, + asset: &Asset, + utxo: &Utxo<C>, + commitment_scheme: &C::CommitmentScheme, + ) -> bool { + &commitment_scheme.commit_one( + asset, + &C::KeyAgreementScheme::agree(&self.spend, ephemeral_key), + ) == utxo + } + + /// Prepares `self` for spending `asset` with the given `ephemeral_key`. + #[inline] + pub fn sender( + &self, + ephemeral_key: PublicKey<C>, + asset: Asset, + commitment_scheme: &C::CommitmentScheme, + ) -> PreSender<C> + where + SecretKey<C>: Clone, + { + PreSender::new(self.spend.clone(), ephemeral_key, asset, commitment_scheme) + } + + /// Prepares `self` for receiving `asset`. + #[inline] + pub fn receiver(&self, asset: Asset) -> PreReceiver<C> { + self.derive().into_receiver(asset) + } +} + +/// Receiving Key +pub struct ReceivingKey<C> +where + C: Configuration, +{ + /// Spend Part of the Receiving Key + pub spend: PublicKey<C>, + + /// View Part of the Receiving Key + pub view: PublicKey<C>, +} + +impl<C> ReceivingKey<C> +where + C: Configuration, +{ + /// Prepares `self` for receiving `asset`. + #[inline] + pub fn into_receiver(self, asset: Asset) -> PreReceiver<C> { + PreReceiver::new(self.spend, self.view, asset) + } +} + +/// Pre-Sender +pub struct PreSender<C> +where + C: Configuration, +{ + /// Secret Spend Key + spend: SecretKey<C>, + + /// Ephemeral Public Spend Key + ephemeral_key: PublicKey<C>, + + /// Asset + asset: Asset, + + /// Unspent Transaction Output + utxo: Utxo<C>, + + /// Void Number + void_number: VoidNumber<C>, +} + +impl<C> PreSender<C> +where + C: Configuration, +{ + /// Builds a new [`PreSender`] for `spend` to spend `asset` with `ephemeral_key`. + #[inline] + pub fn new( + spend: SecretKey<C>, + ephemeral_key: PublicKey<C>, + asset: Asset, + commitment_scheme: &C::CommitmentScheme, + ) -> Self { + let trapdoor = C::KeyAgreementScheme::agree(&spend, &ephemeral_key); + Self { + utxo: commitment_scheme.commit_one(&asset, &trapdoor), + void_number: commitment_scheme.commit_one(&spend, &trapdoor), + spend, + ephemeral_key, + asset, + } + } + + /// Inserts the [`Utxo`] corresponding to `self` into the `utxo_set` with the intention of + /// returning a proof later by a call to [`get_proof`](Self::get_proof). + #[inline] + pub fn insert_utxo<S>(&self, utxo_set: &mut S) -> bool + where + S: Accumulator<Item = Utxo<C>, Verifier = C::UtxoSetVerifier>, + { + utxo_set.insert(&self.utxo) + } + + /// Requests the membership proof of the [`Utxo`] corresponding to `self` from `utxo_set` to + /// prepare the conversion from `self` into a [`Sender`]. + #[inline] + pub fn get_proof<S>(&self, utxo_set: &S) -> Option<SenderProof<C>> + where + S: Accumulator<Item = Utxo<C>, Verifier = C::UtxoSetVerifier>, + { + Some(SenderProof { + utxo_membership_proof: utxo_set.prove(&self.utxo)?, + }) + } + + /// Converts `self` into a [`Sender`] by attaching `proof` to it. + /// + /// # Note + /// + /// When using this method, be sure to check that [`SenderProof::can_upgrade`] returns `true`. + /// Otherwise, using the sender returned here will most likely return an error when posting to + /// the ledger. + #[inline] + pub fn upgrade(self, proof: SenderProof<C>) -> Sender<C> { + Sender { + spend: self.spend, + ephemeral_key: self.ephemeral_key, + asset: self.asset, + utxo: self.utxo, + utxo_membership_proof: proof.utxo_membership_proof, + void_number: self.void_number, + } + } + + /// Tries to convert `self` into a [`Sender`] by getting a proof from `utxo_set`. + #[inline] + pub fn try_upgrade<S>(self, utxo_set: &S) -> Option<Sender<C>> + where + S: Accumulator<Item = Utxo<C>, Verifier = C::UtxoSetVerifier>, + { + Some(self.get_proof(utxo_set)?.upgrade(self)) + } +} + +/// Sender Proof /// -pub struct Transfer2< - C, - const SOURCES: usize, - const SENDERS: usize, - const RECEIVERS: usize, - const SINKS: usize, -> where +/// This `struct` is created by the [`get_proof`](PreSender::get_proof) method on [`PreSender`]. +/// See its documentation for more. +pub struct SenderProof<C> +where C: Configuration, { - /// Asset Id - asset_id: Option<AssetId>, + /// UTXO Membership Proof + utxo_membership_proof: UtxoMembershipProof<C>, +} + +impl<C> SenderProof<C> +where + C: Configuration, +{ + /// Returns `true` if a [`PreSender`] could be upgraded using `self` given the `utxo_set`. + #[inline] + pub fn can_upgrade<S>(&self, utxo_set: &S) -> bool + where + S: Accumulator<Item = Utxo<C>, Verifier = C::UtxoSetVerifier>, + { + self.utxo_membership_proof.matching_output(utxo_set) + } + + /// Upgrades the `pre_sender` to a [`Sender`] by attaching `self` to it. + /// + /// # Note + /// + /// When using this method, be sure to check that [`can_upgrade`](Self::can_upgrade) returns + /// `true`. Otherwise, using the sender returned here will most likely return an error when + /// posting to the ledger. + #[inline] + pub fn upgrade(self, pre_sender: PreSender<C>) -> Sender<C> { + pre_sender.upgrade(self) + } +} + +/// Sender +pub struct Sender<C> +where + C: Configuration, +{ + /// Secret Spend Key + spend: SecretKey<C>, + + /// Ephemeral Public Spend Key + ephemeral_key: PublicKey<C>, + + /// Asset + asset: Asset, + + /// Unspent Transaction Output + utxo: Utxo<C>, + + /// UTXO Membership Proof + utxo_membership_proof: UtxoMembershipProof<C>, + + /// Void Number + void_number: VoidNumber<C>, +} + +impl<C> Sender<C> +where + C: Configuration, +{ + /// Reverts `self` back into a [`PreSender`]. + /// + /// This method should be called if the [`Utxo`] membership proof attached to `self` was deemed + /// invalid or had expired. + #[inline] + pub fn downgrade(self) -> PreSender<C> { + PreSender { + spend: self.spend, + ephemeral_key: self.ephemeral_key, + asset: self.asset, + utxo: self.utxo, + void_number: self.void_number, + } + } + + /* TODO: + /// Returns the asset for `self`, checking if `self` is well-formed in the given constraint + /// system `cs`. + #[inline] + pub fn get_well_formed_asset<CS>( + self, + commitment_scheme: &C::CommitmentScheme, + utxo_set_verifier: &V, + cs: &mut CS, + ) -> C::Asset + where + CS: ConstraintSystem, + Commitment<C>: Equal<CS>, + V: Verifier<Verification = CS::Bool>, + { + let trapdoor = C::into_trapdoor(C::KeyAgreementScheme::agree( + &self.spend, + &self.ephemeral_key, + )); + cs.assert(self.utxo_membership_proof.verify( + &commitment_scheme.commit_one(&self.asset, &trapdoor), + utxo_set_verifier, + )); + cs.assert_eq( + &self.void_number, + &commitment_scheme.commit_one(&self.spend, &trapdoor), + ); + self.asset + } + */ + + /// Extracts the ledger posting data from `self`. + #[inline] + pub fn into_post(self) -> SenderPost<C> { + SenderPost { + utxo_accumulator_output: self.utxo_membership_proof.into_output(), + void_number: self.void_number, + } + } +} + +/// Sender Ledger +pub trait SenderLedger<C> +where + C: Configuration, +{ + /// Valid [`VoidNumber`] Posting Key + /// + /// # Safety + /// + /// This type must be some wrapper around [`VoidNumber`] which can only be constructed by this + /// implementation of [`SenderLedger`]. This is to prevent that [`spend`](Self::spend) is + /// called before [`is_unspent`](Self::is_unspent) and + /// [`has_matching_utxo_accumulator_output`](Self::has_matching_utxo_accumulator_output). + type ValidVoidNumber; + + /// Valid UTXO Accumulator Output Posting Key + /// + /// # Safety + /// + /// This type must be some wrapper around [`S::Output`] which can only be constructed by this + /// implementation of [`SenderLedger`]. This is to prevent that [`spend`](Self::spend) is + /// called before [`is_unspent`](Self::is_unspent) and + /// [`has_matching_utxo_accumulator_output`](Self::has_matching_utxo_accumulator_output). + /// + /// [`S::Output`]: Verifier::Output + type ValidUtxoAccumulatorOutput; + + /// Super Posting Key + /// + /// Type that allows super-traits of [`SenderLedger`] to customize posting key behavior. + type SuperPostingKey: Copy; + + /// Checks if the ledger already contains the `void_number` in its set of void numbers. + /// + /// Existence of such a void number could indicate a possible double-spend. + fn is_unspent(&self, void_number: VoidNumber<C>) -> Option<Self::ValidVoidNumber>; + + /// Checks if `output` matches the current accumulated value of the UTXO set that is stored on + /// the ledger. + /// + /// Failure to match the ledger state means that the sender was constructed under an invalid or + /// older state of the ledger. + fn has_matching_utxo_accumulator_output( + &self, + output: UtxoAccumulatorOutput<C>, + ) -> Option<Self::ValidUtxoAccumulatorOutput>; + + /// Posts the `void_number` to the ledger, spending the asset. + /// + /// # Safety + /// + /// This method can only be called once we check that `void_number` is not already stored on + /// the ledger. See [`is_unspent`](Self::is_unspent). + fn spend( + &mut self, + utxo_accumulator_output: Self::ValidUtxoAccumulatorOutput, + void_number: Self::ValidVoidNumber, + super_key: &Self::SuperPostingKey, + ); +} + +/// Sender Post Error +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum SenderPostError { + /// Asset Spent Error + /// + /// The asset has already been spent. + AssetSpent, + + /// Invalid UTXO Accumulator Error + /// + /// The sender was not constructed under the current state of the UTXO set. + InvalidUtxoAccumulator, +} + +/// Sender Post +pub struct SenderPost<C> +where + C: Configuration, +{ + /// UTXO Accumulator Output + utxo_accumulator_output: UtxoAccumulatorOutput<C>, + + /// Void Number + void_number: VoidNumber<C>, +} + +impl<C> SenderPost<C> +where + C: Configuration, +{ + /// Validates `self` on the sender `ledger`. + #[inline] + pub fn validate<L>(self, ledger: &L) -> Result<SenderPostingKey<C, L>, SenderPostError> + where + L: SenderLedger<C>, + { + Ok(SenderPostingKey { + utxo_accumulator_output: ledger + .has_matching_utxo_accumulator_output(self.utxo_accumulator_output) + .ok_or(SenderPostError::InvalidUtxoAccumulator)?, + void_number: ledger + .is_unspent(self.void_number) + .ok_or(SenderPostError::AssetSpent)?, + }) + } +} + +/// Sender Posting Key +pub struct SenderPostingKey<C, L> +where + C: Configuration, + L: SenderLedger<C>, +{ + /// UTXO Accumulator Output Posting Key + utxo_accumulator_output: L::ValidUtxoAccumulatorOutput, + + /// Void Number Posting Key + void_number: L::ValidVoidNumber, +} + +impl<C, L> SenderPostingKey<C, L> +where + C: Configuration, + L: SenderLedger<C>, +{ + /// Posts `self` to the sender `ledger`. + #[inline] + pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) { + ledger.spend(self.utxo_accumulator_output, self.void_number, super_key); + } +} + +/// Pre-Receiver +pub struct PreReceiver<C> +where + C: Configuration, +{ + /// Public Spend Key + spend: PublicKey<C>, + + /// Public View Key + view: PublicKey<C>, + + /// Asset + asset: Asset, +} + +impl<C> PreReceiver<C> +where + C: Configuration, +{ + /// Builds a new [`PreReceiver`] for `spend` to receive `asset`, encrypted with `view`. + #[inline] + pub fn new(spend: PublicKey<C>, view: PublicKey<C>, asset: Asset) -> Self { + Self { spend, view, asset } + } + + /// Upgrades `self` into a [`Receiver`] with the designated `ephemeral_key`. + #[inline] + pub fn upgrade( + self, + ephemeral_key: SecretKey<C>, + commitment_scheme: &C::CommitmentScheme, + ) -> Receiver<C> { + Receiver::new( + self.spend, + self.view, + ephemeral_key, + self.asset, + commitment_scheme, + ) + } +} + +/// Receiver +pub struct Receiver<C> +where + C: Configuration, +{ + /// Public Spend Key + spend: PublicKey<C>, + + /// Public View Key + view: PublicKey<C>, + + /// Ephemeral Secret Spend Key + ephemeral_key: SecretKey<C>, + + /// Asset + asset: Asset, + + /// Unspent Transaction Output + utxo: Utxo<C>, +} + +impl<C> Receiver<C> +where + C: Configuration, +{ + /// Builds a new [`Receiver`] for `spend` to receive `asset` with `ephemeral_key`. + #[inline] + pub fn new( + spend: PublicKey<C>, + view: PublicKey<C>, + ephemeral_key: SecretKey<C>, + asset: Asset, + commitment_scheme: &C::CommitmentScheme, + ) -> Self { + Self { + utxo: commitment_scheme.commit_one( + &asset, + &C::KeyAgreementScheme::agree(&ephemeral_key, &spend), + ), + spend, + view, + ephemeral_key, + asset, + } + } + + /* TODO: + /// Returns the asset for `self`, checking if `self` is well-formed in the given constraint + /// system `cs`. + #[inline] + pub fn get_well_formed_asset<CS>( + self, + commitment_scheme: &C::CommitmentScheme, + cs: &mut CS, + ) -> C::Asset + where + CS: ConstraintSystem, + Utxo<C>: Equal<CS>, + { + cs.assert_eq( + &self.utxo, + &Self::generate_utxo( + &self.spending_key, + &self.ephemeral_key, + &self.asset, + commitment_scheme, + ), + ); + self.asset + } + */ + + /// Converts `self` into its [`PreReceiver`], dropping the ephemeral key. + #[inline] + pub fn downgrade(self) -> PreReceiver<C> { + PreReceiver::new(self.spend, self.view, self.asset) + } + + /// Extracts the ledger posting data from `self`. + #[inline] + pub fn into_post(self) -> ReceiverPost<C> { + ReceiverPost { + utxo: self.utxo, + note: EncryptedMessage::new(&self.view, self.ephemeral_key, self.asset), + } + } +} + +/// Receiver Ledger +pub trait ReceiverLedger<C> +where + C: Configuration, +{ + /// Valid [`Utxo`] Posting Key + /// + /// # Safety + /// + /// This type must be some wrapper around [`Utxo`] which can only be constructed by this + /// implementation of [`ReceiverLedger`]. This is to prevent that [`register`](Self::register) + /// is called before [`is_not_registered`](Self::is_not_registered). + type ValidUtxo; + + /// Super Posting Key + /// + /// Type that allows super-traits of [`ReceiverLedger`] to customize posting key behavior. + type SuperPostingKey: Copy; + + /// Checks if the ledger already contains the `utxo` in its set of UTXOs. + /// + /// Existence of such a UTXO could indicate a possible double-spend. + fn is_not_registered(&self, utxo: Utxo<C>) -> Option<Self::ValidUtxo>; + + /// Posts the `utxo` and `encrypted_asset` to the ledger, registering the asset. + /// + /// # Safety + /// + /// This method can only be called once we check that `utxo` is not already stored on the + /// ledger. See [`is_not_registered`](Self::is_not_registered). + fn register( + &mut self, + utxo: Self::ValidUtxo, + note: EncryptedNote<C>, + super_key: &Self::SuperPostingKey, + ); +} + +/// Receiver Post Error +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum ReceiverPostError { + /// Asset Registered Error + /// + /// The asset has already been registered with the ledger. + AssetRegistered, +} + +/// Receiver Post +pub struct ReceiverPost<C> +where + C: Configuration, +{ + /// Unspent Transaction Output + utxo: Utxo<C>, - /// Sources - sources: [AssetValue; SOURCES], + /// Encrypted Note + note: EncryptedNote<C>, +} - /// Senders - senders: [Sender<C>; SENDERS], +impl<C> ReceiverPost<C> +where + C: Configuration, +{ + /// Returns the ephemeral key associated to `self`. + #[inline] + pub fn ephemeral_key(&self) -> &PublicKey<C> { + self.note.ephemeral_public_key() + } - /// Receivers - receivers: [FullReceiver<C>; RECEIVERS], + /// Validates `self` on the receiver `ledger`. + #[inline] + pub fn validate<L>(self, ledger: &L) -> Result<ReceiverPostingKey<C, L>, ReceiverPostError> + where + L: ReceiverLedger<C>, + { + Ok(ReceiverPostingKey { + utxo: ledger + .is_not_registered(self.utxo) + .ok_or(ReceiverPostError::AssetRegistered)?, + note: self.note, + }) + } +} - /// Sinks - sinks: [AssetValue; SINKS], +/// Receiver Posting Key +pub struct ReceiverPostingKey<C, L> +where + C: Configuration, + L: ReceiverLedger<C>, +{ + /// UTXO Posting Key + utxo: L::ValidUtxo, - /// Fairness Constant Trapdoor - fair_trapdoor: Trapdoor<C>, + /// Encrypted Note + note: EncryptedNote<C>, +} - /// Fairness Constant - fair: Commitment<C>, +impl<C, L> ReceiverPostingKey<C, L> +where + C: Configuration, + L: ReceiverLedger<C>, +{ + /// Posts `self` to the receiver `ledger`. + #[inline] + pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) { + ledger.register(self.utxo, self.note, super_key); + } } -*/ /// Transfer pub struct Transfer< @@ -321,103 +975,6 @@ where } } - /// Generates the unknown variables for the transfer validity proof. - #[inline] - fn unknown_variables( - commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &C::UtxoSetVerifier, - cs: &mut C::ConstraintSystem, - ) -> ( - Option<AssetIdVar<C>>, - TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS>, - CommitmentSchemeVar<C>, - UtxoSetVerifierVar<C>, - ) { - let base_asset_id = if has_no_public_participants(SOURCES, SENDERS, RECEIVERS, SINKS) { - None - } else { - Some(AssetIdVar::<C>::new_unknown(cs, Public)) - }; - /* TODO: - ( - base_asset_id, - TransferParticipantsVar::new_unknown(cs, Derived), - commitment_scheme.as_known(cs, Public), - utxo_set_verifier.as_known(cs, Public), - ) - */ - todo!() - } - - /// Generates the known variables for the transfer validity proof. - #[inline] - fn known_variables( - &self, - commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &C::UtxoSetVerifier, - cs: &mut C::ConstraintSystem, - ) -> ( - Option<AssetIdVar<C>>, - TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS>, - CommitmentSchemeVar<C>, - UtxoSetVerifierVar<C>, - ) { - /* TODO: - ( - self.public.asset_id.map(|id| id.as_known(cs, Public)), - TransferParticipantsVar::new_known(cs, self, Derived), - commitment_scheme.as_known(cs, Public), - utxo_set_verifier.as_known(cs, Public), - ) - */ - todo!() - } - - /// Builds constraints for the transfer validity proof. - #[inline] - fn build_constraints( - base_asset_id: Option<AssetIdVar<C>>, - participants: TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS>, - commitment_scheme: CommitmentSchemeVar<C>, - utxo_set_verifier: UtxoSetVerifierVar<C>, - cs: &mut C::ConstraintSystem, - ) { - // FIXME: Add fair randomness constraint. - - let mut secret_asset_ids = Vec::with_capacity(SENDERS + RECEIVERS); - - let input_sum = participants - .senders - .into_iter() - .map(|s| { - let asset = s.get_well_formed_asset(&commitment_scheme, &utxo_set_verifier, cs); - secret_asset_ids.push(asset.id); - asset.value - }) - .chain(participants.sources) - .reduce(Add::add) - .unwrap(); - - let output_sum = participants - .receivers - .into_iter() - .map(|r| { - let asset = r.get_well_formed_asset(&commitment_scheme, cs); - secret_asset_ids.push(asset.id); - asset.value - }) - .chain(participants.sinks) - .reduce(Add::add) - .unwrap(); - - cs.assert_eq(&input_sum, &output_sum); - - match base_asset_id { - Some(asset_id) => cs.assert_all_eq_to_base(&asset_id, secret_asset_ids.iter()), - _ => cs.assert_all_eq(secret_asset_ids.iter()), - } - } - /// Generates the constraint system for an unknown transfer. #[inline] fn unknown_constraints( @@ -425,35 +982,12 @@ where utxo_set_verifier: &C::UtxoSetVerifier, ) -> C::ConstraintSystem { let mut cs = C::ProofSystem::for_unknown(); - let (base_asset_id, participants, commitment_scheme, utxo_set_verifier) = - Self::unknown_variables(commitment_scheme, utxo_set_verifier, &mut cs); - Self::build_constraints( - base_asset_id, - participants, - commitment_scheme, - utxo_set_verifier, - &mut cs, - ); - cs - } - - /// Generates the constraint system for a known transfer. - #[inline] - fn known_constraints( - &self, - commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &C::UtxoSetVerifier, - ) -> C::ConstraintSystem { - let mut cs = C::ProofSystem::for_known(); - let (base_asset_id, participants, commitment_scheme, utxo_set_verifier) = - self.known_variables(commitment_scheme, utxo_set_verifier, &mut cs); - Self::build_constraints( - base_asset_id, - participants, - commitment_scheme, - utxo_set_verifier, - &mut cs, - ); + TransferVar::<C, SOURCES, SENDERS, RECEIVERS, SINKS>::new_unknown(&mut cs, Derived) + .build_validity_constraints( + &commitment_scheme.as_known(&mut cs, Public), + &utxo_set_verifier.as_known(&mut cs, Public), + &mut cs, + ); cs } @@ -471,34 +1005,50 @@ where .generate_context::<C::ProofSystem, _>(rng) } - /// Generates a validity proof for this transfer. - #[inline] - fn is_valid<R>( - &self, - commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &C::UtxoSetVerifier, - context: &ProvingContext<C>, - rng: &mut R, - ) -> Result<Proof<C>, ProofSystemError<C>> - where - R: CryptoRng + RngCore + ?Sized, - { - self.known_constraints(commitment_scheme, utxo_set_verifier) - .prove::<C::ProofSystem, _>(context, rng) - } - /// Converts `self` into its ledger post. #[inline] - pub fn into_post<R>( + pub fn into_post<S, R>( self, commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &C::UtxoSetVerifier, + utxo_set: &S, + ledger_accumulator_output: UtxoAccumulatorOutput<C>, context: &ProvingContext<C>, rng: &mut R, ) -> Result<TransferPost<C>, ProofSystemError<C>> where + S: Accumulator<Item = Utxo<C>, Verifier = C::UtxoSetVerifier>, R: CryptoRng + RngCore + ?Sized, { + /* TODO: + if !utxo_set.matching_output(&ledger_accumulator_output) { + todo!("ERROR") + } + + let senders = IntoIterator::into_iter(self.senders) + .map(move |s| s.try_upgrade(utxo_set)) + .collect::<Option<Vec<_>>>() + .expect("TODO: deal with error."); + + let fair_trapdoor = rng.gen(); + let mut fair = commitment_scheme.start().update(&ledger_accumulator_output); + for s in &senders { + fair.update(&s.spend); + } + fair = fair.commit(&fair_trapdoor); + + let receivers = IntoIterator::into_iter(self.receivers) + .enumerate() + .map(move |(i, r)| { + let ephemeral_key = commitment_scheme + .start() + .update(&(i as u8)) + .update(&r.spend) + .commit(fair); + r.upgrade(ephemeral_key, commitment_scheme) + }) + .collect::<Vec<_>>(); + */ + /* TODO: Ok(TransferPost { validity_proof: self.is_valid(commitment_scheme, utxo_set_verifier, context, rng)?, @@ -517,8 +1067,8 @@ where } } -/// Transfer Participants Variable -struct TransferParticipantsVar< +/// Transfer Variable +pub struct TransferVar< C, const SOURCES: usize, const SENDERS: usize, @@ -527,21 +1077,83 @@ struct TransferParticipantsVar< > where C: Configuration, { - /// Source Variables - sources: Vec<AssetValueVar<C>>, + /// + asset_id: Option<C::AssetIdVar>, + + /// + sources: [C::AssetValueVar; SOURCES], + + /// + // TODO: senders: [SenderVar<C>; SENDERS], + + /// + // TODO: receivers: [ReceiverVar<C>; RECEIVERS], + + /// + sinks: [C::AssetValueVar; SINKS], + + /// + ledger_accumulator_output: UtxoAccumulatorOutputVar<C>, + + /// + fair_trapdoor: TrapdoorVar<C>, + + /// + fair: CommitmentSchemeOutputVar<C>, +} + +impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> + TransferVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> +where + C: Configuration, +{ + /// + #[inline] + fn build_validity_constraints( + self, + commitment_scheme: &C::CommitmentSchemeVar, + utxo_set_verifier: &C::UtxoSetVerifierVar, + cs: &mut C::ConstraintSystem, + ) { + let mut secret_asset_ids = Vec::with_capacity(SENDERS + RECEIVERS); - /// Sender Variables - senders: Vec<SenderVar<C>>, + /* TODO: + let input_sum = self + .senders + .into_iter() + .map(|s| { + let asset = s.get_well_formed_asset(&commitment_scheme, &utxo_set_verifier, cs); + secret_asset_ids.push(asset.id); + asset.value + }) + .chain(self.sources) + .reduce(Add::add) + .unwrap(); + + let output_sum = self + .receivers + .into_iter() + .map(|r| { + let asset = r.get_well_formed_asset(&commitment_scheme, cs); + secret_asset_ids.push(asset.id); + asset.value + }) + .chain(self.sinks) + .reduce(Add::add) + .unwrap(); - /// Receiver Variables - receivers: Vec<ReceiverVar<C>>, + cs.assert_eq(&input_sum, &output_sum); + */ - /// Sink Variables - sinks: Vec<AssetValueVar<C>>, + match self.asset_id { + Some(asset_id) => cs.assert_all_eq_to_base(&asset_id, secret_asset_ids.iter()), + _ => cs.assert_all_eq(secret_asset_ids.iter()), + } + } } impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> - Variable<C::ConstraintSystem> for TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> + Variable<C::ConstraintSystem> for TransferVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> where C: Configuration, { @@ -551,6 +1163,7 @@ where #[inline] fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + /* TODO: match allocation { Allocation::Known(this, mode) => Self { sources: this @@ -605,8 +1218,67 @@ where .collect(), }, } + */ + todo!() + } +} + +/* +impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> + Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS> +where + C: Configuration, +{ + /// Generates the unknown variables for the transfer validity proof. + #[inline] + fn unknown_variables( + commitment_scheme: &C::CommitmentScheme, + utxo_set_verifier: &C::UtxoSetVerifier, + cs: &mut C::ConstraintSystem, + ) -> ( + Option<C::AssetIdVar>, + // TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS>, + C::CommitmentSchemeVar, + C::UtxoSetVerifierVar, + ) { + let base_asset_id = if has_no_public_participants(SOURCES, SENDERS, RECEIVERS, SINKS) { + None + } else { + Some(C::AssetIdVar::new_unknown(cs, Public)) + }; + ( + base_asset_id, + // TransferParticipantsVar::new_unknown(cs, Derived), + commitment_scheme.as_known(cs, Public), + utxo_set_verifier.as_known(cs, Public), + ) + } + + /// Generates the known variables for the transfer validity proof. + #[inline] + fn known_variables( + &self, + commitment_scheme: &C::CommitmentScheme, + utxo_set_verifier: &C::UtxoSetVerifier, + cs: &mut C::ConstraintSystem, + ) -> ( + Option<AssetIdVar<C>>, + TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS>, + CommitmentSchemeVar<C>, + UtxoSetVerifierVar<C>, + ) { + /* TODO: + ( + self.public.asset_id.map(|id| id.as_known(cs, Public)), + TransferParticipantsVar::new_known(cs, self, Derived), + commitment_scheme.as_known(cs, Public), + utxo_set_verifier.as_known(cs, Public), + ) + */ + todo!() } } +*/ /// Transfer Post pub struct TransferPost<C> @@ -628,6 +1300,9 @@ where /// Sinks sinks: Vec<AssetValue>, + /// Ledger Accumulator Output + ledger_accumulator_output: UtxoAccumulatorOutput<C>, + /// Validity Proof validity_proof: Proof<C>, } @@ -636,6 +1311,7 @@ impl<C> TransferPost<C> where C: Configuration, { + /* TODO: /// Returns the ephemeral keys associated to the receiver posts of `self`. #[inline] pub fn receiver_ephemeral_keys(&self) -> Vec<&PublicKey<C>> { @@ -644,6 +1320,7 @@ where .map(ReceiverPost::ephemeral_key) .collect() } + */ } create_seal! {} diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs index 58d53d835..53e1a3a2e 100644 --- a/manta-accounting/src/wallet/ledger.rs +++ b/manta-accounting/src/wallet/ledger.rs @@ -19,10 +19,7 @@ // TODO: Move to asynchronous streaming model so we can process some of the data as it is incoming. // TODO: Add non-atomic transactions. See similar comment in `crate::wallet::signer`. -use crate::{ - identity::{Utxo, VoidNumber}, - transfer::{Configuration, EncryptedNote, TransferPost}, -}; +use crate::transfer::{Configuration, EncryptedNote, TransferPost, Utxo, VoidNumber}; use alloc::vec::Vec; use core::future::Future; diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index ecf4f6ae6..f059d45f6 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -31,13 +31,13 @@ use crate::{ asset::{Asset, AssetId, AssetMap, AssetValue}, - identity::{self, PreSender, PublicKey, SecretKey, Utxo}, key::HierarchicalKeyTable, transfer::{ self, canonical::{Mint, PrivateTransfer, PrivateTransferShape, Reclaim, Transaction}, - EncryptedNote, FullReceiver, ProofSystemError, ProvingContext, Receiver, ReceivingKey, - Sender, Shape, SpendingKey, Transfer, TransferPost, + EncryptedNote, FullReceiver, PreSender, ProofSystemError, ProvingContext, PublicKey, + Receiver, ReceivingKey, SecretKey, Sender, Shape, SpendingKey, Transfer, TransferPost, + Utxo, }, }; use alloc::{vec, vec::Vec}; diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 2351d095c..25adb9cef 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -18,11 +18,10 @@ use crate::{ asset::{Asset, AssetId, AssetValue}, - identity::SecretKey, key::HierarchicalKeyTable, transfer::{ canonical::{Transaction, TransactionKind}, - Configuration, ReceivingKey, + Configuration, ReceivingKey, SecretKey, }, wallet::{ ledger::{self, Checkpoint, PullResponse, PushResponse}, diff --git a/manta-crypto/src/key.rs b/manta-crypto/src/key.rs index 0d826013a..9195b5aad 100644 --- a/manta-crypto/src/key.rs +++ b/manta-crypto/src/key.rs @@ -33,10 +33,10 @@ /// about the value of the [`SharedSecret`](Self::SharedSecret). pub trait KeyAgreementScheme { /// Secret Key Type - type SecretKey: Clone; + type SecretKey; /// Public Key Type - type PublicKey: Clone; + type PublicKey; /// Shared Secret Type type SharedSecret; From 7226055a2b9a99f1966e3d2c663924f01327f073 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 16 Nov 2021 01:29:41 -0500 Subject: [PATCH 128/275] wip: finish merging indentity into transfer --- manta-accounting/Cargo.toml | 2 +- manta-accounting/src/identity.rs | 888 ----------------------- manta-accounting/src/lib.rs | 3 +- manta-accounting/src/transfer.rs | 973 ++++++++++++++++++-------- manta-accounting/src/wallet/signer.rs | 21 +- manta-crypto/Cargo.toml | 3 - manta-crypto/src/accumulator.rs | 2 - manta-crypto/src/lib.rs | 5 +- 8 files changed, 711 insertions(+), 1186 deletions(-) delete mode 100644 manta-accounting/src/identity.rs diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 4b9fbc695..898e3e97a 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -41,7 +41,7 @@ test = [] cocoon = { version = "0.3.0", optional = true, default-features = false } derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } derive_more = { version = "0.99.16", default-features = false, features = ["add", "add_assign", "display", "from", "mul", "mul_assign", "sum"] } -manta-crypto = { path = "../manta-crypto", features = ["constraint"] } +manta-crypto = { path = "../manta-crypto" } manta-util = { path = "../manta-util" } zeroize = { version = "1.4.3", optional = true, default-features = false, features = ["alloc", "zeroize_derive"] } diff --git a/manta-accounting/src/identity.rs b/manta-accounting/src/identity.rs deleted file mode 100644 index 8799d29af..000000000 --- a/manta-accounting/src/identity.rs +++ /dev/null @@ -1,888 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Sender and Receiver Identities - -use core::marker::PhantomData; -use manta_crypto::{ - accumulator::{Accumulator, MembershipProof, Verifier}, - commitment::{CommitmentScheme, Input as CommitmentInput}, - constraint::{ - Allocation, ConstraintSystem, Derived, Equal, Public, PublicOrSecret, Secret, Variable, - VariableSource, - }, - encryption::{DecryptedMessage, EncryptedMessage, HybridPublicKeyEncryptionScheme}, - key::KeyAgreementScheme, -}; - -/// Spending Key -pub struct SpendingKey<K> -where - K: KeyAgreementScheme, -{ - /// Spend Part of the Spending Key - spend: K::SecretKey, - - /// View Part of the Spending Key - view: K::SecretKey, -} - -impl<K> SpendingKey<K> -where - K: KeyAgreementScheme, -{ - /// Builds a new [`SpendingKey`] from `spend` and `view`. - #[inline] - pub fn new(spend: K::SecretKey, view: K::SecretKey) -> Self { - Self { spend, view } - } - - /// Derives the receiving key for `self`. - #[inline] - pub fn derive(&self) -> ReceivingKey<K> { - ReceivingKey { - spend: K::derive(&self.spend), - view: K::derive(&self.view), - } - } - - /// Tries to decrypt `encrypted_note` with the viewing key associated to `self`. - #[inline] - pub fn decrypt<H>( - &self, - encrypted_note: EncryptedMessage<H>, - ) -> Result<DecryptedMessage<H>, EncryptedMessage<H>> - where - H: HybridPublicKeyEncryptionScheme<KeyAgreementScheme = K>, - { - encrypted_note.decrypt(&self.view) - } - - /// Validates the `utxo` against `self` and the given `ephemeral_key` and `asset`. - #[inline] - pub fn validate_utxo<C>( - &self, - ephemeral_key: &PublicKey<C>, - asset: &C::Asset, - utxo: &Utxo<C>, - commitment_scheme: &C::CommitmentScheme, - ) -> bool - where - C: Configuration<KeyAgreementScheme = K>, - Utxo<C>: PartialEq, - { - &commitment_scheme.commit_one( - asset, - &C::into_trapdoor(K::agree(&self.spend, ephemeral_key)), - ) == utxo - } - - /// Prepares `self` for spending `asset` with the given `ephemeral_key`. - #[inline] - pub fn sender<C>( - &self, - ephemeral_key: PublicKey<C>, - asset: C::Asset, - commitment_scheme: &C::CommitmentScheme, - ) -> PreSender<C> - where - K::SecretKey: Clone, - C: Configuration<KeyAgreementScheme = K>, - { - PreSender::new(self.spend.clone(), ephemeral_key, asset, commitment_scheme) - } - - /// Prepares `self` for receiving `asset`. - #[inline] - pub fn receiver<C>(&self, asset: C::Asset) -> PreReceiver<C> - where - C: Configuration<KeyAgreementScheme = K>, - { - self.derive().into_receiver(asset) - } -} - -/// Receiving Key -pub struct ReceivingKey<K> -where - K: KeyAgreementScheme, -{ - /// Spend Part of the Receiving Key - pub spend: K::PublicKey, - - /// View Part of the Receiving Key - pub view: K::PublicKey, -} - -impl<K> ReceivingKey<K> -where - K: KeyAgreementScheme, -{ - /// Prepares `self` for receiving `asset`. - #[inline] - pub fn into_receiver<C>(self, asset: C::Asset) -> PreReceiver<C> - where - C: Configuration<KeyAgreementScheme = K>, - { - PreReceiver::new(self.spend, self.view, asset) - } -} - -/// Identity Configuration -pub trait Configuration { - /// Asset Type - type Asset; - - /// Key Agreement Scheme Type - type KeyAgreementScheme: KeyAgreementScheme; - - /// Commitment Scheme Type - type CommitmentScheme: CommitmentScheme - + CommitmentInput<Self::Asset> - + CommitmentInput<SecretKey<Self>>; - - /// Converts the `shared_secret` returned by the key agreement scheme into a trapdoor for the - /// commitment scheme. - fn into_trapdoor(shared_secret: SharedSecret<Self>) -> Trapdoor<Self>; -} - -/// Secret Key Type -pub type SecretKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::SecretKey; - -/// Public Key Type -pub type PublicKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::PublicKey; - -/// Shared Secret Type -pub type SharedSecret<C> = - <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret; - -/// Trapdoor Type -pub type Trapdoor<C> = <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Trapdoor; - -/// Commitment Scheme Output Type -pub type Commitment<C> = <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Output; - -/// Unspent Transaction Output Type -pub type Utxo<C> = Commitment<C>; - -/// Void Number Type -pub type VoidNumber<C> = Commitment<C>; - -/// Pre-Sender -pub struct PreSender<C> -where - C: Configuration, -{ - /// Secret Spending Key - spending_key: SecretKey<C>, - - /// Ephemeral Public Key - ephemeral_key: PublicKey<C>, - - /// Asset - asset: C::Asset, - - /// Unspent Transaction Output - utxo: Utxo<C>, - - /// Void Number - void_number: VoidNumber<C>, -} - -impl<C> PreSender<C> -where - C: Configuration, -{ - /// Builds a new [`PreSender`] for `spending_key` to spend `asset` with - /// `ephemeral_key`. - #[inline] - pub fn new( - spending_key: SecretKey<C>, - ephemeral_key: PublicKey<C>, - asset: C::Asset, - commitment_scheme: &C::CommitmentScheme, - ) -> Self { - let trapdoor = - C::into_trapdoor(C::KeyAgreementScheme::agree(&spending_key, &ephemeral_key)); - Self { - utxo: commitment_scheme.commit_one(&asset, &trapdoor), - void_number: commitment_scheme.commit_one(&spending_key, &trapdoor), - spending_key, - ephemeral_key, - asset, - } - } - - /// Inserts the [`Utxo`] corresponding to `self` into the `utxo_set` with the intention of - /// returning a proof later by a call to [`get_proof`](Self::get_proof). - #[inline] - pub fn insert_utxo<S>(&self, utxo_set: &mut S) -> bool - where - S: Accumulator<Item = Utxo<C>>, - { - utxo_set.insert(&self.utxo) - } - - /// Requests the membership proof of the [`Utxo`] corresponding to `self` from `utxo_set` to - /// prepare the conversion from `self` into a [`Sender`]. - #[inline] - pub fn get_proof<S>(&self, utxo_set: &S) -> Option<SenderProof<C, S::Verifier>> - where - S: Accumulator<Item = Utxo<C>>, - { - Some(SenderProof { - utxo_membership_proof: utxo_set.prove(&self.utxo)?, - __: PhantomData, - }) - } - - /// Converts `self` into a [`Sender`] by attaching `proof` to it. - /// - /// # Note - /// - /// When using this method, be sure to check that [`SenderProof::can_upgrade`] returns `true`. - /// Otherwise, using the sender returned here will most likely return an error when posting to - /// the ledger. - #[inline] - pub fn upgrade<V>(self, proof: SenderProof<C, V>) -> Sender<C, V> - where - V: Verifier<Item = Utxo<C>> + ?Sized, - { - Sender { - spending_key: self.spending_key, - ephemeral_key: self.ephemeral_key, - asset: self.asset, - utxo_membership_proof: proof.utxo_membership_proof, - void_number: self.void_number, - } - } - - /// Tries to convert `self` into a [`Sender`] by getting a proof from `utxo_set`. - #[inline] - pub fn try_upgrade<S>(self, utxo_set: &S) -> Option<Sender<C, S::Verifier>> - where - S: Accumulator<Item = Utxo<C>>, - { - Some(self.get_proof(utxo_set)?.upgrade(self)) - } -} - -/// Sender Proof -/// -/// This `struct` is created by the [`get_proof`](PreSender::get_proof) method on [`PreSender`]. -/// See its documentation for more. -pub struct SenderProof<C, V> -where - C: Configuration, - V: Verifier<Item = Utxo<C>> + ?Sized, -{ - /// UTXO Membership Proof - utxo_membership_proof: MembershipProof<V>, - - /// Type Parameter Marker - __: PhantomData<C>, -} - -impl<C, V> SenderProof<C, V> -where - C: Configuration, - V: Verifier<Item = Utxo<C>> + ?Sized, -{ - /// Returns `true` if a [`PreSender`] could be upgraded using `self` given the `utxo_set`. - #[inline] - pub fn can_upgrade<S>(&self, utxo_set: &S) -> bool - where - S: Accumulator<Item = V::Item, Verifier = V>, - { - self.utxo_membership_proof.matching_output(utxo_set) - } - - /// Upgrades the `pre_sender` to a [`Sender`] by attaching `self` to it. - /// - /// # Note - /// - /// When using this method, be sure to check that [`can_upgrade`](Self::can_upgrade) returns - /// `true`. Otherwise, using the sender returned here will most likely return an error when - /// posting to the ledger. - #[inline] - pub fn upgrade(self, pre_sender: PreSender<C>) -> Sender<C, V> { - pre_sender.upgrade(self) - } -} - -/// Sender -pub struct Sender<C, V> -where - C: Configuration, - V: Verifier<Item = Utxo<C>> + ?Sized, -{ - /// Secret Spend Key - spending_key: SecretKey<C>, - - /// Ephemeral Public Key - ephemeral_key: PublicKey<C>, - - /// Asset - asset: C::Asset, - - /// UTXO Membership Proof - utxo_membership_proof: MembershipProof<V>, - - /// Void Number - void_number: VoidNumber<C>, -} - -impl<C, V> Sender<C, V> -where - C: Configuration, - V: Verifier<Item = Utxo<C>>, -{ - /// Reverts `self` back into a [`PreSender`]. - /// - /// This method should be called if the [`Utxo`] membership proof attached to `self` was deemed - /// invalid or had expired. - #[inline] - pub fn downgrade(self, commitment_scheme: &C::CommitmentScheme) -> PreSender<C> { - PreSender { - utxo: commitment_scheme.commit_one( - &self.asset, - &C::into_trapdoor(C::KeyAgreementScheme::agree( - &self.spending_key, - &self.ephemeral_key, - )), - ), - spending_key: self.spending_key, - ephemeral_key: self.ephemeral_key, - asset: self.asset, - void_number: self.void_number, - } - } - - /// Returns the asset for `self`, checking if `self` is well-formed in the given constraint - /// system `cs`. - #[inline] - pub fn get_well_formed_asset<CS>( - self, - commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &V, - cs: &mut CS, - ) -> C::Asset - where - CS: ConstraintSystem, - Commitment<C>: Equal<CS>, - V: Verifier<Verification = CS::Bool>, - { - let trapdoor = C::into_trapdoor(C::KeyAgreementScheme::agree( - &self.spending_key, - &self.ephemeral_key, - )); - cs.assert(self.utxo_membership_proof.verify( - &commitment_scheme.commit_one(&self.asset, &trapdoor), - utxo_set_verifier, - )); - cs.assert_eq( - &self.void_number, - &commitment_scheme.commit_one(&self.spending_key, &trapdoor), - ); - self.asset - } - - /// Extracts the ledger posting data from `self`. - #[inline] - pub fn into_post(self) -> SenderPost<C, V> { - SenderPost { - utxo_accumulator_output: self.utxo_membership_proof.into_output(), - void_number: self.void_number, - } - } -} - -/// Sender Ledger -pub trait SenderLedger<C, V> -where - C: Configuration, - V: Verifier<Item = Utxo<C>>, -{ - /// Valid [`VoidNumber`] Posting Key - /// - /// # Safety - /// - /// This type must be some wrapper around [`VoidNumber`] which can only be constructed by this - /// implementation of [`SenderLedger`]. This is to prevent that [`spend`](Self::spend) is - /// called before [`is_unspent`](Self::is_unspent) and - /// [`has_matching_utxo_accumulator_output`](Self::has_matching_utxo_accumulator_output). - type ValidVoidNumber; - - /// Valid UTXO Accumulator Output Posting Key - /// - /// # Safety - /// - /// This type must be some wrapper around [`S::Output`] which can only be constructed by this - /// implementation of [`SenderLedger`]. This is to prevent that [`spend`](Self::spend) is - /// called before [`is_unspent`](Self::is_unspent) and - /// [`has_matching_utxo_accumulator_output`](Self::has_matching_utxo_accumulator_output). - /// - /// [`S::Output`]: Verifier::Output - type ValidUtxoAccumulatorOutput; - - /// Super Posting Key - /// - /// Type that allows super-traits of [`SenderLedger`] to customize posting key behavior. - type SuperPostingKey: Copy; - - /// Checks if the ledger already contains the `void_number` in its set of void numbers. - /// - /// Existence of such a void number could indicate a possible double-spend. - fn is_unspent(&self, void_number: VoidNumber<C>) -> Option<Self::ValidVoidNumber>; - - /// Checks if `output` matches the current accumulated value of the UTXO set that is stored on - /// the ledger. - /// - /// Failure to match the ledger state means that the sender was constructed under an invalid or - /// older state of the ledger. - fn has_matching_utxo_accumulator_output( - &self, - output: V::Output, - ) -> Option<Self::ValidUtxoAccumulatorOutput>; - - /// Posts the `void_number` to the ledger, spending the asset. - /// - /// # Safety - /// - /// This method can only be called once we check that `void_number` is not already stored on - /// the ledger. See [`is_unspent`](Self::is_unspent). - fn spend( - &mut self, - utxo_accumulator_output: Self::ValidUtxoAccumulatorOutput, - void_number: Self::ValidVoidNumber, - super_key: &Self::SuperPostingKey, - ); -} - -/// Sender Post Error -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum SenderPostError { - /// Asset Spent Error - /// - /// The asset has already been spent. - AssetSpent, - - /// Invalid UTXO Accumulator Error - /// - /// The sender was not constructed under the current state of the UTXO set. - InvalidUtxoAccumulator, -} - -/// Sender Post -pub struct SenderPost<C, V> -where - C: Configuration, - V: Verifier<Item = Utxo<C>>, -{ - /// UTXO Accumulator Output - utxo_accumulator_output: V::Output, - - /// Void Number - void_number: VoidNumber<C>, -} - -impl<C, V> SenderPost<C, V> -where - C: Configuration, - V: Verifier<Item = Utxo<C>>, -{ - /// Validates `self` on the sender `ledger`. - #[inline] - pub fn validate<L>(self, ledger: &L) -> Result<SenderPostingKey<C, V, L>, SenderPostError> - where - L: SenderLedger<C, V>, - { - Ok(SenderPostingKey { - utxo_accumulator_output: ledger - .has_matching_utxo_accumulator_output(self.utxo_accumulator_output) - .ok_or(SenderPostError::InvalidUtxoAccumulator)?, - void_number: ledger - .is_unspent(self.void_number) - .ok_or(SenderPostError::AssetSpent)?, - }) - } -} - -/// Sender Posting Key -pub struct SenderPostingKey<C, V, L> -where - C: Configuration, - V: Verifier<Item = Utxo<C>>, - L: SenderLedger<C, V>, -{ - /// UTXO Accumulator Output Posting Key - utxo_accumulator_output: L::ValidUtxoAccumulatorOutput, - - /// Void Number Posting Key - void_number: L::ValidVoidNumber, -} - -impl<C, V, L> SenderPostingKey<C, V, L> -where - C: Configuration, - V: Verifier<Item = Utxo<C>>, - L: SenderLedger<C, V>, -{ - /// Posts `self` to the sender `ledger`. - #[inline] - pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) { - ledger.spend(self.utxo_accumulator_output, self.void_number, super_key); - } -} - -/// Pre-Receiver -pub struct PreReceiver<C> -where - C: Configuration, -{ - /// Public Spending Key - spending_key: PublicKey<C>, - - /// Public Viewing Key - viewing_key: PublicKey<C>, - - /// Asset - asset: C::Asset, -} - -impl<C> PreReceiver<C> -where - C: Configuration, -{ - /// Builds a new [`PreReceiver`] for `spending_key` to receive `asset`. - #[inline] - pub fn new(spending_key: PublicKey<C>, viewing_key: PublicKey<C>, asset: C::Asset) -> Self { - Self { - spending_key, - viewing_key, - asset, - } - } - - /// Upgrades `self` into a [`FullReceiver`] with the designated `ephemeral_key`. - #[inline] - pub fn upgrade( - self, - ephemeral_key: SecretKey<C>, - commitment_scheme: &C::CommitmentScheme, - ) -> FullReceiver<C> { - FullReceiver::new( - self.spending_key, - self.viewing_key, - ephemeral_key, - self.asset, - commitment_scheme, - ) - } -} - -/// Receiver -pub struct Receiver<C> -where - C: Configuration, -{ - /// Public Spending Key - spending_key: PublicKey<C>, - - /// Ephemeral Secret Key - ephemeral_key: SecretKey<C>, - - /// Asset - asset: C::Asset, - - /// Unspent Transaction Output - utxo: Utxo<C>, -} - -impl<C> Receiver<C> -where - C: Configuration, -{ - /// Generates the [`Utxo`] for a [`Receiver`] with the given parameters. - #[inline] - fn generate_utxo( - spending_key: &PublicKey<C>, - ephemeral_key: &SecretKey<C>, - asset: &C::Asset, - commitment_scheme: &C::CommitmentScheme, - ) -> Utxo<C> { - commitment_scheme.commit_one( - asset, - &C::into_trapdoor(C::KeyAgreementScheme::agree(ephemeral_key, spending_key)), - ) - } - - /// Returns the asset for `self`, checking if `self` is well-formed in the given constraint - /// system `cs`. - #[inline] - pub fn get_well_formed_asset<CS>( - self, - commitment_scheme: &C::CommitmentScheme, - cs: &mut CS, - ) -> C::Asset - where - CS: ConstraintSystem, - Utxo<C>: Equal<CS>, - { - cs.assert_eq( - &self.utxo, - &Self::generate_utxo( - &self.spending_key, - &self.ephemeral_key, - &self.asset, - commitment_scheme, - ), - ); - self.asset - } -} - -impl<CS, C> Variable<CS> for Receiver<C> -where - C: Configuration + Variable<CS>, - C::Type: Configuration, - PublicKey<C>: Variable<CS, Type = PublicKey<C::Type>, Mode = Secret>, - SecretKey<C>: Variable<CS, Type = SecretKey<C::Type>, Mode = Secret>, - C::Asset: Variable<CS, Type = <C::Type as Configuration>::Asset, Mode = Secret>, - Utxo<C>: Variable<CS, Type = Utxo<C::Type>, Mode = PublicOrSecret>, -{ - type Type = Receiver<C::Type>; - - type Mode = Derived; - - #[inline] - fn new(cs: &mut CS, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - match allocation { - Allocation::Known(this, mode) => Self { - spending_key: this.spending_key.as_known(cs, mode), - ephemeral_key: this.ephemeral_key.as_known(cs, mode), - asset: this.asset.as_known(cs, mode), - utxo: this.utxo.as_known(cs, Public), - }, - Allocation::Unknown(mode) => Self { - spending_key: PublicKey::<C>::new_unknown(cs, mode), - ephemeral_key: SecretKey::<C>::new_unknown(cs, mode), - asset: C::Asset::new_unknown(cs, mode), - utxo: Utxo::<C>::new_unknown(cs, Public), - }, - } - } -} - -/// Full Receiver -pub struct FullReceiver<C> -where - C: Configuration, -{ - /// Public Spending Key - spending_key: PublicKey<C>, - - /// Public Viewing Key - viewing_key: PublicKey<C>, - - /// Ephemeral Secret Key - ephemeral_key: SecretKey<C>, - - /// Asset - asset: C::Asset, - - /// Unspent Transaction Output - utxo: Utxo<C>, -} - -impl<C> FullReceiver<C> -where - C: Configuration, -{ - /// Builds a new [`Receiver`] for `spending_key` to receive `asset` with - /// `ephemeral_key`. - #[inline] - pub fn new( - spending_key: PublicKey<C>, - viewing_key: PublicKey<C>, - ephemeral_key: SecretKey<C>, - asset: C::Asset, - commitment_scheme: &C::CommitmentScheme, - ) -> Self { - Self { - utxo: Receiver::<C>::generate_utxo( - &spending_key, - &ephemeral_key, - &asset, - commitment_scheme, - ), - spending_key, - viewing_key, - ephemeral_key, - asset, - } - } - - /// Converts `self` into its [`PreReceiver`], dropping the ephemeral key. - #[inline] - pub fn downgrade(self) -> PreReceiver<C> { - PreReceiver::new(self.spending_key, self.viewing_key, self.asset) - } - - /// Extracts the ledger posting data from `self`. - #[inline] - pub fn into_post<H>(self) -> ReceiverPost<C, H> - where - H: HybridPublicKeyEncryptionScheme< - Plaintext = C::Asset, - KeyAgreementScheme = C::KeyAgreementScheme, - >, - { - ReceiverPost { - utxo: self.utxo, - note: EncryptedMessage::new(&self.viewing_key, self.ephemeral_key, self.asset), - } - } -} - -/// Receiver Ledger -pub trait ReceiverLedger<C, H> -where - C: Configuration, - H: HybridPublicKeyEncryptionScheme< - Plaintext = C::Asset, - KeyAgreementScheme = C::KeyAgreementScheme, - >, -{ - /// Valid [`Utxo`] Posting Key - /// - /// # Safety - /// - /// This type must be some wrapper around [`Utxo`] which can only be constructed by this - /// implementation of [`ReceiverLedger`]. This is to prevent that [`register`](Self::register) - /// is called before [`is_not_registered`](Self::is_not_registered). - type ValidUtxo; - - /// Super Posting Key - /// - /// Type that allows super-traits of [`ReceiverLedger`] to customize posting key behavior. - type SuperPostingKey: Copy; - - /// Checks if the ledger already contains the `utxo` in its set of UTXOs. - /// - /// Existence of such a UTXO could indicate a possible double-spend. - fn is_not_registered(&self, utxo: Utxo<C>) -> Option<Self::ValidUtxo>; - - /// Posts the `utxo` and `encrypted_asset` to the ledger, registering the asset. - /// - /// # Safety - /// - /// This method can only be called once we check that `utxo` is not already stored on the - /// ledger. See [`is_not_registered`](Self::is_not_registered). - fn register( - &mut self, - utxo: Self::ValidUtxo, - note: EncryptedMessage<H>, - super_key: &Self::SuperPostingKey, - ); -} - -/// Receiver Post Error -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum ReceiverPostError { - /// Asset Registered Error - /// - /// The asset has already been registered with the ledger. - AssetRegistered, -} - -/// Receiver Post -pub struct ReceiverPost<C, H> -where - C: Configuration, - H: HybridPublicKeyEncryptionScheme< - Plaintext = C::Asset, - KeyAgreementScheme = C::KeyAgreementScheme, - >, -{ - /// Unspent Transaction Output - utxo: Utxo<C>, - - /// Encrypted Note - note: EncryptedMessage<H>, -} - -impl<C, H> ReceiverPost<C, H> -where - C: Configuration, - H: HybridPublicKeyEncryptionScheme< - Plaintext = C::Asset, - KeyAgreementScheme = C::KeyAgreementScheme, - >, -{ - /// Returns the ephemeral key associated to `self`. - #[inline] - pub fn ephemeral_key(&self) -> &PublicKey<C> { - self.note.ephemeral_public_key() - } - - /// Validates `self` on the receiver `ledger`. - #[inline] - pub fn validate<L>(self, ledger: &L) -> Result<ReceiverPostingKey<C, H, L>, ReceiverPostError> - where - L: ReceiverLedger<C, H>, - { - Ok(ReceiverPostingKey { - utxo: ledger - .is_not_registered(self.utxo) - .ok_or(ReceiverPostError::AssetRegistered)?, - note: self.note, - }) - } -} - -/// Receiver Posting Key -pub struct ReceiverPostingKey<C, H, L> -where - C: Configuration, - H: HybridPublicKeyEncryptionScheme< - Plaintext = C::Asset, - KeyAgreementScheme = C::KeyAgreementScheme, - >, - L: ReceiverLedger<C, H>, -{ - /// UTXO Posting Key - utxo: L::ValidUtxo, - - /// Encrypted Note - note: EncryptedMessage<H>, -} - -impl<C, H, L> ReceiverPostingKey<C, H, L> -where - C: Configuration, - H: HybridPublicKeyEncryptionScheme< - Plaintext = C::Asset, - KeyAgreementScheme = C::KeyAgreementScheme, - >, - L: ReceiverLedger<C, H>, -{ - /// Posts `self` to the receiver `ledger`. - #[inline] - pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) { - ledger.register(self.utxo, self.note, super_key); - } -} diff --git a/manta-accounting/src/lib.rs b/manta-accounting/src/lib.rs index 826dc07bb..09d57ffb7 100644 --- a/manta-accounting/src/lib.rs +++ b/manta-accounting/src/lib.rs @@ -29,7 +29,6 @@ extern crate cocoon as cocoon_crate; pub mod asset; pub mod fs; -// TODO[remove]: pub mod identity; pub mod key; pub mod transfer; -// pub mod wallet; +pub mod wallet; diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 60606877a..f52df626d 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -18,37 +18,79 @@ use crate::asset::{Asset, AssetId, AssetValue}; use alloc::vec::Vec; -use core::{marker::PhantomData, ops::Add}; +use core::ops::Add; use manta_crypto::{ accumulator::{Accumulator, MembershipProof, Verifier}, commitment::{CommitmentScheme, Input as CommitmentInput}, constraint::{ - reflection::{HasEqual, HasVariable, Var}, - Allocation, Constant, ConstraintSystem, Derived, Equal, ProofSystem, Public, - PublicOrSecret, Secret, Variable, VariableSource, + Allocation, Constant, ConstraintSystem, Derived, Equal, Input as ProofSystemInput, + ProofSystem, Public, PublicOrSecret, Secret, Variable, VariableSource, }, - encryption::{self, DecryptedMessage, EncryptedMessage, HybridPublicKeyEncryptionScheme}, + encryption::{DecryptedMessage, EncryptedMessage, HybridPublicKeyEncryptionScheme}, key::KeyAgreementScheme, - rand::{CryptoRng, Rand, RngCore}, + rand::{CryptoRng, Rand, RngCore, Sample}, }; -use manta_util::create_seal; +use manta_util::{create_seal, from_variant_impl}; -/// Returns `true` if the transfer with this shape would have no public participants. +/// Returns `true` if the transfer with this shape would have public participants. #[inline] -pub const fn has_no_public_participants( +pub const fn has_public_participants( sources: usize, senders: usize, receivers: usize, sinks: usize, ) -> bool { let _ = (senders, receivers); - sources == 0 && sinks == 0 + sources > 0 || sinks > 0 +} + +/// Generates a UTXO, commiting `asset` with the given `trapdoor`. +#[inline] +fn generate_utxo<C, I, V>( + commitment_scheme: &C, + asset: &Asset<I, V>, + trapdoor: &C::Trapdoor, +) -> C::Output +where + C: CommitmentScheme + CommitmentInput<I> + CommitmentInput<V>, +{ + commitment_scheme + .start() + .update(&asset.id) + .update(&asset.value) + .commit(trapdoor) +} + +/// Generates an ephemeral secret key, commiting to the `spend` key at the given `index` with the +/// current `ledger_checkpoint`. +#[inline] +fn generate_ephemeral_secret_key<C, L, B, PK, SK>( + commitment_scheme: &C, + ledger_checkpoint: &L, + index: &B, + spend: &PK, + ephemeral_key_trapdoor: &C::Trapdoor, +) -> SK +where + C: CommitmentScheme + CommitmentInput<L> + CommitmentInput<B> + CommitmentInput<PK>, + C::Output: Into<SK>, +{ + commitment_scheme + .start() + .update(ledger_checkpoint) + .update(index) + .update(spend) + .commit(ephemeral_key_trapdoor) + .into() } /// Transfer Configuration pub trait Configuration { + /// Shared Secret + type SharedSecret: Sample; + /// Key Agreement Scheme - type KeyAgreementScheme: KeyAgreementScheme; + type KeyAgreementScheme: KeyAgreementScheme<SharedSecret = Self::SharedSecret>; /// Secret Key Variable type SecretKeyVar: Variable<Self::ConstraintSystem, Type = SecretKey<Self>, Mode = Secret>; @@ -56,37 +98,78 @@ pub trait Configuration { /// Public Key Variable type PublicKeyVar: Variable<Self::ConstraintSystem, Type = PublicKey<Self>, Mode = Secret>; + /// Shared Secret Key Variable + type SharedSecretVar: Variable<Self::ConstraintSystem, Type = Self::SharedSecret, Mode = Secret>; + /// Key Agreement Scheme Variable - type KeyAgreementSchemeVar: KeyAgreementScheme<SecretKey = Self::SecretKeyVar, PublicKey = Self::PublicKeyVar> - + Variable<Self::ConstraintSystem, Type = Self::KeyAgreementScheme, Mode = Constant>; + type KeyAgreementSchemeVar: KeyAgreementScheme< + SecretKey = Self::SecretKeyVar, + PublicKey = Self::PublicKeyVar, + SharedSecret = Self::SharedSecretVar, + > + Variable<Self::ConstraintSystem, Type = Self::KeyAgreementScheme, Mode = Constant>; /// Commitment Scheme Output - type CommitmentSchemeOutput: PartialEq; + type CommitmentSchemeOutput: PartialEq + Into<SecretKey<Self>>; /// Commitment Scheme Output Variable - type CommitmentSchemeOutputVar: Variable<Self::ConstraintSystem, Type = Self::CommitmentSchemeOutput, Mode = PublicOrSecret> + type CommitmentSchemeOutputVar: Into<Self::SecretKeyVar> + + Variable<Self::ConstraintSystem, Type = Self::CommitmentSchemeOutput, Mode = PublicOrSecret> + Equal<Self::ConstraintSystem>; /// Commitment Scheme - type CommitmentScheme: CommitmentScheme<Trapdoor = SharedSecret<Self>, Output = Self::CommitmentSchemeOutput> - + CommitmentInput<Asset> - + CommitmentInput<SecretKey<Self>>; + type CommitmentScheme: CommitmentScheme<Trapdoor = Self::SharedSecret, Output = Self::CommitmentSchemeOutput> + + CommitmentInput<AssetId> + + CommitmentInput<AssetValue> + + CommitmentInput<SecretKey<Self>> + + CommitmentInput<PublicKey<Self>> + + CommitmentInput<Self::LedgerCheckpoint> + + CommitmentInput<u8>; /// Commitment Scheme Variable - type CommitmentSchemeVar: CommitmentScheme<Trapdoor = SharedSecretVar<Self>, Output = Self::CommitmentSchemeOutputVar> - + CommitmentInput<AssetVar<Self>> + type CommitmentSchemeVar: CommitmentScheme<Trapdoor = Self::SharedSecretVar, Output = Self::CommitmentSchemeOutputVar> + + CommitmentInput<Self::AssetIdVar> + + CommitmentInput<Self::AssetValueVar> + CommitmentInput<Self::SecretKeyVar> + + CommitmentInput<Self::PublicKeyVar> + + CommitmentInput<Self::LedgerCheckpointVar> + + CommitmentInput<Self::ByteVar> + Variable<Self::ConstraintSystem, Type = Self::CommitmentScheme, Mode = Constant>; /// UTXO Set Verifier type UtxoSetVerifier: Verifier<Item = Utxo<Self>, Verification = bool>; + /// UTXO Set Witness Variable + type UtxoSetWitnessVar: Variable< + Self::ConstraintSystem, + Type = <Self::UtxoSetVerifier as Verifier>::Witness, + Mode = Secret, + >; + + /// UTXO Set Output Variable + type UtxoSetOutputVar: Variable< + Self::ConstraintSystem, + Type = <Self::UtxoSetVerifier as Verifier>::Output, + Mode = Public, + >; + /// UTXO Set Verifier Variable type UtxoSetVerifierVar: Verifier< Item = UtxoVar<Self>, + Witness = Self::UtxoSetWitnessVar, + Output = Self::UtxoSetOutputVar, Verification = <Self::ConstraintSystem as ConstraintSystem>::Bool, > + Variable<Self::ConstraintSystem, Type = Self::UtxoSetVerifier, Mode = Constant>; + /// Ledger Checkpoint Type + type LedgerCheckpoint; + + /// Ledger Checkpoint Variable + type LedgerCheckpointVar: Variable< + Self::ConstraintSystem, + Type = Self::LedgerCheckpoint, + Mode = Public, + >; + /// Asset Id Variable type AssetIdVar: Variable<Self::ConstraintSystem, Type = AssetId, Mode = PublicOrSecret> + Equal<Self::ConstraintSystem>; @@ -96,11 +179,19 @@ pub trait Configuration { + Equal<Self::ConstraintSystem> + Add<Output = Self::AssetValueVar>; + /// Byte Variable + type ByteVar: Variable<Self::ConstraintSystem, Type = u8, Mode = Public>; + /// Constraint System Type type ConstraintSystem: ConstraintSystem; /// Proof System Type - type ProofSystem: ProofSystem<ConstraintSystem = Self::ConstraintSystem, Verification = bool>; + type ProofSystem: ProofSystem<ConstraintSystem = Self::ConstraintSystem, Verification = bool> + + ProofSystemInput<AssetId> + + ProofSystemInput<AssetValue> + + ProofSystemInput<UtxoSetOutput<Self>> + + ProofSystemInput<CommitmentSchemeOutput<Self>> + + ProofSystemInput<Self::LedgerCheckpoint>; /// Note Encryption Scheme Type type NoteEncryptionScheme: HybridPublicKeyEncryptionScheme< @@ -109,64 +200,52 @@ pub trait Configuration { >; } -/// +/// Asset Variable Type pub type AssetVar<C> = Asset<<C as Configuration>::AssetIdVar, <C as Configuration>::AssetValueVar>; -/// +/// Secret Key Type pub type SecretKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::SecretKey; -/// +/// Public Key Type pub type PublicKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::PublicKey; -/// -pub type SharedSecret<C> = - <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret; - -/// -pub type SharedSecretVar<C> = - <<C as Configuration>::KeyAgreementSchemeVar as KeyAgreementScheme>::SharedSecret; - -/// +/// Trapdoor Type pub type Trapdoor<C> = <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Trapdoor; -/// +/// Trapdoor Variable Type pub type TrapdoorVar<C> = <<C as Configuration>::CommitmentSchemeVar as CommitmentScheme>::Trapdoor; -/// +/// Commitment Scheme Output Type pub type CommitmentSchemeOutput<C> = <C as Configuration>::CommitmentSchemeOutput; -/// +/// Commitment Scheme Output Variable Type pub type CommitmentSchemeOutputVar<C> = <C as Configuration>::CommitmentSchemeOutputVar; -/// +/// Unspend Transaction Output Type pub type Utxo<C> = CommitmentSchemeOutput<C>; -/// +/// Unspent Transaction Output Variable Type pub type UtxoVar<C> = CommitmentSchemeOutputVar<C>; -/// -pub type UtxoAccumulatorOutput<C> = <<C as Configuration>::UtxoSetVerifier as Verifier>::Output; +/// UTXO Set Output Type +pub type UtxoSetOutput<C> = <<C as Configuration>::UtxoSetVerifier as Verifier>::Output; -/// -pub type UtxoAccumulatorOutputVar<C> = - <<C as Configuration>::UtxoSetVerifierVar as Verifier>::Output; - -/// +/// UTXO Membership Proof Type pub type UtxoMembershipProof<C> = MembershipProof<<C as Configuration>::UtxoSetVerifier>; -/// +/// UTXO Membership Proof Variable Type pub type UtxoMembershipProofVar<C> = MembershipProof<<C as Configuration>::UtxoSetVerifierVar>; -/// +/// Void Number Type pub type VoidNumber<C> = CommitmentSchemeOutput<C>; -/// +/// Void Number Variable Type pub type VoidNumberVar<C> = CommitmentSchemeOutputVar<C>; -/// +/// Encrypted Note Type pub type EncryptedNote<C> = EncryptedMessage<<C as Configuration>::NoteEncryptionScheme>; -/// +/// Decrypted Note Type pub type Note<C> = DecryptedMessage<<C as Configuration>::NoteEncryptionScheme>; /// Transfer Proof System Type @@ -181,6 +260,9 @@ pub type ProvingContext<C> = <ProofSystemType<C> as ProofSystem>::ProvingContext /// Transfer Verifying Context Type pub type VerifyingContext<C> = <ProofSystemType<C> as ProofSystem>::VerifyingContext; +/// Transfer Proof System Input Type +pub type ProofInput<C> = <<C as Configuration>::ProofSystem as ProofSystem>::Input; + /// Transfer Validity Proof Type pub type Proof<C> = <ProofSystemType<C> as ProofSystem>::Proof; @@ -230,7 +312,8 @@ where utxo: &Utxo<C>, commitment_scheme: &C::CommitmentScheme, ) -> bool { - &commitment_scheme.commit_one( + &generate_utxo( + commitment_scheme, asset, &C::KeyAgreementScheme::agree(&self.spend, ephemeral_key), ) == utxo @@ -315,7 +398,7 @@ where ) -> Self { let trapdoor = C::KeyAgreementScheme::agree(&spend, &ephemeral_key); Self { - utxo: commitment_scheme.commit_one(&asset, &trapdoor), + utxo: generate_utxo(commitment_scheme, &asset, &trapdoor), void_number: commitment_scheme.commit_one(&spend, &trapdoor), spend, ephemeral_key, @@ -455,27 +538,53 @@ where } } - /* TODO: + /// Extracts the ledger posting data from `self`. + #[inline] + pub fn into_post(self) -> SenderPost<C> { + SenderPost { + utxo_set_output: self.utxo_membership_proof.into_output(), + void_number: self.void_number, + } + } +} + +/// Sender Variable +pub struct SenderVar<C> +where + C: Configuration, +{ + /// Secret Spend Key + spend: C::SecretKeyVar, + + /// Ephemeral Public Spend Key + ephemeral_key: C::PublicKeyVar, + + /// Asset + asset: AssetVar<C>, + + /// UTXO Membership Proof + utxo_membership_proof: UtxoMembershipProofVar<C>, + + /// Void Number + void_number: VoidNumberVar<C>, +} + +impl<C> SenderVar<C> +where + C: Configuration, +{ /// Returns the asset for `self`, checking if `self` is well-formed in the given constraint /// system `cs`. #[inline] - pub fn get_well_formed_asset<CS>( + pub fn get_well_formed_asset( self, - commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &V, - cs: &mut CS, - ) -> C::Asset - where - CS: ConstraintSystem, - Commitment<C>: Equal<CS>, - V: Verifier<Verification = CS::Bool>, - { - let trapdoor = C::into_trapdoor(C::KeyAgreementScheme::agree( - &self.spend, - &self.ephemeral_key, - )); + commitment_scheme: &C::CommitmentSchemeVar, + utxo_set_verifier: &C::UtxoSetVerifierVar, + cs: &mut C::ConstraintSystem, + ) -> AssetVar<C> { + let trapdoor = C::KeyAgreementSchemeVar::agree(&self.spend, &self.ephemeral_key); cs.assert(self.utxo_membership_proof.verify( - &commitment_scheme.commit_one(&self.asset, &trapdoor), + &generate_utxo(commitment_scheme, &self.asset, &trapdoor), utxo_set_verifier, )); cs.assert_eq( @@ -484,14 +593,33 @@ where ); self.asset } - */ +} + +impl<C> Variable<C::ConstraintSystem> for SenderVar<C> +where + C: Configuration, +{ + type Type = Sender<C>; + + type Mode = Derived; - /// Extracts the ledger posting data from `self`. #[inline] - pub fn into_post(self) -> SenderPost<C> { - SenderPost { - utxo_accumulator_output: self.utxo_membership_proof.into_output(), - void_number: self.void_number, + fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + match allocation { + Allocation::Known(this, mode) => Self { + spend: this.spend.as_known(cs, mode), + ephemeral_key: this.ephemeral_key.as_known(cs, mode), + asset: this.asset.as_known(cs, mode), + utxo_membership_proof: this.utxo_membership_proof.as_known(cs, mode), + void_number: this.void_number.as_known(cs, Public), + }, + Allocation::Unknown(mode) => Self { + spend: C::SecretKeyVar::new_unknown(cs, mode), + ephemeral_key: C::PublicKeyVar::new_unknown(cs, mode), + asset: AssetVar::<C>::new_unknown(cs, mode), + utxo_membership_proof: UtxoMembershipProofVar::<C>::new_unknown(cs, mode), + void_number: VoidNumberVar::<C>::new_unknown(cs, Public), + }, } } } @@ -508,20 +636,20 @@ where /// This type must be some wrapper around [`VoidNumber`] which can only be constructed by this /// implementation of [`SenderLedger`]. This is to prevent that [`spend`](Self::spend) is /// called before [`is_unspent`](Self::is_unspent) and - /// [`has_matching_utxo_accumulator_output`](Self::has_matching_utxo_accumulator_output). + /// [`has_matching_utxo_set_output`](Self::has_matching_utxo_set_output). type ValidVoidNumber; - /// Valid UTXO Accumulator Output Posting Key + /// Valid UTXO Set Output Posting Key /// /// # Safety /// /// This type must be some wrapper around [`S::Output`] which can only be constructed by this /// implementation of [`SenderLedger`]. This is to prevent that [`spend`](Self::spend) is /// called before [`is_unspent`](Self::is_unspent) and - /// [`has_matching_utxo_accumulator_output`](Self::has_matching_utxo_accumulator_output). + /// [`has_matching_utxo_set_output`](Self::has_matching_utxo_set_output). /// /// [`S::Output`]: Verifier::Output - type ValidUtxoAccumulatorOutput; + type ValidUtxoSetOutput; /// Super Posting Key /// @@ -538,10 +666,10 @@ where /// /// Failure to match the ledger state means that the sender was constructed under an invalid or /// older state of the ledger. - fn has_matching_utxo_accumulator_output( + fn has_matching_utxo_set_output( &self, - output: UtxoAccumulatorOutput<C>, - ) -> Option<Self::ValidUtxoAccumulatorOutput>; + output: UtxoSetOutput<C>, + ) -> Option<Self::ValidUtxoSetOutput>; /// Posts the `void_number` to the ledger, spending the asset. /// @@ -551,7 +679,7 @@ where /// the ledger. See [`is_unspent`](Self::is_unspent). fn spend( &mut self, - utxo_accumulator_output: Self::ValidUtxoAccumulatorOutput, + utxo_set_output: Self::ValidUtxoSetOutput, void_number: Self::ValidVoidNumber, super_key: &Self::SuperPostingKey, ); @@ -565,10 +693,10 @@ pub enum SenderPostError { /// The asset has already been spent. AssetSpent, - /// Invalid UTXO Accumulator Error + /// Invalid UTXO Set Output Error /// /// The sender was not constructed under the current state of the UTXO set. - InvalidUtxoAccumulator, + InvalidUtxoSetOutput, } /// Sender Post @@ -576,8 +704,8 @@ pub struct SenderPost<C> where C: Configuration, { - /// UTXO Accumulator Output - utxo_accumulator_output: UtxoAccumulatorOutput<C>, + /// UTXO Set Output + utxo_set_output: UtxoSetOutput<C>, /// Void Number void_number: VoidNumber<C>, @@ -587,6 +715,15 @@ impl<C> SenderPost<C> where C: Configuration, { + /// Extends proof public input with `self`. + #[inline] + pub fn extend_input(&self, input: &mut ProofInput<C>) { + // TODO: Add a "public part" trait that extracts the public part of `Sender` (using + // `SenderVar` to determine the types), then generate this method automatically. + C::ProofSystem::extend(input, &self.utxo_set_output); + C::ProofSystem::extend(input, &self.void_number); + } + /// Validates `self` on the sender `ledger`. #[inline] pub fn validate<L>(self, ledger: &L) -> Result<SenderPostingKey<C, L>, SenderPostError> @@ -594,9 +731,9 @@ where L: SenderLedger<C>, { Ok(SenderPostingKey { - utxo_accumulator_output: ledger - .has_matching_utxo_accumulator_output(self.utxo_accumulator_output) - .ok_or(SenderPostError::InvalidUtxoAccumulator)?, + utxo_set_output: ledger + .has_matching_utxo_set_output(self.utxo_set_output) + .ok_or(SenderPostError::InvalidUtxoSetOutput)?, void_number: ledger .is_unspent(self.void_number) .ok_or(SenderPostError::AssetSpent)?, @@ -608,10 +745,10 @@ where pub struct SenderPostingKey<C, L> where C: Configuration, - L: SenderLedger<C>, + L: SenderLedger<C> + ?Sized, { - /// UTXO Accumulator Output Posting Key - utxo_accumulator_output: L::ValidUtxoAccumulatorOutput, + /// UTXO Set Output Posting Key + utxo_set_output: L::ValidUtxoSetOutput, /// Void Number Posting Key void_number: L::ValidVoidNumber, @@ -620,12 +757,12 @@ where impl<C, L> SenderPostingKey<C, L> where C: Configuration, - L: SenderLedger<C>, + L: SenderLedger<C> + ?Sized, { /// Posts `self` to the sender `ledger`. #[inline] pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) { - ledger.spend(self.utxo_accumulator_output, self.void_number, super_key); + ledger.spend(self.utxo_set_output, self.void_number, super_key); } } @@ -706,7 +843,8 @@ where commitment_scheme: &C::CommitmentScheme, ) -> Self { Self { - utxo: commitment_scheme.commit_one( + utxo: generate_utxo( + commitment_scheme, &asset, &C::KeyAgreementScheme::agree(&ephemeral_key, &spend), ), @@ -717,44 +855,92 @@ where } } - /* TODO: + /// Converts `self` into its [`PreReceiver`], dropping the ephemeral key. + #[inline] + pub fn downgrade(self) -> PreReceiver<C> { + PreReceiver::new(self.spend, self.view, self.asset) + } + + /// Extracts the ledger posting data from `self`. + #[inline] + pub fn into_post(self) -> ReceiverPost<C> { + ReceiverPost { + utxo: self.utxo, + note: EncryptedMessage::new(&self.view, self.ephemeral_key, self.asset), + } + } +} + +/// Receiver Variable +pub struct ReceiverVar<C> +where + C: Configuration, +{ + /// Public Spend Key + spend: C::PublicKeyVar, + + /// Asset + asset: AssetVar<C>, + + /// Unspent Transaction Output + utxo: UtxoVar<C>, +} + +impl<C> ReceiverVar<C> +where + C: Configuration, +{ /// Returns the asset for `self`, checking if `self` is well-formed in the given constraint /// system `cs`. #[inline] - pub fn get_well_formed_asset<CS>( + pub fn get_well_formed_asset( self, - commitment_scheme: &C::CommitmentScheme, - cs: &mut CS, - ) -> C::Asset - where - CS: ConstraintSystem, - Utxo<C>: Equal<CS>, - { + index: C::ByteVar, + ledger_checkpoint: &C::LedgerCheckpointVar, + ephemeral_key_trapdoor: &TrapdoorVar<C>, + commitment_scheme: &C::CommitmentSchemeVar, + cs: &mut C::ConstraintSystem, + ) -> AssetVar<C> { + let ephemeral_key = generate_ephemeral_secret_key( + commitment_scheme, + ledger_checkpoint, + &index, + &self.spend, + ephemeral_key_trapdoor, + ); cs.assert_eq( &self.utxo, - &Self::generate_utxo( - &self.spending_key, - &self.ephemeral_key, - &self.asset, + &generate_utxo( commitment_scheme, + &self.asset, + &C::KeyAgreementSchemeVar::agree(&ephemeral_key, &self.spend), ), ); self.asset } - */ +} - /// Converts `self` into its [`PreReceiver`], dropping the ephemeral key. - #[inline] - pub fn downgrade(self) -> PreReceiver<C> { - PreReceiver::new(self.spend, self.view, self.asset) - } +impl<C> Variable<C::ConstraintSystem> for ReceiverVar<C> +where + C: Configuration, +{ + type Type = Receiver<C>; + + type Mode = Derived; - /// Extracts the ledger posting data from `self`. #[inline] - pub fn into_post(self) -> ReceiverPost<C> { - ReceiverPost { - utxo: self.utxo, - note: EncryptedMessage::new(&self.view, self.ephemeral_key, self.asset), + fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + match allocation { + Allocation::Known(this, mode) => Self { + spend: this.spend.as_known(cs, mode), + asset: this.asset.as_known(cs, mode), + utxo: this.utxo.as_known(cs, Public), + }, + Allocation::Unknown(mode) => Self { + spend: C::PublicKeyVar::new_unknown(cs, mode), + asset: AssetVar::<C>::new_unknown(cs, mode), + utxo: UtxoVar::<C>::new_unknown(cs, Public), + }, } } } @@ -828,6 +1014,14 @@ where self.note.ephemeral_public_key() } + /// Extends proof public input with `self`. + #[inline] + pub fn extend_input(&self, input: &mut ProofInput<C>) { + // TODO: Add a "public part" trait that extracts the public part of `Receiver` (using + // `ReceiverVar` to determine the types), then generate this method automatically. + C::ProofSystem::extend(input, &self.utxo); + } + /// Validates `self` on the receiver `ledger`. #[inline] pub fn validate<L>(self, ledger: &L) -> Result<ReceiverPostingKey<C, L>, ReceiverPostError> @@ -847,7 +1041,7 @@ where pub struct ReceiverPostingKey<C, L> where C: Configuration, - L: ReceiverLedger<C>, + L: ReceiverLedger<C> + ?Sized, { /// UTXO Posting Key utxo: L::ValidUtxo, @@ -859,7 +1053,7 @@ where impl<C, L> ReceiverPostingKey<C, L> where C: Configuration, - L: ReceiverLedger<C>, + L: ReceiverLedger<C> + ?Sized, { /// Posts `self` to the receiver `ledger`. #[inline] @@ -885,7 +1079,7 @@ pub struct Transfer< sources: [AssetValue; SOURCES], /// Senders - senders: [PreSender<C>; SENDERS], + senders: [Sender<C>; SENDERS], /// Receivers receivers: [PreReceiver<C>; RECEIVERS], @@ -904,7 +1098,7 @@ where fn new( asset_id: Option<AssetId>, sources: [AssetValue; SOURCES], - senders: [PreSender<C>; SENDERS], + senders: [Sender<C>; SENDERS], receivers: [PreReceiver<C>; RECEIVERS], sinks: [AssetValue; SINKS], ) -> Self { @@ -943,7 +1137,7 @@ where /// Checks that the given `asset_id` for [`Transfer`] building is visible exactly when required. #[inline] fn has_visible_asset_id_when_required(has_visible_asset_id: bool) { - if SOURCES > 0 || SINKS > 0 { + if has_public_participants(SOURCES, SENDERS, RECEIVERS, SINKS) { assert!( has_visible_asset_id, "Missing public asset id when required." @@ -962,7 +1156,7 @@ where fn new_unchecked( asset_id: Option<AssetId>, sources: [AssetValue; SOURCES], - senders: [PreSender<C>; SENDERS], + senders: [Sender<C>; SENDERS], receivers: [PreReceiver<C>; RECEIVERS], sinks: [AssetValue; SINKS], ) -> Self { @@ -975,100 +1169,144 @@ where } } - /// Generates the constraint system for an unknown transfer. + /// Generates a proving and verifying context for this transfer shape. #[inline] - fn unknown_constraints( + pub fn generate_context<R>( commitment_scheme: &C::CommitmentScheme, utxo_set_verifier: &C::UtxoSetVerifier, - ) -> C::ConstraintSystem { + rng: &mut R, + ) -> Result<(ProvingContext<C>, VerifyingContext<C>), ProofSystemError<C>> + where + R: CryptoRng + RngCore + ?Sized, + { let mut cs = C::ProofSystem::for_unknown(); - TransferVar::<C, SOURCES, SENDERS, RECEIVERS, SINKS>::new_unknown(&mut cs, Derived) + FullTransferVar::<C, SOURCES, SENDERS, RECEIVERS, SINKS>::new_unknown(&mut cs, Derived) .build_validity_constraints( &commitment_scheme.as_known(&mut cs, Public), &utxo_set_verifier.as_known(&mut cs, Public), &mut cs, ); - cs + cs.generate_context::<C::ProofSystem, _>(rng) } - /// Generates a proving and verifying context for this transfer shape. + /// Converts `self` into its ledger post. #[inline] - pub fn generate_context<R>( + pub fn into_post<R>( + self, commitment_scheme: &C::CommitmentScheme, utxo_set_verifier: &C::UtxoSetVerifier, + ledger_checkpoint: C::LedgerCheckpoint, + context: &ProvingContext<C>, rng: &mut R, - ) -> Result<(ProvingContext<C>, VerifyingContext<C>), ProofSystemError<C>> + ) -> Result<TransferPost<C>, ProofSystemError<C>> where R: CryptoRng + RngCore + ?Sized, { - Self::unknown_constraints(commitment_scheme, utxo_set_verifier) - .generate_context::<C::ProofSystem, _>(rng) + let ephemeral_key_trapdoor = rng.gen(); + FullTransfer::<_, SOURCES, SENDERS, RECEIVERS, SINKS> { + asset_id: self.asset_id, + sources: self.sources, + senders: self.senders, + receivers: IntoIterator::into_iter(self.receivers) + .enumerate() + .map(|(i, r)| { + let ephemeral_key = generate_ephemeral_secret_key( + commitment_scheme, + &ledger_checkpoint, + &(i as u8), + &r.spend, + &ephemeral_key_trapdoor, + ); + r.upgrade(ephemeral_key, commitment_scheme) + }) + .collect::<Vec<_>>(), + sinks: self.sinks, + ephemeral_key_trapdoor, + ledger_checkpoint, + } + .into_post(commitment_scheme, utxo_set_verifier, context, rng) } +} - /// Converts `self` into its ledger post. +/// Full Transfer +struct FullTransfer< + C, + const SOURCES: usize, + const SENDERS: usize, + const RECEIVERS: usize, + const SINKS: usize, +> where + C: Configuration, +{ + /// Asset Id + asset_id: Option<AssetId>, + + /// Sources + sources: [AssetValue; SOURCES], + + /// Senders + senders: [Sender<C>; SENDERS], + + /// Receivers + receivers: Vec<Receiver<C>>, + + /// Sinks + sinks: [AssetValue; SINKS], + + /// Ephemeral Key Trapdoor + ephemeral_key_trapdoor: Trapdoor<C>, + + /// Ledger Checkpoint + ledger_checkpoint: C::LedgerCheckpoint, +} + +impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> + FullTransfer<C, SOURCES, SENDERS, RECEIVERS, SINKS> +where + C: Configuration, +{ + /// Computes the [`TransferPost`] for `self`. #[inline] - pub fn into_post<S, R>( + fn into_post<R>( self, commitment_scheme: &C::CommitmentScheme, - utxo_set: &S, - ledger_accumulator_output: UtxoAccumulatorOutput<C>, + utxo_set_verifier: &C::UtxoSetVerifier, context: &ProvingContext<C>, rng: &mut R, ) -> Result<TransferPost<C>, ProofSystemError<C>> where - S: Accumulator<Item = Utxo<C>, Verifier = C::UtxoSetVerifier>, R: CryptoRng + RngCore + ?Sized, { - /* TODO: - if !utxo_set.matching_output(&ledger_accumulator_output) { - todo!("ERROR") - } - - let senders = IntoIterator::into_iter(self.senders) - .map(move |s| s.try_upgrade(utxo_set)) - .collect::<Option<Vec<_>>>() - .expect("TODO: deal with error."); - - let fair_trapdoor = rng.gen(); - let mut fair = commitment_scheme.start().update(&ledger_accumulator_output); - for s in &senders { - fair.update(&s.spend); - } - fair = fair.commit(&fair_trapdoor); - - let receivers = IntoIterator::into_iter(self.receivers) - .enumerate() - .map(move |(i, r)| { - let ephemeral_key = commitment_scheme - .start() - .update(&(i as u8)) - .update(&r.spend) - .commit(fair); - r.upgrade(ephemeral_key, commitment_scheme) - }) - .collect::<Vec<_>>(); - */ - - /* TODO: Ok(TransferPost { - validity_proof: self.is_valid(commitment_scheme, utxo_set_verifier, context, rng)?, + validity_proof: { + let mut cs = C::ProofSystem::for_known(); + let transfer: FullTransferVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> = + self.as_known(&mut cs, Derived); + transfer.build_validity_constraints( + &commitment_scheme.as_known(&mut cs, Public), + &utxo_set_verifier.as_known(&mut cs, Public), + &mut cs, + ); + cs.prove::<C::ProofSystem, _>(context, rng)? + }, asset_id: self.asset_id, sources: self.sources.into(), sender_posts: IntoIterator::into_iter(self.senders) .map(Sender::into_post) .collect(), - receiver_posts: IntoIterator::into_iter(self.receivers) - .map(FullReceiver::into_post) + receiver_posts: self + .receivers + .into_iter() + .map(Receiver::into_post) .collect(), sinks: self.sinks.into(), + ledger_checkpoint: self.ledger_checkpoint, }) - */ - todo!() } } -/// Transfer Variable -pub struct TransferVar< +/// Full Transfer Variable +struct FullTransferVar< C, const SOURCES: usize, const SENDERS: usize, @@ -1077,37 +1315,34 @@ pub struct TransferVar< > where C: Configuration, { - /// + /// Asset Id asset_id: Option<C::AssetIdVar>, - /// - sources: [C::AssetValueVar; SOURCES], + /// Sources + sources: Vec<C::AssetValueVar>, - /// - // TODO: senders: [SenderVar<C>; SENDERS], + /// Senders + senders: Vec<SenderVar<C>>, - /// - // TODO: receivers: [ReceiverVar<C>; RECEIVERS], + /// Receivers + receivers: Vec<(C::ByteVar, ReceiverVar<C>)>, - /// - sinks: [C::AssetValueVar; SINKS], + /// Sinks + sinks: Vec<C::AssetValueVar>, - /// - ledger_accumulator_output: UtxoAccumulatorOutputVar<C>, + /// Ephemeral Key Trapdoor + ephemeral_key_trapdoor: TrapdoorVar<C>, - /// - fair_trapdoor: TrapdoorVar<C>, - - /// - fair: CommitmentSchemeOutputVar<C>, + /// Ledger Checkpoint + ledger_checkpoint: C::LedgerCheckpointVar, } impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> - TransferVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> + FullTransferVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> where C: Configuration, { - /// + /// Builds constraints for the [`Transfer`] validity proof. #[inline] fn build_validity_constraints( self, @@ -1117,12 +1352,11 @@ where ) { let mut secret_asset_ids = Vec::with_capacity(SENDERS + RECEIVERS); - /* TODO: let input_sum = self .senders .into_iter() .map(|s| { - let asset = s.get_well_formed_asset(&commitment_scheme, &utxo_set_verifier, cs); + let asset = s.get_well_formed_asset(commitment_scheme, utxo_set_verifier, cs); secret_asset_ids.push(asset.id); asset.value }) @@ -1130,11 +1364,20 @@ where .reduce(Add::add) .unwrap(); + let ledger_checkpoint = &self.ledger_checkpoint; + let ephemeral_key_trapdoor = &self.ephemeral_key_trapdoor; + let output_sum = self .receivers .into_iter() - .map(|r| { - let asset = r.get_well_formed_asset(&commitment_scheme, cs); + .map(|(index, r)| { + let asset = r.get_well_formed_asset( + index, + ledger_checkpoint, + ephemeral_key_trapdoor, + commitment_scheme, + cs, + ); secret_asset_ids.push(asset.id); asset.value }) @@ -1143,7 +1386,6 @@ where .unwrap(); cs.assert_eq(&input_sum, &output_sum); - */ match self.asset_id { Some(asset_id) => cs.assert_all_eq_to_base(&asset_id, secret_asset_ids.iter()), @@ -1153,19 +1395,19 @@ where } impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> - Variable<C::ConstraintSystem> for TransferVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> + Variable<C::ConstraintSystem> for FullTransferVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> where C: Configuration, { - type Type = Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>; + type Type = FullTransfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>; type Mode = Derived; #[inline] fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - /* TODO: match allocation { Allocation::Known(this, mode) => Self { + asset_id: this.asset_id.map(|id| id.as_known(cs, Public)), sources: this .sources .iter() @@ -1174,17 +1416,14 @@ where senders: this .senders .iter() - .map(|sender| { - // - todo!() - }) + .map(|sender| sender.as_known(cs, mode)) .collect(), receivers: this .receivers .iter() - .map(|receiver| { - // - todo!() + .enumerate() + .map(|(i, receiver)| { + ((i as u8).as_known(cs, mode), receiver.as_known(cs, mode)) }) .collect(), sinks: this @@ -1192,93 +1431,152 @@ where .iter() .map(|sink| sink.as_known(cs, Public)) .collect(), + ephemeral_key_trapdoor: this.ephemeral_key_trapdoor.as_known(cs, mode), + ledger_checkpoint: this.ledger_checkpoint.as_known(cs, mode), }, Allocation::Unknown(mode) => Self { + asset_id: has_public_participants(SOURCES, SENDERS, RECEIVERS, SINKS) + .then(|| C::AssetIdVar::new_unknown(cs, Public)), sources: (0..SOURCES) .into_iter() - .map(|_| AssetValueVar::<C>::new_unknown(cs, Public)) + .map(|_| C::AssetValueVar::new_unknown(cs, Public)) .collect(), senders: (0..SENDERS) .into_iter() - .map(|_| { - // - todo!() - }) + .map(|_| SenderVar::<C>::new_unknown(cs, mode)) .collect(), receivers: (0..RECEIVERS) .into_iter() .map(|_| { - // - todo!() + ( + C::ByteVar::new_unknown(cs, mode), + ReceiverVar::<C>::new_unknown(cs, mode), + ) }) .collect(), sinks: (0..SINKS) .into_iter() - .map(|_| AssetValueVar::<C>::new_unknown(cs, Public)) + .map(|_| C::AssetValueVar::new_unknown(cs, Public)) .collect(), + ephemeral_key_trapdoor: TrapdoorVar::<C>::new_unknown(cs, mode), + ledger_checkpoint: C::LedgerCheckpointVar::new_unknown(cs, mode), }, } - */ - todo!() } } -/* -impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> - Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS> +/// Transfer Ledger +pub trait TransferLedger<C>: SenderLedger<C, SuperPostingKey = (Self::ValidProof, TransferLedgerSuperPostingKey<C, Self>)> + + ReceiverLedger<C, SuperPostingKey = (Self::ValidProof, TransferLedgerSuperPostingKey<C, Self>)> where C: Configuration, { - /// Generates the unknown variables for the transfer validity proof. - #[inline] - fn unknown_variables( - commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &C::UtxoSetVerifier, - cs: &mut C::ConstraintSystem, - ) -> ( - Option<C::AssetIdVar>, - // TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS>, - C::CommitmentSchemeVar, - C::UtxoSetVerifierVar, - ) { - let base_asset_id = if has_no_public_participants(SOURCES, SENDERS, RECEIVERS, SINKS) { - None - } else { - Some(C::AssetIdVar::new_unknown(cs, Public)) - }; - ( - base_asset_id, - // TransferParticipantsVar::new_unknown(cs, Derived), - commitment_scheme.as_known(cs, Public), - utxo_set_verifier.as_known(cs, Public), - ) - } + /// Valid [`AssetValue`] for [`TransferPost`] source + /// + /// # Safety + /// + /// This type must be restricted so that it can only be constructed by this implementation of + /// [`TransferLedger`]. + type ValidSourceBalance; - /// Generates the known variables for the transfer validity proof. - #[inline] - fn known_variables( + /// Valid [`Proof`] Posting Key + /// + /// # Safety + /// + /// This type must be restricted so that it can only be constructed by this implementation + /// of [`TransferLedger`]. This is to prevent that [`SenderPostingKey::post`] and + /// [`ReceiverPostingKey::post`] are called before [`SenderPost::validate`], + /// [`ReceiverPost::validate`], [`check_source_balances`](Self::check_source_balances), and + /// [`is_valid`](Self::is_valid). + type ValidProof: Copy; + + /// Super Posting Key + /// + /// Type that allows super-traits of [`TransferLedger`] to customize posting key behavior. + type SuperPostingKey: Copy; + + /// Checks that the balances associated to the source accounts are sufficient to withdraw the + /// amount given in `sources`. + fn check_source_balances( &self, - commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &C::UtxoSetVerifier, - cs: &mut C::ConstraintSystem, - ) -> ( - Option<AssetIdVar<C>>, - TransferParticipantsVar<C, SOURCES, SENDERS, RECEIVERS, SINKS>, - CommitmentSchemeVar<C>, - UtxoSetVerifierVar<C>, - ) { - /* TODO: - ( - self.public.asset_id.map(|id| id.as_known(cs, Public)), - TransferParticipantsVar::new_known(cs, self, Derived), - commitment_scheme.as_known(cs, Public), - utxo_set_verifier.as_known(cs, Public), - ) - */ - todo!() - } + sources: Vec<AssetValue>, + ) -> Result<Vec<Self::ValidSourceBalance>, InsufficientPublicBalance>; + + /// Checks that the transfer `proof` is valid. + #[allow(clippy::too_many_arguments)] // FIXME: Write a better abstraction for this. + fn is_valid( + &self, + asset_id: Option<AssetId>, + sources: &[Self::ValidSourceBalance], + senders: &[SenderPostingKey<C, Self>], + receivers: &[ReceiverPostingKey<C, Self>], + sinks: &[AssetValue], + ledger_checkpoint: &C::LedgerCheckpoint, + proof: Proof<C>, + ) -> Option<Self::ValidProof>; + + /// Updates the public balances in the ledger, finishing the transaction. + /// + /// # Safety + /// + /// This method can only be called once we check that `proof` is a valid proof and that + /// `senders` and `receivers` are valid participants in the transaction. See + /// [`is_valid`](Self::is_valid) for more. + fn update_public_balances( + &mut self, + asset_id: AssetId, + sources: Vec<Self::ValidSourceBalance>, + sinks: Vec<AssetValue>, + proof: Self::ValidProof, + super_key: &TransferLedgerSuperPostingKey<C, Self>, + ); +} + +/// Transfer Source Posting Key Type +pub type SourcePostingKey<C, L> = <L as TransferLedger<C>>::ValidSourceBalance; + +/// Transfer Ledger Super Posting Key Type +pub type TransferLedgerSuperPostingKey<C, L> = <L as TransferLedger<C>>::SuperPostingKey; + +/// Insufficient Public Balance Error +/// +/// This `enum` is the error state of the [`TransferLedger::check_source_balances`] method. See its +/// documentation for more. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct InsufficientPublicBalance { + /// Index of the Public Address + pub index: usize, + + /// Current Balance + pub balance: AssetValue, + + /// Amount Attempting to Withdraw + pub withdraw: AssetValue, } -*/ + +/// Transfer Post Error +/// +/// This `enum` is the error state of the [`TransferPost::validate`] method. See its documentation +/// for more. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum TransferPostError { + /// Insufficient Public Balance + InsufficientPublicBalance(InsufficientPublicBalance), + + /// Sender Post Error + Sender(SenderPostError), + + /// Receiver Post Error + Receiver(ReceiverPostError), + + /// Invalid Transfer Proof Error + /// + /// Validity of the transfer could not be proved by the ledger. + InvalidProof, +} + +from_variant_impl!(TransferPostError, Sender, SenderPostError); +from_variant_impl!(TransferPostError, Receiver, ReceiverPostError); /// Transfer Post pub struct TransferPost<C> @@ -1300,8 +1598,8 @@ where /// Sinks sinks: Vec<AssetValue>, - /// Ledger Accumulator Output - ledger_accumulator_output: UtxoAccumulatorOutput<C>, + /// Ledger Checkpoint + ledger_checkpoint: C::LedgerCheckpoint, /// Validity Proof validity_proof: Proof<C>, @@ -1311,16 +1609,129 @@ impl<C> TransferPost<C> where C: Configuration, { - /* TODO: - /// Returns the ephemeral keys associated to the receiver posts of `self`. + /// Generates the public input for the [`Transfer`] validation proof. #[inline] - pub fn receiver_ephemeral_keys(&self) -> Vec<&PublicKey<C>> { + pub fn generate_proof_input(&self) -> ProofInput<C> { + // TODO: See comments in `crate::identity::constraint` about automatically deriving this + // method from possibly `TransferParticipantsVar`? + let mut input = Default::default(); + if let Some(asset_id) = self.asset_id { + C::ProofSystem::extend(&mut input, &asset_id); + } + self.sources + .iter() + .for_each(|source| C::ProofSystem::extend(&mut input, source)); + self.sender_posts + .iter() + .for_each(|post| post.extend_input(&mut input)); self.receiver_posts .iter() - .map(ReceiverPost::ephemeral_key) - .collect() + .for_each(|post| post.extend_input(&mut input)); + self.sinks + .iter() + .for_each(|sink| C::ProofSystem::extend(&mut input, sink)); + C::ProofSystem::extend(&mut input, &self.ledger_checkpoint); + input + } + + /// Validates `self` on the transfer `ledger`. + #[inline] + pub fn validate<L>(self, ledger: &L) -> Result<TransferPostingKey<C, L>, TransferPostError> + where + L: TransferLedger<C>, + { + let source_posting_keys = ledger + .check_source_balances(self.sources) + .map_err(TransferPostError::InsufficientPublicBalance)?; + // FIXME: The ledger needs to check that the senders are all unique! + let sender_posting_keys = self + .sender_posts + .into_iter() + .map(move |s| s.validate(ledger)) + .collect::<Result<Vec<_>, _>>()?; + let receiver_posting_keys = self + .receiver_posts + .into_iter() + .map(move |r| r.validate(ledger)) + .collect::<Result<Vec<_>, _>>()?; + Ok(TransferPostingKey { + validity_proof: match ledger.is_valid( + self.asset_id, + &source_posting_keys, + &sender_posting_keys, + &receiver_posting_keys, + &self.sinks, + &self.ledger_checkpoint, + self.validity_proof, + ) { + Some(key) => key, + _ => return Err(TransferPostError::InvalidProof), + }, + asset_id: self.asset_id, + source_posting_keys, + sender_posting_keys, + receiver_posting_keys, + sinks: self.sinks, + }) + } +} + +/// Transfer Posting Key +pub struct TransferPostingKey<C, L> +where + C: Configuration, + L: TransferLedger<C>, +{ + /// Asset Id + asset_id: Option<AssetId>, + + /// Source Posting Keys + source_posting_keys: Vec<SourcePostingKey<C, L>>, + + /// Sender Posting Keys + sender_posting_keys: Vec<SenderPostingKey<C, L>>, + + /// Receiver Posting Keys + receiver_posting_keys: Vec<ReceiverPostingKey<C, L>>, + + /// Sinks + sinks: Vec<AssetValue>, + + /// Validity Proof Posting Key + validity_proof: L::ValidProof, +} + +impl<C, L> TransferPostingKey<C, L> +where + C: Configuration, + L: TransferLedger<C>, +{ + /// Posts `self` to the transfer `ledger`. + /// + /// # Safety + /// + /// This method assumes that posting `self` to `ledger` is atomic and cannot fail. See + /// [`SenderLedger::spend`] and [`ReceiverLedger::register`] for more information on the + /// contract for this method. + #[inline] + pub fn post(self, super_key: &TransferLedgerSuperPostingKey<C, L>, ledger: &mut L) { + let proof = self.validity_proof; + for key in self.sender_posting_keys { + key.post(&(proof, *super_key), ledger); + } + for key in self.receiver_posting_keys { + key.post(&(proof, *super_key), ledger); + } + if let Some(asset_id) = self.asset_id { + ledger.update_public_balances( + asset_id, + self.source_posting_keys, + self.sinks, + proof, + super_key, + ); + } } - */ } create_seal! {} @@ -1432,7 +1843,7 @@ pub mod canonical { /// Builds a [`PrivateTransfer`] from `senders` and `receivers`. #[inline] pub fn build( - senders: [PreSender<C>; PrivateTransferShape::SENDERS], + senders: [Sender<C>; PrivateTransferShape::SENDERS], receivers: [PreReceiver<C>; PrivateTransferShape::RECEIVERS], ) -> Self { Self::new( @@ -1474,7 +1885,7 @@ pub mod canonical { /// Builds a [`Reclaim`] from `senders`, `receivers`, and `reclaim`. #[inline] pub fn build( - senders: [PreSender<C>; ReclaimShape::SENDERS], + senders: [Sender<C>; ReclaimShape::SENDERS], receivers: [PreReceiver<C>; ReclaimShape::RECEIVERS], reclaim: Asset, ) -> Self { diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index f059d45f6..761e993a7 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -35,9 +35,8 @@ use crate::{ transfer::{ self, canonical::{Mint, PrivateTransfer, PrivateTransferShape, Reclaim, Transaction}, - EncryptedNote, FullReceiver, PreSender, ProofSystemError, ProvingContext, PublicKey, - Receiver, ReceivingKey, SecretKey, Sender, Shape, SpendingKey, Transfer, TransferPost, - Utxo, + EncryptedNote, PreSender, ProofSystemError, ProvingContext, PublicKey, Receiver, + ReceivingKey, SecretKey, Sender, Shape, SpendingKey, Transfer, TransferPost, Utxo, }, }; use alloc::{vec, vec::Vec}; @@ -454,13 +453,17 @@ where /// Builds the pre-sender associated to `ephemeral_key` and `asset`. #[inline] fn build_pre_sender(&self, ephemeral_key: PublicKey<C>, asset: Asset) -> PreSender<C> { + /* TODO: self.spending_key .sender(ephemeral_key, asset, &self.commitment_scheme) + */ + todo!() } /// Selects the pre-senders which collectively own at least `asset`, returning any change. #[inline] fn select(&mut self, asset: Asset) -> Result<Selection<C>, Error<C::HierarchicalKeyTable, C>> { + /* TODO: let selection = self.assets.select(asset); if selection.is_empty() { return Err(Error::InsufficientBalance(asset)); @@ -474,6 +477,8 @@ where .map(move |(k, v)| self.build_pre_sender(k, asset.id.with(v))) .collect(), )) + */ + todo!() } /// Builds a [`TransferPost`] for the given `transfer`. @@ -485,17 +490,20 @@ where const SINKS: usize, >( &mut self, - transfer: impl Into<Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>>, + transfer: Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>, ) -> Result<TransferPost<C>, Error<C::HierarchicalKeyTable, C>> { + /* TODO: transfer - .into() .into_post( &self.commitment_scheme, self.utxo_set.verifier(), + ledger_checkpoint, &self.proving_context, &mut self.rng, ) .map_err(Error::ProofSystemError) + */ + todo!() } /* TODO: @@ -702,11 +710,14 @@ where self.commit(); match transaction { Transaction::Mint(asset) => { + /* TODO: let mint_post = self.build_post(Mint::build(asset, self.spending_key.receiver(asset)))?; self.pending_assets.insert = Some((mint_post.receiver_ephemeral_keys()[0].clone(), asset)); Ok(SignResponse::new(vec![mint_post])) + */ + todo!() } Transaction::PrivateTransfer(asset, receiver) => { self.sign_withdraw(asset, Some(receiver)) diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index 2a7e4f123..c68e38027 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -25,9 +25,6 @@ is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } [features] -# Constraint System Gadgets -constraint = [] - # Enable `getrandom` Entropy Source getrandom = ["rand_core/getrandom"] diff --git a/manta-crypto/src/accumulator.rs b/manta-crypto/src/accumulator.rs index b584915a2..45f170d0f 100644 --- a/manta-crypto/src/accumulator.rs +++ b/manta-crypto/src/accumulator.rs @@ -291,8 +291,6 @@ where } /// Constraint System Gadgets for Accumulators -#[cfg(feature = "constraint")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "constraint")))] pub mod constraint { use super::*; use crate::constraint::{Allocation, AllocationMode, Derived, Variable, VariableSource}; diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index 6ea563c9b..b6b76bb00 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -25,11 +25,8 @@ extern crate alloc; pub mod accumulator; pub mod commitment; +pub mod constraint; pub mod encryption; pub mod key; pub mod merkle_tree; pub mod rand; - -#[cfg(feature = "constraint")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "constraint")))] -pub mod constraint; From 73bf730a8e8e306e8c4e5f8c6f04a34fc8e5d1db Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 21 Nov 2021 12:39:31 -0800 Subject: [PATCH 129/275] wip: implement suggestions/changes from meeting --- Cargo.toml | 2 +- manta-accounting/Cargo.toml | 2 +- manta-accounting/src/transfer.rs | 179 ++++++++++++++++++++------- manta-accounting/src/wallet/state.rs | 5 +- manta-cli/Cargo.toml | 2 +- manta-codec/Cargo.toml | 2 +- manta-crypto/Cargo.toml | 2 +- manta-crypto/src/encryption.rs | 8 +- manta-crypto/src/key.rs | 42 ++++++- manta-pay/Cargo.toml | 2 +- manta-pay/src/crypto/key.rs | 6 +- manta-util/Cargo.toml | 2 +- 12 files changed, 193 insertions(+), 61 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d15475eb2..131a8d9b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "manta" -edition = "2018" +edition = "2021" version = "0.4.0" authors = ["Manta Network <contact@manta.network>"] readme = "README.md" diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 898e3e97a..7ae42841f 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "manta-accounting" -edition = "2018" +edition = "2021" version = "0.4.0" authors = ["Manta Network <contact@manta.network>"] readme = "README.md" diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index f52df626d..2c2dbe3bd 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -27,7 +27,7 @@ use manta_crypto::{ ProofSystem, Public, PublicOrSecret, Secret, Variable, VariableSource, }, encryption::{DecryptedMessage, EncryptedMessage, HybridPublicKeyEncryptionScheme}, - key::KeyAgreementScheme, + key::{KeyAgreementScheme, KeyDerivationFunction}, rand::{CryptoRng, Rand, RngCore, Sample}, }; use manta_util::{create_seal, from_variant_impl}; @@ -44,6 +44,16 @@ pub const fn has_public_participants( sources > 0 || sinks > 0 } +/// Generates a commitment trapdoor from a key agreement with `secret_key` and `public_key`. +#[inline] +fn generate_trapdoor<KA, F>(secret_key: &KA::SecretKey, public_key: &KA::PublicKey) -> F::Output +where + KA: KeyAgreementScheme, + F: KeyDerivationFunction<KA::SharedSecret>, +{ + F::derive(KA::agree(secret_key, public_key)) +} + /// Generates a UTXO, commiting `asset` with the given `trapdoor`. #[inline] fn generate_utxo<C, I, V>( @@ -61,19 +71,31 @@ where .commit(trapdoor) } +/// Generates a void number, commiting `secret_key` with the given `trapdoor`. +#[inline] +fn generate_void_number<C, SK>( + commitment_scheme: &C, + secret_key: &SK, + trapdoor: &C::Trapdoor, +) -> C::Output +where + C: CommitmentScheme + CommitmentInput<SK>, +{ + commitment_scheme.commit_one(secret_key, trapdoor) +} + /// Generates an ephemeral secret key, commiting to the `spend` key at the given `index` with the /// current `ledger_checkpoint`. #[inline] -fn generate_ephemeral_secret_key<C, L, B, PK, SK>( +fn generate_ephemeral_secret_key<C, L, B, PK>( commitment_scheme: &C, ledger_checkpoint: &L, index: &B, spend: &PK, ephemeral_key_trapdoor: &C::Trapdoor, -) -> SK +) -> C::Output where C: CommitmentScheme + CommitmentInput<L> + CommitmentInput<B> + CommitmentInput<PK>, - C::Output: Into<SK>, { commitment_scheme .start() @@ -81,16 +103,12 @@ where .update(index) .update(spend) .commit(ephemeral_key_trapdoor) - .into() } /// Transfer Configuration pub trait Configuration { - /// Shared Secret - type SharedSecret: Sample; - /// Key Agreement Scheme - type KeyAgreementScheme: KeyAgreementScheme<SharedSecret = Self::SharedSecret>; + type KeyAgreementScheme: KeyAgreementScheme; /// Secret Key Variable type SecretKeyVar: Variable<Self::ConstraintSystem, Type = SecretKey<Self>, Mode = Secret>; @@ -98,41 +116,63 @@ pub trait Configuration { /// Public Key Variable type PublicKeyVar: Variable<Self::ConstraintSystem, Type = PublicKey<Self>, Mode = Secret>; - /// Shared Secret Key Variable - type SharedSecretVar: Variable<Self::ConstraintSystem, Type = Self::SharedSecret, Mode = Secret>; - /// Key Agreement Scheme Variable - type KeyAgreementSchemeVar: KeyAgreementScheme< - SecretKey = Self::SecretKeyVar, - PublicKey = Self::PublicKeyVar, - SharedSecret = Self::SharedSecretVar, - > + Variable<Self::ConstraintSystem, Type = Self::KeyAgreementScheme, Mode = Constant>; + type KeyAgreementSchemeVar: KeyAgreementScheme<SecretKey = Self::SecretKeyVar, PublicKey = Self::PublicKeyVar> + + Variable<Self::ConstraintSystem, Type = Self::KeyAgreementScheme, Mode = Constant>; + + /// Trapdoor Derivation Function + type TrapdoorDerivationFunction: KeyDerivationFunction< + SharedSecret<Self>, + Output = Trapdoor<Self>, + >; + + /// Trapdoor Derivation Function Variable + type TrapdoorDerivationFunctionVar: KeyDerivationFunction< + SharedSecretVar<Self>, + Output = TrapdoorVar<Self>, + >; + + /// Ephemeral-Key Trapdoor + type EphemeralKeyTrapdoor: Sample; + + /// Ephemeral-Key Trapdoor Variable + type EphemeralKeyTrapdoorVar: Variable< + Self::ConstraintSystem, + Type = Self::EphemeralKeyTrapdoor, + Mode = Secret, + >; + + /// Ephemeral-Key Commitment Scheme + type EphemeralKeyCommitmentScheme: CommitmentScheme<Trapdoor = Self::EphemeralKeyTrapdoor, Output = SecretKey<Self>> + + CommitmentInput<Self::LedgerCheckpoint> + + CommitmentInput<u8> + + CommitmentInput<PublicKey<Self>>; + + /// Ephemeral-Key Commitment Scheme Variable + type EphemeralKeyCommitmentSchemeVar: CommitmentScheme<Trapdoor = Self::EphemeralKeyTrapdoorVar, Output = Self::SecretKeyVar> + + CommitmentInput<Self::LedgerCheckpointVar> + + CommitmentInput<Self::ByteVar> + + CommitmentInput<Self::PublicKeyVar> + + Variable<Self::ConstraintSystem, Type = Self::EphemeralKeyCommitmentScheme, Mode = Constant>; /// Commitment Scheme Output - type CommitmentSchemeOutput: PartialEq + Into<SecretKey<Self>>; + type CommitmentSchemeOutput: PartialEq; /// Commitment Scheme Output Variable - type CommitmentSchemeOutputVar: Into<Self::SecretKeyVar> - + Variable<Self::ConstraintSystem, Type = Self::CommitmentSchemeOutput, Mode = PublicOrSecret> + type CommitmentSchemeOutputVar: Variable<Self::ConstraintSystem, Type = Self::CommitmentSchemeOutput, Mode = PublicOrSecret> + Equal<Self::ConstraintSystem>; /// Commitment Scheme - type CommitmentScheme: CommitmentScheme<Trapdoor = Self::SharedSecret, Output = Self::CommitmentSchemeOutput> + type CommitmentScheme: CommitmentScheme<Output = Self::CommitmentSchemeOutput> + CommitmentInput<AssetId> + CommitmentInput<AssetValue> - + CommitmentInput<SecretKey<Self>> - + CommitmentInput<PublicKey<Self>> - + CommitmentInput<Self::LedgerCheckpoint> - + CommitmentInput<u8>; + + CommitmentInput<SecretKey<Self>>; /// Commitment Scheme Variable - type CommitmentSchemeVar: CommitmentScheme<Trapdoor = Self::SharedSecretVar, Output = Self::CommitmentSchemeOutputVar> + type CommitmentSchemeVar: CommitmentScheme<Output = Self::CommitmentSchemeOutputVar> + CommitmentInput<Self::AssetIdVar> + CommitmentInput<Self::AssetValueVar> + CommitmentInput<Self::SecretKeyVar> - + CommitmentInput<Self::PublicKeyVar> - + CommitmentInput<Self::LedgerCheckpointVar> - + CommitmentInput<Self::ByteVar> + Variable<Self::ConstraintSystem, Type = Self::CommitmentScheme, Mode = Constant>; /// UTXO Set Verifier @@ -209,6 +249,14 @@ pub type SecretKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreemen /// Public Key Type pub type PublicKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::PublicKey; +/// Shared Secret Type +pub type SharedSecret<C> = + <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret; + +/// Shared Secret Variable Type +pub type SharedSecretVar<C> = + <<C as Configuration>::KeyAgreementSchemeVar as KeyAgreementScheme>::SharedSecret; + /// Trapdoor Type pub type Trapdoor<C> = <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Trapdoor; @@ -266,6 +314,22 @@ pub type ProofInput<C> = <<C as Configuration>::ProofSystem as ProofSystem>::Inp /// Transfer Validity Proof Type pub type Proof<C> = <ProofSystemType<C> as ProofSystem>::Proof; +/* TODO: +pub struct Parameters<C> +where + C: Configuration, +{ + /// + ephemeral_key_commitment_scheme: C::EphemeralKeyCommitmentScheme, + + /// + certificate_commitment_scheme: C::CertificateCommitmentScheme, + + /// + utxo_set_verifier: C::UtxoSetVerifier, +} +*/ + /// Spending Key pub struct SpendingKey<C> where @@ -315,7 +379,10 @@ where &generate_utxo( commitment_scheme, asset, - &C::KeyAgreementScheme::agree(&self.spend, ephemeral_key), + &generate_trapdoor::<C::KeyAgreementScheme, C::TrapdoorDerivationFunction>( + &self.spend, + ephemeral_key, + ), ) == utxo } @@ -396,10 +463,13 @@ where asset: Asset, commitment_scheme: &C::CommitmentScheme, ) -> Self { - let trapdoor = C::KeyAgreementScheme::agree(&spend, &ephemeral_key); + let trapdoor = generate_trapdoor::<C::KeyAgreementScheme, C::TrapdoorDerivationFunction>( + &spend, + &ephemeral_key, + ); Self { utxo: generate_utxo(commitment_scheme, &asset, &trapdoor), - void_number: commitment_scheme.commit_one(&spend, &trapdoor), + void_number: generate_void_number(commitment_scheme, &spend, &trapdoor), spend, ephemeral_key, asset, @@ -582,14 +652,17 @@ where utxo_set_verifier: &C::UtxoSetVerifierVar, cs: &mut C::ConstraintSystem, ) -> AssetVar<C> { - let trapdoor = C::KeyAgreementSchemeVar::agree(&self.spend, &self.ephemeral_key); + let trapdoor = generate_trapdoor::< + C::KeyAgreementSchemeVar, + C::TrapdoorDerivationFunctionVar, + >(&self.spend, &self.ephemeral_key); cs.assert(self.utxo_membership_proof.verify( &generate_utxo(commitment_scheme, &self.asset, &trapdoor), utxo_set_verifier, )); cs.assert_eq( &self.void_number, - &commitment_scheme.commit_one(&self.spend, &trapdoor), + &generate_void_number(commitment_scheme, &self.spend, &trapdoor), ); self.asset } @@ -846,7 +919,10 @@ where utxo: generate_utxo( commitment_scheme, &asset, - &C::KeyAgreementScheme::agree(&ephemeral_key, &spend), + &generate_trapdoor::<C::KeyAgreementScheme, C::TrapdoorDerivationFunction>( + &ephemeral_key, + &spend, + ), ), spend, view, @@ -897,12 +973,13 @@ where self, index: C::ByteVar, ledger_checkpoint: &C::LedgerCheckpointVar, - ephemeral_key_trapdoor: &TrapdoorVar<C>, + ephemeral_key_trapdoor: &C::EphemeralKeyTrapdoorVar, + ephemeral_key_commitment_scheme: &C::EphemeralKeyCommitmentSchemeVar, commitment_scheme: &C::CommitmentSchemeVar, cs: &mut C::ConstraintSystem, ) -> AssetVar<C> { let ephemeral_key = generate_ephemeral_secret_key( - commitment_scheme, + ephemeral_key_commitment_scheme, ledger_checkpoint, &index, &self.spend, @@ -913,7 +990,10 @@ where &generate_utxo( commitment_scheme, &self.asset, - &C::KeyAgreementSchemeVar::agree(&ephemeral_key, &self.spend), + &generate_trapdoor::<C::KeyAgreementSchemeVar, C::TrapdoorDerivationFunctionVar>( + &ephemeral_key, + &self.spend, + ), ), ); self.asset @@ -1172,6 +1252,7 @@ where /// Generates a proving and verifying context for this transfer shape. #[inline] pub fn generate_context<R>( + ephemeral_key_commitment_scheme: &C::EphemeralKeyCommitmentScheme, commitment_scheme: &C::CommitmentScheme, utxo_set_verifier: &C::UtxoSetVerifier, rng: &mut R, @@ -1182,6 +1263,7 @@ where let mut cs = C::ProofSystem::for_unknown(); FullTransferVar::<C, SOURCES, SENDERS, RECEIVERS, SINKS>::new_unknown(&mut cs, Derived) .build_validity_constraints( + &ephemeral_key_commitment_scheme.as_known(&mut cs, Public), &commitment_scheme.as_known(&mut cs, Public), &utxo_set_verifier.as_known(&mut cs, Public), &mut cs, @@ -1193,6 +1275,7 @@ where #[inline] pub fn into_post<R>( self, + ephemeral_key_commitment_scheme: &C::EphemeralKeyCommitmentScheme, commitment_scheme: &C::CommitmentScheme, utxo_set_verifier: &C::UtxoSetVerifier, ledger_checkpoint: C::LedgerCheckpoint, @@ -1211,7 +1294,7 @@ where .enumerate() .map(|(i, r)| { let ephemeral_key = generate_ephemeral_secret_key( - commitment_scheme, + ephemeral_key_commitment_scheme, &ledger_checkpoint, &(i as u8), &r.spend, @@ -1224,7 +1307,13 @@ where ephemeral_key_trapdoor, ledger_checkpoint, } - .into_post(commitment_scheme, utxo_set_verifier, context, rng) + .into_post( + ephemeral_key_commitment_scheme, + commitment_scheme, + utxo_set_verifier, + context, + rng, + ) } } @@ -1254,7 +1343,7 @@ struct FullTransfer< sinks: [AssetValue; SINKS], /// Ephemeral Key Trapdoor - ephemeral_key_trapdoor: Trapdoor<C>, + ephemeral_key_trapdoor: C::EphemeralKeyTrapdoor, /// Ledger Checkpoint ledger_checkpoint: C::LedgerCheckpoint, @@ -1269,6 +1358,7 @@ where #[inline] fn into_post<R>( self, + ephemeral_key_commitment_scheme: &C::EphemeralKeyCommitmentScheme, commitment_scheme: &C::CommitmentScheme, utxo_set_verifier: &C::UtxoSetVerifier, context: &ProvingContext<C>, @@ -1283,6 +1373,7 @@ where let transfer: FullTransferVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> = self.as_known(&mut cs, Derived); transfer.build_validity_constraints( + &ephemeral_key_commitment_scheme.as_known(&mut cs, Public), &commitment_scheme.as_known(&mut cs, Public), &utxo_set_verifier.as_known(&mut cs, Public), &mut cs, @@ -1331,7 +1422,7 @@ struct FullTransferVar< sinks: Vec<C::AssetValueVar>, /// Ephemeral Key Trapdoor - ephemeral_key_trapdoor: TrapdoorVar<C>, + ephemeral_key_trapdoor: C::EphemeralKeyTrapdoorVar, /// Ledger Checkpoint ledger_checkpoint: C::LedgerCheckpointVar, @@ -1346,6 +1437,7 @@ where #[inline] fn build_validity_constraints( self, + ephemeral_key_commitment_scheme: &C::EphemeralKeyCommitmentSchemeVar, commitment_scheme: &C::CommitmentSchemeVar, utxo_set_verifier: &C::UtxoSetVerifierVar, cs: &mut C::ConstraintSystem, @@ -1375,6 +1467,7 @@ where index, ledger_checkpoint, ephemeral_key_trapdoor, + ephemeral_key_commitment_scheme, commitment_scheme, cs, ); @@ -1458,7 +1551,7 @@ where .into_iter() .map(|_| C::AssetValueVar::new_unknown(cs, Public)) .collect(), - ephemeral_key_trapdoor: TrapdoorVar::<C>::new_unknown(cs, mode), + ephemeral_key_trapdoor: C::EphemeralKeyTrapdoorVar::new_unknown(cs, mode), ledger_checkpoint: C::LedgerCheckpointVar::new_unknown(cs, mode), }, } diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 25adb9cef..403cbc2ab 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -82,7 +82,10 @@ fn withdraw_unchecked(balance: Option<&mut AssetValue>, withdraw: AssetValue) { .expect("Overdrawn balance state."); } -impl BalanceState for Vec<Asset> { +/// Vector [`BalanceState`] Implementation +pub type VecBalanceState = Vec<Asset>; + +impl BalanceState for VecBalanceState { #[inline] fn balance(&self, id: AssetId) -> AssetValue { self.iter() diff --git a/manta-cli/Cargo.toml b/manta-cli/Cargo.toml index f10c0a39d..46032293b 100644 --- a/manta-cli/Cargo.toml +++ b/manta-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "manta-cli" -edition = "2018" +edition = "2021" version = "0.4.0" authors = ["Manta Network <contact@manta.network>"] readme = "README.md" diff --git a/manta-codec/Cargo.toml b/manta-codec/Cargo.toml index e8e727beb..4fa336df4 100644 --- a/manta-codec/Cargo.toml +++ b/manta-codec/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "manta-codec" -edition = "2018" +edition = "2021" version = "0.4.0" authors = ["Manta Network <contact@manta.network>"] readme = "README.md" diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index c68e38027..af75492df 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "manta-crypto" -edition = "2018" +edition = "2021" version = "0.4.0" authors = ["Manta Network <contact@manta.network>"] readme = "README.md" diff --git a/manta-crypto/src/encryption.rs b/manta-crypto/src/encryption.rs index 6297f4792..e0d90057c 100644 --- a/manta-crypto/src/encryption.rs +++ b/manta-crypto/src/encryption.rs @@ -57,7 +57,7 @@ pub trait HybridPublicKeyEncryptionScheme: SymmetricKeyEncryptionScheme { /// Key Derivation Function Type type KeyDerivationFunction: KeyDerivationFunction< <Self::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret, - Self::Key, + Output = Self::Key, >; } @@ -76,7 +76,7 @@ pub struct Hybrid<K, S, F> where K: KeyAgreementScheme, S: SymmetricKeyEncryptionScheme, - F: KeyDerivationFunction<K::SharedSecret, S::Key>, + F: KeyDerivationFunction<K::SharedSecret, Output = S::Key>, { /// Type Parameter Marker __: PhantomData<(K, S, F)>, @@ -86,7 +86,7 @@ impl<K, S, F> SymmetricKeyEncryptionScheme for Hybrid<K, S, F> where K: KeyAgreementScheme, S: SymmetricKeyEncryptionScheme, - F: KeyDerivationFunction<K::SharedSecret, S::Key>, + F: KeyDerivationFunction<K::SharedSecret, Output = S::Key>, { type Key = S::Key; @@ -109,7 +109,7 @@ impl<K, S, F> HybridPublicKeyEncryptionScheme for Hybrid<K, S, F> where K: KeyAgreementScheme, S: SymmetricKeyEncryptionScheme, - F: KeyDerivationFunction<K::SharedSecret, S::Key>, + F: KeyDerivationFunction<K::SharedSecret, Output = S::Key>, { type KeyAgreementScheme = K; type KeyDerivationFunction = F; diff --git a/manta-crypto/src/key.rs b/manta-crypto/src/key.rs index 9195b5aad..05f40e0a1 100644 --- a/manta-crypto/src/key.rs +++ b/manta-crypto/src/key.rs @@ -16,6 +16,15 @@ //! Cryptographic Key Primitives +/// Key Derivation Function +pub trait KeyDerivationFunction<S> { + /// + type Output; + + /// Derives an output key from `secret` computed from a cryptographic agreement scheme. + fn derive(secret: S) -> Self::Output; +} + /// Key Agreement Scheme /// /// # Specification @@ -78,8 +87,33 @@ pub trait KeyAgreementScheme { } } -/// Key Derivation Function -pub trait KeyDerivationFunction<A, B> { - /// Derives an output key from `secret` computed from a key-agreement scheme. - fn derive(secret: A) -> B; +/// Key Agreement Scheme with an attached Key Derivation Function +pub trait KeyAgreementWithDerivation: KeyAgreementScheme { + /// Output Key Type + type Output; + + /// Key Derivation Function Type + type KeyDerivationFunction: KeyDerivationFunction<Self::SharedSecret, Output = Self::Output>; + + /// Computes the shared secret given the known `secret_key` and the given `public_key` and then + /// uses the key derivation function to derive a final shared secret. + #[inline] + fn agree_derive(secret_key: &Self::SecretKey, public_key: &Self::PublicKey) -> Self::Output { + Self::KeyDerivationFunction::derive(Self::agree(secret_key, public_key)) + } + + /// Computes the shared secret given the known `secret_key` and the given `public_key` and then + /// uses the key derivation function to derive a final shared secret. + /// + /// # Implementation Note + /// + /// This method is an optimization path for [`agree_derive`](Self::agree_derive). See + /// [`KeyAgreementScheme::agree_owned`] for more on this optimization. + #[inline] + fn agree_derive_owned( + secret_key: Self::SecretKey, + public_key: Self::PublicKey, + ) -> Self::Output { + Self::KeyDerivationFunction::derive(Self::agree_owned(secret_key, public_key)) + } } diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index a023def3d..a93151afc 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "manta-pay" -edition = "2018" +edition = "2021" version = "0.4.0" authors = ["Manta Network <contact@manta.network>"] readme = "README.md" diff --git a/manta-pay/src/crypto/key.rs b/manta-pay/src/crypto/key.rs index bc7c8d0d0..e60cb6d55 100644 --- a/manta-pay/src/crypto/key.rs +++ b/manta-pay/src/crypto/key.rs @@ -62,12 +62,14 @@ where /// Blake2s KDF pub struct Blake2sKdf; -impl<T> KeyDerivationFunction<T, [u8; 32]> for Blake2sKdf +impl<T> KeyDerivationFunction<T> for Blake2sKdf where T: AsRef<[u8]>, { + type Output = [u8; 32]; + #[inline] - fn derive(secret: T) -> [u8; 32] { + fn derive(secret: T) -> Self::Output { let mut hasher = Blake2s::new(); hasher.update(secret.as_ref()); hasher.update(b"manta kdf instantiated with blake2s hash function"); diff --git a/manta-util/Cargo.toml b/manta-util/Cargo.toml index 1534af460..c4b62419f 100644 --- a/manta-util/Cargo.toml +++ b/manta-util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "manta-util" -edition = "2018" +edition = "2021" version = "0.4.0" authors = ["Manta Network <contact@manta.network>"] readme = "README.md" From 8101ea0299cc6a2e91d0fe8ac51f154c0886ee48 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 21 Nov 2021 12:52:44 -0800 Subject: [PATCH 130/275] feat: add some optimization paths for hybrid encryption --- manta-crypto/src/encryption.rs | 31 +++++++++++++++++++++++-------- manta-crypto/src/key.rs | 5 +++++ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/manta-crypto/src/encryption.rs b/manta-crypto/src/encryption.rs index e0d90057c..e2100be96 100644 --- a/manta-crypto/src/encryption.rs +++ b/manta-crypto/src/encryption.rs @@ -59,6 +59,18 @@ pub trait HybridPublicKeyEncryptionScheme: SymmetricKeyEncryptionScheme { <Self::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret, Output = Self::Key, >; + + /// Computes the shared secret given the known `secret_key` and the given `public_key` and then + /// uses the key derivation function to derive a final shared secret. + /// + /// # Implementation Note + /// + /// This method is an optimization path for calling [`KeyAgreementScheme::agree`] and then + /// [`KeyDerivationFunction::derive`]. + #[inline] + fn agree_derive(secret_key: &SecretKey<Self>, public_key: &PublicKey<Self>) -> Self::Key { + Self::KeyDerivationFunction::derive(Self::KeyAgreementScheme::agree(secret_key, public_key)) + } } /// Secret Key Type @@ -70,6 +82,15 @@ pub type PublicKey<H> = <<H as HybridPublicKeyEncryptionScheme>::KeyAgreementScheme as KeyAgreementScheme>::PublicKey; /// Hybrid Public Key Encryption Scheme +/// +/// # Optimization Note +/// +/// Since [`Hybrid`] takes the three parts of the [`HybridPublicKeyEncryptionScheme`] implementation +/// as type parameters, the [`agree_derive`] optimization cannot be implemented. To implement a +/// custom optimization, the entire [`HybridPublicKeyEncryptionScheme`] trait will need to be +/// implemented. +/// +/// [`agree_derive`]: HybridPublicKeyEncryptionScheme::agree_derive #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, PartialOrd)] pub struct Hybrid<K, S, F> @@ -141,10 +162,7 @@ where ) -> Self { Self { ciphertext: H::encrypt( - H::KeyDerivationFunction::derive(H::KeyAgreementScheme::agree( - &ephemeral_secret_key, - public_key, - )), + H::agree_derive(&ephemeral_secret_key, public_key), plaintext, ), ephemeral_public_key: H::KeyAgreementScheme::derive_owned(ephemeral_secret_key), @@ -162,10 +180,7 @@ where #[inline] pub fn decrypt(self, secret_key: &SecretKey<H>) -> Result<DecryptedMessage<H>, Self> { match H::decrypt( - H::KeyDerivationFunction::derive(H::KeyAgreementScheme::agree( - secret_key, - &self.ephemeral_public_key, - )), + H::agree_derive(secret_key, &self.ephemeral_public_key), &self.ciphertext, ) { Some(plaintext) => Ok(DecryptedMessage::new(plaintext, self.ephemeral_public_key)), diff --git a/manta-crypto/src/key.rs b/manta-crypto/src/key.rs index 05f40e0a1..d6c3feed4 100644 --- a/manta-crypto/src/key.rs +++ b/manta-crypto/src/key.rs @@ -97,6 +97,11 @@ pub trait KeyAgreementWithDerivation: KeyAgreementScheme { /// Computes the shared secret given the known `secret_key` and the given `public_key` and then /// uses the key derivation function to derive a final shared secret. + /// + /// # Implementation Note + /// + /// This method is an optimization path for calling [`KeyAgreementScheme::agree`] and then + /// [`KeyDerivationFunction::derive`]. #[inline] fn agree_derive(secret_key: &Self::SecretKey, public_key: &Self::PublicKey) -> Self::Output { Self::KeyDerivationFunction::derive(Self::agree(secret_key, public_key)) From 42c16a33983bf0a77be92e0827d505337a324d16 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 22 Nov 2021 00:03:19 -0800 Subject: [PATCH 131/275] wip: add hierarchical keys to signer --- manta-accounting/src/key.rs | 317 ++++++++++++++++---------- manta-accounting/src/transfer.rs | 13 +- manta-accounting/src/wallet/signer.rs | 48 ++-- manta-accounting/src/wallet/state.rs | 6 +- 4 files changed, 241 insertions(+), 143 deletions(-) diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index cd7c64d0d..59020dd2e 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -14,107 +14,234 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Hierarchical Key Tables +//! Hierarchical Key Derivation Schemes use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; -/// Hierarchical Key Table Parameter -pub trait HierarchicalKeyTableParameter: +/// Hierarchical Key Derivation Parameter +pub trait HierarchicalKeyDerivationParameter: Clone + Copy + Default + PartialOrd + From<usize> + Into<usize> { /// Increments the key parameter by one unit. fn increment(&mut self); } -/// Hierarchical Key Table -pub trait HierarchicalKeyTable { +/// Hierarchical Key Derivation Scheme +pub trait HierarchicalKeyDerivationScheme { /// Account Type - type Account: HierarchicalKeyTableParameter; + type Account: HierarchicalKeyDerivationParameter; /// Index Type - type Index: HierarchicalKeyTableParameter; - - /// Key Kind Type - type Kind; + type Index: HierarchicalKeyDerivationParameter; /// Secret Key Type type SecretKey; - /// Key Access Error Type + /// Key Derivation Error Type type Error; - /// Returns the secret key associated to `account` and `index` of the given `kind`. - fn get( + /// + fn derive( &self, account: Self::Account, - index: Self::Index, - kind: &Self::Kind, - ) -> Result<Self::SecretKey, Self::Error>; + spend: Self::Index, + view: Self::Index, + ) -> Result<Key<Self>, Self::Error>; } -impl<H> HierarchicalKeyTable for &H +impl<H> HierarchicalKeyDerivationScheme for &H where - H: HierarchicalKeyTable, + H: HierarchicalKeyDerivationScheme, { type Account = H::Account; type Index = H::Index; - type Kind = H::Kind; - type SecretKey = H::SecretKey; type Error = H::Error; #[inline] - fn get( + fn derive( &self, account: Self::Account, - index: Self::Index, - kind: &Self::Kind, - ) -> Result<Self::SecretKey, Self::Error> { - (*self).get(account, index, kind) + spend: Self::Index, + view: Self::Index, + ) -> Result<Key<Self>, Self::Error> { + let key = (*self).derive(account, spend, view)?; + Ok(Key { + spend: key.spend, + view: key.view, + }) } } -/// Account Map +/// Hierarchical Key Derivation Key Type +pub struct Key<H> +where + H: HierarchicalKeyDerivationScheme + ?Sized, +{ + /// Spend Part of the Key + pub spend: H::SecretKey, + + /// View Part of the Key + pub view: H::SecretKey, +} + +impl<H> Key<H> +where + H: HierarchicalKeyDerivationScheme + ?Sized, +{ + /// Builds a new [`Key`] from `spend` and `view`. + #[inline] + pub fn new(spend: H::SecretKey, view: H::SecretKey) -> Self { + Self { spend, view } + } +} + +/// Error Type +pub enum Error<H> +where + H: HierarchicalKeyDerivationScheme, +{ + /// Exceeded Current Maximum Spend Index + /// + /// See the [`increment_spend`](AccountMap::increment_spend) method on [`AccountMap`] for more. + ExceedingCurrentMaximumSpendIndex, + + /// Exceeded Current Maximum View Index + /// + /// See the [`increment_view`](AccountMap::increment_view) method on [`AccountMap`] for more. + ExceedingCurrentMaximumViewIndex, + + /// Key Derivation Error + KeyDerivationError(H::Error), +} + +/// Key Index Type +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Default, Eq, PartialEq)] +pub struct Index<H> +where + H: HierarchicalKeyDerivationScheme, +{ + /// Spend Part of the Key Index + pub spend: H::Index, + + /// View Part of the Key Index + pub view: H::Index, +} + +impl<H> Index<H> +where + H: HierarchicalKeyDerivationScheme, +{ + /// Builds a new [`Index`] using `spend` and `view`. + #[inline] + pub fn new(spend: H::Index, view: H::Index) -> Self { + Self { spend, view } + } +} + +/// Account Keys +#[derive(derivative::Derivative)] +pub struct AccountKeys<'h, H> +where + H: HierarchicalKeyDerivationScheme, +{ + /// Hierarchical Key Derivation Scheme + keys: &'h H, + + /// Account Parameter + account: H::Account, + + /// Maximum Key Index + max_index: Index<H>, +} + +impl<'h, H> AccountKeys<'h, H> +where + H: HierarchicalKeyDerivationScheme, +{ + /// Builds a new [`AccountKeys`] from `keys`, `account`, and `max_index`. + #[inline] + fn new(keys: &'h H, account: H::Account, max_index: Index<H>) -> Self { + Self { + keys, + account, + max_index, + } + } + + /// Returns the key for this account at the `spend` and `view` indices, if those indices do not + /// exceed the maximum indices. + #[inline] + pub fn key(&self, spend: H::Index, view: H::Index) -> Result<Key<H>, Error<H>> { + if spend <= self.max_index.spend { + if view <= self.max_index.view { + self.keys + .derive(self.account, spend, view) + .map_err(Error::KeyDerivationError) + } else { + Err(Error::ExceedingCurrentMaximumViewIndex) + } + } else { + Err(Error::ExceedingCurrentMaximumSpendIndex) + } + } +} + +/// Account Map Trait pub trait AccountMap<H> where - H: HierarchicalKeyTable, + H: HierarchicalKeyDerivationScheme, { - /// Returns the maximum index associated to `account`. - fn max_index(&self, account: H::Account) -> Option<H::Index>; + /// Returns the maximum spend and view indices for `account`, if it exists. + fn max_index(&self, account: H::Account) -> Option<Index<H>>; - /// Creates a new account, returning the new account parameter. + /// Adds a new account to the map, returning the new account parameter. fn create_account(&mut self) -> H::Account; - /// Increments the index on the existing account, returning the new index parameter. - fn increment_index(&mut self, account: H::Account) -> Option<H::Index>; + /// Increments the maximum spend index for `account`, if it exists, returning the current + /// maximum indices. + fn increment_spend(&mut self, account: H::Account) -> Option<Index<H>>; + + /// Increments the maximum view index for `account`, if it exists, returning the current + /// maximum indices. + fn increment_view(&mut self, account: H::Account) -> Option<Index<H>>; } /// [`Vec`] Account Map Type -pub type VecAccountMap<H> = Vec<<H as HierarchicalKeyTable>::Index>; +pub type VecAccountMap<H> = Vec<Index<H>>; impl<H> AccountMap<H> for VecAccountMap<H> where - H: HierarchicalKeyTable, + H: HierarchicalKeyDerivationScheme, { #[inline] - fn max_index(&self, account: H::Account) -> Option<H::Index> { + fn max_index(&self, account: H::Account) -> Option<Index<H>> { self.get(account.into()).copied() } #[inline] fn create_account(&mut self) -> H::Account { - self.push(0.into()); + self.push(Default::default()); (self.len() - 1).into() } #[inline] - fn increment_index(&mut self, account: H::Account) -> Option<H::Index> { + fn increment_spend(&mut self, account: H::Account) -> Option<Index<H>> { + self.get_mut(account.into()).map(move |index| { + index.spend.increment(); + *index + }) + } + + #[inline] + fn increment_view(&mut self, account: H::Account) -> Option<Index<H>> { self.get_mut(account.into()).map(move |index| { - index.increment(); + index.view.increment(); *index }) } @@ -133,11 +260,11 @@ where )] pub struct AccountTable<H, M = VecAccountMap<H>> where - H: HierarchicalKeyTable, + H: HierarchicalKeyDerivationScheme, M: AccountMap<H>, { - /// Hierarchical Key Table - table: H, + /// Hierarchical Key Derivation Scheme + keys: H, /// Account Map accounts: M, @@ -145,113 +272,63 @@ where impl<H, M> AccountTable<H, M> where - H: HierarchicalKeyTable, + H: HierarchicalKeyDerivationScheme, M: AccountMap<H>, { - /// Builds a new [`AccountTable`] from a hierarchical key `table`. + /// Builds a new [`AccountTable`] using `keys` and a default account map. #[inline] - pub fn new(table: H) -> Self + pub fn new(keys: H) -> Self where M: Default, { - Self::with_accounts(table, Default::default()) + Self::with_accounts(keys, Default::default()) } - /// Builds a new [`AccountTable`] from `table` and `accounts`. + /// Builds a new [`AccountTable`] using `keys` and `accounts`. #[inline] - pub fn with_accounts(table: H, accounts: M) -> Self { - Self { table, accounts } + pub fn with_accounts(keys: H, accounts: M) -> Self { + Self { keys, accounts } } - /// Returns the key associated to `account`, `index`, and `kind`. + /// Returns the key associated to `account` if it exists, using the `spend` and `view` indices + /// if they do not exceed the maximum indices. #[inline] pub fn key( &self, account: H::Account, - index: H::Index, - kind: &H::Kind, - ) -> Option<Result<H::SecretKey, H::Error>> { - self.subtable(account, index).map(move |st| st.key(kind)) + spend: H::Index, + view: H::Index, + ) -> Option<Result<Key<H>, Error<H>>> { + self.get(account).map(move |k| k.key(spend, view)) } - /// Returns a subtable of `self` with fixed `account` and `index` parameters. + /// Returns the account keys for `account` if it exists. #[inline] - pub fn subtable(&self, account: H::Account, index: H::Index) -> Option<AccountSubTable<H>> { - match self.accounts.max_index(account) { - Some(max_index) if index <= max_index => { - Some(AccountSubTable::new(&self.table, account, index)) - } - _ => None, - } + pub fn get(&self, account: H::Account) -> Option<AccountKeys<H>> { + Some(AccountKeys::new( + &self.keys, + account, + self.accounts.max_index(account)?, + )) } - /// Creates a new account, returning the new account parameter. + /// Adds a new account to the map, returning the new account parameter. #[inline] pub fn create_account(&mut self) -> H::Account { self.accounts.create_account() } - /// Increments the index on the existing account, returning the new index parameter. - #[inline] - pub fn increment_index(&mut self, account: H::Account) -> Option<H::Index> { - self.accounts.increment_index(account) - } -} - -/// Account Sub-Table -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Debug(bound = "H: Debug, H::Account: Debug, H::Index: Debug"), - Eq(bound = "H: Eq, H::Account: Eq, H::Index: Eq"), - Hash(bound = "H: Hash, H::Account: Hash, H::Index: Hash"), - PartialEq(bound = "H: PartialEq, H::Account: PartialEq, H::Index: PartialEq") -)] -pub struct AccountSubTable<'t, H> -where - H: HierarchicalKeyTable, -{ - /// Hierarchical Key Table - table: &'t H, - - /// Account Parameter - account: H::Account, - - /// Index Parameter - index: H::Index, -} - -impl<'t, H> AccountSubTable<'t, H> -where - H: HierarchicalKeyTable, -{ - /// Builds a new [`AccountSubTable`] for `table`, `account`, and `index`. - #[inline] - fn new(table: &'t H, account: H::Account, index: H::Index) -> Self { - Self { - table, - account, - index, - } - } - - /// Returns the inner account parameter of this subtable. - #[inline] - pub fn account(&self) -> H::Account { - self.account - } - - /// Returns the inner index parameter of this subtable. + /// Increments the maximum spend index for `account`, if it exists, returning the current + /// maximum indices. #[inline] - pub fn index(&self) -> H::Index { - self.index + pub fn increment_spend(&mut self, account: H::Account) -> Option<Index<H>> { + self.accounts.increment_spend(account) } - /// Returns the key of the given `kind` from the hierarchical key table with a fixed account - /// and index. + /// Increments the maximum view index for `account`, if it exists, returning the current + /// maximum indices. #[inline] - pub fn key(&self, kind: &H::Kind) -> Result<H::SecretKey, H::Error> { - self.table.get(self.account, self.index, kind) + pub fn increment_view(&mut self, account: H::Account) -> Option<Index<H>> { + self.accounts.increment_view(account) } } diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 2c2dbe3bd..bf21aad02 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -1736,7 +1736,18 @@ where let source_posting_keys = ledger .check_source_balances(self.sources) .map_err(TransferPostError::InsufficientPublicBalance)?; - // FIXME: The ledger needs to check that the senders are all unique! + + for (i, p) in self.sender_posts.iter().enumerate() { + if self + .sender_posts + .iter() + .skip(i + 1) + .any(move |q| p.void_number == q.void_number) + { + return Err(SenderPostError::AssetSpent.into()); + } + } + let sender_posting_keys = self .sender_posts .into_iter() diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 761e993a7..3c35c70c5 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -106,10 +106,10 @@ where /// Future for the [`rollback`](Self::rollback) method. type RollbackFuture: Future<Output = Result<(), Self::Error>>; - /// Receiver Future Type + /// Receiving Key Future Type /// - /// Future for the [`receiver`](Self::receiver) method. - type ReceiverFuture: Future<Output = ReceiverResult<H, C, Self>>; + /// Future for the [`receiving_key`](Self::receiving_key) method. + type ReceivingKeyFuture: Future<Output = ReceivingKeyResult<H, C, Self>>; /// Error Type type Error; @@ -156,7 +156,12 @@ where /// This method does not interact with the other methods on [`Connection`] so it can be called /// at any point in between calls to [`sync`](Self::sync), [`sign`](Self::sign), and other /// rollback related methods. - fn receiver(&mut self) -> Self::ReceiverFuture; + fn receiving_key( + &mut self, + account: H::Account, + index: H::Index, + view_key_index: H::Index, + ) -> Self::ReceivingKeyFuture; } /// Synchronization State @@ -174,18 +179,18 @@ pub enum SyncState { /// Synchronization Result /// -/// See the [`sync`](Connection::sync) method on [`Connection`] for more information. +/// See the [`sync`](Connection::sync) method on [`Connection`] for more. pub type SyncResult<H, C, S> = Result<SyncResponse, Error<H, C, <S as Connection<H, C>>::Error>>; /// Signing Result /// -/// See the [`sign`](Connection::sign) method on [`Connection`] for more information. +/// See the [`sign`](Connection::sign) method on [`Connection`] for more. pub type SignResult<H, C, S> = Result<SignResponse<C>, Error<H, C, <S as Connection<H, C>>::Error>>; -/// Receiver Result +/// Receving Key Result /// -/// See the [`receiver`](Connection::receiver) method on [`Connection`] for more information. -pub type ReceiverResult<H, C, S> = +/// See the [`receiving_key`](Connection::receiving_key) method on [`Connection`] for more. +pub type ReceivingKeyResult<H, C, S> = Result<ReceivingKey<C>, Error<H, C, <S as Connection<H, C>>::Error>>; /// Signer Synchronization Response @@ -326,6 +331,9 @@ where /// Spending Key spending_key: SpendingKey<C>, + /// Ephemeral Key Commitment Scheme + ephemeral_key_commitment_scheme: C::EphemeralKeyCommitmentScheme, + /// Commitment Scheme commitment_scheme: C::CommitmentScheme, @@ -353,6 +361,7 @@ where #[inline] fn new_inner( spending_key: SpendingKey<C>, + ephemeral_key_commitment_scheme: C::EphemeralKeyCommitmentScheme, commitment_scheme: C::CommitmentScheme, proving_context: ProvingContext<C>, utxo_set: C::UtxoSet, @@ -362,6 +371,7 @@ where ) -> Self { Self { spending_key, + ephemeral_key_commitment_scheme, commitment_scheme, proving_context, utxo_set, @@ -380,12 +390,14 @@ where #[inline] pub fn new( spending_key: SpendingKey<C>, + ephemeral_key_commitment_scheme: C::EphemeralKeyCommitmentScheme, commitment_scheme: C::CommitmentScheme, proving_context: ProvingContext<C>, rng: C::Rng, ) -> Self { Self::new_inner( spending_key, + ephemeral_key_commitment_scheme, commitment_scheme, proving_context, Default::default(), @@ -453,11 +465,8 @@ where /// Builds the pre-sender associated to `ephemeral_key` and `asset`. #[inline] fn build_pre_sender(&self, ephemeral_key: PublicKey<C>, asset: Asset) -> PreSender<C> { - /* TODO: self.spending_key .sender(ephemeral_key, asset, &self.commitment_scheme) - */ - todo!() } /// Selects the pre-senders which collectively own at least `asset`, returning any change. @@ -490,11 +499,12 @@ where const SINKS: usize, >( &mut self, + ledger_checkpoint: C::LedgerCheckpoint, transfer: Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>, ) -> Result<TransferPost<C>, Error<C::HierarchicalKeyTable, C>> { - /* TODO: transfer .into_post( + &self.ephemeral_key_commitment_scheme, &self.commitment_scheme, self.utxo_set.verifier(), ledger_checkpoint, @@ -502,8 +512,6 @@ where &mut self.rng, ) .map_err(Error::ProofSystemError) - */ - todo!() } /* TODO: @@ -710,7 +718,7 @@ where self.commit(); match transaction { Transaction::Mint(asset) => { - /* TODO: + /* let mint_post = self.build_post(Mint::build(asset, self.spending_key.receiver(asset)))?; self.pending_assets.insert = @@ -751,7 +759,7 @@ where /// Generates a new [`ReceivingKey`] for `self` to receive assets. #[inline] - pub fn receiver(&mut self) -> ReceiverResult<C::HierarchicalKeyTable, C, Self> { + pub fn receiving_key(&mut self) -> ReceivingKeyResult<C::HierarchicalKeyTable, C, Self> { Ok(self.spending_key.derive()) } } @@ -768,7 +776,7 @@ where type RollbackFuture = Ready<Result<(), Self::Error>>; - type ReceiverFuture = Ready<ReceiverResult<C::HierarchicalKeyTable, C, Self>>; + type ReceivingKeyFuture = Ready<ReceivingKeyResult<C::HierarchicalKeyTable, C, Self>>; type Error = Infallible; @@ -807,8 +815,8 @@ where } #[inline] - fn receiver(&mut self) -> Self::ReceiverFuture { - future::ready(self.receiver()) + fn receiving_key(&mut self) -> Self::ReceivingKeyFuture { + future::ready(self.receiving_key()) } } diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 403cbc2ab..3a904fb76 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -339,8 +339,10 @@ where /// #[inline] - pub async fn receiver(&mut self) -> Result<ReceivingKey<C>, signer::Error<H, C, S::Error>> { - self.signer.receiver().await + pub async fn receiving_key( + &mut self, + ) -> Result<ReceivingKey<C>, signer::Error<H, C, S::Error>> { + self.signer.receiving_key().await } } From f83166809a1067cfe8db6c3a369acd98876e6a6b Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 22 Nov 2021 02:24:35 -0800 Subject: [PATCH 132/275] wip: clean up changes from accounting/key --- manta-accounting/src/key.rs | 215 ++++++++++++++++++++----- manta-accounting/src/wallet/signer.rs | 217 ++++++++++++++++++-------- manta-accounting/src/wallet/state.rs | 15 +- manta-crypto/src/encryption.rs | 15 ++ 4 files changed, 352 insertions(+), 110 deletions(-) diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index 59020dd2e..1bd8f8036 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -18,6 +18,7 @@ use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; +use manta_crypto::key::KeyAgreementScheme; /// Hierarchical Key Derivation Parameter pub trait HierarchicalKeyDerivationParameter: @@ -41,18 +42,39 @@ pub trait HierarchicalKeyDerivationScheme { /// Key Derivation Error Type type Error; - /// + /// Derives a pair of secret keys for `account` using the `spend` and `view` indices. fn derive( &self, account: Self::Account, spend: Self::Index, view: Self::Index, - ) -> Result<Key<Self>, Self::Error>; + ) -> Result<SecretKeyPair<Self>, Self::Error>; + + /// + #[inline] + fn derive_spend( + &self, + account: Self::Account, + spend: Self::Index, + ) -> Result<Self::SecretKey, Self::Error> { + Ok(self.derive(account, spend, Default::default())?.spend) + } + + /// + #[inline] + fn derive_view( + &self, + account: Self::Account, + spend: Self::Index, + view: Self::Index, + ) -> Result<Self::SecretKey, Self::Error> { + Ok(self.derive(account, spend, view)?.view) + } } impl<H> HierarchicalKeyDerivationScheme for &H where - H: HierarchicalKeyDerivationScheme, + H: HierarchicalKeyDerivationScheme + ?Sized, { type Account = H::Account; @@ -68,42 +90,66 @@ where account: Self::Account, spend: Self::Index, view: Self::Index, - ) -> Result<Key<Self>, Self::Error> { + ) -> Result<SecretKeyPair<Self>, Self::Error> { let key = (*self).derive(account, spend, view)?; - Ok(Key { + Ok(SecretKeyPair { spend: key.spend, view: key.view, }) } } -/// Hierarchical Key Derivation Key Type -pub struct Key<H> +/// Hierarchical Key Derivation Secret Key Pair +pub struct SecretKeyPair<H> where H: HierarchicalKeyDerivationScheme + ?Sized, { - /// Spend Part of the Key - pub spend: H::SecretKey, + /// Spend Part of the Key Pair + spend: H::SecretKey, - /// View Part of the Key - pub view: H::SecretKey, + /// View Part of the Key Pair + view: H::SecretKey, } -impl<H> Key<H> +impl<H> SecretKeyPair<H> where H: HierarchicalKeyDerivationScheme + ?Sized, { - /// Builds a new [`Key`] from `spend` and `view`. + /// Builds a new [`SecretKeyPair`] from `spend` and `view`. #[inline] pub fn new(spend: H::SecretKey, view: H::SecretKey) -> Self { Self { spend, view } } + + /// Derives the public key pair for `self`. + #[inline] + pub fn derive<K>(self) -> PublicKeyPair<K> + where + K: KeyAgreementScheme<SecretKey = H::SecretKey>, + { + PublicKeyPair { + spend: K::derive_owned(self.spend), + view: K::derive_owned(self.view), + } + } +} + +/// Public Key Pair +pub struct PublicKeyPair<K> +where + K: KeyAgreementScheme, +{ + /// Spend Part of the Key Pair + pub spend: K::PublicKey, + + /// View Part of the Key Pair + pub view: K::PublicKey, } /// Error Type pub enum Error<H> where - H: HierarchicalKeyDerivationScheme, + H: HierarchicalKeyDerivationScheme + ?Sized, { /// Exceeded Current Maximum Spend Index /// @@ -124,7 +170,7 @@ where #[derivative(Clone, Copy, Default, Eq, PartialEq)] pub struct Index<H> where - H: HierarchicalKeyDerivationScheme, + H: HierarchicalKeyDerivationScheme + ?Sized, { /// Spend Part of the Key Index pub spend: H::Index, @@ -135,7 +181,7 @@ where impl<H> Index<H> where - H: HierarchicalKeyDerivationScheme, + H: HierarchicalKeyDerivationScheme + ?Sized, { /// Builds a new [`Index`] using `spend` and `view`. #[inline] @@ -148,7 +194,7 @@ where #[derive(derivative::Derivative)] pub struct AccountKeys<'h, H> where - H: HierarchicalKeyDerivationScheme, + H: HierarchicalKeyDerivationScheme + ?Sized, { /// Hierarchical Key Derivation Scheme keys: &'h H, @@ -162,7 +208,7 @@ where impl<'h, H> AccountKeys<'h, H> where - H: HierarchicalKeyDerivationScheme, + H: HierarchicalKeyDerivationScheme + ?Sized, { /// Builds a new [`AccountKeys`] from `keys`, `account`, and `max_index`. #[inline] @@ -174,15 +220,15 @@ where } } - /// Returns the key for this account at the `spend` and `view` indices, if those indices do not - /// exceed the maximum indices. + /// Performs the bounds check on `spend` and `view` and then runs `f`. #[inline] - pub fn key(&self, spend: H::Index, view: H::Index) -> Result<Key<H>, Error<H>> { + fn with_bounds_check<T, F>(&self, spend: H::Index, view: H::Index, f: F) -> Result<T, Error<H>> + where + F: FnOnce(&'h H, H::Account) -> Result<T, H::Error>, + { if spend <= self.max_index.spend { if view <= self.max_index.view { - self.keys - .derive(self.account, spend, view) - .map_err(Error::KeyDerivationError) + f(self.keys, self.account).map_err(Error::KeyDerivationError) } else { Err(Error::ExceedingCurrentMaximumViewIndex) } @@ -190,13 +236,85 @@ where Err(Error::ExceedingCurrentMaximumSpendIndex) } } + + /// Returns the spend key for this account at the `spend` index, if it does not exceed the + /// maximum index. + #[inline] + pub fn spend_key(&self, spend: H::Index) -> Result<H::SecretKey, Error<H>> { + self.with_bounds_check(spend, Default::default(), |keys, account| { + keys.derive_spend(account, spend) + }) + } + + /// Returns the view key for this account at `index`, if it does not exceed the maximum index. + #[inline] + pub fn view_key(&self, index: Index<H>) -> Result<H::SecretKey, Error<H>> { + self.view_key_with(index.spend, index.view) + } + + /// Returns the view key for this account at the `spend` and `view` indices, if those indices + /// do not exceed the maximum indices. + #[inline] + pub fn view_key_with(&self, spend: H::Index, view: H::Index) -> Result<H::SecretKey, Error<H>> { + self.with_bounds_check(spend, view, |keys, account| { + keys.derive_view(account, spend, view) + }) + } + + /// Returns the key pair for this account at `index`, if it does not exceed the maximum index. + #[inline] + pub fn keypair(&self, index: Index<H>) -> Result<SecretKeyPair<H>, Error<H>> { + self.keypair_with(index.spend, index.view) + } + + /// Returns the key pair for this account at the `spend` and `view` indices, if those indices + /// do not exceed the maximum indices. + #[inline] + pub fn keypair_with( + &self, + spend: H::Index, + view: H::Index, + ) -> Result<SecretKeyPair<H>, Error<H>> { + self.with_bounds_check(spend, view, |keys, account| { + keys.derive(account, spend, view) + }) + } + + /// Applies `f` to the view keys generated by `self` returning the first non-`None` result with + /// it's spend key index attached, or returns an error if the key derivation failed. + #[inline] + pub fn find_index<T, F>(&self, mut f: F) -> Result<Option<(H::Index, T)>, H::Error> + where + F: FnMut(H::SecretKey) -> Option<T>, + { + let mut index = Index::default(); + loop { + loop { + match self.view_key(index) { + Ok(key) => { + if let Some(value) = f(key) { + return Ok(Some((index.spend, value))); + } + } + Err(Error::ExceedingCurrentMaximumViewIndex) => break, + Err(Error::ExceedingCurrentMaximumSpendIndex) => return Ok(None), + Err(Error::KeyDerivationError(err)) => return Err(err), + } + index.view.increment(); + } + index.spend.increment(); + } + } } /// Account Map Trait pub trait AccountMap<H> where - H: HierarchicalKeyDerivationScheme, + H: HierarchicalKeyDerivationScheme + ?Sized, { + /// Builds a new [`AccountMap`] with a starting account with default max indices. + fn new() -> Self; + /// Returns the maximum spend and view indices for `account`, if it exists. fn max_index(&self, account: H::Account) -> Option<Index<H>>; @@ -217,8 +335,15 @@ pub type VecAccountMap<H> = Vec<Index<H>>; impl<H> AccountMap<H> for VecAccountMap<H> where - H: HierarchicalKeyDerivationScheme, + H: HierarchicalKeyDerivationScheme + ?Sized, { + #[inline] + fn new() -> Self { + let mut this = Self::new(); + this.create_account(); + this + } + #[inline] fn max_index(&self, account: H::Account) -> Option<Index<H>> { self.get(account.into()).copied() @@ -253,7 +378,6 @@ where Clone(bound = "H: Clone, M: Clone"), Copy(bound = "H: Copy, M: Copy"), Debug(bound = "H: Debug, M: Debug"), - Default(bound = "H: Default, M: Default"), Eq(bound = "H: Eq, M: Eq"), Hash(bound = "H: Hash, M: Hash"), PartialEq(bound = "H: PartialEq, M: PartialEq") @@ -275,13 +399,10 @@ where H: HierarchicalKeyDerivationScheme, M: AccountMap<H>, { - /// Builds a new [`AccountTable`] using `keys` and a default account map. + /// Builds a new [`AccountTable`] using `keys` and the default account map. #[inline] - pub fn new(keys: H) -> Self - where - M: Default, - { - Self::with_accounts(keys, Default::default()) + pub fn new(keys: H) -> Self { + Self::with_accounts(keys, M::new()) } /// Builds a new [`AccountTable`] using `keys` and `accounts`. @@ -290,16 +411,27 @@ where Self { keys, accounts } } + /// Returns the key associated to `account` if it exists, using `index` if it does not exceed + /// the maximum index. + #[inline] + pub fn keypair( + &self, + account: H::Account, + index: Index<H>, + ) -> Option<Result<SecretKeyPair<H>, Error<H>>> { + self.keypair_with(account, index.spend, index.view) + } + /// Returns the key associated to `account` if it exists, using the `spend` and `view` indices /// if they do not exceed the maximum indices. #[inline] - pub fn key( + pub fn keypair_with( &self, account: H::Account, spend: H::Index, view: H::Index, - ) -> Option<Result<Key<H>, Error<H>>> { - self.get(account).map(move |k| k.key(spend, view)) + ) -> Option<Result<SecretKeyPair<H>, Error<H>>> { + self.get(account).map(move |k| k.keypair_with(spend, view)) } /// Returns the account keys for `account` if it exists. @@ -332,3 +464,14 @@ where self.accounts.increment_view(account) } } + +impl<H, M> Default for AccountTable<H, M> +where + H: Default + HierarchicalKeyDerivationScheme, + M: AccountMap<H>, +{ + #[inline] + fn default() -> Self { + Self::new(Default::default()) + } +} diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 3c35c70c5..ef7aa1292 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -31,12 +31,13 @@ use crate::{ asset::{Asset, AssetId, AssetMap, AssetValue}, - key::HierarchicalKeyTable, + key::{self, AccountKeys, HierarchicalKeyDerivationScheme}, transfer::{ self, canonical::{Mint, PrivateTransfer, PrivateTransferShape, Reclaim, Transaction}, - EncryptedNote, PreSender, ProofSystemError, ProvingContext, PublicKey, Receiver, - ReceivingKey, SecretKey, Sender, Shape, SpendingKey, Transfer, TransferPost, Utxo, + EncryptedNote, PreReceiver, PreSender, ProofSystemError, ProvingContext, PublicKey, + Receiver, ReceivingKey, SecretKey, Sender, Shape, SpendingKey, Transfer, TransferPost, + Utxo, }, }; use alloc::{vec, vec::Vec}; @@ -83,7 +84,7 @@ pub trait Rollback { /// Signer Connection pub trait Connection<H, C> where - H: HierarchicalKeyTable<SecretKey = SecretKey<C>>, + H: HierarchicalKeyDerivationScheme<SecretKey = SecretKey<C>>, C: transfer::Configuration, { /// Sync Future Type @@ -149,19 +150,14 @@ where /// See the [`Rollback`] trait for expectations on the behavior of [`rollback`](Self::rollback). fn rollback(&mut self) -> Self::RollbackFuture; - /// Returns a [`ReceivingKey`] for `self` to receive assets. + /// Returns a [`ReceivingKey`] for `self` to receive assets with `index`. /// /// # Note /// /// This method does not interact with the other methods on [`Connection`] so it can be called /// at any point in between calls to [`sync`](Self::sync), [`sign`](Self::sign), and other /// rollback related methods. - fn receiving_key( - &mut self, - account: H::Account, - index: H::Index, - view_key_index: H::Index, - ) -> Self::ReceivingKeyFuture; + fn receiving_key(&mut self, index: key::Index<H>) -> Self::ReceivingKeyFuture; } /// Synchronization State @@ -236,11 +232,11 @@ where /// Signer Error pub enum Error<H, C, CE = Infallible> where - H: HierarchicalKeyTable<SecretKey = SecretKey<C>>, + H: HierarchicalKeyDerivationScheme<SecretKey = SecretKey<C>>, C: transfer::Configuration, { - /// Hierarchical Key Table Error - HierarchicalKeyTableError(H::Error), + /// Hierarchical Key Derivation Scheme Error + HierarchicalKeyDerivationSchemeError(key::Error<H>), /// Missing [`Utxo`] Membership Proof MissingUtxoMembershipProof, @@ -258,10 +254,23 @@ where ConnectionError(CE), } +impl<H, C, CE> From<key::Error<H>> for Error<H, C, CE> +where + H: HierarchicalKeyDerivationScheme<SecretKey = SecretKey<C>>, + C: transfer::Configuration, +{ + #[inline] + fn from(err: key::Error<H>) -> Self { + Self::HierarchicalKeyDerivationSchemeError(err) + } +} + /// Signer Configuration pub trait Configuration: transfer::Configuration { - /// Hierarchical Key Table - type HierarchicalKeyTable: HierarchicalKeyTable<SecretKey = SecretKey<Self>>; + /// Hierarchical Key Derivation Scheme + type HierarchicalKeyDerivationScheme: HierarchicalKeyDerivationScheme< + SecretKey = SecretKey<Self>, + >; /// [`Utxo`] Accumulator Type type UtxoSet: Accumulator< @@ -274,12 +283,31 @@ pub trait Configuration: transfer::Configuration { + Rollback; /// Asset Map Type - type AssetMap: AssetMap<Key = PublicKey<Self>>; + type AssetMap: AssetMap<Key = AssetMapKey<Self>>; /// Random Number Generator Type type Rng: CryptoRng + RngCore; } +/// Index Type +pub type Index<C> = key::Index<<C as Configuration>::HierarchicalKeyDerivationScheme>; + +/// Hierarchical Key Derivation Scheme Index +type HierarchicalKeyDerivationSchemeIndex<C> = + <<C as Configuration>::HierarchicalKeyDerivationScheme as HierarchicalKeyDerivationScheme>::Index; + +/// Spend Index Type +pub type SpendIndex<C> = HierarchicalKeyDerivationSchemeIndex<C>; + +/// View Index Type +pub type ViewIndex<C> = HierarchicalKeyDerivationSchemeIndex<C>; + +/// Asset Map Key Type +pub type AssetMapKey<C> = (SpendIndex<C>, PublicKey<C>); + +/// Account Table Type +pub type AccountTable<C> = key::AccountTable<<C as Configuration>::HierarchicalKeyDerivationScheme>; + /// Pending Asset Map #[derive(derivative::Derivative)] #[derivative(Default(bound = ""))] @@ -288,13 +316,13 @@ where C: Configuration, { /// Pending Insert Data - insert: Option<(PublicKey<C>, Asset)>, + insert: Option<(AssetMapKey<C>, Asset)>, /// Pending Insert Zeroes Data - insert_zeroes: Option<(AssetId, Vec<PublicKey<C>>)>, + insert_zeroes: Option<(AssetId, Vec<AssetMapKey<C>>)>, /// Pending Remove Data - remove: Vec<PublicKey<C>>, + remove: Vec<AssetMapKey<C>>, } impl<C> PendingAssetMap<C> @@ -303,10 +331,7 @@ where { /// Commits the pending asset map data to `assets`. #[inline] - fn commit<M>(&mut self, assets: &mut M) - where - M: AssetMap<Key = PublicKey<C>>, - { + fn commit(&mut self, assets: &mut C::AssetMap) { if let Some((key, asset)) = self.insert.take() { assets.insert(key, asset); } @@ -328,8 +353,8 @@ pub struct Signer<C> where C: Configuration, { - /// Spending Key - spending_key: SpendingKey<C>, + /// Account Table + account_table: AccountTable<C>, /// Ephemeral Key Commitment Scheme ephemeral_key_commitment_scheme: C::EphemeralKeyCommitmentScheme, @@ -360,7 +385,7 @@ where /// Builds a new [`Signer`]. #[inline] fn new_inner( - spending_key: SpendingKey<C>, + account_table: AccountTable<C>, ephemeral_key_commitment_scheme: C::EphemeralKeyCommitmentScheme, commitment_scheme: C::CommitmentScheme, proving_context: ProvingContext<C>, @@ -370,7 +395,7 @@ where rng: C::Rng, ) -> Self { Self { - spending_key, + account_table, ephemeral_key_commitment_scheme, commitment_scheme, proving_context, @@ -381,22 +406,22 @@ where } } - /// Builds a new [`Signer`] from a fresh `spending_key`. + /// Builds a new [`Signer`] from a fresh `account_table`. /// /// # Warning /// - /// This method assumes that `spending_key` has never been used before, and does not attempt to - /// perform wallet recovery on this key. + /// This method assumes that `account_table` has never been used before, and does not attempt + /// to perform wallet recovery on this table. #[inline] pub fn new( - spending_key: SpendingKey<C>, + account_table: AccountTable<C>, ephemeral_key_commitment_scheme: C::EphemeralKeyCommitmentScheme, commitment_scheme: C::CommitmentScheme, proving_context: ProvingContext<C>, rng: C::Rng, ) -> Self { Self::new_inner( - spending_key, + account_table, ephemeral_key_commitment_scheme, commitment_scheme, proving_context, @@ -407,18 +432,34 @@ where ) } + /// + #[inline] + fn account(&self) -> AccountKeys<C::HierarchicalKeyDerivationScheme> { + self.account_table.get(Default::default()).unwrap() + } + /// Updates the internal ledger state, returning the new asset distribution. #[inline] - fn sync_inner<I>(&mut self, updates: I) -> SyncResult<C::HierarchicalKeyTable, C, Self> + fn sync_inner<I>( + &mut self, + updates: I, + ) -> SyncResult<C::HierarchicalKeyDerivationScheme, C, Self> where I: Iterator<Item = (Utxo<C>, EncryptedNote<C>)>, { let mut assets = Vec::new(); for (utxo, encrypted_note) in updates { - if let Ok(DecryptedMessage { - plaintext: asset, - ephemeral_public_key, - }) = self.spending_key.decrypt(encrypted_note) + let mut encrypted_note = Some(encrypted_note); + if let Some(( + spend_index, + DecryptedMessage { + plaintext: asset, + ephemeral_public_key, + }, + )) = self + .account() + .find_index(move |k| DecryptedMessage::try_new(&mut encrypted_note, &k)) + .map_err(key::Error::KeyDerivationError)? { /* FIXME: Add UTXO validation check. Is this necessary? if self.spending_key.validate_utxo::<C>( @@ -428,12 +469,13 @@ where &self.commitment_scheme, ) { assets.push(asset); - self.assets.insert(ephemeral_public_key, asset); + self.assets.insert((index, ephemeral_public_key), asset); self.utxo_set.insert(&utxo); } */ assets.push(asset); - self.assets.insert(ephemeral_public_key, asset); + self.assets + .insert((spend_index, ephemeral_public_key), asset); self.utxo_set.insert(&utxo); } else { self.utxo_set.insert_nonprovable(&utxo); @@ -449,7 +491,7 @@ where sync_state: SyncState, starting_index: usize, updates: I, - ) -> SyncResult<C::HierarchicalKeyTable, C, Self> + ) -> SyncResult<C::HierarchicalKeyDerivationScheme, C, Self> where I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, { @@ -462,35 +504,62 @@ where } } - /// Builds the pre-sender associated to `ephemeral_key` and `asset`. + /// Builds the pre-sender associated to `spend_index`, `ephemeral_key`, and `asset`. + #[inline] + fn build_pre_sender( + &self, + spend_index: SpendIndex<C>, + ephemeral_key: PublicKey<C>, + asset: Asset, + ) -> Result<PreSender<C>, Error<C::HierarchicalKeyDerivationScheme, C>> { + Ok(PreSender::new( + self.account().spend_key(spend_index)?, + ephemeral_key, + asset, + &self.commitment_scheme, + )) + } + + /// Builds the pre-receiver associated to `spend_index`, `view_index`, and `asset`. #[inline] - fn build_pre_sender(&self, ephemeral_key: PublicKey<C>, asset: Asset) -> PreSender<C> { - self.spending_key - .sender(ephemeral_key, asset, &self.commitment_scheme) + fn build_pre_receiver( + &self, + spend_index: SpendIndex<C>, + view_index: ViewIndex<C>, + asset: Asset, + ) -> Result<PreReceiver<C>, Error<C::HierarchicalKeyDerivationScheme, C>> { + let keypair = self + .account() + .keypair_with(spend_index, view_index)? + .derive::<C::KeyAgreementScheme>(); + Ok(PreReceiver::new(keypair.spend, keypair.view, asset)) } /// Selects the pre-senders which collectively own at least `asset`, returning any change. #[inline] - fn select(&mut self, asset: Asset) -> Result<Selection<C>, Error<C::HierarchicalKeyTable, C>> { - /* TODO: + fn select( + &mut self, + asset: Asset, + ) -> Result<Selection<C>, Error<C::HierarchicalKeyDerivationScheme, C>> { let selection = self.assets.select(asset); if selection.is_empty() { return Err(Error::InsufficientBalance(asset)); } + /* TODO: self.pending_assets.remove = selection.keys().cloned().collect(); Ok(Selection::new( selection.change, selection .values .into_iter() - .map(move |(k, v)| self.build_pre_sender(k, asset.id.with(v))) + .map(move |((i, ek), v)| self.build_pre_sender(i, ek, asset.id.with(v))) .collect(), )) */ todo!() } - /// Builds a [`TransferPost`] for the given `transfer`. + /// Builds a [`TransferPost`] for the given `transfer` at `ledger_checkpoint`. #[inline] fn build_post< const SOURCES: usize, @@ -501,7 +570,7 @@ where &mut self, ledger_checkpoint: C::LedgerCheckpoint, transfer: Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>, - ) -> Result<TransferPost<C>, Error<C::HierarchicalKeyTable, C>> { + ) -> Result<TransferPost<C>, Error<C::HierarchicalKeyDerivationScheme, C>> { transfer .into_post( &self.ephemeral_key_commitment_scheme, @@ -665,7 +734,7 @@ where &mut self, asset: Asset, receiver: Option<ReceivingKey<C>>, - ) -> SignResult<C::HierarchicalKeyTable, C, Self> { + ) -> SignResult<C::HierarchicalKeyDerivationScheme, C, Self> { /* TODO: const SENDERS: usize = PrivateTransferShape::SENDERS; const RECEIVERS: usize = PrivateTransferShape::RECEIVERS; @@ -701,7 +770,7 @@ where &mut self, asset: Asset, receiver: Option<ReceivingKey<C>>, - ) -> SignResult<C::HierarchicalKeyTable, C, Self> { + ) -> SignResult<C::HierarchicalKeyDerivationScheme, C, Self> { let result = self.sign_withdraw_inner(asset, receiver); if result.is_err() { self.rollback(); @@ -714,15 +783,21 @@ where pub fn sign( &mut self, transaction: Transaction<C>, - ) -> SignResult<C::HierarchicalKeyTable, C, Self> { + ) -> SignResult<C::HierarchicalKeyDerivationScheme, C, Self> { self.commit(); match transaction { Transaction::Mint(asset) => { - /* - let mint_post = - self.build_post(Mint::build(asset, self.spending_key.receiver(asset)))?; - self.pending_assets.insert = - Some((mint_post.receiver_ephemeral_keys()[0].clone(), asset)); + /* TODO: + let default_index = Default::default(); + let mint_post = self.build_post(ledger_checkpoint, Mint::build( + asset, + self.build_pre_receiver(default_index, default_index, asset), + ))?; + self.pending_assets.insert = Some(( + default_index, + mint_post.receiver_ephemeral_keys()[0].clone(), + asset, + )); Ok(SignResponse::new(vec![mint_post])) */ todo!() @@ -757,26 +832,34 @@ where } } - /// Generates a new [`ReceivingKey`] for `self` to receive assets. + /// Returns a [`ReceivingKey`] for `self` to receive assets with `index`. #[inline] - pub fn receiving_key(&mut self) -> ReceivingKeyResult<C::HierarchicalKeyTable, C, Self> { - Ok(self.spending_key.derive()) + pub fn receiving_key( + &mut self, + index: Index<C>, + ) -> ReceivingKeyResult<C::HierarchicalKeyDerivationScheme, C, Self> { + let _ = self + .account() + .keypair(index)? + .derive::<C::KeyAgreementScheme>(); + todo!() } } -impl<C> Connection<C::HierarchicalKeyTable, C> for Signer<C> +impl<C> Connection<C::HierarchicalKeyDerivationScheme, C> for Signer<C> where C: Configuration, { - type SyncFuture = Ready<SyncResult<C::HierarchicalKeyTable, C, Self>>; + type SyncFuture = Ready<SyncResult<C::HierarchicalKeyDerivationScheme, C, Self>>; - type SignFuture = Ready<SignResult<C::HierarchicalKeyTable, C, Self>>; + type SignFuture = Ready<SignResult<C::HierarchicalKeyDerivationScheme, C, Self>>; type CommitFuture = Ready<Result<(), Self::Error>>; type RollbackFuture = Ready<Result<(), Self::Error>>; - type ReceivingKeyFuture = Ready<ReceivingKeyResult<C::HierarchicalKeyTable, C, Self>>; + type ReceivingKeyFuture = + Ready<ReceivingKeyResult<C::HierarchicalKeyDerivationScheme, C, Self>>; type Error = Infallible; @@ -815,8 +898,8 @@ where } #[inline] - fn receiving_key(&mut self) -> Self::ReceivingKeyFuture { - future::ready(self.receiving_key()) + fn receiving_key(&mut self, index: Index<C>) -> Self::ReceivingKeyFuture { + future::ready(self.receiving_key(index)) } } diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 3a904fb76..bcb092c4b 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -18,7 +18,7 @@ use crate::{ asset::{Asset, AssetId, AssetValue}, - key::HierarchicalKeyTable, + key::{HierarchicalKeyDerivationScheme, Index}, transfer::{ canonical::{Transaction, TransactionKind}, Configuration, ReceivingKey, SecretKey, @@ -161,7 +161,7 @@ where /// Wallet pub struct Wallet<H, C, L, S, B = BTreeMapBalanceState> where - H: HierarchicalKeyTable<SecretKey = SecretKey<C>>, + H: HierarchicalKeyDerivationScheme<SecretKey = SecretKey<C>>, C: Configuration, L: ledger::Connection<C>, S: signer::Connection<H, C>, @@ -188,7 +188,7 @@ where impl<H, C, L, S, B> Wallet<H, C, L, S, B> where - H: HierarchicalKeyTable<SecretKey = SecretKey<C>>, + H: HierarchicalKeyDerivationScheme<SecretKey = SecretKey<C>>, C: Configuration, L: ledger::Connection<C>, S: signer::Connection<H, C>, @@ -337,12 +337,13 @@ where } } - /// + /// Returns a [`ReceivingKey`] for `self` to receive assets with `index`. #[inline] pub async fn receiving_key( &mut self, + index: Index<H>, ) -> Result<ReceivingKey<C>, signer::Error<H, C, S::Error>> { - self.signer.receiving_key().await + self.signer.receiving_key(index).await } } @@ -352,7 +353,7 @@ where /// [`post`](Wallet::post) for more. pub enum Error<H, C, L, S> where - H: HierarchicalKeyTable<SecretKey = SecretKey<C>>, + H: HierarchicalKeyDerivationScheme<SecretKey = SecretKey<C>>, C: Configuration, L: ledger::Connection<C>, S: signer::Connection<H, C>, @@ -369,7 +370,7 @@ where impl<H, C, L, S> From<signer::Error<H, C, S::Error>> for Error<H, C, L, S> where - H: HierarchicalKeyTable<SecretKey = SecretKey<C>>, + H: HierarchicalKeyDerivationScheme<SecretKey = SecretKey<C>>, C: Configuration, L: ledger::Connection<C>, S: signer::Connection<H, C>, diff --git a/manta-crypto/src/encryption.rs b/manta-crypto/src/encryption.rs index e2100be96..331b14eb7 100644 --- a/manta-crypto/src/encryption.rs +++ b/manta-crypto/src/encryption.rs @@ -213,4 +213,19 @@ where ephemeral_public_key, } } + + /// Tries to decrypt `encrypted_message` with `secret_key`, if the `Option` contains a message. + #[inline] + pub fn try_new( + encrypted_message: &mut Option<EncryptedMessage<H>>, + secret_key: &SecretKey<H>, + ) -> Option<Self> { + if let Some(message) = encrypted_message.take() { + match message.decrypt(secret_key) { + Ok(decrypted_message) => return Some(decrypted_message), + Err(message) => *encrypted_message = Some(message), + } + } + None + } } From 272b7669be1ae5680be6c141a203ad55964c26b6 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 12 Dec 2021 16:53:04 -0500 Subject: [PATCH 133/275] wip: update to new constraint system abstraction --- manta-accounting/src/key.rs | 47 +++++---- manta-cli/src/command/mod.rs | 4 - manta-crypto/src/accumulator.rs | 144 +++++++++++++++++--------- manta-crypto/src/commitment.rs | 148 +++++++++++++++------------ manta-crypto/src/constraint.rs | 27 ----- manta-crypto/src/encryption.rs | 2 +- manta-crypto/src/key.rs | 32 +++++- manta-crypto/src/merkle_tree/tree.rs | 64 +++++++----- manta-pay/Cargo.toml | 4 +- manta-pay/src/accounting/key.rs | 50 +++++---- manta-pay/src/crypto/encryption.rs | 1 + 11 files changed, 307 insertions(+), 216 deletions(-) diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index 1bd8f8036..f1fb2365b 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -42,33 +42,33 @@ pub trait HierarchicalKeyDerivationScheme { /// Key Derivation Error Type type Error; - /// Derives a pair of secret keys for `account` using the `spend` and `view` indices. - fn derive( + /// Derives a spend secret key for `account` using the `spend` index. + fn derive_spend( &self, account: Self::Account, spend: Self::Index, - view: Self::Index, - ) -> Result<SecretKeyPair<Self>, Self::Error>; + ) -> Result<Self::SecretKey, Self::Error>; - /// - #[inline] - fn derive_spend( + /// Derives a view secret key for `account` using the `spend` and `view` indices. + fn derive_view( &self, account: Self::Account, spend: Self::Index, - ) -> Result<Self::SecretKey, Self::Error> { - Ok(self.derive(account, spend, Default::default())?.spend) - } + view: Self::Index, + ) -> Result<Self::SecretKey, Self::Error>; - /// + /// Derives a spend-view pair of secret keys for `account` using the `spend` and `view` indices. #[inline] - fn derive_view( + fn derive( &self, account: Self::Account, spend: Self::Index, view: Self::Index, - ) -> Result<Self::SecretKey, Self::Error> { - Ok(self.derive(account, spend, view)?.view) + ) -> Result<SecretKeyPair<Self>, Self::Error> { + Ok(SecretKeyPair::new( + self.derive_spend(account, spend)?, + self.derive_view(account, spend, view)?, + )) } } @@ -85,17 +85,22 @@ where type Error = H::Error; #[inline] - fn derive( + fn derive_spend( + &self, + account: Self::Account, + spend: Self::Index, + ) -> Result<Self::SecretKey, Self::Error> { + (*self).derive_spend(account, spend) + } + + #[inline] + fn derive_view( &self, account: Self::Account, spend: Self::Index, view: Self::Index, - ) -> Result<SecretKeyPair<Self>, Self::Error> { - let key = (*self).derive(account, spend, view)?; - Ok(SecretKeyPair { - spend: key.spend, - view: key.view, - }) + ) -> Result<Self::SecretKey, Self::Error> { + (*self).derive_view(account, spend, view) } } diff --git a/manta-cli/src/command/mod.rs b/manta-cli/src/command/mod.rs index b39467897..d754e8015 100644 --- a/manta-cli/src/command/mod.rs +++ b/manta-cli/src/command/mod.rs @@ -15,7 +15,3 @@ // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. //! Manta CLI Commands - -#![cfg_attr(doc_cfg, feature(doc_cfg))] -#![forbid(rustdoc::broken_intra_doc_links)] -#![forbid(missing_docs)] diff --git a/manta-crypto/src/accumulator.rs b/manta-crypto/src/accumulator.rs index 45f170d0f..0bb021d97 100644 --- a/manta-crypto/src/accumulator.rs +++ b/manta-crypto/src/accumulator.rs @@ -39,6 +39,9 @@ where /// Accumulator Membership Verifier pub trait Verifier { + /// Parameters Type + type Parameters; + /// Item Type type Item: ?Sized; @@ -49,41 +52,20 @@ pub trait Verifier { type Output; /// Verification Type + /// + /// Typically this is either [`bool`] or some [`Result`] type. type Verification; /// Verifies that `item` is stored in a known accumulator with accumulated `output` and /// membership `witness`. fn verify( - &self, + parameters: &Self::Parameters, item: &Self::Item, witness: &Self::Witness, output: &Self::Output, ) -> Self::Verification; } -impl<V> Verifier for &V -where - V: Verifier + ?Sized, -{ - type Item = V::Item; - - type Witness = V::Witness; - - type Output = V::Output; - - type Verification = V::Verification; - - #[inline] - fn verify( - &self, - item: &Self::Item, - witness: &Self::Witness, - output: &Self::Output, - ) -> Self::Verification { - (*self).verify(item, witness, output) - } -} - /// Accumulator Output Type pub type Output<A> = <<A as Accumulator>::Verifier as Verifier>::Output; @@ -98,8 +80,8 @@ pub trait Accumulator { /// Output Matching Set Type type OutputSet: MatchingSet<<Self::Verifier as Verifier>::Output>; - /// Returns the verifier for `self`. - fn verifier(&self) -> &Self::Verifier; + /// Returns the parameters associated with the verifier attached to `self`. + fn parameters(&self) -> &<Self::Verifier as Verifier>::Parameters; /// Returns the output matching set for the current state of `self`. fn outputs(&self) -> Self::OutputSet; @@ -149,8 +131,8 @@ where type OutputSet = A::OutputSet; #[inline] - fn verifier(&self) -> &Self::Verifier { - (**self).verifier() + fn parameters(&self) -> &<Self::Verifier as Verifier>::Parameters { + (**self).parameters() } #[inline] @@ -213,7 +195,7 @@ pub trait OptimizedAccumulator: Accumulator { /// [`insert`]: Accumulator::insert /// [`contains`]: Accumulator::contains #[inline] - fn insert_nonprovable(&mut self, item: &<Self::Verifier as Verifier>::Item) -> bool { + fn insert_nonprovable(&mut self, item: &Self::Item) -> bool { self.insert(item) } @@ -229,7 +211,7 @@ pub trait OptimizedAccumulator: Accumulator { /// efficient enough. Space and time tradeoffs should be studied to determine the usefulness of /// this method. #[inline] - fn remove_proof(&mut self, item: &<Self::Verifier as Verifier>::Item) -> bool { + fn remove_proof(&mut self, item: &Self::Item) -> bool { let _ = item; false } @@ -283,19 +265,49 @@ where accumulator.matching_output(&self.output) } - /// Verifies that `item` is stored in a known accumulator using `verifier`. + /// Verifies that `item` is stored in a known accumulator using `parameters`. #[inline] - pub fn verify(&self, item: &V::Item, verifier: &V) -> V::Verification { - verifier.verify(item, &self.witness, &self.output) + pub fn verify(&self, parameters: &V::Parameters, item: &V::Item) -> V::Verification { + V::verify(parameters, item, &self.witness, &self.output) } } -/// Constraint System Gadgets for Accumulators +/// Constraint System Gadgets pub mod constraint { - use super::*; use crate::constraint::{Allocation, AllocationMode, Derived, Variable, VariableSource}; use core::marker::PhantomData; + /// Accumulator Verifier Gadget + pub trait Verifier<V> + where + V: super::Verifier, + { + /// Paramters Type + type Parameters: Variable<Self, Type = V::Parameters>; + + /// Item Type + type Item: Variable<Self, Type = V::Item>; + + /// Secret Witness Type + type Witness: Variable<Self, Type = V::Witness>; + + /// Output Type + type Output: Variable<Self, Type = V::Output>; + + /// Verification Type + type Verification: Variable<Self, Type = V::Verification>; + + /// Verifies that `item` is stored in a known accumulator with accumulated `output` and + /// membership `witness`. + fn verify( + &mut self, + parameters: &Self::Parameters, + item: &Self::Item, + witness: &Self::Witness, + output: &Self::Output, + ) -> Self::Verification; + } + /// Membership Proof Allocation Mode Entry #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] @@ -343,23 +355,63 @@ pub mod constraint { type Unknown = MembershipProofModeEntry<WitnessMode::Unknown, OutputMode::Unknown>; } - impl<C, V> Variable<C> for MembershipProof<V> + /// Accumulator Membership Proof + pub struct MembershipProof<B, V> + where + B: super::Verifier, + V: Verifier<B>, + { + /// Secret Membership Witness + witness: V::Witness, + + /// Accumulator Output + output: V::Output, + } + + impl<B, V> MembershipProof<B, V> + where + B: super::Verifier, + V: Verifier<B>, + { + /// Builds a new [`MembershipProof`] from `witness` and `output`. + #[inline] + pub fn new(witness: V::Witness, output: V::Output) -> Self { + Self { witness, output } + } + + /// Returns the accumulated output part of `self`, dropping the + /// [`V::Witness`](Verifier::Witness). + #[inline] + pub fn into_output(self) -> V::Output { + self.output + } + + /// Verifies that `item` is stored in a known accumulator using `verifier`. + #[inline] + pub fn verify( + &self, + parameters: &V::Parameters, + item: &V::Item, + cs: &mut V, + ) -> V::Verification { + cs.verify(parameters, item, &self.witness, &self.output) + } + } + + impl<B, V> Variable<V> for MembershipProof<B, V> where - C: ?Sized, - V: Variable<C> + Verifier + ?Sized, - V::Type: Verifier, - V::Witness: Variable<C, Type = <V::Type as Verifier>::Witness>, - V::Output: Variable<C, Type = <V::Type as Verifier>::Output>, + B: super::Verifier, + V: Verifier<B>, { - type Type = MembershipProof<V::Type>; + type Type = super::MembershipProof<B>; type Mode = MembershipProofMode< - <V::Witness as Variable<C>>::Mode, - <V::Output as Variable<C>>::Mode, + <V::Witness as Variable<V>>::Mode, + <V::Output as Variable<V>>::Mode, >; #[inline] - fn new(cs: &mut C, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new(cs: &mut V, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self::new( this.witness.as_known(cs, mode.witness), @@ -399,7 +451,7 @@ pub mod test { ); if let Some(proof) = accumulator.prove(item) { assert!( - proof.verify(item, accumulator.verifier()), + proof.verify(accumulator.parameters(), item), "Invalid proof returned for inserted item." ); proof.into_output() diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index 6edf2f9a3..6edf1b34f 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -16,94 +16,87 @@ //! Commitment Schemes -// FIXME: Change this so that commiting one value is the default, and commiting a "concatenation" -// of values is the special case. - use core::{fmt::Debug, hash::Hash}; -use manta_util::{Concat, ConcatAccumulator}; /// Commitment Scheme pub trait CommitmentScheme { - /// Commitment Input Type - type Input: Default; + /// Parameters Type + type Parameters; - /// Commitment Trapdoor Parameter Type + /// Trapdoor Type type Trapdoor; - /// Commitment Output Type - type Output; + /// Input Type + type Input; - /// Returns a new [`Builder`] to build up a commitment. - #[inline] - fn start(&self) -> Builder<Self> { - Builder::new(self) - } + /// Output Type + type Output; - /// Commits the `input` with the given `trapdoor` parameter. - fn commit(&self, input: Self::Input, trapdoor: &Self::Trapdoor) -> Self::Output; + /// Commits to the `input` value using `parameters` and randomness `trapdoor`. + fn commit( + parameters: &Self::Parameters, + trapdoor: &Self::Trapdoor, + input: &Self::Input, + ) -> Self::Output; - /// Commits the single `input` value with the given `trapdoor` parameter. + /// Starts a new [`Builder`] for extended commitments. #[inline] - fn commit_one<T>(&self, input: &T, trapdoor: &Self::Trapdoor) -> Self::Output + fn start<'c>( + parameters: &'c Self::Parameters, + trapdoor: &'c Self::Trapdoor, + ) -> Builder<'c, Self> where - T: ?Sized, - Self: Input<T>, + Self::Input: Default, { - self.start().update(input).commit(trapdoor) + Builder::new(parameters, trapdoor) } } -/// Commitment Input +/// Commitment Extended Input pub trait Input<T>: CommitmentScheme where T: ?Sized, { - /// Extends the `input` with the `next` element. + /// Extends the `input` data with `next`. fn extend(input: &mut Self::Input, next: &T); } -impl<C, T> Input<T> for C -where - C: CommitmentScheme + ?Sized, - C::Input: ConcatAccumulator<T::Item>, - T: Concat + ?Sized, -{ - #[inline] - fn extend(input: &mut Self::Input, next: &T) { - next.concat(input); - } -} - /// Commitment Builder #[derive(derivative::Derivative)] #[derivative( Clone(bound = "C::Input: Clone"), Copy(bound = "C::Input: Copy"), - Debug(bound = "C: Debug, C::Input: Debug"), - Eq(bound = "C: Eq, C::Input: Eq"), - Hash(bound = "C: Hash, C::Input: Hash"), - PartialEq(bound = "C: PartialEq, C::Input: PartialEq") + Debug(bound = "C::Parameters: Debug, C::Trapdoor: Debug, C::Input: Debug"), + Eq(bound = "C::Parameters: Eq, C::Trapdoor: Eq, C::Input: Eq"), + Hash(bound = "C::Parameters: Hash, C::Trapdoor: Hash, C::Input: Hash"), + PartialEq(bound = "C::Parameters: PartialEq, C::Trapdoor: PartialEq, C::Input: PartialEq") )] pub struct Builder<'c, C> where C: CommitmentScheme + ?Sized, + C::Input: Default, { - /// Commitment Scheme - commitment_scheme: &'c C, + /// Commitment Parameters + parameters: &'c C::Parameters, + + /// Commitment Trapdoor + trapdoor: &'c C::Trapdoor, - /// Commitment Input + /// Commitment Input Accumulator input: C::Input, } impl<'c, C> Builder<'c, C> where C: CommitmentScheme + ?Sized, + C::Input: Default, { - /// Returns a new [`Builder`] for this `commitment_scheme`. + /// Returns a new [`Builder`] with fixed `parameters` and `trapdoor`. #[inline] - pub fn new(commitment_scheme: &'c C) -> Self { + pub fn new(parameters: &'c C::Parameters, trapdoor: &'c C::Trapdoor) -> Self { Self { - commitment_scheme, + parameters, + trapdoor, input: Default::default(), } } @@ -119,33 +112,54 @@ where self } - /// Commits to the input stored in the builder with the given `trapdoor`. + /// Updates the builder with each item in `iter`. #[inline] - pub fn commit(self, trapdoor: &C::Trapdoor) -> C::Output { - self.commitment_scheme.commit(self.input, trapdoor) + pub fn update_all<'t, T, I>(mut self, iter: I) -> Self + where + T: 't + ?Sized, + I: IntoIterator<Item = &'t T>, + C: Input<T>, + { + for next in iter { + C::extend(&mut self.input, next); + } + self + } + + /// Commits to the input stored in the builder. + #[inline] + pub fn commit(self) -> C::Output { + C::commit(self.parameters, self.trapdoor, &self.input) } } -/// Testing Framework -#[cfg(feature = "test")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] -pub mod test { - use super::*; - use core::fmt::Debug; +/// Constraint System Gadgets +pub mod constraint { + use crate::constraint::Variable; - /// Asserts that the given commitment `output` is equal to commiting `input` with `trapdoor` - /// using the `commitment_scheme`. - #[inline] - pub fn assert_commitment_matches<T, C>( - commitment_scheme: &C, - input: &T, - trapdoor: &C::Trapdoor, - output: &C::Output, - ) where - T: ?Sized, - C: CommitmentScheme + Input<T> + ?Sized, - C::Output: Debug + PartialEq, + /// Commitment Scheme Gadget + pub trait CommitmentScheme<C> + where + C: super::CommitmentScheme, { - assert_eq!(&commitment_scheme.commit_one(input, trapdoor), output); + /// Parameters Type + type Parameters: Variable<Self, Type = C::Parameters>; + + /// Input Type + type Input: Variable<Self, Type = C::Input>; + + /// Trapdoor Type + type Trapdoor: Variable<Self, Type = C::Trapdoor>; + + /// Output Type + type Output: Variable<Self, Type = C::Output>; + + /// Commits to the `input` value using `parameters` and randomness `trapdoor`. + fn commit( + &mut self, + parameters: &Self::Parameters, + input: &Self::Input, + trapdoor: &Self::Trapdoor, + ) -> Self::Output; } } diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index c71a4b1a5..77320d24f 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -447,33 +447,6 @@ pub trait ConstraintSystem { } } -/// Native Constraint System -pub struct Native; - -impl ConstraintSystem for Native { - type Bool = bool; - - #[inline] - fn assert(&mut self, b: Self::Bool) { - assert!(b) - } -} - -impl Variable<Native> for bool { - type Type = bool; - - type Mode = Constant<()>; - - #[inline] - fn new(cs: &mut Native, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - let _ = cs; - match allocation { - Allocation::Known(b, _) => *b, - _ => unreachable!(), - } - } -} - /// Equality Trait pub trait Equal<C> where diff --git a/manta-crypto/src/encryption.rs b/manta-crypto/src/encryption.rs index 331b14eb7..cf38399a8 100644 --- a/manta-crypto/src/encryption.rs +++ b/manta-crypto/src/encryption.rs @@ -92,7 +92,7 @@ pub type PublicKey<H> = /// /// [`agree_derive`]: HybridPublicKeyEncryptionScheme::agree_derive #[derive(derivative::Derivative)] -#[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, PartialOrd)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Hybrid<K, S, F> where K: KeyAgreementScheme, diff --git a/manta-crypto/src/key.rs b/manta-crypto/src/key.rs index d6c3feed4..bfe09d345 100644 --- a/manta-crypto/src/key.rs +++ b/manta-crypto/src/key.rs @@ -18,7 +18,7 @@ /// Key Derivation Function pub trait KeyDerivationFunction<S> { - /// + /// Output Key Type type Output; /// Derives an output key from `secret` computed from a cryptographic agreement scheme. @@ -122,3 +122,33 @@ pub trait KeyAgreementWithDerivation: KeyAgreementScheme { Self::KeyDerivationFunction::derive(Self::agree_owned(secret_key, public_key)) } } + +/// Constraint System Gadgets +pub mod constraint { + use crate::constraint::Variable; + + /// Key Agreement Scheme Gadget + pub trait KeyAgreementScheme<K> + where + K: super::KeyAgreementScheme, + { + /// Secret Key Type + type SecretKey: Variable<Self, Type = K::SecretKey>; + + /// Public Key Type + type PublicKey: Variable<Self, Type = K::PublicKey>; + + /// Shared Secret Type + type SharedSecret: Variable<Self, Type = K::SharedSecret>; + + /// Derives a public key corresponding to `secret_key`. + fn derive(&mut self, secret_key: &Self::SecretKey) -> Self::PublicKey; + + /// Computes the shared secret given the known `secret_key` and the given `public_key`. + fn agree( + &mut self, + secret_key: &Self::SecretKey, + public_key: &Self::PublicKey, + ) -> Self::SharedSecret; + } +} diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index c76cf5da2..0b33d7ea6 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -26,7 +26,7 @@ use crate::{ accumulator::{ self, Accumulator, ConstantCapacityAccumulator, ExactSizeAccumulator, MembershipProof, - OptimizedAccumulator, Verifier, + OptimizedAccumulator, }, merkle_tree::{ fork::{self, Trunk}, @@ -150,8 +150,8 @@ pub type InnerDigest<C> = <<C as HashConfiguration>::InnerHash as InnerHash>::Ou /// parameter. /// /// The capacity of a merkle tree with height `H` is `2^(H-1)`. -#[must_use] #[inline] +#[must_use] pub fn capacity<C>() -> usize where C: Configuration + ?Sized, @@ -163,8 +163,8 @@ where /// parameter. /// /// The path length of a merkle tree with height `H` is `H - 2`. -#[must_use] #[inline] +#[must_use] pub fn path_length<C>() -> usize where C: Configuration + ?Sized, @@ -497,29 +497,6 @@ where } } -impl<C> Verifier for Parameters<C> -where - C: Configuration + ?Sized, -{ - type Item = Leaf<C>; - - type Witness = Path<C>; - - type Output = Root<C>; - - type Verification = bool; - - #[inline] - fn verify( - &self, - item: &Self::Item, - witness: &Self::Witness, - output: &Self::Output, - ) -> Self::Verification { - self.verify_path(witness, output, item) - } -} - /// Merkle Tree Root Wrapper Type #[derive(derivative::Derivative)] #[derivative( @@ -548,6 +525,37 @@ where } } +/// Merkle Tree Verifier +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Verifier<C>(PhantomData<C>) +where + C: Configuration + ?Sized; + +impl<C> accumulator::Verifier for Verifier<C> +where + C: Configuration + ?Sized, +{ + type Parameters = Parameters<C>; + + type Item = Leaf<C>; + + type Witness = Path<C>; + + type Output = Root<C>; + + type Verification = bool; + + #[inline] + fn verify( + parameters: &Self::Parameters, + item: &Self::Item, + witness: &Self::Witness, + output: &Self::Output, + ) -> Self::Verification { + parameters.verify_path(witness, output, item) + } +} + /// Merkle Tree #[derive(derivative::Derivative)] #[derivative( @@ -818,12 +826,12 @@ where { type Item = Leaf<C>; - type Verifier = Parameters<C>; + type Verifier = Verifier<C>; type OutputSet = accumulator::Output<Self>; #[inline] - fn verifier(&self) -> &Parameters<C> { + fn parameters(&self) -> &<Self::Verifier as accumulator::Verifier>::Parameters { &self.parameters } diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index a93151afc..1faa49f99 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -63,9 +63,9 @@ ark-groth16 = { version = "0.3.0", optional = true, default-features = false } ark-r1cs-std = { version = "0.3.1", default-features = false } ark-relations = { version = "0.3.0", default-features = false } bip32 = { version = "0.2.2", default-features = false, features = ["bip39", "secp256k1"] } -blake2 = { version = "0.9.2", default-features = false } +blake2 = { version = "0.10.0", default-features = false } derivative = { version = "2.2.0", default-features = false } -dusk-plonk = { version = "0.8.2", optional = true, default-features = false } +dusk-plonk = { version = "0.9.0", optional = true, default-features = false } generic-array = { version = "0.14.4", default-features = false } manta-accounting = { path = "../manta-accounting" } manta-crypto = { path = "../manta-crypto" } diff --git a/manta-pay/src/accounting/key.rs b/manta-pay/src/accounting/key.rs index 9f2f0c987..cd1638d19 100644 --- a/manta-pay/src/accounting/key.rs +++ b/manta-pay/src/accounting/key.rs @@ -27,7 +27,9 @@ use alloc::{format, string::String}; use bip32::{Seed, XPrv}; use core::{marker::PhantomData, num::ParseIntError, str::FromStr}; -use manta_accounting::key::{HierarchicalKeyTable, HierarchicalKeyTableParameter}; +use manta_accounting::key::{ + HierarchicalKeyDerivationParameter, HierarchicalKeyDerivationScheme, SecretKeyPair, +}; use manta_util::{create_seal, seal}; pub use bip32::{Error, Mnemonic}; @@ -103,10 +105,10 @@ macro_rules! impl_from_for_parameter { } } -/// Implements the [`HierarchicalKeyTableParameter`] trait for `$name`. +/// Implements the [`HierarchicalKeyDerivationParameter`] trait for `$name`. macro_rules! impl_parameter { ($name:ty) => { - impl HierarchicalKeyTableParameter for $name { + impl HierarchicalKeyDerivationParameter for $name { #[inline] fn increment(&mut self) { self.0 += 1; @@ -167,8 +169,8 @@ where C: CoinType, { /// Converts a `mnemonic` phrase into a [`KeySecret`], locking it with `password`. - #[must_use] #[inline] + #[must_use] pub fn new(mnemonic: Mnemonic, password: &str) -> Self { Self { seed: mnemonic.to_seed(password), @@ -180,9 +182,9 @@ where /// Computes the [`BIP-0044`] path string for the given coin settings. /// /// [`BIP-0044`]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki -#[must_use] #[inline] -pub fn path_string<C>(account: &AccountParameter, index: &IndexParameter, kind: u32) -> String +#[must_use] +pub fn path_string<C>(account: ParameterType, spend: ParameterType, view: ParameterType) -> String where C: CoinType, { @@ -191,13 +193,13 @@ where "m/{}'/{}'/{}'/{}/{}", BIP_44_PURPOSE_ID, C::COIN_TYPE_ID, - account.0, - kind, - index.0 + account, + spend, + view, ) } -impl<C> HierarchicalKeyTable for KeySecret<C> +impl<C> HierarchicalKeyDerivationScheme for KeySecret<C> where C: CoinType, { @@ -207,20 +209,30 @@ where type Index = IndexParameter; - type Kind = u32; - type Error = Error; #[inline] - fn get( + fn derive_spend( + &self, + account: Self::Account, + spend: Self::Index, + ) -> Result<Self::SecretKey, Self::Error> { + Ok(XPrv::derive_from_path( + &self.seed, + &path_string::<C>(account.0, spend.0, 0).parse()?, + )) + } + + #[inline] + fn derive_view( &self, - account: &Self::Account, - index: &Self::Index, - kind: &Self::Kind, + account: Self::Account, + spend: Self::Index, + view: Self::Index, ) -> Result<Self::SecretKey, Self::Error> { - XPrv::derive_from_path( + Ok(XPrv::derive_from_path( &self.seed, - &path_string::<C>(account, index, *kind).parse()?, - ) + &path_string::<C>(account.0, spend.0, view.0 + 1).parse()?, + )) } } diff --git a/manta-pay/src/crypto/encryption.rs b/manta-pay/src/crypto/encryption.rs index c2a916e64..ff1fdebeb 100644 --- a/manta-pay/src/crypto/encryption.rs +++ b/manta-pay/src/crypto/encryption.rs @@ -23,6 +23,7 @@ use aes_gcm::{ aead::{Aead, NewAead}, Aes256Gcm, Nonce, }; +use alloc::vec::Vec; use core::marker::PhantomData; use generic_array::GenericArray; use manta_crypto::encryption::SymmetricKeyEncryptionScheme; From 26eddddd350eec1a3e0845bc2432c2ec3e40d51f Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 12 Dec 2021 17:58:58 -0500 Subject: [PATCH 134/275] wip: start updating transfer trait --- manta-accounting/src/lib.rs | 2 +- manta-accounting/src/transfer.rs | 323 +++++++++++++++++-------------- manta-crypto/src/commitment.rs | 14 +- 3 files changed, 190 insertions(+), 149 deletions(-) diff --git a/manta-accounting/src/lib.rs b/manta-accounting/src/lib.rs index 09d57ffb7..c77494417 100644 --- a/manta-accounting/src/lib.rs +++ b/manta-accounting/src/lib.rs @@ -31,4 +31,4 @@ pub mod asset; pub mod fs; pub mod key; pub mod transfer; -pub mod wallet; +// TODO: pub mod wallet; diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index bf21aad02..b2844c4d5 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -20,14 +20,14 @@ use crate::asset::{Asset, AssetId, AssetValue}; use alloc::vec::Vec; use core::ops::Add; use manta_crypto::{ - accumulator::{Accumulator, MembershipProof, Verifier}, - commitment::{CommitmentScheme, Input as CommitmentInput}, + accumulator::{self, Accumulator, MembershipProof, Verifier}, + commitment::{self, CommitmentScheme, Input as CommitmentInput}, constraint::{ Allocation, Constant, ConstraintSystem, Derived, Equal, Input as ProofSystemInput, ProofSystem, Public, PublicOrSecret, Secret, Variable, VariableSource, }, encryption::{DecryptedMessage, EncryptedMessage, HybridPublicKeyEncryptionScheme}, - key::{KeyAgreementScheme, KeyDerivationFunction}, + key::{self, KeyAgreementScheme, KeyDerivationFunction}, rand::{CryptoRng, Rand, RngCore, Sample}, }; use manta_util::{create_seal, from_variant_impl}; @@ -57,68 +57,83 @@ where /// Generates a UTXO, commiting `asset` with the given `trapdoor`. #[inline] fn generate_utxo<C, I, V>( - commitment_scheme: &C, - asset: &Asset<I, V>, + parameters: &C::Parameters, trapdoor: &C::Trapdoor, + asset: &Asset<I, V>, ) -> C::Output where - C: CommitmentScheme + CommitmentInput<I> + CommitmentInput<V>, + C: CommitmentScheme, + C::Input: Default + CommitmentInput<I> + CommitmentInput<V>, { - commitment_scheme - .start() + C::start(parameters, trapdoor) .update(&asset.id) .update(&asset.value) - .commit(trapdoor) + .commit() +} + +/// +#[inline] +fn generate_full_utxo<KA, F, C, I, V>( + parameters: &C::Parameters, + secret_key: &KA::SecretKey, + public_key: &KA::PublicKey, + asset: &Asset<I, V>, +) -> C::Output +where + KA: KeyAgreementScheme, + F: KeyDerivationFunction<KA::SharedSecret, Output = C::Trapdoor>, + C: CommitmentScheme, + C::Input: Default + CommitmentInput<I> + CommitmentInput<V>, +{ + generate_utxo::<C, I, V>( + parameters, + &generate_trapdoor::<KA, F>(secret_key, public_key), + asset, + ) } /// Generates a void number, commiting `secret_key` with the given `trapdoor`. #[inline] fn generate_void_number<C, SK>( - commitment_scheme: &C, - secret_key: &SK, + parameters: &C::Parameters, trapdoor: &C::Trapdoor, + secret_key: &SK, ) -> C::Output where - C: CommitmentScheme + CommitmentInput<SK>, + C: CommitmentScheme, + C::Input: Default + CommitmentInput<SK>, { - commitment_scheme.commit_one(secret_key, trapdoor) + C::start(parameters, trapdoor).update(secret_key).commit() } /// Generates an ephemeral secret key, commiting to the `spend` key at the given `index` with the /// current `ledger_checkpoint`. #[inline] fn generate_ephemeral_secret_key<C, L, B, PK>( - commitment_scheme: &C, + parameters: &C::Parameters, + trapdoor: &C::Trapdoor, ledger_checkpoint: &L, index: &B, spend: &PK, - ephemeral_key_trapdoor: &C::Trapdoor, ) -> C::Output where - C: CommitmentScheme + CommitmentInput<L> + CommitmentInput<B> + CommitmentInput<PK>, + C: CommitmentScheme, + C::Input: Default + CommitmentInput<L> + CommitmentInput<B> + CommitmentInput<PK>, { - commitment_scheme - .start() + C::start(parameters, trapdoor) .update(ledger_checkpoint) .update(index) .update(spend) - .commit(ephemeral_key_trapdoor) + .commit() } /// Transfer Configuration pub trait Configuration { - /// Key Agreement Scheme - type KeyAgreementScheme: KeyAgreementScheme; - - /// Secret Key Variable - type SecretKeyVar: Variable<Self::ConstraintSystem, Type = SecretKey<Self>, Mode = Secret>; + /// Secret Key + type SecretKey: Clone; - /// Public Key Variable - type PublicKeyVar: Variable<Self::ConstraintSystem, Type = PublicKey<Self>, Mode = Secret>; - - /// Key Agreement Scheme Variable - type KeyAgreementSchemeVar: KeyAgreementScheme<SecretKey = Self::SecretKeyVar, PublicKey = Self::PublicKeyVar> - + Variable<Self::ConstraintSystem, Type = Self::KeyAgreementScheme, Mode = Constant>; + /// Key Agreement Scheme + type KeyAgreementScheme: KeyAgreementScheme<SecretKey = Self::SecretKey>; /// Trapdoor Derivation Function type TrapdoorDerivationFunction: KeyDerivationFunction< @@ -126,79 +141,53 @@ pub trait Configuration { Output = Trapdoor<Self>, >; - /// Trapdoor Derivation Function Variable - type TrapdoorDerivationFunctionVar: KeyDerivationFunction< - SharedSecretVar<Self>, - Output = TrapdoorVar<Self>, - >; - - /// Ephemeral-Key Trapdoor + /// Ephemeral Key Trapdoor type EphemeralKeyTrapdoor: Sample; - /// Ephemeral-Key Trapdoor Variable - type EphemeralKeyTrapdoorVar: Variable< - Self::ConstraintSystem, - Type = Self::EphemeralKeyTrapdoor, - Mode = Secret, - >; - - /// Ephemeral-Key Commitment Scheme - type EphemeralKeyCommitmentScheme: CommitmentScheme<Trapdoor = Self::EphemeralKeyTrapdoor, Output = SecretKey<Self>> + /// Ephemeral Key Commitment Scheme Input + type EphemeralKeyCommitmentSchemeInput: Default + CommitmentInput<Self::LedgerCheckpoint> + CommitmentInput<u8> + CommitmentInput<PublicKey<Self>>; - /// Ephemeral-Key Commitment Scheme Variable - type EphemeralKeyCommitmentSchemeVar: CommitmentScheme<Trapdoor = Self::EphemeralKeyTrapdoorVar, Output = Self::SecretKeyVar> + /// + type EphemeralKeyCommitmentSchemeInputVar: Default + CommitmentInput<Self::LedgerCheckpointVar> + CommitmentInput<Self::ByteVar> - + CommitmentInput<Self::PublicKeyVar> - + Variable<Self::ConstraintSystem, Type = Self::EphemeralKeyCommitmentScheme, Mode = Constant>; + + CommitmentInput<PublicKeyVar<Self>>; - /// Commitment Scheme Output - type CommitmentSchemeOutput: PartialEq; - - /// Commitment Scheme Output Variable - type CommitmentSchemeOutputVar: Variable<Self::ConstraintSystem, Type = Self::CommitmentSchemeOutput, Mode = PublicOrSecret> - + Equal<Self::ConstraintSystem>; + /// Ephemeral Key Commitment Scheme + type EphemeralKeyCommitmentScheme: CommitmentScheme< + Trapdoor = Self::EphemeralKeyTrapdoor, + Output = SecretKey<Self>, + >; - /// Commitment Scheme - type CommitmentScheme: CommitmentScheme<Output = Self::CommitmentSchemeOutput> + /// Commitment Scheme Input + type CommitmentSchemeInput: Default + CommitmentInput<AssetId> + CommitmentInput<AssetValue> + CommitmentInput<SecretKey<Self>>; - /// Commitment Scheme Variable - type CommitmentSchemeVar: CommitmentScheme<Output = Self::CommitmentSchemeOutputVar> + /// Commitment Scheme Input Variable + type CommitmentSchemeInputVar: Default + CommitmentInput<Self::AssetIdVar> + CommitmentInput<Self::AssetValueVar> - + CommitmentInput<Self::SecretKeyVar> - + Variable<Self::ConstraintSystem, Type = Self::CommitmentScheme, Mode = Constant>; + + CommitmentInput<SecretKeyVar<Self>>; - /// UTXO Set Verifier - type UtxoSetVerifier: Verifier<Item = Utxo<Self>, Verification = bool>; + /// Commitment Scheme Output + type CommitmentSchemeOutput: PartialEq; - /// UTXO Set Witness Variable - type UtxoSetWitnessVar: Variable< - Self::ConstraintSystem, - Type = <Self::UtxoSetVerifier as Verifier>::Witness, - Mode = Secret, - >; + /// Commitment Scheme Output Variable + type CommitmentSchemeOutputVar: Equal<Self::ConstraintSystem>; - /// UTXO Set Output Variable - type UtxoSetOutputVar: Variable< - Self::ConstraintSystem, - Type = <Self::UtxoSetVerifier as Verifier>::Output, - Mode = Public, + /// Commitment Scheme + type CommitmentScheme: CommitmentScheme< + Input = Self::CommitmentSchemeInput, + Output = Self::CommitmentSchemeOutput, >; - /// UTXO Set Verifier Variable - type UtxoSetVerifierVar: Verifier< - Item = UtxoVar<Self>, - Witness = Self::UtxoSetWitnessVar, - Output = Self::UtxoSetOutputVar, - Verification = <Self::ConstraintSystem as ConstraintSystem>::Bool, - > + Variable<Self::ConstraintSystem, Type = Self::UtxoSetVerifier, Mode = Constant>; + /// UTXO Set Verifier + type UtxoSetVerifier: Verifier<Item = Utxo<Self>, Verification = bool>; /// Ledger Checkpoint Type type LedgerCheckpoint; @@ -222,8 +211,15 @@ pub trait Configuration { /// Byte Variable type ByteVar: Variable<Self::ConstraintSystem, Type = u8, Mode = Public>; - /// Constraint System Type - type ConstraintSystem: ConstraintSystem; + /// Constraint System + type ConstraintSystem: ConstraintSystem + + key::constraint::KeyAgreementScheme<Self::KeyAgreementScheme> + + commitment::constraint::CommitmentScheme<Self::EphemeralKeyCommitmentScheme> + + commitment::constraint::CommitmentScheme< + Self::CommitmentScheme, + Input = Self::CommitmentSchemeInputVar, + Output = Self::CommitmentSchemeOutputVar, + > + accumulator::constraint::Verifier<Self::UtxoSetVerifier>; /// Proof System Type type ProofSystem: ProofSystem<ConstraintSystem = Self::ConstraintSystem, Verification = bool> @@ -246,22 +242,48 @@ pub type AssetVar<C> = Asset<<C as Configuration>::AssetIdVar, <C as Configurati /// Secret Key Type pub type SecretKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::SecretKey; +/// Secret Key Variable Type +pub type SecretKeyVar<C> = + <<C as Configuration>::ConstraintSystem as key::constraint::KeyAgreementScheme< + <C as Configuration>::KeyAgreementScheme, + >>::SecretKey; + /// Public Key Type pub type PublicKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::PublicKey; +/// Public Key Variable Type +pub type PublicKeyVar<C> = + <<C as Configuration>::ConstraintSystem as key::constraint::KeyAgreementScheme< + <C as Configuration>::KeyAgreementScheme, + >>::PublicKey; + /// Shared Secret Type pub type SharedSecret<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret; +/* /// Shared Secret Variable Type pub type SharedSecretVar<C> = <<C as Configuration>::KeyAgreementSchemeVar as KeyAgreementScheme>::SharedSecret; +*/ + +/// Ephemeral Key Trapdoor Type +pub type EphemeralKeyTrapdoor<C> = + <<C as Configuration>::EphemeralKeyCommitmentScheme as CommitmentScheme>::Trapdoor; + +/// Ephemeral Key Trapdoor Variable Type +pub type EphemeralKeyTrapdoorVar<C> = + <<C as Configuration>::ConstraintSystem as commitment::constraint::CommitmentScheme< + <C as Configuration>::EphemeralKeyCommitmentScheme, + >>::Trapdoor; /// Trapdoor Type pub type Trapdoor<C> = <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Trapdoor; +/* /// Trapdoor Variable Type pub type TrapdoorVar<C> = <<C as Configuration>::CommitmentSchemeVar as CommitmentScheme>::Trapdoor; +*/ /// Commitment Scheme Output Type pub type CommitmentSchemeOutput<C> = <C as Configuration>::CommitmentSchemeOutput; @@ -282,7 +304,10 @@ pub type UtxoSetOutput<C> = <<C as Configuration>::UtxoSetVerifier as Verifier>: pub type UtxoMembershipProof<C> = MembershipProof<<C as Configuration>::UtxoSetVerifier>; /// UTXO Membership Proof Variable Type -pub type UtxoMembershipProofVar<C> = MembershipProof<<C as Configuration>::UtxoSetVerifierVar>; +pub type UtxoMembershipProofVar<C> = accumulator::constraint::MembershipProof< + <C as Configuration>::UtxoSetVerifier, + <C as Configuration>::ConstraintSystem, +>; /// Void Number Type pub type VoidNumber<C> = CommitmentSchemeOutput<C>; @@ -314,21 +339,30 @@ pub type ProofInput<C> = <<C as Configuration>::ProofSystem as ProofSystem>::Inp /// Transfer Validity Proof Type pub type Proof<C> = <ProofSystemType<C> as ProofSystem>::Proof; -/* TODO: +/// Transfer Parameters pub struct Parameters<C> where C: Configuration, { - /// - ephemeral_key_commitment_scheme: C::EphemeralKeyCommitmentScheme, + /// Ephemeral Key Commitment Scheme Parameters + pub ephemeral_key_commitment_scheme: + <C::EphemeralKeyCommitmentScheme as CommitmentScheme>::Parameters, - /// - certificate_commitment_scheme: C::CertificateCommitmentScheme, + /// Commitment Scheme Parameters + pub commitment_scheme: <C::CommitmentScheme as CommitmentScheme>::Parameters, - /// - utxo_set_verifier: C::UtxoSetVerifier, + /// UTXO Set Verifier Parameters + pub utxo_set_verifier: <C::UtxoSetVerifier as Verifier>::Parameters, +} + +/// Transfer Parameters Variable +pub struct ParametersVar<C> +where + C: Configuration, +{ + // FIXME: ... + __: C, } -*/ /// Spending Key pub struct SpendingKey<C> @@ -371,33 +405,30 @@ where #[inline] pub fn validate_utxo( &self, + parameters: &<C::CommitmentScheme as CommitmentScheme>::Parameters, ephemeral_key: &PublicKey<C>, asset: &Asset, utxo: &Utxo<C>, - commitment_scheme: &C::CommitmentScheme, ) -> bool { - &generate_utxo( - commitment_scheme, - asset, - &generate_trapdoor::<C::KeyAgreementScheme, C::TrapdoorDerivationFunction>( - &self.spend, - ephemeral_key, - ), - ) == utxo + &generate_full_utxo::< + C::KeyAgreementScheme, + C::TrapdoorDerivationFunction, + C::CommitmentScheme, + _, + _, + >(parameters, &self.spend, ephemeral_key, asset) + == utxo } /// Prepares `self` for spending `asset` with the given `ephemeral_key`. #[inline] pub fn sender( &self, + parameters: &<C::CommitmentScheme as CommitmentScheme>::Parameters, ephemeral_key: PublicKey<C>, asset: Asset, - commitment_scheme: &C::CommitmentScheme, - ) -> PreSender<C> - where - SecretKey<C>: Clone, - { - PreSender::new(self.spend.clone(), ephemeral_key, asset, commitment_scheme) + ) -> PreSender<C> { + PreSender::new(parameters, self.spend.clone(), ephemeral_key, asset) } /// Prepares `self` for receiving `asset`. @@ -458,18 +489,20 @@ where /// Builds a new [`PreSender`] for `spend` to spend `asset` with `ephemeral_key`. #[inline] pub fn new( + parameters: &<C::CommitmentScheme as CommitmentScheme>::Parameters, spend: SecretKey<C>, ephemeral_key: PublicKey<C>, asset: Asset, - commitment_scheme: &C::CommitmentScheme, ) -> Self { let trapdoor = generate_trapdoor::<C::KeyAgreementScheme, C::TrapdoorDerivationFunction>( &spend, &ephemeral_key, ); Self { - utxo: generate_utxo(commitment_scheme, &asset, &trapdoor), - void_number: generate_void_number(commitment_scheme, &spend, &trapdoor), + utxo: generate_utxo::<C::CommitmentScheme, _, _>(parameters, &trapdoor, &asset), + void_number: generate_void_number::<C::CommitmentScheme, _>( + parameters, &trapdoor, &spend, + ), spend, ephemeral_key, asset, @@ -624,10 +657,10 @@ where C: Configuration, { /// Secret Spend Key - spend: C::SecretKeyVar, + spend: SecretKeyVar<C>, /// Ephemeral Public Spend Key - ephemeral_key: C::PublicKeyVar, + ephemeral_key: PublicKeyVar<C>, /// Asset asset: AssetVar<C>, @@ -648,10 +681,10 @@ where #[inline] pub fn get_well_formed_asset( self, - commitment_scheme: &C::CommitmentSchemeVar, - utxo_set_verifier: &C::UtxoSetVerifierVar, + parameters: &ParametersVar<C>, cs: &mut C::ConstraintSystem, ) -> AssetVar<C> { + /* TODO: let trapdoor = generate_trapdoor::< C::KeyAgreementSchemeVar, C::TrapdoorDerivationFunctionVar, @@ -665,6 +698,8 @@ where &generate_void_number(commitment_scheme, &self.spend, &trapdoor), ); self.asset + */ + todo!() } } @@ -678,6 +713,7 @@ where #[inline] fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + /* TODO: match allocation { Allocation::Known(this, mode) => Self { spend: this.spend.as_known(cs, mode), @@ -694,6 +730,8 @@ where void_number: VoidNumberVar::<C>::new_unknown(cs, Public), }, } + */ + todo!() } } @@ -868,16 +906,10 @@ where #[inline] pub fn upgrade( self, + parameters: &<C::CommitmentScheme as CommitmentScheme>::Parameters, ephemeral_key: SecretKey<C>, - commitment_scheme: &C::CommitmentScheme, ) -> Receiver<C> { - Receiver::new( - self.spend, - self.view, - ephemeral_key, - self.asset, - commitment_scheme, - ) + Receiver::new(parameters, self.spend, self.view, ephemeral_key, self.asset) } } @@ -909,21 +941,20 @@ where /// Builds a new [`Receiver`] for `spend` to receive `asset` with `ephemeral_key`. #[inline] pub fn new( + parameters: &<C::CommitmentScheme as CommitmentScheme>::Parameters, spend: PublicKey<C>, view: PublicKey<C>, ephemeral_key: SecretKey<C>, asset: Asset, - commitment_scheme: &C::CommitmentScheme, ) -> Self { Self { - utxo: generate_utxo( - commitment_scheme, - &asset, - &generate_trapdoor::<C::KeyAgreementScheme, C::TrapdoorDerivationFunction>( - &ephemeral_key, - &spend, - ), - ), + utxo: generate_full_utxo::< + C::KeyAgreementScheme, + C::TrapdoorDerivationFunction, + C::CommitmentScheme, + _, + _, + >(parameters, &ephemeral_key, &spend, &asset), spend, view, ephemeral_key, @@ -953,7 +984,7 @@ where C: Configuration, { /// Public Spend Key - spend: C::PublicKeyVar, + spend: PublicKeyVar<C>, /// Asset asset: AssetVar<C>, @@ -971,13 +1002,13 @@ where #[inline] pub fn get_well_formed_asset( self, + parameters: &ParametersVar<C>, index: C::ByteVar, ledger_checkpoint: &C::LedgerCheckpointVar, - ephemeral_key_trapdoor: &C::EphemeralKeyTrapdoorVar, - ephemeral_key_commitment_scheme: &C::EphemeralKeyCommitmentSchemeVar, - commitment_scheme: &C::CommitmentSchemeVar, + ephemeral_key_trapdoor: &EphemeralKeyTrapdoorVar<C>, cs: &mut C::ConstraintSystem, ) -> AssetVar<C> { + /* TODO: let ephemeral_key = generate_ephemeral_secret_key( ephemeral_key_commitment_scheme, ledger_checkpoint, @@ -997,6 +1028,8 @@ where ), ); self.asset + */ + todo!() } } @@ -1010,6 +1043,7 @@ where #[inline] fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + /* match allocation { Allocation::Known(this, mode) => Self { spend: this.spend.as_known(cs, mode), @@ -1022,6 +1056,8 @@ where utxo: UtxoVar::<C>::new_unknown(cs, Public), }, } + */ + todo!() } } @@ -1249,6 +1285,7 @@ where } } + /* /// Generates a proving and verifying context for this transfer shape. #[inline] pub fn generate_context<R>( @@ -1315,6 +1352,7 @@ where rng, ) } + */ } /// Full Transfer @@ -1354,6 +1392,7 @@ impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, cons where C: Configuration, { + /* /// Computes the [`TransferPost`] for `self`. #[inline] fn into_post<R>( @@ -1394,6 +1433,7 @@ where ledger_checkpoint: self.ledger_checkpoint, }) } + */ } /// Full Transfer Variable @@ -1422,7 +1462,7 @@ struct FullTransferVar< sinks: Vec<C::AssetValueVar>, /// Ephemeral Key Trapdoor - ephemeral_key_trapdoor: C::EphemeralKeyTrapdoorVar, + ephemeral_key_trapdoor: EphemeralKeyTrapdoorVar<C>, /// Ledger Checkpoint ledger_checkpoint: C::LedgerCheckpointVar, @@ -1433,6 +1473,7 @@ impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, cons where C: Configuration, { + /* /// Builds constraints for the [`Transfer`] validity proof. #[inline] fn build_validity_constraints( @@ -1485,6 +1526,7 @@ where _ => cs.assert_all_eq(secret_asset_ids.iter()), } } + */ } impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> @@ -1498,6 +1540,7 @@ where #[inline] fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + /* match allocation { Allocation::Known(this, mode) => Self { asset_id: this.asset_id.map(|id| id.as_known(cs, Public)), @@ -1555,6 +1598,8 @@ where ledger_checkpoint: C::LedgerCheckpointVar::new_unknown(cs, mode), }, } + */ + todo!() } } @@ -1705,8 +1750,6 @@ where /// Generates the public input for the [`Transfer`] validation proof. #[inline] pub fn generate_proof_input(&self) -> ProofInput<C> { - // TODO: See comments in `crate::identity::constraint` about automatically deriving this - // method from possibly `TransferParticipantsVar`? let mut input = Default::default(); if let Some(asset_id) = self.asset_id { C::ProofSystem::extend(&mut input, &asset_id); @@ -1736,7 +1779,6 @@ where let source_posting_keys = ledger .check_source_balances(self.sources) .map_err(TransferPostError::InsufficientPublicBalance)?; - for (i, p) in self.sender_posts.iter().enumerate() { if self .sender_posts @@ -1747,7 +1789,6 @@ where return Err(SenderPostError::AssetSpent.into()); } } - let sender_posting_keys = self .sender_posts .into_iter() diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index 6edf1b34f..8e496b161 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -53,12 +53,12 @@ pub trait CommitmentScheme { } /// Commitment Extended Input -pub trait Input<T>: CommitmentScheme +pub trait Input<T> where T: ?Sized, { - /// Extends the `input` data with `next`. - fn extend(input: &mut Self::Input, next: &T); + /// Extends `self` with input data `next`. + fn extend(&mut self, next: &T); } /// Commitment Builder @@ -106,9 +106,9 @@ where pub fn update<T>(mut self, next: &T) -> Self where T: ?Sized, - C: Input<T>, + C::Input: Input<T>, { - C::extend(&mut self.input, next); + self.input.extend(next); self } @@ -118,10 +118,10 @@ where where T: 't + ?Sized, I: IntoIterator<Item = &'t T>, - C: Input<T>, + C::Input: Input<T>, { for next in iter { - C::extend(&mut self.input, next); + self.input.extend(next); } self } From 76fe73e872af2161f31b4390d53ce95846d7dd65 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 15 Dec 2021 00:58:24 -0500 Subject: [PATCH 135/275] fix: update to new transfer protocol --- manta-accounting/src/transfer.rs | 916 +++++++++++++++---------------- manta-crypto/src/commitment.rs | 80 ++- manta-crypto/src/encryption.rs | 17 +- manta-crypto/src/key.rs | 27 +- 4 files changed, 564 insertions(+), 476 deletions(-) diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index b2844c4d5..48ba6e0b1 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -28,7 +28,7 @@ use manta_crypto::{ }, encryption::{DecryptedMessage, EncryptedMessage, HybridPublicKeyEncryptionScheme}, key::{self, KeyAgreementScheme, KeyDerivationFunction}, - rand::{CryptoRng, Rand, RngCore, Sample}, + rand::{CryptoRng, RngCore, Sample}, }; use manta_util::{create_seal, from_variant_impl}; @@ -44,182 +44,155 @@ pub const fn has_public_participants( sources > 0 || sinks > 0 } -/// Generates a commitment trapdoor from a key agreement with `secret_key` and `public_key`. -#[inline] -fn generate_trapdoor<KA, F>(secret_key: &KA::SecretKey, public_key: &KA::PublicKey) -> F::Output -where - KA: KeyAgreementScheme, - F: KeyDerivationFunction<KA::SharedSecret>, -{ - F::derive(KA::agree(secret_key, public_key)) -} - -/// Generates a UTXO, commiting `asset` with the given `trapdoor`. -#[inline] -fn generate_utxo<C, I, V>( - parameters: &C::Parameters, - trapdoor: &C::Trapdoor, - asset: &Asset<I, V>, -) -> C::Output -where - C: CommitmentScheme, - C::Input: Default + CommitmentInput<I> + CommitmentInput<V>, -{ - C::start(parameters, trapdoor) - .update(&asset.id) - .update(&asset.value) - .commit() -} - -/// -#[inline] -fn generate_full_utxo<KA, F, C, I, V>( - parameters: &C::Parameters, - secret_key: &KA::SecretKey, - public_key: &KA::PublicKey, - asset: &Asset<I, V>, -) -> C::Output -where - KA: KeyAgreementScheme, - F: KeyDerivationFunction<KA::SharedSecret, Output = C::Trapdoor>, - C: CommitmentScheme, - C::Input: Default + CommitmentInput<I> + CommitmentInput<V>, -{ - generate_utxo::<C, I, V>( - parameters, - &generate_trapdoor::<KA, F>(secret_key, public_key), - asset, - ) -} - -/// Generates a void number, commiting `secret_key` with the given `trapdoor`. -#[inline] -fn generate_void_number<C, SK>( - parameters: &C::Parameters, - trapdoor: &C::Trapdoor, - secret_key: &SK, -) -> C::Output -where - C: CommitmentScheme, - C::Input: Default + CommitmentInput<SK>, -{ - C::start(parameters, trapdoor).update(secret_key).commit() -} - -/// Generates an ephemeral secret key, commiting to the `spend` key at the given `index` with the -/// current `ledger_checkpoint`. -#[inline] -fn generate_ephemeral_secret_key<C, L, B, PK>( - parameters: &C::Parameters, - trapdoor: &C::Trapdoor, - ledger_checkpoint: &L, - index: &B, - spend: &PK, -) -> C::Output -where - C: CommitmentScheme, - C::Input: Default + CommitmentInput<L> + CommitmentInput<B> + CommitmentInput<PK>, -{ - C::start(parameters, trapdoor) - .update(ledger_checkpoint) - .update(index) - .update(spend) - .commit() -} - /// Transfer Configuration pub trait Configuration { - /// Secret Key + /// Secret Key Type type SecretKey: Clone; - /// Key Agreement Scheme + /// Secret Key Variable Type + type SecretKeyVar: Variable<Self::ConstraintSystem, Type = SecretKey<Self>, Mode = Secret>; + + /// Public Key Variable Type + type PublicKeyVar: Variable<Self::ConstraintSystem, Type = PublicKey<Self>, Mode = Public> + + Equal<Self::ConstraintSystem>; + + /// Key Agreement Scheme Type type KeyAgreementScheme: KeyAgreementScheme<SecretKey = Self::SecretKey>; - /// Trapdoor Derivation Function - type TrapdoorDerivationFunction: KeyDerivationFunction< - SharedSecret<Self>, - Output = Trapdoor<Self>, + /// Ephemeral Key Trapdoor Type + type EphemeralKeyTrapdoor: Sample; + + /// Ephemeral Key Trapdoor Variable Type + type EphemeralKeyTrapdoorVar: Variable< + Self::ConstraintSystem, + Type = Self::EphemeralKeyTrapdoor, + Mode = Secret, >; - /// Ephemeral Key Trapdoor - type EphemeralKeyTrapdoor: Sample; + /// Ephemeral Key Commitment Scheme Parameters Variable Type + type EphemeralKeyParametersVar: Variable< + Self::ConstraintSystem, + Type = EphemeralKeyParameters<Self>, + Mode = Constant, + >; - /// Ephemeral Key Commitment Scheme Input + /// Ephemeral Key Commitment Scheme Input Type type EphemeralKeyCommitmentSchemeInput: Default - + CommitmentInput<Self::LedgerCheckpoint> - + CommitmentInput<u8> - + CommitmentInput<PublicKey<Self>>; + + CommitmentInput<AssetId> + + CommitmentInput<AssetValue>; - /// + /// Ephemeral Key Commitment Scheme Input Variable type EphemeralKeyCommitmentSchemeInputVar: Default - + CommitmentInput<Self::LedgerCheckpointVar> - + CommitmentInput<Self::ByteVar> - + CommitmentInput<PublicKeyVar<Self>>; + + CommitmentInput<Self::AssetIdVar> + + CommitmentInput<Self::AssetValueVar>; - /// Ephemeral Key Commitment Scheme + /// Ephemeral Key Commitment Scheme Type type EphemeralKeyCommitmentScheme: CommitmentScheme< Trapdoor = Self::EphemeralKeyTrapdoor, - Output = SecretKey<Self>, + Input = Self::EphemeralKeyCommitmentSchemeInput, + Output = Self::SecretKey, + >; + + /// Trapdoor Derivation Function Type + type TrapdoorDerivationFunction: KeyDerivationFunction< + Key = SharedSecret<Self>, + Output = Trapdoor<Self>, + >; + + /// Commitment Scheme Parameters Variable Type + type CommitmentSchemeParametersVar: Variable< + Self::ConstraintSystem, + Type = CommitmentSchemeParameters<Self>, + Mode = Constant, >; - /// Commitment Scheme Input + /// Commitment Scheme Input Type type CommitmentSchemeInput: Default + CommitmentInput<AssetId> + CommitmentInput<AssetValue> - + CommitmentInput<SecretKey<Self>>; + + CommitmentInput<Self::SecretKey>; - /// Commitment Scheme Input Variable + /// Commitment Scheme Input Variable Type type CommitmentSchemeInputVar: Default + CommitmentInput<Self::AssetIdVar> + CommitmentInput<Self::AssetValueVar> + CommitmentInput<SecretKeyVar<Self>>; - /// Commitment Scheme Output + /// Commitment Scheme Output Type type CommitmentSchemeOutput: PartialEq; - /// Commitment Scheme Output Variable - type CommitmentSchemeOutputVar: Equal<Self::ConstraintSystem>; + /// Commitment Scheme Output Variable Type + type CommitmentSchemeOutputVar: Variable<Self::ConstraintSystem, Type = CommitmentSchemeOutput<Self>, Mode = PublicOrSecret> + + Equal<Self::ConstraintSystem>; - /// Commitment Scheme + /// Commitment Scheme Type type CommitmentScheme: CommitmentScheme< Input = Self::CommitmentSchemeInput, Output = Self::CommitmentSchemeOutput, >; - /// UTXO Set Verifier - type UtxoSetVerifier: Verifier<Item = Utxo<Self>, Verification = bool>; + /// UTXO Set Parameters Variable Type + type UtxoSetParametersVar: Variable< + Self::ConstraintSystem, + Type = UtxoSetParameters<Self>, + Mode = Constant, + >; - /// Ledger Checkpoint Type - type LedgerCheckpoint; + /// UTXO Set Witness Variable Type + type UtxoSetWitnessVar: Variable< + Self::ConstraintSystem, + Type = UtxoSetWitness<Self>, + Mode = Secret, + >; - /// Ledger Checkpoint Variable - type LedgerCheckpointVar: Variable< + /// UTXO Set Output Variable Type + type UtxoSetOutputVar: Variable< Self::ConstraintSystem, - Type = Self::LedgerCheckpoint, + Type = UtxoSetOutput<Self>, Mode = Public, >; - /// Asset Id Variable + /// UTXO Set Verifier Type + type UtxoSetVerifier: Verifier<Item = Utxo<Self>, Verification = bool>; + + /// Asset Id Variable Type type AssetIdVar: Variable<Self::ConstraintSystem, Type = AssetId, Mode = PublicOrSecret> + Equal<Self::ConstraintSystem>; - /// Asset Value Variable + /// Asset Value Variable Type type AssetValueVar: Variable<Self::ConstraintSystem, Type = AssetValue, Mode = PublicOrSecret> + Equal<Self::ConstraintSystem> + Add<Output = Self::AssetValueVar>; - /// Byte Variable - type ByteVar: Variable<Self::ConstraintSystem, Type = u8, Mode = Public>; - - /// Constraint System + /// Constraint System Type type ConstraintSystem: ConstraintSystem - + key::constraint::KeyAgreementScheme<Self::KeyAgreementScheme> - + commitment::constraint::CommitmentScheme<Self::EphemeralKeyCommitmentScheme> - + commitment::constraint::CommitmentScheme< + + key::constraint::KeyAgreementScheme< + Self::KeyAgreementScheme, + SecretKey = SecretKeyVar<Self>, + PublicKey = PublicKeyVar<Self>, + > + key::constraint::KeyDerivationFunction< + Self::TrapdoorDerivationFunction, + Key = SharedSecretVar<Self>, + Output = TrapdoorVar<Self>, + > + commitment::constraint::CommitmentScheme< + Self::EphemeralKeyCommitmentScheme, + Parameters = Self::EphemeralKeyParametersVar, + Trapdoor = Self::EphemeralKeyTrapdoorVar, + Input = Self::EphemeralKeyCommitmentSchemeInputVar, + Output = SecretKeyVar<Self>, + > + commitment::constraint::CommitmentScheme< Self::CommitmentScheme, + Parameters = Self::CommitmentSchemeParametersVar, Input = Self::CommitmentSchemeInputVar, Output = Self::CommitmentSchemeOutputVar, - > + accumulator::constraint::Verifier<Self::UtxoSetVerifier>; + > + accumulator::constraint::Verifier< + Self::UtxoSetVerifier, + Parameters = Self::UtxoSetParametersVar, + Item = UtxoVar<Self>, + Witness = Self::UtxoSetWitnessVar, + Output = Self::UtxoSetOutputVar, + Verification = <Self::ConstraintSystem as ConstraintSystem>::Bool, + >; /// Proof System Type type ProofSystem: ProofSystem<ConstraintSystem = Self::ConstraintSystem, Verification = bool> @@ -227,13 +200,152 @@ pub trait Configuration { + ProofSystemInput<AssetValue> + ProofSystemInput<UtxoSetOutput<Self>> + ProofSystemInput<CommitmentSchemeOutput<Self>> - + ProofSystemInput<Self::LedgerCheckpoint>; + + ProofSystemInput<PublicKey<Self>>; /// Note Encryption Scheme Type type NoteEncryptionScheme: HybridPublicKeyEncryptionScheme< Plaintext = Asset, KeyAgreementScheme = Self::KeyAgreementScheme, >; + + /// Generates the ephemeral secret key from `parameters`, `trapdoor`, and `asset`. + #[inline] + fn ephemeral_secret_key( + parameters: &EphemeralKeyParameters<Self>, + trapdoor: &EphemeralKeyTrapdoor<Self>, + asset: Asset, + ) -> SecretKey<Self> { + Self::EphemeralKeyCommitmentScheme::start(parameters, trapdoor) + .update(&asset.id) + .update(&asset.value) + .commit() + } + + /// Generates the ephemeral secret key from `parameters`, `trapdoor`, and `asset`. + #[inline] + fn ephemeral_secret_key_var( + cs: &mut Self::ConstraintSystem, + parameters: &EphemeralKeyParametersVar<Self>, + trapdoor: &EphemeralKeyTrapdoorVar<Self>, + asset: &AssetVar<Self>, + ) -> SecretKeyVar<Self> { + commitment::constraint::CommitmentScheme::<Self::EphemeralKeyCommitmentScheme>::start( + parameters, trapdoor, + ) + .update(&asset.id) + .update(&asset.value) + .commit(cs) + } + + /// Derives a public key variable from a secret key variable. + #[inline] + fn ephemeral_public_key_var( + cs: &mut Self::ConstraintSystem, + secret_key: &SecretKeyVar<Self>, + ) -> PublicKeyVar<Self> { + key::constraint::KeyAgreementScheme::<Self::KeyAgreementScheme>::derive(cs, secret_key) + } + + /// Generates the commitment trapdoor associated to `secret_key` and `public_key`. + #[inline] + fn trapdoor(secret_key: &SecretKey<Self>, public_key: &PublicKey<Self>) -> Trapdoor<Self> { + Self::TrapdoorDerivationFunction::derive(Self::KeyAgreementScheme::agree( + secret_key, public_key, + )) + } + + /// Generates the commitment trapdoor associated to `secret_key` and `public_key`. + #[inline] + fn trapdoor_var( + cs: &mut Self::ConstraintSystem, + secret_key: &SecretKeyVar<Self>, + public_key: &PublicKeyVar<Self>, + ) -> TrapdoorVar<Self> { + let shared_secret = key::constraint::KeyAgreementScheme::agree(cs, secret_key, public_key); + key::constraint::KeyDerivationFunction::derive(cs, shared_secret) + } + + /// Generates the UTXO associated to `trapdoor` and `asset` from `parameters`. + #[inline] + fn utxo( + parameters: &CommitmentSchemeParameters<Self>, + trapdoor: &Trapdoor<Self>, + asset: &Asset, + ) -> Utxo<Self> { + Self::CommitmentScheme::start(parameters, trapdoor) + .update(&asset.id) + .update(&asset.value) + .commit() + } + + /// Generates the UTXO associated to `trapdoor` and `asset` from `parameters`. + #[inline] + fn utxo_var( + cs: &mut Self::ConstraintSystem, + parameters: &CommitmentSchemeParametersVar<Self>, + trapdoor: &TrapdoorVar<Self>, + asset: &AssetVar<Self>, + ) -> UtxoVar<Self> { + commitment::constraint::CommitmentScheme::<Self::CommitmentScheme>::start( + parameters, trapdoor, + ) + .update(&asset.id) + .update(&asset.value) + .commit(cs) + } + + /// Generates the trapdoor associated to `secret_key` and `public_key` and then uses it to + /// generate the UTXO associated to `asset`. + #[inline] + fn full_utxo( + parameters: &CommitmentSchemeParameters<Self>, + secret_key: &SecretKey<Self>, + public_key: &PublicKey<Self>, + asset: &Asset, + ) -> Utxo<Self> { + Self::utxo(parameters, &Self::trapdoor(secret_key, public_key), asset) + } + + /// Generates the trapdoor associated to `secret_key` and `public_key` and then uses it to + /// generate the UTXO associated to `asset`. + #[inline] + fn full_utxo_var( + cs: &mut Self::ConstraintSystem, + parameters: &CommitmentSchemeParametersVar<Self>, + secret_key: &SecretKeyVar<Self>, + public_key: &PublicKeyVar<Self>, + asset: &AssetVar<Self>, + ) -> UtxoVar<Self> { + let trapdoor = Self::trapdoor_var(cs, secret_key, public_key); + Self::utxo_var(cs, parameters, &trapdoor, asset) + } + + /// Generates the void number associated to `trapdoor` and `secret_key` using `parameters`. + #[inline] + fn void_number( + parameters: &CommitmentSchemeParameters<Self>, + trapdoor: &Trapdoor<Self>, + secret_key: &SecretKey<Self>, + ) -> VoidNumber<Self> { + Self::CommitmentScheme::start(parameters, trapdoor) + .update(secret_key) + .commit() + } + + /// Generates the void number associated to `trapdoor` and `secret_key` using `parameters`. + #[inline] + fn void_number_var( + cs: &mut Self::ConstraintSystem, + parameters: &CommitmentSchemeParametersVar<Self>, + trapdoor: &TrapdoorVar<Self>, + secret_key: &SecretKeyVar<Self>, + ) -> VoidNumberVar<Self> { + commitment::constraint::CommitmentScheme::<Self::CommitmentScheme>::start( + parameters, trapdoor, + ) + .update(secret_key) + .commit(cs) + } } /// Asset Variable Type @@ -243,47 +355,53 @@ pub type AssetVar<C> = Asset<<C as Configuration>::AssetIdVar, <C as Configurati pub type SecretKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::SecretKey; /// Secret Key Variable Type -pub type SecretKeyVar<C> = - <<C as Configuration>::ConstraintSystem as key::constraint::KeyAgreementScheme< - <C as Configuration>::KeyAgreementScheme, - >>::SecretKey; +pub type SecretKeyVar<C> = <C as Configuration>::SecretKeyVar; /// Public Key Type pub type PublicKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::PublicKey; /// Public Key Variable Type -pub type PublicKeyVar<C> = - <<C as Configuration>::ConstraintSystem as key::constraint::KeyAgreementScheme< - <C as Configuration>::KeyAgreementScheme, - >>::PublicKey; +pub type PublicKeyVar<C> = <C as Configuration>::PublicKeyVar; /// Shared Secret Type pub type SharedSecret<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret; -/* /// Shared Secret Variable Type pub type SharedSecretVar<C> = - <<C as Configuration>::KeyAgreementSchemeVar as KeyAgreementScheme>::SharedSecret; -*/ + <<C as Configuration>::ConstraintSystem as key::constraint::KeyAgreementScheme< + <C as Configuration>::KeyAgreementScheme, + >>::SharedSecret; + +/// Ephemeral Key Commitment Scheme Parameters Type +pub type EphemeralKeyParameters<C> = + <<C as Configuration>::EphemeralKeyCommitmentScheme as CommitmentScheme>::Parameters; + +/// Ephemeral Key Commitment Scheme Parameters Variable Type +pub type EphemeralKeyParametersVar<C> = <C as Configuration>::EphemeralKeyParametersVar; /// Ephemeral Key Trapdoor Type pub type EphemeralKeyTrapdoor<C> = <<C as Configuration>::EphemeralKeyCommitmentScheme as CommitmentScheme>::Trapdoor; /// Ephemeral Key Trapdoor Variable Type -pub type EphemeralKeyTrapdoorVar<C> = - <<C as Configuration>::ConstraintSystem as commitment::constraint::CommitmentScheme< - <C as Configuration>::EphemeralKeyCommitmentScheme, - >>::Trapdoor; +pub type EphemeralKeyTrapdoorVar<C> = <C as Configuration>::EphemeralKeyTrapdoorVar; /// Trapdoor Type pub type Trapdoor<C> = <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Trapdoor; -/* /// Trapdoor Variable Type -pub type TrapdoorVar<C> = <<C as Configuration>::CommitmentSchemeVar as CommitmentScheme>::Trapdoor; -*/ +pub type TrapdoorVar<C> = + <<C as Configuration>::ConstraintSystem as commitment::constraint::CommitmentScheme< + <C as Configuration>::CommitmentScheme, + >>::Trapdoor; + +/// Commitment Scheme Parameters Type +pub type CommitmentSchemeParameters<C> = + <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Parameters; + +/// Commitment Scheme Parameters Variable Type +pub type CommitmentSchemeParametersVar<C> = <C as Configuration>::CommitmentSchemeParametersVar; /// Commitment Scheme Output Type pub type CommitmentSchemeOutput<C> = <C as Configuration>::CommitmentSchemeOutput; @@ -297,6 +415,15 @@ pub type Utxo<C> = CommitmentSchemeOutput<C>; /// Unspent Transaction Output Variable Type pub type UtxoVar<C> = CommitmentSchemeOutputVar<C>; +/// UTXO Set Accumulator Verifier Parameters Type +pub type UtxoSetParameters<C> = <<C as Configuration>::UtxoSetVerifier as Verifier>::Parameters; + +/// UTXO Set Accumulator Verifier Parameters Variable Type +pub type UtxoSetParametersVar<C> = <C as Configuration>::UtxoSetParametersVar; + +/// UTXO Set Witness Type +pub type UtxoSetWitness<C> = <<C as Configuration>::UtxoSetVerifier as Verifier>::Witness; + /// UTXO Set Output Type pub type UtxoSetOutput<C> = <<C as Configuration>::UtxoSetVerifier as Verifier>::Output; @@ -345,23 +472,51 @@ where C: Configuration, { /// Ephemeral Key Commitment Scheme Parameters - pub ephemeral_key_commitment_scheme: - <C::EphemeralKeyCommitmentScheme as CommitmentScheme>::Parameters, + pub ephemeral_key_commitment_scheme: EphemeralKeyParameters<C>, /// Commitment Scheme Parameters - pub commitment_scheme: <C::CommitmentScheme as CommitmentScheme>::Parameters, + pub commitment_scheme: CommitmentSchemeParameters<C>, /// UTXO Set Verifier Parameters - pub utxo_set_verifier: <C::UtxoSetVerifier as Verifier>::Parameters, + pub utxo_set_verifier: UtxoSetParameters<C>, } -/// Transfer Parameters Variable +/// Transfer Parameters Variables pub struct ParametersVar<C> where C: Configuration, { - // FIXME: ... - __: C, + /// Ephemeral Key Commitment Scheme Parameters + pub ephemeral_key_commitment_scheme: EphemeralKeyParametersVar<C>, + + /// Commitment Scheme Parameters + pub commitment_scheme: CommitmentSchemeParametersVar<C>, + + /// UTXO Set Verifier Parameters + pub utxo_set_verifier: UtxoSetParametersVar<C>, +} + +impl<C> Variable<C::ConstraintSystem> for ParametersVar<C> +where + C: Configuration, +{ + type Type = Parameters<C>; + + type Mode = Public; + + #[inline] + fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + match allocation { + Allocation::Known(this, mode) => Self { + ephemeral_key_commitment_scheme: this + .ephemeral_key_commitment_scheme + .as_known(cs, mode), + commitment_scheme: this.commitment_scheme.as_known(cs, mode), + utxo_set_verifier: this.utxo_set_verifier.as_known(cs, mode), + }, + _ => unreachable!(), + } + } } /// Spending Key @@ -405,26 +560,19 @@ where #[inline] pub fn validate_utxo( &self, - parameters: &<C::CommitmentScheme as CommitmentScheme>::Parameters, + parameters: &CommitmentSchemeParameters<C>, ephemeral_key: &PublicKey<C>, asset: &Asset, utxo: &Utxo<C>, ) -> bool { - &generate_full_utxo::< - C::KeyAgreementScheme, - C::TrapdoorDerivationFunction, - C::CommitmentScheme, - _, - _, - >(parameters, &self.spend, ephemeral_key, asset) - == utxo + &C::full_utxo(parameters, &self.spend, ephemeral_key, asset) == utxo } /// Prepares `self` for spending `asset` with the given `ephemeral_key`. #[inline] pub fn sender( &self, - parameters: &<C::CommitmentScheme as CommitmentScheme>::Parameters, + parameters: &CommitmentSchemeParameters<C>, ephemeral_key: PublicKey<C>, asset: Asset, ) -> PreSender<C> { @@ -433,8 +581,19 @@ where /// Prepares `self` for receiving `asset`. #[inline] - pub fn receiver(&self, asset: Asset) -> PreReceiver<C> { - self.derive().into_receiver(asset) + pub fn receiver( + &self, + ephemeral_key_commitment_scheme_parameters: &EphemeralKeyParameters<C>, + commitment_scheme_parameters: &CommitmentSchemeParameters<C>, + ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, + asset: Asset, + ) -> Receiver<C> { + self.derive().into_receiver( + ephemeral_key_commitment_scheme_parameters, + commitment_scheme_parameters, + ephemeral_key_trapdoor, + asset, + ) } } @@ -456,8 +615,21 @@ where { /// Prepares `self` for receiving `asset`. #[inline] - pub fn into_receiver(self, asset: Asset) -> PreReceiver<C> { - PreReceiver::new(self.spend, self.view, asset) + pub fn into_receiver( + self, + ephemeral_key_commitment_scheme_parameters: &EphemeralKeyParameters<C>, + commitment_scheme_parameters: &CommitmentSchemeParameters<C>, + ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, + asset: Asset, + ) -> Receiver<C> { + Receiver::new( + ephemeral_key_commitment_scheme_parameters, + commitment_scheme_parameters, + ephemeral_key_trapdoor, + self.spend, + self.view, + asset, + ) } } @@ -470,7 +642,7 @@ where spend: SecretKey<C>, /// Ephemeral Public Spend Key - ephemeral_key: PublicKey<C>, + ephemeral_public_key: PublicKey<C>, /// Asset asset: Asset, @@ -486,25 +658,20 @@ impl<C> PreSender<C> where C: Configuration, { - /// Builds a new [`PreSender`] for `spend` to spend `asset` with `ephemeral_key`. + /// Builds a new [`PreSender`] for `spend` to spend `asset` with `ephemeral_public_key`. #[inline] pub fn new( - parameters: &<C::CommitmentScheme as CommitmentScheme>::Parameters, + parameters: &CommitmentSchemeParameters<C>, spend: SecretKey<C>, - ephemeral_key: PublicKey<C>, + ephemeral_public_key: PublicKey<C>, asset: Asset, ) -> Self { - let trapdoor = generate_trapdoor::<C::KeyAgreementScheme, C::TrapdoorDerivationFunction>( - &spend, - &ephemeral_key, - ); + let trapdoor = C::trapdoor(&spend, &ephemeral_public_key); Self { - utxo: generate_utxo::<C::CommitmentScheme, _, _>(parameters, &trapdoor, &asset), - void_number: generate_void_number::<C::CommitmentScheme, _>( - parameters, &trapdoor, &spend, - ), + utxo: C::utxo(parameters, &trapdoor, &asset), + void_number: C::void_number(parameters, &trapdoor, &spend), spend, - ephemeral_key, + ephemeral_public_key, asset, } } @@ -542,7 +709,7 @@ where pub fn upgrade(self, proof: SenderProof<C>) -> Sender<C> { Sender { spend: self.spend, - ephemeral_key: self.ephemeral_key, + ephemeral_public_key: self.ephemeral_public_key, asset: self.asset, utxo: self.utxo, utxo_membership_proof: proof.utxo_membership_proof, @@ -607,7 +774,7 @@ where spend: SecretKey<C>, /// Ephemeral Public Spend Key - ephemeral_key: PublicKey<C>, + ephemeral_public_key: PublicKey<C>, /// Asset asset: Asset, @@ -634,7 +801,7 @@ where pub fn downgrade(self) -> PreSender<C> { PreSender { spend: self.spend, - ephemeral_key: self.ephemeral_key, + ephemeral_public_key: self.ephemeral_public_key, asset: self.asset, utxo: self.utxo, void_number: self.void_number, @@ -660,7 +827,7 @@ where spend: SecretKeyVar<C>, /// Ephemeral Public Spend Key - ephemeral_key: PublicKeyVar<C>, + ephemeral_public_key: PublicKeyVar<C>, /// Asset asset: AssetVar<C>, @@ -684,22 +851,16 @@ where parameters: &ParametersVar<C>, cs: &mut C::ConstraintSystem, ) -> AssetVar<C> { - /* TODO: - let trapdoor = generate_trapdoor::< - C::KeyAgreementSchemeVar, - C::TrapdoorDerivationFunctionVar, - >(&self.spend, &self.ephemeral_key); - cs.assert(self.utxo_membership_proof.verify( - &generate_utxo(commitment_scheme, &self.asset, &trapdoor), - utxo_set_verifier, - )); - cs.assert_eq( - &self.void_number, - &generate_void_number(commitment_scheme, &self.spend, &trapdoor), - ); + let trapdoor = C::trapdoor_var(cs, &self.spend, &self.ephemeral_public_key); + let utxo = C::utxo_var(cs, &parameters.commitment_scheme, &trapdoor, &self.asset); + let is_valid_proof = + self.utxo_membership_proof + .verify(&parameters.utxo_set_verifier, &utxo, cs); + cs.assert(is_valid_proof); + let void_number = + C::void_number_var(cs, &parameters.commitment_scheme, &trapdoor, &self.spend); + cs.assert_eq(&self.void_number, &void_number); self.asset - */ - todo!() } } @@ -713,25 +874,22 @@ where #[inline] fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - /* TODO: match allocation { Allocation::Known(this, mode) => Self { spend: this.spend.as_known(cs, mode), - ephemeral_key: this.ephemeral_key.as_known(cs, mode), + ephemeral_public_key: this.ephemeral_public_key.as_known(cs, mode), asset: this.asset.as_known(cs, mode), utxo_membership_proof: this.utxo_membership_proof.as_known(cs, mode), void_number: this.void_number.as_known(cs, Public), }, Allocation::Unknown(mode) => Self { - spend: C::SecretKeyVar::new_unknown(cs, mode), - ephemeral_key: C::PublicKeyVar::new_unknown(cs, mode), + spend: SecretKeyVar::<C>::new_unknown(cs, mode), + ephemeral_public_key: PublicKeyVar::<C>::new_unknown(cs, mode), asset: AssetVar::<C>::new_unknown(cs, mode), utxo_membership_proof: UtxoMembershipProofVar::<C>::new_unknown(cs, mode), void_number: VoidNumberVar::<C>::new_unknown(cs, Public), }, } - */ - todo!() } } @@ -877,103 +1035,66 @@ where } } -/// Pre-Receiver -pub struct PreReceiver<C> -where - C: Configuration, -{ - /// Public Spend Key - spend: PublicKey<C>, - - /// Public View Key - view: PublicKey<C>, - - /// Asset - asset: Asset, -} - -impl<C> PreReceiver<C> -where - C: Configuration, -{ - /// Builds a new [`PreReceiver`] for `spend` to receive `asset`, encrypted with `view`. - #[inline] - pub fn new(spend: PublicKey<C>, view: PublicKey<C>, asset: Asset) -> Self { - Self { spend, view, asset } - } - - /// Upgrades `self` into a [`Receiver`] with the designated `ephemeral_key`. - #[inline] - pub fn upgrade( - self, - parameters: &<C::CommitmentScheme as CommitmentScheme>::Parameters, - ephemeral_key: SecretKey<C>, - ) -> Receiver<C> { - Receiver::new(parameters, self.spend, self.view, ephemeral_key, self.asset) - } -} - /// Receiver pub struct Receiver<C> where C: Configuration, { + /// Ephemeral Secret Spend Key Trapdoor + ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, + /// Public Spend Key spend: PublicKey<C>, - /// Public View Key - view: PublicKey<C>, - - /// Ephemeral Secret Spend Key - ephemeral_key: SecretKey<C>, - /// Asset asset: Asset, /// Unspent Transaction Output utxo: Utxo<C>, + + /// Encrypted Asset Note + note: EncryptedNote<C>, } impl<C> Receiver<C> where C: Configuration, { - /// Builds a new [`Receiver`] for `spend` to receive `asset` with `ephemeral_key`. + /// Builds a new [`Receiver`] for `spend` to receive `asset` with `ephemeral_secret_key`. #[inline] pub fn new( - parameters: &<C::CommitmentScheme as CommitmentScheme>::Parameters, + ephemeral_key_commitment_scheme_parameters: &EphemeralKeyParameters<C>, + commitment_scheme_parameters: &CommitmentSchemeParameters<C>, + ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, spend: PublicKey<C>, view: PublicKey<C>, - ephemeral_key: SecretKey<C>, asset: Asset, ) -> Self { + let ephemeral_secret_key = C::ephemeral_secret_key( + ephemeral_key_commitment_scheme_parameters, + &ephemeral_key_trapdoor, + asset, + ); Self { - utxo: generate_full_utxo::< - C::KeyAgreementScheme, - C::TrapdoorDerivationFunction, - C::CommitmentScheme, - _, - _, - >(parameters, &ephemeral_key, &spend, &asset), + utxo: C::full_utxo( + commitment_scheme_parameters, + &ephemeral_secret_key, + &spend, + &asset, + ), + note: EncryptedMessage::new(&view, &ephemeral_secret_key, asset), + ephemeral_key_trapdoor, spend, - view, - ephemeral_key, asset, } } - /// Converts `self` into its [`PreReceiver`], dropping the ephemeral key. - #[inline] - pub fn downgrade(self) -> PreReceiver<C> { - PreReceiver::new(self.spend, self.view, self.asset) - } - /// Extracts the ledger posting data from `self`. #[inline] pub fn into_post(self) -> ReceiverPost<C> { ReceiverPost { utxo: self.utxo, - note: EncryptedMessage::new(&self.view, self.ephemeral_key, self.asset), + note: self.note, } } } @@ -983,6 +1104,12 @@ pub struct ReceiverVar<C> where C: Configuration, { + /// Ephemeral Secret Spend Key Trapdoor + ephemeral_key_trapdoor: EphemeralKeyTrapdoorVar<C>, + + /// Ephemeral Public Spend Key + ephemeral_public_key: PublicKeyVar<C>, + /// Public Spend Key spend: PublicKeyVar<C>, @@ -1003,33 +1130,25 @@ where pub fn get_well_formed_asset( self, parameters: &ParametersVar<C>, - index: C::ByteVar, - ledger_checkpoint: &C::LedgerCheckpointVar, - ephemeral_key_trapdoor: &EphemeralKeyTrapdoorVar<C>, cs: &mut C::ConstraintSystem, ) -> AssetVar<C> { - /* TODO: - let ephemeral_key = generate_ephemeral_secret_key( - ephemeral_key_commitment_scheme, - ledger_checkpoint, - &index, - &self.spend, - ephemeral_key_trapdoor, + let ephemeral_secret_key = C::ephemeral_secret_key_var( + cs, + &parameters.ephemeral_key_commitment_scheme, + &self.ephemeral_key_trapdoor, + &self.asset, ); - cs.assert_eq( - &self.utxo, - &generate_utxo( - commitment_scheme, - &self.asset, - &generate_trapdoor::<C::KeyAgreementSchemeVar, C::TrapdoorDerivationFunctionVar>( - &ephemeral_key, - &self.spend, - ), - ), + let ephemeral_public_key = C::ephemeral_public_key_var(cs, &ephemeral_secret_key); + cs.assert_eq(&self.ephemeral_public_key, &ephemeral_public_key); + let utxo = C::full_utxo_var( + cs, + &parameters.commitment_scheme, + &ephemeral_secret_key, + &self.spend, + &self.asset, ); + cs.assert_eq(&self.utxo, &utxo); self.asset - */ - todo!() } } @@ -1043,21 +1162,22 @@ where #[inline] fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - /* match allocation { Allocation::Known(this, mode) => Self { + ephemeral_key_trapdoor: this.ephemeral_key_trapdoor.as_known(cs, mode), + ephemeral_public_key: this.note.ephemeral_public_key().as_known(cs, mode), spend: this.spend.as_known(cs, mode), asset: this.asset.as_known(cs, mode), utxo: this.utxo.as_known(cs, Public), }, Allocation::Unknown(mode) => Self { - spend: C::PublicKeyVar::new_unknown(cs, mode), + ephemeral_key_trapdoor: EphemeralKeyTrapdoorVar::<C>::new_unknown(cs, mode), + ephemeral_public_key: PublicKeyVar::<C>::new_unknown(cs, mode), + spend: PublicKeyVar::<C>::new_unknown(cs, mode), asset: AssetVar::<C>::new_unknown(cs, mode), utxo: UtxoVar::<C>::new_unknown(cs, Public), }, } - */ - todo!() } } @@ -1136,6 +1256,7 @@ where // TODO: Add a "public part" trait that extracts the public part of `Receiver` (using // `ReceiverVar` to determine the types), then generate this method automatically. C::ProofSystem::extend(input, &self.utxo); + C::ProofSystem::extend(input, self.ephemeral_key()); } /// Validates `self` on the receiver `ledger`. @@ -1198,7 +1319,7 @@ pub struct Transfer< senders: [Sender<C>; SENDERS], /// Receivers - receivers: [PreReceiver<C>; RECEIVERS], + receivers: [Receiver<C>; RECEIVERS], /// Sinks sinks: [AssetValue; SINKS], @@ -1215,7 +1336,7 @@ where asset_id: Option<AssetId>, sources: [AssetValue; SOURCES], senders: [Sender<C>; SENDERS], - receivers: [PreReceiver<C>; RECEIVERS], + receivers: [Receiver<C>; RECEIVERS], sinks: [AssetValue; SINKS], ) -> Self { Self::check_shape(asset_id.is_some()); @@ -1273,7 +1394,7 @@ where asset_id: Option<AssetId>, sources: [AssetValue; SOURCES], senders: [Sender<C>; SENDERS], - receivers: [PreReceiver<C>; RECEIVERS], + receivers: [Receiver<C>; RECEIVERS], sinks: [AssetValue; SINKS], ) -> Self { Self { @@ -1285,26 +1406,18 @@ where } } - /* /// Generates a proving and verifying context for this transfer shape. #[inline] pub fn generate_context<R>( - ephemeral_key_commitment_scheme: &C::EphemeralKeyCommitmentScheme, - commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &C::UtxoSetVerifier, + parameters: &Parameters<C>, rng: &mut R, ) -> Result<(ProvingContext<C>, VerifyingContext<C>), ProofSystemError<C>> where R: CryptoRng + RngCore + ?Sized, { let mut cs = C::ProofSystem::for_unknown(); - FullTransferVar::<C, SOURCES, SENDERS, RECEIVERS, SINKS>::new_unknown(&mut cs, Derived) - .build_validity_constraints( - &ephemeral_key_commitment_scheme.as_known(&mut cs, Public), - &commitment_scheme.as_known(&mut cs, Public), - &utxo_set_verifier.as_known(&mut cs, Public), - &mut cs, - ); + TransferVar::<C, SOURCES, SENDERS, RECEIVERS, SINKS>::new_unknown(&mut cs, Derived) + .build_validity_constraints(&parameters.as_known(&mut cs, Public), &mut cs); cs.generate_context::<C::ProofSystem, _>(rng) } @@ -1312,94 +1425,7 @@ where #[inline] pub fn into_post<R>( self, - ephemeral_key_commitment_scheme: &C::EphemeralKeyCommitmentScheme, - commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &C::UtxoSetVerifier, - ledger_checkpoint: C::LedgerCheckpoint, - context: &ProvingContext<C>, - rng: &mut R, - ) -> Result<TransferPost<C>, ProofSystemError<C>> - where - R: CryptoRng + RngCore + ?Sized, - { - let ephemeral_key_trapdoor = rng.gen(); - FullTransfer::<_, SOURCES, SENDERS, RECEIVERS, SINKS> { - asset_id: self.asset_id, - sources: self.sources, - senders: self.senders, - receivers: IntoIterator::into_iter(self.receivers) - .enumerate() - .map(|(i, r)| { - let ephemeral_key = generate_ephemeral_secret_key( - ephemeral_key_commitment_scheme, - &ledger_checkpoint, - &(i as u8), - &r.spend, - &ephemeral_key_trapdoor, - ); - r.upgrade(ephemeral_key, commitment_scheme) - }) - .collect::<Vec<_>>(), - sinks: self.sinks, - ephemeral_key_trapdoor, - ledger_checkpoint, - } - .into_post( - ephemeral_key_commitment_scheme, - commitment_scheme, - utxo_set_verifier, - context, - rng, - ) - } - */ -} - -/// Full Transfer -struct FullTransfer< - C, - const SOURCES: usize, - const SENDERS: usize, - const RECEIVERS: usize, - const SINKS: usize, -> where - C: Configuration, -{ - /// Asset Id - asset_id: Option<AssetId>, - - /// Sources - sources: [AssetValue; SOURCES], - - /// Senders - senders: [Sender<C>; SENDERS], - - /// Receivers - receivers: Vec<Receiver<C>>, - - /// Sinks - sinks: [AssetValue; SINKS], - - /// Ephemeral Key Trapdoor - ephemeral_key_trapdoor: C::EphemeralKeyTrapdoor, - - /// Ledger Checkpoint - ledger_checkpoint: C::LedgerCheckpoint, -} - -impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> - FullTransfer<C, SOURCES, SENDERS, RECEIVERS, SINKS> -where - C: Configuration, -{ - /* - /// Computes the [`TransferPost`] for `self`. - #[inline] - fn into_post<R>( - self, - ephemeral_key_commitment_scheme: &C::EphemeralKeyCommitmentScheme, - commitment_scheme: &C::CommitmentScheme, - utxo_set_verifier: &C::UtxoSetVerifier, + parameters: &Parameters<C>, context: &ProvingContext<C>, rng: &mut R, ) -> Result<TransferPost<C>, ProofSystemError<C>> @@ -1409,14 +1435,9 @@ where Ok(TransferPost { validity_proof: { let mut cs = C::ProofSystem::for_known(); - let transfer: FullTransferVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> = + let transfer: TransferVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> = self.as_known(&mut cs, Derived); - transfer.build_validity_constraints( - &ephemeral_key_commitment_scheme.as_known(&mut cs, Public), - &commitment_scheme.as_known(&mut cs, Public), - &utxo_set_verifier.as_known(&mut cs, Public), - &mut cs, - ); + transfer.build_validity_constraints(&parameters.as_known(&mut cs, Public), &mut cs); cs.prove::<C::ProofSystem, _>(context, rng)? }, asset_id: self.asset_id, @@ -1424,20 +1445,16 @@ where sender_posts: IntoIterator::into_iter(self.senders) .map(Sender::into_post) .collect(), - receiver_posts: self - .receivers - .into_iter() + receiver_posts: IntoIterator::into_iter(self.receivers) .map(Receiver::into_post) .collect(), sinks: self.sinks.into(), - ledger_checkpoint: self.ledger_checkpoint, }) } - */ } -/// Full Transfer Variable -struct FullTransferVar< +/// Transfer Variable +struct TransferVar< C, const SOURCES: usize, const SENDERS: usize, @@ -1456,31 +1473,22 @@ struct FullTransferVar< senders: Vec<SenderVar<C>>, /// Receivers - receivers: Vec<(C::ByteVar, ReceiverVar<C>)>, + receivers: Vec<ReceiverVar<C>>, /// Sinks sinks: Vec<C::AssetValueVar>, - - /// Ephemeral Key Trapdoor - ephemeral_key_trapdoor: EphemeralKeyTrapdoorVar<C>, - - /// Ledger Checkpoint - ledger_checkpoint: C::LedgerCheckpointVar, } impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> - FullTransferVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> + TransferVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> where C: Configuration, { - /* /// Builds constraints for the [`Transfer`] validity proof. #[inline] fn build_validity_constraints( self, - ephemeral_key_commitment_scheme: &C::EphemeralKeyCommitmentSchemeVar, - commitment_scheme: &C::CommitmentSchemeVar, - utxo_set_verifier: &C::UtxoSetVerifierVar, + parameters: &ParametersVar<C>, cs: &mut C::ConstraintSystem, ) { let mut secret_asset_ids = Vec::with_capacity(SENDERS + RECEIVERS); @@ -1489,7 +1497,7 @@ where .senders .into_iter() .map(|s| { - let asset = s.get_well_formed_asset(commitment_scheme, utxo_set_verifier, cs); + let asset = s.get_well_formed_asset(parameters, cs); secret_asset_ids.push(asset.id); asset.value }) @@ -1497,21 +1505,11 @@ where .reduce(Add::add) .unwrap(); - let ledger_checkpoint = &self.ledger_checkpoint; - let ephemeral_key_trapdoor = &self.ephemeral_key_trapdoor; - let output_sum = self .receivers .into_iter() - .map(|(index, r)| { - let asset = r.get_well_formed_asset( - index, - ledger_checkpoint, - ephemeral_key_trapdoor, - ephemeral_key_commitment_scheme, - commitment_scheme, - cs, - ); + .map(|r| { + let asset = r.get_well_formed_asset(parameters, cs); secret_asset_ids.push(asset.id); asset.value }) @@ -1526,21 +1524,19 @@ where _ => cs.assert_all_eq(secret_asset_ids.iter()), } } - */ } impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> - Variable<C::ConstraintSystem> for FullTransferVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> + Variable<C::ConstraintSystem> for TransferVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> where C: Configuration, { - type Type = FullTransfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>; + type Type = Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>; type Mode = Derived; #[inline] fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - /* match allocation { Allocation::Known(this, mode) => Self { asset_id: this.asset_id.map(|id| id.as_known(cs, Public)), @@ -1557,18 +1553,13 @@ where receivers: this .receivers .iter() - .enumerate() - .map(|(i, receiver)| { - ((i as u8).as_known(cs, mode), receiver.as_known(cs, mode)) - }) + .map(|receiver| receiver.as_known(cs, mode)) .collect(), sinks: this .sinks .iter() .map(|sink| sink.as_known(cs, Public)) .collect(), - ephemeral_key_trapdoor: this.ephemeral_key_trapdoor.as_known(cs, mode), - ledger_checkpoint: this.ledger_checkpoint.as_known(cs, mode), }, Allocation::Unknown(mode) => Self { asset_id: has_public_participants(SOURCES, SENDERS, RECEIVERS, SINKS) @@ -1583,23 +1574,14 @@ where .collect(), receivers: (0..RECEIVERS) .into_iter() - .map(|_| { - ( - C::ByteVar::new_unknown(cs, mode), - ReceiverVar::<C>::new_unknown(cs, mode), - ) - }) + .map(|_| ReceiverVar::<C>::new_unknown(cs, mode)) .collect(), sinks: (0..SINKS) .into_iter() .map(|_| C::AssetValueVar::new_unknown(cs, Public)) .collect(), - ephemeral_key_trapdoor: C::EphemeralKeyTrapdoorVar::new_unknown(cs, mode), - ledger_checkpoint: C::LedgerCheckpointVar::new_unknown(cs, mode), }, } - */ - todo!() } } @@ -1649,7 +1631,6 @@ where senders: &[SenderPostingKey<C, Self>], receivers: &[ReceiverPostingKey<C, Self>], sinks: &[AssetValue], - ledger_checkpoint: &C::LedgerCheckpoint, proof: Proof<C>, ) -> Option<Self::ValidProof>; @@ -1707,6 +1688,12 @@ pub enum TransferPostError { /// Receiver Post Error Receiver(ReceiverPostError), + /// Duplicate Spend Error + DuplicateSpend, + + /// Duplicate Register Error + DuplicateRegister, + /// Invalid Transfer Proof Error /// /// Validity of the transfer could not be proved by the ledger. @@ -1736,9 +1723,6 @@ where /// Sinks sinks: Vec<AssetValue>, - /// Ledger Checkpoint - ledger_checkpoint: C::LedgerCheckpoint, - /// Validity Proof validity_proof: Proof<C>, } @@ -1766,7 +1750,6 @@ where self.sinks .iter() .for_each(|sink| C::ProofSystem::extend(&mut input, sink)); - C::ProofSystem::extend(&mut input, &self.ledger_checkpoint); input } @@ -1786,7 +1769,17 @@ where .skip(i + 1) .any(move |q| p.void_number == q.void_number) { - return Err(SenderPostError::AssetSpent.into()); + return Err(TransferPostError::DuplicateSpend); + } + } + for (i, p) in self.receiver_posts.iter().enumerate() { + if self + .receiver_posts + .iter() + .skip(i + 1) + .any(move |q| p.utxo == q.utxo) + { + return Err(TransferPostError::DuplicateRegister); } } let sender_posting_keys = self @@ -1806,7 +1799,6 @@ where &sender_posting_keys, &receiver_posting_keys, &self.sinks, - &self.ledger_checkpoint, self.validity_proof, ) { Some(key) => key, @@ -1957,7 +1949,7 @@ pub mod canonical { { /// Builds a [`Mint`] from `asset` and `receiver`. #[inline] - pub fn build(asset: Asset, receiver: PreReceiver<C>) -> Self { + pub fn build(asset: Asset, receiver: Receiver<C>) -> Self { Self::new( Some(asset.id), [asset.value], @@ -1989,7 +1981,7 @@ pub mod canonical { #[inline] pub fn build( senders: [Sender<C>; PrivateTransferShape::SENDERS], - receivers: [PreReceiver<C>; PrivateTransferShape::RECEIVERS], + receivers: [Receiver<C>; PrivateTransferShape::RECEIVERS], ) -> Self { Self::new( Default::default(), @@ -2031,7 +2023,7 @@ pub mod canonical { #[inline] pub fn build( senders: [Sender<C>; ReclaimShape::SENDERS], - receivers: [PreReceiver<C>; ReclaimShape::RECEIVERS], + receivers: [Receiver<C>; ReclaimShape::RECEIVERS], reclaim: Asset, ) -> Self { Self::new( diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index 8e496b161..d5aaf707f 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -135,6 +135,7 @@ where /// Constraint System Gadgets pub mod constraint { + use super::Input; use crate::constraint::Variable; /// Commitment Scheme Gadget @@ -158,8 +159,85 @@ pub mod constraint { fn commit( &mut self, parameters: &Self::Parameters, - input: &Self::Input, trapdoor: &Self::Trapdoor, + input: &Self::Input, ) -> Self::Output; + + /// Starts a new [`Builder`] for extended commitments. + #[inline] + fn start<'c>( + parameters: &'c Self::Parameters, + trapdoor: &'c Self::Trapdoor, + ) -> Builder<'c, C, Self> + where + Self::Input: Default, + { + Builder::new(parameters, trapdoor) + } + } + + /// Commitment Builder + pub struct Builder<'c, B, C> + where + B: super::CommitmentScheme, + C: CommitmentScheme<B> + ?Sized, + C::Input: Default, + { + /// Commitment Parameters + parameters: &'c C::Parameters, + + /// Commitment Trapdoor + trapdoor: &'c C::Trapdoor, + + /// Commitment Input Accumulator + input: C::Input, + } + + impl<'c, B, C> Builder<'c, B, C> + where + B: super::CommitmentScheme, + C: CommitmentScheme<B> + ?Sized, + C::Input: Default, + { + /// Returns a new [`Builder`] with fixed `parameters` and `trapdoor`. + #[inline] + pub fn new(parameters: &'c C::Parameters, trapdoor: &'c C::Trapdoor) -> Self { + Self { + parameters, + trapdoor, + input: Default::default(), + } + } + + /// Updates the builder with the `next` input. + #[inline] + pub fn update<T>(mut self, next: &T) -> Self + where + T: ?Sized, + C::Input: Input<T>, + { + self.input.extend(next); + self + } + + /// Updates the builder with each item in `iter`. + #[inline] + pub fn update_all<'t, T, I>(mut self, iter: I) -> Self + where + T: 't + ?Sized, + I: IntoIterator<Item = &'t T>, + C::Input: Input<T>, + { + for next in iter { + self.input.extend(next); + } + self + } + + /// Commits to the input stored in the builder. + #[inline] + pub fn commit(self, cs: &mut C) -> C::Output { + cs.commit(self.parameters, self.trapdoor, &self.input) + } } } diff --git a/manta-crypto/src/encryption.rs b/manta-crypto/src/encryption.rs index cf38399a8..59028a76b 100644 --- a/manta-crypto/src/encryption.rs +++ b/manta-crypto/src/encryption.rs @@ -56,7 +56,7 @@ pub trait HybridPublicKeyEncryptionScheme: SymmetricKeyEncryptionScheme { /// Key Derivation Function Type type KeyDerivationFunction: KeyDerivationFunction< - <Self::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret, + Key = <Self::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret, Output = Self::Key, >; @@ -97,7 +97,7 @@ pub struct Hybrid<K, S, F> where K: KeyAgreementScheme, S: SymmetricKeyEncryptionScheme, - F: KeyDerivationFunction<K::SharedSecret, Output = S::Key>, + F: KeyDerivationFunction<Key = K::SharedSecret, Output = S::Key>, { /// Type Parameter Marker __: PhantomData<(K, S, F)>, @@ -107,7 +107,7 @@ impl<K, S, F> SymmetricKeyEncryptionScheme for Hybrid<K, S, F> where K: KeyAgreementScheme, S: SymmetricKeyEncryptionScheme, - F: KeyDerivationFunction<K::SharedSecret, Output = S::Key>, + F: KeyDerivationFunction<Key = K::SharedSecret, Output = S::Key>, { type Key = S::Key; @@ -130,7 +130,7 @@ impl<K, S, F> HybridPublicKeyEncryptionScheme for Hybrid<K, S, F> where K: KeyAgreementScheme, S: SymmetricKeyEncryptionScheme, - F: KeyDerivationFunction<K::SharedSecret, Output = S::Key>, + F: KeyDerivationFunction<Key = K::SharedSecret, Output = S::Key>, { type KeyAgreementScheme = K; type KeyDerivationFunction = F; @@ -157,15 +157,12 @@ where #[inline] pub fn new( public_key: &PublicKey<H>, - ephemeral_secret_key: SecretKey<H>, + ephemeral_secret_key: &SecretKey<H>, plaintext: H::Plaintext, ) -> Self { Self { - ciphertext: H::encrypt( - H::agree_derive(&ephemeral_secret_key, public_key), - plaintext, - ), - ephemeral_public_key: H::KeyAgreementScheme::derive_owned(ephemeral_secret_key), + ciphertext: H::encrypt(H::agree_derive(ephemeral_secret_key, public_key), plaintext), + ephemeral_public_key: H::KeyAgreementScheme::derive(ephemeral_secret_key), } } diff --git a/manta-crypto/src/key.rs b/manta-crypto/src/key.rs index bfe09d345..64ebd4795 100644 --- a/manta-crypto/src/key.rs +++ b/manta-crypto/src/key.rs @@ -17,12 +17,15 @@ //! Cryptographic Key Primitives /// Key Derivation Function -pub trait KeyDerivationFunction<S> { +pub trait KeyDerivationFunction { + /// Input Key Type + type Key; + /// Output Key Type type Output; /// Derives an output key from `secret` computed from a cryptographic agreement scheme. - fn derive(secret: S) -> Self::Output; + fn derive(secret: Self::Key) -> Self::Output; } /// Key Agreement Scheme @@ -93,7 +96,10 @@ pub trait KeyAgreementWithDerivation: KeyAgreementScheme { type Output; /// Key Derivation Function Type - type KeyDerivationFunction: KeyDerivationFunction<Self::SharedSecret, Output = Self::Output>; + type KeyDerivationFunction: KeyDerivationFunction< + Key = Self::SharedSecret, + Output = Self::Output, + >; /// Computes the shared secret given the known `secret_key` and the given `public_key` and then /// uses the key derivation function to derive a final shared secret. @@ -127,6 +133,21 @@ pub trait KeyAgreementWithDerivation: KeyAgreementScheme { pub mod constraint { use crate::constraint::Variable; + /// Key Derivation Function Gadget + pub trait KeyDerivationFunction<F> + where + F: super::KeyDerivationFunction, + { + /// Input Key Type + type Key: Variable<Self, Type = F::Key>; + + /// Output Key Type + type Output: Variable<Self, Type = F::Output>; + + /// Derives an output key from `secret` computed from a cryptographic agreement scheme. + fn derive(&mut self, secret: Self::Key) -> Self::Output; + } + /// Key Agreement Scheme Gadget pub trait KeyAgreementScheme<K> where From 0a6d1fac6038c059b49a1ce72b4c2491fd4bb6d0 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 15 Dec 2021 01:41:34 -0500 Subject: [PATCH 136/275] wip: start moving signer core algorithm to transfer --- manta-accounting/src/transfer.rs | 58 ++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer.rs index 48ba6e0b1..1d1825134 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer.rs @@ -1895,6 +1895,7 @@ pub trait Shape: sealed::Sealed { /// Canonical Transaction Types pub mod canonical { use super::*; + use crate::asset; use manta_util::seal; /// Implements [`Shape`] for a given shape type. @@ -2089,4 +2090,61 @@ pub mod canonical { /// A transaction of this kind will result in a withdraw of `asset`. Withdraw(Asset), } + + /// Transfer Asset Selection + pub struct Selection<C> + where + C: Configuration, + { + /// Change Value + pub change: AssetValue, + + /// Senders + pub senders: Vec<PreSender<C>>, + } + + /// Selection Error + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] + pub enum SelectionError<E> { + /// Insufficient Balance + InsufficientBalance, + + /// Building Error + Error(E), + } + + impl<C> Selection<C> + where + C: Configuration, + { + /// Builds a new [`Selection`] from `change` and `senders`. + #[inline] + fn build(change: AssetValue, senders: Vec<PreSender<C>>) -> Self { + Self { change, senders } + } + + /// Builds a new [`Selection`] by mapping over an asset selection with `builder`. + #[inline] + pub fn new<M, E, F>( + selection: &asset::Selection<M>, + asset_id: AssetId, + mut builder: F, + ) -> Result<Self, SelectionError<E>> + where + M: asset::AssetMap, + F: FnMut(&M::Key, Asset) -> Result<PreSender<C>, E>, + { + if selection.is_empty() { + return Err(SelectionError::InsufficientBalance); + } + Ok(Self::build( + selection.change, + selection + .iter() + .map(move |(k, v)| builder(k, asset_id.with(*v))) + .collect::<Result<_, _>>() + .map_err(SelectionError::Error)?, + )) + } + } } From ca0d6dcd62bc4bdbb43f01b956746eaba292c43d Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 15 Dec 2021 16:44:08 -0500 Subject: [PATCH 137/275] wip: move batch implementation to transfer, modularize --- manta-accounting/src/transfer/batch.rs | 86 +++++ manta-accounting/src/transfer/canonical.rs | 294 ++++++++++++++++++ .../src/{transfer.rs => transfer/mod.rs} | 283 +---------------- manta-accounting/src/wallet/signer.rs | 6 +- 4 files changed, 386 insertions(+), 283 deletions(-) create mode 100644 manta-accounting/src/transfer/batch.rs create mode 100644 manta-accounting/src/transfer/canonical.rs rename manta-accounting/src/{transfer.rs => transfer/mod.rs} (88%) diff --git a/manta-accounting/src/transfer/batch.rs b/manta-accounting/src/transfer/batch.rs new file mode 100644 index 000000000..83e749533 --- /dev/null +++ b/manta-accounting/src/transfer/batch.rs @@ -0,0 +1,86 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Batched Transfers + +use crate::transfer::{Configuration, PreSender, Receiver, Transfer, TransferPost, Utxo}; +use alloc::vec; +use manta_crypto::accumulator::Accumulator; +use manta_util::{ + fallible_array_map, + iter::{ChunkBy, IteratorExt}, + seal, +}; + +/// +pub struct BatchRound<C, const SENDERS: usize, const RECEIVERS: usize> +where + C: Configuration, +{ + /// PreSender Chunk Iterator + pre_senders: ChunkBy<vec::IntoIter<PreSender<C>>, SENDERS>, + + /// + accumulators: Vec<PreSender<C>>, + + /// Final Receiver + receiver: Option<Receiver<C>>, +} + +impl<C, const SENDERS: usize, const RECEIVERS: usize> BatchRound<C, SENDERS, RECEIVERS> +where + C: Configuration, +{ + /// + pub fn next_transfer<S>(&mut self, utxo_set: &mut S) -> Option<TransferPost<C>> + where + S: Accumulator<Item = Utxo<C>, Verifier = C::UtxoSetVerifier>, + { + if let Some(chunk) = self.pre_senders.next() { + let senders = + fallible_array_map(chunk, move |ps| ps.try_upgrade(utxo_set).ok_or(())).ok()?; + + /* + let mut accumulator = self.signer.next_accumulator::<_, _, RECEIVERS>( + parameters, + asset_id, + senders.iter().map(Sender::asset_value).sum(), + &mut self.rng, + )?; + + let post = + self.build_post(Transfer::new(None, [], senders, accumulator.receivers, []))?; + + for zero in &accumulator.zeros { + zero.as_ref().insert_utxo(utxo_set); + } + accumulator.pre_sender.insert_utxo(&mut self.utxo_set); + + new_zeroes.append(&mut accumulator.zeroes); + accumulators.push(accumulator.pre_sender); + */ + + todo!() + } else { + /* + accumulators.append(&mut self.pre_senders.remainder()); + self.pre_senders = accumulators.into_iter().chunk_by::<SENDERS>(); + */ + + todo!() + } + } +} diff --git a/manta-accounting/src/transfer/canonical.rs b/manta-accounting/src/transfer/canonical.rs new file mode 100644 index 000000000..eaacfc0d7 --- /dev/null +++ b/manta-accounting/src/transfer/canonical.rs @@ -0,0 +1,294 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Canonical Transaction Types + +use crate::{ + asset::{self, Asset, AssetId, AssetValue}, + transfer::{Configuration, PreSender, Receiver, ReceivingKey, Sender, Transfer}, +}; +use manta_util::{create_seal, seal}; + +create_seal! {} + +/// Transfer Shapes +/// +/// This trait identifies a transfer shape, i.e. the number and type of participants on the input +/// and output sides of the transaction. This trait is sealed and can only be used with the +/// [existing canonical implementations](canonical). +pub trait Shape: sealed::Sealed { + /// Number of Sources + const SOURCES: usize; + + /// Number of Senders + const SENDERS: usize; + + /// Number of Receivers + const RECEIVERS: usize; + + /// Number of Sinks + const SINKS: usize; +} + +/// Implements [`Shape`] for a given shape type. +macro_rules! impl_shape { + ($shape:tt, $sources:expr, $senders:expr, $receivers:expr, $sinks:expr) => { + seal!($shape); + impl Shape for $shape { + const SOURCES: usize = $sources; + const SENDERS: usize = $senders; + const RECEIVERS: usize = $receivers; + const SINKS: usize = $sinks; + } + }; +} + +/// Builds a new alias using the given shape type. +macro_rules! alias_type { + ($type:tt, $t:ident, $shape:tt) => { + $type< + $t, + { $shape::SOURCES }, + { $shape::SENDERS }, + { $shape::RECEIVERS }, + { $shape::SINKS }, + > + } +} + +/// Builds a new [`Transfer`] alias using the given shape type. +macro_rules! transfer_alias { + ($t:ident, $shape:tt) => { + alias_type!(Transfer, $t, $shape) + }; +} + +/// Mint Transaction Shape +/// +/// ```text +/// <1, 0, 1, 0> +/// ``` +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd)] +pub struct MintShape; + +impl_shape!(MintShape, 1, 0, 1, 0); + +/// Mint Transaction +pub type Mint<C> = transfer_alias!(C, MintShape); + +impl<C> Mint<C> +where + C: Configuration, +{ + /// Builds a [`Mint`] from `asset` and `receiver`. + #[inline] + pub fn build(asset: Asset, receiver: Receiver<C>) -> Self { + Self::new( + Some(asset.id), + [asset.value], + Default::default(), + [receiver], + Default::default(), + ) + } +} + +/// Private Transfer Transaction Shape +/// +/// ```text +/// <0, 2, 2, 0> +/// ``` +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd)] +pub struct PrivateTransferShape; + +impl_shape!(PrivateTransferShape, 0, 2, 2, 0); + +/// Private Transfer Transaction +pub type PrivateTransfer<C> = transfer_alias!(C, PrivateTransferShape); + +impl<C> PrivateTransfer<C> +where + C: Configuration, +{ + /// Builds a [`PrivateTransfer`] from `senders` and `receivers`. + #[inline] + pub fn build( + senders: [Sender<C>; PrivateTransferShape::SENDERS], + receivers: [Receiver<C>; PrivateTransferShape::RECEIVERS], + ) -> Self { + Self::new( + Default::default(), + Default::default(), + senders, + receivers, + Default::default(), + ) + } +} + +/// Reclaim Transaction Shape +/// +/// ```text +/// <0, 2, 1, 1> +/// ``` +/// +/// The [`ReclaimShape`] is defined in terms of the [`PrivateTransferShape`]. It is defined to +/// have the same number of senders and one secret receiver turned into a public sink. +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd)] +pub struct ReclaimShape; + +impl_shape!( + ReclaimShape, + 0, + PrivateTransferShape::SENDERS, + PrivateTransferShape::RECEIVERS - 1, + 1 +); + +/// Reclaim Transaction +pub type Reclaim<C> = transfer_alias!(C, ReclaimShape); + +impl<C> Reclaim<C> +where + C: Configuration, +{ + /// Builds a [`Reclaim`] from `senders`, `receivers`, and `reclaim`. + #[inline] + pub fn build( + senders: [Sender<C>; ReclaimShape::SENDERS], + receivers: [Receiver<C>; ReclaimShape::RECEIVERS], + reclaim: Asset, + ) -> Self { + Self::new( + Some(reclaim.id), + Default::default(), + senders, + receivers, + [reclaim.value], + ) + } +} + +/// Canonical Transaction Type +pub enum Transaction<C> +where + C: Configuration, +{ + /// Mint Private Asset + Mint(Asset), + + /// Private Transfer Asset to Receiver + PrivateTransfer(Asset, ReceivingKey<C>), + + /// Reclaim Private Asset + Reclaim(Asset), +} + +impl<C> Transaction<C> +where + C: Configuration, +{ + /// Checks that `self` can be executed for a given `balance` state, returning the + /// transaction kind if successful, and returning the asset back if the balance was + /// insufficient. + #[inline] + pub fn check<F>(&self, balance: F) -> Result<TransactionKind, Asset> + where + F: FnOnce(Asset) -> bool, + { + match self { + Self::Mint(asset) => Ok(TransactionKind::Deposit(*asset)), + Self::PrivateTransfer(asset, _) | Self::Reclaim(asset) => { + if balance(*asset) { + Ok(TransactionKind::Withdraw(*asset)) + } else { + Err(*asset) + } + } + } + } +} + +/// Transaction Kind +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum TransactionKind { + /// Deposit Transaction + /// + /// A transaction of this kind will result in a deposit of `asset`. + Deposit(Asset), + + /// Withdraw Transaction + /// + /// A transaction of this kind will result in a withdraw of `asset`. + Withdraw(Asset), +} + +/// Transfer Asset Selection +pub struct Selection<C> +where + C: Configuration, +{ + /// Change Value + pub change: AssetValue, + + /// Senders + pub senders: Vec<PreSender<C>>, +} + +/// Selection Error +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum SelectionError<E> { + /// Insufficient Balance + InsufficientBalance, + + /// Building Error + Error(E), +} + +impl<C> Selection<C> +where + C: Configuration, +{ + /// Builds a new [`Selection`] from `change` and `senders`. + #[inline] + fn build(change: AssetValue, senders: Vec<PreSender<C>>) -> Self { + Self { change, senders } + } + + /// Builds a new [`Selection`] by mapping over an asset selection with `builder`. + #[inline] + pub fn new<M, E, F>( + selection: &asset::Selection<M>, + asset_id: AssetId, + mut builder: F, + ) -> Result<Self, SelectionError<E>> + where + M: asset::AssetMap, + F: FnMut(&M::Key, Asset) -> Result<PreSender<C>, E>, + { + if selection.is_empty() { + return Err(SelectionError::InsufficientBalance); + } + Ok(Self::build( + selection.change, + selection + .iter() + .map(move |(k, v)| builder(k, asset_id.with(*v))) + .collect::<Result<_, _>>() + .map_err(SelectionError::Error)?, + )) + } +} diff --git a/manta-accounting/src/transfer.rs b/manta-accounting/src/transfer/mod.rs similarity index 88% rename from manta-accounting/src/transfer.rs rename to manta-accounting/src/transfer/mod.rs index 1d1825134..a108a6f31 100644 --- a/manta-accounting/src/transfer.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -30,7 +30,10 @@ use manta_crypto::{ key::{self, KeyAgreementScheme, KeyDerivationFunction}, rand::{CryptoRng, RngCore, Sample}, }; -use manta_util::{create_seal, from_variant_impl}; +use manta_util::from_variant_impl; + +pub mod batch; +pub mod canonical; /// Returns `true` if the transfer with this shape would have public participants. #[inline] @@ -1870,281 +1873,3 @@ where } } } - -create_seal! {} - -/// Transfer Shapes -/// -/// This trait identifies a transfer shape, i.e. the number and type of participants on the input -/// and output sides of the transaction. This trait is sealed and can only be used with the -/// [existing canonical implementations](canonical). -pub trait Shape: sealed::Sealed { - /// Number of Sources - const SOURCES: usize; - - /// Number of Senders - const SENDERS: usize; - - /// Number of Receivers - const RECEIVERS: usize; - - /// Number of Sinks - const SINKS: usize; -} - -/// Canonical Transaction Types -pub mod canonical { - use super::*; - use crate::asset; - use manta_util::seal; - - /// Implements [`Shape`] for a given shape type. - macro_rules! impl_shape { - ($shape:tt, $sources:expr, $senders:expr, $receivers:expr, $sinks:expr) => { - seal!($shape); - impl Shape for $shape { - const SOURCES: usize = $sources; - const SENDERS: usize = $senders; - const RECEIVERS: usize = $receivers; - const SINKS: usize = $sinks; - } - }; - } - - /// Builds a new alias using the given shape type. - macro_rules! alias_type { - ($type:tt, $t:ident, $shape:tt) => { - $type< - $t, - { $shape::SOURCES }, - { $shape::SENDERS }, - { $shape::RECEIVERS }, - { $shape::SINKS }, - > - } - } - - /// Builds a new [`Transfer`] alias using the given shape type. - macro_rules! transfer_alias { - ($t:ident, $shape:tt) => { - alias_type!(Transfer, $t, $shape) - }; - } - - /// Mint Transaction Shape - /// - /// ```text - /// <1, 0, 1, 0> - /// ``` - #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd)] - pub struct MintShape; - - impl_shape!(MintShape, 1, 0, 1, 0); - - /// Mint Transaction - pub type Mint<C> = transfer_alias!(C, MintShape); - - impl<C> Mint<C> - where - C: Configuration, - { - /// Builds a [`Mint`] from `asset` and `receiver`. - #[inline] - pub fn build(asset: Asset, receiver: Receiver<C>) -> Self { - Self::new( - Some(asset.id), - [asset.value], - Default::default(), - [receiver], - Default::default(), - ) - } - } - - /// Private Transfer Transaction Shape - /// - /// ```text - /// <0, 2, 2, 0> - /// ``` - #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd)] - pub struct PrivateTransferShape; - - impl_shape!(PrivateTransferShape, 0, 2, 2, 0); - - /// Private Transfer Transaction - pub type PrivateTransfer<C> = transfer_alias!(C, PrivateTransferShape); - - impl<C> PrivateTransfer<C> - where - C: Configuration, - { - /// Builds a [`PrivateTransfer`] from `senders` and `receivers`. - #[inline] - pub fn build( - senders: [Sender<C>; PrivateTransferShape::SENDERS], - receivers: [Receiver<C>; PrivateTransferShape::RECEIVERS], - ) -> Self { - Self::new( - Default::default(), - Default::default(), - senders, - receivers, - Default::default(), - ) - } - } - - /// Reclaim Transaction Shape - /// - /// ```text - /// <0, 2, 1, 1> - /// ``` - /// - /// The [`ReclaimShape`] is defined in terms of the [`PrivateTransferShape`]. It is defined to - /// have the same number of senders and one secret receiver turned into a public sink. - #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd)] - pub struct ReclaimShape; - - impl_shape!( - ReclaimShape, - 0, - PrivateTransferShape::SENDERS, - PrivateTransferShape::RECEIVERS - 1, - 1 - ); - - /// Reclaim Transaction - pub type Reclaim<C> = transfer_alias!(C, ReclaimShape); - - impl<C> Reclaim<C> - where - C: Configuration, - { - /// Builds a [`Reclaim`] from `senders`, `receivers`, and `reclaim`. - #[inline] - pub fn build( - senders: [Sender<C>; ReclaimShape::SENDERS], - receivers: [Receiver<C>; ReclaimShape::RECEIVERS], - reclaim: Asset, - ) -> Self { - Self::new( - Some(reclaim.id), - Default::default(), - senders, - receivers, - [reclaim.value], - ) - } - } - - /// Canonical Transaction Type - pub enum Transaction<C> - where - C: Configuration, - { - /// Mint Private Asset - Mint(Asset), - - /// Private Transfer Asset to Receiver - PrivateTransfer(Asset, ReceivingKey<C>), - - /// Reclaim Private Asset - Reclaim(Asset), - } - - impl<C> Transaction<C> - where - C: Configuration, - { - /// Checks that `self` can be executed for a given `balance` state, returning the - /// transaction kind if successful, and returning the asset back if the balance was - /// insufficient. - #[inline] - pub fn check<F>(&self, balance: F) -> Result<TransactionKind, Asset> - where - F: FnOnce(Asset) -> bool, - { - match self { - Self::Mint(asset) => Ok(TransactionKind::Deposit(*asset)), - Self::PrivateTransfer(asset, _) | Self::Reclaim(asset) => { - if balance(*asset) { - Ok(TransactionKind::Withdraw(*asset)) - } else { - Err(*asset) - } - } - } - } - } - - /// Transaction Kind - #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] - pub enum TransactionKind { - /// Deposit Transaction - /// - /// A transaction of this kind will result in a deposit of `asset`. - Deposit(Asset), - - /// Withdraw Transaction - /// - /// A transaction of this kind will result in a withdraw of `asset`. - Withdraw(Asset), - } - - /// Transfer Asset Selection - pub struct Selection<C> - where - C: Configuration, - { - /// Change Value - pub change: AssetValue, - - /// Senders - pub senders: Vec<PreSender<C>>, - } - - /// Selection Error - #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] - pub enum SelectionError<E> { - /// Insufficient Balance - InsufficientBalance, - - /// Building Error - Error(E), - } - - impl<C> Selection<C> - where - C: Configuration, - { - /// Builds a new [`Selection`] from `change` and `senders`. - #[inline] - fn build(change: AssetValue, senders: Vec<PreSender<C>>) -> Self { - Self { change, senders } - } - - /// Builds a new [`Selection`] by mapping over an asset selection with `builder`. - #[inline] - pub fn new<M, E, F>( - selection: &asset::Selection<M>, - asset_id: AssetId, - mut builder: F, - ) -> Result<Self, SelectionError<E>> - where - M: asset::AssetMap, - F: FnMut(&M::Key, Asset) -> Result<PreSender<C>, E>, - { - if selection.is_empty() { - return Err(SelectionError::InsufficientBalance); - } - Ok(Self::build( - selection.change, - selection - .iter() - .map(move |(k, v)| builder(k, asset_id.with(*v))) - .collect::<Result<_, _>>() - .map_err(SelectionError::Error)?, - )) - } - } -} diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index ef7aa1292..5bc06c281 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -559,7 +559,7 @@ where todo!() } - /// Builds a [`TransferPost`] for the given `transfer` at `ledger_checkpoint`. + /// Builds a [`TransferPost`] for the given `transfer`. #[inline] fn build_post< const SOURCES: usize, @@ -568,7 +568,6 @@ where const SINKS: usize, >( &mut self, - ledger_checkpoint: C::LedgerCheckpoint, transfer: Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>, ) -> Result<TransferPost<C>, Error<C::HierarchicalKeyDerivationScheme, C>> { transfer @@ -576,7 +575,6 @@ where &self.ephemeral_key_commitment_scheme, &self.commitment_scheme, self.utxo_set.verifier(), - ledger_checkpoint, &self.proving_context, &mut self.rng, ) @@ -789,7 +787,7 @@ where Transaction::Mint(asset) => { /* TODO: let default_index = Default::default(); - let mint_post = self.build_post(ledger_checkpoint, Mint::build( + let mint_post = self.build_post(Mint::build( asset, self.build_pre_receiver(default_index, default_index, asset), ))?; From af011556bffb520c50fce97668ed0d885865a1db Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 19 Dec 2021 15:00:45 -0500 Subject: [PATCH 138/275] wip: move to concurrency-friendly algortithms --- manta-accounting/src/lib.rs | 2 +- manta-accounting/src/transfer/batch.rs | 197 ++++++++++++--- manta-accounting/src/transfer/canonical.rs | 31 +-- manta-accounting/src/transfer/mod.rs | 99 ++++++-- manta-accounting/src/wallet/signer.rs | 219 ++++++++-------- manta-crypto/src/accumulator.rs | 58 +---- manta-crypto/src/commitment.rs | 4 + manta-crypto/src/merkle_tree/forest.rs | 280 +++++++++++++++++++++ manta-crypto/src/merkle_tree/full.rs | 4 +- manta-crypto/src/merkle_tree/inner_tree.rs | 1 + manta-crypto/src/merkle_tree/mod.rs | 3 +- manta-crypto/src/merkle_tree/node.rs | 7 + manta-crypto/src/merkle_tree/partial.rs | 4 +- manta-crypto/src/merkle_tree/path.rs | 16 +- manta-crypto/src/merkle_tree/sharded.rs | 75 ------ manta-crypto/src/merkle_tree/tree.rs | 73 ++---- manta-util/src/concat.rs | 1 + 17 files changed, 695 insertions(+), 379 deletions(-) create mode 100644 manta-crypto/src/merkle_tree/forest.rs delete mode 100644 manta-crypto/src/merkle_tree/sharded.rs diff --git a/manta-accounting/src/lib.rs b/manta-accounting/src/lib.rs index c77494417..09d57ffb7 100644 --- a/manta-accounting/src/lib.rs +++ b/manta-accounting/src/lib.rs @@ -31,4 +31,4 @@ pub mod asset; pub mod fs; pub mod key; pub mod transfer; -// TODO: pub mod wallet; +pub mod wallet; diff --git a/manta-accounting/src/transfer/batch.rs b/manta-accounting/src/transfer/batch.rs index 83e749533..285ed5c39 100644 --- a/manta-accounting/src/transfer/batch.rs +++ b/manta-accounting/src/transfer/batch.rs @@ -16,28 +16,148 @@ //! Batched Transfers -use crate::transfer::{Configuration, PreSender, Receiver, Transfer, TransferPost, Utxo}; +use crate::{ + asset::{Asset, AssetId, AssetValue}, + transfer::{ + Configuration, Parameters, PreSender, ProofSystemError, Receiver, ReceivingKey, Sender, + SpendingKey, Transfer, TransferPost, Utxo, + }, +}; use alloc::vec; -use manta_crypto::accumulator::Accumulator; +use core::mem; +use manta_crypto::{ + accumulator::Accumulator, + rand::{CryptoRng, Rand, RngCore}, +}; use manta_util::{ - fallible_array_map, + fallible_array_map, into_array_unchecked, iter::{ChunkBy, IteratorExt}, - seal, }; +/// Secret Transfer +pub type SecretTransfer<C, const SENDERS: usize, const RECEIVERS: usize> = + Transfer<C, 0, { SENDERS }, { RECEIVERS }, 0>; + +/// Zero Coin Pre-Sender +pub struct Zero<C, K> +where + C: Configuration, +{ + /// Spend Access Key + key: K, + + /// Pre-Sender + pre_sender: PreSender<C>, +} + +/// Batch Join Structure +pub struct Join<C, K, const RECEIVERS: usize> +where + C: Configuration, +{ + /// Receivers + receivers: [Receiver<C>; RECEIVERS], + + /// Zero Coin Pre-Senders + zeroes: Vec<Zero<C, K>>, + + /// Accumulated Balance Pre-Sender + pre_sender: PreSender<C>, +} + +impl<C, K, const RECEIVERS: usize> Join<C, K, RECEIVERS> +where + C: Configuration, + K: Clone, +{ + /// + #[inline] + pub fn new<R>( + parameters: &Parameters<C>, + asset: Asset, + spending_key: &SpendingKey<C>, + zero_key: K, + rng: &mut R, + ) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + let mut receivers = Vec::with_capacity(RECEIVERS); + let mut zeroes = Vec::with_capacity(RECEIVERS - 1); + for _ in 0..RECEIVERS - 2 { + let (receiver, pre_sender) = spending_key.internal_zero_pair( + &parameters.ephemeral_key_commitment_scheme, + &parameters.commitment_scheme, + rng.gen(), + asset.id, + ); + receivers.push(receiver); + zeroes.push(Zero { + key: zero_key.clone(), + pre_sender, + }); + } + let (receiver, pre_sender) = spending_key.internal_pair( + &parameters.ephemeral_key_commitment_scheme, + &parameters.commitment_scheme, + rng.gen(), + asset, + ); + receivers.push(receiver); + Self { + receivers: into_array_unchecked(receivers), + zeroes, + pre_sender, + } + } +} + /// -pub struct BatchRound<C, const SENDERS: usize, const RECEIVERS: usize> +pub trait Batcher<C> where C: Configuration, { - /// PreSender Chunk Iterator - pre_senders: ChunkBy<vec::IntoIter<PreSender<C>>, SENDERS>, + /// + type UtxoSet: Accumulator<Item = Utxo<C>, Verifier = C::UtxoSetVerifier>; + + /// + type Rng: CryptoRng + RngCore + ?Sized; + + /// + fn utxo_set(&mut self) -> &mut Self::UtxoSet; /// - accumulators: Vec<PreSender<C>>, + fn rng(&mut self) -> &mut Self::Rng; + + /// + fn prove<const SENDERS: usize, const RECEIVERS: usize>( + &mut self, + transfer: SecretTransfer<C, SENDERS, RECEIVERS>, + ) -> Result<TransferPost<C>, ProofSystemError<C>>; +} + +/// Batching Error +pub enum Error<C> +where + C: Configuration, +{ + /// Missing UTXO Membership Proof + MissingUtxoMembershipProof, - /// Final Receiver - receiver: Option<Receiver<C>>, + /// Proof System Error + ProofSystemError(ProofSystemError<C>), +} + +/// +pub struct BatchRound<C, const SENDERS: usize, const RECEIVERS: usize> +where + C: Configuration, +{ + /// Pre-Sender Chunk Iterator + pre_senders: ChunkBy<vec::IntoIter<PreSender<C>>, SENDERS>, + + /// Joined Pre-Senders + joins: Vec<PreSender<C>>, } impl<C, const SENDERS: usize, const RECEIVERS: usize> BatchRound<C, SENDERS, RECEIVERS> @@ -45,39 +165,56 @@ where C: Configuration, { /// - pub fn next_transfer<S>(&mut self, utxo_set: &mut S) -> Option<TransferPost<C>> + #[inline] + pub fn next<K, R, S, P>( + &mut self, + parameters: &Parameters<C>, + spending_key: &SpendingKey<C>, + zero_key: K, + asset_id: AssetId, + zeroes: &mut Vec<Zero<C, K>>, + utxo_set: &mut S, + mut prover: P, + rng: &mut R, + ) -> Result<TransferPost<C>, Error<C>> where + K: Clone, + R: CryptoRng + RngCore + ?Sized, S: Accumulator<Item = Utxo<C>, Verifier = C::UtxoSetVerifier>, + P: FnMut( + SecretTransfer<C, SENDERS, RECEIVERS>, + ) -> Result<TransferPost<C>, ProofSystemError<C>>, { if let Some(chunk) = self.pre_senders.next() { - let senders = - fallible_array_map(chunk, move |ps| ps.try_upgrade(utxo_set).ok_or(())).ok()?; + let senders = fallible_array_map(chunk, |ps| { + ps.try_upgrade(utxo_set) + .ok_or(Error::MissingUtxoMembershipProof) + })?; - /* - let mut accumulator = self.signer.next_accumulator::<_, _, RECEIVERS>( + let mut join = Join::new( parameters, - asset_id, - senders.iter().map(Sender::asset_value).sum(), - &mut self.rng, - )?; + asset_id.with(senders.iter().map(Sender::asset_value).sum()), + spending_key, + zero_key, + rng, + ); - let post = - self.build_post(Transfer::new(None, [], senders, accumulator.receivers, []))?; + let post = prover(Transfer::new(None, [], senders, join.receivers, [])) + .map_err(Error::ProofSystemError)?; - for zero in &accumulator.zeros { - zero.as_ref().insert_utxo(utxo_set); + for zero in &join.zeroes { + zero.pre_sender.insert_utxo(utxo_set); } - accumulator.pre_sender.insert_utxo(&mut self.utxo_set); + join.pre_sender.insert_utxo(utxo_set); - new_zeroes.append(&mut accumulator.zeroes); - accumulators.push(accumulator.pre_sender); - */ + zeroes.append(&mut join.zeroes); + self.joins.push(join.pre_sender); - todo!() + Ok(post) } else { /* - accumulators.append(&mut self.pre_senders.remainder()); - self.pre_senders = accumulators.into_iter().chunk_by::<SENDERS>(); + self.joins.append(&mut self.pre_senders.remainder()); + self.pre_senders = mem::take(&mut self.joins).into_iter().chunk_by::<SENDERS>(); */ todo!() diff --git a/manta-accounting/src/transfer/canonical.rs b/manta-accounting/src/transfer/canonical.rs index eaacfc0d7..bcbc7fcf3 100644 --- a/manta-accounting/src/transfer/canonical.rs +++ b/manta-accounting/src/transfer/canonical.rs @@ -28,7 +28,7 @@ create_seal! {} /// /// This trait identifies a transfer shape, i.e. the number and type of participants on the input /// and output sides of the transaction. This trait is sealed and can only be used with the -/// [existing canonical implementations](canonical). +/// existing canonical implementations. pub trait Shape: sealed::Sealed { /// Number of Sources const SOURCES: usize; @@ -248,16 +248,6 @@ where pub senders: Vec<PreSender<C>>, } -/// Selection Error -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum SelectionError<E> { - /// Insufficient Balance - InsufficientBalance, - - /// Building Error - Error(E), -} - impl<C> Selection<C> where C: Configuration, @@ -270,25 +260,18 @@ where /// Builds a new [`Selection`] by mapping over an asset selection with `builder`. #[inline] - pub fn new<M, E, F>( - selection: &asset::Selection<M>, - asset_id: AssetId, - mut builder: F, - ) -> Result<Self, SelectionError<E>> + pub fn new<M, E, F>(selection: asset::Selection<M>, mut builder: F) -> Result<Self, E> where M: asset::AssetMap, - F: FnMut(&M::Key, Asset) -> Result<PreSender<C>, E>, + F: FnMut(M::Key, AssetValue) -> Result<PreSender<C>, E>, { - if selection.is_empty() { - return Err(SelectionError::InsufficientBalance); - } Ok(Self::build( selection.change, selection - .iter() - .map(move |(k, v)| builder(k, asset_id.with(*v))) - .collect::<Result<_, _>>() - .map_err(SelectionError::Error)?, + .values + .into_iter() + .map(move |(k, v)| builder(k, v)) + .collect::<Result<_, _>>()?, )) } } diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index a108a6f31..deed9578a 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -18,7 +18,7 @@ use crate::asset::{Asset, AssetId, AssetValue}; use alloc::vec::Vec; -use core::ops::Add; +use core::{fmt::Debug, hash::Hash, ops::Add}; use manta_crypto::{ accumulator::{self, Accumulator, MembershipProof, Verifier}, commitment::{self, CommitmentScheme, Input as CommitmentInput}, @@ -52,6 +52,9 @@ pub trait Configuration { /// Secret Key Type type SecretKey: Clone; + /// Public Key Type + type PublicKey: Clone; + /// Secret Key Variable Type type SecretKeyVar: Variable<Self::ConstraintSystem, Type = SecretKey<Self>, Mode = Secret>; @@ -60,7 +63,10 @@ pub trait Configuration { + Equal<Self::ConstraintSystem>; /// Key Agreement Scheme Type - type KeyAgreementScheme: KeyAgreementScheme<SecretKey = Self::SecretKey>; + type KeyAgreementScheme: KeyAgreementScheme< + SecretKey = Self::SecretKey, + PublicKey = Self::PublicKey, + >; /// Ephemeral Key Trapdoor Type type EphemeralKeyTrapdoor: Sample; @@ -523,6 +529,8 @@ where } /// Spending Key +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""))] pub struct SpendingKey<C> where C: Configuration, @@ -579,6 +587,7 @@ where ephemeral_key: PublicKey<C>, asset: Asset, ) -> PreSender<C> { + // FIXME: See if this clone is really needed. PreSender::new(parameters, self.spend.clone(), ephemeral_key, asset) } @@ -586,21 +595,70 @@ where #[inline] pub fn receiver( &self, - ephemeral_key_commitment_scheme_parameters: &EphemeralKeyParameters<C>, + ephemeral_key_parameters: &EphemeralKeyParameters<C>, commitment_scheme_parameters: &CommitmentSchemeParameters<C>, ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, asset: Asset, ) -> Receiver<C> { self.derive().into_receiver( - ephemeral_key_commitment_scheme_parameters, + ephemeral_key_parameters, commitment_scheme_parameters, ephemeral_key_trapdoor, asset, ) } + + /// Returns an receiver-sender pair for internal transactions. + #[inline] + pub fn internal_pair( + &self, + ephemeral_key_parameters: &EphemeralKeyParameters<C>, + commitment_scheme_parameters: &CommitmentSchemeParameters<C>, + ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, + asset: Asset, + ) -> (Receiver<C>, PreSender<C>) { + let receiver = self.receiver( + ephemeral_key_parameters, + commitment_scheme_parameters, + ephemeral_key_trapdoor, + asset, + ); + let sender = self.sender( + commitment_scheme_parameters, + receiver.ephemeral_public_key().clone(), + asset, + ); + (receiver, sender) + } + + /// Returns an receiver-sender pair of zeroes for internal transactions. + #[inline] + pub fn internal_zero_pair( + &self, + ephemeral_key_parameters: &EphemeralKeyParameters<C>, + commitment_scheme_parameters: &CommitmentSchemeParameters<C>, + ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, + asset_id: AssetId, + ) -> (Receiver<C>, PreSender<C>) { + self.internal_pair( + ephemeral_key_parameters, + commitment_scheme_parameters, + ephemeral_key_trapdoor, + Asset::zero(asset_id), + ) + } } /// Receiving Key +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = ""), + Copy(bound = "PublicKey<C>: Copy"), + Debug(bound = "PublicKey<C>: Debug"), + Eq(bound = "PublicKey<C>: Eq"), + Hash(bound = "PublicKey<C>: Hash"), + PartialEq(bound = "PublicKey<C>: PartialEq") +)] pub struct ReceivingKey<C> where C: Configuration, @@ -620,13 +678,13 @@ where #[inline] pub fn into_receiver( self, - ephemeral_key_commitment_scheme_parameters: &EphemeralKeyParameters<C>, + ephemeral_key_parameters: &EphemeralKeyParameters<C>, commitment_scheme_parameters: &CommitmentSchemeParameters<C>, ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, asset: Asset, ) -> Receiver<C> { Receiver::new( - ephemeral_key_commitment_scheme_parameters, + ephemeral_key_parameters, commitment_scheme_parameters, ephemeral_key_trapdoor, self.spend, @@ -796,6 +854,12 @@ impl<C> Sender<C> where C: Configuration, { + /// Returns the asset value sent by `self` in the transaction. + #[inline] + pub fn asset_value(&self) -> AssetValue { + self.asset.value + } + /// Reverts `self` back into a [`PreSender`]. /// /// This method should be called if the [`Utxo`] membership proof attached to `self` was deemed @@ -1066,18 +1130,15 @@ where /// Builds a new [`Receiver`] for `spend` to receive `asset` with `ephemeral_secret_key`. #[inline] pub fn new( - ephemeral_key_commitment_scheme_parameters: &EphemeralKeyParameters<C>, + ephemeral_key_parameters: &EphemeralKeyParameters<C>, commitment_scheme_parameters: &CommitmentSchemeParameters<C>, ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, spend: PublicKey<C>, view: PublicKey<C>, asset: Asset, ) -> Self { - let ephemeral_secret_key = C::ephemeral_secret_key( - ephemeral_key_commitment_scheme_parameters, - &ephemeral_key_trapdoor, - asset, - ); + let ephemeral_secret_key = + C::ephemeral_secret_key(ephemeral_key_parameters, &ephemeral_key_trapdoor, asset); Self { utxo: C::full_utxo( commitment_scheme_parameters, @@ -1092,6 +1153,12 @@ where } } + /// Returns the ephemeral public key associated to `self`. + #[inline] + pub fn ephemeral_public_key(&self) -> &PublicKey<C> { + self.note.ephemeral_public_key() + } + /// Extracts the ledger posting data from `self`. #[inline] pub fn into_post(self) -> ReceiverPost<C> { @@ -1168,7 +1235,7 @@ where match allocation { Allocation::Known(this, mode) => Self { ephemeral_key_trapdoor: this.ephemeral_key_trapdoor.as_known(cs, mode), - ephemeral_public_key: this.note.ephemeral_public_key().as_known(cs, mode), + ephemeral_public_key: this.ephemeral_public_key().as_known(cs, mode), spend: this.spend.as_known(cs, mode), asset: this.asset.as_known(cs, mode), utxo: this.utxo.as_known(cs, Public), @@ -1247,9 +1314,9 @@ impl<C> ReceiverPost<C> where C: Configuration, { - /// Returns the ephemeral key associated to `self`. + /// Returns the ephemeral public key associated to `self`. #[inline] - pub fn ephemeral_key(&self) -> &PublicKey<C> { + pub fn ephemeral_public_key(&self) -> &PublicKey<C> { self.note.ephemeral_public_key() } @@ -1259,7 +1326,7 @@ where // TODO: Add a "public part" trait that extracts the public part of `Receiver` (using // `ReceiverVar` to determine the types), then generate this method automatically. C::ProofSystem::extend(input, &self.utxo); - C::ProofSystem::extend(input, self.ephemeral_key()); + C::ProofSystem::extend(input, self.ephemeral_public_key()); } /// Validates `self` on the receiver `ledger`. diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 5bc06c281..1ad39ae3a 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -34,10 +34,11 @@ use crate::{ key::{self, AccountKeys, HierarchicalKeyDerivationScheme}, transfer::{ self, - canonical::{Mint, PrivateTransfer, PrivateTransferShape, Reclaim, Transaction}, - EncryptedNote, PreReceiver, PreSender, ProofSystemError, ProvingContext, PublicKey, - Receiver, ReceivingKey, SecretKey, Sender, Shape, SpendingKey, Transfer, TransferPost, - Utxo, + canonical::{ + Mint, PrivateTransfer, PrivateTransferShape, Reclaim, Selection, Shape, Transaction, + }, + EncryptedNote, Parameters, PreSender, ProofSystemError, ProvingContext, PublicKey, + Receiver, ReceivingKey, SecretKey, Sender, SpendingKey, Transfer, TransferPost, Utxo, }, }; use alloc::{vec, vec::Vec}; @@ -56,7 +57,7 @@ use manta_crypto::{ }, encryption::DecryptedMessage, key::KeyAgreementScheme, - rand::{CryptoRng, RngCore}, + rand::{CryptoRng, Rand, RngCore}, }; use manta_util::{fallible_array_map, into_array_unchecked, iter::IteratorExt}; @@ -356,11 +357,8 @@ where /// Account Table account_table: AccountTable<C>, - /// Ephemeral Key Commitment Scheme - ephemeral_key_commitment_scheme: C::EphemeralKeyCommitmentScheme, - - /// Commitment Scheme - commitment_scheme: C::CommitmentScheme, + /// Transfer Parameters, + parameters: Parameters<C>, /// Proving Context proving_context: ProvingContext<C>, @@ -386,8 +384,7 @@ where #[inline] fn new_inner( account_table: AccountTable<C>, - ephemeral_key_commitment_scheme: C::EphemeralKeyCommitmentScheme, - commitment_scheme: C::CommitmentScheme, + parameters: Parameters<C>, proving_context: ProvingContext<C>, utxo_set: C::UtxoSet, assets: C::AssetMap, @@ -396,8 +393,7 @@ where ) -> Self { Self { account_table, - ephemeral_key_commitment_scheme, - commitment_scheme, + parameters, proving_context, utxo_set, assets, @@ -415,15 +411,13 @@ where #[inline] pub fn new( account_table: AccountTable<C>, - ephemeral_key_commitment_scheme: C::EphemeralKeyCommitmentScheme, - commitment_scheme: C::CommitmentScheme, + parameters: Parameters<C>, proving_context: ProvingContext<C>, rng: C::Rng, ) -> Self { Self::new_inner( account_table, - ephemeral_key_commitment_scheme, - commitment_scheme, + parameters, proving_context, Default::default(), Default::default(), @@ -432,15 +426,57 @@ where ) } - /// + /// Returns the hierarchical key indices for the current account. #[inline] fn account(&self) -> AccountKeys<C::HierarchicalKeyDerivationScheme> { + // FIXME: Implement multiple accounts. self.account_table.get(Default::default()).unwrap() } + /// Inserts the new `utxo`-`encrypted_note` pair if a known key can decrypt the note and + /// validate the utxo. + #[inline] + fn insert_update_item( + &mut self, + utxo: Utxo<C>, + encrypted_note: EncryptedNote<C>, + assets: &mut Vec<Asset>, + ) -> Result<(), Error<C::HierarchicalKeyDerivationScheme, C>> { + let mut encrypted_note = Some(encrypted_note); + if let Some(( + spend_index, + DecryptedMessage { + plaintext: asset, + ephemeral_public_key, + }, + )) = self + .account() + .find_index(move |k| DecryptedMessage::try_new(&mut encrypted_note, &k)) + .map_err(key::Error::KeyDerivationError)? + { + /* TODO: + if self.get_spending_key(spend_index).validate_utxo( + &self.parameters.commitment_scheme, + &ephemeral_public_key, + &asset, + &utxo, + ) { + assets.push(asset); + self.assets + .insert((spend_index, ephemeral_public_key), asset); + self.utxo_set.insert(&utxo); + return Ok(()); + } + */ + todo!() + } + self.utxo_set.insert_nonprovable(&utxo); + Ok(()) + } + /// Updates the internal ledger state, returning the new asset distribution. #[inline] - fn sync_inner<I>( + fn sync_with_updates<I>( &mut self, updates: I, ) -> SyncResult<C::HierarchicalKeyDerivationScheme, C, Self> @@ -449,37 +485,7 @@ where { let mut assets = Vec::new(); for (utxo, encrypted_note) in updates { - let mut encrypted_note = Some(encrypted_note); - if let Some(( - spend_index, - DecryptedMessage { - plaintext: asset, - ephemeral_public_key, - }, - )) = self - .account() - .find_index(move |k| DecryptedMessage::try_new(&mut encrypted_note, &k)) - .map_err(key::Error::KeyDerivationError)? - { - /* FIXME: Add UTXO validation check. Is this necessary? - if self.spending_key.validate_utxo::<C>( - &ephemeral_public_key, - &asset, - &utxo, - &self.commitment_scheme, - ) { - assets.push(asset); - self.assets.insert((index, ephemeral_public_key), asset); - self.utxo_set.insert(&utxo); - } - */ - assets.push(asset); - self.assets - .insert((spend_index, ephemeral_public_key), asset); - self.utxo_set.insert(&utxo); - } else { - self.utxo_set.insert_nonprovable(&utxo); - } + self.insert_update_item(utxo, encrypted_note, &mut assets)?; } Ok(SyncResponse::new(assets)) } @@ -499,7 +505,7 @@ where // FIXME: Do a capacity check on the current UTXO set. match self.utxo_set.len().checked_sub(starting_index) { - Some(diff) => self.sync_inner(updates.into_iter().skip(diff)), + Some(diff) => self.sync_with_updates(updates.into_iter().skip(diff)), _ => Err(Error::InconsistentSynchronization), } } @@ -512,27 +518,32 @@ where ephemeral_key: PublicKey<C>, asset: Asset, ) -> Result<PreSender<C>, Error<C::HierarchicalKeyDerivationScheme, C>> { - Ok(PreSender::new( - self.account().spend_key(spend_index)?, + /* TODO: + Ok(self.account().spend_key(spend_index)?.pre_sender( + &self.parameters.commitment_scheme, ephemeral_key, asset, - &self.commitment_scheme, )) + */ + todo!() } /// Builds the pre-receiver associated to `spend_index`, `view_index`, and `asset`. #[inline] - fn build_pre_receiver( + fn build_receiver( &self, spend_index: SpendIndex<C>, view_index: ViewIndex<C>, asset: Asset, - ) -> Result<PreReceiver<C>, Error<C::HierarchicalKeyDerivationScheme, C>> { + ) -> Result<Receiver<C>, Error<C::HierarchicalKeyDerivationScheme, C>> { + /* TODO: let keypair = self .account() .keypair_with(spend_index, view_index)? .derive::<C::KeyAgreementScheme>(); Ok(PreReceiver::new(keypair.spend, keypair.view, asset)) + */ + todo!() } /// Selects the pre-senders which collectively own at least `asset`, returning any change. @@ -545,18 +556,10 @@ where if selection.is_empty() { return Err(Error::InsufficientBalance(asset)); } - /* TODO: self.pending_assets.remove = selection.keys().cloned().collect(); - Ok(Selection::new( - selection.change, - selection - .values - .into_iter() - .map(move |((i, ek), v)| self.build_pre_sender(i, ek, asset.id.with(v))) - .collect(), - )) - */ - todo!() + Selection::new(selection, move |(i, ek), v| { + self.build_pre_sender(i, ek, asset.id.with(v)) + }) } /// Builds a [`TransferPost`] for the given `transfer`. @@ -570,15 +573,18 @@ where &mut self, transfer: Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>, ) -> Result<TransferPost<C>, Error<C::HierarchicalKeyDerivationScheme, C>> { + /* TODO: transfer .into_post( - &self.ephemeral_key_commitment_scheme, - &self.commitment_scheme, + &self.parameters.ephemeral_key_commitment_scheme, + &self.parameters.commitment_scheme, self.utxo_set.verifier(), &self.proving_context, &mut self.rng, ) .map_err(Error::ProofSystemError) + */ + todo!() } /* TODO: @@ -696,6 +702,7 @@ where Ok(()) } + */ /// Returns the next change receiver for `asset`. #[inline] @@ -703,63 +710,67 @@ where &mut self, asset_id: AssetId, change: AssetValue, - ) -> Result<Receiver<C>, Error<C::DerivedSecretKeyGenerator, C>> { + ) -> Result<Receiver<C>, Error<C::HierarchicalKeyDerivationScheme, C>> { let asset = asset_id.with(change); - let (receiver, index) = self - .signer - .next_change(&self.commitment_scheme, asset, &mut self.rng)? - .into(); - self.pending_assets.insert = Some((index, asset)); + let default_index = Default::default(); + let receiver = self.build_receiver(default_index, default_index, asset)?; + self.pending_assets.insert = Some(( + (default_index, receiver.ephemeral_public_key().clone()), + asset, + )); Ok(receiver) } - /// Prepares a given [`ShieldedIdentity`] for receiving `asset`. + /// Prepares a given [`ReceivingKey`] for receiving `asset`. #[inline] - pub fn prepare_receiver( - &mut self, - asset: Asset, - receiver: ShieldedIdentity<C>, - ) -> Result<Receiver<C>, Error<C::DerivedSecretKeyGenerator, C>> { - receiver - .into_receiver(&self.commitment_scheme, asset, &mut self.rng) - .map_err(Error::EncryptionError) + pub fn prepare_receiver(&mut self, asset: Asset, receiver: ReceivingKey<C>) -> Receiver<C> { + receiver.into_receiver( + &self.parameters.ephemeral_key_commitment_scheme, + &self.parameters.commitment_scheme, + self.rng.gen(), + asset, + ) } - */ /// Signs a withdraw transaction without resetting on error. #[inline] - fn sign_withdraw_inner( + fn sign_withdraw_without_rollback( &mut self, asset: Asset, receiver: Option<ReceivingKey<C>>, ) -> SignResult<C::HierarchicalKeyDerivationScheme, C, Self> { - /* TODO: const SENDERS: usize = PrivateTransferShape::SENDERS; const RECEIVERS: usize = PrivateTransferShape::RECEIVERS; let selection = self.select(asset)?; let mut posts = Vec::new(); + + /* let senders = self.accumulate_transfers::<SENDERS, RECEIVERS>( asset.id, selection.pre_senders, &mut posts, )?; + */ let change = self.next_change(asset.id, selection.change)?; + let final_post = match receiver { Some(receiver) => { - let receiver = self.prepare_receiver(asset, receiver)?; - self.build_post(PrivateTransfer::build(senders, [change, receiver]))? + let receiver = self.prepare_receiver(asset, receiver); + // self.build_post(PrivateTransfer::build(senders, [change, receiver]))? + todo!() + } + _ => { + // self.build_post(Reclaim::build(senders, [change], asset))? + todo!() } - _ => self.build_post(Reclaim::build(senders, [change], asset))?, }; - posts.push(final_post); + // posts.push(final_post); Ok(SignResponse::new(posts)) - */ - todo!() } /// Signs a withdraw transaction, resetting the internal state on an error. @@ -769,7 +780,7 @@ where asset: Asset, receiver: Option<ReceivingKey<C>>, ) -> SignResult<C::HierarchicalKeyDerivationScheme, C, Self> { - let result = self.sign_withdraw_inner(asset, receiver); + let result = self.sign_withdraw_without_rollback(asset, receiver); if result.is_err() { self.rollback(); } @@ -785,20 +796,12 @@ where self.commit(); match transaction { Transaction::Mint(asset) => { - /* TODO: let default_index = Default::default(); - let mint_post = self.build_post(Mint::build( - asset, - self.build_pre_receiver(default_index, default_index, asset), - ))?; - self.pending_assets.insert = Some(( - default_index, - mint_post.receiver_ephemeral_keys()[0].clone(), - asset, - )); + let receiver = self.build_receiver(default_index, default_index, asset)?; + let ephemeral_public_key = receiver.ephemeral_public_key().clone(); + let mint_post = self.build_post(Mint::build(asset, receiver))?; + self.pending_assets.insert = Some(((default_index, ephemeral_public_key), asset)); Ok(SignResponse::new(vec![mint_post])) - */ - todo!() } Transaction::PrivateTransfer(asset, receiver) => { self.sign_withdraw(asset, Some(receiver)) @@ -901,6 +904,7 @@ where } } +/* TODO: /// Pre-Sender Selection struct Selection<C> where @@ -927,7 +931,6 @@ where } } -/* TODO: impl<D> Signer<D> where D: DerivedSecretKeyGenerator, diff --git a/manta-crypto/src/accumulator.rs b/manta-crypto/src/accumulator.rs index 0bb021d97..a65bdbcdc 100644 --- a/manta-crypto/src/accumulator.rs +++ b/manta-crypto/src/accumulator.rs @@ -18,25 +18,6 @@ // TODO: See if we can modify `Accumulator` so that it can extend the `Verifier` trait directly. -/// Matching Set -/// -/// This is a generalization of a single-element matching system, where there can be multiple -/// elements to match against. -pub trait MatchingSet<T> { - /// Checks if `t` matches any element in `self`. - fn contains(&self, t: &T) -> bool; -} - -impl<T> MatchingSet<T> for T -where - T: PartialEq, -{ - #[inline] - fn contains(&self, t: &T) -> bool { - self.eq(t) - } -} - /// Accumulator Membership Verifier pub trait Verifier { /// Parameters Type @@ -66,6 +47,9 @@ pub trait Verifier { ) -> Self::Verification; } +/// Accumulator Parameters Type +pub type Parameters<A> = <<A as Accumulator>::Verifier as Verifier>::Parameters; + /// Accumulator Output Type pub type Output<A> = <<A as Accumulator>::Verifier as Verifier>::Output; @@ -77,26 +61,12 @@ pub trait Accumulator { /// Verifier Type type Verifier: Verifier<Item = Self::Item> + ?Sized; - /// Output Matching Set Type - type OutputSet: MatchingSet<<Self::Verifier as Verifier>::Output>; - /// Returns the parameters associated with the verifier attached to `self`. - fn parameters(&self) -> &<Self::Verifier as Verifier>::Parameters; - - /// Returns the output matching set for the current state of `self`. - fn outputs(&self) -> Self::OutputSet; + fn parameters(&self) -> &Parameters<Self>; /// Returns `true` if `output` is contained in the current output matching set associated to /// `self`. - /// - /// # Implementation Note - /// - /// This method is an optimization path for implementations of [`Accumulator`] which can do a - /// output matching without having to return an entire owned [`Self::OutputSet`]. - #[inline] - fn matching_output(&self, output: &Output<Self>) -> bool { - self.outputs().contains(output) - } + fn matching_output(&self, output: &Output<Self>) -> bool; /// Inserts `item` into `self` with the guarantee that `self` can later return a valid /// membership proof for `item` with a call to [`prove`](Self::prove). This method returns @@ -128,18 +98,11 @@ where type Verifier = A::Verifier; - type OutputSet = A::OutputSet; - #[inline] - fn parameters(&self) -> &<Self::Verifier as Verifier>::Parameters { + fn parameters(&self) -> &Parameters<Self> { (**self).parameters() } - #[inline] - fn outputs(&self) -> Self::OutputSet { - (**self).outputs() - } - #[inline] fn matching_output(&self, output: &Output<Self>) -> bool { (**self).matching_output(output) @@ -246,15 +209,6 @@ where self.output } - /// Returns `true` if the output associated to `self` is contained in `outputs`. - #[inline] - pub fn output_contained_in<S>(&self, outputs: &S) -> bool - where - S: MatchingSet<V::Output>, - { - outputs.contains(&self.output) - } - /// Returns `true` if the output associated to `self` is contained in the current output /// matching set associated to `accumulator`. #[inline] diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index d5aaf707f..9c7c7b488 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -103,6 +103,7 @@ where /// Updates the builder with the `next` input. #[inline] + #[must_use] pub fn update<T>(mut self, next: &T) -> Self where T: ?Sized, @@ -114,6 +115,7 @@ where /// Updates the builder with each item in `iter`. #[inline] + #[must_use] pub fn update_all<'t, T, I>(mut self, iter: I) -> Self where T: 't + ?Sized, @@ -211,6 +213,7 @@ pub mod constraint { /// Updates the builder with the `next` input. #[inline] + #[must_use] pub fn update<T>(mut self, next: &T) -> Self where T: ?Sized, @@ -222,6 +225,7 @@ pub mod constraint { /// Updates the builder with each item in `iter`. #[inline] + #[must_use] pub fn update_all<'t, T, I>(mut self, iter: I) -> Self where T: 't + ?Sized, diff --git a/manta-crypto/src/merkle_tree/forest.rs b/manta-crypto/src/merkle_tree/forest.rs new file mode 100644 index 000000000..f3f74d096 --- /dev/null +++ b/manta-crypto/src/merkle_tree/forest.rs @@ -0,0 +1,280 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Merkle Forest Abstractions + +use crate::{ + accumulator::{ + self, Accumulator, ConstantCapacityAccumulator, ExactSizeAccumulator, MembershipProof, + OptimizedAccumulator, + }, + merkle_tree::{ + tree::{self, Configuration, Leaf, Parameters, Tree, Verifier}, + WithProofs, + }, +}; + +/// Forest Configuration +pub trait Forest<C> +where + C: Configuration + ?Sized, +{ + /// Tree Type + type Tree: Tree<C>; + + /// Tree Index + type Index; + + /// Fixed Width of the Merkle Forest + const WIDTH: usize; + + /// Builds a new empty merkle forest. + fn new(parameters: &Parameters<C>) -> Self; + + /// Returns the number of items in `self`. + fn len(&self) -> usize; + + /// Returns `true` if the length of `self` is zero. + #[inline] + fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Returns the index of the merkle tree where `leaf` should be inserted. + /// + /// # Contract + /// + /// This method should be deterministic and should distribute the space of leaves uniformly over + /// the space of indices. + fn tree_index(&self, leaf: &Leaf<C>) -> Self::Index; + + /// Returns a shared reference to the tree at the given `index`. + fn get(&self, index: Self::Index) -> &Self::Tree; + + /// Returns a shared reference to the tree which `leaf` corresponds with. + /// + /// # Implementation Note + /// + /// This method is an optimization path for computing [`tree_index`](Self::tree_index) followed + /// by [`get`](Self::get). + #[inline] + fn get_tree(&self, leaf: &Leaf<C>) -> &Self::Tree { + self.get(self.tree_index(leaf)) + } + + /// Returns a mutable reference to the tree at the given `index`. + fn get_mut(&mut self, index: Self::Index) -> &mut Self::Tree; + + /// Returns a mutable reference to the tree which `leaf` corresponds with. + /// + /// # Implementation Note + /// + /// This method is an optimization path for computing [`tree_index`](Self::tree_index) followed + /// by [`get_mut`](Self::get_mut). + #[inline] + fn get_tree_mut(&mut self, leaf: &Leaf<C>) -> &mut Self::Tree { + self.get_mut(self.tree_index(leaf)) + } +} + +/// Returns the capacity of the merkle forest with the given [`C::HEIGHT`](Configuration::HEIGHT) +/// and [`F::WIDTH`](Forest::WIDTH) parameters. +/// +/// The capacity of a merkle forest with height `H` and width `W` is `W * 2^(H - 1)`. +#[inline] +pub fn capacity<C, F>() -> usize +where + C: Configuration + ?Sized, + F: Forest<C>, +{ + F::WIDTH * tree::capacity::<C>() +} + +/// Merkle Forest +pub struct MerkleForest<C, F> +where + C: Configuration + ?Sized, + F: Forest<C>, +{ + /// Underlying Forest Structure + forest: F, + + /// Merkle Forest Parameters + parameters: Parameters<C>, +} + +impl<C, F> MerkleForest<C, F> +where + C: Configuration + ?Sized, + F: Forest<C>, +{ + /// Builds a new [`MerkleForest`] from `parameters`. + /// + /// See [`Forest::new`] for more. + #[inline] + pub fn new(parameters: Parameters<C>) -> Self { + Self::from_forest(F::new(&parameters), parameters) + } + + /// Builds a new [`MerkleForest`] from a pre-constructed `forest` and `parameters`. + #[inline] + pub fn from_forest(forest: F, parameters: Parameters<C>) -> Self { + Self { forest, parameters } + } + + /// Returns a shared reference to the parameters used by this merkle forest. + #[inline] + pub fn parameters(&self) -> &Parameters<C> { + &self.parameters + } + + /// Returns the number of leaves that can fit in this merkle tree. + /// + /// See [`capacity`] for more. + #[inline] + pub fn capacity(&self) -> usize { + capacity::<C, F>() + } + + /// Returns the number of items in this merkle forest. + /// + /// See [`Forest::len`] for more. + #[inline] + pub fn len(&self) -> usize { + self.forest.len() + } + + /// Returns `true` if this merkle forest is empty. + /// + /// See [`Forest::is_empty`] for more. + #[inline] + pub fn is_empty(&self) -> bool { + self.forest.is_empty() + } +} + +impl<C, F> AsMut<F> for MerkleForest<C, F> +where + C: Configuration + ?Sized, + F: Forest<C>, +{ + #[inline] + fn as_mut(&mut self) -> &mut F { + &mut self.forest + } +} + +impl<C, F> AsRef<F> for MerkleForest<C, F> +where + C: Configuration + ?Sized, + F: Forest<C>, +{ + #[inline] + fn as_ref(&self) -> &F { + &self.forest + } +} + +impl<C, F> Accumulator for MerkleForest<C, F> +where + C: Configuration + ?Sized, + F: Forest<C>, + F::Tree: WithProofs<C>, +{ + type Item = Leaf<C>; + + type Verifier = Verifier<C>; + + #[inline] + fn parameters(&self) -> &accumulator::Parameters<Self> { + self.parameters() + } + + #[inline] + fn matching_output(&self, output: &accumulator::Output<Self>) -> bool { + // FIXME: Implement this + let _ = output; + todo!() + } + + #[inline] + fn insert(&mut self, item: &Self::Item) -> bool { + self.forest + .get_tree_mut(item) + .push_provable(&self.parameters, item) + } + + #[inline] + fn prove(&self, item: &Self::Item) -> Option<MembershipProof<Self::Verifier>> { + let tree = self.forest.get_tree(item); + Some(MembershipProof::new( + tree.path( + &self.parameters, + tree.index_of(&self.parameters.digest(item))?, + ) + .ok()?, + tree.root(&self.parameters), + )) + } + + #[inline] + fn contains(&self, item: &Self::Item) -> bool { + self.forest + .get_tree(item) + .contains(&self.parameters.digest(item)) + } +} + +impl<C, F> ConstantCapacityAccumulator for MerkleForest<C, F> +where + C: Configuration + ?Sized, + F: Forest<C>, + F::Tree: WithProofs<C>, +{ + #[inline] + fn capacity() -> usize { + capacity::<C, F>() + } +} + +impl<C, F> ExactSizeAccumulator for MerkleForest<C, F> +where + C: Configuration + ?Sized, + F: Forest<C>, + F::Tree: WithProofs<C>, +{ + #[inline] + fn len(&self) -> usize { + self.len() + } + + #[inline] + fn is_empty(&self) -> bool { + self.is_empty() + } +} + +impl<C, F> OptimizedAccumulator for MerkleForest<C, F> +where + C: Configuration + ?Sized, + F: Forest<C>, + F::Tree: WithProofs<C>, +{ + #[inline] + fn insert_nonprovable(&mut self, item: &Self::Item) -> bool { + self.forest.get_tree_mut(item).push(&self.parameters, item) + } +} diff --git a/manta-crypto/src/merkle_tree/full.rs b/manta-crypto/src/merkle_tree/full.rs index 0b3082989..9e26e83eb 100644 --- a/manta-crypto/src/merkle_tree/full.rs +++ b/manta-crypto/src/merkle_tree/full.rs @@ -160,13 +160,13 @@ where #[inline] fn root(&self, parameters: &Parameters<C>) -> Root<C> { let _ = parameters; - Root(self.root().clone()) + self.root().clone() } #[inline] fn matching_root(&self, parameters: &Parameters<C>, root: &Root<C>) -> bool { let _ = parameters; - self.root() == &root.0 + self.root() == root } #[inline] diff --git a/manta-crypto/src/merkle_tree/inner_tree.rs b/manta-crypto/src/merkle_tree/inner_tree.rs index e6f70bdbb..e1e9a1f1d 100644 --- a/manta-crypto/src/merkle_tree/inner_tree.rs +++ b/manta-crypto/src/merkle_tree/inner_tree.rs @@ -77,6 +77,7 @@ impl InnerNode { /// Returns the [`InnerNode`] which is the sibling of `self`. #[inline] + #[must_use] pub const fn sibling(&self) -> Self { Self::new(self.depth, self.index.sibling()) } diff --git a/manta-crypto/src/merkle_tree/mod.rs b/manta-crypto/src/merkle_tree/mod.rs index 59bf11393..f5a2d2a38 100644 --- a/manta-crypto/src/merkle_tree/mod.rs +++ b/manta-crypto/src/merkle_tree/mod.rs @@ -25,6 +25,7 @@ mod node; mod tree; +pub mod forest; pub mod fork; pub mod full; pub mod inner_tree; @@ -32,8 +33,6 @@ pub mod partial; pub mod path; pub mod single_path; -// TODO: pub mod sharded; - #[cfg(feature = "test")] #[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] pub mod test; diff --git a/manta-crypto/src/merkle_tree/node.rs b/manta-crypto/src/merkle_tree/node.rs index db9c98030..4cf6a5fa1 100644 --- a/manta-crypto/src/merkle_tree/node.rs +++ b/manta-crypto/src/merkle_tree/node.rs @@ -173,6 +173,7 @@ impl Node { /// Returns the [`Node`] which is the sibling to `self`. #[inline] + #[must_use] pub const fn sibling(&self) -> Self { match self.parity() { Parity::Left => Self(self.0 + 1), @@ -201,6 +202,7 @@ impl Node { /// Returns `self` if `self` has left parity or returns the sibling of `self` if `self` has /// right parity. #[inline] + #[must_use] pub const fn as_left(&self) -> Self { match self.parity() { Parity::Left => *self, @@ -211,6 +213,7 @@ impl Node { /// Returns `self` if `self` has right parity or returns the sibling of `self` if `self` has /// left parity. #[inline] + #[must_use] pub const fn as_right(&self) -> Self { match self.parity() { Parity::Left => Self(self.0 + 1), @@ -220,12 +223,14 @@ impl Node { /// Returns the left child [`Node`] of this node. #[inline] + #[must_use] pub const fn left_child(&self) -> Self { Self(self.0 << 1) } /// Returns the right child [`Node`] of this node. #[inline] + #[must_use] pub const fn right_child(&self) -> Self { Self(self.left_child().0 + 1) } @@ -239,12 +244,14 @@ impl Node { /// Returns the parent [`Node`] of this node. #[inline] + #[must_use] pub const fn parent(&self) -> Self { Self(self.0 >> 1) } /// Converts `self` into its parent, returning the parent [`Node`]. #[inline] + #[must_use] pub fn into_parent(&mut self) -> Self { *self = self.parent(); *self diff --git a/manta-crypto/src/merkle_tree/partial.rs b/manta-crypto/src/merkle_tree/partial.rs index c69697c17..16fd613a2 100644 --- a/manta-crypto/src/merkle_tree/partial.rs +++ b/manta-crypto/src/merkle_tree/partial.rs @@ -194,13 +194,13 @@ where #[inline] fn root(&self, parameters: &Parameters<C>) -> Root<C> { let _ = parameters; - Root(self.root().clone()) + self.root().clone() } #[inline] fn matching_root(&self, parameters: &Parameters<C>, root: &Root<C>) -> bool { let _ = parameters; - self.root() == &root.0 + self.root() == root } #[inline] diff --git a/manta-crypto/src/merkle_tree/path.rs b/manta-crypto/src/merkle_tree/path.rs index 1bfc2d832..64b3e0e91 100644 --- a/manta-crypto/src/merkle_tree/path.rs +++ b/manta-crypto/src/merkle_tree/path.rs @@ -149,10 +149,8 @@ where InnerDigest<C>: 'i, I: IntoIterator<Item = &'i InnerDigest<C>>, { - Root( - iter.into_iter() - .fold(base, Self::fold_fn(parameters, index)), - ) + iter.into_iter() + .fold(base, Self::fold_fn(parameters, index)) } } @@ -352,11 +350,11 @@ where I: IntoIterator<Item = &'i InnerDigest<C>>, { let mut iter = iter.into_iter().peekable(); - Root((depth..path_length::<C>()).fold(base, |acc, _| { + (depth..path_length::<C>()).fold(base, move |acc, _| { Self::fold_fn(parameters, index.into_parent(), &acc, default, || { iter.next().unwrap() }) - })) + }) } /// Updates `self` to the next current path with `next_leaf_digest`, updating `leaf_digest` @@ -378,11 +376,8 @@ where &mem::take(sibling_digest), &mem::replace(leaf_digest, next_leaf_digest), ); - let mut accumulator = parameters.join_leaves(leaf_digest, sibling_digest); - let default_inner_digest = Default::default(); - let mut i = 0; let mut depth = 0; while !Node::are_siblings(&last_index.into_parent(), &index.into_parent()) { @@ -400,12 +395,9 @@ where accumulator = parameters.join(&accumulator, &default_inner_digest); depth += 1; } - mem::drop(self.path.drain(1..i)); - self.path[0] = last_accumulator; accumulator = parameters.join(&self.path[0], &accumulator); - Self::fold( parameters, depth + 1, diff --git a/manta-crypto/src/merkle_tree/sharded.rs b/manta-crypto/src/merkle_tree/sharded.rs deleted file mode 100644 index f4cf151fc..000000000 --- a/manta-crypto/src/merkle_tree/sharded.rs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Sharded Merkle Tree Abstractions - -use crate::merkle_tree::tree::{Configuration, Leaf, Parameters, Tree}; - -/// Sharding Configuration -pub trait Sharding<C> -where - C: Configuration + ?Sized, -{ - /// Sharded Tree Type - type Tree: ShardedTree<C>; - - /// Tree over Shard Roots Type - type RootTree: Tree<C>; - - /// Returns the shard index for the given `leaf`. - fn shard(leaf: &Leaf<C>) -> usize; -} - -/// Sharded Merkle Tree -pub trait ShardedTree<C> -where - C: Configuration + ?Sized, -{ - /// Builds a new sharded merkle tree from `parameters`. - fn new(parameters: &Parameters<C>) -> Self; -} - -/// Sharded Merkle Tree -pub struct ShardedMerkleTree<C, S> -where - C: Configuration + ?Sized, - S: Sharding<C>, -{ - /// Sharded Tree - tree: S::Tree, - - /// Tree over the Shard Roots - root_tree: S::RootTree, - - /// Merkle Tree Parameters - parameters: Parameters<C>, -} - -impl<C, S> ShardedMerkleTree<C, S> -where - C: Configuration + ?Sized, - S: Sharding<C>, -{ - /// Builds a new [`ShardedMerkleTree`] from `parameters`. - #[inline] - pub fn new(parameters: Parameters<C>) -> Self { - Self { - tree: S::Tree::new(&parameters), - root_tree: S::RootTree::new(&parameters), - parameters, - } - } -} diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index 0b33d7ea6..ee2458b8e 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -89,15 +89,12 @@ pub trait HashConfiguration { /// Merkle Tree Configuration pub trait Configuration: HashConfiguration { - /// Height Type - type Height: Copy + Into<usize>; - /// Fixed Height of the Merkle Tree /// /// # Contract /// /// Trees must always have height at least `2`. - const HEIGHT: Self::Height; + const HEIGHT: usize; } /// Configuration Structure @@ -126,9 +123,7 @@ impl<C, const HEIGHT: usize> Configuration for Config<C, HEIGHT> where C: HashConfiguration + ?Sized, { - type Height = usize; - - const HEIGHT: Self::Height = HEIGHT; + const HEIGHT: usize = HEIGHT; } /// Leaf Type @@ -156,7 +151,7 @@ pub fn capacity<C>() -> usize where C: Configuration + ?Sized, { - 1_usize << (C::HEIGHT.into() - 1) + 1_usize << (C::HEIGHT - 1) } /// Returns the path length of the merkle tree with the given [`C::HEIGHT`](Configuration::HEIGHT) @@ -169,7 +164,7 @@ pub fn path_length<C>() -> usize where C: Configuration + ?Sized, { - C::HEIGHT.into() - 2 + C::HEIGHT - 2 } /// Merkle Tree Structure @@ -177,7 +172,7 @@ pub trait Tree<C>: Sized where C: Configuration + ?Sized, { - /// Builds a new merkle tree. + /// Builds a new empty merkle tree. fn new(parameters: &Parameters<C>) -> Self; /// Builds a new merkle tree with the given `leaves` returning `None` if the iterator @@ -202,7 +197,7 @@ where Self::from_iter(parameters, slice) } - /// Returns the length of `self`. + /// Returns the number of items in `self`. fn len(&self) -> usize; /// Returns `true` if the length of `self` is zero. @@ -497,33 +492,8 @@ where } } -/// Merkle Tree Root Wrapper Type -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "InnerDigest<C>: Clone"), - Copy(bound = "InnerDigest<C>: Copy"), - Debug(bound = "InnerDigest<C>: Debug"), - Default(bound = "InnerDigest<C>: Default"), - Eq(bound = "InnerDigest<C>: Eq"), - Hash(bound = "InnerDigest<C>: Hash"), - PartialEq(bound = "InnerDigest<C>: PartialEq") -)] -pub struct Root<C>( - /// Root Inner Digest - pub InnerDigest<C>, -) -where - C: HashConfiguration + ?Sized; - -impl<C> AsRef<InnerDigest<C>> for Root<C> -where - C: HashConfiguration + ?Sized, -{ - #[inline] - fn as_ref(&self) -> &InnerDigest<C> { - &self.0 - } -} +/// Merkle Tree Root +pub type Root<C> = InnerDigest<C>; /// Merkle Tree Verifier #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -627,7 +597,7 @@ where Self { tree, parameters } } - /// Returns a reference to the parameters used by this merkle tree. + /// Returns a shared reference to the parameters used by this merkle tree. #[inline] pub fn parameters(&self) -> &Parameters<C> { &self.parameters @@ -641,7 +611,7 @@ where capacity::<C>() } - /// Returns the length of this merkle tree. + /// Returns the number of items this merkle tree. /// /// See [`Tree::len`] for more. #[inline] @@ -797,25 +767,25 @@ where } } -impl<C, T> AsRef<T> for MerkleTree<C, T> +impl<C, T> AsMut<T> for MerkleTree<C, T> where C: Configuration + ?Sized, T: Tree<C>, { #[inline] - fn as_ref(&self) -> &T { - &self.tree + fn as_mut(&mut self) -> &mut T { + &mut self.tree } } -impl<C, T> AsMut<T> for MerkleTree<C, T> +impl<C, T> AsRef<T> for MerkleTree<C, T> where C: Configuration + ?Sized, T: Tree<C>, { #[inline] - fn as_mut(&mut self) -> &mut T { - &mut self.tree + fn as_ref(&self) -> &T { + &self.tree } } @@ -828,16 +798,9 @@ where type Verifier = Verifier<C>; - type OutputSet = accumulator::Output<Self>; - - #[inline] - fn parameters(&self) -> &<Self::Verifier as accumulator::Verifier>::Parameters { - &self.parameters - } - #[inline] - fn outputs(&self) -> Self::OutputSet { - self.root() + fn parameters(&self) -> &accumulator::Parameters<Self> { + self.parameters() } #[inline] diff --git a/manta-util/src/concat.rs b/manta-util/src/concat.rs index 746cb8be4..c69f9c733 100644 --- a/manta-util/src/concat.rs +++ b/manta-util/src/concat.rs @@ -35,6 +35,7 @@ pub trait ConcatAccumulator<T> { /// Captures the accumulator and drops extra capacity before returning an owned copy. #[inline] + #[must_use] fn finish(mut self) -> Self where Self: Sized, From f6384a2fae7936164bd0a4557c3fad09f0ef4ba5 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 19 Dec 2021 16:06:38 -0500 Subject: [PATCH 139/275] wip: improve accumulator/merkle-forest interface --- manta-crypto/src/accumulator.rs | 21 +- manta-crypto/src/merkle_tree/forest.rs | 222 ++++++++++++++++---- manta-crypto/src/merkle_tree/fork.rs | 19 +- manta-crypto/src/merkle_tree/full.rs | 15 +- manta-crypto/src/merkle_tree/inner_tree.rs | 22 +- manta-crypto/src/merkle_tree/mod.rs | 2 +- manta-crypto/src/merkle_tree/partial.rs | 15 +- manta-crypto/src/merkle_tree/path.rs | 10 +- manta-crypto/src/merkle_tree/single_path.rs | 14 +- manta-crypto/src/merkle_tree/test.rs | 2 +- manta-crypto/src/merkle_tree/tree.rs | 47 ++--- 11 files changed, 236 insertions(+), 153 deletions(-) diff --git a/manta-crypto/src/accumulator.rs b/manta-crypto/src/accumulator.rs index a65bdbcdc..7b713f9cc 100644 --- a/manta-crypto/src/accumulator.rs +++ b/manta-crypto/src/accumulator.rs @@ -64,10 +64,6 @@ pub trait Accumulator { /// Returns the parameters associated with the verifier attached to `self`. fn parameters(&self) -> &Parameters<Self>; - /// Returns `true` if `output` is contained in the current output matching set associated to - /// `self`. - fn matching_output(&self, output: &Output<Self>) -> bool; - /// Inserts `item` into `self` with the guarantee that `self` can later return a valid /// membership proof for `item` with a call to [`prove`](Self::prove). This method returns /// `false` if the maximum capacity of the accumulator would be exceeded by inserting `item`. @@ -103,11 +99,6 @@ where (**self).parameters() } - #[inline] - fn matching_output(&self, output: &Output<Self>) -> bool { - (**self).matching_output(output) - } - #[inline] fn insert(&mut self, item: &Self::Item) -> bool { (**self).insert(item) @@ -209,16 +200,6 @@ where self.output } - /// Returns `true` if the output associated to `self` is contained in the current output - /// matching set associated to `accumulator`. - #[inline] - pub fn matching_output<A>(&self, accumulator: &A) -> bool - where - A: Accumulator<Verifier = V>, - { - accumulator.matching_output(&self.output) - } - /// Verifies that `item` is stored in a known accumulator using `parameters`. #[inline] pub fn verify(&self, parameters: &V::Parameters, item: &V::Item) -> V::Verification { @@ -431,7 +412,7 @@ pub mod test { .collect::<Vec<_>>(); for (i, x) in outputs.iter().enumerate() { for (j, y) in outputs.iter().enumerate().skip(i + 1) { - assert_ne!(x, y, "Found matching checkpoints at {:?} and {:?}.", i, j) + assert_ne!(x, y, "Found matching outputs at {:?} and {:?}.", i, j) } } } diff --git a/manta-crypto/src/merkle_tree/forest.rs b/manta-crypto/src/merkle_tree/forest.rs index f3f74d096..15411246f 100644 --- a/manta-crypto/src/merkle_tree/forest.rs +++ b/manta-crypto/src/merkle_tree/forest.rs @@ -14,7 +14,10 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Merkle Forest Abstractions +//! Merkle Forests + +// FIXME: Replace `N` parameter on `FixedIndex` with an associated value in the trait when +// `generic_const_exprs` is stabilized and get rid of `ConstantWidthForest`. use crate::{ accumulator::{ @@ -22,12 +25,39 @@ use crate::{ OptimizedAccumulator, }, merkle_tree::{ - tree::{self, Configuration, Leaf, Parameters, Tree, Verifier}, + tree::{self, Leaf, Parameters, Tree, Verifier}, WithProofs, }, }; +use core::{fmt::Debug, hash::Hash, marker::PhantomData}; +use manta_util::into_array_unchecked; + +/// Merkle Forest Configuration +pub trait Configuration: tree::Configuration { + /// Tree Index Type + type Index; + + /// Returns the index of the merkle tree where `leaf` should be inserted. + /// + /// # Contract + /// + /// This method should be deterministic and should distribute the space of leaves uniformly over + /// the space of indices. + fn tree_index(leaf: &Leaf<Self>) -> Self::Index; +} -/// Forest Configuration +/// Merkle Forest Fixed Index Type +/// +/// # Contract +/// +/// For a type to be a fixed index, the number of possible values must be known at compile time. In +/// this case, `N` must be the the number of values. If this type is used as an +/// [`Index`](Configuration::Index) for a merkle forest configuration, the +/// [`tree_index`](Configuration::tree_index) method must return values from a distribution over +/// exactly `N` values. +pub trait FixedIndex<const N: usize>: Into<usize> {} + +/// Merkle Forest Structure pub trait Forest<C> where C: Configuration + ?Sized, @@ -35,12 +65,6 @@ where /// Tree Type type Tree: Tree<C>; - /// Tree Index - type Index; - - /// Fixed Width of the Merkle Forest - const WIDTH: usize; - /// Builds a new empty merkle forest. fn new(parameters: &Parameters<C>) -> Self; @@ -53,57 +77,82 @@ where self.len() == 0 } - /// Returns the index of the merkle tree where `leaf` should be inserted. - /// - /// # Contract - /// - /// This method should be deterministic and should distribute the space of leaves uniformly over - /// the space of indices. - fn tree_index(&self, leaf: &Leaf<C>) -> Self::Index; + /// Returns the number of items that can be stored in `self`. + fn capacity(&self) -> usize; /// Returns a shared reference to the tree at the given `index`. - fn get(&self, index: Self::Index) -> &Self::Tree; + /// + /// # Panics + /// + /// This method is allowed to panic if `index` is out-of-bounds. + fn get(&self, index: C::Index) -> &Self::Tree; /// Returns a shared reference to the tree which `leaf` corresponds with. /// /// # Implementation Note /// - /// This method is an optimization path for computing [`tree_index`](Self::tree_index) followed - /// by [`get`](Self::get). + /// This method is an optimization path for computing [`tree_index`](Configuration::tree_index) + /// followed by [`get`](Self::get). #[inline] fn get_tree(&self, leaf: &Leaf<C>) -> &Self::Tree { - self.get(self.tree_index(leaf)) + self.get(C::tree_index(leaf)) } /// Returns a mutable reference to the tree at the given `index`. - fn get_mut(&mut self, index: Self::Index) -> &mut Self::Tree; + /// + /// # Panics + /// + /// This method is allowed to panic if `index` is out-of-bounds. + fn get_mut(&mut self, index: C::Index) -> &mut Self::Tree; /// Returns a mutable reference to the tree which `leaf` corresponds with. /// /// # Implementation Note /// - /// This method is an optimization path for computing [`tree_index`](Self::tree_index) followed - /// by [`get_mut`](Self::get_mut). + /// This method is an optimization path for computing [`tree_index`](Configuration::tree_index) + /// followed by [`get_mut`](Self::get_mut). #[inline] fn get_tree_mut(&mut self, leaf: &Leaf<C>) -> &mut Self::Tree { - self.get_mut(self.tree_index(leaf)) + self.get_mut(C::tree_index(leaf)) } } -/// Returns the capacity of the merkle forest with the given [`C::HEIGHT`](Configuration::HEIGHT) -/// and [`F::WIDTH`](Forest::WIDTH) parameters. +/// Constant Width Forest +pub trait ConstantWidthForest<C>: Forest<C> +where + C: Configuration + ?Sized, +{ + /// Fixed Number of Trees in the Forest + const WIDTH: usize; +} + +/// Returns the capacity of the merkle forest with the given [`C::HEIGHT`] and [`F::WIDTH`] +/// parameters. /// /// The capacity of a merkle forest with height `H` and width `W` is `W * 2^(H - 1)`. +/// +/// [`C::HEIGHT`]: tree::Configuration::HEIGHT +/// [`F::WIDTH`]: ConstantWidthForest::WIDTH #[inline] pub fn capacity<C, F>() -> usize where C: Configuration + ?Sized, - F: Forest<C>, + F: ConstantWidthForest<C>, { F::WIDTH * tree::capacity::<C>() } /// Merkle Forest +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "Parameters<C>: Clone, F: Clone"), + Copy(bound = "Parameters<C>: Copy, F: Copy"), + Debug(bound = "Parameters<C>: Debug, F: Debug"), + Default(bound = "Parameters<C>: Default, F: Default"), + Eq(bound = "Parameters<C>: Eq, F: Eq"), + Hash(bound = "Parameters<C>: Hash, F: Hash"), + PartialEq(bound = "Parameters<C>: PartialEq, F: PartialEq") +)] pub struct MerkleForest<C, F> where C: Configuration + ?Sized, @@ -146,7 +195,7 @@ where /// See [`capacity`] for more. #[inline] pub fn capacity(&self) -> usize { - capacity::<C, F>() + self.forest.capacity() } /// Returns the number of items in this merkle forest. @@ -203,13 +252,6 @@ where self.parameters() } - #[inline] - fn matching_output(&self, output: &accumulator::Output<Self>) -> bool { - // FIXME: Implement this - let _ = output; - todo!() - } - #[inline] fn insert(&mut self, item: &Self::Item) -> bool { self.forest @@ -226,7 +268,7 @@ where tree.index_of(&self.parameters.digest(item))?, ) .ok()?, - tree.root(&self.parameters), + tree.root().clone(), )) } @@ -241,7 +283,7 @@ where impl<C, F> ConstantCapacityAccumulator for MerkleForest<C, F> where C: Configuration + ?Sized, - F: Forest<C>, + F: ConstantWidthForest<C>, F::Tree: WithProofs<C>, { #[inline] @@ -278,3 +320,109 @@ where self.forest.get_tree_mut(item).push(&self.parameters, item) } } + +/// Tree Array Merkle Forest Alias +pub type TreeArrayMerkleForest<C, T, const N: usize> = MerkleForest<C, TreeArray<C, T, N>>; + +/// Tree Array +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "T: Clone"), + Copy(bound = "T: Copy"), + Debug(bound = "T: Debug"), + Eq(bound = "T: Eq"), + Hash(bound = "T: Hash"), + PartialEq(bound = "T: PartialEq") +)] +pub struct TreeArray<C, T, const N: usize> +where + C: Configuration + ?Sized, + C::Index: FixedIndex<N>, + T: Tree<C>, +{ + /// Array of Trees + array: [T; N], + + /// Type Parameter Marker + __: PhantomData<C>, +} + +impl<C, T, const N: usize> Default for TreeArray<C, T, N> +where + C: Configuration + ?Sized, + C::Index: FixedIndex<N>, + T: Default + Tree<C>, +{ + #[inline] + fn default() -> Self { + Self::from(into_array_unchecked( + (0..N) + .into_iter() + .map(move |_| Default::default()) + .collect::<Vec<_>>(), + )) + } +} + +impl<C, T, const N: usize> Forest<C> for TreeArray<C, T, N> +where + C: Configuration + ?Sized, + C::Index: FixedIndex<N>, + T: Tree<C>, +{ + type Tree = T; + + #[inline] + fn new(parameters: &Parameters<C>) -> Self { + Self::from(into_array_unchecked( + (0..N) + .into_iter() + .map(move |_| T::new(parameters)) + .collect::<Vec<_>>(), + )) + } + + #[inline] + fn len(&self) -> usize { + self.array.iter().map(T::len).sum() + } + + #[inline] + fn capacity(&self) -> usize { + capacity::<C, Self>() + } + + #[inline] + fn get(&self, index: C::Index) -> &Self::Tree { + &self.array[index.into()] + } + + #[inline] + fn get_mut(&mut self, index: C::Index) -> &mut Self::Tree { + &mut self.array[index.into()] + } +} + +impl<C, T, const N: usize> ConstantWidthForest<C> for TreeArray<C, T, N> +where + C: Configuration + ?Sized, + C::Index: FixedIndex<N>, + T: Tree<C>, +{ + const WIDTH: usize = N; +} + +impl<C, T, const N: usize> From<[T; N]> for TreeArray<C, T, N> +where + C: Configuration + ?Sized, + C::Index: FixedIndex<N>, + T: Tree<C>, +{ + #[inline] + fn from(array: [T; N]) -> Self { + Self { + array, + __: PhantomData, + } + } +} diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs index 63841b430..a89f89f94 100644 --- a/manta-crypto/src/merkle_tree/fork.rs +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -130,11 +130,10 @@ where self.base = Some(fork_base); let mut base = P::claim(mem::take(&mut self.base).unwrap()); assert!(base - .tree - .extend_digests( - &base.parameters, - Fork::<C, T, P, M>::extract_leaves(base_contribution, branch), - ) + .extend_digests(Fork::<C, T, P, M>::extract_leaves( + base_contribution, + branch + )) .is_ok()); self.base = Some(P::new(base)); } @@ -283,11 +282,11 @@ where Self::generate_branch_setup(base); let mut partial = Partial::new_unchecked( base_leaf_digests, - PartialInnerTree::from_current(&base.parameters, base_inner_digest, inner_path), + PartialInnerTree::from_current(base.parameters(), base_inner_digest, inner_path), ); let partial_tree_len = partial.len(); for (i, digest) in leaf_digests.into_iter().enumerate() { - partial.push_leaf_digest(&base.parameters, Node(partial_tree_len + i), digest); + partial.push_leaf_digest(base.parameters(), Node(partial_tree_len + i), digest); } (base_contribution, partial) } @@ -315,14 +314,14 @@ where match current_path.leaf_index().parity() { Parity::Left => ( BaseContribution::LeftLeaf, - base.parameters + base.parameters() .join_leaves(&current_leaf, &current_path.sibling_digest), vec![current_leaf], current_path.inner_path, ), Parity::Right => ( BaseContribution::BothLeaves, - base.parameters + base.parameters() .join_leaves(&current_path.sibling_digest, &current_leaf), vec![current_path.sibling_digest, current_leaf], current_path.inner_path, @@ -423,7 +422,7 @@ where pub fn push(&mut self, leaf: &Leaf<C>) -> Option<bool> { Some( self.branch - .push(&P::upgrade(&self.base)?.as_ref().parameters, leaf), + .push(P::upgrade(&self.base)?.as_ref().parameters(), leaf), ) } } diff --git a/manta-crypto/src/merkle_tree/full.rs b/manta-crypto/src/merkle_tree/full.rs index 9e26e83eb..7374331c9 100644 --- a/manta-crypto/src/merkle_tree/full.rs +++ b/manta-crypto/src/merkle_tree/full.rs @@ -33,7 +33,7 @@ pub type FullMerkleTree<C, M = BTreeMap<C>> = MerkleTree<C, Full<C, M>>; /// Full Merkle Tree Backing Structure #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone, M: Clone"), + Clone(bound = "LeafDigest<C>: Clone, M: Clone"), Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug, M: Debug"), Default(bound = "M: Default"), Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq, M: Eq"), @@ -139,7 +139,6 @@ where C: Configuration + ?Sized, M: InnerMap<C> + Default, LeafDigest<C>: Clone, - InnerDigest<C>: Clone, { #[inline] fn new(parameters: &Parameters<C>) -> Self { @@ -158,15 +157,8 @@ where } #[inline] - fn root(&self, parameters: &Parameters<C>) -> Root<C> { - let _ = parameters; - self.root().clone() - } - - #[inline] - fn matching_root(&self, parameters: &Parameters<C>, root: &Root<C>) -> bool { - let _ = parameters; - self.root() == root + fn root(&self) -> &Root<C> { + self.root() } #[inline] @@ -198,7 +190,6 @@ where C: Configuration + ?Sized, M: InnerMap<C> + Default, LeafDigest<C>: Clone + PartialEq, - InnerDigest<C>: Clone, { #[inline] fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>> { diff --git a/manta-crypto/src/merkle_tree/inner_tree.rs b/manta-crypto/src/merkle_tree/inner_tree.rs index e1e9a1f1d..8fb9415f8 100644 --- a/manta-crypto/src/merkle_tree/inner_tree.rs +++ b/manta-crypto/src/merkle_tree/inner_tree.rs @@ -314,7 +314,7 @@ where /// Sentinel Source for a Single Sentinel Value #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "InnerDigest<C>: Clone"), + Clone(bound = ""), Copy(bound = "InnerDigest<C>: Copy"), Debug(bound = "InnerDigest<C>: Debug"), Default(bound = ""), @@ -539,10 +539,7 @@ where /// Returns the path at `leaf_index`. #[inline] - pub fn path(&self, leaf_index: Node) -> InnerPath<C> - where - InnerDigest<C>: Clone, - { + pub fn path(&self, leaf_index: Node) -> InnerPath<C> { InnerPath::new( leaf_index, self.path_iter_for_leaf(leaf_index).cloned().collect(), @@ -558,10 +555,7 @@ where /// Returns the path at `leaf_index`, assuming that `leaf_index` is the right-most index, /// so that the return value is a valid [`CurrentInnerPath`]. #[inline] - pub fn current_path_unchecked(&self, leaf_index: Node) -> CurrentInnerPath<C> - where - InnerDigest<C>: Clone, - { + pub fn current_path_unchecked(&self, leaf_index: Node) -> CurrentInnerPath<C> { CurrentInnerPath::new( leaf_index, self.path_iter_for_leaf(leaf_index) @@ -742,10 +736,7 @@ where /// Returns the path at `leaf_index` without checking if `leaf_index` is later than the /// starting index of this tree. #[inline] - pub fn path_unchecked(&self, leaf_index: Node) -> InnerPath<C> - where - InnerDigest<C>: Clone, - { + pub fn path_unchecked(&self, leaf_index: Node) -> InnerPath<C> { self.inner_tree.path(leaf_index) } } @@ -758,10 +749,7 @@ where /// Returns the path at `leaf_index`, assuming that `leaf_index` is the right-most index, /// so that the return value is a valid [`CurrentInnerPath`]. #[inline] - pub fn current_path_unchecked(&self, leaf_index: Node) -> CurrentInnerPath<C> - where - InnerDigest<C>: Clone, - { + pub fn current_path_unchecked(&self, leaf_index: Node) -> CurrentInnerPath<C> { self.inner_tree.current_path_unchecked(leaf_index) } } diff --git a/manta-crypto/src/merkle_tree/mod.rs b/manta-crypto/src/merkle_tree/mod.rs index f5a2d2a38..f5eaabb13 100644 --- a/manta-crypto/src/merkle_tree/mod.rs +++ b/manta-crypto/src/merkle_tree/mod.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Merkle Trees +//! Merkle Trees and Forests // TODO: Should `Leaf` move into `Tree`/`Configuration` since we might want the tree to have // special kinds of leaf input (metadata along with just the digest)? diff --git a/manta-crypto/src/merkle_tree/partial.rs b/manta-crypto/src/merkle_tree/partial.rs index 16fd613a2..c05b68468 100644 --- a/manta-crypto/src/merkle_tree/partial.rs +++ b/manta-crypto/src/merkle_tree/partial.rs @@ -33,7 +33,7 @@ pub type PartialMerkleTree<C, M = BTreeMap<C>> = MerkleTree<C, Partial<C, M>>; /// Partial Merkle Tree Backing Structure #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone, M: Clone"), + Clone(bound = "LeafDigest<C>: Clone, M: Clone"), Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug, M: Debug"), Default(bound = "M: Default"), Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq, M: Eq"), @@ -173,7 +173,6 @@ where C: Configuration + ?Sized, M: InnerMap<C> + Default, LeafDigest<C>: Clone, - InnerDigest<C>: Clone, { #[inline] fn new(parameters: &Parameters<C>) -> Self { @@ -192,15 +191,8 @@ where } #[inline] - fn root(&self, parameters: &Parameters<C>) -> Root<C> { - let _ = parameters; - self.root().clone() - } - - #[inline] - fn matching_root(&self, parameters: &Parameters<C>, root: &Root<C>) -> bool { - let _ = parameters; - self.root() == root + fn root(&self) -> &Root<C> { + self.root() } #[inline] @@ -234,7 +226,6 @@ where C: Configuration + ?Sized, M: InnerMap<C>, LeafDigest<C>: Clone, - InnerDigest<C>: Clone, { type Error = (); diff --git a/manta-crypto/src/merkle_tree/path.rs b/manta-crypto/src/merkle_tree/path.rs index 64b3e0e91..ceab12e79 100644 --- a/manta-crypto/src/merkle_tree/path.rs +++ b/manta-crypto/src/merkle_tree/path.rs @@ -41,7 +41,7 @@ pub(super) mod prelude { /// Merkle Tree Inner Path #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "InnerDigest<C>: Clone"), + Clone(bound = ""), Debug(bound = "InnerDigest<C>: Debug"), Eq(bound = "InnerDigest<C>: Eq"), Hash(bound = "InnerDigest<C>: Hash"), @@ -214,7 +214,7 @@ where /// Merkle Tree Current Inner Path #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "InnerDigest<C>: Clone"), + Clone(bound = ""), Debug(bound = "InnerDigest<C>: Debug"), Default(bound = ""), Eq(bound = "InnerDigest<C>: Eq"), @@ -552,7 +552,7 @@ impl<C> FusedIterator for CurrentInnerPathNodeIter<C> where C: Configuration + ? /// Merkle Tree Path #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), + Clone(bound = "LeafDigest<C>: Clone"), Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), Default(bound = ""), Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), @@ -677,7 +677,7 @@ where /// Merkle Tree Current Path #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), + Clone(bound = "LeafDigest<C>: Clone"), Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), Default(bound = ""), Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), @@ -805,7 +805,7 @@ where /// Compressed Path #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), + Clone(bound = "LeafDigest<C>: Clone"), Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), diff --git a/manta-crypto/src/merkle_tree/single_path.rs b/manta-crypto/src/merkle_tree/single_path.rs index 532cb11fb..1961f6c9a 100644 --- a/manta-crypto/src/merkle_tree/single_path.rs +++ b/manta-crypto/src/merkle_tree/single_path.rs @@ -43,7 +43,7 @@ pub type SinglePathMerkleTree<C> = MerkleTree<C, SinglePath<C>>; /// Single Path Merkle Tree Backing Structure #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), + Clone(bound = "LeafDigest<C>: Clone"), Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default"), Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), @@ -121,7 +121,6 @@ impl<C> Tree<C> for SinglePath<C> where C: Configuration + ?Sized, LeafDigest<C>: Clone, - InnerDigest<C>: Clone, { #[inline] fn new(parameters: &Parameters<C>) -> Self { @@ -145,15 +144,8 @@ where } #[inline] - fn root(&self, parameters: &Parameters<C>) -> Root<C> { - let _ = parameters; - self.root.clone() - } - - #[inline] - fn matching_root(&self, parameters: &Parameters<C>, root: &Root<C>) -> bool { - let _ = parameters; - &self.root == root + fn root(&self) -> &Root<C> { + self.root() } #[inline] diff --git a/manta-crypto/src/merkle_tree/test.rs b/manta-crypto/src/merkle_tree/test.rs index 6b7ebbf0b..01ce19da3 100644 --- a/manta-crypto/src/merkle_tree/test.rs +++ b/manta-crypto/src/merkle_tree/test.rs @@ -134,7 +134,7 @@ where assert!( tree.path(index) .expect("Only valid queries are accepted.") - .verify(&tree.parameters, &tree.root(), leaf), + .verify(tree.parameters(), tree.root(), leaf), "Path returned from tree was not valid." ); } diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index ee2458b8e..b63dc1b00 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -59,7 +59,7 @@ pub trait InnerHash { type Parameters; /// Inner Hash Output Type - type Output: Default + PartialEq; + type Output: Clone + Default + PartialEq; /// Returns `true` if `digest` is the default inner hash output value. #[inline] @@ -210,13 +210,7 @@ where fn current_leaf(&self) -> LeafDigest<C>; /// Returns the [`Root`] of the merkle tree. - fn root(&self, parameters: &Parameters<C>) -> Root<C>; - - /// Returns `true` if `root` matches the root of `self`. - #[inline] - fn matching_root(&self, parameters: &Parameters<C>, root: &Root<C>) -> bool { - &self.root(parameters) == root - } + fn root(&self) -> &Root<C>; /// Returns the [`CurrentPath`] of the current (i.e. right-most) leaf. fn current_path(&self, parameters: &Parameters<C>) -> CurrentPath<C>; @@ -413,7 +407,7 @@ where /// Digest Type #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), + Clone(bound = "LeafDigest<C>: Clone"), Copy(bound = "LeafDigest<C>: Copy, InnerDigest<C>: Copy"), Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), @@ -543,10 +537,10 @@ where T: Tree<C>, { /// Underlying Tree Structure - pub(super) tree: T, + tree: T, /// Merkle Tree Parameters - pub(super) parameters: Parameters<C>, + parameters: Parameters<C>, } impl<C, T> MerkleTree<C, T> @@ -639,16 +633,8 @@ where /// /// See [`Tree::root`] for more. #[inline] - pub fn root(&self) -> Root<C> { - self.tree.root(&self.parameters) - } - - /// Returns `true` if `root` matches the root of `self`. - /// - /// See [`Tree::matching_root`] for more. - #[inline] - fn matching_root(&self, root: &Root<C>) -> bool { - self.tree.matching_root(&self.parameters, root) + pub fn root(&self) -> &Root<C> { + self.tree.root() } /// Returns the [`CurrentPath`] of the current (i.e right-most) leaf. @@ -693,6 +679,18 @@ where self.tree.extend_slice(&self.parameters, leaves) } + /// Appends an iterator of leaf digests at the end of the tree, returning the iterator back + /// if it could not be inserted because the tree has exhausted its capacity. + /// + /// See [`Tree::extend_digests`] for more. + #[inline] + pub fn extend_digests<L>(&mut self, leaf_digests: L) -> Result<(), L::IntoIter> + where + L: IntoIterator<Item = LeafDigest<C>>, + { + self.tree.extend_digests(&self.parameters, leaf_digests) + } + /// Returns the leaf digest at the given `index`. /// /// See [`WithProofs::leaf_digest`] for more. @@ -803,11 +801,6 @@ where self.parameters() } - #[inline] - fn matching_output(&self, output: &accumulator::Output<Self>) -> bool { - self.matching_root(output) - } - #[inline] fn insert(&mut self, item: &Self::Item) -> bool { self.push_provable(item) @@ -818,7 +811,7 @@ where Some(MembershipProof::new( self.path(self.index_of(&self.parameters.digest(item))?) .ok()?, - self.root(), + self.root().clone(), )) } From 42fb14e585134803df087dfb1338b60b7ad0c508 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 20 Dec 2021 17:43:43 -0500 Subject: [PATCH 140/275] wip: improve decoupling between signer/wallet-state --- manta-accounting/Cargo.toml | 4 +- manta-accounting/src/key.rs | 81 ++++-- manta-accounting/src/transfer/batch.rs | 16 +- manta-accounting/src/transfer/mod.rs | 102 ++++--- manta-accounting/src/wallet/ledger.rs | 65 +---- manta-accounting/src/wallet/signer.rs | 376 ++++++++----------------- manta-accounting/src/wallet/state.rs | 62 +--- manta-crypto/src/accumulator.rs | 9 + manta-crypto/src/encryption.rs | 46 ++- manta-crypto/src/merkle_tree/forest.rs | 8 +- 10 files changed, 327 insertions(+), 442 deletions(-) diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 7ae42841f..c36fe212e 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -25,9 +25,6 @@ is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } [features] -# Constraint System Gadgets -# TODO: constraints = [] - # Cocoon Filesystem Adapters cocoon-adapters = ["cocoon/std", "zeroize"] @@ -43,6 +40,7 @@ derivative = { version = "2.2.0", default-features = false, features = ["use_cor derive_more = { version = "0.99.16", default-features = false, features = ["add", "add_assign", "display", "from", "mul", "mul_assign", "sum"] } manta-crypto = { path = "../manta-crypto" } manta-util = { path = "../manta-util" } +rayon = { version = "1.5.1", optional = true } zeroize = { version = "1.4.3", optional = true, default-features = false, features = ["alloc", "zeroize_derive"] } [dev-dependencies] diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index f1fb2365b..a72317463 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -22,7 +22,7 @@ use manta_crypto::key::KeyAgreementScheme; /// Hierarchical Key Derivation Parameter pub trait HierarchicalKeyDerivationParameter: - Clone + Copy + Default + PartialOrd + From<usize> + Into<usize> + Copy + Default + PartialOrd + From<usize> + Into<usize> { /// Increments the key parameter by one unit. fn increment(&mut self); @@ -110,10 +110,10 @@ where H: HierarchicalKeyDerivationScheme + ?Sized, { /// Spend Part of the Key Pair - spend: H::SecretKey, + pub spend: H::SecretKey, /// View Part of the Key Pair - view: H::SecretKey, + pub view: H::SecretKey, } impl<H> SecretKeyPair<H> @@ -225,15 +225,33 @@ where } } + /// Performs the bounds check on `spend` and then runs `f`. + #[inline] + fn with_spend_bounds_check<T, F>(&self, spend: H::Index, f: F) -> Result<T, Error<H>> + where + F: FnOnce(&Self, H::Index) -> Result<T, H::Error>, + { + if spend <= self.max_index.spend { + f(self, spend).map_err(Error::KeyDerivationError) + } else { + Err(Error::ExceedingCurrentMaximumSpendIndex) + } + } + /// Performs the bounds check on `spend` and `view` and then runs `f`. #[inline] - fn with_bounds_check<T, F>(&self, spend: H::Index, view: H::Index, f: F) -> Result<T, Error<H>> + fn with_view_bounds_check<T, F>( + &self, + spend: H::Index, + view: H::Index, + f: F, + ) -> Result<T, Error<H>> where - F: FnOnce(&'h H, H::Account) -> Result<T, H::Error>, + F: FnOnce(&Self, H::Index, H::Index) -> Result<T, H::Error>, { if spend <= self.max_index.spend { if view <= self.max_index.view { - f(self.keys, self.account).map_err(Error::KeyDerivationError) + f(self, spend, view).map_err(Error::KeyDerivationError) } else { Err(Error::ExceedingCurrentMaximumViewIndex) } @@ -242,13 +260,24 @@ where } } + /// Derives the spend key for this account at `spend` without performing bounds checks. + #[inline] + fn derive_spend(&self, spend: H::Index) -> Result<H::SecretKey, H::Error> { + self.keys.derive_spend(self.account, spend) + } + /// Returns the spend key for this account at the `spend` index, if it does not exceed the /// maximum index. #[inline] pub fn spend_key(&self, spend: H::Index) -> Result<H::SecretKey, Error<H>> { - self.with_bounds_check(spend, Default::default(), |keys, account| { - keys.derive_spend(account, spend) - }) + self.with_spend_bounds_check(spend, Self::derive_spend) + } + + /// Derives the view key for this account at `spend` and `view` without performing bounds + /// checks. + #[inline] + fn derive_view(&self, spend: H::Index, view: H::Index) -> Result<H::SecretKey, H::Error> { + self.keys.derive_view(self.account, spend, view) } /// Returns the view key for this account at `index`, if it does not exceed the maximum index. @@ -261,9 +290,14 @@ where /// do not exceed the maximum indices. #[inline] pub fn view_key_with(&self, spend: H::Index, view: H::Index) -> Result<H::SecretKey, Error<H>> { - self.with_bounds_check(spend, view, |keys, account| { - keys.derive_view(account, spend, view) - }) + self.with_view_bounds_check(spend, view, Self::derive_view) + } + + /// Derives the secret key pair for this account at `spend` and `view` without performing bounds + /// checks. + #[inline] + fn derive(&self, spend: H::Index, view: H::Index) -> Result<SecretKeyPair<H>, H::Error> { + self.keys.derive(self.account, spend, view) } /// Returns the key pair for this account at `index`, if it does not exceed the maximum index. @@ -280,25 +314,30 @@ where spend: H::Index, view: H::Index, ) -> Result<SecretKeyPair<H>, Error<H>> { - self.with_bounds_check(spend, view, |keys, account| { - keys.derive(account, spend, view) - }) + self.with_view_bounds_check(spend, view, Self::derive) } /// Applies `f` to the view keys generated by `self` returning the first non-`None` result with - /// it's spend key index attached, or returns an error if the key derivation failed. + /// it's key index and key attached, or returns an error if the key derivation failed. #[inline] - pub fn find_index<T, F>(&self, mut f: F) -> Result<Option<(H::Index, T)>, H::Error> + pub fn find_index<T, F>( + &self, + mut f: F, + ) -> Result<Option<(Index<H>, SecretKeyPair<H>, T)>, H::Error> where - F: FnMut(H::SecretKey) -> Option<T>, + F: FnMut(&H::SecretKey) -> Option<T>, { let mut index = Index::default(); loop { loop { match self.view_key(index) { - Ok(key) => { - if let Some(value) = f(key) { - return Ok(Some((index.spend, value))); + Ok(view_key) => { + if let Some(value) = f(&view_key) { + return Ok(Some(( + index, + SecretKeyPair::new(self.derive_spend(index.spend)?, view_key), + value, + ))); } } Err(Error::ExceedingCurrentMaximumViewIndex) => break, diff --git a/manta-accounting/src/transfer/batch.rs b/manta-accounting/src/transfer/batch.rs index 285ed5c39..8c0203a10 100644 --- a/manta-accounting/src/transfer/batch.rs +++ b/manta-accounting/src/transfer/batch.rs @@ -72,8 +72,8 @@ where { /// #[inline] - pub fn new<R>( - parameters: &Parameters<C>, + pub fn new<'p, R>( + parameters: Parameters<'p, C>, asset: Asset, spending_key: &SpendingKey<C>, zero_key: K, @@ -86,8 +86,8 @@ where let mut zeroes = Vec::with_capacity(RECEIVERS - 1); for _ in 0..RECEIVERS - 2 { let (receiver, pre_sender) = spending_key.internal_zero_pair( - &parameters.ephemeral_key_commitment_scheme, - &parameters.commitment_scheme, + parameters.ephemeral_key, + parameters.commitment_scheme, rng.gen(), asset.id, ); @@ -98,8 +98,8 @@ where }); } let (receiver, pre_sender) = spending_key.internal_pair( - &parameters.ephemeral_key_commitment_scheme, - &parameters.commitment_scheme, + parameters.ephemeral_key, + parameters.commitment_scheme, rng.gen(), asset, ); @@ -166,9 +166,9 @@ where { /// #[inline] - pub fn next<K, R, S, P>( + pub fn next<'p, K, R, S, P>( &mut self, - parameters: &Parameters<C>, + parameters: Parameters<'p, C>, spending_key: &SpendingKey<C>, zero_key: K, asset_id: AssetId, diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index deed9578a..4df715311 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -18,7 +18,7 @@ use crate::asset::{Asset, AssetId, AssetValue}; use alloc::vec::Vec; -use core::{fmt::Debug, hash::Hash, ops::Add}; +use core::{fmt::Debug, hash::Hash, marker::PhantomData, ops::Add}; use manta_crypto::{ accumulator::{self, Accumulator, MembershipProof, Verifier}, commitment::{self, CommitmentScheme, Input as CommitmentInput}, @@ -355,6 +355,21 @@ pub trait Configuration { .update(secret_key) .commit(cs) } + + /// Checks that the `utxo` is correctly constructed from the `secret_key`, `public_key`, and + /// `asset`, returning the void number for the asset if so. + #[inline] + fn check_full_asset( + parameters: &CommitmentSchemeParameters<Self>, + secret_key: &SecretKey<Self>, + public_key: &PublicKey<Self>, + asset: &Asset, + utxo: &Utxo<Self>, + ) -> Option<VoidNumber<Self>> { + let trapdoor = Self::trapdoor(secret_key, public_key); + (&Self::utxo(parameters, &trapdoor, asset) == utxo) + .then(move || Self::void_number(parameters, &trapdoor, secret_key)) + } } /// Asset Variable Type @@ -476,40 +491,66 @@ pub type ProofInput<C> = <<C as Configuration>::ProofSystem as ProofSystem>::Inp pub type Proof<C> = <ProofSystemType<C> as ProofSystem>::Proof; /// Transfer Parameters -pub struct Parameters<C> +pub struct Parameters<'p, C> where C: Configuration, { /// Ephemeral Key Commitment Scheme Parameters - pub ephemeral_key_commitment_scheme: EphemeralKeyParameters<C>, + pub ephemeral_key: &'p EphemeralKeyParameters<C>, /// Commitment Scheme Parameters - pub commitment_scheme: CommitmentSchemeParameters<C>, + pub commitment_scheme: &'p CommitmentSchemeParameters<C>, /// UTXO Set Verifier Parameters - pub utxo_set_verifier: UtxoSetParameters<C>, + pub utxo_set_verifier: &'p UtxoSetParameters<C>, +} + +impl<'p, C> Parameters<'p, C> +where + C: Configuration, +{ + /// Builds a new [`Parameters`] from `ephemeral_key`, `commitment_scheme`, and + /// `utxo_set_verifier` parameters. + #[inline] + pub fn new( + ephemeral_key: &'p EphemeralKeyParameters<C>, + commitment_scheme: &'p CommitmentSchemeParameters<C>, + utxo_set_verifier: &'p UtxoSetParameters<C>, + ) -> Self { + Self { + ephemeral_key, + commitment_scheme, + utxo_set_verifier, + } + } } /// Transfer Parameters Variables -pub struct ParametersVar<C> +pub struct ParametersVar<'p, C> where C: Configuration, { /// Ephemeral Key Commitment Scheme Parameters - pub ephemeral_key_commitment_scheme: EphemeralKeyParametersVar<C>, + pub ephemeral_key: EphemeralKeyParametersVar<C>, /// Commitment Scheme Parameters pub commitment_scheme: CommitmentSchemeParametersVar<C>, /// UTXO Set Verifier Parameters pub utxo_set_verifier: UtxoSetParametersVar<C>, + + /// Type Parameter Marker + __: PhantomData<&'p ()>, } -impl<C> Variable<C::ConstraintSystem> for ParametersVar<C> +impl<'p, C> Variable<C::ConstraintSystem> for ParametersVar<'p, C> where C: Configuration, + EphemeralKeyParameters<C>: 'p, + CommitmentSchemeParameters<C>: 'p, + UtxoSetParameters<C>: 'p, { - type Type = Parameters<C>; + type Type = Parameters<'p, C>; type Mode = Public; @@ -517,11 +558,10 @@ where fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self { - ephemeral_key_commitment_scheme: this - .ephemeral_key_commitment_scheme - .as_known(cs, mode), + ephemeral_key: this.ephemeral_key.as_known(cs, mode), commitment_scheme: this.commitment_scheme.as_known(cs, mode), utxo_set_verifier: this.utxo_set_verifier.as_known(cs, mode), + __: PhantomData, }, _ => unreachable!(), } @@ -567,16 +607,17 @@ where encrypted_note.decrypt(&self.view) } - /// Validates the `utxo` against `self` and the given `ephemeral_key` and `asset`. + /// Validates the `utxo` against `self` and the given `ephemeral_key` and `asset`, returning + /// the void number if the `utxo` is valid. #[inline] - pub fn validate_utxo( + pub fn check_full_asset( &self, parameters: &CommitmentSchemeParameters<C>, ephemeral_key: &PublicKey<C>, asset: &Asset, utxo: &Utxo<C>, - ) -> bool { - &C::full_utxo(parameters, &self.spend, ephemeral_key, asset) == utxo + ) -> Option<VoidNumber<C>> { + C::check_full_asset(parameters, &self.spend, ephemeral_key, asset, utxo) } /// Prepares `self` for spending `asset` with the given `ephemeral_key`. @@ -760,12 +801,6 @@ where } /// Converts `self` into a [`Sender`] by attaching `proof` to it. - /// - /// # Note - /// - /// When using this method, be sure to check that [`SenderProof::can_upgrade`] returns `true`. - /// Otherwise, using the sender returned here will most likely return an error when posting to - /// the ledger. #[inline] pub fn upgrade(self, proof: SenderProof<C>) -> Sender<C> { Sender { @@ -804,22 +839,7 @@ impl<C> SenderProof<C> where C: Configuration, { - /// Returns `true` if a [`PreSender`] could be upgraded using `self` given the `utxo_set`. - #[inline] - pub fn can_upgrade<S>(&self, utxo_set: &S) -> bool - where - S: Accumulator<Item = Utxo<C>, Verifier = C::UtxoSetVerifier>, - { - self.utxo_membership_proof.matching_output(utxo_set) - } - /// Upgrades the `pre_sender` to a [`Sender`] by attaching `self` to it. - /// - /// # Note - /// - /// When using this method, be sure to check that [`can_upgrade`](Self::can_upgrade) returns - /// `true`. Otherwise, using the sender returned here will most likely return an error when - /// posting to the ledger. #[inline] pub fn upgrade(self, pre_sender: PreSender<C>) -> Sender<C> { pre_sender.upgrade(self) @@ -1204,7 +1224,7 @@ where ) -> AssetVar<C> { let ephemeral_secret_key = C::ephemeral_secret_key_var( cs, - &parameters.ephemeral_key_commitment_scheme, + &parameters.ephemeral_key, &self.ephemeral_key_trapdoor, &self.asset, ); @@ -1479,7 +1499,7 @@ where /// Generates a proving and verifying context for this transfer shape. #[inline] pub fn generate_context<R>( - parameters: &Parameters<C>, + parameters: Parameters<C>, rng: &mut R, ) -> Result<(ProvingContext<C>, VerifyingContext<C>), ProofSystemError<C>> where @@ -1493,9 +1513,9 @@ where /// Converts `self` into its ledger post. #[inline] - pub fn into_post<R>( + pub fn into_post<'p, R>( self, - parameters: &Parameters<C>, + parameters: Parameters<'p, C>, context: &ProvingContext<C>, rng: &mut R, ) -> Result<TransferPost<C>, ProofSystemError<C>> diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs index 53e1a3a2e..07e7a49a0 100644 --- a/manta-accounting/src/wallet/ledger.rs +++ b/manta-accounting/src/wallet/ledger.rs @@ -16,9 +16,6 @@ //! Ledger Source -// TODO: Move to asynchronous streaming model so we can process some of the data as it is incoming. -// TODO: Add non-atomic transactions. See similar comment in `crate::wallet::signer`. - use crate::transfer::{Configuration, EncryptedNote, TransferPost, Utxo, VoidNumber}; use alloc::vec::Vec; use core::future::Future; @@ -40,22 +37,17 @@ where /// Ledger State Checkpoint Type type Checkpoint: Checkpoint; - /// Receiver Data Iterator Type - type ReceiverData: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>; + /// Receiver Chunk Iterator Type + type ReceiverChunk: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>; - /// Sender Data Iterator Type - type SenderData: IntoIterator<Item = VoidNumber<C>>; + /// Sender Chunk Iterator Type + type SenderChunk: IntoIterator<Item = VoidNumber<C>>; /// Pull Future Type /// /// Future for the [`pull`](Self::pull) method. type PullFuture: Future<Output = PullResult<C, Self>>; - /// Pull All Future Type - /// - /// Future for the [`pull_all`](Self::pull_all) method. - type PullAllFuture: Future<Output = PullAllResult<C, Self>>; - /// Push Future Type /// /// Future for the [`push`](Self::push) method. @@ -68,11 +60,8 @@ where /// [`Checkpoint`](Self::Checkpoint). fn pull(&self, checkpoint: &Self::Checkpoint) -> Self::PullFuture; - /// Pulls all data from the ledger, returning the current [`Checkpoint`](Self::Checkpoint). - fn pull_all(&self) -> Self::PullAllFuture; - - /// Sends `posts` to the ledger to be appended atomically, returning `false` if the posts were - /// invalid. + /// Sends `posts` to the ledger, returning `true` or `false` depending on whether the entire + /// transaction succeeded or not. fn push(&self, posts: Vec<TransferPost<C>>) -> Self::PushFuture; } @@ -81,15 +70,10 @@ where /// See the [`pull`](Connection::pull) method on [`Connection`] for more information. pub type PullResult<C, L> = Result<PullResponse<C, L>, <L as Connection<C>>::Error>; -/// Ledger Source Pull All Result -/// -/// See the [`pull_all`](Connection::pull_all) method on [`Connection`] for more information. -pub type PullAllResult<C, L> = Result<PullAllResponse<C, L>, <L as Connection<C>>::Error>; - /// Ledger Source Push Result /// /// See the [`push`](Connection::push) method on [`Connection`] for more information. -pub type PushResult<C, L> = Result<PushResponse<C, L>, <L as Connection<C>>::Error>; +pub type PushResult<C, L> = Result<PushResponse, <L as Connection<C>>::Error>; /// Ledger Source Pull Response /// @@ -103,41 +87,18 @@ where /// Current Ledger Checkpoint pub checkpoint: L::Checkpoint, - /// Ledger Receiver Data - pub receiver_data: L::ReceiverData, -} - -/// Ledger Source Pull All Response -/// -/// This `struct` is created by the [`pull_all`](Connection::pull_all) method on [`Connection`]. -/// See its documentation for more. -pub struct PullAllResponse<C, L> -where - C: Configuration, - L: Connection<C> + ?Sized, -{ - /// Current Ledger Checkpoint - pub checkpoint: L::Checkpoint, - - /// Ledger Receiver Data - pub receiver_data: L::ReceiverData, + /// Ledger Receiver Chunk + pub receivers: L::ReceiverChunk, - /// Ledger Sender Data - pub sender_data: L::SenderData, + /// Ledger Sender Chunk + pub senders: L::SenderChunk, } /// Ledger Source Push Response /// /// This `struct` is created by the [`push`](Connection::push) method on [`Connection`]. /// See its documentation for more. -pub struct PushResponse<C, L> -where - C: Configuration, - L: Connection<C> + ?Sized, -{ - /// Current Ledger Checkpoint - pub checkpoint: L::Checkpoint, - - /// Successful Push +pub struct PushResponse { + /// Whether or not the Transaction Succeeded in Full pub success: bool, } diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 1ad39ae3a..51fb2b2fc 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -37,8 +37,9 @@ use crate::{ canonical::{ Mint, PrivateTransfer, PrivateTransferShape, Reclaim, Selection, Shape, Transaction, }, - EncryptedNote, Parameters, PreSender, ProofSystemError, ProvingContext, PublicKey, - Receiver, ReceivingKey, SecretKey, Sender, SpendingKey, Transfer, TransferPost, Utxo, + CommitmentSchemeParameters, EncryptedNote, EphemeralKeyParameters, Parameters, PreSender, + ProofSystemError, ProvingContext, PublicKey, Receiver, ReceivingKey, SecretKey, Sender, + SpendingKey, Transfer, TransferPost, Utxo, VoidNumber, }, }; use alloc::{vec, vec::Vec}; @@ -63,22 +64,12 @@ use manta_util::{fallible_array_map, into_array_unchecked, iter::IteratorExt}; /// Rollback Trait pub trait Rollback { - /// Commits `self` to the current state. - /// - /// # Implementation Note - /// - /// Commiting to the current state must be idempotent. Calling [`rollback`](Self::rollback) - /// after [`commit`](Self::commit) must not change the state after the call to - /// [`commit`](Self::commit). - fn commit(&mut self); - /// Rolls back `self` to the previous state. /// /// # Implementation Note /// - /// Rolling back to the previous state must be idempotent. Calling [`commit`](Self::commit) - /// after [`rollback`](Self::rollback) must not change the state after the call to - /// [`rollback`](Self::rollback). + /// Rolling back to the previous state must be idempotent, i.e. two consecutive calls to + /// [`rollback`](Self::rollback) should do the same as one call. fn rollback(&mut self); } @@ -98,16 +89,6 @@ where /// Future for the [`sign`](Self::sign) method. type SignFuture: Future<Output = SignResult<H, C, Self>>; - /// Sign Commit Future Type - /// - /// Future for the [`commit`](Self::commit) method. - type CommitFuture: Future<Output = Result<(), Self::Error>>; - - /// Sign Rollback Future Type - /// - /// Future for the [`rollback`](Self::rollback) method. - type RollbackFuture: Future<Output = Result<(), Self::Error>>; - /// Receiving Key Future Type /// /// Future for the [`receiving_key`](Self::receiving_key) method. @@ -117,63 +98,33 @@ where type Error; /// Pushes updates from the ledger to the wallet, synchronizing it with the ledger state and - /// returning an updated asset distribution. Depending on the `sync_state`, the signer will - /// either commit to the current state before synchronizing or rollback to the previous state. - fn sync<I>( + /// returning an updated asset distribution. + fn sync<I, R>( &mut self, - sync_state: SyncState, - starting_index: usize, - updates: I, + insert_starting_index: usize, + inserts: I, + removes: R, ) -> Self::SyncFuture where - I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>; + I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, + R: IntoIterator<Item = VoidNumber<C>>; /// Signs a `transaction` and returns the ledger transfer posts if successful. /// /// # Safety /// - /// To preserve consistency, calls to [`sign`](Self::sign) should be followed by a call to - /// either [`commit`](Self::commit), [`rollback`](Self::rollback), or [`sync`](Self::sync) with - /// the appropriate [`SyncState`]. Repeated calls to [`sign`](Self::sign) should automatically - /// commit the current state before signing. - /// - /// See the [`Rollback`] trait for expectations on the behavior of [`commit`](Self::commit) - /// and [`rollback`](Self::rollback). + /// The caller of this method should call [`finish`](Self::finish) once the posts have been + /// returned from the ledger to preserve the signer's internal state. fn sign(&mut self, transaction: Transaction<C>) -> Self::SignFuture; - /// Commits to the state after the last call to [`sign`](Self::sign). - /// - /// See the [`Rollback`] trait for expectations on the behavior of [`commit`](Self::commit). - fn commit(&mut self) -> Self::CommitFuture; - - /// Rolls back to the state before the last call to [`sign`](Self::sign). - /// - /// See the [`Rollback`] trait for expectations on the behavior of [`rollback`](Self::rollback). - fn rollback(&mut self) -> Self::RollbackFuture; - /// Returns a [`ReceivingKey`] for `self` to receive assets with `index`. /// - /// # Note + /// # Safety /// - /// This method does not interact with the other methods on [`Connection`] so it can be called - /// at any point in between calls to [`sync`](Self::sync), [`sign`](Self::sign), and other - /// rollback related methods. + /// This method can be called at any point, since it is independent of the signing state. fn receiving_key(&mut self, index: key::Index<H>) -> Self::ReceivingKeyFuture; } -/// Synchronization State -/// -/// This `enum` is used by the [`sync`](Connection::sync) method on [`Connection`]. See its -/// documentation for more. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum SyncState { - /// Should commit the current state before synchronizing - Commit, - - /// Should rollback to the previous state before synchronizing - Rollback, -} - /// Synchronization Result /// /// See the [`sync`](Connection::sync) method on [`Connection`] for more. @@ -278,7 +229,6 @@ pub trait Configuration: transfer::Configuration { Item = <Self::UtxoSetVerifier as Verifier>::Item, Verifier = Self::UtxoSetVerifier, > + ConstantCapacityAccumulator - + Default + ExactSizeAccumulator + OptimizedAccumulator + Rollback; @@ -309,46 +259,6 @@ pub type AssetMapKey<C> = (SpendIndex<C>, PublicKey<C>); /// Account Table Type pub type AccountTable<C> = key::AccountTable<<C as Configuration>::HierarchicalKeyDerivationScheme>; -/// Pending Asset Map -#[derive(derivative::Derivative)] -#[derivative(Default(bound = ""))] -struct PendingAssetMap<C> -where - C: Configuration, -{ - /// Pending Insert Data - insert: Option<(AssetMapKey<C>, Asset)>, - - /// Pending Insert Zeroes Data - insert_zeroes: Option<(AssetId, Vec<AssetMapKey<C>>)>, - - /// Pending Remove Data - remove: Vec<AssetMapKey<C>>, -} - -impl<C> PendingAssetMap<C> -where - C: Configuration, -{ - /// Commits the pending asset map data to `assets`. - #[inline] - fn commit(&mut self, assets: &mut C::AssetMap) { - if let Some((key, asset)) = self.insert.take() { - assets.insert(key, asset); - } - if let Some((asset_id, zeroes)) = self.insert_zeroes.take() { - assets.insert_zeroes(asset_id, zeroes); - } - assets.remove_all(mem::take(&mut self.remove)); - } - - /// Clears the pending asset map. - #[inline] - fn rollback(&mut self) { - *self = Default::default(); - } -} - /// Signer pub struct Signer<C> where @@ -357,21 +267,21 @@ where /// Account Table account_table: AccountTable<C>, - /// Transfer Parameters, - parameters: Parameters<C>, - /// Proving Context proving_context: ProvingContext<C>, + /// Ephemeral Key Parameters + ephemeral_key_parameters: EphemeralKeyParameters<C>, + + /// Commitment Scheme Parameters + commitment_scheme_parameters: CommitmentSchemeParameters<C>, + /// UTXO Set utxo_set: C::UtxoSet, /// Asset Distribution assets: C::AssetMap, - /// Pending Asset Distribution - pending_assets: PendingAssetMap<C>, - /// Random Number Generator rng: C::Rng, } @@ -384,20 +294,20 @@ where #[inline] fn new_inner( account_table: AccountTable<C>, - parameters: Parameters<C>, proving_context: ProvingContext<C>, + ephemeral_key_parameters: EphemeralKeyParameters<C>, + commitment_scheme_parameters: CommitmentSchemeParameters<C>, utxo_set: C::UtxoSet, assets: C::AssetMap, - pending_assets: PendingAssetMap<C>, rng: C::Rng, ) -> Self { Self { account_table, - parameters, proving_context, + ephemeral_key_parameters, + commitment_scheme_parameters, utxo_set, assets, - pending_assets, rng, } } @@ -411,16 +321,18 @@ where #[inline] pub fn new( account_table: AccountTable<C>, - parameters: Parameters<C>, proving_context: ProvingContext<C>, + ephemeral_key_parameters: EphemeralKeyParameters<C>, + commitment_scheme_parameters: CommitmentSchemeParameters<C>, + utxo_set: C::UtxoSet, rng: C::Rng, ) -> Self { Self::new_inner( account_table, - parameters, proving_context, - Default::default(), - Default::default(), + ephemeral_key_parameters, + commitment_scheme_parameters, + utxo_set, Default::default(), rng, ) @@ -436,39 +348,43 @@ where /// Inserts the new `utxo`-`encrypted_note` pair if a known key can decrypt the note and /// validate the utxo. #[inline] - fn insert_update_item( + fn insert_next_item( &mut self, utxo: Utxo<C>, encrypted_note: EncryptedNote<C>, + void_numbers: &mut Vec<VoidNumber<C>>, assets: &mut Vec<Asset>, ) -> Result<(), Error<C::HierarchicalKeyDerivationScheme, C>> { - let mut encrypted_note = Some(encrypted_note); + let mut finder = DecryptedMessage::find(encrypted_note); if let Some(( - spend_index, + index, + key, DecryptedMessage { plaintext: asset, ephemeral_public_key, }, )) = self .account() - .find_index(move |k| DecryptedMessage::try_new(&mut encrypted_note, &k)) + .find_index(move |k| finder.decrypt(k)) .map_err(key::Error::KeyDerivationError)? { - /* TODO: - if self.get_spending_key(spend_index).validate_utxo( - &self.parameters.commitment_scheme, + if let Some(void_number) = C::check_full_asset( + &self.commitment_scheme_parameters, + &key.spend, &ephemeral_public_key, &asset, &utxo, ) { - assets.push(asset); - self.assets - .insert((spend_index, ephemeral_public_key), asset); - self.utxo_set.insert(&utxo); - return Ok(()); + if let Some(index) = void_numbers.iter().position(move |v| v == &void_number) { + void_numbers.remove(index); + } else { + assets.push(asset); + self.assets + .insert((index.spend, ephemeral_public_key), asset); + self.utxo_set.insert(&utxo); + return Ok(()); + } } - */ - todo!() } self.utxo_set.insert_nonprovable(&utxo); Ok(()) @@ -476,36 +392,49 @@ where /// Updates the internal ledger state, returning the new asset distribution. #[inline] - fn sync_with_updates<I>( + fn sync_with<I>( &mut self, - updates: I, + inserts: I, + mut void_numbers: Vec<VoidNumber<C>>, ) -> SyncResult<C::HierarchicalKeyDerivationScheme, C, Self> where I: Iterator<Item = (Utxo<C>, EncryptedNote<C>)>, { + // TODO: Do this loop in parallel. let mut assets = Vec::new(); - for (utxo, encrypted_note) in updates { - self.insert_update_item(utxo, encrypted_note, &mut assets)?; + for (utxo, encrypted_note) in inserts { + self.insert_next_item(utxo, encrypted_note, &mut void_numbers, &mut assets)?; } + // FIXME: Do we need to check the void numbers which survived the above loop? Ok(SyncResponse::new(assets)) } /// Updates the internal ledger state, returning the new asset distribution. #[inline] - pub fn sync<I>( + pub fn sync<I, R>( &mut self, - sync_state: SyncState, - starting_index: usize, - updates: I, + insert_starting_index: usize, + inserts: I, + removes: R, ) -> SyncResult<C::HierarchicalKeyDerivationScheme, C, Self> where I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, + R: IntoIterator<Item = VoidNumber<C>>, { - self.start_sync(sync_state); - - // FIXME: Do a capacity check on the current UTXO set. - match self.utxo_set.len().checked_sub(starting_index) { - Some(diff) => self.sync_with_updates(updates.into_iter().skip(diff)), + // FIXME: Do a capacity check on the current UTXO set? + // + // if self.utxo_set.capacity() < starting_index { + // panic!("something is very wrong here") + // } + // + // TODO: Use a smarter object than `Vec` for `removes.into_iter().collect()` like a + // `HashSet` or some other set-like container with fast membership and remove ops. + + match self.utxo_set.len().checked_sub(insert_starting_index) { + Some(diff) => self.sync_with( + inserts.into_iter().skip(diff), + removes.into_iter().collect(), + ), _ => Err(Error::InconsistentSynchronization), } } @@ -518,32 +447,35 @@ where ephemeral_key: PublicKey<C>, asset: Asset, ) -> Result<PreSender<C>, Error<C::HierarchicalKeyDerivationScheme, C>> { - /* TODO: - Ok(self.account().spend_key(spend_index)?.pre_sender( - &self.parameters.commitment_scheme, + let spend_key = self.account().spend_key(spend_index)?; + Ok(PreSender::new( + &self.commitment_scheme_parameters, + spend_key, ephemeral_key, asset, )) - */ - todo!() } /// Builds the pre-receiver associated to `spend_index`, `view_index`, and `asset`. #[inline] fn build_receiver( - &self, + &mut self, spend_index: SpendIndex<C>, view_index: ViewIndex<C>, asset: Asset, ) -> Result<Receiver<C>, Error<C::HierarchicalKeyDerivationScheme, C>> { - /* TODO: let keypair = self .account() .keypair_with(spend_index, view_index)? .derive::<C::KeyAgreementScheme>(); - Ok(PreReceiver::new(keypair.spend, keypair.view, asset)) - */ - todo!() + Ok(Receiver::new( + &self.ephemeral_key_parameters, + &self.commitment_scheme_parameters, + self.rng.gen(), + keypair.spend, + keypair.view, + asset, + )) } /// Selects the pre-senders which collectively own at least `asset`, returning any change. @@ -556,7 +488,6 @@ where if selection.is_empty() { return Err(Error::InsufficientBalance(asset)); } - self.pending_assets.remove = selection.keys().cloned().collect(); Selection::new(selection, move |(i, ek), v| { self.build_pre_sender(i, ek, asset.id.with(v)) }) @@ -573,18 +504,17 @@ where &mut self, transfer: Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>, ) -> Result<TransferPost<C>, Error<C::HierarchicalKeyDerivationScheme, C>> { - /* TODO: transfer .into_post( - &self.parameters.ephemeral_key_commitment_scheme, - &self.parameters.commitment_scheme, - self.utxo_set.verifier(), + Parameters::new( + &self.ephemeral_key_parameters, + &self.commitment_scheme_parameters, + self.utxo_set.parameters(), + ), &self.proving_context, &mut self.rng, ) .map_err(Error::ProofSystemError) - */ - todo!() } /* TODO: @@ -683,11 +613,6 @@ where } } - self.pending_assets.insert_zeroes = Some(( - asset_id, - new_zeroes.into_iter().map(move |z| z.index).collect(), - )); - if needed_mints == 0 { return Ok(()); } @@ -704,6 +629,14 @@ where } */ + /// + #[inline] + fn compute_batched_transaction<const SENDERS: usize, const RECEIVERS: usize>( + &mut self, + ) -> Result<[Sender<C>; SENDERS], Error<C::HierarchicalKeyDerivationScheme, C>> { + todo!() + } + /// Returns the next change receiver for `asset`. #[inline] fn next_change( @@ -711,22 +644,16 @@ where asset_id: AssetId, change: AssetValue, ) -> Result<Receiver<C>, Error<C::HierarchicalKeyDerivationScheme, C>> { - let asset = asset_id.with(change); let default_index = Default::default(); - let receiver = self.build_receiver(default_index, default_index, asset)?; - self.pending_assets.insert = Some(( - (default_index, receiver.ephemeral_public_key().clone()), - asset, - )); - Ok(receiver) + self.build_receiver(default_index, default_index, asset_id.with(change)) } /// Prepares a given [`ReceivingKey`] for receiving `asset`. #[inline] pub fn prepare_receiver(&mut self, asset: Asset, receiver: ReceivingKey<C>) -> Receiver<C> { receiver.into_receiver( - &self.parameters.ephemeral_key_commitment_scheme, - &self.parameters.commitment_scheme, + &self.ephemeral_key_parameters, + &self.commitment_scheme_parameters, self.rng.gen(), asset, ) @@ -734,7 +661,7 @@ where /// Signs a withdraw transaction without resetting on error. #[inline] - fn sign_withdraw_without_rollback( + fn sign_withdraw( &mut self, asset: Asset, receiver: Option<ReceivingKey<C>>, @@ -754,53 +681,31 @@ where )?; */ + let senders = self.compute_batched_transaction::<SENDERS, RECEIVERS>()?; let change = self.next_change(asset.id, selection.change)?; - let final_post = match receiver { Some(receiver) => { let receiver = self.prepare_receiver(asset, receiver); - // self.build_post(PrivateTransfer::build(senders, [change, receiver]))? - todo!() - } - _ => { - // self.build_post(Reclaim::build(senders, [change], asset))? - todo!() + self.build_post(PrivateTransfer::build(senders, [change, receiver]))? } + _ => self.build_post(Reclaim::build(senders, [change], asset))?, }; - - // posts.push(final_post); - + posts.push(final_post); + self.utxo_set.rollback(); Ok(SignResponse::new(posts)) } - /// Signs a withdraw transaction, resetting the internal state on an error. - #[inline] - fn sign_withdraw( - &mut self, - asset: Asset, - receiver: Option<ReceivingKey<C>>, - ) -> SignResult<C::HierarchicalKeyDerivationScheme, C, Self> { - let result = self.sign_withdraw_without_rollback(asset, receiver); - if result.is_err() { - self.rollback(); - } - result - } - /// Signs the `transaction`, generating transfer posts. #[inline] pub fn sign( &mut self, transaction: Transaction<C>, ) -> SignResult<C::HierarchicalKeyDerivationScheme, C, Self> { - self.commit(); match transaction { Transaction::Mint(asset) => { let default_index = Default::default(); let receiver = self.build_receiver(default_index, default_index, asset)?; - let ephemeral_public_key = receiver.ephemeral_public_key().clone(); let mint_post = self.build_post(Mint::build(asset, receiver))?; - self.pending_assets.insert = Some(((default_index, ephemeral_public_key), asset)); Ok(SignResponse::new(vec![mint_post])) } Transaction::PrivateTransfer(asset, receiver) => { @@ -810,39 +715,18 @@ where } } - /// Commits to the state after the last call to [`sign`](Self::sign). - #[inline] - pub fn commit(&mut self) { - self.utxo_set.commit(); - self.pending_assets.commit(&mut self.assets); - } - - /// Rolls back to the state before the last call to [`sign`](Self::sign). - #[inline] - pub fn rollback(&mut self) { - self.utxo_set.rollback(); - self.pending_assets.rollback(); - } - - /// Commits or rolls back the state depending on the value of `sync_state`. - #[inline] - pub fn start_sync(&mut self, sync_state: SyncState) { - match sync_state { - SyncState::Commit => self.commit(), - SyncState::Rollback => self.rollback(), - } - } - /// Returns a [`ReceivingKey`] for `self` to receive assets with `index`. #[inline] pub fn receiving_key( &mut self, index: Index<C>, ) -> ReceivingKeyResult<C::HierarchicalKeyDerivationScheme, C, Self> { + /* let _ = self .account() .keypair(index)? .derive::<C::KeyAgreementScheme>(); + */ todo!() } } @@ -855,26 +739,23 @@ where type SignFuture = Ready<SignResult<C::HierarchicalKeyDerivationScheme, C, Self>>; - type CommitFuture = Ready<Result<(), Self::Error>>; - - type RollbackFuture = Ready<Result<(), Self::Error>>; - type ReceivingKeyFuture = Ready<ReceivingKeyResult<C::HierarchicalKeyDerivationScheme, C, Self>>; type Error = Infallible; #[inline] - fn sync<I>( + fn sync<I, R>( &mut self, - sync_state: SyncState, - starting_index: usize, - updates: I, + insert_starting_index: usize, + inserts: I, + removes: R, ) -> Self::SyncFuture where I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, + R: IntoIterator<Item = VoidNumber<C>>, { - future::ready(self.sync(sync_state, starting_index, updates)) + future::ready(self.sync(insert_starting_index, inserts, removes)) } #[inline] @@ -882,22 +763,6 @@ where future::ready(self.sign(transaction)) } - #[inline] - fn commit(&mut self) -> Self::CommitFuture { - future::ready({ - self.commit(); - Ok(()) - }) - } - - #[inline] - fn rollback(&mut self) -> Self::RollbackFuture { - future::ready({ - self.rollback(); - Ok(()) - }) - } - #[inline] fn receiving_key(&mut self, index: Index<C>) -> Self::ReceivingKeyFuture { future::ready(self.receiving_key(index)) @@ -1274,9 +1139,6 @@ where /// Asset Distribution assets: C::AssetMap, - /// Pending Asset Distribution - pending_assets: PendingAssetMap<C::DerivedSecretKeyGenerator>, - /// Random Number Generator rng: C::Rng, } diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index bcb092c4b..ea93fed54 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -25,7 +25,7 @@ use crate::{ }, wallet::{ ledger::{self, Checkpoint, PullResponse, PushResponse}, - signer::{self, SignResponse, SyncState}, + signer::{self, SignResponse}, }, }; use alloc::{ @@ -176,9 +176,6 @@ where /// Signer Connection signer: S, - /// Signer Synchronization State - sync_state: SyncState, - /// Balance State assets: B, @@ -196,16 +193,9 @@ where { /// Builds a new [`Wallet`]. #[inline] - pub fn new( - signer: S, - sync_state: SyncState, - ledger: L, - checkpoint: L::Checkpoint, - assets: B, - ) -> Self { + pub fn new(signer: S, ledger: L, checkpoint: L::Checkpoint, assets: B) -> Self { Self { signer, - sync_state, ledger, checkpoint, assets, @@ -239,7 +229,8 @@ where // recovery mode, like starting from the beginning of the state? let PullResponse { checkpoint, - receiver_data, + receivers, + senders, } = self .ledger .pull(&self.checkpoint) @@ -247,15 +238,10 @@ where .map_err(Error::LedgerError)?; self.assets.deposit_all( self.signer - .sync( - self.sync_state, - self.checkpoint.receiver_index(), - receiver_data, - ) + .sync(self.checkpoint.receiver_index(), receivers, senders) .await? .assets, ); - self.sync_state = SyncState::Commit; self.checkpoint = checkpoint; Ok(()) } @@ -273,22 +259,6 @@ where transaction.check(move |a| self.contains(a)) } - /// Tries to commit to the current signer state. - #[inline] - async fn try_commit(&mut self) { - if self.signer.commit().await.is_err() { - self.sync_state = SyncState::Commit; - } - } - - /// Tries to rollback to the previous signer state. - #[inline] - async fn try_rollback(&mut self) { - if self.signer.rollback().await.is_err() { - self.sync_state = SyncState::Rollback; - } - } - /// Posts a transaction to the ledger, returning `true` if the `transaction` was successfully /// saved onto the ledger. This method automatically synchronizes with the ledger before /// posting. To amortize the cost of future calls to [`post`](Self::post), the @@ -308,32 +278,20 @@ where #[inline] pub async fn post(&mut self, transaction: Transaction<C>) -> Result<bool, Error<H, C, L, S>> { self.sync().await?; - let balance_update = self + let transaction_kind = self .check(&transaction) .map_err(Error::InsufficientBalance)?; let SignResponse { posts } = self.signer.sign(transaction).await?; match self.ledger.push(posts).await { - Ok(PushResponse { - checkpoint, - success: true, - }) => { - self.try_commit().await; - match balance_update { + Ok(PushResponse { success: true }) => { + match transaction_kind { TransactionKind::Deposit(asset) => self.assets.deposit(asset), TransactionKind::Withdraw(asset) => self.assets.withdraw_unchecked(asset), } - self.checkpoint = checkpoint; Ok(true) } - Ok(PushResponse { success: false, .. }) => { - // FIXME: What about the checkpoint returned in the response? - self.try_rollback().await; - Ok(false) - } - Err(err) => { - self.try_rollback().await; - Err(Error::LedgerError(err)) - } + Ok(PushResponse { success: false }) => Ok(false), + Err(err) => Err(Error::LedgerError(err)), } } diff --git a/manta-crypto/src/accumulator.rs b/manta-crypto/src/accumulator.rs index 7b713f9cc..8574bd2da 100644 --- a/manta-crypto/src/accumulator.rs +++ b/manta-crypto/src/accumulator.rs @@ -69,6 +69,15 @@ pub trait Accumulator { /// `false` if the maximum capacity of the accumulator would be exceeded by inserting `item`. fn insert(&mut self, item: &Self::Item) -> bool; + /// Returns `true` whenever `fst` and `snd` can be inserted in any order into the accumulator. + /// This method should return `false`, the worst-case result, in the case that the insertion + /// order is unknown or unspecified. + #[inline] + fn are_independent(&self, fst: &Self::Item, snd: &Self::Item) -> bool { + let _ = (fst, snd); + false + } + /// Returns a membership proof for `item` if it is contained in `self`. fn prove(&self, item: &Self::Item) -> Option<MembershipProof<Self::Verifier>>; diff --git a/manta-crypto/src/encryption.rs b/manta-crypto/src/encryption.rs index 59028a76b..8805a089b 100644 --- a/manta-crypto/src/encryption.rs +++ b/manta-crypto/src/encryption.rs @@ -211,16 +211,48 @@ where } } - /// Tries to decrypt `encrypted_message` with `secret_key`, if the `Option` contains a message. + /// Builds a new [`DecryptionFinder`] for `encrypted_message`. Use [`DecryptionFinder::decrypt`] + /// to try and decrypt the message. #[inline] - pub fn try_new( - encrypted_message: &mut Option<EncryptedMessage<H>>, - secret_key: &SecretKey<H>, - ) -> Option<Self> { - if let Some(message) = encrypted_message.take() { + pub fn find(encrypted_message: EncryptedMessage<H>) -> DecryptionFinder<H> { + DecryptionFinder::new(encrypted_message) + } +} + +/// Decryption Finder +pub struct DecryptionFinder<H> +where + H: HybridPublicKeyEncryptionScheme, +{ + /// Encrypted Message + encrypted_message: Option<EncryptedMessage<H>>, +} + +impl<H> DecryptionFinder<H> +where + H: HybridPublicKeyEncryptionScheme, +{ + /// Builds a new [`DecryptionFinder`] for `encrypted_message`. + #[inline] + pub fn new(encrypted_message: EncryptedMessage<H>) -> Self { + Self { + encrypted_message: Some(encrypted_message), + } + } + + /// Returns `true` if the decryption was found. + #[inline] + pub fn found(&self) -> bool { + self.encrypted_message.is_none() + } + + /// Tries to decrypt with `secret_key`, if `self` still contains a message. + #[inline] + pub fn decrypt(&mut self, secret_key: &SecretKey<H>) -> Option<DecryptedMessage<H>> { + if let Some(message) = self.encrypted_message.take() { match message.decrypt(secret_key) { Ok(decrypted_message) => return Some(decrypted_message), - Err(message) => *encrypted_message = Some(message), + Err(message) => self.encrypted_message = Some(message), } } None diff --git a/manta-crypto/src/merkle_tree/forest.rs b/manta-crypto/src/merkle_tree/forest.rs index 15411246f..b8eb33117 100644 --- a/manta-crypto/src/merkle_tree/forest.rs +++ b/manta-crypto/src/merkle_tree/forest.rs @@ -29,13 +29,14 @@ use crate::{ WithProofs, }, }; +use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use manta_util::into_array_unchecked; /// Merkle Forest Configuration pub trait Configuration: tree::Configuration { /// Tree Index Type - type Index; + type Index: PartialEq; /// Returns the index of the merkle tree where `leaf` should be inserted. /// @@ -259,6 +260,11 @@ where .push_provable(&self.parameters, item) } + #[inline] + fn are_independent(&self, fst: &Self::Item, snd: &Self::Item) -> bool { + C::tree_index(fst) != C::tree_index(snd) + } + #[inline] fn prove(&self, item: &Self::Item) -> Option<MembershipProof<Self::Verifier>> { let tree = self.forest.get_tree(item); From e1981c36bea6703144953414c6a1e5188cf5ceac Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 20 Dec 2021 18:57:31 -0500 Subject: [PATCH 141/275] wip: implement batching system --- manta-accounting/src/key.rs | 45 +- manta-accounting/src/transfer/batch.rs | 187 ++---- manta-accounting/src/transfer/canonical.rs | 37 +- manta-accounting/src/transfer/mod.rs | 5 +- manta-accounting/src/wallet/signer.rs | 647 ++++----------------- manta-accounting/src/wallet/state.rs | 2 +- 6 files changed, 184 insertions(+), 739 deletions(-) diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index a72317463..55deaa70b 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -18,7 +18,6 @@ use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; -use manta_crypto::key::KeyAgreementScheme; /// Hierarchical Key Derivation Parameter pub trait HierarchicalKeyDerivationParameter: @@ -125,30 +124,6 @@ where pub fn new(spend: H::SecretKey, view: H::SecretKey) -> Self { Self { spend, view } } - - /// Derives the public key pair for `self`. - #[inline] - pub fn derive<K>(self) -> PublicKeyPair<K> - where - K: KeyAgreementScheme<SecretKey = H::SecretKey>, - { - PublicKeyPair { - spend: K::derive_owned(self.spend), - view: K::derive_owned(self.view), - } - } -} - -/// Public Key Pair -pub struct PublicKeyPair<K> -where - K: KeyAgreementScheme, -{ - /// Spend Part of the Key Pair - pub spend: K::PublicKey, - - /// View Part of the Key Pair - pub view: K::PublicKey, } /// Error Type @@ -266,6 +241,12 @@ where self.keys.derive_spend(self.account, spend) } + /// Returns the default spend key for this account. + #[inline] + pub fn default_spend_key(&self) -> Result<H::SecretKey, H::Error> { + self.derive_spend(Default::default()) + } + /// Returns the spend key for this account at the `spend` index, if it does not exceed the /// maximum index. #[inline] @@ -280,6 +261,13 @@ where self.keys.derive_view(self.account, spend, view) } + /// Returns the default view key for this account. + #[inline] + pub fn default_view_key(&self) -> Result<H::SecretKey, H::Error> { + let default_index = Default::default(); + self.derive_view(default_index, default_index) + } + /// Returns the view key for this account at `index`, if it does not exceed the maximum index. #[inline] pub fn view_key(&self, index: Index<H>) -> Result<H::SecretKey, Error<H>> { @@ -300,6 +288,13 @@ where self.keys.derive(self.account, spend, view) } + /// Returns the default secret key pair for this account. + #[inline] + pub fn default_keypair(&self) -> Result<SecretKeyPair<H>, H::Error> { + let default_index = Default::default(); + self.derive(default_index, default_index) + } + /// Returns the key pair for this account at `index`, if it does not exceed the maximum index. #[inline] pub fn keypair(&self, index: Index<H>) -> Result<SecretKeyPair<H>, Error<H>> { diff --git a/manta-accounting/src/transfer/batch.rs b/manta-accounting/src/transfer/batch.rs index 8c0203a10..05082037c 100644 --- a/manta-accounting/src/transfer/batch.rs +++ b/manta-accounting/src/transfer/batch.rs @@ -19,8 +19,9 @@ use crate::{ asset::{Asset, AssetId, AssetValue}, transfer::{ - Configuration, Parameters, PreSender, ProofSystemError, Receiver, ReceivingKey, Sender, - SpendingKey, Transfer, TransferPost, Utxo, + CommitmentSchemeParameters, Configuration, EphemeralKeyParameters, Parameters, PreSender, + ProofSystemError, Receiver, ReceivingKey, Sender, SpendingKey, Transfer, TransferPost, + Utxo, }, }; use alloc::vec; @@ -34,190 +35,68 @@ use manta_util::{ iter::{ChunkBy, IteratorExt}, }; -/// Secret Transfer -pub type SecretTransfer<C, const SENDERS: usize, const RECEIVERS: usize> = - Transfer<C, 0, { SENDERS }, { RECEIVERS }, 0>; - -/// Zero Coin Pre-Sender -pub struct Zero<C, K> -where - C: Configuration, -{ - /// Spend Access Key - key: K, - - /// Pre-Sender - pre_sender: PreSender<C>, -} - /// Batch Join Structure -pub struct Join<C, K, const RECEIVERS: usize> +pub struct Join<C> where C: Configuration, { - /// Receivers - receivers: [Receiver<C>; RECEIVERS], + /// Accumulated Balance Pre-Sender + pub pre_sender: PreSender<C>, /// Zero Coin Pre-Senders - zeroes: Vec<Zero<C, K>>, - - /// Accumulated Balance Pre-Sender - pre_sender: PreSender<C>, + pub zeroes: Vec<PreSender<C>>, } -impl<C, K, const RECEIVERS: usize> Join<C, K, RECEIVERS> +impl<C> Join<C> where C: Configuration, - K: Clone, { - /// + /// Builds a new [`Join`] for `asset` using `spending_key` and `zero_key`. #[inline] - pub fn new<'p, R>( - parameters: Parameters<'p, C>, + pub fn new<R, const RECEIVERS: usize>( + ephemeral_key_parameters: &EphemeralKeyParameters<C>, + commitment_scheme_parameters: &CommitmentSchemeParameters<C>, asset: Asset, spending_key: &SpendingKey<C>, - zero_key: K, rng: &mut R, - ) -> Self + ) -> ([Receiver<C>; RECEIVERS], Self) where R: CryptoRng + RngCore + ?Sized, { + // TODO: Add optimization path for receiver re-sampling so that we ensure that all UTXOs + // are maximally independent. + // let mut receivers = Vec::with_capacity(RECEIVERS); let mut zeroes = Vec::with_capacity(RECEIVERS - 1); + let (receiver, pre_sender) = spending_key.internal_pair( + ephemeral_key_parameters, + commitment_scheme_parameters, + rng.gen(), + asset, + ); + receivers.push(receiver); for _ in 0..RECEIVERS - 2 { let (receiver, pre_sender) = spending_key.internal_zero_pair( - parameters.ephemeral_key, - parameters.commitment_scheme, + ephemeral_key_parameters, + commitment_scheme_parameters, rng.gen(), asset.id, ); receivers.push(receiver); - zeroes.push(Zero { - key: zero_key.clone(), - pre_sender, - }); - } - let (receiver, pre_sender) = spending_key.internal_pair( - parameters.ephemeral_key, - parameters.commitment_scheme, - rng.gen(), - asset, - ); - receivers.push(receiver); - Self { - receivers: into_array_unchecked(receivers), - zeroes, - pre_sender, + zeroes.push(pre_sender); } + (into_array_unchecked(receivers), Self { zeroes, pre_sender }) } -} - -/// -pub trait Batcher<C> -where - C: Configuration, -{ - /// - type UtxoSet: Accumulator<Item = Utxo<C>, Verifier = C::UtxoSetVerifier>; - - /// - type Rng: CryptoRng + RngCore + ?Sized; - - /// - fn utxo_set(&mut self) -> &mut Self::UtxoSet; - /// - fn rng(&mut self) -> &mut Self::Rng; - - /// - fn prove<const SENDERS: usize, const RECEIVERS: usize>( - &mut self, - transfer: SecretTransfer<C, SENDERS, RECEIVERS>, - ) -> Result<TransferPost<C>, ProofSystemError<C>>; -} - -/// Batching Error -pub enum Error<C> -where - C: Configuration, -{ - /// Missing UTXO Membership Proof - MissingUtxoMembershipProof, - - /// Proof System Error - ProofSystemError(ProofSystemError<C>), -} - -/// -pub struct BatchRound<C, const SENDERS: usize, const RECEIVERS: usize> -where - C: Configuration, -{ - /// Pre-Sender Chunk Iterator - pre_senders: ChunkBy<vec::IntoIter<PreSender<C>>, SENDERS>, - - /// Joined Pre-Senders - joins: Vec<PreSender<C>>, -} - -impl<C, const SENDERS: usize, const RECEIVERS: usize> BatchRound<C, SENDERS, RECEIVERS> -where - C: Configuration, -{ - /// + /// Inserts UTXOs for each sender in `self` into the `utxo_set` for future proof selection. #[inline] - pub fn next<'p, K, R, S, P>( - &mut self, - parameters: Parameters<'p, C>, - spending_key: &SpendingKey<C>, - zero_key: K, - asset_id: AssetId, - zeroes: &mut Vec<Zero<C, K>>, - utxo_set: &mut S, - mut prover: P, - rng: &mut R, - ) -> Result<TransferPost<C>, Error<C>> + pub fn insert_utxos<A>(&self, utxo_set: &mut A) where - K: Clone, - R: CryptoRng + RngCore + ?Sized, - S: Accumulator<Item = Utxo<C>, Verifier = C::UtxoSetVerifier>, - P: FnMut( - SecretTransfer<C, SENDERS, RECEIVERS>, - ) -> Result<TransferPost<C>, ProofSystemError<C>>, + A: Accumulator<Item = Utxo<C>, Verifier = C::UtxoSetVerifier>, { - if let Some(chunk) = self.pre_senders.next() { - let senders = fallible_array_map(chunk, |ps| { - ps.try_upgrade(utxo_set) - .ok_or(Error::MissingUtxoMembershipProof) - })?; - - let mut join = Join::new( - parameters, - asset_id.with(senders.iter().map(Sender::asset_value).sum()), - spending_key, - zero_key, - rng, - ); - - let post = prover(Transfer::new(None, [], senders, join.receivers, [])) - .map_err(Error::ProofSystemError)?; - - for zero in &join.zeroes { - zero.pre_sender.insert_utxo(utxo_set); - } - join.pre_sender.insert_utxo(utxo_set); - - zeroes.append(&mut join.zeroes); - self.joins.push(join.pre_sender); - - Ok(post) - } else { - /* - self.joins.append(&mut self.pre_senders.remainder()); - self.pre_senders = mem::take(&mut self.joins).into_iter().chunk_by::<SENDERS>(); - */ - - todo!() + self.pre_sender.insert_utxo(utxo_set); + for zero in &self.zeroes { + zero.insert_utxo(utxo_set); } } } diff --git a/manta-accounting/src/transfer/canonical.rs b/manta-accounting/src/transfer/canonical.rs index bcbc7fcf3..e2f4d22f8 100644 --- a/manta-accounting/src/transfer/canonical.rs +++ b/manta-accounting/src/transfer/canonical.rs @@ -96,13 +96,7 @@ where /// Builds a [`Mint`] from `asset` and `receiver`. #[inline] pub fn build(asset: Asset, receiver: Receiver<C>) -> Self { - Self::new( - Some(asset.id), - [asset.value], - Default::default(), - [receiver], - Default::default(), - ) + Self::new(Some(asset.id), [asset.value], [], [receiver], []) } } @@ -129,13 +123,7 @@ where senders: [Sender<C>; PrivateTransferShape::SENDERS], receivers: [Receiver<C>; PrivateTransferShape::RECEIVERS], ) -> Self { - Self::new( - Default::default(), - Default::default(), - senders, - receivers, - Default::default(), - ) + Self::new(None, [], senders, receivers, []) } } @@ -172,13 +160,7 @@ where receivers: [Receiver<C>; ReclaimShape::RECEIVERS], reclaim: Asset, ) -> Self { - Self::new( - Some(reclaim.id), - Default::default(), - senders, - receivers, - [reclaim.value], - ) + Self::new(reclaim.id, [], senders, receivers, [reclaim.value]) } } @@ -244,18 +226,21 @@ where /// Change Value pub change: AssetValue, - /// Senders - pub senders: Vec<PreSender<C>>, + /// Pre-Senders + pub pre_senders: Vec<PreSender<C>>, } impl<C> Selection<C> where C: Configuration, { - /// Builds a new [`Selection`] from `change` and `senders`. + /// Builds a new [`Selection`] from `change` and `pre_senders`. #[inline] - fn build(change: AssetValue, senders: Vec<PreSender<C>>) -> Self { - Self { change, senders } + fn build(change: AssetValue, pre_senders: Vec<PreSender<C>>) -> Self { + Self { + change, + pre_senders, + } } /// Builds a new [`Selection`] by mapping over an asset selection with `builder`. diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index 4df715311..cff10f228 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -1422,13 +1422,14 @@ where { /// Builds a new [`Transfer`]. #[inline] - fn new( - asset_id: Option<AssetId>, + pub fn new( + asset_id: impl Into<Option<AssetId>>, sources: [AssetValue; SOURCES], senders: [Sender<C>; SENDERS], receivers: [Receiver<C>; RECEIVERS], sinks: [AssetValue; SINKS], ) -> Self { + let asset_id = asset_id.into(); Self::check_shape(asset_id.is_some()); Self::new_unchecked(asset_id, sources, senders, receivers, sinks) } diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 51fb2b2fc..2765bf583 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -33,7 +33,7 @@ use crate::{ asset::{Asset, AssetId, AssetMap, AssetValue}, key::{self, AccountKeys, HierarchicalKeyDerivationScheme}, transfer::{ - self, + self, batch, canonical::{ Mint, PrivateTransfer, PrivateTransferShape, Reclaim, Selection, Shape, Transaction, }, @@ -110,18 +110,9 @@ where R: IntoIterator<Item = VoidNumber<C>>; /// Signs a `transaction` and returns the ledger transfer posts if successful. - /// - /// # Safety - /// - /// The caller of this method should call [`finish`](Self::finish) once the posts have been - /// returned from the ledger to preserve the signer's internal state. fn sign(&mut self, transaction: Transaction<C>) -> Self::SignFuture; /// Returns a [`ReceivingKey`] for `self` to receive assets with `index`. - /// - /// # Safety - /// - /// This method can be called at any point, since it is independent of the signing state. fn receiving_key(&mut self, index: key::Index<H>) -> Self::ReceivingKeyFuture; } @@ -429,7 +420,7 @@ where // // TODO: Use a smarter object than `Vec` for `removes.into_iter().collect()` like a // `HashSet` or some other set-like container with fast membership and remove ops. - + // match self.utxo_set.len().checked_sub(insert_starting_index) { Some(diff) => self.sync_with( inserts.into_iter().skip(diff), @@ -443,14 +434,13 @@ where #[inline] fn build_pre_sender( &self, - spend_index: SpendIndex<C>, - ephemeral_key: PublicKey<C>, + key: AssetMapKey<C>, asset: Asset, ) -> Result<PreSender<C>, Error<C::HierarchicalKeyDerivationScheme, C>> { - let spend_key = self.account().spend_key(spend_index)?; + let (spend_index, ephemeral_key) = key; Ok(PreSender::new( &self.commitment_scheme_parameters, - spend_key, + self.account().spend_key(spend_index)?, ephemeral_key, asset, )) @@ -460,24 +450,40 @@ where #[inline] fn build_receiver( &mut self, - spend_index: SpendIndex<C>, - view_index: ViewIndex<C>, asset: Asset, ) -> Result<Receiver<C>, Error<C::HierarchicalKeyDerivationScheme, C>> { let keypair = self .account() - .keypair_with(spend_index, view_index)? - .derive::<C::KeyAgreementScheme>(); - Ok(Receiver::new( + .default_keypair() + .map_err(key::Error::KeyDerivationError)?; + Ok(SpendingKey::new(keypair.spend, keypair.view).receiver( &self.ephemeral_key_parameters, &self.commitment_scheme_parameters, self.rng.gen(), - keypair.spend, - keypair.view, asset, )) } + /// + #[inline] + fn mint_zero( + &mut self, + asset_id: AssetId, + ) -> Result<(Mint<C>, PreSender<C>), Error<C::HierarchicalKeyDerivationScheme, C>> { + let asset = Asset::zero(asset_id); + let keypair = self + .account() + .default_keypair() + .map_err(key::Error::KeyDerivationError)?; + let (receiver, pre_sender) = SpendingKey::new(keypair.spend, keypair.view).internal_pair( + &self.ephemeral_key_parameters, + &self.commitment_scheme_parameters, + self.rng.gen(), + asset, + ); + Ok((Mint::build(asset, receiver), pre_sender)) + } + /// Selects the pre-senders which collectively own at least `asset`, returning any change. #[inline] fn select( @@ -488,8 +494,8 @@ where if selection.is_empty() { return Err(Error::InsufficientBalance(asset)); } - Selection::new(selection, move |(i, ek), v| { - self.build_pre_sender(i, ek, asset.id.with(v)) + Selection::new(selection, move |k, v| { + self.build_pre_sender(k, asset.id.with(v)) }) } @@ -517,135 +523,123 @@ where .map_err(Error::ProofSystemError) } - /* TODO: - /// Accumulate transfers using the `SENDERS -> RECEIVERS` shape. + /// #[inline] - fn accumulate_transfers<const SENDERS: usize, const RECEIVERS: usize>( + fn next_join<const RECEIVERS: usize>( &mut self, asset_id: AssetId, - mut pre_senders: Vec<PreSender<C>>, - posts: &mut Vec<TransferPost<C>>, - ) -> Result<[Sender<C>; SENDERS], Error<C::DerivedSecretKeyGenerator, C>> { - assert!( - (SENDERS > 1) && (RECEIVERS > 1), - "The transfer shape must include at least two senders and two receivers." - ); - assert!( - !pre_senders.is_empty(), - "The set of initial senders cannot be empty." - ); - - let mut new_zeroes = Vec::new(); - - while pre_senders.len() > SENDERS { - let mut accumulators = Vec::new(); - let mut iter = pre_senders.into_iter().chunk_by::<SENDERS>(); - for chunk in &mut iter { - let senders = fallible_array_map(chunk, |ps| { - ps.try_upgrade(&self.utxo_set) - .ok_or(Error::MissingUtxoMembershipProof) - })?; - - let mut accumulator = self.signer.next_accumulator::<_, _, RECEIVERS>( - &self.commitment_scheme, - asset_id, - senders.iter().map(Sender::asset_value).sum(), - &mut self.rng, - )?; - - posts.push(self.build_post(SecretTransfer::new(senders, accumulator.receivers))?); - - for zero in &accumulator.zeroes { - zero.as_ref().insert_utxo(&mut self.utxo_set); - } - accumulator.pre_sender.insert_utxo(&mut self.utxo_set); - - new_zeroes.append(&mut accumulator.zeroes); - accumulators.push(accumulator.pre_sender); - } - - accumulators.append(&mut iter.remainder()); - pre_senders = accumulators; - } - - self.prepare_final_pre_senders::<SENDERS>(asset_id, new_zeroes, &mut pre_senders, posts)?; - - Ok(into_array_unchecked( - pre_senders - .into_iter() - .map(move |ps| ps.try_upgrade(&self.utxo_set)) - .collect::<Option<Vec<_>>>() - .ok_or(Error::MissingUtxoMembershipProof)?, + total: AssetValue, + ) -> Result< + ([Receiver<C>; RECEIVERS], batch::Join<C>), + Error<C::HierarchicalKeyDerivationScheme, C>, + > { + let keypair = self + .account() + .default_keypair() + .map_err(key::Error::KeyDerivationError)?; + Ok(batch::Join::new( + &self.ephemeral_key_parameters, + &self.commitment_scheme_parameters, + asset_id.with(total), + &SpendingKey::new(keypair.spend, keypair.view), + &mut self.rng, )) } - /// Prepare final pre-senders for the transaction. + /// #[inline] fn prepare_final_pre_senders<const SENDERS: usize>( &mut self, asset_id: AssetId, - mut new_zeroes: Vec<InternalKeyOwned<C::DerivedSecretKeyGenerator, PreSender<C>>>, + mut new_zeroes: Vec<PreSender<C>>, pre_senders: &mut Vec<PreSender<C>>, posts: &mut Vec<TransferPost<C>>, - ) -> Result<(), Error<C::DerivedSecretKeyGenerator, C>> { + ) -> Result<(), Error<C::HierarchicalKeyDerivationScheme, C>> { let mut needed_zeroes = SENDERS - pre_senders.len(); if needed_zeroes == 0 { return Ok(()); } - let zeroes = self.assets.zeroes(needed_zeroes, asset_id); needed_zeroes -= zeroes.len(); - for zero in zeroes { - pre_senders.push(self.get_pre_sender(zero, Asset::zero(asset_id))?); + pre_senders.push(self.build_pre_sender(zero, Asset::zero(asset_id))?); } - if needed_zeroes == 0 { return Ok(()); } - let needed_mints = needed_zeroes.saturating_sub(new_zeroes.len()); - for _ in 0..needed_zeroes { match new_zeroes.pop() { - Some(zero) => pre_senders.push(zero.unwrap()), + Some(zero) => pre_senders.push(zero), _ => break, } } - if needed_mints == 0 { return Ok(()); } - for _ in 0..needed_mints { - let (mint, pre_sender) = - self.signer - .mint_zero(&self.commitment_scheme, asset_id, &mut self.rng)?; - pre_senders.push(pre_sender); + let (mint, pre_sender) = self.mint_zero(asset_id)?; posts.push(self.build_post(mint)?); + pre_senders.push(pre_sender); } - Ok(()) } - */ /// #[inline] fn compute_batched_transaction<const SENDERS: usize, const RECEIVERS: usize>( &mut self, + asset_id: AssetId, + mut pre_senders: Vec<PreSender<C>>, + posts: &mut Vec<TransferPost<C>>, ) -> Result<[Sender<C>; SENDERS], Error<C::HierarchicalKeyDerivationScheme, C>> { - todo!() - } + assert!( + (SENDERS >= 2) && (RECEIVERS >= 2), + "The transfer shape must include at least two senders and two receivers." + ); + assert!( + !pre_senders.is_empty(), + "The set of initial senders cannot be empty." + ); - /// Returns the next change receiver for `asset`. - #[inline] - fn next_change( - &mut self, - asset_id: AssetId, - change: AssetValue, - ) -> Result<Receiver<C>, Error<C::HierarchicalKeyDerivationScheme, C>> { - let default_index = Default::default(); - self.build_receiver(default_index, default_index, asset_id.with(change)) + let mut new_zeroes = Vec::new(); + + while pre_senders.len() > SENDERS { + let mut joins = Vec::new(); + let mut iter = pre_senders.into_iter().chunk_by::<SENDERS>(); + + for chunk in &mut iter { + let senders = fallible_array_map(chunk, |s| { + s.try_upgrade(&self.utxo_set) + .ok_or(Error::MissingUtxoMembershipProof) + })?; + + let (receivers, mut join) = self.next_join::<RECEIVERS>( + asset_id, + senders.iter().map(Sender::asset_value).sum(), + )?; + + posts.push(self.build_post(Transfer::new(None, [], senders, receivers, []))?); + + join.insert_utxos(&mut self.utxo_set); + + joins.push(join.pre_sender); + new_zeroes.append(&mut join.zeroes); + } + + joins.append(&mut iter.remainder()); + pre_senders = joins; + } + + self.prepare_final_pre_senders::<SENDERS>(asset_id, new_zeroes, &mut pre_senders, posts)?; + + Ok(into_array_unchecked( + pre_senders + .into_iter() + .map(move |s| s.try_upgrade(&self.utxo_set)) + .collect::<Option<Vec<_>>>() + .ok_or(Error::MissingUtxoMembershipProof)?, + )) } /// Prepares a given [`ReceivingKey`] for receiving `asset`. @@ -659,31 +653,24 @@ where ) } - /// Signs a withdraw transaction without resetting on error. + /// Signs a withdraw transaction. #[inline] fn sign_withdraw( &mut self, asset: Asset, - receiver: Option<ReceivingKey<C>>, + receiver: impl Into<Option<ReceivingKey<C>>>, ) -> SignResult<C::HierarchicalKeyDerivationScheme, C, Self> { const SENDERS: usize = PrivateTransferShape::SENDERS; const RECEIVERS: usize = PrivateTransferShape::RECEIVERS; - let selection = self.select(asset)?; - + let change = self.build_receiver(asset.id.with(selection.change))?; let mut posts = Vec::new(); - - /* - let senders = self.accumulate_transfers::<SENDERS, RECEIVERS>( + let senders = self.compute_batched_transaction::<SENDERS, RECEIVERS>( asset.id, selection.pre_senders, &mut posts, )?; - */ - - let senders = self.compute_batched_transaction::<SENDERS, RECEIVERS>()?; - let change = self.next_change(asset.id, selection.change)?; - let final_post = match receiver { + let final_post = match receiver.into() { Some(receiver) => { let receiver = self.prepare_receiver(asset, receiver); self.build_post(PrivateTransfer::build(senders, [change, receiver]))? @@ -703,14 +690,12 @@ where ) -> SignResult<C::HierarchicalKeyDerivationScheme, C, Self> { match transaction { Transaction::Mint(asset) => { - let default_index = Default::default(); - let receiver = self.build_receiver(default_index, default_index, asset)?; - let mint_post = self.build_post(Mint::build(asset, receiver))?; - Ok(SignResponse::new(vec![mint_post])) - } - Transaction::PrivateTransfer(asset, receiver) => { - self.sign_withdraw(asset, Some(receiver)) + let receiver = self.build_receiver(asset)?; + Ok(SignResponse::new(vec![ + self.build_post(Mint::build(asset, receiver))? + ])) } + Transaction::PrivateTransfer(asset, receiver) => self.sign_withdraw(asset, receiver), Transaction::Reclaim(asset) => self.sign_withdraw(asset, None), } } @@ -721,13 +706,8 @@ where &mut self, index: Index<C>, ) -> ReceivingKeyResult<C::HierarchicalKeyDerivationScheme, C, Self> { - /* - let _ = self - .account() - .keypair(index)? - .derive::<C::KeyAgreementScheme>(); - */ - todo!() + let keypair = self.account().keypair(index)?; + Ok(SpendingKey::new(keypair.spend, keypair.view).derive()) } } @@ -769,288 +749,7 @@ where } } -/* TODO: -/// Pre-Sender Selection -struct Selection<C> -where - C: transfer::Configuration, -{ - /// Selection Change - pub change: AssetValue, - - /// Selection Pre-Senders - pub pre_senders: Vec<PreSender<C>>, -} - -impl<C> Selection<C> -where - C: transfer::Configuration, -{ - /// Builds a new [`Selection`] from `change` and `pre_senders`. - #[inline] - pub fn new(change: AssetValue, pre_senders: Vec<PreSender<C>>) -> Self { - Self { - change, - pre_senders, - } - } -} - -impl<D> Signer<D> -where - D: DerivedSecretKeyGenerator, -{ - /// Builds a new [`Signer`] for `account` from a `secret_key_source`. - #[inline] - pub fn new(secret_key_source: D, account: D::Account) -> Self { - Self::with_account(secret_key_source, Account::new(account)) - } - - /// Builds a new [`Signer`] for `account` from a `secret_key_source`. - #[inline] - pub fn with_account(secret_key_source: D, account: Account<D>) -> Self { - Self { - secret_key_source, - account, - } - } - - /// Builds a new [`Signer`] for `account` from a `secret_key_source` with starting ranges - /// `external_indices` and `internal_indices`. - #[inline] - pub fn with_ranges( - secret_key_source: D, - account: D::Account, - external_indices: Range<D::Index>, - internal_indices: Range<D::Index>, - ) -> Self { - Self::with_account( - secret_key_source, - Account::with_ranges(account, external_indices, internal_indices), - ) - } - - /// Returns the next [`Signer`] after `self`, incrementing the account number. - #[inline] - pub fn next(self) -> Self { - Self::with_account(self.secret_key_source, self.account.next()) - } - - - /// Returns a [`PreSender`] for the key at the given `index`. - #[inline] - pub fn get_pre_sender<C>( - &self, - index: Index<D>, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - ) -> Result<PreSender<C>, D::Error> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - { - Ok(self.get(&index)?.into_pre_sender(commitment_scheme, asset)) - } - - /// Generates the next external identity for this signer. - #[inline] - fn next_external_identity<C>(&mut self) -> Result<Identity<C>, D::Error> - where - C: identity::Configuration<SecretKey = D::SecretKey>, - { - Ok(self - .account - .next_external_key(&self.secret_key_source)? - .map(Identity::new) - .unwrap()) - } - - /// Generates the next internal identity for this signer. - #[inline] - fn next_internal_identity<C>(&mut self) -> Result<InternalKeyOwned<D, Identity<C>>, D::Error> - where - C: identity::Configuration<SecretKey = D::SecretKey>, - { - Ok(self - .account - .next_internal_key(&self.secret_key_source)? - .map(Identity::new)) - } - - /// Generates a new [`ShieldedIdentity`] to receive assets to this account via an external - /// transaction. - #[inline] - pub fn next_shielded<C>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - ) -> Result<ShieldedIdentity<C>, D::Error> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - { - Ok(self - .next_external_identity()? - .into_shielded(commitment_scheme)) - } - - /// Generates a new [`InternalIdentity`] to receive assets in this account via an internal - /// transaction. - #[inline] - pub fn next_internal<C, R>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - rng: &mut R, - ) -> Result<InternalKeyOwned<D, InternalIdentity<C>>, InternalIdentityError<D, C>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - R: CryptoRng + RngCore + ?Sized, - { - self.next_internal_identity() - .map_err(InternalIdentityError::SecretKeyError)? - .map_ok(move |identity| { - identity - .into_internal(commitment_scheme, asset, rng) - .map_err(InternalIdentityError::EncryptionError) - }) - } - - /// Builds the next transfer accumulator. - #[inline] - pub fn next_accumulator<C, R, const RECEIVERS: usize>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - asset_id: AssetId, - sender_sum: AssetValue, - rng: &mut R, - ) -> Result<TransferAccumulator<D, C, RECEIVERS>, InternalIdentityError<D, C>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - R: CryptoRng + RngCore + ?Sized, - { - let mut receivers = Vec::with_capacity(RECEIVERS); - let mut zero_pre_senders = Vec::with_capacity(RECEIVERS - 1); - - for _ in 0..RECEIVERS - 1 { - let (internal, index) = self - .next_internal(commitment_scheme, Asset::zero(asset_id), rng)? - .into(); - receivers.push(internal.receiver); - zero_pre_senders.push(KeyOwned::new(internal.pre_sender, index)); - } - - let internal = self - .next_internal(commitment_scheme, asset_id.with(sender_sum), rng)? - .unwrap(); - - receivers.push(internal.receiver); - - Ok(TransferAccumulator::new( - into_array_unchecked(receivers), - zero_pre_senders, - internal.pre_sender, - )) - } - - /// Builds the change receiver for the end of a transaction. - #[inline] - pub fn next_change<C, R>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - rng: &mut R, - ) -> Result<InternalKeyOwned<D, Receiver<C>>, InternalIdentityError<D, C>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - R: CryptoRng + RngCore + ?Sized, - { - self.next_internal_identity() - .map_err(InternalIdentityError::SecretKeyError)? - .map_ok(move |identity| identity.into_receiver(commitment_scheme, asset, rng)) - .map_err(InternalIdentityError::EncryptionError) - } - - /// Builds a [`Mint`] transaction to mint `asset` and returns the index for that asset. - #[inline] - pub fn mint<C, R>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - asset: Asset, - rng: &mut R, - ) -> Result<InternalKeyOwned<D, Mint<C>>, InternalIdentityError<D, C>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - R: CryptoRng + RngCore + ?Sized, - { - self.next_internal_identity() - .map_err(InternalIdentityError::SecretKeyError)? - .map_ok(|identity| { - Mint::from_identity(identity, commitment_scheme, asset, rng) - .map_err(InternalIdentityError::EncryptionError) - }) - } - - /// Builds a [`Mint`] transaction to mint a zero asset with the given `asset_id`, returning a - /// [`PreSender`] for that asset. - #[inline] - pub fn mint_zero<C, R>( - &mut self, - commitment_scheme: &C::CommitmentScheme, - asset_id: AssetId, - rng: &mut R, - ) -> Result<(Mint<C>, PreSender<C>), InternalIdentityError<D, C>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - R: CryptoRng + RngCore + ?Sized, - { - Mint::zero( - self.next_internal_identity() - .map_err(InternalIdentityError::SecretKeyError)? - .unwrap(), - commitment_scheme, - asset_id, - rng, - ) - .map_err(InternalIdentityError::EncryptionError) - } - - /// Tries to decrypt `encrypted_asset` using the `secret_key`. - #[inline] - fn try_open_asset<C>( - secret_key: Result<ExternalSecretKey<D>, D::Error>, - encrypted_asset: &EncryptedAsset<C>, - ) -> Option<ExternalKeyOwned<D, Asset>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - { - let KeyOwned { inner, index } = secret_key.ok()?; - Some( - index.wrap( - Identity::<C>::new(inner) - .try_open(encrypted_asset) - .ok()? - .into_asset(), - ), - ) - } - - /// Looks for an index that can decrypt the given `encrypted_asset`. - #[inline] - pub fn find_external_asset<C>( - &mut self, - encrypted_asset: &EncryptedAsset<C>, - ) -> Option<ExternalKeyOwned<D, Asset>> - where - C: transfer::Configuration<SecretKey = D::SecretKey>, - { - let asset = self - .account - .external_keys(&self.secret_key_source) - .find_map(move |k| Self::try_open_asset::<C>(k, encrypted_asset))?; - self.account - .conditional_increment_external_range(&asset.index.index); - Some(asset) - } -} - +/* TODO[remove]: impl<D> Load for Signer<D> where D: DerivedSecretKeyGenerator + LoadWith<Account<D>>, @@ -1090,118 +789,4 @@ where .save_with(self.account, path, saving_key) } } - -/// Signer Configuration -pub trait Configuration { - /// - type TransferConfiguration: transfer::Configuration; - - /// - type TransferProofSystemConfiguration: - transfer::ProofSystemConfiguration<Self::TransferConfiguration>; - - /// - type AccountKeyTable: AccountKeyTable<SecretKey = SecretKey<Self::TransferConfiguration>>; - - /// [`Utxo`] Accumulator Type - type UtxoSet: Accumulator< - Item = <Self::UtxoSetVerifier as Verifier>::Item, - Verifier = Self::UtxoSetVerifier, - > + ConstantCapacityAccumulator - + ExactSizeAccumulator - + OptimizedAccumulator - + Rollback; - - /// Asset Map Type - type AssetMap: AssetMap<Key = ??>; - - /// Random Number Generator Type - type Rng: CryptoRng + RngCore; -} - -/// Full Signer -pub struct Signer<C> -where - C: Configuration, -{ - /// Signer - signer: Signer<C::DerivedSecretKeyGenerator>, - - /// Commitment Scheme - commitment_scheme: C::CommitmentScheme, - - /// Proving Context - proving_context: ProvingContext<C>, - - /// UTXO Set - utxo_set: C::UtxoSet, - - /// Asset Distribution - assets: C::AssetMap, - - /// Random Number Generator - rng: C::Rng, -} - - -/// Internal Identity Error -/// -/// This `enum` is the error state for any construction of an [`InternalIdentity`] from a derived -/// secret key generator. -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "D::Error: Clone, IntegratedEncryptionSchemeError<C>: Clone"), - Copy(bound = "D::Error: Copy, IntegratedEncryptionSchemeError<C>: Copy"), - Debug(bound = "D::Error: Debug, IntegratedEncryptionSchemeError<C>: Debug"), - Eq(bound = "D::Error: Eq, IntegratedEncryptionSchemeError<C>: Eq"), - Hash(bound = "D::Error: Hash, IntegratedEncryptionSchemeError<C>: Hash"), - PartialEq(bound = "D::Error: PartialEq, IntegratedEncryptionSchemeError<C>: PartialEq") -)] -pub enum InternalIdentityError<D, C> -where - D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, -{ - /// Secret Key Generator Error - SecretKeyError(D::Error), - - /// Encryption Error - EncryptionError(IntegratedEncryptionSchemeError<C>), -} - -/// Transfer Accumulator -pub struct TransferAccumulator<D, C, const RECEIVERS: usize> -where - D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, -{ - /// Receivers - pub receivers: [Receiver<C>; RECEIVERS], - - /// Zero Balance Pre-Senders - pub zeroes: Vec<InternalKeyOwned<D, PreSender<C>>>, - - /// Accumulated Balance Pre-Sender - pub pre_sender: PreSender<C>, -} - -impl<D, C, const RECEIVERS: usize> TransferAccumulator<D, C, RECEIVERS> -where - D: DerivedSecretKeyGenerator, - C: transfer::Configuration<SecretKey = D::SecretKey>, -{ - /// Builds a new [`TransferAccumulator`] from `receivers`, `zeroes`, and `pre_sender`. - #[inline] - pub fn new( - receivers: [Receiver<C>; RECEIVERS], - zeroes: Vec<InternalKeyOwned<D, PreSender<C>>>, - pre_sender: PreSender<C>, - ) -> Self { - Self { - receivers, - zeroes, - pre_sender, - } - } -} */ diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index ea93fed54..0826b8060 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -238,7 +238,7 @@ where .map_err(Error::LedgerError)?; self.assets.deposit_all( self.signer - .sync(self.checkpoint.receiver_index(), receivers, senders) + .sync(checkpoint.receiver_index(), receivers, senders) .await? .assets, ); From b25a5811d36350f05f0c28e8295d216acd297fc9 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 21 Dec 2021 03:03:34 -0500 Subject: [PATCH 142/275] feat: finish setting up draft of new signer protocol --- manta-accounting/src/asset.rs | 123 +++++++++- manta-accounting/src/key.rs | 51 ++++- manta-accounting/src/transfer/batch.rs | 14 +- manta-accounting/src/transfer/canonical.rs | 2 +- manta-accounting/src/transfer/mod.rs | 2 +- manta-accounting/src/wallet/ledger.rs | 6 +- manta-accounting/src/wallet/signer.rs | 144 ++++-------- manta-accounting/src/wallet/state.rs | 1 + manta-crypto/src/merkle_tree/forest.rs | 11 +- manta-crypto/src/merkle_tree/fork.rs | 250 +++++++-------------- manta-crypto/src/merkle_tree/full.rs | 4 +- manta-crypto/src/merkle_tree/partial.rs | 48 +++- manta-crypto/src/merkle_tree/tree.rs | 62 ++--- manta-util/src/lib.rs | 1 + manta-util/src/pointer.rs | 111 +++++++++ 15 files changed, 487 insertions(+), 343 deletions(-) create mode 100644 manta-util/src/pointer.rs diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index d71ab5fc7..4888503a6 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -18,11 +18,10 @@ // TODO: Add macro to build `AssetId` and `AssetValue`. // TODO: Implement all `rand` sampling traits. -// TODO: Should we rename `AssetValue` to `AssetValue` to be more consistent? // TODO: Implement `Concat` for `AssetId` and `AssetValue`. -// TODO: Add implementations for `AssetMap` using key-value maps like `BTreeMap` and `HashMap` +// TODO: Add implementations for `AssetMap` using sorted vectors and/or max-heaps -use alloc::vec::Vec; +use alloc::{collections::BTreeMap, vec::Vec}; use core::{ fmt::Debug, hash::Hash, @@ -40,6 +39,12 @@ use manta_crypto::{ }; use manta_util::{into_array_unchecked, Concat, ConcatAccumulator}; +#[cfg(feature = "std")] +use std::{ + collections::hash_map::{HashMap, RandomState}, + hash::BuildHasher, +}; + /// [`AssetId`] Base Type pub type AssetIdType = u32; @@ -396,6 +401,28 @@ impl Asset { None } } + + /// Adds the value of `asset` to `self` if it has the same [`AssetId`]. + #[inline] + pub fn try_add_assign(&mut self, asset: Asset) -> bool { + if self.id == asset.id { + self.value += asset.value; + true + } else { + false + } + } + + /// Subtracts the value of `asset` from `self` if it has the same [`AssetId`]. + #[inline] + pub fn try_sub_assign(&mut self, asset: Asset) -> bool { + if self.id == asset.id { + self.value -= asset.value; + true + } else { + false + } + } } impl<I, V> Add<V> for Asset<I, V> @@ -585,10 +612,94 @@ pub trait AssetMap: Default { } } +/// +macro_rules! impl_asset_map_for_maps_body { + ($k:tt) => { + type Key = $k; + + #[inline] + fn select(&self, asset: Asset) -> Selection<Self> { + // TODO: Use a smarter coin-selection algorithm (max-heap?). + if asset.is_zero() { + return Selection::default(); + } + let mut sum = Asset::zero(asset.id); + let mut values = Vec::new(); + for (key, item) in self { + if item.value != AssetValue(0) && sum.try_add_assign(*item) { + values.push((key.clone(), item.value)); + if sum.value >= asset.value { + break; + } + } + } + if sum.value < asset.value { + Selection::default() + } else { + Selection::new(sum.value - asset.value, values) + } + } + + #[inline] + fn zeroes(&self, n: usize, id: AssetId) -> Vec<Self::Key> { + self.iter() + .filter_map(move |(k, a)| { + (a.id == id && a.value == AssetValue(0)).then(move || k.clone()) + }) + .take(n) + .collect() + } + + #[inline] + fn insert(&mut self, key: Self::Key, asset: Asset) { + self.insert(key, asset); + } + + #[inline] + fn remove(&mut self, key: Self::Key) { + self.remove(&key); + } + }; +} + +/// B-Tree Map [`AssetMap`] Implementation +pub type BTreeAssetMap<K> = BTreeMap<K, Asset>; + +impl<K> AssetMap for BTreeAssetMap<K> +where + K: Clone + Ord, +{ + impl_asset_map_for_maps_body! { K } +} + +/// Hash Map [`AssetMap`] Implementation +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +pub type HashAssetMap<K, S = RandomState> = HashMap<K, Asset, S>; + +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +impl<K, S> AssetMap for HashAssetMap<K, S> +where + K: Clone + Hash + Eq, + S: BuildHasher + Default, +{ + impl_asset_map_for_maps_body! { K } +} + /// Asset Selection /// /// This `struct` is created by the [`select`](AssetMap::select) method of [`AssetMap`]. See its /// documentation for more. +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "M::Key: Clone"), + Debug(bound = "M::Key: Debug"), + Default(bound = ""), + Eq(bound = "M::Key: Eq"), + Hash(bound = "M::Key: Hash"), + PartialEq(bound = "M::Key: PartialEq") +)] pub struct Selection<M> where M: AssetMap + ?Sized, @@ -604,6 +715,12 @@ impl<M> Selection<M> where M: AssetMap + ?Sized, { + /// Builds a new [`Selection`] from `change` and `values`. + #[inline] + pub fn new(change: AssetValue, values: Vec<(M::Key, AssetValue)>) -> Self { + Self { change, values } + } + /// Returns `true` if `self` is an empty [`Selection`]. #[inline] pub fn is_empty(&self) -> bool { diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index 55deaa70b..668582e21 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -147,7 +147,15 @@ where /// Key Index Type #[derive(derivative::Derivative)] -#[derivative(Clone, Copy, Default, Eq, PartialEq)] +#[derivative( + Clone, + Copy, + Debug(bound = "H::Index: Debug"), + Default, + Eq, + Hash(bound = "H::Index: Hash"), + PartialEq +)] pub struct Index<H> where H: HierarchicalKeyDerivationScheme + ?Sized, @@ -170,8 +178,31 @@ where } } +/// View Key Selection +pub struct ViewKeySelection<H, T> +where + H: HierarchicalKeyDerivationScheme + ?Sized, +{ + /// Selection Index + pub index: Index<H>, + + /// Selection Key Pair + pub keypair: SecretKeyPair<H>, + + /// Selection Item + pub item: T, +} + /// Account Keys #[derive(derivative::Derivative)] +#[derivative( + Clone(bound = ""), + Copy(bound = ""), + Debug(bound = "H: Debug, H::Account: Debug, H::Index: Debug"), + Eq(bound = "H: Eq"), + Hash(bound = "H: Hash, H::Account: Hash, H::Index: Hash"), + PartialEq(bound = "H: PartialEq") +)] pub struct AccountKeys<'h, H> where H: HierarchicalKeyDerivationScheme + ?Sized, @@ -315,10 +346,7 @@ where /// Applies `f` to the view keys generated by `self` returning the first non-`None` result with /// it's key index and key attached, or returns an error if the key derivation failed. #[inline] - pub fn find_index<T, F>( - &self, - mut f: F, - ) -> Result<Option<(Index<H>, SecretKeyPair<H>, T)>, H::Error> + pub fn find_index<T, F>(&self, mut f: F) -> Result<Option<ViewKeySelection<H, T>>, H::Error> where F: FnMut(&H::SecretKey) -> Option<T>, { @@ -327,12 +355,15 @@ where loop { match self.view_key(index) { Ok(view_key) => { - if let Some(value) = f(&view_key) { - return Ok(Some(( + if let Some(item) = f(&view_key) { + return Ok(Some(ViewKeySelection { index, - SecretKeyPair::new(self.derive_spend(index.spend)?, view_key), - value, - ))); + keypair: SecretKeyPair::new( + self.derive_spend(index.spend)?, + view_key, + ), + item, + })); } } Err(Error::ExceedingCurrentMaximumViewIndex) => break, diff --git a/manta-accounting/src/transfer/batch.rs b/manta-accounting/src/transfer/batch.rs index 05082037c..7bb80b6ee 100644 --- a/manta-accounting/src/transfer/batch.rs +++ b/manta-accounting/src/transfer/batch.rs @@ -17,23 +17,17 @@ //! Batched Transfers use crate::{ - asset::{Asset, AssetId, AssetValue}, + asset::Asset, transfer::{ - CommitmentSchemeParameters, Configuration, EphemeralKeyParameters, Parameters, PreSender, - ProofSystemError, Receiver, ReceivingKey, Sender, SpendingKey, Transfer, TransferPost, - Utxo, + CommitmentSchemeParameters, Configuration, EphemeralKeyParameters, PreSender, Receiver, + SpendingKey, Utxo, }, }; -use alloc::vec; -use core::mem; use manta_crypto::{ accumulator::Accumulator, rand::{CryptoRng, Rand, RngCore}, }; -use manta_util::{ - fallible_array_map, into_array_unchecked, - iter::{ChunkBy, IteratorExt}, -}; +use manta_util::into_array_unchecked; /// Batch Join Structure pub struct Join<C> diff --git a/manta-accounting/src/transfer/canonical.rs b/manta-accounting/src/transfer/canonical.rs index e2f4d22f8..d1ebfcab3 100644 --- a/manta-accounting/src/transfer/canonical.rs +++ b/manta-accounting/src/transfer/canonical.rs @@ -17,7 +17,7 @@ //! Canonical Transaction Types use crate::{ - asset::{self, Asset, AssetId, AssetValue}, + asset::{self, Asset, AssetValue}, transfer::{Configuration, PreSender, Receiver, ReceivingKey, Sender, Transfer}, }; use manta_util::{create_seal, seal}; diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index cff10f228..377e34ee5 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -628,7 +628,7 @@ where ephemeral_key: PublicKey<C>, asset: Asset, ) -> PreSender<C> { - // FIXME: See if this clone is really needed. + // TODO: See if this clone is really needed. PreSender::new(parameters, self.spend.clone(), ephemeral_key, asset) } diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs index 07e7a49a0..505d6d27d 100644 --- a/manta-accounting/src/wallet/ledger.rs +++ b/manta-accounting/src/wallet/ledger.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Ledger Source +//! Ledger Connection use crate::transfer::{Configuration, EncryptedNote, TransferPost, Utxo, VoidNumber}; use alloc::vec::Vec; @@ -61,7 +61,7 @@ where fn pull(&self, checkpoint: &Self::Checkpoint) -> Self::PullFuture; /// Sends `posts` to the ledger, returning `true` or `false` depending on whether the entire - /// transaction succeeded or not. + /// batch succeeded or not. fn push(&self, posts: Vec<TransferPost<C>>) -> Self::PushFuture; } @@ -99,6 +99,6 @@ where /// This `struct` is created by the [`push`](Connection::push) method on [`Connection`]. /// See its documentation for more. pub struct PushResponse { - /// Whether or not the Transaction Succeeded in Full + /// Transaction Success Flag pub success: bool, } diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 2765bf583..98d352d9c 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -16,14 +16,8 @@ //! Wallet Signer -// FIXME: Change the name of `TransferAccumulator`, its not an `Accumulator`. // TODO: Add wallet recovery i.e. remove the assumption that a new signer represents a completely // new derived secret key generator. -// TODO: Allow for non-atomic signing, i.e. rollback state to something in-between two calls to -// sign`. Will have to upgrade `Rollback` and `manta_crypto::merkle_tree::fork` as well. -// TODO: Add checkpointing/garbage-collection in `utxo_set` so we can remove old UTXOs once they -// are irrelevant. Once we create a sender and its transaction succeeds we can drop the UTXO. -// See `OptimizedAccumulator::remove_proof`. // TODO: Should have a mode on the signer where we return a generic error which reveals no detail // about what went wrong during signing. The kind of error returned from a signing could // reveal information about the internal state (privacy leak, not a secrecy leak). @@ -31,9 +25,10 @@ use crate::{ asset::{Asset, AssetId, AssetMap, AssetValue}, - key::{self, AccountKeys, HierarchicalKeyDerivationScheme}, + key::{self, AccountKeys, HierarchicalKeyDerivationScheme, ViewKeySelection}, transfer::{ - self, batch, + self, + batch::Join, canonical::{ Mint, PrivateTransfer, PrivateTransferShape, Reclaim, Selection, Shape, Transaction, }, @@ -45,11 +40,7 @@ use crate::{ use alloc::{vec, vec::Vec}; use core::{ convert::Infallible, - fmt::Debug, future::{self, Future, Ready}, - hash::Hash, - mem, - ops::Range, }; use manta_crypto::{ accumulator::{ @@ -57,7 +48,6 @@ use manta_crypto::{ Verifier, }, encryption::DecryptedMessage, - key::KeyAgreementScheme, rand::{CryptoRng, Rand, RngCore}, }; use manta_util::{fallible_array_map, into_array_unchecked, iter::IteratorExt}; @@ -208,6 +198,9 @@ where } } +/// Signer Error over a [`Configuration`] +type SignerError<C> = Error<<C as Configuration>::HierarchicalKeyDerivationScheme, C>; + /// Signer Configuration pub trait Configuration: transfer::Configuration { /// Hierarchical Key Derivation Scheme @@ -345,29 +338,34 @@ where encrypted_note: EncryptedNote<C>, void_numbers: &mut Vec<VoidNumber<C>>, assets: &mut Vec<Asset>, - ) -> Result<(), Error<C::HierarchicalKeyDerivationScheme, C>> { + ) -> Result<(), SignerError<C>> { let mut finder = DecryptedMessage::find(encrypted_note); - if let Some(( + if let Some(ViewKeySelection { index, - key, - DecryptedMessage { - plaintext: asset, - ephemeral_public_key, - }, - )) = self + keypair, + item: + DecryptedMessage { + plaintext: asset, + ephemeral_public_key, + }, + }) = self .account() .find_index(move |k| finder.decrypt(k)) .map_err(key::Error::KeyDerivationError)? { if let Some(void_number) = C::check_full_asset( &self.commitment_scheme_parameters, - &key.spend, + &keypair.spend, &ephemeral_public_key, &asset, &utxo, ) { - if let Some(index) = void_numbers.iter().position(move |v| v == &void_number) { - void_numbers.remove(index); + if let Some(void_number_index) = + void_numbers.iter().position(move |v| v == &void_number) + { + void_numbers.remove(void_number_index); + self.utxo_set.remove_proof(&utxo); + self.assets.remove((index.spend, ephemeral_public_key)); } else { assets.push(asset); self.assets @@ -436,7 +434,7 @@ where &self, key: AssetMapKey<C>, asset: Asset, - ) -> Result<PreSender<C>, Error<C::HierarchicalKeyDerivationScheme, C>> { + ) -> Result<PreSender<C>, SignerError<C>> { let (spend_index, ephemeral_key) = key; Ok(PreSender::new( &self.commitment_scheme_parameters, @@ -448,10 +446,7 @@ where /// Builds the pre-receiver associated to `spend_index`, `view_index`, and `asset`. #[inline] - fn build_receiver( - &mut self, - asset: Asset, - ) -> Result<Receiver<C>, Error<C::HierarchicalKeyDerivationScheme, C>> { + fn build_receiver(&mut self, asset: Asset) -> Result<Receiver<C>, SignerError<C>> { let keypair = self .account() .default_keypair() @@ -464,12 +459,10 @@ where )) } - /// + /// Mints an asset with zero value for the given `asset_id`, returning the appropriate + /// transfer and pre-sender. #[inline] - fn mint_zero( - &mut self, - asset_id: AssetId, - ) -> Result<(Mint<C>, PreSender<C>), Error<C::HierarchicalKeyDerivationScheme, C>> { + fn mint_zero(&mut self, asset_id: AssetId) -> Result<(Mint<C>, PreSender<C>), SignerError<C>> { let asset = Asset::zero(asset_id); let keypair = self .account() @@ -486,10 +479,7 @@ where /// Selects the pre-senders which collectively own at least `asset`, returning any change. #[inline] - fn select( - &mut self, - asset: Asset, - ) -> Result<Selection<C>, Error<C::HierarchicalKeyDerivationScheme, C>> { + fn select(&mut self, asset: Asset) -> Result<Selection<C>, SignerError<C>> { let selection = self.assets.select(asset); if selection.is_empty() { return Err(Error::InsufficientBalance(asset)); @@ -509,7 +499,7 @@ where >( &mut self, transfer: Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>, - ) -> Result<TransferPost<C>, Error<C::HierarchicalKeyDerivationScheme, C>> { + ) -> Result<TransferPost<C>, SignerError<C>> { transfer .into_post( Parameters::new( @@ -523,21 +513,19 @@ where .map_err(Error::ProofSystemError) } - /// + /// Computes the next [`Join`](Join) element for an asset rebalancing round. + #[allow(clippy::type_complexity)] // NOTE: Clippy is too harsh here. #[inline] fn next_join<const RECEIVERS: usize>( &mut self, asset_id: AssetId, total: AssetValue, - ) -> Result< - ([Receiver<C>; RECEIVERS], batch::Join<C>), - Error<C::HierarchicalKeyDerivationScheme, C>, - > { + ) -> Result<([Receiver<C>; RECEIVERS], Join<C>), SignerError<C>> { let keypair = self .account() .default_keypair() .map_err(key::Error::KeyDerivationError)?; - Ok(batch::Join::new( + Ok(Join::new( &self.ephemeral_key_parameters, &self.commitment_scheme_parameters, asset_id.with(total), @@ -546,7 +534,7 @@ where )) } - /// + /// Prepares the final pre-senders for the last part of the transaction. #[inline] fn prepare_final_pre_senders<const SENDERS: usize>( &mut self, @@ -554,7 +542,7 @@ where mut new_zeroes: Vec<PreSender<C>>, pre_senders: &mut Vec<PreSender<C>>, posts: &mut Vec<TransferPost<C>>, - ) -> Result<(), Error<C::HierarchicalKeyDerivationScheme, C>> { + ) -> Result<(), SignerError<C>> { let mut needed_zeroes = SENDERS - pre_senders.len(); if needed_zeroes == 0 { return Ok(()); @@ -585,14 +573,14 @@ where Ok(()) } - /// + /// Computes the batched transactions for rebalancing before a final transfer. #[inline] - fn compute_batched_transaction<const SENDERS: usize, const RECEIVERS: usize>( + fn compute_batched_transactions<const SENDERS: usize, const RECEIVERS: usize>( &mut self, asset_id: AssetId, mut pre_senders: Vec<PreSender<C>>, posts: &mut Vec<TransferPost<C>>, - ) -> Result<[Sender<C>; SENDERS], Error<C::HierarchicalKeyDerivationScheme, C>> { + ) -> Result<[Sender<C>; SENDERS], SignerError<C>> { assert!( (SENDERS >= 2) && (RECEIVERS >= 2), "The transfer shape must include at least two senders and two receivers." @@ -601,38 +589,28 @@ where !pre_senders.is_empty(), "The set of initial senders cannot be empty." ); - let mut new_zeroes = Vec::new(); - while pre_senders.len() > SENDERS { let mut joins = Vec::new(); let mut iter = pre_senders.into_iter().chunk_by::<SENDERS>(); - for chunk in &mut iter { let senders = fallible_array_map(chunk, |s| { s.try_upgrade(&self.utxo_set) .ok_or(Error::MissingUtxoMembershipProof) })?; - let (receivers, mut join) = self.next_join::<RECEIVERS>( asset_id, senders.iter().map(Sender::asset_value).sum(), )?; - posts.push(self.build_post(Transfer::new(None, [], senders, receivers, []))?); - join.insert_utxos(&mut self.utxo_set); - joins.push(join.pre_sender); new_zeroes.append(&mut join.zeroes); } - joins.append(&mut iter.remainder()); pre_senders = joins; } - self.prepare_final_pre_senders::<SENDERS>(asset_id, new_zeroes, &mut pre_senders, posts)?; - Ok(into_array_unchecked( pre_senders .into_iter() @@ -653,7 +631,7 @@ where ) } - /// Signs a withdraw transaction. + /// Signs a withdraw transaction for `asset` sent to `receiver`. #[inline] fn sign_withdraw( &mut self, @@ -665,7 +643,7 @@ where let selection = self.select(asset)?; let change = self.build_receiver(asset.id.with(selection.change))?; let mut posts = Vec::new(); - let senders = self.compute_batched_transaction::<SENDERS, RECEIVERS>( + let senders = self.compute_batched_transactions::<SENDERS, RECEIVERS>( asset.id, selection.pre_senders, &mut posts, @@ -748,45 +726,3 @@ where future::ready(self.receiving_key(index)) } } - -/* TODO[remove]: -impl<D> Load for Signer<D> -where - D: DerivedSecretKeyGenerator + LoadWith<Account<D>>, -{ - type Path = D::Path; - - type LoadingKey = D::LoadingKey; - - type Error = <D as Load>::Error; - - #[inline] - fn load<P>(path: P, loading_key: &Self::LoadingKey) -> Result<Self, Self::Error> - where - P: AsRef<Self::Path>, - { - let (secret_key_source, account) = D::load_with(path, loading_key)?; - Ok(Self::with_account(secret_key_source, account)) - } -} - -impl<D> Save for Signer<D> -where - D: DerivedSecretKeyGenerator + SaveWith<Account<D>>, -{ - type Path = D::Path; - - type SavingKey = D::SavingKey; - - type Error = <D as Save>::Error; - - #[inline] - fn save<P>(self, path: P, saving_key: &Self::SavingKey) -> Result<(), Self::Error> - where - P: AsRef<Self::Path>, - { - self.secret_key_source - .save_with(self.account, path, saving_key) - } -} -*/ diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 0826b8060..08b8ac194 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -151,6 +151,7 @@ impl BalanceState for BTreeMapBalanceState { pub type HashMapBalanceState<S = RandomState> = HashMap<AssetId, AssetValue, S>; #[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] impl<S> BalanceState for HashMapBalanceState<S> where S: BuildHasher, diff --git a/manta-crypto/src/merkle_tree/forest.rs b/manta-crypto/src/merkle_tree/forest.rs index b8eb33117..0eb559615 100644 --- a/manta-crypto/src/merkle_tree/forest.rs +++ b/manta-crypto/src/merkle_tree/forest.rs @@ -18,6 +18,7 @@ // FIXME: Replace `N` parameter on `FixedIndex` with an associated value in the trait when // `generic_const_exprs` is stabilized and get rid of `ConstantWidthForest`. +// FIXME: Reduce code duplication between this and `tree.rs` code. use crate::{ accumulator::{ @@ -271,7 +272,7 @@ where Some(MembershipProof::new( tree.path( &self.parameters, - tree.index_of(&self.parameters.digest(item))?, + tree.position(&self.parameters.digest(item))?, ) .ok()?, tree.root().clone(), @@ -325,6 +326,14 @@ where fn insert_nonprovable(&mut self, item: &Self::Item) -> bool { self.forest.get_tree_mut(item).push(&self.parameters, item) } + + #[inline] + fn remove_proof(&mut self, item: &Self::Item) -> bool { + let tree = self.forest.get_tree_mut(item); + tree.position(&self.parameters.digest(item)) + .map(move |i| tree.remove_path(i)) + .unwrap_or(false) + } } /// Tree Array Merkle Forest Alias diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs index a89f89f94..9cac559b1 100644 --- a/manta-crypto/src/merkle_tree/fork.rs +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -16,78 +16,83 @@ //! Merkle Tree Forks -// TODO: Think about whether we want to keep the `raw::MerkleTreePointerFamily` API sealed or not. // TODO: Implement derive-able traits for these types. // TODO: See if we can get rid of the smart pointer logic. -// TODO: Implement partial merging. Will need to upgrade `PartialInnerTree`. use crate::merkle_tree::{ capacity, inner_tree::{BTreeMap, InnerMap, PartialInnerTree}, partial::Partial, path::CurrentInnerPath, - Configuration, InnerDigest, Leaf, LeafDigest, MerkleTree, Node, Parity, Tree, + Configuration, InnerDigest, Leaf, LeafDigest, Node, Parameters, Parity, Tree, }; use alloc::{vec, vec::Vec}; -use core::{borrow::Borrow, fmt::Debug, hash::Hash, mem, ops::Deref}; +use core::{borrow::Borrow, fmt::Debug, hash::Hash, marker::PhantomData, mem, ops::Deref}; +use manta_util::pointer::{self, PointerFamily}; /// Fork-able Merkle Tree -pub struct Trunk<C, T, P = raw::SingleThreaded> +pub struct Trunk<C, T, P = pointer::SingleThreaded> where C: Configuration + ?Sized, T: Tree<C>, - P: raw::MerkleTreePointerFamily<C, T>, + P: PointerFamily<T>, { /// Base Merkle Tree base: Option<P::Strong>, + + /// Type Parameter Marker + __: PhantomData<C>, } impl<C, T, P> Trunk<C, T, P> where C: Configuration + ?Sized, T: Tree<C>, - P: raw::MerkleTreePointerFamily<C, T>, + P: PointerFamily<T>, { - /// Builds a new [`Trunk`] from a reference-counted [`MerkleTree`]. + /// Builds a new [`Trunk`] from a reference-counted tree. #[inline] - fn new_inner(base: Option<P::Strong>) -> Self { - Self { base } + fn build(base: Option<P::Strong>) -> Self { + Self { + base, + __: PhantomData, + } } /// Builds a new [`Trunk`] from a `base` merkle tree. #[inline] - pub fn new(base: MerkleTree<C, T>) -> Self { - Self::new_inner(Some(P::new(base))) + pub fn new(base: T) -> Self { + Self::build(Some(P::new(base))) } - /// Converts `self` back into its inner [`MerkleTree`]. + /// Converts `self` back into its inner [`Tree`]. /// /// # Safety /// /// This method automatically detaches all of the forks associated to this trunk. To attach them /// to another trunk, use [`Fork::attach`]. #[inline] - pub fn into_tree(self) -> MerkleTree<C, T> { + pub fn into_tree(self) -> T { P::claim(self.base.unwrap()) } /// Creates a new fork of this trunk. #[inline] - pub fn fork<M>(&self) -> Fork<C, T, P, M> + pub fn fork<M>(&self, parameters: &Parameters<C>) -> Fork<C, T, P, M> where - M: InnerMap<C> + Default, + M: Default + InnerMap<C>, { - Fork::new(self) + Fork::new(parameters, self) } /// Tries to attach `fork` to `self` as its new trunk, returning `false` if `fork` has /// too many leaves to fit in `self`. #[inline] - pub fn attach<M>(&self, fork: &mut Fork<C, T, P, M>) -> bool + pub fn attach<M>(&self, parameters: &Parameters<C>, fork: &mut Fork<C, T, P, M>) -> bool where - M: InnerMap<C> + Default, + M: Default + InnerMap<C>, { - fork.attach(self) + fork.attach(parameters, self) } /// Tries to merge `fork` onto `self`, returning `fork` back if it could not be merged. @@ -103,13 +108,17 @@ where /// the leaves which were added in this merge will exist before the first leaf in the fork in /// the final tree. #[inline] - pub fn merge<M>(&mut self, fork: Fork<C, T, P, M>) -> Result<(), Fork<C, T, P, M>> + pub fn merge<M>( + &mut self, + parameters: &Parameters<C>, + fork: Fork<C, T, P, M>, + ) -> Result<(), Fork<C, T, P, M>> where - M: InnerMap<C> + Default, + M: Default + InnerMap<C>, { match fork.get_attached_base(self) { Some(base) => { - self.merge_branch(base, fork.base_contribution, fork.branch); + self.merge_branch(parameters, base, fork.base_contribution, fork.branch); Ok(()) } _ => Err(fork), @@ -121,6 +130,7 @@ where #[inline] fn merge_branch<M>( &mut self, + parameters: &Parameters<C>, fork_base: P::Strong, base_contribution: BaseContribution, branch: Partial<C, M>, @@ -130,10 +140,10 @@ where self.base = Some(fork_base); let mut base = P::claim(mem::take(&mut self.base).unwrap()); assert!(base - .extend_digests(Fork::<C, T, P, M>::extract_leaves( - base_contribution, - branch - )) + .extend_digests( + parameters, + Fork::<C, T, P, M>::extract_leaves(base_contribution, branch) + ) .is_ok()); self.base = Some(P::new(base)); } @@ -157,26 +167,26 @@ where } } -impl<C, T, P> AsRef<MerkleTree<C, T>> for Trunk<C, T, P> +impl<C, T, P> AsRef<T> for Trunk<C, T, P> where C: Configuration + ?Sized, T: Tree<C>, - P: raw::MerkleTreePointerFamily<C, T>, + P: PointerFamily<T>, { #[inline] - fn as_ref(&self) -> &MerkleTree<C, T> { + fn as_ref(&self) -> &T { self.borrow_base().as_ref() } } -impl<C, T, P> Borrow<MerkleTree<C, T>> for Trunk<C, T, P> +impl<C, T, P> Borrow<T> for Trunk<C, T, P> where C: Configuration + ?Sized, T: Tree<C>, - P: raw::MerkleTreePointerFamily<C, T>, + P: PointerFamily<T>, { #[inline] - fn borrow(&self) -> &MerkleTree<C, T> { + fn borrow(&self) -> &T { self.borrow_base().borrow() } } @@ -185,9 +195,9 @@ impl<C, T, P> Deref for Trunk<C, T, P> where C: Configuration + ?Sized, T: Tree<C>, - P: raw::MerkleTreePointerFamily<C, T>, + P: PointerFamily<T>, { - type Target = MerkleTree<C, T>; + type Target = T; #[inline] fn deref(&self) -> &Self::Target { @@ -216,12 +226,12 @@ impl Default for BaseContribution { } /// Merkle Tree Fork -pub struct Fork<C, T, P = raw::SingleThreaded, M = BTreeMap<C>> +pub struct Fork<C, T, P = pointer::SingleThreaded, M = BTreeMap<C>> where C: Configuration + ?Sized, T: Tree<C>, - P: raw::MerkleTreePointerFamily<C, T>, - M: InnerMap<C> + Default, + P: PointerFamily<T>, + M: Default + InnerMap<C>, { /// Base Merkle Tree base: P::Weak, @@ -237,21 +247,25 @@ impl<C, T, P, M> Fork<C, T, P, M> where C: Configuration + ?Sized, T: Tree<C>, - P: raw::MerkleTreePointerFamily<C, T>, - M: InnerMap<C> + Default, + P: PointerFamily<T>, + M: Default + InnerMap<C>, { /// Builds a new [`Fork`] from `trunk`. #[inline] - pub fn new(trunk: &Trunk<C, T, P>) -> Self { - Self::with_leaves(trunk, Default::default()).unwrap() + pub fn new(parameters: &Parameters<C>, trunk: &Trunk<C, T, P>) -> Self { + Self::with_leaves(parameters, trunk, Default::default()).unwrap() } /// Builds a new [`Fork`] from `trunk` extended by `leaf_digests`, returning `None` if /// appending `leaf_digests` would exceed the capacity of the `trunk`. #[inline] - pub fn with_leaves(trunk: &Trunk<C, T, P>, leaf_digests: Vec<LeafDigest<C>>) -> Option<Self> { + pub fn with_leaves( + parameters: &Parameters<C>, + trunk: &Trunk<C, T, P>, + leaf_digests: Vec<LeafDigest<C>>, + ) -> Option<Self> { let (base_contribution, branch) = - Self::new_branch(trunk.borrow_base().as_ref(), leaf_digests)?; + Self::new_branch(parameters, trunk.borrow_base().as_ref(), leaf_digests)?; Some(Self { base: trunk.downgrade(), base_contribution, @@ -262,31 +276,33 @@ where /// Builds a new branch off of `base`, extending by `leaf_digests`. #[inline] fn new_branch( - base: &MerkleTree<C, T>, + parameters: &Parameters<C>, + base: &T, leaf_digests: Vec<LeafDigest<C>>, ) -> Option<(BaseContribution, Partial<C, M>)> { if leaf_digests.len() + base.len() >= capacity::<C>() { return None; } - Some(Self::new_branch_unchecked(base, leaf_digests)) + Some(Self::new_branch_unchecked(parameters, base, leaf_digests)) } /// Builds a new branch off of `base`, extending by `leaf_digests` without checking that /// `base` can accept new leaves. #[inline] fn new_branch_unchecked( - base: &MerkleTree<C, T>, + parameters: &Parameters<C>, + base: &T, leaf_digests: Vec<LeafDigest<C>>, ) -> (BaseContribution, Partial<C, M>) { let (base_contribution, base_inner_digest, base_leaf_digests, inner_path) = - Self::generate_branch_setup(base); + Self::generate_branch_setup(parameters, base); let mut partial = Partial::new_unchecked( base_leaf_digests, - PartialInnerTree::from_current(base.parameters(), base_inner_digest, inner_path), + PartialInnerTree::from_current(parameters, base_inner_digest, inner_path), ); let partial_tree_len = partial.len(); for (i, digest) in leaf_digests.into_iter().enumerate() { - partial.push_leaf_digest(base.parameters(), Node(partial_tree_len + i), digest); + partial.push_leaf_digest(parameters, Node(partial_tree_len + i), digest); } (base_contribution, partial) } @@ -294,7 +310,8 @@ where /// Generates the setup data to compute [`new_branch_unchecked`](Self::new_branch_unchecked). #[inline] fn generate_branch_setup( - base: &MerkleTree<C, T>, + parameters: &Parameters<C>, + base: &T, ) -> ( BaseContribution, InnerDigest<C>, @@ -306,23 +323,21 @@ where BaseContribution::Empty, Default::default(), Default::default(), - base.current_path().inner_path, + base.current_path(parameters).inner_path, ) } else { let current_leaf = base.current_leaf(); - let current_path = base.current_path(); + let current_path = base.current_path(parameters); match current_path.leaf_index().parity() { Parity::Left => ( BaseContribution::LeftLeaf, - base.parameters() - .join_leaves(&current_leaf, &current_path.sibling_digest), + parameters.join_leaves(&current_leaf, &current_path.sibling_digest), vec![current_leaf], current_path.inner_path, ), Parity::Right => ( BaseContribution::BothLeaves, - base.parameters() - .join_leaves(&current_path.sibling_digest, &current_leaf), + parameters.join_leaves(&current_path.sibling_digest, &current_leaf), vec![current_path.sibling_digest, current_leaf], current_path.inner_path, ), @@ -344,7 +359,8 @@ where /// Tries to rebase `branch` at `base`. #[inline] fn try_rebase( - base: &MerkleTree<C, T>, + parameters: &Parameters<C>, + base: &T, base_contribution: &mut BaseContribution, branch: &mut Partial<C, M>, ) -> bool { @@ -352,6 +368,7 @@ where return false; } let (new_base_contribution, new_branch) = Self::new_branch_unchecked( + parameters, base, Self::extract_leaves(*base_contribution, mem::take(branch)), ); @@ -363,8 +380,9 @@ where /// Tries to attach this fork to a new `trunk`, returning `false` if `self` has too many leaves /// to fit in `trunk`. #[inline] - pub fn attach(&mut self, trunk: &Trunk<C, T, P>) -> bool { + pub fn attach(&mut self, parameters: &Parameters<C>, trunk: &Trunk<C, T, P>) -> bool { if !Self::try_rebase( + parameters, trunk.borrow_base().as_ref(), &mut self.base_contribution, &mut self.branch, @@ -419,116 +437,8 @@ where /// Returns `None` if this fork has been detached from its trunk. Use [`attach`](Self::attach) /// to re-associate a trunk to this fork. #[inline] - pub fn push(&mut self, leaf: &Leaf<C>) -> Option<bool> { - Some( - self.branch - .push(P::upgrade(&self.base)?.as_ref().parameters(), leaf), - ) + pub fn push(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> Option<bool> { + let _ = P::upgrade(&self.base)?; + Some(self.branch.push(parameters, leaf)) } } - -/// Raw Forking Primitives -pub mod raw { - use super::*; - use alloc::{ - rc::{Rc, Weak as WeakRc}, - sync::{Arc, Weak as WeakArc}, - }; - use manta_util::{create_seal, seal}; - - create_seal! {} - - /// Merkle Tree Pointer Family - pub trait MerkleTreePointerFamily<C, T>: sealed::Sealed - where - C: Configuration + ?Sized, - T: Tree<C>, - { - /// Strong Pointer - type Strong: AsRef<MerkleTree<C, T>> - + Borrow<MerkleTree<C, T>> - + Deref<Target = MerkleTree<C, T>>; - - /// Weak Pointer - type Weak; - - /// Returns a new strong pointer holding `base`. - fn new(base: MerkleTree<C, T>) -> Self::Strong; - - /// Claims ownership of the underlying merkle tree from `strong`. - /// - /// # Panics - /// - /// This method can only panic if there are other outstanding strong pointers. This method - /// will still succeed if there are other outstanding weak pointers, but they will all be - /// disassociated to `strong`. - fn claim(strong: Self::Strong) -> MerkleTree<C, T>; - - /// Returns a new weak pointer to `strong`. - fn downgrade(strong: &Self::Strong) -> Self::Weak; - - /// Tries to upgrade `weak` to a strong pointer, returning `None` if there is no strong - /// pointer associated to `weak`. - fn upgrade(weak: &Self::Weak) -> Option<Self::Strong>; - - /// Checks if two strong pointers point to the same allocation. - fn strong_ptr_eq(lhs: &Self::Strong, rhs: &Self::Strong) -> bool; - } - - /// Implements [`MerkleTreePointerFamily`] for `$type` with `$strong` and `$weak` pointers. - macro_rules! impl_pointer_family { - ($type:tt, $strong:ident, $weak:ident) => { - seal!($type); - impl<C, T> MerkleTreePointerFamily<C, T> for $type - where - C: Configuration + ?Sized, - T: Tree<C>, - { - type Strong = $strong<MerkleTree<C, T>>; - - type Weak = $weak<MerkleTree<C, T>>; - - #[inline] - fn new(base: MerkleTree<C, T>) -> Self::Strong { - $strong::new(base) - } - - #[inline] - fn claim(strong: Self::Strong) -> MerkleTree<C, T> { - $strong::try_unwrap(strong).ok().unwrap() - } - - #[inline] - fn downgrade(strong: &Self::Strong) -> Self::Weak { - $strong::downgrade(strong) - } - - #[inline] - fn upgrade(weak: &Self::Weak) -> Option<Self::Strong> { - weak.upgrade() - } - - #[inline] - fn strong_ptr_eq(lhs: &Self::Strong, rhs: &Self::Strong) -> bool { - $strong::ptr_eq(lhs, rhs) - } - } - }; - } - - /// Single-Threaded Merkle Tree Pointer Family - /// - /// This is the pointer family for [`Rc`]. - #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct SingleThreaded; - - impl_pointer_family!(SingleThreaded, Rc, WeakRc); - - /// Thread-Safe Merkle Tree Pointer Family - /// - /// This is the pointer family for [`Arc`]. - #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct ThreadSafe; - - impl_pointer_family!(ThreadSafe, Arc, WeakArc); -} diff --git a/manta-crypto/src/merkle_tree/full.rs b/manta-crypto/src/merkle_tree/full.rs index 7374331c9..c1b5a7589 100644 --- a/manta-crypto/src/merkle_tree/full.rs +++ b/manta-crypto/src/merkle_tree/full.rs @@ -188,7 +188,7 @@ where impl<C, M> WithProofs<C> for Full<C, M> where C: Configuration + ?Sized, - M: InnerMap<C> + Default, + M: Default + InnerMap<C>, LeafDigest<C>: Clone + PartialEq, { #[inline] @@ -197,7 +197,7 @@ where } #[inline] - fn index_of(&self, leaf_digest: &LeafDigest<C>) -> Option<usize> { + fn position(&self, leaf_digest: &LeafDigest<C>) -> Option<usize> { self.leaf_digests.iter().position(move |d| d == leaf_digest) } diff --git a/manta-crypto/src/merkle_tree/partial.rs b/manta-crypto/src/merkle_tree/partial.rs index c05b68468..d1a131d94 100644 --- a/manta-crypto/src/merkle_tree/partial.rs +++ b/manta-crypto/src/merkle_tree/partial.rs @@ -21,8 +21,8 @@ use crate::merkle_tree::{ capacity, inner_tree::{BTreeMap, InnerMap, PartialInnerTree}, - Configuration, CurrentPath, InnerDigest, Leaf, LeafDigest, MerkleTree, Node, Parameters, Root, - Tree, + Configuration, CurrentPath, InnerDigest, Leaf, LeafDigest, MerkleTree, Node, Parameters, Path, + PathError, Root, Tree, WithProofs, }; use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; @@ -210,6 +210,7 @@ where where F: FnOnce() -> Option<LeafDigest<C>>, { + // TODO: Push without keeping unnecessary proof. let len = self.len(); if len >= capacity::<C>() { return Some(false); @@ -219,20 +220,39 @@ where } } -/* TODO: Implement `WithProofs` for `Partial` - impl<C, M> WithProofs<C> for Partial<C, M> where C: Configuration + ?Sized, - M: InnerMap<C>, - LeafDigest<C>: Clone, + M: Default + InnerMap<C>, + LeafDigest<C>: Clone + PartialEq, { - type Error = (); + #[inline] + fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>> { + self.leaf_digests.get(index) + } + + #[inline] + fn position(&self, leaf_digest: &LeafDigest<C>) -> Option<usize> { + self.leaf_digests.iter().position(move |d| d == leaf_digest) + } + + #[inline] + fn maybe_push_provable_digest<F>( + &mut self, + parameters: &Parameters<C>, + leaf_digest: F, + ) -> Option<bool> + where + F: FnOnce() -> Option<LeafDigest<C>>, + { + self.maybe_push_digest(parameters, leaf_digest) + } #[inline] - fn path(&self, parameters: &Parameters<C>, index: usize) -> Result<Path<C>, Self::Error> { + fn path(&self, parameters: &Parameters<C>, index: usize) -> Result<Path<C>, PathError> { + // FIXME: Implement this: // TODO: Make sure we don't query paths too far to the left. - /* TODO: + /* let _ = parameters; if index > 0 && index >= self.leaf_digests.len() { return Err(()); @@ -244,8 +264,14 @@ where self.inner_digests.path_unchecked(leaf_index), )) */ + let _ = (parameters, index); todo!() } -} -*/ + #[inline] + fn remove_path(&mut self, index: usize) -> bool { + // TODO: Implement this optimization. + let _ = index; + false + } +} diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index b63dc1b00..2bdfa50da 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -20,8 +20,6 @@ // implementations for the trivial tree sizes? // TODO: Add "copy-on-write" adapters for `Root` and `Path`, and see if we can incorporate them // into `Tree`. -// TODO: Should we add optimization paths for when `cloning` the default value is cheaper than -// creating a new one from scratch (for inner digests)? use crate::{ accumulator::{ @@ -29,11 +27,12 @@ use crate::{ OptimizedAccumulator, }, merkle_tree::{ - fork::{self, Trunk}, + fork::Trunk, path::{CurrentPath, Path}, }, }; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; +use manta_util::pointer::PointerFamily; /// Merkle Tree Leaf Hash pub trait LeafHash { @@ -329,7 +328,7 @@ where /// # Implementation Note /// /// This method is allowed to return `None` even if `index` is less than the current length of - /// the tree. See [`index_of`](Self::index_of) for more. + /// the tree. See [`position`](Self::position) for more. fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>>; /// Returns the index of the `leaf_digest` if it is contained in `self`. @@ -339,15 +338,15 @@ where /// This method is allowed to return `None` even if `leaf_digest` was inserted with a call to /// [`push_digest`](Tree::push_digest). This method need only return an index for leaves which /// are inserted with a call to [`push_provable`](Self::push_provable). - fn index_of(&self, leaf_digest: &LeafDigest<C>) -> Option<usize>; + fn position(&self, leaf_digest: &LeafDigest<C>) -> Option<usize>; /// Returns `true` if `leaf_digest` is provably stored in `self`. /// - /// See the [`index_of`](Self::index_of) and [`push_provable`](Self::push_provable) methods + /// See the [`position`](Self::position) and [`push_provable`](Self::push_provable) methods /// for more. #[inline] fn contains(&self, leaf_digest: &LeafDigest<C>) -> bool { - self.index_of(leaf_digest).is_some() + self.position(leaf_digest).is_some() } /// Checks if a leaf can be inserted into the tree and if it can, it runs `leaf_digest` to @@ -382,27 +381,19 @@ where /// Returns the path for the leaf stored at the given `index` if it exists. fn path(&self, parameters: &Parameters<C>, index: usize) -> Result<Path<C>, PathError>; -} -/* TODO: -/// Merkle Tree Removable Membership Proof Mixin -pub trait WithRemovableProofs<C>: WithProofs<C> -where - C: Configuration + ?Sized, -{ + /// Removes a single path at the given `index`, returning `true` if it was removed. /// - fn remove_paths<R>(&mut self, range: R) -> bool - where - R: RangeBounds<usize>; - + /// # Implementation Note /// + /// This method may return `false` for arbitrary inputs and is only an optimization path for + /// removing unused memory. #[inline] fn remove_path(&mut self, index: usize) -> bool { - self.remove_paths(index..=index) + let _ = index; + false } - } -*/ /// Digest Type #[derive(derivative::Derivative)] @@ -591,6 +582,15 @@ where Self { tree, parameters } } + /// Builds a new [`MerkleTree`] from a `trunk` and `parameters`. + #[inline] + pub fn from_trunk<P>(trunk: Trunk<C, T, P>, parameters: Parameters<C>) -> Self + where + P: PointerFamily<T>, + { + Self::from_tree(trunk.into_tree(), parameters) + } + /// Returns a shared reference to the parameters used by this merkle tree. #[inline] pub fn parameters(&self) -> &Parameters<C> { @@ -704,13 +704,13 @@ where /// Returns the index of the `leaf_digest` if it is contained in `self`. /// - /// See [`WithProofs::index_of`] for more. + /// See [`WithProofs::position`] for more. #[inline] - pub fn index_of(&self, leaf_digest: &LeafDigest<C>) -> Option<usize> + pub fn position(&self, leaf_digest: &LeafDigest<C>) -> Option<usize> where T: WithProofs<C>, { - self.tree.index_of(leaf_digest) + self.tree.position(leaf_digest) } /// Returns `true` if `leaf_digest` is provably stored in `self`. @@ -753,9 +753,9 @@ where #[inline] pub fn into_trunk<P>(self) -> Trunk<C, T, P> where - P: fork::raw::MerkleTreePointerFamily<C, T>, + P: PointerFamily<T>, { - Trunk::new(self) + Trunk::new(self.tree) } /// Extracts the parameters of the merkle tree, dropping the internal tree. @@ -809,7 +809,7 @@ where #[inline] fn prove(&self, item: &Self::Item) -> Option<MembershipProof<Self::Verifier>> { Some(MembershipProof::new( - self.path(self.index_of(&self.parameters.digest(item))?) + self.path(self.position(&self.parameters.digest(item))?) .ok()?, self.root().clone(), )) @@ -857,4 +857,12 @@ where fn insert_nonprovable(&mut self, item: &Self::Item) -> bool { self.push(item) } + + #[inline] + fn remove_proof(&mut self, item: &Self::Item) -> bool { + self.tree + .position(&self.parameters.digest(item)) + .map(move |i| self.tree.remove_path(i)) + .unwrap_or(false) + } } diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index efc7d2441..65cabbc79 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -29,6 +29,7 @@ mod sealed; pub mod iter; pub mod num; +pub mod pointer; pub use array::*; pub use concat::*; diff --git a/manta-util/src/pointer.rs b/manta-util/src/pointer.rs new file mode 100644 index 000000000..f50126330 --- /dev/null +++ b/manta-util/src/pointer.rs @@ -0,0 +1,111 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Pointer Utilities + +// TODO: Think about whether we want to keep the `PointerFamily` API sealed or not. + +use crate::{create_seal, seal}; +use alloc::{rc::Weak as WeakRc, sync::Weak as WeakArc}; +use core::{borrow::Borrow, ops::Deref}; + +create_seal! {} + +pub use alloc::{rc::Rc, sync::Arc}; + +/// Pointer Family +pub trait PointerFamily<T>: sealed::Sealed { + /// Strong Pointer + type Strong: AsRef<T> + Borrow<T> + Deref<Target = T>; + + /// Weak Pointer + type Weak; + + /// Returns a new strong pointer holding `base`. + fn new(base: T) -> Self::Strong; + + /// Claims ownership of the underlying merkle tree from `strong`. + /// + /// # Panics + /// + /// This method can only panic if there are other outstanding strong pointers. This method + /// will still succeed if there are other outstanding weak pointers, but they will all be + /// disassociated to `strong`. + fn claim(strong: Self::Strong) -> T; + + /// Returns a new weak pointer to `strong`. + fn downgrade(strong: &Self::Strong) -> Self::Weak; + + /// Tries to upgrade `weak` to a strong pointer, returning `None` if there is no strong + /// pointer associated to `weak`. + fn upgrade(weak: &Self::Weak) -> Option<Self::Strong>; + + /// Checks if two strong pointers point to the same allocation. + fn strong_ptr_eq(lhs: &Self::Strong, rhs: &Self::Strong) -> bool; +} + +/// Implements [`PointerFamily`] for `$type` with `$strong` and `$weak` pointers. +macro_rules! impl_pointer_family { + ($type:tt, $strong:ident, $weak:ident) => { + seal!($type); + impl<T> PointerFamily<T> for $type { + type Strong = $strong<T>; + + type Weak = $weak<T>; + + #[inline] + fn new(base: T) -> Self::Strong { + $strong::new(base) + } + + #[inline] + fn claim(strong: Self::Strong) -> T { + $strong::try_unwrap(strong).ok().unwrap() + } + + #[inline] + fn downgrade(strong: &Self::Strong) -> Self::Weak { + $strong::downgrade(strong) + } + + #[inline] + fn upgrade(weak: &Self::Weak) -> Option<Self::Strong> { + weak.upgrade() + } + + #[inline] + fn strong_ptr_eq(lhs: &Self::Strong, rhs: &Self::Strong) -> bool { + $strong::ptr_eq(lhs, rhs) + } + } + }; +} + +/// Single-Threaded Pointer Family +/// +/// This is the pointer family for [`Rc`]. +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct SingleThreaded; + +impl_pointer_family!(SingleThreaded, Rc, WeakRc); + +/// Thread-Safe Pointer Family +/// +/// This is the pointer family for [`Arc`]. +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct ThreadSafe; + +impl_pointer_family!(ThreadSafe, Arc, WeakArc); From 9ae29c5dce8041e1164599619c1879429eceb871 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 27 Dec 2021 18:48:06 -0500 Subject: [PATCH 143/275] wip: start moving to new crypto system --- Cargo.toml | 4 - manta-accounting/src/key.rs | 6 +- manta-accounting/src/transfer/batch.rs | 1 + manta-accounting/src/transfer/canonical.rs | 1 + manta-pay/Cargo.toml | 44 +- manta-pay/benches/main.rs | 154 +---- manta-pay/src/accounting/config.rs | 153 ----- manta-pay/src/accounting/identity.rs | 95 --- manta-pay/src/accounting/key.rs | 45 +- manta-pay/src/accounting/mod.rs | 144 +++- manta-pay/src/accounting/transfer.rs | 23 +- manta-pay/src/crypto/commitment/mod.rs | 1 + manta-pay/src/crypto/commitment/pedersen.rs | 621 ++---------------- manta-pay/src/crypto/commitment/poseidon.rs | 212 ++++++ .../dusk_network/proof_systems/mod.rs | 17 - manta-pay/src/crypto/constraint/mod.rs | 6 +- .../constraint_system.rs | 2 +- .../{dusk_network => zk_garage}/mod.rs | 2 +- .../zk_garage/proof_systems/mod.rs} | 4 +- manta-pay/src/crypto/key.rs | 57 +- manta-pay/src/crypto/mod.rs | 4 +- manta-pay/src/lib.rs | 4 +- 22 files changed, 532 insertions(+), 1068 deletions(-) delete mode 100644 manta-pay/src/accounting/config.rs delete mode 100644 manta-pay/src/accounting/identity.rs create mode 100644 manta-pay/src/crypto/commitment/poseidon.rs delete mode 100644 manta-pay/src/crypto/constraint/dusk_network/proof_systems/mod.rs rename manta-pay/src/crypto/constraint/{dusk_network => zk_garage}/constraint_system.rs (93%) rename manta-pay/src/crypto/constraint/{dusk_network => zk_garage}/mod.rs (92%) rename manta-pay/{build.rs => src/crypto/constraint/zk_garage/proof_systems/mod.rs} (94%) diff --git a/Cargo.toml b/Cargo.toml index 131a8d9b9..f5dd5f9b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,3 @@ manta-crypto = { path = "manta-crypto" } manta-pay = { path = "manta-pay" } parity-scale-codec = { version = "2.2.0", default-features = false } -[dev-dependencies] -manta-accounting = { path = "manta-accounting", features = ["test"] } -manta-crypto = { path = "manta-crypto", features = ["test"] } - diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index 668582e21..7e948a1c3 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -20,9 +20,7 @@ use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; /// Hierarchical Key Derivation Parameter -pub trait HierarchicalKeyDerivationParameter: - Copy + Default + PartialOrd + From<usize> + Into<usize> -{ +pub trait HierarchicalKeyDerivationParameter: Copy + Default + PartialOrd { /// Increments the key parameter by one unit. fn increment(&mut self); } @@ -30,7 +28,7 @@ pub trait HierarchicalKeyDerivationParameter: /// Hierarchical Key Derivation Scheme pub trait HierarchicalKeyDerivationScheme { /// Account Type - type Account: HierarchicalKeyDerivationParameter; + type Account: HierarchicalKeyDerivationParameter + From<usize> + Into<usize>; /// Index Type type Index: HierarchicalKeyDerivationParameter; diff --git a/manta-accounting/src/transfer/batch.rs b/manta-accounting/src/transfer/batch.rs index 7bb80b6ee..9e6797331 100644 --- a/manta-accounting/src/transfer/batch.rs +++ b/manta-accounting/src/transfer/batch.rs @@ -23,6 +23,7 @@ use crate::{ SpendingKey, Utxo, }, }; +use alloc::vec::Vec; use manta_crypto::{ accumulator::Accumulator, rand::{CryptoRng, Rand, RngCore}, diff --git a/manta-accounting/src/transfer/canonical.rs b/manta-accounting/src/transfer/canonical.rs index d1ebfcab3..69753eef5 100644 --- a/manta-accounting/src/transfer/canonical.rs +++ b/manta-accounting/src/transfer/canonical.rs @@ -20,6 +20,7 @@ use crate::{ asset::{self, Asset, AssetValue}, transfer::{Configuration, PreSender, Receiver, ReceivingKey, Sender, Transfer}, }; +use alloc::vec::Vec; use manta_util::{create_seal, seal}; create_seal! {} diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 1faa49f99..809994e9b 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -29,22 +29,22 @@ name = "main" harness = false [features] -# Default Features -default = ["arkworks-groth16"] +# Enable Arkworks Backend +arkworks = [ + "ark-bls12-381", + "ark-crypto-primitives", + "ark-ec", + "ark-ed-on-bls12-381", + "ark-ff", + "ark-r1cs-std", + "ark-relations", +] -# Enable Arkworks Zero-Knowledge Proof Backends -arkworks-zkp = [] -# Arkworks Groth16 Backend -arkworks-groth16 = ["arkworks-zkp", "ark-groth16"] -# Enable All Arkworks Zero-Knowledge Proof Backends -arkworks-zkp-all = ["arkworks-groth16"] +# Enable Groth16 ZKP System +groth16 = ["arkworks", "ark-groth16"] -# Enable Dusk-Netowrk Zero-Knowledge Proof Backends -dusk-network-zkp = [] -# Dusk-Network Plonk Backend -dusk-network-plonk = ["dusk-network-zkp", "dusk-plonk"] -# Enable All Dusk-Network Zero-Knowledge Proof Backends -dusk-network-zkp-all = ["dusk-network-plonk"] +# Enable PLONK ZKP System +plonk = ["zk-garage-plonk"] # Testing Frameworks test = ["manta-accounting/test", "manta-crypto/test"] @@ -54,18 +54,18 @@ std = [] [dependencies] aes-gcm = { version = "0.9.4", default-features = false, features = ["aes", "alloc"] } -ark-bls12-381 = { version = "0.3.0", default-features = false, features = ["curve"] } -ark-crypto-primitives = { version = "0.3.0", default-features = false, features = ["r1cs"] } -ark-ec = { version = "0.3.0", default-features = false } -ark-ed-on-bls12-381 = { version = "0.3.0", default-features = false, features = ["r1cs"] } -ark-ff = { version = "0.3.0", default-features = false } +ark-bls12-381 = { version = "0.3.0", optional = true, default-features = false, features = ["curve"] } +ark-crypto-primitives = { version = "0.3.0", optional = true, default-features = false, features = ["r1cs"] } +ark-ec = { version = "0.3.0", optional = true, default-features = false } +ark-ed-on-bls12-381 = { version = "0.3.0", optional = true, default-features = false, features = ["r1cs"] } +ark-ff = { version = "0.3.0", optional = true, default-features = false } ark-groth16 = { version = "0.3.0", optional = true, default-features = false } -ark-r1cs-std = { version = "0.3.1", default-features = false } -ark-relations = { version = "0.3.0", default-features = false } +ark-r1cs-std = { version = "0.3.1", optional = true, default-features = false } +ark-relations = { version = "0.3.0", optional = true, default-features = false } bip32 = { version = "0.2.2", default-features = false, features = ["bip39", "secp256k1"] } blake2 = { version = "0.10.0", default-features = false } derivative = { version = "2.2.0", default-features = false } -dusk-plonk = { version = "0.9.0", optional = true, default-features = false } +zk-garage-plonk = { package = "ark-plonk", git = "https://github.com/zk-garage/plonk", optional = true, default-features = false } generic-array = { version = "0.14.4", default-features = false } manta-accounting = { path = "../manta-accounting" } manta-crypto = { path = "../manta-crypto" } diff --git a/manta-pay/benches/main.rs b/manta-pay/benches/main.rs index 1d6edd584..baca3dd05 100644 --- a/manta-pay/benches/main.rs +++ b/manta-pay/benches/main.rs @@ -14,156 +14,4 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Benchmarks - -extern crate manta_pay; - -use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use manta_accounting::transfer::test; -use manta_crypto::{ - accumulator::Accumulator, - rand::{Rand, TrySample}, -}; -use manta_pay::accounting::{ - identity::UtxoSet, - transfer::{Mint, PrivateTransfer, Reclaim}, -}; -use rand::thread_rng; - -/// Runs the following benchmarks for a given transfer: -/// 1. Constraint generation -/// 2. TODO: Constraint satisfaction check -/// 3. TODO: ZKP Setup generating Proving Key and Verifying Key -/// 4. TODO: ZKP Prove with Proving Key -/// 5. TODO: ZKP Verify with Verifying Key -/// 6. TODO: ZKP Verify with Processed Verifying Key -macro_rules! transfer_benchmark { - ( - $c:ident, - $name:expr, - $rng:ident, - $transfer:ident, - $commitment_scheme: ident, - $utxo_set_verifier: ident - ) => { - let mut group = $c.benchmark_group($name); - - group.bench_function("generate-constraints", |b| { - b.iter(|| { - black_box($transfer.known_constraints(&$commitment_scheme, &$utxo_set_verifier)); - }) - }); - - let _known_constraints = - $transfer.known_constraints(&$commitment_scheme, &$utxo_set_verifier); - - /* TODO: - group.bench_function("is-satisfied", |b| { - b.iter(|| { - black_box(constraints.is_satisfied().unwrap()); - }) - }); - */ - - /* TODO: - group.bench_function("zkp-setup", |b| { - b.iter_batched( - || $circuit.clone(), - |c| { - black_box(Groth::generate_(c, &mut $rng).unwrap()); - }, - BatchSize::SmallInput, - ) - }); - - let (pk, vk) = Groth::circuit_specific_setup($circuit.clone(), &mut $rng).unwrap(); - - group.bench_function("zkp-prove", |b| { - b.iter_batched( - || $circuit.clone(), - |c| { - black_box(Groth::prove(&pk, c, &mut $rng).unwrap()); - }, - BatchSize::SmallInput, - ) - }); - - let proof = Groth::prove(&pk, $circuit.clone(), &mut $rng).unwrap(); - let input = $circuit.raw_input().unwrap(); - - group.bench_function("zkp-verify", |b| { - b.iter(|| { - black_box(Groth::verify(&vk, &input, &proof).unwrap()); - }) - }); - - let pvk = Groth::process_vk(&vk).unwrap(); - - group.bench_function("zkp-verify-processed", |b| { - b.iter(|| { - black_box(Groth::verify_with_processed_vk(&pvk, &input, &proof).unwrap()); - }) - }); - */ - - group.finish(); - }; -} - -/// Runs the circuit benchmark for the [`Mint`] transfer. -fn mint(c: &mut Criterion) { - let mut rng = thread_rng(); - let commitment_scheme = rng.gen(); - let mut utxo_set = UtxoSet::new(rng.gen()); - let mint = Mint::try_sample( - test::distribution::Transfer { - commitment_scheme: &commitment_scheme, - utxo_set: &mut utxo_set, - }, - &mut rng, - ) - .unwrap(); - transfer_benchmark!(c, "Mint", rng, mint, commitment_scheme, utxo_set); -} - -/// Runs the circuit benchmark for the [`PrivateTransfer`] transfer. -fn private_transfer(c: &mut Criterion) { - let mut rng = thread_rng(); - let commitment_scheme = rng.gen(); - let mut utxo_set = UtxoSet::new(rng.gen()); - let private_transfer = PrivateTransfer::try_sample( - test::distribution::Transfer { - commitment_scheme: &commitment_scheme, - utxo_set: &mut utxo_set, - }, - &mut rng, - ) - .unwrap(); - transfer_benchmark!( - c, - "PrivateTransfer", - rng, - private_transfer, - commitment_scheme, - utxo_set - ); -} - -/// Runs the circuit benchmark for the [`Reclaim`] transfer. -fn reclaim(c: &mut Criterion) { - let mut rng = thread_rng(); - let commitment_scheme = rng.gen(); - let mut utxo_set = UtxoSet::new(rng.gen()); - let reclaim = Reclaim::try_sample( - test::distribution::Transfer { - commitment_scheme: &commitment_scheme, - utxo_set: &mut utxo_set, - }, - &mut rng, - ) - .unwrap(); - transfer_benchmark!(c, "Reclaim", rng, reclaim, commitment_scheme, utxo_set); -} - -criterion_group!(circuits, mint, private_transfer, reclaim); -criterion_main!(circuits); +//! Manta-Pay Benchmarks diff --git a/manta-pay/src/accounting/config.rs b/manta-pay/src/accounting/config.rs deleted file mode 100644 index 8926f1df8..000000000 --- a/manta-pay/src/accounting/config.rs +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Identity and Transfer Configurations - -// TODO: Make this generic over the backend we use. Automatically compute which features are -// enabled when using whichever backend. - -use crate::{ - accounting::identity::{Parameters, Utxo}, - crypto::{ - commitment::pedersen::{self, PedersenWindow}, - constraint::arkworks::{ - proof_systems::groth16::Groth16, ArkConstraintSystem, AssetIdVar, AssetValueVar, - }, - key::EllipticCurveDiffieHellman, - merkle_tree::{ - constraint as merkle_tree_constraint, ConfigConverter as ArkMerkleTreeConfigConverter, - Configuration as ArkMerkleTreeConfiguration, - }, - }, -}; -use ark_bls12_381::Bls12_381; -use ark_crypto_primitives::crh::pedersen::{constraints::CRHGadget, CRH}; -use ark_ed_on_bls12_381::{constraints::EdwardsVar, EdwardsProjective, Fq}; -use manta_accounting::{asset::Asset, identity, transfer}; -use manta_crypto::{commitment::CommitmentScheme, merkle_tree, rand::SeedIntoRng}; -use rand_chacha::ChaCha20Rng; - -/// Pedersen Window Parameters -#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct PedersenCommitmentWindowParameters; - -impl PedersenWindow for PedersenCommitmentWindowParameters { - const WINDOW_SIZE: usize = 4; - const NUM_WINDOWS: usize = 256; -} - -/// Pedersen Commitment Projective Curve -pub type PedersenCommitmentProjectiveCurve = EdwardsProjective; - -/// Pedersen Commitment Projective Curve -pub type PedersenCommitmentProjectiveCurveVar = EdwardsVar; - -/// Pedersen Commitment Scheme -pub type PedersenCommitment = pedersen::constraint::PedersenCommitmentWrapper< - PedersenCommitmentWindowParameters, - PedersenCommitmentProjectiveCurve, - PedersenCommitmentProjectiveCurveVar, ->; - -/// Pedersen Commitment Scheme Variable -pub type PedersenCommitmentVar = pedersen::constraint::PedersenCommitmentVar< - PedersenCommitmentWindowParameters, - PedersenCommitmentProjectiveCurve, - PedersenCommitmentProjectiveCurveVar, ->; - -/// Arkworks Pedersen Commitment Scheme -type ArkPedersenCommitment = - CRH<PedersenCommitmentProjectiveCurve, PedersenCommitmentWindowParameters>; - -/// Constraint Field -pub type ConstraintField = Fq; - -/// Constraint System -pub type ConstraintSystem = ArkConstraintSystem<ConstraintField>; - -/// Proof System -pub type ProofSystem = Groth16<Bls12_381>; - -/// Configuration Structure -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Configuration; - -impl ArkMerkleTreeConfiguration for Configuration { - type Leaf = Utxo; - type LeafHash = ArkPedersenCommitment; - type InnerHash = ArkPedersenCommitment; - type Height = u8; - - const HEIGHT: Self::Height = 20; -} - -impl merkle_tree::HashConfiguration for Configuration { - type LeafHash = - <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::HashConfiguration>::LeafHash; - type InnerHash = - <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::HashConfiguration>::InnerHash; -} - -impl merkle_tree::Configuration for Configuration { - type Height = - <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::Configuration>::Height; - - const HEIGHT: Self::Height = - <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::Configuration>::HEIGHT; -} - -impl merkle_tree_constraint::Configuration for Configuration { - type ConstraintField = ConstraintField; - type LeafHashVar = CRHGadget< - PedersenCommitmentProjectiveCurve, - PedersenCommitmentProjectiveCurveVar, - PedersenCommitmentWindowParameters, - >; - type InnerHashVar = CRHGadget< - PedersenCommitmentProjectiveCurve, - PedersenCommitmentProjectiveCurveVar, - PedersenCommitmentWindowParameters, - >; -} - -impl identity::Configuration for Configuration { - type Asset = Asset; - type KeyAgreementScheme = EllipticCurveDiffieHellman<PedersenCommitmentProjectiveCurve>; - type CommitmentScheme = PedersenCommitment; -} - -/* -/// Transfer Constraint Configuration Structure -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct TransferConstraintConfiguration; - -impl identity::Configuration for TransferConstraintConfiguration { - type Asset = AssetVar; - type KeyAgreementScheme = (); - type CommitmentScheme = (); -} - -impl transfer::ConstraintConfiguration<ConstraintSystem> for TransferConstraintConfiguration {} - -impl transfer::Configuration for Configuration { - type EncryptionScheme = (); - type UtxoSetVerifier = (); - type ConstraintSystem = ConstraintSystem; - type ConstraintConfiguration = TransferConstraintConfiguration; - type ProofSystem = ProofSystem; -} -*/ diff --git a/manta-pay/src/accounting/identity.rs b/manta-pay/src/accounting/identity.rs deleted file mode 100644 index fb01d24b7..000000000 --- a/manta-pay/src/accounting/identity.rs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Identity Implementations - -use crate::{accounting::config::Configuration, crypto::merkle_tree::ConfigConverter}; -use manta_accounting::{identity, transfer}; -use manta_crypto::merkle_tree::{self, full::Full}; - -/// Unspent Transaction Output -pub type Utxo = identity::Utxo<Configuration>; - -/// Void Number -pub type VoidNumber = identity::VoidNumber<Configuration>; - -/// UTXO Set Parameters -pub type Parameters = merkle_tree::Parameters<ConfigConverter<Configuration>>; - -/// UTXO Set Root -pub type Root = merkle_tree::Root<ConfigConverter<Configuration>>; - -/// UTXO Set Path -pub type Path = merkle_tree::Path<ConfigConverter<Configuration>>; - -/// Identity Constraint System Variables -pub mod constraint { - use super::*; - use crate::{ - accounting::config::ConstraintSystem, - crypto::merkle_tree::constraint as merkle_tree_constraint, - }; - use manta_crypto::{ - accumulator::Verifier, - constraint::{reflection::HasAllocation, Allocation, Constant, Variable}, - }; - use manta_util::concatenate; - - /// UTXO Set Parameters Variable - pub type ParametersVar = merkle_tree_constraint::ParametersVar<Configuration>; - - /// UTXO Set Root Variable - pub type RootVar = merkle_tree_constraint::RootVar<Configuration>; - - /// UTXO Set Path Variable - pub type PathVar = merkle_tree_constraint::PathVar<Configuration>; - - /// UTXO Set Verifier Variable - #[derive(Clone)] - pub struct UtxoSetVerifier(ParametersVar); - - impl Variable<ConstraintSystem> for UtxoSetVerifier { - type Type = Parameters; - - type Mode = Constant; - - #[inline] - fn new(ps: &mut ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - let (this, mode) = allocation.into_known(); - Self(this.known(ps, mode)) - } - } - - impl Verifier for UtxoSetVerifier { - type Item = UtxoVar; - - type Witness = <ParametersVar as Verifier>::Witness; - - type Output = <ParametersVar as Verifier>::Output; - - type Verification = <ParametersVar as Verifier>::Verification; - - #[inline] - fn verify( - &self, - item: &Self::Item, - witness: &Self::Witness, - output: &Self::Output, - ) -> Self::Verification { - self.0.verify(output, witness, &concatenate!(item)) - } - } -} diff --git a/manta-pay/src/accounting/key.rs b/manta-pay/src/accounting/key.rs index cd1638d19..4ac85189e 100644 --- a/manta-pay/src/accounting/key.rs +++ b/manta-pay/src/accounting/key.rs @@ -27,9 +27,7 @@ use alloc::{format, string::String}; use bip32::{Seed, XPrv}; use core::{marker::PhantomData, num::ParseIntError, str::FromStr}; -use manta_accounting::key::{ - HierarchicalKeyDerivationParameter, HierarchicalKeyDerivationScheme, SecretKeyPair, -}; +use manta_accounting::key::{HierarchicalKeyDerivationParameter, HierarchicalKeyDerivationScheme}; use manta_util::{create_seal, seal}; pub use bip32::{Error, Mnemonic}; @@ -115,7 +113,7 @@ macro_rules! impl_parameter { } } - impl_from_for_parameter!($name, bool, u8, u16, u32, u64, u128); + impl_from_for_parameter!($name, bool, u8, u16, u32); impl FromStr for $name { type Err = ParseParameterError; @@ -128,18 +126,35 @@ macro_rules! impl_parameter { }; } -/// Parameter Type -type ParameterType = u128; +/// Account Parameter Type +type AccountParameterType = u64; /// Account Parameter #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct AccountParameter(ParameterType); +pub struct AccountParameter(AccountParameterType); impl_parameter!(AccountParameter); +impl From<usize> for AccountParameter { + #[inline] + fn from(index: usize) -> Self { + Self(index as u64) + } +} + +impl From<AccountParameter> for usize { + #[inline] + fn from(parameter: AccountParameter) -> Self { + parameter.0 as usize + } +} + +/// Index Parameter Type +type IndexParameterType = u128; + /// Index Parameter #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct IndexParameter(ParameterType); +pub struct IndexParameter(IndexParameterType); impl_parameter!(IndexParameter); @@ -184,7 +199,11 @@ where /// [`BIP-0044`]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki #[inline] #[must_use] -pub fn path_string<C>(account: ParameterType, spend: ParameterType, view: ParameterType) -> String +pub fn path_string<C>( + account: AccountParameterType, + spend: IndexParameterType, + view: IndexParameterType, +) -> String where C: CoinType, { @@ -217,10 +236,10 @@ where account: Self::Account, spend: Self::Index, ) -> Result<Self::SecretKey, Self::Error> { - Ok(XPrv::derive_from_path( + XPrv::derive_from_path( &self.seed, &path_string::<C>(account.0, spend.0, 0).parse()?, - )) + ) } #[inline] @@ -230,9 +249,9 @@ where spend: Self::Index, view: Self::Index, ) -> Result<Self::SecretKey, Self::Error> { - Ok(XPrv::derive_from_path( + XPrv::derive_from_path( &self.seed, &path_string::<C>(account.0, spend.0, view.0 + 1).parse()?, - )) + ) } } diff --git a/manta-pay/src/accounting/mod.rs b/manta-pay/src/accounting/mod.rs index 22a1bf290..445cc8a7e 100644 --- a/manta-pay/src/accounting/mod.rs +++ b/manta-pay/src/accounting/mod.rs @@ -16,8 +16,146 @@ //! Accounting Implementations -pub mod config; -pub mod identity; +// TODO: Make this generic over the backend we use. Automatically compute which features are +// enabled when using whichever backend. + +use crate::crypto::key::EllipticCurveDiffieHellman; +use ark_bls12_381::Bls12_381; +use ark_crypto_primitives::crh::pedersen::{constraints::CRHGadget, CRH}; +use ark_ed_on_bls12_381::{constraints::EdwardsVar, EdwardsProjective, Fq}; +use manta_accounting::{asset::Asset, transfer}; +use manta_crypto::{commitment::CommitmentScheme, key::KeyAgreementScheme, merkle_tree}; +use rand_chacha::ChaCha20Rng; + pub mod key; // TODO: pub mod ledger; -pub mod transfer; +// TODO: pub mod transfer; + +/// Configuration Structure +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Configuration; + +impl transfer::Configuration for Configuration { + type SecretKey = <Self::KeyAgreementScheme as KeyAgreementScheme>::SecretKey; + type SecretKeyVar = (); + type PublicKey = <Self::KeyAgreementScheme as KeyAgreementScheme>::PublicKey; + type PublicKeyVar = (); + type KeyAgreementScheme = EllipticCurveDiffieHellman<EdwardsProjective>; + type EphemeralKeyTrapdoor = <Self::EphemeralKeyCommitmentScheme as CommitmentScheme>::Trapdoor; + type EphemeralKeyTrapdoorVar = (); + type EphemeralKeyParametersVar = (); + type EphemeralKeyCommitmentSchemeInput = + <Self::EphemeralKeyCommitmentScheme as CommitmentScheme>::Input; + type EphemeralKeyCommitmentSchemeInputVar = (); + type EphemeralKeyCommitmentScheme = (); +} + +/* TODO: +/// Pedersen Window Parameters +#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct PedersenCommitmentWindowParameters; + +impl PedersenWindow for PedersenCommitmentWindowParameters { + const WINDOW_SIZE: usize = 4; + const NUM_WINDOWS: usize = 256; +} + +/// Pedersen Commitment Projective Curve +pub type PedersenCommitmentProjectiveCurve = EdwardsProjective; + +/// Pedersen Commitment Projective Curve +pub type PedersenCommitmentProjectiveCurveVar = EdwardsVar; + +/// Pedersen Commitment Scheme +pub type PedersenCommitment = pedersen::constraint::PedersenCommitmentWrapper< + PedersenCommitmentWindowParameters, + PedersenCommitmentProjectiveCurve, + PedersenCommitmentProjectiveCurveVar, +>; + +/// Pedersen Commitment Scheme Variable +pub type PedersenCommitmentVar = pedersen::constraint::PedersenCommitmentVar< + PedersenCommitmentWindowParameters, + PedersenCommitmentProjectiveCurve, + PedersenCommitmentProjectiveCurveVar, +>; + +/// Arkworks Pedersen Commitment Scheme +type ArkPedersenCommitment = + CRH<PedersenCommitmentProjectiveCurve, PedersenCommitmentWindowParameters>; + +/// Constraint Field +pub type ConstraintField = Fq; + +/// Constraint System +pub type ConstraintSystem = ArkConstraintSystem<ConstraintField>; + +/// Proof System +pub type ProofSystem = Groth16<Bls12_381>; + +impl ArkMerkleTreeConfiguration for Configuration { + type Leaf = Utxo; + type LeafHash = ArkPedersenCommitment; + type InnerHash = ArkPedersenCommitment; + type Height = u8; + + const HEIGHT: Self::Height = 20; +} + +impl merkle_tree::HashConfiguration for Configuration { + type LeafHash = + <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::HashConfiguration>::LeafHash; + type InnerHash = + <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::HashConfiguration>::InnerHash; +} + +impl merkle_tree::Configuration for Configuration { + type Height = + <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::Configuration>::Height; + + const HEIGHT: Self::Height = + <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::Configuration>::HEIGHT; +} + +impl merkle_tree_constraint::Configuration for Configuration { + type ConstraintField = ConstraintField; + type LeafHashVar = CRHGadget< + PedersenCommitmentProjectiveCurve, + PedersenCommitmentProjectiveCurveVar, + PedersenCommitmentWindowParameters, + >; + type InnerHashVar = CRHGadget< + PedersenCommitmentProjectiveCurve, + PedersenCommitmentProjectiveCurveVar, + PedersenCommitmentWindowParameters, + >; +} + +impl identity::Configuration for Configuration { + type Asset = Asset; + type KeyAgreementScheme = EllipticCurveDiffieHellman<PedersenCommitmentProjectiveCurve>; + type CommitmentScheme = PedersenCommitment; +} + +/* +/// Transfer Constraint Configuration Structure +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct TransferConstraintConfiguration; + +impl identity::Configuration for TransferConstraintConfiguration { + type Asset = AssetVar; + type KeyAgreementScheme = (); + type CommitmentScheme = (); +} + +impl transfer::ConstraintConfiguration<ConstraintSystem> for TransferConstraintConfiguration {} + +impl transfer::Configuration for Configuration { + type EncryptionScheme = (); + type UtxoSetVerifier = (); + type ConstraintSystem = ConstraintSystem; + type ConstraintConfiguration = TransferConstraintConfiguration; + type ProofSystem = ProofSystem; +} +*/ +*/ diff --git a/manta-pay/src/accounting/transfer.rs b/manta-pay/src/accounting/transfer.rs index 91abb62b0..abc334240 100644 --- a/manta-pay/src/accounting/transfer.rs +++ b/manta-pay/src/accounting/transfer.rs @@ -16,8 +16,27 @@ //! Transfer Implementations -use crate::accounting::config::Configuration; -use manta_accounting::transfer::{self as transfer, canonical}; +use crate::crypto::merkle_tree::ConfigConverter; +use manta_accounting::{ + identity, transfer, + transfer::{self as transfer, canonical}, +}; +use manta_crypto::merkle_tree::{self, full::Full}; + +/// Unspent Transaction Output +pub type Utxo = identity::Utxo<Configuration>; + +/// Void Number +pub type VoidNumber = identity::VoidNumber<Configuration>; + +/// UTXO Set Parameters +pub type Parameters = merkle_tree::Parameters<ConfigConverter<Configuration>>; + +/// UTXO Set Root +pub type Root = merkle_tree::Root<ConfigConverter<Configuration>>; + +/// UTXO Set Path +pub type Path = merkle_tree::Path<ConfigConverter<Configuration>>; /// Mint Transaction Type pub type Mint = canonical::Mint<Configuration>; diff --git a/manta-pay/src/crypto/commitment/mod.rs b/manta-pay/src/crypto/commitment/mod.rs index 3e8cff1b6..84f470644 100644 --- a/manta-pay/src/crypto/commitment/mod.rs +++ b/manta-pay/src/crypto/commitment/mod.rs @@ -17,3 +17,4 @@ //! Commitment Scheme Implementations pub mod pedersen; +pub mod poseidon; diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs index 585672298..8e2d5dfd7 100644 --- a/manta-pay/src/crypto/commitment/pedersen.rs +++ b/manta-pay/src/crypto/commitment/pedersen.rs @@ -14,612 +14,97 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Arkworks Pedersen Commitment Implementation +//! Pedersen Commitments -use alloc::vec::Vec; -use ark_crypto_primitives::commitment::{ - pedersen::Commitment as ArkPedersenCommitment, CommitmentScheme as ArkCommitmentScheme, -}; -use ark_ff::{bytes::ToBytes, UniformRand}; -use manta_crypto::{ - commitment::CommitmentScheme, - rand::{CryptoRng, Rand, RngCore, Sample, SizedRng, Standard}, -}; -use manta_util::{Concat, ConcatAccumulator}; +// TODO: Describe contract for `Group`. -/// Pedersen Window Parameters Trait -pub use ark_crypto_primitives::commitment::pedersen::Window as PedersenWindow; +use core::marker::PhantomData; +use manta_crypto::commitment::CommitmentScheme; -pub use ark_ec::ProjectiveCurve; +/// Pedersen Group +pub trait Group { + /// Scalar Field Type + type Scalar; -/// Arkworks Pedersen Commitment Parameters -type ArkPedersenCommitmentParameters<W, C> = - <ArkPedersenCommitment<C, W> as ArkCommitmentScheme>::Parameters; + /// Adds two points of the group together. + fn add(lhs: Self, rhs: Self) -> Self; -/// Arkworks Pedersen Commitment Randomness -type ArkPedersenCommitmentRandomness<W, C> = - <ArkPedersenCommitment<C, W> as ArkCommitmentScheme>::Randomness; - -/// Arkworks Pedersen Commitment Output -type ArkPedersenCommitmentOutput<W, C> = - <ArkPedersenCommitment<C, W> as ArkCommitmentScheme>::Output; - -/// Pedersen Commitment Randomness -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Debug(bound = ""), - Default(bound = ""), - Eq(bound = ""), - PartialEq(bound = "") -)] -pub struct PedersenCommitmentRandomness<W, C>(ArkPedersenCommitmentRandomness<W, C>) -where - W: PedersenWindow, - C: ProjectiveCurve; - -impl<W, C> Sample for PedersenCommitmentRandomness<W, C> -where - W: PedersenWindow, - C: ProjectiveCurve, -{ - #[inline] - fn sample<R>(distribution: Standard, rng: &mut R) -> Self - where - R: CryptoRng + RngCore + ?Sized, - { - let _ = distribution; - Self(ArkPedersenCommitmentRandomness::<W, _>::rand(rng)) - } + /// Multiplies the given `point` with a `scalar` value. + fn scalar_mul(point: &Self, scalar: &Self::Scalar) -> Self; } -/// Pedersen Commitment Output -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Debug(bound = ""), - Default(bound = ""), - Eq(bound = ""), - Hash(bound = ""), - PartialEq(bound = "") -)] -pub struct PedersenCommitmentOutput<W, C>(ArkPedersenCommitmentOutput<W, C>) -where - W: PedersenWindow, - C: ProjectiveCurve; - -impl<W, C> Concat for PedersenCommitmentOutput<W, C> +/// Commitment Paramters +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct Parameters<G, const ARITY: usize = 1> where - W: PedersenWindow, - C: ProjectiveCurve, + G: Group, { - type Item = u8; + /// Trapdoor Generator + pub trapdoor_generator: G, - #[inline] - fn concat<A>(&self, accumulator: &mut A) - where - A: ConcatAccumulator<Self::Item> + ?Sized, - { - // TODO: See if we can extend `ConcatAccumulator` to allow something like `Vec::append`, - // to improve the efficiency here. - let mut buffer = Vec::new(); - self.0 - .write(&mut buffer) - .expect("This does not fail. See the implementation of `Write` for `Vec`."); - accumulator.extend(&buffer); - } + /// Input Generators + pub input_generators: [G; ARITY], } -/// Implementation of [`CommitmentScheme`] for Pedersen Commitments -#[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""))] -pub struct PedersenCommitment<W, C>(ArkPedersenCommitmentParameters<W, C>) +/// Commitment Scheme +pub struct Commitment<G, const ARITY: usize = 1>(PhantomData<G>) where - W: PedersenWindow, - C: ProjectiveCurve; + G: Group; -impl<W, C> PedersenCommitment<W, C> +impl<G, const ARITY: usize> CommitmentScheme for Commitment<G, ARITY> where - W: PedersenWindow, - C: ProjectiveCurve, + G: Group, { - /// Pedersen Window Size - pub const WINDOW_SIZE: usize = W::WINDOW_SIZE; + type Parameters = Parameters<G, ARITY>; - /// Pedersen Window Count - pub const NUM_WINDOWS: usize = W::NUM_WINDOWS; -} + type Trapdoor = G::Scalar; -impl<W, C> CommitmentScheme for PedersenCommitment<W, C> -where - W: PedersenWindow, - C: ProjectiveCurve, -{ - type Input = Vec<u8>; - - type Randomness = PedersenCommitmentRandomness<W, C>; + type Input = [G::Scalar; ARITY]; - type Output = PedersenCommitmentOutput<W, C>; + type Output = G; #[inline] - fn commit(&self, input: Self::Input, randomness: &Self::Randomness) -> Self::Output { - PedersenCommitmentOutput( - ArkPedersenCommitment::<_, W>::commit(&self.0, &input, &randomness.0) - .expect("Failure outcomes are not accepted."), + fn commit( + parameters: &Self::Parameters, + trapdoor: &Self::Trapdoor, + input: &Self::Input, + ) -> Self::Output { + parameters.input_generators.iter().zip(input).fold( + G::scalar_mul(&parameters.trapdoor_generator, trapdoor), + move |acc, (g, i)| G::add(acc, G::scalar_mul(g, i)), ) } } -impl<W, C> Sample for PedersenCommitment<W, C> -where - W: PedersenWindow, - C: ProjectiveCurve, -{ - #[inline] - fn sample<R>(distribution: Standard, rng: &mut R) -> Self - where - R: CryptoRng + RngCore + ?Sized, - { - let _ = distribution; - Self( - ArkPedersenCommitment::<_, W>::setup(&mut SizedRng(rng)) - .expect("Sampling is not allowed to fail."), - ) - } -} - -/// Pedersen Commitment Scheme Constraint System Implementation -pub mod constraint { - use super::*; - use crate::crypto::constraint::arkworks::{empty, full, ArkConstraintSystem}; - use ark_crypto_primitives::{ - commitment::pedersen::constraints::{CommGadget, ParametersVar, RandomnessVar}, - CommitmentGadget, - }; - use ark_ff::{Field, ToConstraintField}; - use ark_r1cs_std::{ - alloc::AllocVar, - groups::{CurveVar, GroupOpsBounds}, - uint8::UInt8, - }; - use ark_relations::ns; - use core::marker::PhantomData; - use manta_crypto::constraint::{ - reflection::HasAllocation, types::Bool, Allocation, Constant, Equal, PublicOrSecret, - Secret, Variable, - }; - - /// Constraint Field Type - pub type ConstraintField<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField; - - /// Constraint System Type - pub type ConstraintSystem<C> = ArkConstraintSystem<ConstraintField<C>>; - - /// Input Buffer Type - type InputBuffer<F> = Vec<UInt8<F>>; - - /// Pedersen Commitment Output Wrapper - #[derive(derivative::Derivative)] - #[derivative( - Clone(bound = ""), - Debug(bound = ""), - Default(bound = ""), - Eq(bound = ""), - Hash(bound = ""), - PartialEq(bound = "") - )] - pub struct PedersenCommitmentOutputWrapper<W, C, GG>( - PedersenCommitmentOutput<W, C>, - PhantomData<GG>, - ) - where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; - - impl<W, C, GG> From<PedersenCommitmentOutput<W, C>> for PedersenCommitmentOutputWrapper<W, C, GG> - where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - { - #[inline] - fn from(output: PedersenCommitmentOutput<W, C>) -> Self { - Self(output, PhantomData) - } - } - - impl<W, C, GG> From<PedersenCommitmentOutputWrapper<W, C, GG>> for PedersenCommitmentOutput<W, C> - where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - { - #[inline] - fn from(wrapper: PedersenCommitmentOutputWrapper<W, C, GG>) -> Self { - wrapper.0 - } - } - - impl<W, C, GG> Concat for PedersenCommitmentOutputWrapper<W, C, GG> - where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - { - type Item = u8; +/// Constraint System Gadgets +pub mod constraint {} - #[inline] - fn concat<A>(&self, accumulator: &mut A) - where - A: ConcatAccumulator<Self::Item> + ?Sized, - { - self.0.concat(accumulator); - } - } +/// Arkworks Backend +#[cfg(feature = "arkworks")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] +pub mod arkworks { + use ark_ec::ProjectiveCurve; + use ark_ff::PrimeField; - /// Pedersen Commitment Scheme Wrapper - #[derive(derivative::Derivative)] - #[derivative(Clone(bound = ""))] - pub struct PedersenCommitmentWrapper<W, C, GG>(PedersenCommitment<W, C>, PhantomData<GG>) + /// Pedersen Group Wrapper for a [`ProjectiveCurve`] + pub struct Group<C>(C) where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; - - impl<W, C, GG> PedersenCommitmentWrapper<W, C, GG> - where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - { - /// Pedersen Window Size - pub const WINDOW_SIZE: usize = W::WINDOW_SIZE; - - /// Pedersen Window Count - pub const NUM_WINDOWS: usize = W::NUM_WINDOWS; - } - - impl<W, C, GG> From<PedersenCommitment<W, C>> for PedersenCommitmentWrapper<W, C, GG> - where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - { - #[inline] - fn from(commitment_scheme: PedersenCommitment<W, C>) -> Self { - Self(commitment_scheme, PhantomData) - } - } - - impl<W, C, GG> From<PedersenCommitmentWrapper<W, C, GG>> for PedersenCommitment<W, C> - where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - { - #[inline] - fn from(wrapper: PedersenCommitmentWrapper<W, C, GG>) -> Self { - wrapper.0 - } - } - - impl<W, C, GG> CommitmentScheme for PedersenCommitmentWrapper<W, C, GG> - where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - { - type Input = <PedersenCommitment<W, C> as CommitmentScheme>::Input; - - type Randomness = <PedersenCommitment<W, C> as CommitmentScheme>::Randomness; - - type Output = PedersenCommitmentOutputWrapper<W, C, GG>; - - #[inline] - fn commit(&self, input: Self::Input, randomness: &Self::Randomness) -> Self::Output { - self.0.commit(input, randomness).into() - } - } - - impl<W, C, GG, D> Sample<D> for PedersenCommitmentWrapper<W, C, GG> - where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - PedersenCommitment<W, C>: Sample<D>, - { - #[inline] - fn sample<R>(distribution: D, rng: &mut R) -> PedersenCommitmentWrapper<W, C, GG> - where - R: CryptoRng + RngCore + ?Sized, - { - Self(rng.sample(distribution), PhantomData) - } - } - - /// Pedersen Commitment Randomness Variable - #[derive(derivative::Derivative)] - #[derivative(Clone(bound = ""))] - pub struct PedersenCommitmentRandomnessVar<W, C>( - RandomnessVar<ConstraintField<C>>, - PhantomData<W>, - ) - where - W: PedersenWindow, C: ProjectiveCurve; - impl<W, C> Variable<ConstraintSystem<C>> for PedersenCommitmentRandomnessVar<W, C> + impl<C> super::Group for Group<C> where - W: PedersenWindow, C: ProjectiveCurve, { - type Type = PedersenCommitmentRandomness<W, C>; - - type Mode = Secret; + type Scalar = C::ScalarField; #[inline] - fn new( - cs: &mut ConstraintSystem<C>, - allocation: Allocation<Self::Type, Self::Mode>, - ) -> Self { - // SAFETY: We can use `empty` here because `RandomnessVar` has an internal default and - // so its allocation never fails. - Self( - match allocation.known() { - Some((this, _)) => RandomnessVar::new_witness( - ns!(cs.cs, "pedersen commitment randomness secret witness"), - full(&this.0), - ), - _ => RandomnessVar::new_witness( - ns!(cs.cs, "pedersen commitment randomness secret witness"), - empty::<ArkPedersenCommitmentRandomness<W, C>>, - ), - } - .expect("Variable allocation is not allowed to fail."), - PhantomData, - ) + fn add(lhs: Self, rhs: Self) -> Self { + Self(lhs.0 + rhs.0) } - } - - impl<W, C> HasAllocation<ConstraintSystem<C>> for PedersenCommitmentRandomness<W, C> - where - W: PedersenWindow, - C: ProjectiveCurve, - { - type Variable = PedersenCommitmentRandomnessVar<W, C>; - type Mode = Secret; - } - - /// Pedersen Commitment Output Variable - #[derive(derivative::Derivative)] - #[derivative(Clone(bound = ""))] - pub struct PedersenCommitmentOutputVar<W, C, GG>(GG, PhantomData<(W, C)>) - where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; - - impl<W, C, GG> PedersenCommitmentOutputVar<W, C, GG> - where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - { - /// Builds a new [`PedersenCommitmentOutputVar`] from `output_var`. - #[inline] - fn new(output_var: GG) -> Self { - Self(output_var, PhantomData) - } - } - - impl<W, C, GG> Concat for PedersenCommitmentOutputVar<W, C, GG> - where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - { - type Item = UInt8<ConstraintField<C>>; - - #[inline] - fn concat<A>(&self, accumulator: &mut A) - where - A: ConcatAccumulator<Self::Item> + ?Sized, - { - accumulator.extend(&self.0.to_bytes().expect("This is not allowed to fail.")); - } - } - - impl<W, C, GG> Variable<ConstraintSystem<C>> for PedersenCommitmentOutputVar<W, C, GG> - where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - { - type Type = PedersenCommitmentOutputWrapper<W, C, GG>; - - type Mode = PublicOrSecret; - - #[inline] - fn new( - cs: &mut ConstraintSystem<C>, - allocation: Allocation<Self::Type, Self::Mode>, - ) -> Self { - Self( - match allocation { - Allocation::Known(this, PublicOrSecret::Public) => { - AllocVar::<ArkPedersenCommitmentOutput<W, C>, _>::new_input( - ns!(cs.cs, "pedersen commitment output public input"), - full(&(this.0).0), - ) - } - Allocation::Known(this, PublicOrSecret::Secret) => { - AllocVar::<ArkPedersenCommitmentOutput<W, C>, _>::new_witness( - ns!(cs.cs, "pedersen commitment output secret witness"), - full(&(this.0).0), - ) - } - Allocation::Unknown(PublicOrSecret::Public) => GG::new_input( - ns!(cs.cs, "pedersen commitment output public input"), - empty::<ArkPedersenCommitmentOutput<W, C>>, - ), - Allocation::Unknown(PublicOrSecret::Secret) => GG::new_witness( - ns!(cs.cs, "pedersen commitment output secret witness"), - empty::<ArkPedersenCommitmentOutput<W, C>>, - ), - } - .expect("Variable allocation is not allowed to fail."), - PhantomData, - ) - } - } - - impl<W, C, GG> HasAllocation<ConstraintSystem<C>> for PedersenCommitmentOutputWrapper<W, C, GG> - where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - { - type Variable = PedersenCommitmentOutputVar<W, C, GG>; - type Mode = PublicOrSecret; - } - - impl<W, C, GG> Equal<ConstraintSystem<C>> for PedersenCommitmentOutputVar<W, C, GG> - where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - { - #[inline] - fn eq(cs: &mut ConstraintSystem<C>, lhs: &Self, rhs: &Self) -> Bool<ConstraintSystem<C>> { - let _ = cs; - lhs.0 - .is_eq(&rhs.0) - .expect("Equality checking is not allowed to fail.") - } - } - - impl<W, C> PedersenCommitmentOutput<W, C> - where - W: PedersenWindow, - C: ProjectiveCurve, - { - /// Extends the `input` vector by constraint field elements that make up `self`. - #[inline] - pub fn extend_input(&self, input: &mut Vec<ConstraintField<C>>) - where - C::Affine: ToConstraintField<ConstraintField<C>>, - { - input.append( - &mut self - .0 - .to_field_elements() - .expect("Conversion to constraint field elements is not allowed to fail."), - ); - } - } - - impl<W, C, GG> PedersenCommitmentOutputWrapper<W, C, GG> - where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - { - /// Extends the `input` vector by constraint field elements that make up `self`. - #[inline] - pub fn extend_input(&self, input: &mut Vec<ConstraintField<C>>) - where - C::Affine: ToConstraintField<ConstraintField<C>>, - { - self.0.extend_input(input); - } - } - - /// Pedersen Commitment Scheme Variable - #[derive(derivative::Derivative)] - #[derivative(Clone(bound = ""))] - pub struct PedersenCommitmentVar<W, C, GG>(ParametersVar<C, GG>, PhantomData<W>) - where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; - - impl<W, C, GG> Variable<ConstraintSystem<C>> for PedersenCommitmentVar<W, C, GG> - where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - { - type Type = PedersenCommitmentWrapper<W, C, GG>; - - type Mode = Constant; - - #[inline] - fn new( - cs: &mut ConstraintSystem<C>, - allocation: Allocation<Self::Type, Self::Mode>, - ) -> Self { - let (this, _) = allocation.into_known(); - Self( - ParametersVar::new_constant( - ns!(cs.cs, "pedersen commitment paramters constant"), - &(this.0).0, - ) - .expect("Variable allocation is not allowed to fail."), - PhantomData, - ) - } - } - - impl<W, C, GG> HasAllocation<ConstraintSystem<C>> for PedersenCommitmentWrapper<W, C, GG> - where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - { - type Variable = PedersenCommitmentVar<W, C, GG>; - type Mode = Constant; - } - - impl<W, C, GG> CommitmentScheme for PedersenCommitmentVar<W, C, GG> - where - W: PedersenWindow, - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, - { - type Input = InputBuffer<ConstraintField<C>>; - - type Randomness = PedersenCommitmentRandomnessVar<W, C>; - - type Output = PedersenCommitmentOutputVar<W, C, GG>; #[inline] - fn commit(&self, input: Self::Input, randomness: &Self::Randomness) -> Self::Output { - PedersenCommitmentOutputVar::new( - CommGadget::<_, _, W>::commit(&self.0, &input, &randomness.0) - .expect("Failure outcomes are not accepted."), - ) + fn scalar_mul(point: &Self, scalar: &Self::Scalar) -> Self { + Self(point.0.mul(scalar.into_repr())) } } } diff --git a/manta-pay/src/crypto/commitment/poseidon.rs b/manta-pay/src/crypto/commitment/poseidon.rs new file mode 100644 index 000000000..2d0a75aa4 --- /dev/null +++ b/manta-pay/src/crypto/commitment/poseidon.rs @@ -0,0 +1,212 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Poseidon Commitment + +use alloc::vec::Vec; +use core::{iter, marker::PhantomData, mem}; +use manta_crypto::commitment::CommitmentScheme; + +/// Poseidon Field +pub trait Field { + /// Adds two field elements together. + fn add(lhs: &Self, rhs: &Self) -> Self; + + /// Multiplies two field elements together. + fn mul(lhs: &Self, rhs: &Self) -> Self; + + /// Adds the `rhs` field element to `self`, storing the value in `self`. + fn add_assign(&mut self, rhs: &Self); +} + +/// Poseidon Configuration +pub trait Configuration { + /// Number of Full Rounds + /// + /// This is counted twice for the first set of full rounds and then the second set after the + /// partial rounds. + const FULL_ROUNDS: usize; + + /// Number of Partial Rounds + const PARTIAL_ROUNDS: usize; + + /// Field Type + type Field: Field; + + /// Applies the S-BOX to `point`. + fn apply_sbox(point: &mut Self::Field); +} + +/// Internal State Vector +type State<C> = Vec<<C as Configuration>::Field>; + +/// Returns the total number of rounds in a Poseidon permutation. +#[inline] +pub fn rounds<C>() -> usize +where + C: Configuration, +{ + 2 * C::FULL_ROUNDS + C::PARTIAL_ROUNDS +} + +/// Poseidon Permutation Parameters +pub struct Parameters<C, const ARITY: usize> +where + C: Configuration, +{ + /// Additive Round Keys + additive_round_keys: Vec<C::Field>, + + /// MDS Matrix + mds_matrix: Vec<C::Field>, +} + +impl<C, const ARITY: usize> Parameters<C, ARITY> +where + C: Configuration, +{ + /// Builds a new [`Parameters`] form `additive_round_keys` and `mds_matrix`. + /// + /// # Panics + /// + /// This method panics if the input vectors are not the correct size for the specified + /// [`Configuration`]. + #[inline] + pub fn new(additive_round_keys: Vec<C::Field>, mds_matrix: Vec<C::Field>) -> Self { + assert_eq!( + additive_round_keys.len(), + rounds::<C>() * (ARITY + 1), + "Additive Rounds Keys are not the correct size." + ); + assert_eq!( + mds_matrix.len(), + (ARITY + 1).pow(2), + "MDS Matrix is not the correct size." + ); + Self { + additive_round_keys, + mds_matrix, + } + } + + /// Returns the additive keys for the given `round`. + #[inline] + fn additive_keys(&self, round: usize) -> &[C::Field] { + let width = ARITY + 1; + let start = round * width; + &self.additive_round_keys[start..start + width] + } + + /// Computes the MDS matrix multiplication against the `state`. + #[inline] + fn mds_matrix_multiply(&self, state: &mut State<C>) { + let width = ARITY + 1; + let mut next = Vec::with_capacity(width); + for i in 0..width { + next.push( + state + .iter() + .enumerate() + .map(|(j, elem)| C::Field::mul(elem, &self.mds_matrix[width * i + j])) + .reduce(|acc, next| C::Field::add(&acc, &next)) + .unwrap(), + ); + } + mem::swap(&mut next, state); + } + + /// Computes the first round of the Poseidon permutation from `trapdoor` and `input`. + #[inline] + fn first_round(&self, trapdoor: &C::Field, input: &[C::Field; ARITY]) -> State<C> { + let mut state = Vec::with_capacity(ARITY + 1); + for (i, point) in iter::once(trapdoor).chain(input).enumerate() { + let mut elem = C::Field::add(point, &self.additive_round_keys[i]); + C::apply_sbox(&mut elem); + state.push(elem); + } + self.mds_matrix_multiply(&mut state); + state + } + + /// Computes a full round at the given `round` index on the internal permutation `state`. + #[inline] + fn full_round(&self, round: usize, state: &mut State<C>) { + let keys = self.additive_keys(round); + for (i, elem) in state.iter_mut().enumerate() { + C::Field::add_assign(elem, &keys[i]); + C::apply_sbox(elem); + } + self.mds_matrix_multiply(state); + } + + /// Computes a partial round at the given `round` index on the internal permutation `state`. + #[inline] + fn partial_round(&self, round: usize, state: &mut State<C>) { + let keys = self.additive_keys(round); + for (i, elem) in state.iter_mut().enumerate() { + C::Field::add_assign(elem, &keys[i]); + } + C::apply_sbox(&mut state[0]); + self.mds_matrix_multiply(state); + } +} + +/// Poseidon Commitment Scheme +pub struct Commitment<C, const ARITY: usize>(PhantomData<C>) +where + C: Configuration; + +impl<C, const ARITY: usize> CommitmentScheme for Commitment<C, ARITY> +where + C: Configuration, +{ + type Parameters = Parameters<C, ARITY>; + + type Trapdoor = C::Field; + + type Input = [C::Field; ARITY]; + + type Output = C::Field; + + #[inline] + fn commit( + parameters: &Self::Parameters, + trapdoor: &Self::Trapdoor, + input: &Self::Input, + ) -> Self::Output { + let mut state = parameters.first_round(trapdoor, input); + for round in 1..C::FULL_ROUNDS { + parameters.full_round(round, &mut state); + } + for round in C::FULL_ROUNDS..(C::FULL_ROUNDS + C::PARTIAL_ROUNDS) { + parameters.partial_round(round, &mut state); + } + for round in (C::FULL_ROUNDS + C::PARTIAL_ROUNDS)..(2 * C::FULL_ROUNDS + C::PARTIAL_ROUNDS) + { + parameters.full_round(round, &mut state); + } + state.truncate(1); + state.remove(0) + } +} + +/// Constraint System Gadgets +pub mod constraint {} + +/// Arkworks Backend +#[cfg(feature = "arkworks")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] +pub mod arkworks {} diff --git a/manta-pay/src/crypto/constraint/dusk_network/proof_systems/mod.rs b/manta-pay/src/crypto/constraint/dusk_network/proof_systems/mod.rs deleted file mode 100644 index 5a558f8a2..000000000 --- a/manta-pay/src/crypto/constraint/dusk_network/proof_systems/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Dusk-Network Proof System Implementations diff --git a/manta-pay/src/crypto/constraint/mod.rs b/manta-pay/src/crypto/constraint/mod.rs index f53060e3d..b47f181bd 100644 --- a/manta-pay/src/crypto/constraint/mod.rs +++ b/manta-pay/src/crypto/constraint/mod.rs @@ -20,6 +20,6 @@ #[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks-zkp")))] pub mod arkworks; -#[cfg(feature = "dusk-network-zkp")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "dusk-network-zkp")))] -pub mod dusk_network; +#[cfg(feature = "zk-garage-zkp")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "zk-garage-zkp")))] +pub mod zk_garage; diff --git a/manta-pay/src/crypto/constraint/dusk_network/constraint_system.rs b/manta-pay/src/crypto/constraint/zk_garage/constraint_system.rs similarity index 93% rename from manta-pay/src/crypto/constraint/dusk_network/constraint_system.rs rename to manta-pay/src/crypto/constraint/zk_garage/constraint_system.rs index e674c4153..565a60be7 100644 --- a/manta-pay/src/crypto/constraint/dusk_network/constraint_system.rs +++ b/manta-pay/src/crypto/constraint/zk_garage/constraint_system.rs @@ -14,4 +14,4 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Dusk-Network Constraint System Implementation +//! ZK-Garage Constraint System Implementation diff --git a/manta-pay/src/crypto/constraint/dusk_network/mod.rs b/manta-pay/src/crypto/constraint/zk_garage/mod.rs similarity index 92% rename from manta-pay/src/crypto/constraint/dusk_network/mod.rs rename to manta-pay/src/crypto/constraint/zk_garage/mod.rs index 2c9e35e6d..9d9e803c9 100644 --- a/manta-pay/src/crypto/constraint/dusk_network/mod.rs +++ b/manta-pay/src/crypto/constraint/zk_garage/mod.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Dusk-Network Constraint System and Proof System Implementations +//! ZK-Garage Constraint System and Proof System Implementations mod constraint_system; diff --git a/manta-pay/build.rs b/manta-pay/src/crypto/constraint/zk_garage/proof_systems/mod.rs similarity index 94% rename from manta-pay/build.rs rename to manta-pay/src/crypto/constraint/zk_garage/proof_systems/mod.rs index 9ab651580..d9b543651 100644 --- a/manta-pay/build.rs +++ b/manta-pay/src/crypto/constraint/zk_garage/proof_systems/mod.rs @@ -14,6 +14,4 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Manta Pay Build Script - -fn main() {} +//! ZK-Garage Proof System Implementations diff --git a/manta-pay/src/crypto/key.rs b/manta-pay/src/crypto/key.rs index e60cb6d55..f184b0948 100644 --- a/manta-pay/src/crypto/key.rs +++ b/manta-pay/src/crypto/key.rs @@ -16,17 +16,47 @@ //! Cryptographic Key Primitive Implementations -use ark_ec::{AffineCurve, ProjectiveCurve}; use blake2::{Blake2s, Digest}; use core::marker::PhantomData; -use manta_crypto::key::{KeyAgreementScheme, KeyDerivationFunction}; +use manta_crypto::key::KeyDerivationFunction; use manta_util::into_array_unchecked; +#[cfg(feature = "arkworks")] +use { + ark_ec::{AffineCurve, ProjectiveCurve}, + manta_crypto::key::KeyAgreementScheme, +}; + +/// Blake2s KDF +pub struct Blake2sKdf<T>(PhantomData<T>) +where + T: AsRef<[u8]>; + +impl<T> KeyDerivationFunction for Blake2sKdf<T> +where + T: AsRef<[u8]>, +{ + type Key = T; + + type Output = [u8; 32]; + + #[inline] + fn derive(secret: Self::Key) -> Self::Output { + let mut hasher = Blake2s::new(); + hasher.update(secret.as_ref()); + hasher.update(b"manta kdf instantiated with blake2s hash function"); + into_array_unchecked(hasher.finalize()) + } +} + /// Elliptic Curve Diffie Hellman Protocol +#[cfg(feature = "arkworks")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] pub struct EllipticCurveDiffieHellman<C>(PhantomData<C>) where C: ProjectiveCurve; +#[cfg(feature = "arkworks")] impl<C> KeyAgreementScheme for EllipticCurveDiffieHellman<C> where C: ProjectiveCurve, @@ -53,26 +83,11 @@ where } #[inline] - fn agree_owned(secret_key: Self::SecretKey, public_key: Self::PublicKey) -> Self::SharedSecret { + fn agree_owned( + secret_key: Self::SecretKey, + mut public_key: Self::PublicKey, + ) -> Self::SharedSecret { public_key *= secret_key; public_key } } - -/// Blake2s KDF -pub struct Blake2sKdf; - -impl<T> KeyDerivationFunction<T> for Blake2sKdf -where - T: AsRef<[u8]>, -{ - type Output = [u8; 32]; - - #[inline] - fn derive(secret: T) -> Self::Output { - let mut hasher = Blake2s::new(); - hasher.update(secret.as_ref()); - hasher.update(b"manta kdf instantiated with blake2s hash function"); - into_array_unchecked(hasher.finalize()) - } -} diff --git a/manta-pay/src/crypto/mod.rs b/manta-pay/src/crypto/mod.rs index b03b90657..72f2c5740 100644 --- a/manta-pay/src/crypto/mod.rs +++ b/manta-pay/src/crypto/mod.rs @@ -17,7 +17,7 @@ //! Manta Pay Cryptographic Primitives Implementations pub mod commitment; -pub mod constraint; +// TODO: pub mod constraint; pub mod encryption; pub mod key; -pub mod merkle_tree; +// TODO: pub mod merkle_tree; diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index 36d63544f..6fec0ba5e 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -16,8 +16,6 @@ //! Manta Pay Implementation -// FIXME: Ensure that `no-default-features` is a valid build. - #![cfg_attr(not(any(feature = "std", test)), no_std)] #![cfg_attr(doc_cfg, feature(doc_cfg))] #![forbid(rustdoc::broken_intra_doc_links)] @@ -25,5 +23,5 @@ extern crate alloc; -pub mod accounting; +// TODO: pub mod accounting; pub mod crypto; From 75ff3d704a5264e6297861270b006c16bc01b41a Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 27 Dec 2021 19:34:18 -0500 Subject: [PATCH 144/275] wip: adding concrete implementations --- Cargo.toml | 2 +- manta-cli/Cargo.toml | 40 ----------- manta-cli/LICENSE | 1 - manta-cli/README.md | 1 - manta-cli/src/command/mod.rs | 17 ----- manta-cli/src/lib.rs | 21 ------ manta-cli/src/main.rs | 73 --------------------- manta-pay/src/accounting/mod.rs | 46 ++++++++++--- manta-pay/src/crypto/commitment/poseidon.rs | 30 ++++++++- manta-pay/src/crypto/encryption.rs | 26 ++++---- manta-pay/src/lib.rs | 5 +- src/lib.rs | 7 +- 12 files changed, 91 insertions(+), 178 deletions(-) delete mode 100644 manta-cli/Cargo.toml delete mode 120000 manta-cli/LICENSE delete mode 100644 manta-cli/README.md delete mode 100644 manta-cli/src/command/mod.rs delete mode 100644 manta-cli/src/lib.rs delete mode 100644 manta-cli/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index f5dd5f9b1..c3f269f2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,6 @@ maintenance = { status = "actively-developed" } resolver = "2" members = [ "manta-accounting", - "manta-cli", "manta-codec", "manta-crypto", "manta-pay", @@ -50,5 +49,6 @@ manta-accounting = { path = "manta-accounting" } manta-codec = { path = "manta-codec" } manta-crypto = { path = "manta-crypto" } manta-pay = { path = "manta-pay" } +manta-util = { path = "manta-util" } parity-scale-codec = { version = "2.2.0", default-features = false } diff --git a/manta-cli/Cargo.toml b/manta-cli/Cargo.toml deleted file mode 100644 index 46032293b..000000000 --- a/manta-cli/Cargo.toml +++ /dev/null @@ -1,40 +0,0 @@ -[package] -name = "manta-cli" -edition = "2021" -version = "0.4.0" -authors = ["Manta Network <contact@manta.network>"] -readme = "README.md" -license-file = "LICENSE" -repository = "https://github.com/Manta-Network/manta-rs" -homepage = "https://github.com/Manta-Network" -documentation = "https://github.com/Manta-Network/manta-rs" -categories = [""] -keywords = [""] -description = "Manta CLI" -publish = false - -[package.metadata.docs.rs] -# To build locally: -# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --open -all-features = true -rustdoc-args = ["--cfg", "doc_cfg"] - -[badges] -is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } -is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } -maintenance = { status = "actively-developed" } - -[[bin]] -name = "manta" -path = "src/main.rs" - -[dependencies] -clap = "~3.0.0-beta.5" -manta-accounting = { path = "../manta-accounting" } -manta-crypto = { path = "../manta-crypto" } -manta-pay = { path = "../manta-pay" } - -[dev-dependencies] -manta-accounting = { path = "../manta-accounting", features = ["test"] } -manta-crypto = { path = "../manta-crypto", features = ["test"] } -manta-pay = { path = "../manta-pay", features = ["test"] } diff --git a/manta-cli/LICENSE b/manta-cli/LICENSE deleted file mode 120000 index ea5b60640..000000000 --- a/manta-cli/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE \ No newline at end of file diff --git a/manta-cli/README.md b/manta-cli/README.md deleted file mode 100644 index 0a794e609..000000000 --- a/manta-cli/README.md +++ /dev/null @@ -1 +0,0 @@ -# manta-cli diff --git a/manta-cli/src/command/mod.rs b/manta-cli/src/command/mod.rs deleted file mode 100644 index d754e8015..000000000 --- a/manta-cli/src/command/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Manta CLI Commands diff --git a/manta-cli/src/lib.rs b/manta-cli/src/lib.rs deleted file mode 100644 index fdaa03b4b..000000000 --- a/manta-cli/src/lib.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Manta CLI - -#![cfg_attr(doc_cfg, feature(doc_cfg))] -#![forbid(rustdoc::broken_intra_doc_links)] -#![forbid(missing_docs)] diff --git a/manta-cli/src/main.rs b/manta-cli/src/main.rs deleted file mode 100644 index 9fdfbf8bf..000000000 --- a/manta-cli/src/main.rs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Manta CLI - -// NOTE: In order to customize the help messages and formatting of the CLI, some objects are not -// documented and the documentation is instead written in macros or omitted entirely. -// TODO: In order to hook up a node instance here, we will want to eventually move this out of -// `manta-rs` into its own repo. - -use clap::{crate_version, AppSettings, Parser}; - -mod command; - -/// Manta Network's Command Line Interface -#[derive(Parser)] -#[clap( - name = "manta", - version = crate_version!(), - setting = AppSettings::PropagateVersion, - setting = AppSettings::AllArgsOverrideSelf, - after_help = "For more information about Manta, see 'https://github.com/Manta-Network'." -)] -struct Args { - /// Path to configuration file - #[clap(short, long, value_name = "PATH")] - config: Option<String>, - - /// Set the verbosity level - #[clap(short, long, parse(from_occurrences))] - verbose: u8, - - #[clap(subcommand)] - command: Command, -} - -#[derive(Parser)] -enum Command { - /// Run the testing suite and tools - Test { - /// Set the verbosity level - #[clap(short, long, parse(from_occurrences))] - verbose: u8, - }, - - /// Interact with a local wallet - Wallet { - /// Set the verbosity level - #[clap(short, long, parse(from_occurrences))] - verbose: u8, - }, -} - -fn main() { - let args = Args::parse(); - match args.command { - Command::Test { .. } => println!("Test ..."), - Command::Wallet { .. } => println!("Wallet ..."), - } -} diff --git a/manta-pay/src/accounting/mod.rs b/manta-pay/src/accounting/mod.rs index 445cc8a7e..a8dfa00fe 100644 --- a/manta-pay/src/accounting/mod.rs +++ b/manta-pay/src/accounting/mod.rs @@ -19,13 +19,18 @@ // TODO: Make this generic over the backend we use. Automatically compute which features are // enabled when using whichever backend. -use crate::crypto::key::EllipticCurveDiffieHellman; -use ark_bls12_381::Bls12_381; -use ark_crypto_primitives::crh::pedersen::{constraints::CRHGadget, CRH}; -use ark_ed_on_bls12_381::{constraints::EdwardsVar, EdwardsProjective, Fq}; +use crate::crypto::{ + commitment::{pedersen, poseidon}, + encryption::AesGcm, + key::{Blake2sKdf, EllipticCurveDiffieHellman}, +}; use manta_accounting::{asset::Asset, transfer}; -use manta_crypto::{commitment::CommitmentScheme, key::KeyAgreementScheme, merkle_tree}; -use rand_chacha::ChaCha20Rng; +use manta_crypto::{ + commitment::CommitmentScheme, encryption, key::KeyAgreementScheme, merkle_tree, +}; + +pub use ark_bls12_381::Bls12_381; +pub use ark_ed_on_bls12_381::EdwardsProjective as Bls12_381_Edwards; pub mod key; // TODO: pub mod ledger; @@ -35,19 +40,44 @@ pub mod key; #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Configuration; +impl poseidon::Configuration for Configuration { + const FULL_ROUNDS: usize = 1; + const PARTIAL_ROUNDS: usize = 1; + type Field = poseidon::arkworks::Field<Bls12_381_Edwards>; +} + impl transfer::Configuration for Configuration { type SecretKey = <Self::KeyAgreementScheme as KeyAgreementScheme>::SecretKey; type SecretKeyVar = (); type PublicKey = <Self::KeyAgreementScheme as KeyAgreementScheme>::PublicKey; type PublicKeyVar = (); - type KeyAgreementScheme = EllipticCurveDiffieHellman<EdwardsProjective>; + type KeyAgreementScheme = EllipticCurveDiffieHellman<Bls12_381_Edwards>; type EphemeralKeyTrapdoor = <Self::EphemeralKeyCommitmentScheme as CommitmentScheme>::Trapdoor; type EphemeralKeyTrapdoorVar = (); type EphemeralKeyParametersVar = (); type EphemeralKeyCommitmentSchemeInput = <Self::EphemeralKeyCommitmentScheme as CommitmentScheme>::Input; type EphemeralKeyCommitmentSchemeInputVar = (); - type EphemeralKeyCommitmentScheme = (); + type EphemeralKeyCommitmentScheme = poseidon::Commitment<Self, 2>; + type TrapdoorDerivationFunction = (); + type CommitmentSchemeParametersVar = (); + type CommitmentSchemeInput = <Self::CommitmentScheme as CommitmentScheme>::Input; + type CommitmentSchemeInputVar = (); + type CommitmentSchemeOutput = <Self::CommitmentScheme as CommitmentScheme>::Output; + type CommitmentScheme = pedersen::Commitment<pedersen::arkworks::Group<Bls12_381_Edwards>, 2>; + type UtxoSetParametersVar = (); + type UtxoSetWitnessVar = (); + type UtxoSetOutputVar = (); + type UtxoSetVerifier = (); + type AssetIdVar = (); + type AssetValueVar = (); + type ConstraintSystem = (); + type ProofSystem = (); + type NoteEncryptionScheme = encryption::Hybrid< + Self::KeyAgreementScheme, + AesGcm<Asset, { Asset::SIZE }>, + Blake2sKdf<<Self::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret>, + >; } /* TODO: diff --git a/manta-pay/src/crypto/commitment/poseidon.rs b/manta-pay/src/crypto/commitment/poseidon.rs index 2d0a75aa4..db6baaf94 100644 --- a/manta-pay/src/crypto/commitment/poseidon.rs +++ b/manta-pay/src/crypto/commitment/poseidon.rs @@ -209,4 +209,32 @@ pub mod constraint {} /// Arkworks Backend #[cfg(feature = "arkworks")] #[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] -pub mod arkworks {} +pub mod arkworks { + use ark_ec::ProjectiveCurve; + use ark_ff::PrimeField; + + /// Poseidon Field Wrapper for a [`ProjectiveCurve`] + pub struct Field<C>(C::ScalarField) + where + C: ProjectiveCurve; + + impl<C> super::Field for Field<C> + where + C: ProjectiveCurve, + { + #[inline] + fn add(lhs: &Self, rhs: &Self) -> Self { + Self(lhs.0 + rhs.0) + } + + #[inline] + fn mul(lhs: &Self, rhs: &Self) -> Self { + Self(lhs.0 * rhs.0) + } + + #[inline] + fn add_assign(&mut self, rhs: &Self) { + self.0 += rhs.0; + } + } +} diff --git a/manta-pay/src/crypto/encryption.rs b/manta-pay/src/crypto/encryption.rs index ff1fdebeb..f86e26f6c 100644 --- a/manta-pay/src/crypto/encryption.rs +++ b/manta-pay/src/crypto/encryption.rs @@ -16,7 +16,7 @@ //! Encryption Implementations -// FIXME: Don't use raw bytes as secret and public key. +// FIXME: Don't use raw bytes as encryption/decryption key. // FIXME: Make sure secret keys are protected. use aes_gcm::{ @@ -27,36 +27,39 @@ use alloc::vec::Vec; use core::marker::PhantomData; use generic_array::GenericArray; use manta_crypto::encryption::SymmetricKeyEncryptionScheme; +use manta_util::into_array_unchecked; /// AES Galois Counter Mode -pub struct AesGcm<T>(PhantomData<T>) +pub struct AesGcm<T, const SIZE: usize>(PhantomData<T>) where - T: AsRef<[u8]> + From<Vec<u8>>; + T: Into<[u8; SIZE]> + From<[u8; SIZE]>; -impl<T> AesGcm<T> +impl<T, const SIZE: usize> AesGcm<T, SIZE> where - T: AsRef<[u8]> + From<Vec<u8>>, + T: Into<[u8; SIZE]> + From<[u8; SIZE]>, { /// Encryption/Decryption Nonce const NONCE: &'static [u8] = b"manta rocks!"; } -impl<T> SymmetricKeyEncryptionScheme for AesGcm<T> +impl<T, const SIZE: usize> SymmetricKeyEncryptionScheme for AesGcm<T, SIZE> where - T: AsRef<[u8]> + From<Vec<u8>>, + T: Into<[u8; SIZE]> + From<[u8; SIZE]>, { type Key = [u8; 32]; type Plaintext = T; - type Ciphertext = Vec<u8>; + type Ciphertext = [u8; SIZE]; #[inline] fn encrypt(key: Self::Key, plaintext: Self::Plaintext) -> Self::Ciphertext { // SAFETY: Using a deterministic nonce is ok since we never reuse keys. - Aes256Gcm::new(GenericArray::from_slice(&key)) - .encrypt(Nonce::from_slice(Self::NONCE), plaintext.as_ref()) - .expect("Symmetric encryption is not allowed to fail.") + into_array_unchecked( + Aes256Gcm::new(GenericArray::from_slice(&key)) + .encrypt(Nonce::from_slice(Self::NONCE), plaintext.into().as_ref()) + .expect("Symmetric encryption is not allowed to fail."), + ) } #[inline] @@ -65,6 +68,7 @@ where Aes256Gcm::new(GenericArray::from_slice(&key)) .decrypt(Nonce::from_slice(Self::NONCE), ciphertext.as_ref()) .ok() + .map(into_array_unchecked) .map(Into::into) } } diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index 6fec0ba5e..e7dcc9f1d 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -23,5 +23,8 @@ extern crate alloc; -// TODO: pub mod accounting; pub mod crypto; + +#[cfg(all(feature = "arkworks", feature = "groth16"))] +#[cfg_attr(doc_cfg, doc(cfg(all(feature = "arkworks", feature = "groth16"))))] +pub mod accounting; diff --git a/src/lib.rs b/src/lib.rs index 6414f9731..37193b8a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,10 +14,8 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Manta Network +//! Manta Network Crates -// TODO: Given the structure of `manta-pay` does this crate make sense anymore? Maybe only as -// `accounting` + `crypto`? // TODO: Reconsider how `manta-codec` is designed. Do we need it? Should it just be part of // `manta-pay` or some `substrate`-specific interoperability crates? @@ -37,3 +35,6 @@ pub use manta_crypto as crypto; #[doc(inline)] pub use manta_pay as pay; + +#[doc(inline)] +pub use manta_util as util; From 231c27f5a59b8f02b969d7118d7fad5ede4b07de Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 28 Dec 2021 14:20:02 -0500 Subject: [PATCH 145/275] wip: move to new compiler model --- manta-accounting/src/transfer/batch.rs | 13 +- manta-accounting/src/transfer/mod.rs | 535 ++++++++++---------- manta-accounting/src/wallet/signer.rs | 46 +- manta-crypto/src/accumulator.rs | 125 ++--- manta-crypto/src/commitment.rs | 134 +---- manta-crypto/src/constraint.rs | 9 + manta-crypto/src/encryption.rs | 7 +- manta-crypto/src/key.rs | 106 +--- manta-crypto/src/merkle_tree/mod.rs | 1 + manta-crypto/src/merkle_tree/path.rs | 164 +----- manta-crypto/src/merkle_tree/tree.rs | 2 + manta-pay/src/accounting/mod.rs | 52 +- manta-pay/src/crypto/commitment/pedersen.rs | 99 ++-- manta-pay/src/crypto/commitment/poseidon.rs | 189 ++++--- manta-pay/src/crypto/encryption.rs | 1 - manta-pay/src/crypto/key.rs | 20 +- manta-pay/src/lib.rs | 2 + 17 files changed, 619 insertions(+), 886 deletions(-) diff --git a/manta-accounting/src/transfer/batch.rs b/manta-accounting/src/transfer/batch.rs index 9e6797331..1adf53285 100644 --- a/manta-accounting/src/transfer/batch.rs +++ b/manta-accounting/src/transfer/batch.rs @@ -19,8 +19,8 @@ use crate::{ asset::Asset, transfer::{ - CommitmentSchemeParameters, Configuration, EphemeralKeyParameters, PreSender, Receiver, - SpendingKey, Utxo, + Configuration, EphemeralKeyParameters, PreSender, Receiver, SpendingKey, Utxo, + UtxoCommitmentParameters, VoidNumberCommitmentParameters, }, }; use alloc::vec::Vec; @@ -50,7 +50,8 @@ where #[inline] pub fn new<R, const RECEIVERS: usize>( ephemeral_key_parameters: &EphemeralKeyParameters<C>, - commitment_scheme_parameters: &CommitmentSchemeParameters<C>, + utxo_parameters: &UtxoCommitmentParameters<C>, + void_number_parameters: &VoidNumberCommitmentParameters<C>, asset: Asset, spending_key: &SpendingKey<C>, rng: &mut R, @@ -65,7 +66,8 @@ where let mut zeroes = Vec::with_capacity(RECEIVERS - 1); let (receiver, pre_sender) = spending_key.internal_pair( ephemeral_key_parameters, - commitment_scheme_parameters, + utxo_parameters, + void_number_parameters, rng.gen(), asset, ); @@ -73,7 +75,8 @@ where for _ in 0..RECEIVERS - 2 { let (receiver, pre_sender) = spending_key.internal_zero_pair( ephemeral_key_parameters, - commitment_scheme_parameters, + utxo_parameters, + void_number_parameters, rng.gen(), asset.id, ); diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index 377e34ee5..198c44786 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -18,16 +18,16 @@ use crate::asset::{Asset, AssetId, AssetValue}; use alloc::vec::Vec; -use core::{fmt::Debug, hash::Hash, marker::PhantomData, ops::Add}; +use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use manta_crypto::{ accumulator::{self, Accumulator, MembershipProof, Verifier}, - commitment::{self, CommitmentScheme, Input as CommitmentInput}, + commitment::CommitmentScheme, constraint::{ - Allocation, Constant, ConstraintSystem, Derived, Equal, Input as ProofSystemInput, + Add, Allocation, Constant, ConstraintSystem, Derived, Equal, Input as ProofSystemInput, ProofSystem, Public, PublicOrSecret, Secret, Variable, VariableSource, }, encryption::{DecryptedMessage, EncryptedMessage, HybridPublicKeyEncryptionScheme}, - key::{self, KeyAgreementScheme, KeyDerivationFunction}, + key::{KeyAgreementScheme, KeyDerivationFunction}, rand::{CryptoRng, RngCore, Sample}, }; use manta_util::from_variant_impl; @@ -56,159 +56,151 @@ pub trait Configuration { type PublicKey: Clone; /// Secret Key Variable Type - type SecretKeyVar: Variable<Self::ConstraintSystem, Type = SecretKey<Self>, Mode = Secret>; + type SecretKeyVar: Variable<Self::Compiler, Type = SecretKey<Self>, Mode = Secret>; /// Public Key Variable Type - type PublicKeyVar: Variable<Self::ConstraintSystem, Type = PublicKey<Self>, Mode = Public> - + Equal<Self::ConstraintSystem>; + type PublicKeyVar: Variable<Self::Compiler, Type = PublicKey<Self>, Mode = Public> + + Equal<Self::Compiler>; /// Key Agreement Scheme Type - type KeyAgreementScheme: KeyAgreementScheme< - SecretKey = Self::SecretKey, - PublicKey = Self::PublicKey, - >; + type KeyAgreementScheme: KeyAgreementScheme<SecretKey = Self::SecretKey, PublicKey = Self::PublicKey> + + KeyAgreementScheme< + Self::Compiler, + SecretKey = Self::SecretKeyVar, + PublicKey = Self::PublicKeyVar, + >; /// Ephemeral Key Trapdoor Type type EphemeralKeyTrapdoor: Sample; /// Ephemeral Key Trapdoor Variable Type type EphemeralKeyTrapdoorVar: Variable< - Self::ConstraintSystem, + Self::Compiler, Type = Self::EphemeralKeyTrapdoor, Mode = Secret, >; /// Ephemeral Key Commitment Scheme Parameters Variable Type type EphemeralKeyParametersVar: Variable< - Self::ConstraintSystem, + Self::Compiler, Type = EphemeralKeyParameters<Self>, Mode = Constant, >; - /// Ephemeral Key Commitment Scheme Input Type - type EphemeralKeyCommitmentSchemeInput: Default - + CommitmentInput<AssetId> - + CommitmentInput<AssetValue>; - - /// Ephemeral Key Commitment Scheme Input Variable - type EphemeralKeyCommitmentSchemeInputVar: Default - + CommitmentInput<Self::AssetIdVar> - + CommitmentInput<Self::AssetValueVar>; - /// Ephemeral Key Commitment Scheme Type type EphemeralKeyCommitmentScheme: CommitmentScheme< - Trapdoor = Self::EphemeralKeyTrapdoor, - Input = Self::EphemeralKeyCommitmentSchemeInput, - Output = Self::SecretKey, - >; + Trapdoor = Self::EphemeralKeyTrapdoor, + Input = Asset, + Output = Self::SecretKey, + > + CommitmentScheme< + Self::Compiler, + Parameters = Self::EphemeralKeyParametersVar, + Trapdoor = Self::EphemeralKeyTrapdoorVar, + Input = AssetVar<Self>, + Output = Self::SecretKeyVar, + >; /// Trapdoor Derivation Function Type - type TrapdoorDerivationFunction: KeyDerivationFunction< - Key = SharedSecret<Self>, - Output = Trapdoor<Self>, - >; + type TrapdoorDerivationFunction: KeyDerivationFunction<Key = SharedSecret<Self>, Output = Trapdoor<Self>> + + KeyDerivationFunction< + Self::Compiler, + Key = SharedSecretVar<Self>, + Output = TrapdoorVar<Self>, + >; - /// Commitment Scheme Parameters Variable Type - type CommitmentSchemeParametersVar: Variable< - Self::ConstraintSystem, - Type = CommitmentSchemeParameters<Self>, + /// UTXO Commitment Parameters Variable Type + type UtxoCommitmentParametersVar: Variable< + Self::Compiler, + Type = UtxoCommitmentParameters<Self>, Mode = Constant, >; - /// Commitment Scheme Input Type - type CommitmentSchemeInput: Default - + CommitmentInput<AssetId> - + CommitmentInput<AssetValue> - + CommitmentInput<Self::SecretKey>; - - /// Commitment Scheme Input Variable Type - type CommitmentSchemeInputVar: Default - + CommitmentInput<Self::AssetIdVar> - + CommitmentInput<Self::AssetValueVar> - + CommitmentInput<SecretKeyVar<Self>>; - - /// Commitment Scheme Output Type - type CommitmentSchemeOutput: PartialEq; - - /// Commitment Scheme Output Variable Type - type CommitmentSchemeOutputVar: Variable<Self::ConstraintSystem, Type = CommitmentSchemeOutput<Self>, Mode = PublicOrSecret> - + Equal<Self::ConstraintSystem>; - - /// Commitment Scheme Type - type CommitmentScheme: CommitmentScheme< - Input = Self::CommitmentSchemeInput, - Output = Self::CommitmentSchemeOutput, + /// Unspent Transaction Output Type + type Utxo: PartialEq; + + /// UTXO Variable Type + type UtxoVar: Variable<Self::Compiler, Type = Self::Utxo, Mode = PublicOrSecret> + + Equal<Self::Compiler>; + + /// UTXO Commitment Scheme Type + type UtxoCommitmentScheme: CommitmentScheme<Input = Asset, Output = Self::Utxo> + + CommitmentScheme< + Self::Compiler, + Parameters = Self::UtxoCommitmentParametersVar, + Input = AssetVar<Self>, + Output = Self::UtxoVar, + >; + + /// Void Number Commitment Scheme Parameters Variable Type + type VoidNumberCommitmentParametersVar: Variable< + Self::Compiler, + Type = VoidNumberCommitmentParameters<Self>, + Mode = Constant, >; + /// Void Number Type + type VoidNumber: PartialEq; + + /// Void Number Variable Type + type VoidNumberVar: Variable<Self::Compiler, Type = Self::VoidNumber, Mode = Public> + + Equal<Self::Compiler>; + + /// Void Number Commitment Scheme Type + type VoidNumberCommitmentScheme: CommitmentScheme< + Trapdoor = Trapdoor<Self>, + Input = Self::SecretKey, + Output = Self::VoidNumber, + > + CommitmentScheme< + Self::Compiler, + Parameters = Self::VoidNumberCommitmentParametersVar, + Trapdoor = TrapdoorVar<Self>, + Input = Self::SecretKeyVar, + Output = Self::VoidNumberVar, + >; + /// UTXO Set Parameters Variable Type type UtxoSetParametersVar: Variable< - Self::ConstraintSystem, + Self::Compiler, Type = UtxoSetParameters<Self>, Mode = Constant, >; /// UTXO Set Witness Variable Type - type UtxoSetWitnessVar: Variable< - Self::ConstraintSystem, - Type = UtxoSetWitness<Self>, - Mode = Secret, - >; + type UtxoSetWitnessVar: Variable<Self::Compiler, Type = UtxoSetWitness<Self>, Mode = Secret>; /// UTXO Set Output Variable Type - type UtxoSetOutputVar: Variable< - Self::ConstraintSystem, - Type = UtxoSetOutput<Self>, - Mode = Public, - >; + type UtxoSetOutputVar: Variable<Self::Compiler, Type = UtxoSetOutput<Self>, Mode = Public>; /// UTXO Set Verifier Type - type UtxoSetVerifier: Verifier<Item = Utxo<Self>, Verification = bool>; + type UtxoSetVerifier: Verifier<Item = Self::Utxo, Verification = bool> + + Verifier< + Self::Compiler, + Parameters = Self::UtxoSetParametersVar, + Item = Self::UtxoVar, + Witness = Self::UtxoSetWitnessVar, + Output = Self::UtxoSetOutputVar, + Verification = <Self::Compiler as ConstraintSystem>::Bool, + >; /// Asset Id Variable Type - type AssetIdVar: Variable<Self::ConstraintSystem, Type = AssetId, Mode = PublicOrSecret> - + Equal<Self::ConstraintSystem>; + type AssetIdVar: Variable<Self::Compiler, Type = AssetId, Mode = PublicOrSecret> + + Equal<Self::Compiler>; /// Asset Value Variable Type - type AssetValueVar: Variable<Self::ConstraintSystem, Type = AssetValue, Mode = PublicOrSecret> - + Equal<Self::ConstraintSystem> - + Add<Output = Self::AssetValueVar>; + type AssetValueVar: Variable<Self::Compiler, Type = AssetValue, Mode = PublicOrSecret> + + Equal<Self::Compiler> + + Add<Self::Compiler>; /// Constraint System Type - type ConstraintSystem: ConstraintSystem - + key::constraint::KeyAgreementScheme< - Self::KeyAgreementScheme, - SecretKey = SecretKeyVar<Self>, - PublicKey = PublicKeyVar<Self>, - > + key::constraint::KeyDerivationFunction< - Self::TrapdoorDerivationFunction, - Key = SharedSecretVar<Self>, - Output = TrapdoorVar<Self>, - > + commitment::constraint::CommitmentScheme< - Self::EphemeralKeyCommitmentScheme, - Parameters = Self::EphemeralKeyParametersVar, - Trapdoor = Self::EphemeralKeyTrapdoorVar, - Input = Self::EphemeralKeyCommitmentSchemeInputVar, - Output = SecretKeyVar<Self>, - > + commitment::constraint::CommitmentScheme< - Self::CommitmentScheme, - Parameters = Self::CommitmentSchemeParametersVar, - Input = Self::CommitmentSchemeInputVar, - Output = Self::CommitmentSchemeOutputVar, - > + accumulator::constraint::Verifier< - Self::UtxoSetVerifier, - Parameters = Self::UtxoSetParametersVar, - Item = UtxoVar<Self>, - Witness = Self::UtxoSetWitnessVar, - Output = Self::UtxoSetOutputVar, - Verification = <Self::ConstraintSystem as ConstraintSystem>::Bool, - >; + type Compiler: ConstraintSystem; /// Proof System Type - type ProofSystem: ProofSystem<ConstraintSystem = Self::ConstraintSystem, Verification = bool> + type ProofSystem: ProofSystem<ConstraintSystem = Self::Compiler, Verification = bool> + ProofSystemInput<AssetId> + ProofSystemInput<AssetValue> + ProofSystemInput<UtxoSetOutput<Self>> - + ProofSystemInput<CommitmentSchemeOutput<Self>> + + ProofSystemInput<Utxo<Self>> + + ProofSystemInput<VoidNumber<Self>> + ProofSystemInput<PublicKey<Self>>; /// Note Encryption Scheme Type @@ -224,90 +216,73 @@ pub trait Configuration { trapdoor: &EphemeralKeyTrapdoor<Self>, asset: Asset, ) -> SecretKey<Self> { - Self::EphemeralKeyCommitmentScheme::start(parameters, trapdoor) - .update(&asset.id) - .update(&asset.value) - .commit() + Self::EphemeralKeyCommitmentScheme::commit(&mut (), parameters, trapdoor, &asset) } /// Generates the ephemeral secret key from `parameters`, `trapdoor`, and `asset`. #[inline] fn ephemeral_secret_key_var( - cs: &mut Self::ConstraintSystem, + cs: &mut Self::Compiler, parameters: &EphemeralKeyParametersVar<Self>, trapdoor: &EphemeralKeyTrapdoorVar<Self>, asset: &AssetVar<Self>, ) -> SecretKeyVar<Self> { - commitment::constraint::CommitmentScheme::<Self::EphemeralKeyCommitmentScheme>::start( - parameters, trapdoor, - ) - .update(&asset.id) - .update(&asset.value) - .commit(cs) + Self::EphemeralKeyCommitmentScheme::commit(cs, parameters, trapdoor, asset) } /// Derives a public key variable from a secret key variable. #[inline] fn ephemeral_public_key_var( - cs: &mut Self::ConstraintSystem, + cs: &mut Self::Compiler, secret_key: &SecretKeyVar<Self>, ) -> PublicKeyVar<Self> { - key::constraint::KeyAgreementScheme::<Self::KeyAgreementScheme>::derive(cs, secret_key) + Self::KeyAgreementScheme::derive(cs, secret_key) } /// Generates the commitment trapdoor associated to `secret_key` and `public_key`. #[inline] fn trapdoor(secret_key: &SecretKey<Self>, public_key: &PublicKey<Self>) -> Trapdoor<Self> { - Self::TrapdoorDerivationFunction::derive(Self::KeyAgreementScheme::agree( - secret_key, public_key, - )) + let shared_secret = Self::KeyAgreementScheme::agree(&mut (), secret_key, public_key); + Self::TrapdoorDerivationFunction::derive(&mut (), shared_secret) } /// Generates the commitment trapdoor associated to `secret_key` and `public_key`. #[inline] fn trapdoor_var( - cs: &mut Self::ConstraintSystem, + cs: &mut Self::Compiler, secret_key: &SecretKeyVar<Self>, public_key: &PublicKeyVar<Self>, ) -> TrapdoorVar<Self> { - let shared_secret = key::constraint::KeyAgreementScheme::agree(cs, secret_key, public_key); - key::constraint::KeyDerivationFunction::derive(cs, shared_secret) + let shared_secret = Self::KeyAgreementScheme::agree(cs, secret_key, public_key); + Self::TrapdoorDerivationFunction::derive(cs, shared_secret) } /// Generates the UTXO associated to `trapdoor` and `asset` from `parameters`. #[inline] fn utxo( - parameters: &CommitmentSchemeParameters<Self>, + parameters: &UtxoCommitmentParameters<Self>, trapdoor: &Trapdoor<Self>, asset: &Asset, ) -> Utxo<Self> { - Self::CommitmentScheme::start(parameters, trapdoor) - .update(&asset.id) - .update(&asset.value) - .commit() + Self::UtxoCommitmentScheme::commit(&mut (), parameters, trapdoor, asset) } /// Generates the UTXO associated to `trapdoor` and `asset` from `parameters`. #[inline] fn utxo_var( - cs: &mut Self::ConstraintSystem, - parameters: &CommitmentSchemeParametersVar<Self>, + cs: &mut Self::Compiler, + parameters: &UtxoCommitmentParametersVar<Self>, trapdoor: &TrapdoorVar<Self>, asset: &AssetVar<Self>, ) -> UtxoVar<Self> { - commitment::constraint::CommitmentScheme::<Self::CommitmentScheme>::start( - parameters, trapdoor, - ) - .update(&asset.id) - .update(&asset.value) - .commit(cs) + Self::UtxoCommitmentScheme::commit(cs, parameters, trapdoor, asset) } /// Generates the trapdoor associated to `secret_key` and `public_key` and then uses it to /// generate the UTXO associated to `asset`. #[inline] fn full_utxo( - parameters: &CommitmentSchemeParameters<Self>, + parameters: &UtxoCommitmentParameters<Self>, secret_key: &SecretKey<Self>, public_key: &PublicKey<Self>, asset: &Asset, @@ -319,8 +294,8 @@ pub trait Configuration { /// generate the UTXO associated to `asset`. #[inline] fn full_utxo_var( - cs: &mut Self::ConstraintSystem, - parameters: &CommitmentSchemeParametersVar<Self>, + cs: &mut Self::Compiler, + parameters: &UtxoCommitmentParametersVar<Self>, secret_key: &SecretKeyVar<Self>, public_key: &PublicKeyVar<Self>, asset: &AssetVar<Self>, @@ -332,43 +307,38 @@ pub trait Configuration { /// Generates the void number associated to `trapdoor` and `secret_key` using `parameters`. #[inline] fn void_number( - parameters: &CommitmentSchemeParameters<Self>, + parameters: &VoidNumberCommitmentParameters<Self>, trapdoor: &Trapdoor<Self>, secret_key: &SecretKey<Self>, ) -> VoidNumber<Self> { - Self::CommitmentScheme::start(parameters, trapdoor) - .update(secret_key) - .commit() + Self::VoidNumberCommitmentScheme::commit(&mut (), parameters, trapdoor, secret_key) } /// Generates the void number associated to `trapdoor` and `secret_key` using `parameters`. #[inline] fn void_number_var( - cs: &mut Self::ConstraintSystem, - parameters: &CommitmentSchemeParametersVar<Self>, + cs: &mut Self::Compiler, + parameters: &VoidNumberCommitmentParametersVar<Self>, trapdoor: &TrapdoorVar<Self>, secret_key: &SecretKeyVar<Self>, ) -> VoidNumberVar<Self> { - commitment::constraint::CommitmentScheme::<Self::CommitmentScheme>::start( - parameters, trapdoor, - ) - .update(secret_key) - .commit(cs) + Self::VoidNumberCommitmentScheme::commit(cs, parameters, trapdoor, secret_key) } /// Checks that the `utxo` is correctly constructed from the `secret_key`, `public_key`, and /// `asset`, returning the void number for the asset if so. #[inline] fn check_full_asset( - parameters: &CommitmentSchemeParameters<Self>, + utxo_parameters: &UtxoCommitmentParameters<Self>, + void_number_parameters: &VoidNumberCommitmentParameters<Self>, secret_key: &SecretKey<Self>, public_key: &PublicKey<Self>, asset: &Asset, utxo: &Utxo<Self>, ) -> Option<VoidNumber<Self>> { let trapdoor = Self::trapdoor(secret_key, public_key); - (&Self::utxo(parameters, &trapdoor, asset) == utxo) - .then(move || Self::void_number(parameters, &trapdoor, secret_key)) + (&Self::utxo(utxo_parameters, &trapdoor, asset) == utxo) + .then(move || Self::void_number(void_number_parameters, &trapdoor, secret_key)) } } @@ -392,10 +362,9 @@ pub type SharedSecret<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret; /// Shared Secret Variable Type -pub type SharedSecretVar<C> = - <<C as Configuration>::ConstraintSystem as key::constraint::KeyAgreementScheme< - <C as Configuration>::KeyAgreementScheme, - >>::SharedSecret; +pub type SharedSecretVar<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme< + <C as Configuration>::Compiler, +>>::SharedSecret; /// Ephemeral Key Commitment Scheme Parameters Type pub type EphemeralKeyParameters<C> = @@ -412,32 +381,25 @@ pub type EphemeralKeyTrapdoor<C> = pub type EphemeralKeyTrapdoorVar<C> = <C as Configuration>::EphemeralKeyTrapdoorVar; /// Trapdoor Type -pub type Trapdoor<C> = <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Trapdoor; +pub type Trapdoor<C> = <<C as Configuration>::UtxoCommitmentScheme as CommitmentScheme>::Trapdoor; /// Trapdoor Variable Type -pub type TrapdoorVar<C> = - <<C as Configuration>::ConstraintSystem as commitment::constraint::CommitmentScheme< - <C as Configuration>::CommitmentScheme, - >>::Trapdoor; - -/// Commitment Scheme Parameters Type -pub type CommitmentSchemeParameters<C> = - <<C as Configuration>::CommitmentScheme as CommitmentScheme>::Parameters; +pub type TrapdoorVar<C> = <<C as Configuration>::UtxoCommitmentScheme as CommitmentScheme< + <C as Configuration>::Compiler, +>>::Trapdoor; -/// Commitment Scheme Parameters Variable Type -pub type CommitmentSchemeParametersVar<C> = <C as Configuration>::CommitmentSchemeParametersVar; +/// UTXO Commitment Scheme Parameters Type +pub type UtxoCommitmentParameters<C> = + <<C as Configuration>::UtxoCommitmentScheme as CommitmentScheme>::Parameters; -/// Commitment Scheme Output Type -pub type CommitmentSchemeOutput<C> = <C as Configuration>::CommitmentSchemeOutput; - -/// Commitment Scheme Output Variable Type -pub type CommitmentSchemeOutputVar<C> = <C as Configuration>::CommitmentSchemeOutputVar; +/// UTXO Commitment Scheme Parameters Variable Type +pub type UtxoCommitmentParametersVar<C> = <C as Configuration>::UtxoCommitmentParametersVar; /// Unspend Transaction Output Type -pub type Utxo<C> = CommitmentSchemeOutput<C>; +pub type Utxo<C> = <<C as Configuration>::UtxoCommitmentScheme as CommitmentScheme>::Output; /// Unspent Transaction Output Variable Type -pub type UtxoVar<C> = CommitmentSchemeOutputVar<C>; +pub type UtxoVar<C> = <C as Configuration>::UtxoVar; /// UTXO Set Accumulator Verifier Parameters Type pub type UtxoSetParameters<C> = <<C as Configuration>::UtxoSetVerifier as Verifier>::Parameters; @@ -455,16 +417,25 @@ pub type UtxoSetOutput<C> = <<C as Configuration>::UtxoSetVerifier as Verifier>: pub type UtxoMembershipProof<C> = MembershipProof<<C as Configuration>::UtxoSetVerifier>; /// UTXO Membership Proof Variable Type -pub type UtxoMembershipProofVar<C> = accumulator::constraint::MembershipProof< +pub type UtxoMembershipProofVar<C> = accumulator::MembershipProof< <C as Configuration>::UtxoSetVerifier, - <C as Configuration>::ConstraintSystem, + <C as Configuration>::Compiler, >; +/// Void Number Commitment Scheme Parameters Type +pub type VoidNumberCommitmentParameters<C> = + <<C as Configuration>::VoidNumberCommitmentScheme as CommitmentScheme>::Parameters; + +/// Void Number Commitment Scheme Parameters Variable Type +pub type VoidNumberCommitmentParametersVar<C> = + <C as Configuration>::VoidNumberCommitmentParametersVar; + /// Void Number Type -pub type VoidNumber<C> = CommitmentSchemeOutput<C>; +pub type VoidNumber<C> = + <<C as Configuration>::VoidNumberCommitmentScheme as CommitmentScheme>::Output; /// Void Number Variable Type -pub type VoidNumberVar<C> = CommitmentSchemeOutputVar<C>; +pub type VoidNumberVar<C> = <C as Configuration>::VoidNumberVar; /// Encrypted Note Type pub type EncryptedNote<C> = EncryptedMessage<<C as Configuration>::NoteEncryptionScheme>; @@ -498,8 +469,11 @@ where /// Ephemeral Key Commitment Scheme Parameters pub ephemeral_key: &'p EphemeralKeyParameters<C>, - /// Commitment Scheme Parameters - pub commitment_scheme: &'p CommitmentSchemeParameters<C>, + /// UTXO Commitment Scheme Parameters + pub utxo_commitment: &'p UtxoCommitmentParameters<C>, + + /// Void Number Commitment Scheme Parameters + pub void_number_commitment: &'p VoidNumberCommitmentParameters<C>, /// UTXO Set Verifier Parameters pub utxo_set_verifier: &'p UtxoSetParameters<C>, @@ -509,17 +483,19 @@ impl<'p, C> Parameters<'p, C> where C: Configuration, { - /// Builds a new [`Parameters`] from `ephemeral_key`, `commitment_scheme`, and - /// `utxo_set_verifier` parameters. + /// Builds a new [`Parameters`] from `ephemeral_key`, `utxo_commitment`, + /// `void_number_commitment`, and `utxo_set_verifier` parameters. #[inline] pub fn new( ephemeral_key: &'p EphemeralKeyParameters<C>, - commitment_scheme: &'p CommitmentSchemeParameters<C>, + utxo_commitment: &'p UtxoCommitmentParameters<C>, + void_number_commitment: &'p VoidNumberCommitmentParameters<C>, utxo_set_verifier: &'p UtxoSetParameters<C>, ) -> Self { Self { ephemeral_key, - commitment_scheme, + utxo_commitment, + void_number_commitment, utxo_set_verifier, } } @@ -533,8 +509,11 @@ where /// Ephemeral Key Commitment Scheme Parameters pub ephemeral_key: EphemeralKeyParametersVar<C>, - /// Commitment Scheme Parameters - pub commitment_scheme: CommitmentSchemeParametersVar<C>, + /// UTXO Commitment Scheme Parameters + pub utxo_commitment: UtxoCommitmentParametersVar<C>, + + /// Void Number Commitment Scheme Parameters + pub void_number_commitment: VoidNumberCommitmentParametersVar<C>, /// UTXO Set Verifier Parameters pub utxo_set_verifier: UtxoSetParametersVar<C>, @@ -543,27 +522,29 @@ where __: PhantomData<&'p ()>, } -impl<'p, C> Variable<C::ConstraintSystem> for ParametersVar<'p, C> +impl<'p, C> Variable<C::Compiler> for ParametersVar<'p, C> where C: Configuration, EphemeralKeyParameters<C>: 'p, - CommitmentSchemeParameters<C>: 'p, + UtxoCommitmentParameters<C>: 'p, + VoidNumberCommitmentParameters<C>: 'p, UtxoSetParameters<C>: 'p, { type Type = Parameters<'p, C>; - type Mode = Public; + type Mode = Constant; #[inline] - fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new(cs: &mut C::Compiler, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self { ephemeral_key: this.ephemeral_key.as_known(cs, mode), - commitment_scheme: this.commitment_scheme.as_known(cs, mode), + utxo_commitment: this.utxo_commitment.as_known(cs, mode), + void_number_commitment: this.void_number_commitment.as_known(cs, mode), utxo_set_verifier: this.utxo_set_verifier.as_known(cs, mode), __: PhantomData, }, - _ => unreachable!(), + _ => unreachable!("Constant variables cannot be unknown."), } } } @@ -596,8 +577,8 @@ where #[inline] pub fn derive(&self) -> ReceivingKey<C> { ReceivingKey { - spend: C::KeyAgreementScheme::derive(&self.spend), - view: C::KeyAgreementScheme::derive(&self.view), + spend: C::KeyAgreementScheme::derive(&mut (), &self.spend), + view: C::KeyAgreementScheme::derive(&mut (), &self.view), } } @@ -612,24 +593,39 @@ where #[inline] pub fn check_full_asset( &self, - parameters: &CommitmentSchemeParameters<C>, + utxo_parameters: &UtxoCommitmentParameters<C>, + void_number_parameters: &VoidNumberCommitmentParameters<C>, ephemeral_key: &PublicKey<C>, asset: &Asset, utxo: &Utxo<C>, ) -> Option<VoidNumber<C>> { - C::check_full_asset(parameters, &self.spend, ephemeral_key, asset, utxo) + C::check_full_asset( + utxo_parameters, + void_number_parameters, + &self.spend, + ephemeral_key, + asset, + utxo, + ) } /// Prepares `self` for spending `asset` with the given `ephemeral_key`. #[inline] pub fn sender( &self, - parameters: &CommitmentSchemeParameters<C>, + utxo_parameters: &UtxoCommitmentParameters<C>, + void_number_parameters: &VoidNumberCommitmentParameters<C>, ephemeral_key: PublicKey<C>, asset: Asset, ) -> PreSender<C> { // TODO: See if this clone is really needed. - PreSender::new(parameters, self.spend.clone(), ephemeral_key, asset) + PreSender::new( + utxo_parameters, + void_number_parameters, + self.spend.clone(), + ephemeral_key, + asset, + ) } /// Prepares `self` for receiving `asset`. @@ -637,13 +633,13 @@ where pub fn receiver( &self, ephemeral_key_parameters: &EphemeralKeyParameters<C>, - commitment_scheme_parameters: &CommitmentSchemeParameters<C>, + utxo_parameters: &UtxoCommitmentParameters<C>, ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, asset: Asset, ) -> Receiver<C> { self.derive().into_receiver( ephemeral_key_parameters, - commitment_scheme_parameters, + utxo_parameters, ephemeral_key_trapdoor, asset, ) @@ -654,18 +650,20 @@ where pub fn internal_pair( &self, ephemeral_key_parameters: &EphemeralKeyParameters<C>, - commitment_scheme_parameters: &CommitmentSchemeParameters<C>, + utxo_parameters: &UtxoCommitmentParameters<C>, + void_number_parameters: &VoidNumberCommitmentParameters<C>, ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, asset: Asset, ) -> (Receiver<C>, PreSender<C>) { let receiver = self.receiver( ephemeral_key_parameters, - commitment_scheme_parameters, + utxo_parameters, ephemeral_key_trapdoor, asset, ); let sender = self.sender( - commitment_scheme_parameters, + utxo_parameters, + void_number_parameters, receiver.ephemeral_public_key().clone(), asset, ); @@ -677,13 +675,15 @@ where pub fn internal_zero_pair( &self, ephemeral_key_parameters: &EphemeralKeyParameters<C>, - commitment_scheme_parameters: &CommitmentSchemeParameters<C>, + utxo_parameters: &UtxoCommitmentParameters<C>, + void_number_parameters: &VoidNumberCommitmentParameters<C>, ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, asset_id: AssetId, ) -> (Receiver<C>, PreSender<C>) { self.internal_pair( ephemeral_key_parameters, - commitment_scheme_parameters, + utxo_parameters, + void_number_parameters, ephemeral_key_trapdoor, Asset::zero(asset_id), ) @@ -720,13 +720,13 @@ where pub fn into_receiver( self, ephemeral_key_parameters: &EphemeralKeyParameters<C>, - commitment_scheme_parameters: &CommitmentSchemeParameters<C>, + utxo_parameters: &UtxoCommitmentParameters<C>, ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, asset: Asset, ) -> Receiver<C> { Receiver::new( ephemeral_key_parameters, - commitment_scheme_parameters, + utxo_parameters, ephemeral_key_trapdoor, self.spend, self.view, @@ -763,15 +763,16 @@ where /// Builds a new [`PreSender`] for `spend` to spend `asset` with `ephemeral_public_key`. #[inline] pub fn new( - parameters: &CommitmentSchemeParameters<C>, + utxo_parameters: &UtxoCommitmentParameters<C>, + void_number_parameters: &VoidNumberCommitmentParameters<C>, spend: SecretKey<C>, ephemeral_public_key: PublicKey<C>, asset: Asset, ) -> Self { let trapdoor = C::trapdoor(&spend, &ephemeral_public_key); Self { - utxo: C::utxo(parameters, &trapdoor, &asset), - void_number: C::void_number(parameters, &trapdoor, &spend), + utxo: C::utxo(utxo_parameters, &trapdoor, &asset), + void_number: C::void_number(void_number_parameters, &trapdoor, &spend), spend, ephemeral_public_key, asset, @@ -936,22 +937,28 @@ where pub fn get_well_formed_asset( self, parameters: &ParametersVar<C>, - cs: &mut C::ConstraintSystem, + cs: &mut C::Compiler, ) -> AssetVar<C> { let trapdoor = C::trapdoor_var(cs, &self.spend, &self.ephemeral_public_key); - let utxo = C::utxo_var(cs, &parameters.commitment_scheme, &trapdoor, &self.asset); - let is_valid_proof = - self.utxo_membership_proof - .verify(&parameters.utxo_set_verifier, &utxo, cs); + let utxo = C::utxo_var(cs, &parameters.utxo_commitment, &trapdoor, &self.asset); + let is_valid_proof = self.utxo_membership_proof.verify_with_compiler( + &parameters.utxo_set_verifier, + &utxo, + cs, + ); cs.assert(is_valid_proof); - let void_number = - C::void_number_var(cs, &parameters.commitment_scheme, &trapdoor, &self.spend); + let void_number = C::void_number_var( + cs, + &parameters.void_number_commitment, + &trapdoor, + &self.spend, + ); cs.assert_eq(&self.void_number, &void_number); self.asset } } -impl<C> Variable<C::ConstraintSystem> for SenderVar<C> +impl<C> Variable<C::Compiler> for SenderVar<C> where C: Configuration, { @@ -960,7 +967,7 @@ where type Mode = Derived; #[inline] - fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new(cs: &mut C::Compiler, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self { spend: this.spend.as_known(cs, mode), @@ -1151,7 +1158,7 @@ where #[inline] pub fn new( ephemeral_key_parameters: &EphemeralKeyParameters<C>, - commitment_scheme_parameters: &CommitmentSchemeParameters<C>, + utxo_parameters: &UtxoCommitmentParameters<C>, ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, spend: PublicKey<C>, view: PublicKey<C>, @@ -1160,12 +1167,7 @@ where let ephemeral_secret_key = C::ephemeral_secret_key(ephemeral_key_parameters, &ephemeral_key_trapdoor, asset); Self { - utxo: C::full_utxo( - commitment_scheme_parameters, - &ephemeral_secret_key, - &spend, - &asset, - ), + utxo: C::full_utxo(utxo_parameters, &ephemeral_secret_key, &spend, &asset), note: EncryptedMessage::new(&view, &ephemeral_secret_key, asset), ephemeral_key_trapdoor, spend, @@ -1220,7 +1222,7 @@ where pub fn get_well_formed_asset( self, parameters: &ParametersVar<C>, - cs: &mut C::ConstraintSystem, + cs: &mut C::Compiler, ) -> AssetVar<C> { let ephemeral_secret_key = C::ephemeral_secret_key_var( cs, @@ -1232,7 +1234,7 @@ where cs.assert_eq(&self.ephemeral_public_key, &ephemeral_public_key); let utxo = C::full_utxo_var( cs, - &parameters.commitment_scheme, + &parameters.utxo_commitment, &ephemeral_secret_key, &self.spend, &self.asset, @@ -1242,7 +1244,7 @@ where } } -impl<C> Variable<C::ConstraintSystem> for ReceiverVar<C> +impl<C> Variable<C::Compiler> for ReceiverVar<C> where C: Configuration, { @@ -1251,7 +1253,7 @@ where type Mode = Derived; #[inline] - fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new(cs: &mut C::Compiler, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self { ephemeral_key_trapdoor: this.ephemeral_key_trapdoor.as_known(cs, mode), @@ -1577,36 +1579,34 @@ where { /// Builds constraints for the [`Transfer`] validity proof. #[inline] - fn build_validity_constraints( - self, - parameters: &ParametersVar<C>, - cs: &mut C::ConstraintSystem, - ) { + fn build_validity_constraints(self, parameters: &ParametersVar<C>, cs: &mut C::Compiler) { let mut secret_asset_ids = Vec::with_capacity(SENDERS + RECEIVERS); - let input_sum = self - .senders - .into_iter() - .map(|s| { - let asset = s.get_well_formed_asset(parameters, cs); - secret_asset_ids.push(asset.id); - asset.value - }) - .chain(self.sources) - .reduce(Add::add) - .unwrap(); - - let output_sum = self - .receivers - .into_iter() - .map(|r| { - let asset = r.get_well_formed_asset(parameters, cs); - secret_asset_ids.push(asset.id); - asset.value - }) - .chain(self.sinks) - .reduce(Add::add) - .unwrap(); + let input_sum = Self::value_sum( + self.senders + .into_iter() + .map(|s| { + let asset = s.get_well_formed_asset(parameters, cs); + secret_asset_ids.push(asset.id); + asset.value + }) + .chain(self.sources) + .collect::<Vec<_>>(), + cs, + ); + + let output_sum = Self::value_sum( + self.receivers + .into_iter() + .map(|r| { + let asset = r.get_well_formed_asset(parameters, cs); + secret_asset_ids.push(asset.id); + asset.value + }) + .chain(self.sinks) + .collect::<Vec<_>>(), + cs, + ); cs.assert_eq(&input_sum, &output_sum); @@ -1615,10 +1615,21 @@ where _ => cs.assert_all_eq(secret_asset_ids.iter()), } } + + /// Computes the sum of the asset values over `iter` inside of `cs`. + #[inline] + fn value_sum<I>(iter: I, cs: &mut C::Compiler) -> C::AssetValueVar + where + I: IntoIterator<Item = C::AssetValueVar>, + { + iter.into_iter() + .reduce(move |l, r| Add::add(cs, l, r)) + .unwrap() + } } impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> - Variable<C::ConstraintSystem> for TransferVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> + Variable<C::Compiler> for TransferVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> where C: Configuration, { @@ -1627,7 +1638,7 @@ where type Mode = Derived; #[inline] - fn new(cs: &mut C::ConstraintSystem, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new(cs: &mut C::Compiler, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self { asset_id: this.asset_id.map(|id| id.as_known(cs, Public)), diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 98d352d9c..c6cd6e3c2 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -32,9 +32,10 @@ use crate::{ canonical::{ Mint, PrivateTransfer, PrivateTransferShape, Reclaim, Selection, Shape, Transaction, }, - CommitmentSchemeParameters, EncryptedNote, EphemeralKeyParameters, Parameters, PreSender, - ProofSystemError, ProvingContext, PublicKey, Receiver, ReceivingKey, SecretKey, Sender, - SpendingKey, Transfer, TransferPost, Utxo, VoidNumber, + EncryptedNote, EphemeralKeyParameters, Parameters, PreSender, ProofSystemError, + ProvingContext, PublicKey, Receiver, ReceivingKey, SecretKey, Sender, SpendingKey, + Transfer, TransferPost, Utxo, UtxoCommitmentParameters, VoidNumber, + VoidNumberCommitmentParameters, }, }; use alloc::{vec, vec::Vec}; @@ -257,8 +258,11 @@ where /// Ephemeral Key Parameters ephemeral_key_parameters: EphemeralKeyParameters<C>, - /// Commitment Scheme Parameters - commitment_scheme_parameters: CommitmentSchemeParameters<C>, + /// UTXO Commitment Scheme Parameters + utxo_commitment_parameters: UtxoCommitmentParameters<C>, + + /// Void Number Commitment Scheme Parameters + void_number_commitment_parameters: VoidNumberCommitmentParameters<C>, /// UTXO Set utxo_set: C::UtxoSet, @@ -275,12 +279,14 @@ where C: Configuration, { /// Builds a new [`Signer`]. + #[allow(clippy::too_many_arguments)] // FIXME: Find a better way to construct this. #[inline] fn new_inner( account_table: AccountTable<C>, proving_context: ProvingContext<C>, ephemeral_key_parameters: EphemeralKeyParameters<C>, - commitment_scheme_parameters: CommitmentSchemeParameters<C>, + utxo_commitment_parameters: UtxoCommitmentParameters<C>, + void_number_commitment_parameters: VoidNumberCommitmentParameters<C>, utxo_set: C::UtxoSet, assets: C::AssetMap, rng: C::Rng, @@ -289,7 +295,8 @@ where account_table, proving_context, ephemeral_key_parameters, - commitment_scheme_parameters, + utxo_commitment_parameters, + void_number_commitment_parameters, utxo_set, assets, rng, @@ -307,7 +314,8 @@ where account_table: AccountTable<C>, proving_context: ProvingContext<C>, ephemeral_key_parameters: EphemeralKeyParameters<C>, - commitment_scheme_parameters: CommitmentSchemeParameters<C>, + utxo_commitment_parameters: UtxoCommitmentParameters<C>, + void_number_commitment_parameters: VoidNumberCommitmentParameters<C>, utxo_set: C::UtxoSet, rng: C::Rng, ) -> Self { @@ -315,7 +323,8 @@ where account_table, proving_context, ephemeral_key_parameters, - commitment_scheme_parameters, + utxo_commitment_parameters, + void_number_commitment_parameters, utxo_set, Default::default(), rng, @@ -354,7 +363,8 @@ where .map_err(key::Error::KeyDerivationError)? { if let Some(void_number) = C::check_full_asset( - &self.commitment_scheme_parameters, + &self.utxo_commitment_parameters, + &self.void_number_commitment_parameters, &keypair.spend, &ephemeral_public_key, &asset, @@ -437,7 +447,8 @@ where ) -> Result<PreSender<C>, SignerError<C>> { let (spend_index, ephemeral_key) = key; Ok(PreSender::new( - &self.commitment_scheme_parameters, + &self.utxo_commitment_parameters, + &self.void_number_commitment_parameters, self.account().spend_key(spend_index)?, ephemeral_key, asset, @@ -453,7 +464,7 @@ where .map_err(key::Error::KeyDerivationError)?; Ok(SpendingKey::new(keypair.spend, keypair.view).receiver( &self.ephemeral_key_parameters, - &self.commitment_scheme_parameters, + &self.utxo_commitment_parameters, self.rng.gen(), asset, )) @@ -470,7 +481,8 @@ where .map_err(key::Error::KeyDerivationError)?; let (receiver, pre_sender) = SpendingKey::new(keypair.spend, keypair.view).internal_pair( &self.ephemeral_key_parameters, - &self.commitment_scheme_parameters, + &self.utxo_commitment_parameters, + &self.void_number_commitment_parameters, self.rng.gen(), asset, ); @@ -504,7 +516,8 @@ where .into_post( Parameters::new( &self.ephemeral_key_parameters, - &self.commitment_scheme_parameters, + &self.utxo_commitment_parameters, + &self.void_number_commitment_parameters, self.utxo_set.parameters(), ), &self.proving_context, @@ -527,7 +540,8 @@ where .map_err(key::Error::KeyDerivationError)?; Ok(Join::new( &self.ephemeral_key_parameters, - &self.commitment_scheme_parameters, + &self.utxo_commitment_parameters, + &self.void_number_commitment_parameters, asset_id.with(total), &SpendingKey::new(keypair.spend, keypair.view), &mut self.rng, @@ -625,7 +639,7 @@ where pub fn prepare_receiver(&mut self, asset: Asset, receiver: ReceivingKey<C>) -> Receiver<C> { receiver.into_receiver( &self.ephemeral_key_parameters, - &self.commitment_scheme_parameters, + &self.utxo_commitment_parameters, self.rng.gen(), asset, ) diff --git a/manta-crypto/src/accumulator.rs b/manta-crypto/src/accumulator.rs index 8574bd2da..04f83a913 100644 --- a/manta-crypto/src/accumulator.rs +++ b/manta-crypto/src/accumulator.rs @@ -19,7 +19,7 @@ // TODO: See if we can modify `Accumulator` so that it can extend the `Verifier` trait directly. /// Accumulator Membership Verifier -pub trait Verifier { +pub trait Verifier<J = ()> { /// Parameters Type type Parameters; @@ -34,12 +34,13 @@ pub trait Verifier { /// Verification Type /// - /// Typically this is either [`bool`] or some [`Result`] type. + /// Typically this is either [`bool`], a [`Result`] type, or a circuit boolean variable. type Verification; /// Verifies that `item` is stored in a known accumulator with accumulated `output` and /// membership `witness`. fn verify( + compiler: &mut J, parameters: &Self::Parameters, item: &Self::Item, witness: &Self::Witness, @@ -181,9 +182,9 @@ pub trait OptimizedAccumulator: Accumulator { } /// Accumulator Membership Proof -pub struct MembershipProof<V> +pub struct MembershipProof<V, J = ()> where - V: Verifier + ?Sized, + V: Verifier<J> + ?Sized, { /// Secret Membership Witness witness: V::Witness, @@ -192,9 +193,9 @@ where output: V::Output, } -impl<V> MembershipProof<V> +impl<V, J> MembershipProof<V, J> where - V: Verifier + ?Sized, + V: Verifier<J> + ?Sized, { /// Builds a new [`MembershipProof`] from `witness` and `output`. #[inline] @@ -209,49 +210,35 @@ where self.output } + /// Verifies that `item` is stored in a known accumulator using `parameters`. + #[inline] + pub fn verify_with_compiler( + &self, + parameters: &V::Parameters, + item: &V::Item, + compiler: &mut J, + ) -> V::Verification { + V::verify(compiler, parameters, item, &self.witness, &self.output) + } +} + +impl<V> MembershipProof<V> +where + V: Verifier + ?Sized, +{ /// Verifies that `item` is stored in a known accumulator using `parameters`. #[inline] pub fn verify(&self, parameters: &V::Parameters, item: &V::Item) -> V::Verification { - V::verify(parameters, item, &self.witness, &self.output) + self.verify_with_compiler(parameters, item, &mut ()) } } /// Constraint System Gadgets pub mod constraint { + use super::*; use crate::constraint::{Allocation, AllocationMode, Derived, Variable, VariableSource}; use core::marker::PhantomData; - /// Accumulator Verifier Gadget - pub trait Verifier<V> - where - V: super::Verifier, - { - /// Paramters Type - type Parameters: Variable<Self, Type = V::Parameters>; - - /// Item Type - type Item: Variable<Self, Type = V::Item>; - - /// Secret Witness Type - type Witness: Variable<Self, Type = V::Witness>; - - /// Output Type - type Output: Variable<Self, Type = V::Output>; - - /// Verification Type - type Verification: Variable<Self, Type = V::Verification>; - - /// Verifies that `item` is stored in a known accumulator with accumulated `output` and - /// membership `witness`. - fn verify( - &mut self, - parameters: &Self::Parameters, - item: &Self::Item, - witness: &Self::Witness, - output: &Self::Output, - ) -> Self::Verification; - } - /// Membership Proof Allocation Mode Entry #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] @@ -299,71 +286,29 @@ pub mod constraint { type Unknown = MembershipProofModeEntry<WitnessMode::Unknown, OutputMode::Unknown>; } - /// Accumulator Membership Proof - pub struct MembershipProof<B, V> - where - B: super::Verifier, - V: Verifier<B>, - { - /// Secret Membership Witness - witness: V::Witness, - - /// Accumulator Output - output: V::Output, - } - - impl<B, V> MembershipProof<B, V> - where - B: super::Verifier, - V: Verifier<B>, - { - /// Builds a new [`MembershipProof`] from `witness` and `output`. - #[inline] - pub fn new(witness: V::Witness, output: V::Output) -> Self { - Self { witness, output } - } - - /// Returns the accumulated output part of `self`, dropping the - /// [`V::Witness`](Verifier::Witness). - #[inline] - pub fn into_output(self) -> V::Output { - self.output - } - - /// Verifies that `item` is stored in a known accumulator using `verifier`. - #[inline] - pub fn verify( - &self, - parameters: &V::Parameters, - item: &V::Item, - cs: &mut V, - ) -> V::Verification { - cs.verify(parameters, item, &self.witness, &self.output) - } - } - - impl<B, V> Variable<V> for MembershipProof<B, V> + impl<V, J> Variable<J> for MembershipProof<V, J> where - B: super::Verifier, - V: Verifier<B>, + V: Verifier + Verifier<J>, + <V as Verifier<J>>::Witness: Variable<J, Type = <V as Verifier>::Witness>, + <V as Verifier<J>>::Output: Variable<J, Type = <V as Verifier>::Output>, { - type Type = super::MembershipProof<B>; + type Type = MembershipProof<V>; type Mode = MembershipProofMode< - <V::Witness as Variable<V>>::Mode, - <V::Output as Variable<V>>::Mode, + <<V as Verifier<J>>::Witness as Variable<J>>::Mode, + <<V as Verifier<J>>::Output as Variable<J>>::Mode, >; #[inline] - fn new(cs: &mut V, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new(cs: &mut J, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self::new( this.witness.as_known(cs, mode.witness), this.output.as_known(cs, mode.output), ), Allocation::Unknown(mode) => Self::new( - V::Witness::new_unknown(cs, mode.witness), - V::Output::new_unknown(cs, mode.output), + <V as Verifier<J>>::Witness::new_unknown(cs, mode.witness), + <V as Verifier<J>>::Output::new_unknown(cs, mode.output), ), } } diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index 9c7c7b488..e154e5d24 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -19,7 +19,7 @@ use core::{fmt::Debug, hash::Hash}; /// Commitment Scheme -pub trait CommitmentScheme { +pub trait CommitmentScheme<J = ()> { /// Parameters Type type Parameters; @@ -34,6 +34,7 @@ pub trait CommitmentScheme { /// Commits to the `input` value using `parameters` and randomness `trapdoor`. fn commit( + compiler: &mut J, parameters: &Self::Parameters, trapdoor: &Self::Trapdoor, input: &Self::Input, @@ -44,7 +45,7 @@ pub trait CommitmentScheme { fn start<'c>( parameters: &'c Self::Parameters, trapdoor: &'c Self::Trapdoor, - ) -> Builder<'c, Self> + ) -> Builder<'c, Self, J> where Self::Input: Default, { @@ -71,9 +72,9 @@ where Hash(bound = "C::Parameters: Hash, C::Trapdoor: Hash, C::Input: Hash"), PartialEq(bound = "C::Parameters: PartialEq, C::Trapdoor: PartialEq, C::Input: PartialEq") )] -pub struct Builder<'c, C> +pub struct Builder<'c, C, J = ()> where - C: CommitmentScheme + ?Sized, + C: CommitmentScheme<J> + ?Sized, C::Input: Default, { /// Commitment Parameters @@ -86,9 +87,9 @@ where input: C::Input, } -impl<'c, C> Builder<'c, C> +impl<'c, C, J> Builder<'c, C, J> where - C: CommitmentScheme + ?Sized, + C: CommitmentScheme<J> + ?Sized, C::Input: Default, { /// Returns a new [`Builder`] with fixed `parameters` and `trapdoor`. @@ -130,118 +131,19 @@ where /// Commits to the input stored in the builder. #[inline] - pub fn commit(self) -> C::Output { - C::commit(self.parameters, self.trapdoor, &self.input) + pub fn commit_with_compiler(self, compiler: &mut J) -> C::Output { + C::commit(compiler, self.parameters, self.trapdoor, &self.input) } } -/// Constraint System Gadgets -pub mod constraint { - use super::Input; - use crate::constraint::Variable; - - /// Commitment Scheme Gadget - pub trait CommitmentScheme<C> - where - C: super::CommitmentScheme, - { - /// Parameters Type - type Parameters: Variable<Self, Type = C::Parameters>; - - /// Input Type - type Input: Variable<Self, Type = C::Input>; - - /// Trapdoor Type - type Trapdoor: Variable<Self, Type = C::Trapdoor>; - - /// Output Type - type Output: Variable<Self, Type = C::Output>; - - /// Commits to the `input` value using `parameters` and randomness `trapdoor`. - fn commit( - &mut self, - parameters: &Self::Parameters, - trapdoor: &Self::Trapdoor, - input: &Self::Input, - ) -> Self::Output; - - /// Starts a new [`Builder`] for extended commitments. - #[inline] - fn start<'c>( - parameters: &'c Self::Parameters, - trapdoor: &'c Self::Trapdoor, - ) -> Builder<'c, C, Self> - where - Self::Input: Default, - { - Builder::new(parameters, trapdoor) - } - } - - /// Commitment Builder - pub struct Builder<'c, B, C> - where - B: super::CommitmentScheme, - C: CommitmentScheme<B> + ?Sized, - C::Input: Default, - { - /// Commitment Parameters - parameters: &'c C::Parameters, - - /// Commitment Trapdoor - trapdoor: &'c C::Trapdoor, - - /// Commitment Input Accumulator - input: C::Input, - } - - impl<'c, B, C> Builder<'c, B, C> - where - B: super::CommitmentScheme, - C: CommitmentScheme<B> + ?Sized, - C::Input: Default, - { - /// Returns a new [`Builder`] with fixed `parameters` and `trapdoor`. - #[inline] - pub fn new(parameters: &'c C::Parameters, trapdoor: &'c C::Trapdoor) -> Self { - Self { - parameters, - trapdoor, - input: Default::default(), - } - } - - /// Updates the builder with the `next` input. - #[inline] - #[must_use] - pub fn update<T>(mut self, next: &T) -> Self - where - T: ?Sized, - C::Input: Input<T>, - { - self.input.extend(next); - self - } - - /// Updates the builder with each item in `iter`. - #[inline] - #[must_use] - pub fn update_all<'t, T, I>(mut self, iter: I) -> Self - where - T: 't + ?Sized, - I: IntoIterator<Item = &'t T>, - C::Input: Input<T>, - { - for next in iter { - self.input.extend(next); - } - self - } - - /// Commits to the input stored in the builder. - #[inline] - pub fn commit(self, cs: &mut C) -> C::Output { - cs.commit(self.parameters, self.trapdoor, &self.input) - } +impl<'c, C> Builder<'c, C> +where + C: CommitmentScheme + ?Sized, + C::Input: Default, +{ + /// Commits to the input stored in the builder. + #[inline] + pub fn commit(self) -> C::Output { + self.commit_with_compiler(&mut ()) } } diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index 77320d24f..9b4ca4856 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -487,6 +487,15 @@ where } } +/// Addition Trait +pub trait Add<C> +where + C: ConstraintSystem + ?Sized, +{ + /// Adds `lhs` to `rhs` inside of `cs`, returning the sum. + fn add(cs: &mut C, lhs: Self, rhs: Self) -> Self; +} + /// Proof System pub trait ProofSystem { /// Constraint System diff --git a/manta-crypto/src/encryption.rs b/manta-crypto/src/encryption.rs index 8805a089b..eb1d7e641 100644 --- a/manta-crypto/src/encryption.rs +++ b/manta-crypto/src/encryption.rs @@ -69,7 +69,10 @@ pub trait HybridPublicKeyEncryptionScheme: SymmetricKeyEncryptionScheme { /// [`KeyDerivationFunction::derive`]. #[inline] fn agree_derive(secret_key: &SecretKey<Self>, public_key: &PublicKey<Self>) -> Self::Key { - Self::KeyDerivationFunction::derive(Self::KeyAgreementScheme::agree(secret_key, public_key)) + Self::KeyDerivationFunction::derive( + &mut (), + Self::KeyAgreementScheme::agree(&mut (), secret_key, public_key), + ) } } @@ -162,7 +165,7 @@ where ) -> Self { Self { ciphertext: H::encrypt(H::agree_derive(ephemeral_secret_key, public_key), plaintext), - ephemeral_public_key: H::KeyAgreementScheme::derive(ephemeral_secret_key), + ephemeral_public_key: H::KeyAgreementScheme::derive(&mut (), ephemeral_secret_key), } } diff --git a/manta-crypto/src/key.rs b/manta-crypto/src/key.rs index 64ebd4795..ffe7a7897 100644 --- a/manta-crypto/src/key.rs +++ b/manta-crypto/src/key.rs @@ -17,7 +17,7 @@ //! Cryptographic Key Primitives /// Key Derivation Function -pub trait KeyDerivationFunction { +pub trait KeyDerivationFunction<J = ()> { /// Input Key Type type Key; @@ -25,7 +25,7 @@ pub trait KeyDerivationFunction { type Output; /// Derives an output key from `secret` computed from a cryptographic agreement scheme. - fn derive(secret: Self::Key) -> Self::Output; + fn derive(compiler: &mut J, secret: Self::Key) -> Self::Output; } /// Key Agreement Scheme @@ -43,7 +43,7 @@ pub trait KeyDerivationFunction { /// ``` /// This ensures that both parties in the shared computation will arrive at the same conclusion /// about the value of the [`SharedSecret`](Self::SharedSecret). -pub trait KeyAgreementScheme { +pub trait KeyAgreementScheme<J = ()> { /// Secret Key Type type SecretKey; @@ -55,7 +55,7 @@ pub trait KeyAgreementScheme { /// Derives a public key corresponding to `secret_key`. This public key should be sent to the /// other party involved in the shared computation. - fn derive(secret_key: &Self::SecretKey) -> Self::PublicKey; + fn derive(compiler: &mut J, secret_key: &Self::SecretKey) -> Self::PublicKey; /// Derives a public key corresponding to `secret_key`. This public key should be sent to the /// other party involved in the shared computation. @@ -68,12 +68,16 @@ pub trait KeyAgreementScheme { /// /// [`derive`]: Self::derive #[inline] - fn derive_owned(secret_key: Self::SecretKey) -> Self::PublicKey { - Self::derive(&secret_key) + fn derive_owned(compiler: &mut J, secret_key: Self::SecretKey) -> Self::PublicKey { + Self::derive(compiler, &secret_key) } /// Computes the shared secret given the known `secret_key` and the given `public_key`. - fn agree(secret_key: &Self::SecretKey, public_key: &Self::PublicKey) -> Self::SharedSecret; + fn agree( + compiler: &mut J, + secret_key: &Self::SecretKey, + public_key: &Self::PublicKey, + ) -> Self::SharedSecret; /// Computes the shared secret given the known `secret_key` and the given `public_key`. /// @@ -85,91 +89,11 @@ pub trait KeyAgreementScheme { /// /// [`agree`]: Self::agree #[inline] - fn agree_owned(secret_key: Self::SecretKey, public_key: Self::PublicKey) -> Self::SharedSecret { - Self::agree(&secret_key, &public_key) - } -} - -/// Key Agreement Scheme with an attached Key Derivation Function -pub trait KeyAgreementWithDerivation: KeyAgreementScheme { - /// Output Key Type - type Output; - - /// Key Derivation Function Type - type KeyDerivationFunction: KeyDerivationFunction< - Key = Self::SharedSecret, - Output = Self::Output, - >; - - /// Computes the shared secret given the known `secret_key` and the given `public_key` and then - /// uses the key derivation function to derive a final shared secret. - /// - /// # Implementation Note - /// - /// This method is an optimization path for calling [`KeyAgreementScheme::agree`] and then - /// [`KeyDerivationFunction::derive`]. - #[inline] - fn agree_derive(secret_key: &Self::SecretKey, public_key: &Self::PublicKey) -> Self::Output { - Self::KeyDerivationFunction::derive(Self::agree(secret_key, public_key)) - } - - /// Computes the shared secret given the known `secret_key` and the given `public_key` and then - /// uses the key derivation function to derive a final shared secret. - /// - /// # Implementation Note - /// - /// This method is an optimization path for [`agree_derive`](Self::agree_derive). See - /// [`KeyAgreementScheme::agree_owned`] for more on this optimization. - #[inline] - fn agree_derive_owned( + fn agree_owned( + compiler: &mut J, secret_key: Self::SecretKey, public_key: Self::PublicKey, - ) -> Self::Output { - Self::KeyDerivationFunction::derive(Self::agree_owned(secret_key, public_key)) - } -} - -/// Constraint System Gadgets -pub mod constraint { - use crate::constraint::Variable; - - /// Key Derivation Function Gadget - pub trait KeyDerivationFunction<F> - where - F: super::KeyDerivationFunction, - { - /// Input Key Type - type Key: Variable<Self, Type = F::Key>; - - /// Output Key Type - type Output: Variable<Self, Type = F::Output>; - - /// Derives an output key from `secret` computed from a cryptographic agreement scheme. - fn derive(&mut self, secret: Self::Key) -> Self::Output; - } - - /// Key Agreement Scheme Gadget - pub trait KeyAgreementScheme<K> - where - K: super::KeyAgreementScheme, - { - /// Secret Key Type - type SecretKey: Variable<Self, Type = K::SecretKey>; - - /// Public Key Type - type PublicKey: Variable<Self, Type = K::PublicKey>; - - /// Shared Secret Type - type SharedSecret: Variable<Self, Type = K::SharedSecret>; - - /// Derives a public key corresponding to `secret_key`. - fn derive(&mut self, secret_key: &Self::SecretKey) -> Self::PublicKey; - - /// Computes the shared secret given the known `secret_key` and the given `public_key`. - fn agree( - &mut self, - secret_key: &Self::SecretKey, - public_key: &Self::PublicKey, - ) -> Self::SharedSecret; + ) -> Self::SharedSecret { + Self::agree(compiler, &secret_key, &public_key) } } diff --git a/manta-crypto/src/merkle_tree/mod.rs b/manta-crypto/src/merkle_tree/mod.rs index f5eaabb13..5cb2f8c42 100644 --- a/manta-crypto/src/merkle_tree/mod.rs +++ b/manta-crypto/src/merkle_tree/mod.rs @@ -21,6 +21,7 @@ // TODO: Maybe we should require `INNER_HEIGHT` instead of `HEIGHT` so that we don't have to rely // on the user to check that `HEIGHT >= 2`. // FIXME: Get rid of as many `pub(super)` declarations as we can. +// TODO: Extend to arbitrary arity. mod node; mod tree; diff --git a/manta-crypto/src/merkle_tree/path.rs b/manta-crypto/src/merkle_tree/path.rs index ceab12e79..77529cbf5 100644 --- a/manta-crypto/src/merkle_tree/path.rs +++ b/manta-crypto/src/merkle_tree/path.rs @@ -800,165 +800,5 @@ where } } -/* TODO: Compressed Path Implementation - -/// Compressed Path -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "LeafDigest<C>: Clone"), - Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), - Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), - Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), - PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") -)] -pub struct CompressedPath<C> -where - C: Configuration + ?Sized, -{ - /// Leaf Index - pub leaf_index: Node, - - /// Sibling Digest - pub sibling_digest: LeafDigest<C>, - - /// Inner Path - /// - /// Inner digests are stored from leaf to root, not including the root. - pub inner_path: Vec<InnerDigest<C>>, - - /// Sentinel Ranges - pub sentinel_ranges: Vec<usize>, -} - -impl<C> CompressedPath<C> -where - C: Configuration + ?Sized, -{ - /// Builds a new [`CompressedPath`] from `leaf_index`, `sibling_digest`, `inner_path`, and - /// `sentinel_ranges`. - /// - /// # Safety - /// - /// In order for paths to compute the correct root, they should always have an `inner_path` - /// with length given by [`path_length`]. - /// - /// For compressed paths, we need the `sentinel_ranges` to contain all the ranges which include - /// the default inner hash, and then `inner_path` contains the non-default values. The total - /// number of values represented in this way must equal the [`path_length`]. - #[inline] - pub fn new( - leaf_index: Node, - sibling_digest: LeafDigest<C>, - inner_path: Vec<InnerDigest<C>>, - sentinel_ranges: Vec<usize>, - ) -> Self { - Self { - leaf_index, - sibling_digest, - inner_path, - sentinel_ranges, - } - } - - /// Uncompresses a path by re-inserting the default values into [`self.inner_path`] at the - /// indices described by [`self.sentinel_ranges`]. - /// - /// [`self.inner_path`]: Self::inner_path - /// [`self.sentinel_ranges`]: Self::sentinel_ranges - #[inline] - pub fn uncompress(mut self) -> Path<C> { - let path_length = path_length::<C>(); - self.inner_path.reserve(path_length); - let mut start = 0; - for (i, index) in self.sentinel_ranges.into_iter().enumerate() { - if i % 2 == 0 { - start = index; - } else { - self.inner_path - .splice(start..start, (start..index).map(|_| Default::default())); - } - } - self.inner_path.resize_with(path_length, Default::default); - Path::new(self.leaf_index, self.sibling_digest, self.inner_path) - } -} - -impl<C> Default for CompressedPath<C> -where - C: Configuration + ?Sized, -{ - #[inline] - fn default() -> Self { - Self::new( - Default::default(), - Default::default(), - Default::default(), - vec![0, path_length::<C>()], - ) - } -} - -impl<C> From<Path<C>> for CompressedPath<C> -where - C: Configuration + ?Sized, -{ - #[inline] - fn from(path: Path<C>) -> Self { - path.compress() - } -} - -impl<C> Path<C> -where - C: Configuration + ?Sized, -{ - /// Compresses [`self.inner_path`] by removing all default values and saving only the positions - /// in the path of those default values. - /// - /// [`self.inner_path`]: Self::inner_path - #[inline] - pub fn compress(self) -> CompressedPath<C> { - let default = Default::default(); - let mut started = false; - let mut sentinel_ranges = Vec::new(); - let inner_path = self - .inner_path - .into_iter() - .enumerate() - .filter_map(|(i, d)| { - if d == default { - if !started { - sentinel_ranges.push(i); - started = true; - } - None - } else { - if started { - sentinel_ranges.push(i); - started = false; - } - Some(d) - } - }) - .collect(); - sentinel_ranges.shrink_to_fit(); - CompressedPath::new( - self.leaf_index, - self.sibling_digest, - inner_path, - sentinel_ranges, - ) - } -} - -impl<C> From<CompressedPath<C>> for Path<C> -where - C: Configuration + ?Sized, -{ - #[inline] - fn from(path: CompressedPath<C>) -> Self { - path.uncompress() - } -} - -*/ +/// Constraint System Gadgets +pub mod constraint {} diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index 2bdfa50da..b672f4df7 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -502,11 +502,13 @@ where #[inline] fn verify( + compiler: &mut (), parameters: &Self::Parameters, item: &Self::Item, witness: &Self::Witness, output: &Self::Output, ) -> Self::Verification { + let _ = compiler; parameters.verify_path(witness, output, item) } } diff --git a/manta-pay/src/accounting/mod.rs b/manta-pay/src/accounting/mod.rs index a8dfa00fe..20e38e468 100644 --- a/manta-pay/src/accounting/mod.rs +++ b/manta-pay/src/accounting/mod.rs @@ -29,8 +29,10 @@ use manta_crypto::{ commitment::CommitmentScheme, encryption, key::KeyAgreementScheme, merkle_tree, }; -pub use ark_bls12_381::Bls12_381; -pub use ark_ed_on_bls12_381::EdwardsProjective as Bls12_381_Edwards; +pub use ark_bls12_381 as bls12_381; +pub use ark_ed_on_bls12_381 as bls12_381_ed; +pub use bls12_381::Bls12_381; +pub use bls12_381_ed::EdwardsProjective as Bls12_381_Edwards; pub mod key; // TODO: pub mod ledger; @@ -38,41 +40,49 @@ pub mod key; /// Configuration Structure #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Configuration; +pub struct Config; -impl poseidon::Configuration for Configuration { - const FULL_ROUNDS: usize = 1; - const PARTIAL_ROUNDS: usize = 1; - type Field = poseidon::arkworks::Field<Bls12_381_Edwards>; +impl poseidon::Specification for Config { + const FULL_ROUNDS: usize = 10; + const PARTIAL_ROUNDS: usize = 10; + const SBOX_EXPONENT: u64 = 5; + type Field = bls12_381::Fr; } -impl transfer::Configuration for Configuration { +impl transfer::Configuration for Config { type SecretKey = <Self::KeyAgreementScheme as KeyAgreementScheme>::SecretKey; type SecretKeyVar = (); type PublicKey = <Self::KeyAgreementScheme as KeyAgreementScheme>::PublicKey; type PublicKeyVar = (); - type KeyAgreementScheme = EllipticCurveDiffieHellman<Bls12_381_Edwards>; + type KeyAgreementScheme = EllipticCurveDiffieHellman<Bls12_381>; + type EphemeralKeyTrapdoor = <Self::EphemeralKeyCommitmentScheme as CommitmentScheme>::Trapdoor; type EphemeralKeyTrapdoorVar = (); type EphemeralKeyParametersVar = (); - type EphemeralKeyCommitmentSchemeInput = - <Self::EphemeralKeyCommitmentScheme as CommitmentScheme>::Input; - type EphemeralKeyCommitmentSchemeInputVar = (); - type EphemeralKeyCommitmentScheme = poseidon::Commitment<Self, 2>; + type EphemeralKeyCommitmentScheme = + poseidon::Commitment<Self, (), 2> + poseidon::Commitment<Self, Self::Compiler, 2>; + type TrapdoorDerivationFunction = (); - type CommitmentSchemeParametersVar = (); - type CommitmentSchemeInput = <Self::CommitmentScheme as CommitmentScheme>::Input; - type CommitmentSchemeInputVar = (); - type CommitmentSchemeOutput = <Self::CommitmentScheme as CommitmentScheme>::Output; - type CommitmentScheme = pedersen::Commitment<pedersen::arkworks::Group<Bls12_381_Edwards>, 2>; + + type UtxoCommitmentParametersVar = (); + type Utxo = <Self::UtxoCommitmentScheme as CommitmentScheme>::Output; + type UtxoCommitmentScheme = pedersen::Commitment<pedersen::arkworks::Group<Bls12_381>, 2>; + + type VoidNumberCommitmentParametersVar = (); + type VoidNumber = <Self::VoidNumberCommitmentScheme as CommitmentScheme>::Output; + type VoidNumberCommitmentScheme = pedersen::Commitment<pedersen::arkworks::Group<Bls12_381>, 1>; + type UtxoSetParametersVar = (); type UtxoSetWitnessVar = (); type UtxoSetOutputVar = (); - type UtxoSetVerifier = (); + type UtxoSetVerifier = merkle_tree::Verifier; + type AssetIdVar = (); type AssetValueVar = (); - type ConstraintSystem = (); - type ProofSystem = (); + + type Compiler = ArkworksR1CS; + type ProofSystem = Groth16; + type NoteEncryptionScheme = encryption::Hybrid< Self::KeyAgreementScheme, AesGcm<Asset, { Asset::SIZE }>, diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs index 8e2d5dfd7..b88dac887 100644 --- a/manta-pay/src/crypto/commitment/pedersen.rs +++ b/manta-pay/src/crypto/commitment/pedersen.rs @@ -16,95 +16,126 @@ //! Pedersen Commitments -// TODO: Describe contract for `Group`. +// TODO: Describe contract for `Specification`. -use core::marker::PhantomData; +use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use manta_crypto::commitment::CommitmentScheme; -/// Pedersen Group -pub trait Group { +/// Pedersen Commitment Specification +pub trait Specification<J = ()> { + /// Group Type + type Group; + /// Scalar Field Type type Scalar; /// Adds two points of the group together. - fn add(lhs: Self, rhs: Self) -> Self; + fn add(compiler: &mut J, lhs: Self::Group, rhs: Self::Group) -> Self::Group; /// Multiplies the given `point` with a `scalar` value. - fn scalar_mul(point: &Self, scalar: &Self::Scalar) -> Self; + fn scalar_mul(compiler: &mut J, point: &Self::Group, scalar: &Self::Scalar) -> Self::Group; + + /// Computes the Pedersen Commitment with `parameters` over `trapdoor` and `input` in the given + /// `compiler`. + #[inline] + fn commit<const ARITY: usize>( + compiler: &mut J, + parameters: &Parameters<Self, J, ARITY>, + trapdoor: &Self::Scalar, + input: &[Self::Scalar; ARITY], + ) -> Self::Group { + parameters.input_generators.iter().zip(input).fold( + Self::scalar_mul(compiler, &parameters.trapdoor_generator, trapdoor), + move |acc, (g, i)| { + let point = Self::scalar_mul(compiler, g, i); + Self::add(compiler, acc, point) + }, + ) + } } -/// Commitment Paramters -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub struct Parameters<G, const ARITY: usize = 1> +/// Commitment Parameters +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "S::Group: Clone"), + Copy(bound = "S::Group: Copy"), + Debug(bound = "S::Group: Debug"), + Eq(bound = "S::Group: Eq"), + Hash(bound = "S::Group: Hash"), + PartialEq(bound = "S::Group: PartialEq") +)] +pub struct Parameters<S, J = (), const ARITY: usize = 1> where - G: Group, + S: Specification<J> + ?Sized, { /// Trapdoor Generator - pub trapdoor_generator: G, + pub trapdoor_generator: S::Group, /// Input Generators - pub input_generators: [G; ARITY], + pub input_generators: [S::Group; ARITY], } /// Commitment Scheme -pub struct Commitment<G, const ARITY: usize = 1>(PhantomData<G>) +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Commitment<S, J = (), const ARITY: usize = 1>(PhantomData<(J, S)>) where - G: Group; + S: Specification<J>; -impl<G, const ARITY: usize> CommitmentScheme for Commitment<G, ARITY> +impl<S, J, const ARITY: usize> CommitmentScheme<J> for Commitment<S, J, ARITY> where - G: Group, + S: Specification<J>, { - type Parameters = Parameters<G, ARITY>; + type Parameters = Parameters<S, J, ARITY>; - type Trapdoor = G::Scalar; + type Trapdoor = S::Scalar; - type Input = [G::Scalar; ARITY]; + type Input = [S::Scalar; ARITY]; - type Output = G; + type Output = S::Group; #[inline] fn commit( + compiler: &mut J, parameters: &Self::Parameters, trapdoor: &Self::Trapdoor, input: &Self::Input, ) -> Self::Output { - parameters.input_generators.iter().zip(input).fold( - G::scalar_mul(&parameters.trapdoor_generator, trapdoor), - move |acc, (g, i)| G::add(acc, G::scalar_mul(g, i)), - ) + S::commit(compiler, parameters, trapdoor, input) } } -/// Constraint System Gadgets -pub mod constraint {} - /// Arkworks Backend #[cfg(feature = "arkworks")] #[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] pub mod arkworks { + use super::*; use ark_ec::ProjectiveCurve; use ark_ff::PrimeField; - /// Pedersen Group Wrapper for a [`ProjectiveCurve`] - pub struct Group<C>(C) + /// Pedersen Commitment Specification + #[derive(derivative::Derivative)] + #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct Specification<C>(PhantomData<C>) where C: ProjectiveCurve; - impl<C> super::Group for Group<C> + impl<C> super::Specification for Specification<C> where C: ProjectiveCurve, { + type Group = C; + type Scalar = C::ScalarField; #[inline] - fn add(lhs: Self, rhs: Self) -> Self { - Self(lhs.0 + rhs.0) + fn add(_: &mut (), lhs: Self::Group, rhs: Self::Group) -> Self::Group { + lhs + rhs } #[inline] - fn scalar_mul(point: &Self, scalar: &Self::Scalar) -> Self { - Self(point.0.mul(scalar.into_repr())) + fn scalar_mul(_: &mut (), point: &Self::Group, scalar: &Self::Scalar) -> Self::Group { + point.mul(scalar.into_repr()) } } } diff --git a/manta-pay/src/crypto/commitment/poseidon.rs b/manta-pay/src/crypto/commitment/poseidon.rs index db6baaf94..474db0463 100644 --- a/manta-pay/src/crypto/commitment/poseidon.rs +++ b/manta-pay/src/crypto/commitment/poseidon.rs @@ -16,24 +16,14 @@ //! Poseidon Commitment +// TODO: Describe the contract for `Specification`. + use alloc::vec::Vec; use core::{iter, marker::PhantomData, mem}; use manta_crypto::commitment::CommitmentScheme; -/// Poseidon Field -pub trait Field { - /// Adds two field elements together. - fn add(lhs: &Self, rhs: &Self) -> Self; - - /// Multiplies two field elements together. - fn mul(lhs: &Self, rhs: &Self) -> Self; - - /// Adds the `rhs` field element to `self`, storing the value in `self`. - fn add_assign(&mut self, rhs: &Self); -} - -/// Poseidon Configuration -pub trait Configuration { +/// Poseidon Permutation Specification +pub trait Specification<J = ()> { /// Number of Full Rounds /// /// This is counted twice for the first set of full rounds and then the second set after the @@ -44,51 +34,60 @@ pub trait Configuration { const PARTIAL_ROUNDS: usize; /// Field Type - type Field: Field; + type Field; + + /// Adds two field elements together. + fn add(compiler: &mut J, lhs: &Self::Field, rhs: &Self::Field) -> Self::Field; + + /// Multiplies two field elements together. + fn mul(compiler: &mut J, lhs: &Self::Field, rhs: &Self::Field) -> Self::Field; + + /// Adds the `rhs` field element to `self`, storing the value in `self`. + fn add_assign(compiler: &mut J, lhs: &mut Self::Field, rhs: &Self::Field); /// Applies the S-BOX to `point`. - fn apply_sbox(point: &mut Self::Field); + fn apply_sbox(compiler: &mut J, point: &mut Self::Field); } /// Internal State Vector -type State<C> = Vec<<C as Configuration>::Field>; +type State<S, J> = Vec<<S as Specification<J>>::Field>; /// Returns the total number of rounds in a Poseidon permutation. #[inline] -pub fn rounds<C>() -> usize +pub fn rounds<S, J>() -> usize where - C: Configuration, + S: Specification<J>, { - 2 * C::FULL_ROUNDS + C::PARTIAL_ROUNDS + 2 * S::FULL_ROUNDS + S::PARTIAL_ROUNDS } /// Poseidon Permutation Parameters -pub struct Parameters<C, const ARITY: usize> +pub struct Parameters<S, J = (), const ARITY: usize = 1> where - C: Configuration, + S: Specification<J>, { /// Additive Round Keys - additive_round_keys: Vec<C::Field>, + additive_round_keys: Vec<S::Field>, /// MDS Matrix - mds_matrix: Vec<C::Field>, + mds_matrix: Vec<S::Field>, } -impl<C, const ARITY: usize> Parameters<C, ARITY> +impl<S, J, const ARITY: usize> Parameters<S, J, ARITY> where - C: Configuration, + S: Specification<J>, { /// Builds a new [`Parameters`] form `additive_round_keys` and `mds_matrix`. /// /// # Panics /// /// This method panics if the input vectors are not the correct size for the specified - /// [`Configuration`]. + /// [`Specification`]. #[inline] - pub fn new(additive_round_keys: Vec<C::Field>, mds_matrix: Vec<C::Field>) -> Self { + pub fn new(additive_round_keys: Vec<S::Field>, mds_matrix: Vec<S::Field>) -> Self { assert_eq!( additive_round_keys.len(), - rounds::<C>() * (ARITY + 1), + rounds::<S, J>() * (ARITY + 1), "Additive Rounds Keys are not the correct size." ); assert_eq!( @@ -104,7 +103,7 @@ where /// Returns the additive keys for the given `round`. #[inline] - fn additive_keys(&self, round: usize) -> &[C::Field] { + fn additive_keys(&self, round: usize) -> &[S::Field] { let width = ARITY + 1; let start = round * width; &self.additive_round_keys[start..start + width] @@ -112,16 +111,20 @@ where /// Computes the MDS matrix multiplication against the `state`. #[inline] - fn mds_matrix_multiply(&self, state: &mut State<C>) { + fn mds_matrix_multiply(&self, state: &mut State<S, J>, compiler: &mut J) { let width = ARITY + 1; let mut next = Vec::with_capacity(width); for i in 0..width { + #[allow(clippy::needless_collect)] // NOTE: Clippy is wrong here, we need `&mut` access. + let linear_combination = state + .iter() + .enumerate() + .map(|(j, elem)| S::mul(compiler, elem, &self.mds_matrix[width * i + j])) + .collect::<Vec<_>>(); next.push( - state - .iter() - .enumerate() - .map(|(j, elem)| C::Field::mul(elem, &self.mds_matrix[width * i + j])) - .reduce(|acc, next| C::Field::add(&acc, &next)) + linear_combination + .into_iter() + .reduce(|acc, next| S::add(compiler, &acc, &next)) .unwrap(), ); } @@ -130,111 +133,137 @@ where /// Computes the first round of the Poseidon permutation from `trapdoor` and `input`. #[inline] - fn first_round(&self, trapdoor: &C::Field, input: &[C::Field; ARITY]) -> State<C> { + fn first_round( + &self, + trapdoor: &S::Field, + input: &[S::Field; ARITY], + compiler: &mut J, + ) -> State<S, J> { let mut state = Vec::with_capacity(ARITY + 1); for (i, point) in iter::once(trapdoor).chain(input).enumerate() { - let mut elem = C::Field::add(point, &self.additive_round_keys[i]); - C::apply_sbox(&mut elem); + let mut elem = S::add(compiler, point, &self.additive_round_keys[i]); + S::apply_sbox(compiler, &mut elem); state.push(elem); } - self.mds_matrix_multiply(&mut state); + self.mds_matrix_multiply(&mut state, compiler); state } /// Computes a full round at the given `round` index on the internal permutation `state`. #[inline] - fn full_round(&self, round: usize, state: &mut State<C>) { + fn full_round(&self, round: usize, state: &mut State<S, J>, compiler: &mut J) { let keys = self.additive_keys(round); for (i, elem) in state.iter_mut().enumerate() { - C::Field::add_assign(elem, &keys[i]); - C::apply_sbox(elem); + S::add_assign(compiler, elem, &keys[i]); + S::apply_sbox(compiler, elem); } - self.mds_matrix_multiply(state); + self.mds_matrix_multiply(state, compiler); } /// Computes a partial round at the given `round` index on the internal permutation `state`. #[inline] - fn partial_round(&self, round: usize, state: &mut State<C>) { + fn partial_round(&self, round: usize, state: &mut State<S, J>, compiler: &mut J) { let keys = self.additive_keys(round); for (i, elem) in state.iter_mut().enumerate() { - C::Field::add_assign(elem, &keys[i]); + S::add_assign(compiler, elem, &keys[i]); } - C::apply_sbox(&mut state[0]); - self.mds_matrix_multiply(state); + S::apply_sbox(compiler, &mut state[0]); + self.mds_matrix_multiply(state, compiler); } } /// Poseidon Commitment Scheme -pub struct Commitment<C, const ARITY: usize>(PhantomData<C>) +pub struct Commitment<S, J = (), const ARITY: usize = 1>(PhantomData<(S, J)>) where - C: Configuration; + S: Specification<J>; -impl<C, const ARITY: usize> CommitmentScheme for Commitment<C, ARITY> +impl<S, J, const ARITY: usize> CommitmentScheme<J> for Commitment<S, J, ARITY> where - C: Configuration, + S: Specification<J>, { - type Parameters = Parameters<C, ARITY>; + type Parameters = Parameters<S, J, ARITY>; - type Trapdoor = C::Field; + type Trapdoor = S::Field; - type Input = [C::Field; ARITY]; + type Input = [S::Field; ARITY]; - type Output = C::Field; + type Output = S::Field; #[inline] fn commit( + compiler: &mut J, parameters: &Self::Parameters, trapdoor: &Self::Trapdoor, input: &Self::Input, ) -> Self::Output { - let mut state = parameters.first_round(trapdoor, input); - for round in 1..C::FULL_ROUNDS { - parameters.full_round(round, &mut state); + let mut state = parameters.first_round(trapdoor, input, compiler); + for round in 1..S::FULL_ROUNDS { + parameters.full_round(round, &mut state, compiler); } - for round in C::FULL_ROUNDS..(C::FULL_ROUNDS + C::PARTIAL_ROUNDS) { - parameters.partial_round(round, &mut state); + for round in S::FULL_ROUNDS..(S::FULL_ROUNDS + S::PARTIAL_ROUNDS) { + parameters.partial_round(round, &mut state, compiler); } - for round in (C::FULL_ROUNDS + C::PARTIAL_ROUNDS)..(2 * C::FULL_ROUNDS + C::PARTIAL_ROUNDS) + for round in (S::FULL_ROUNDS + S::PARTIAL_ROUNDS)..(2 * S::FULL_ROUNDS + S::PARTIAL_ROUNDS) { - parameters.full_round(round, &mut state); + parameters.full_round(round, &mut state, compiler); } state.truncate(1); state.remove(0) } } -/// Constraint System Gadgets -pub mod constraint {} - /// Arkworks Backend #[cfg(feature = "arkworks")] #[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] pub mod arkworks { - use ark_ec::ProjectiveCurve; - use ark_ff::PrimeField; + use ark_ff::Field; - /// Poseidon Field Wrapper for a [`ProjectiveCurve`] - pub struct Field<C>(C::ScalarField) - where - C: ProjectiveCurve; + /// Poseidon Permutation Specification + pub trait Specification { + /// Number of Full Rounds + /// + /// This is counted twice for the first set of full rounds and then the second set after the + /// partial rounds. + const FULL_ROUNDS: usize; + + /// Number of Partial Rounds + const PARTIAL_ROUNDS: usize; + + /// S-BOX Exponenet + const SBOX_EXPONENT: u64; + + /// Field Type + type Field: Field; + } - impl<C> super::Field for Field<C> + impl<S> super::Specification for S where - C: ProjectiveCurve, + S: Specification, { + const FULL_ROUNDS: usize = S::FULL_ROUNDS; + + const PARTIAL_ROUNDS: usize = S::PARTIAL_ROUNDS; + + type Field = S::Field; + + #[inline] + fn add(_: &mut (), lhs: &Self::Field, rhs: &Self::Field) -> Self::Field { + *lhs + *rhs + } + #[inline] - fn add(lhs: &Self, rhs: &Self) -> Self { - Self(lhs.0 + rhs.0) + fn mul(_: &mut (), lhs: &Self::Field, rhs: &Self::Field) -> Self::Field { + *lhs * *rhs } #[inline] - fn mul(lhs: &Self, rhs: &Self) -> Self { - Self(lhs.0 * rhs.0) + fn add_assign(_: &mut (), lhs: &mut Self::Field, rhs: &Self::Field) { + *lhs += rhs; } #[inline] - fn add_assign(&mut self, rhs: &Self) { - self.0 += rhs.0; + fn apply_sbox(_: &mut (), point: &mut Self::Field) { + *point = point.pow(&[Self::SBOX_EXPONENT, 0, 0, 0]); } } } diff --git a/manta-pay/src/crypto/encryption.rs b/manta-pay/src/crypto/encryption.rs index f86e26f6c..32d3144e8 100644 --- a/manta-pay/src/crypto/encryption.rs +++ b/manta-pay/src/crypto/encryption.rs @@ -23,7 +23,6 @@ use aes_gcm::{ aead::{Aead, NewAead}, Aes256Gcm, Nonce, }; -use alloc::vec::Vec; use core::marker::PhantomData; use generic_array::GenericArray; use manta_crypto::encryption::SymmetricKeyEncryptionScheme; diff --git a/manta-pay/src/crypto/key.rs b/manta-pay/src/crypto/key.rs index f184b0948..bb1513602 100644 --- a/manta-pay/src/crypto/key.rs +++ b/manta-pay/src/crypto/key.rs @@ -41,7 +41,8 @@ where type Output = [u8; 32]; #[inline] - fn derive(secret: Self::Key) -> Self::Output { + fn derive(compiler: &mut (), secret: Self::Key) -> Self::Output { + let _ = compiler; let mut hasher = Blake2s::new(); hasher.update(secret.as_ref()); hasher.update(b"manta kdf instantiated with blake2s hash function"); @@ -68,25 +69,32 @@ where type SharedSecret = C; #[inline] - fn derive(secret_key: &Self::SecretKey) -> Self::PublicKey { - Self::derive_owned(*secret_key) + fn derive(compiler: &mut (), secret_key: &Self::SecretKey) -> Self::PublicKey { + Self::derive_owned(compiler, *secret_key) } #[inline] - fn derive_owned(secret_key: Self::SecretKey) -> Self::PublicKey { + fn derive_owned(compiler: &mut (), secret_key: Self::SecretKey) -> Self::PublicKey { + let _ = compiler; C::Affine::prime_subgroup_generator().mul(secret_key) } #[inline] - fn agree(secret_key: &Self::SecretKey, public_key: &Self::PublicKey) -> Self::SharedSecret { - Self::agree_owned(*secret_key, *public_key) + fn agree( + compiler: &mut (), + secret_key: &Self::SecretKey, + public_key: &Self::PublicKey, + ) -> Self::SharedSecret { + Self::agree_owned(compiler, *secret_key, *public_key) } #[inline] fn agree_owned( + compiler: &mut (), secret_key: Self::SecretKey, mut public_key: Self::PublicKey, ) -> Self::SharedSecret { + let _ = compiler; public_key *= secret_key; public_key } diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index e7dcc9f1d..39bb223a7 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -25,6 +25,8 @@ extern crate alloc; pub mod crypto; +/* #[cfg(all(feature = "arkworks", feature = "groth16"))] #[cfg_attr(doc_cfg, doc(cfg(all(feature = "arkworks", feature = "groth16"))))] pub mod accounting; +*/ From 30cb283cb276fb9de4c5d9c48ff50f7b55f648db Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 28 Dec 2021 18:44:41 -0500 Subject: [PATCH 146/275] wip: implement concrete protocol --- manta-pay/src/accounting/mod.rs | 201 -------- manta-pay/src/accounting/transfer.rs | 4 +- manta-pay/src/config.rs | 443 ++++++++++++++++++ manta-pay/src/crypto/commitment/pedersen.rs | 11 + manta-pay/src/crypto/commitment/poseidon.rs | 27 +- .../constraint/arkworks/constraint_system.rs | 356 ++------------ .../src/crypto/constraint/arkworks/mod.rs | 4 +- .../groth16.rs => proof_system.rs} | 105 +---- .../constraint/arkworks/proof_systems/mod.rs | 21 - manta-pay/src/crypto/constraint/mod.rs | 10 +- .../proof_systems/mod.rs => plonk.rs} | 2 +- .../constraint/zk_garage/constraint_system.rs | 17 - .../src/crypto/constraint/zk_garage/mod.rs | 23 - manta-pay/src/crypto/key.rs | 65 ++- manta-pay/src/crypto/mod.rs | 2 +- manta-pay/src/{accounting => }/key.rs | 0 manta-pay/src/lib.rs | 7 +- 17 files changed, 605 insertions(+), 693 deletions(-) delete mode 100644 manta-pay/src/accounting/mod.rs create mode 100644 manta-pay/src/config.rs rename manta-pay/src/crypto/constraint/arkworks/{proof_systems/groth16.rs => proof_system.rs} (60%) delete mode 100644 manta-pay/src/crypto/constraint/arkworks/proof_systems/mod.rs rename manta-pay/src/crypto/constraint/{zk_garage/proof_systems/mod.rs => plonk.rs} (92%) delete mode 100644 manta-pay/src/crypto/constraint/zk_garage/constraint_system.rs delete mode 100644 manta-pay/src/crypto/constraint/zk_garage/mod.rs rename manta-pay/src/{accounting => }/key.rs (100%) diff --git a/manta-pay/src/accounting/mod.rs b/manta-pay/src/accounting/mod.rs deleted file mode 100644 index 20e38e468..000000000 --- a/manta-pay/src/accounting/mod.rs +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Accounting Implementations - -// TODO: Make this generic over the backend we use. Automatically compute which features are -// enabled when using whichever backend. - -use crate::crypto::{ - commitment::{pedersen, poseidon}, - encryption::AesGcm, - key::{Blake2sKdf, EllipticCurveDiffieHellman}, -}; -use manta_accounting::{asset::Asset, transfer}; -use manta_crypto::{ - commitment::CommitmentScheme, encryption, key::KeyAgreementScheme, merkle_tree, -}; - -pub use ark_bls12_381 as bls12_381; -pub use ark_ed_on_bls12_381 as bls12_381_ed; -pub use bls12_381::Bls12_381; -pub use bls12_381_ed::EdwardsProjective as Bls12_381_Edwards; - -pub mod key; -// TODO: pub mod ledger; -// TODO: pub mod transfer; - -/// Configuration Structure -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Config; - -impl poseidon::Specification for Config { - const FULL_ROUNDS: usize = 10; - const PARTIAL_ROUNDS: usize = 10; - const SBOX_EXPONENT: u64 = 5; - type Field = bls12_381::Fr; -} - -impl transfer::Configuration for Config { - type SecretKey = <Self::KeyAgreementScheme as KeyAgreementScheme>::SecretKey; - type SecretKeyVar = (); - type PublicKey = <Self::KeyAgreementScheme as KeyAgreementScheme>::PublicKey; - type PublicKeyVar = (); - type KeyAgreementScheme = EllipticCurveDiffieHellman<Bls12_381>; - - type EphemeralKeyTrapdoor = <Self::EphemeralKeyCommitmentScheme as CommitmentScheme>::Trapdoor; - type EphemeralKeyTrapdoorVar = (); - type EphemeralKeyParametersVar = (); - type EphemeralKeyCommitmentScheme = - poseidon::Commitment<Self, (), 2> + poseidon::Commitment<Self, Self::Compiler, 2>; - - type TrapdoorDerivationFunction = (); - - type UtxoCommitmentParametersVar = (); - type Utxo = <Self::UtxoCommitmentScheme as CommitmentScheme>::Output; - type UtxoCommitmentScheme = pedersen::Commitment<pedersen::arkworks::Group<Bls12_381>, 2>; - - type VoidNumberCommitmentParametersVar = (); - type VoidNumber = <Self::VoidNumberCommitmentScheme as CommitmentScheme>::Output; - type VoidNumberCommitmentScheme = pedersen::Commitment<pedersen::arkworks::Group<Bls12_381>, 1>; - - type UtxoSetParametersVar = (); - type UtxoSetWitnessVar = (); - type UtxoSetOutputVar = (); - type UtxoSetVerifier = merkle_tree::Verifier; - - type AssetIdVar = (); - type AssetValueVar = (); - - type Compiler = ArkworksR1CS; - type ProofSystem = Groth16; - - type NoteEncryptionScheme = encryption::Hybrid< - Self::KeyAgreementScheme, - AesGcm<Asset, { Asset::SIZE }>, - Blake2sKdf<<Self::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret>, - >; -} - -/* TODO: -/// Pedersen Window Parameters -#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct PedersenCommitmentWindowParameters; - -impl PedersenWindow for PedersenCommitmentWindowParameters { - const WINDOW_SIZE: usize = 4; - const NUM_WINDOWS: usize = 256; -} - -/// Pedersen Commitment Projective Curve -pub type PedersenCommitmentProjectiveCurve = EdwardsProjective; - -/// Pedersen Commitment Projective Curve -pub type PedersenCommitmentProjectiveCurveVar = EdwardsVar; - -/// Pedersen Commitment Scheme -pub type PedersenCommitment = pedersen::constraint::PedersenCommitmentWrapper< - PedersenCommitmentWindowParameters, - PedersenCommitmentProjectiveCurve, - PedersenCommitmentProjectiveCurveVar, ->; - -/// Pedersen Commitment Scheme Variable -pub type PedersenCommitmentVar = pedersen::constraint::PedersenCommitmentVar< - PedersenCommitmentWindowParameters, - PedersenCommitmentProjectiveCurve, - PedersenCommitmentProjectiveCurveVar, ->; - -/// Arkworks Pedersen Commitment Scheme -type ArkPedersenCommitment = - CRH<PedersenCommitmentProjectiveCurve, PedersenCommitmentWindowParameters>; - -/// Constraint Field -pub type ConstraintField = Fq; - -/// Constraint System -pub type ConstraintSystem = ArkConstraintSystem<ConstraintField>; - -/// Proof System -pub type ProofSystem = Groth16<Bls12_381>; - -impl ArkMerkleTreeConfiguration for Configuration { - type Leaf = Utxo; - type LeafHash = ArkPedersenCommitment; - type InnerHash = ArkPedersenCommitment; - type Height = u8; - - const HEIGHT: Self::Height = 20; -} - -impl merkle_tree::HashConfiguration for Configuration { - type LeafHash = - <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::HashConfiguration>::LeafHash; - type InnerHash = - <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::HashConfiguration>::InnerHash; -} - -impl merkle_tree::Configuration for Configuration { - type Height = - <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::Configuration>::Height; - - const HEIGHT: Self::Height = - <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::Configuration>::HEIGHT; -} - -impl merkle_tree_constraint::Configuration for Configuration { - type ConstraintField = ConstraintField; - type LeafHashVar = CRHGadget< - PedersenCommitmentProjectiveCurve, - PedersenCommitmentProjectiveCurveVar, - PedersenCommitmentWindowParameters, - >; - type InnerHashVar = CRHGadget< - PedersenCommitmentProjectiveCurve, - PedersenCommitmentProjectiveCurveVar, - PedersenCommitmentWindowParameters, - >; -} - -impl identity::Configuration for Configuration { - type Asset = Asset; - type KeyAgreementScheme = EllipticCurveDiffieHellman<PedersenCommitmentProjectiveCurve>; - type CommitmentScheme = PedersenCommitment; -} - -/* -/// Transfer Constraint Configuration Structure -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct TransferConstraintConfiguration; - -impl identity::Configuration for TransferConstraintConfiguration { - type Asset = AssetVar; - type KeyAgreementScheme = (); - type CommitmentScheme = (); -} - -impl transfer::ConstraintConfiguration<ConstraintSystem> for TransferConstraintConfiguration {} - -impl transfer::Configuration for Configuration { - type EncryptionScheme = (); - type UtxoSetVerifier = (); - type ConstraintSystem = ConstraintSystem; - type ConstraintConfiguration = TransferConstraintConfiguration; - type ProofSystem = ProofSystem; -} -*/ -*/ diff --git a/manta-pay/src/accounting/transfer.rs b/manta-pay/src/accounting/transfer.rs index abc334240..71a09dd84 100644 --- a/manta-pay/src/accounting/transfer.rs +++ b/manta-pay/src/accounting/transfer.rs @@ -24,10 +24,10 @@ use manta_accounting::{ use manta_crypto::merkle_tree::{self, full::Full}; /// Unspent Transaction Output -pub type Utxo = identity::Utxo<Configuration>; +pub type Utxo = transfer::Utxo<Configuration>; /// Void Number -pub type VoidNumber = identity::VoidNumber<Configuration>; +pub type VoidNumber = transfer::VoidNumber<Configuration>; /// UTXO Set Parameters pub type Parameters = merkle_tree::Parameters<ConfigConverter<Configuration>>; diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs new file mode 100644 index 000000000..a49da5b06 --- /dev/null +++ b/manta-pay/src/config.rs @@ -0,0 +1,443 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Manta-Pay Configuration + +use crate::crypto::{ + commitment::{pedersen, poseidon}, + constraint::arkworks::{FpVar, Groth16, R1CS}, + encryption::AesGcm, + key::{Blake2sKdf, EllipticCurveDiffieHellman}, +}; +use manta_accounting::{asset::Asset, transfer}; +use manta_crypto::{ + accumulator, + commitment::CommitmentScheme, + encryption, + key::{KeyAgreementScheme, KeyDerivationFunction}, + merkle_tree, +}; + +#[doc(inline)] +pub use ark_bls12_381 as bls12_381; +#[doc(inline)] +pub use ark_ed_on_bls12_381 as bls12_381_ed; + +/// BLS12-381 Pairing Engine +pub use bls12_381::Bls12_381; + +/// +pub use bls12_381_ed::EdwardsProjective as Bls12_381_Edwards; + +/// +pub use bls12_381_ed::constraints::EdwardsVar as Bls12_381_EdwardsVar; + +/// Constraint Field +pub type ConstraintField = bls12_381::Fr; + +/// +pub type KeyAgreement = EllipticCurveDiffieHellman<Bls12_381_Edwards, Bls12_381_EdwardsVar>; + +/// Constraint Compiler +pub type Compiler = R1CS<ConstraintField>; + +/// Proof System +pub type ProofSystem = Groth16<Bls12_381>; + +/// +pub struct PoseidonSpec<const ARITY: usize>; + +impl poseidon::arkworks::Specification for PoseidonSpec<2> { + type Field = ConstraintField; + const FULL_ROUNDS: usize = 10; + const PARTIAL_ROUNDS: usize = 10; + const SBOX_EXPONENT: u64 = 5; +} + +/// +pub type PedersenSpec = pedersen::arkworks::Specification<Bls12_381_Edwards>; + +/// +pub struct EphemeralKeyCommitmentScheme; + +impl CommitmentScheme for EphemeralKeyCommitmentScheme { + type Parameters = poseidon::Parameters<PoseidonSpec<2>, (), 2>; + + type Trapdoor = poseidon::Trapdoor<PoseidonSpec<2>, (), 2>; + + type Input = Asset; + + type Output = poseidon::Output<PoseidonSpec<2>, (), 2>; + + #[inline] + fn commit( + compiler: &mut (), + parameters: &Self::Parameters, + trapdoor: &Self::Trapdoor, + input: &Self::Input, + ) -> Self::Output { + poseidon::Commitment::<PoseidonSpec<2>, (), 2>::commit( + compiler, + parameters, + trapdoor, + &[input.id.0.into(), input.value.0.into()], + ) + } +} + +/* +impl CommitmentScheme<Compiler> for EphemeralKeyCommitmentScheme { + type Parameters = poseidon::Parameters<Self, Compiler, 2>; + + type Trapdoor = poseidon::Trapdoor<Self, Compiler, 2>; + + type Input = Asset<FpVar<Compiler>, FpVar<Compiler>>; + + type Output = poseidon::Output<Self, Compiler, 2>; + + #[inline] + fn commit( + compiler: &mut Compiler, + parameters: &Self::Parameters, + trapdoor: &Self::Trapdoor, + input: &Self::Input, + ) -> Self::Output { + poseidon::Commitment::<Self, Compiler, 2>::commit( + compiler, + parameters, + trapdoor, + &[input.id.0.into(), input.value.0.into()], + ) + } +} +*/ + +/// +pub struct TrapdoorDerivationFunction; + +impl KeyDerivationFunction for TrapdoorDerivationFunction { + type Key = (); + + type Output = (); + + #[inline] + fn derive(compiler: &mut (), secret: Self::Key) -> Self::Output { + /* TODO: + poseidon::Commitment::<PoseidonSpec<2>, (), 2>::commit( + compiler, + parameters, + Default::default(), + &[secret.x.into(), secret.y.into()], + ) + */ + todo!() + } +} + +impl KeyDerivationFunction<Compiler> for TrapdoorDerivationFunction { + type Key = (); + + type Output = (); + + #[inline] + fn derive(compiler: &mut Compiler, secret: Self::Key) -> Self::Output { + todo!() + } +} + +/// +pub struct UtxoCommitmentScheme; + +impl CommitmentScheme for UtxoCommitmentScheme { + type Parameters = pedersen::Parameters<PedersenSpec, (), 2>; + type Trapdoor = pedersen::Trapdoor<PedersenSpec, (), 2>; + type Input = Asset; + type Output = pedersen::Output<PedersenSpec, (), 2>; + + #[inline] + fn commit( + compiler: &mut (), + parameters: &Self::Parameters, + trapdoor: &Self::Trapdoor, + input: &Self::Input, + ) -> Self::Output { + pedersen::Commitment::<PedersenSpec, (), 2>::commit( + compiler, + parameters, + trapdoor, + &[input.id.0.into(), input.value.0.into()], + ) + } +} + +/* +impl CommitmentScheme<Compiler> for UtxoCommitmentScheme {} +*/ + +/// +pub struct VoidNumberCommitmentScheme; + +impl CommitmentScheme for VoidNumberCommitmentScheme { + type Parameters = pedersen::Parameters<PedersenSpec, (), 1>; + type Trapdoor = pedersen::Trapdoor<PedersenSpec, (), 2>; + type Input = <KeyAgreement as KeyAgreementScheme>::SecretKey; + type Output = pedersen::Output<PedersenSpec, (), 1>; + + #[inline] + fn commit( + compiler: &mut (), + parameters: &Self::Parameters, + trapdoor: &Self::Trapdoor, + input: &Self::Input, + ) -> Self::Output { + pedersen::Commitment::<PedersenSpec, (), 1>::commit( + compiler, + parameters, + trapdoor, + core::array::from_ref(input), + ) + } +} + +/* +impl CommitmentScheme<Compiler> for VoidNumberCommitmentScheme {} +*/ + +/// +pub struct UtxoSetVerifier; + +/* +impl accumulator::Verifier for UtxoSetVerifier {} + +impl accumulator::Verifier<Compiler> for UtxoSetVerifier {} +*/ + +/// Configuration Structure +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Config; + +/* +impl transfer::Configuration for Config { + type SecretKey = <Self::KeyAgreementScheme as KeyAgreementScheme>::SecretKey; + type SecretKeyVar = <Self::KeyAgreementScheme as KeyAgreementScheme<Self::Compiler>>::SecretKey; + type PublicKey = <Self::KeyAgreementScheme as KeyAgreementScheme>::PublicKey; + type PublicKeyVar = <Self::KeyAgreementScheme as KeyAgreementScheme<Self::Compiler>>::PublicKey; + type KeyAgreementScheme = KeyAgreement; + + type EphemeralKeyTrapdoor = <Self::EphemeralKeyCommitmentScheme as CommitmentScheme>::Trapdoor; + type EphemeralKeyTrapdoorVar = + <Self::EphemeralKeyCommitmentScheme as CommitmentScheme<Self::Compiler>>::Trapdoor; + type EphemeralKeyParametersVar = + <Self::EphemeralKeyCommitmentScheme as CommitmentScheme<Self::Compiler>>::Parameters; + type EphemeralKeyCommitmentScheme = EphemeralKeyCommitmentScheme; + + type TrapdoorDerivationFunction = TrapdoorDerivationFunction; + + type UtxoCommitmentParametersVar = + <Self::UtxoCommitmentScheme as CommitmentScheme<Self::Compiler>>::Parameters; + type Utxo = <Self::UtxoCommitmentScheme as CommitmentScheme>::Output; + type UtxoCommitmentScheme = UtxoCommitmentScheme; + + type VoidNumberCommitmentParametersVar = + <Self::VoidNumberCommitmentScheme as CommitmentScheme<Self::Compiler>>::Parameters; + type VoidNumber = <Self::VoidNumberCommitmentScheme as CommitmentScheme>::Output; + type VoidNumberCommitmentScheme = VoidNumberCommitmentScheme; + + type UtxoSetParametersVar = + <Self::UtxoSetVerifier as accumulator::Verifier<Self::Compiler>>::Parameters; + type UtxoSetWitnessVar = + <Self::UtxoSetVerifier as accumulator::Verifier<Self::Compiler>>::Witness; + type UtxoSetOutputVar = + <Self::UtxoSetVerifier as accumulator::Verifier<Self::Compiler>>::Output; + type UtxoSetVerifier = UtxoSetVerifier; + + type AssetIdVar = FpVar<ConstraintField>; + type AssetValueVar = FpVar<ConstraintField>; + + type Compiler = Compiler; + type ProofSystem = ProofSystem; + + type NoteEncryptionScheme = encryption::Hybrid< + Self::KeyAgreementScheme, + AesGcm<Asset, { Asset::SIZE }>, + Blake2sKdf<<Self::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret>, + >; +} +*/ + +/* TODO: +impl<E> Input<AssetId> for Groth16<E> +where + E: PairingEngine, +{ + #[inline] + fn extend(input: &mut Self::Input, next: &AssetId) { + input.push(next.0.into()); + input.append(&mut next.into_bytes().to_field_elements().unwrap()); + } +} + +impl<E> Input<AssetValue> for Groth16<E> +where + E: PairingEngine, +{ + #[inline] + fn extend(input: &mut Self::Input, next: &AssetValue) { + input.push(next.0.into()); + input.append(&mut next.into_bytes().to_field_elements().unwrap()); + } +} + +impl<E> Input<VoidNumber> for Groth16<E> +where + E: PairingEngine, +{ + #[inline] + fn extend(input: &mut Self::Input, next: &VoidNumber) { + next.extend_input(input); + } +} + +impl<E> Input<Root> for Groth16<E> +where + E: PairingEngine<Fr = ark_ff::Fp256<ark_bls12_381::FrParameters>>, +{ + #[inline] + fn extend(input: &mut Self::Input, next: &Root) { + root_extend_input(next, input); + } +} + +impl<E> Input<Utxo> for Groth16<E> +where + E: PairingEngine, +{ + #[inline] + fn extend(input: &mut Self::Input, next: &Utxo) { + next.extend_input(input); + } +} +*/ + +/* TODO: +/// Pedersen Window Parameters +#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct PedersenCommitmentWindowParameters; + +impl PedersenWindow for PedersenCommitmentWindowParameters { + const WINDOW_SIZE: usize = 4; + const NUM_WINDOWS: usize = 256; +} + +/// Pedersen Commitment Projective Curve +pub type PedersenCommitmentProjectiveCurve = EdwardsProjective; + +/// Pedersen Commitment Projective Curve +pub type PedersenCommitmentProjectiveCurveVar = EdwardsVar; + +/// Pedersen Commitment Scheme +pub type PedersenCommitment = pedersen::constraint::PedersenCommitmentWrapper< + PedersenCommitmentWindowParameters, + PedersenCommitmentProjectiveCurve, + PedersenCommitmentProjectiveCurveVar, +>; + +/// Pedersen Commitment Scheme Variable +pub type PedersenCommitmentVar = pedersen::constraint::PedersenCommitmentVar< + PedersenCommitmentWindowParameters, + PedersenCommitmentProjectiveCurve, + PedersenCommitmentProjectiveCurveVar, +>; + +/// Arkworks Pedersen Commitment Scheme +type ArkPedersenCommitment = + CRH<PedersenCommitmentProjectiveCurve, PedersenCommitmentWindowParameters>; + +/// Constraint Field +pub type ConstraintField = Fq; + +/// Constraint System +pub type ConstraintSystem = ArkConstraintSystem<ConstraintField>; + +/// Proof System +pub type ProofSystem = Groth16<Bls12_381>; + +impl ArkMerkleTreeConfiguration for Configuration { + type Leaf = Utxo; + type LeafHash = ArkPedersenCommitment; + type InnerHash = ArkPedersenCommitment; + type Height = u8; + + const HEIGHT: Self::Height = 20; +} + +impl merkle_tree::HashConfiguration for Configuration { + type LeafHash = + <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::HashConfiguration>::LeafHash; + type InnerHash = + <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::HashConfiguration>::InnerHash; +} + +impl merkle_tree::Configuration for Configuration { + type Height = + <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::Configuration>::Height; + + const HEIGHT: Self::Height = + <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::Configuration>::HEIGHT; +} + +impl merkle_tree_constraint::Configuration for Configuration { + type ConstraintField = ConstraintField; + type LeafHashVar = CRHGadget< + PedersenCommitmentProjectiveCurve, + PedersenCommitmentProjectiveCurveVar, + PedersenCommitmentWindowParameters, + >; + type InnerHashVar = CRHGadget< + PedersenCommitmentProjectiveCurve, + PedersenCommitmentProjectiveCurveVar, + PedersenCommitmentWindowParameters, + >; +} + +impl identity::Configuration for Configuration { + type Asset = Asset; + type KeyAgreementScheme = EllipticCurveDiffieHellman<PedersenCommitmentProjectiveCurve>; + type CommitmentScheme = PedersenCommitment; +} + +/* +/// Transfer Constraint Configuration Structure +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct TransferConstraintConfiguration; + +impl identity::Configuration for TransferConstraintConfiguration { + type Asset = AssetVar; + type KeyAgreementScheme = (); + type CommitmentScheme = (); +} + +impl transfer::ConstraintConfiguration<ConstraintSystem> for TransferConstraintConfiguration {} + +impl transfer::Configuration for Configuration { + type EncryptionScheme = (); + type UtxoSetVerifier = (); + type ConstraintSystem = ConstraintSystem; + type ConstraintConfiguration = TransferConstraintConfiguration; + type ProofSystem = ProofSystem; +} +*/ +*/ diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs index b88dac887..aef2e011a 100644 --- a/manta-pay/src/crypto/commitment/pedersen.rs +++ b/manta-pay/src/crypto/commitment/pedersen.rs @@ -105,6 +105,17 @@ where } } +/// Pedersen Commitment Trapdoor Type +pub type Trapdoor<S, J, const ARITY: usize> = + <Commitment<S, J, ARITY> as CommitmentScheme<J>>::Trapdoor; + +/// Pedersen Commitment Input Type +pub type Input<S, J, const ARITY: usize> = <Commitment<S, J, ARITY> as CommitmentScheme<J>>::Input; + +/// Pedersen Commitment Output Type +pub type Output<S, J, const ARITY: usize> = + <Commitment<S, J, ARITY> as CommitmentScheme<J>>::Output; + /// Arkworks Backend #[cfg(feature = "arkworks")] #[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] diff --git a/manta-pay/src/crypto/commitment/poseidon.rs b/manta-pay/src/crypto/commitment/poseidon.rs index 474db0463..e4a903416 100644 --- a/manta-pay/src/crypto/commitment/poseidon.rs +++ b/manta-pay/src/crypto/commitment/poseidon.rs @@ -24,6 +24,9 @@ use manta_crypto::commitment::CommitmentScheme; /// Poseidon Permutation Specification pub trait Specification<J = ()> { + /// Field Type + type Field; + /// Number of Full Rounds /// /// This is counted twice for the first set of full rounds and then the second set after the @@ -33,9 +36,6 @@ pub trait Specification<J = ()> { /// Number of Partial Rounds const PARTIAL_ROUNDS: usize; - /// Field Type - type Field; - /// Adds two field elements together. fn add(compiler: &mut J, lhs: &Self::Field, rhs: &Self::Field) -> Self::Field; @@ -212,6 +212,17 @@ where } } +/// Poseidon Commitment Trapdoor Type +pub type Trapdoor<S, J, const ARITY: usize> = + <Commitment<S, J, ARITY> as CommitmentScheme<J>>::Trapdoor; + +/// Poseidon Commitment Input Type +pub type Input<S, J, const ARITY: usize> = <Commitment<S, J, ARITY> as CommitmentScheme<J>>::Input; + +/// Poseidon Commitment Output Type +pub type Output<S, J, const ARITY: usize> = + <Commitment<S, J, ARITY> as CommitmentScheme<J>>::Output; + /// Arkworks Backend #[cfg(feature = "arkworks")] #[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] @@ -220,6 +231,9 @@ pub mod arkworks { /// Poseidon Permutation Specification pub trait Specification { + /// Field Type + type Field: Field; + /// Number of Full Rounds /// /// This is counted twice for the first set of full rounds and then the second set after the @@ -231,21 +245,18 @@ pub mod arkworks { /// S-BOX Exponenet const SBOX_EXPONENT: u64; - - /// Field Type - type Field: Field; } impl<S> super::Specification for S where S: Specification, { + type Field = S::Field; + const FULL_ROUNDS: usize = S::FULL_ROUNDS; const PARTIAL_ROUNDS: usize = S::PARTIAL_ROUNDS; - type Field = S::Field; - #[inline] fn add(_: &mut (), lhs: &Self::Field, rhs: &Self::Field) -> Self::Field { *lhs + *rhs diff --git a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs index 864043459..aefbf1a79 100644 --- a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs @@ -16,25 +16,19 @@ //! Arkworks Constraint System Implementation -use alloc::{vec, vec::Vec}; use ark_ff::{fields::Field, PrimeField}; -use ark_r1cs_std::{ - alloc::AllocVar, bits::boolean::Boolean, eq::EqGadget, fields::fp::FpVar, uint8::UInt8, -}; +use ark_r1cs_std::{alloc::AllocVar, bits::boolean::Boolean, eq::EqGadget}; use ark_relations::{ns, r1cs as ark_r1cs}; -use core::{ - borrow::Borrow, - ops::{Add, AddAssign}, -}; -use manta_accounting::asset::{AssetId, AssetValue}; use manta_crypto::constraint::{ - measure::Measure, reflection::HasAllocation, types::Bool, Allocation, AllocationMode, - ConstraintSystem, Equal, Public, PublicOrSecret, Secret, Variable, VariableSource, + measure::Measure, Add, Allocation, AllocationMode, ConstraintSystem, Equal, Public, + PublicOrSecret, Secret, Variable, }; -use manta_util::{Concat, ConcatAccumulator}; + +pub use ark_r1cs::SynthesisError; +pub use ark_r1cs_std::fields::fp::FpVar; /// Synthesis Result -pub type SynthesisResult<T = ()> = Result<T, ark_r1cs::SynthesisError>; +pub type SynthesisResult<T = ()> = Result<T, SynthesisError>; /// Returns an empty variable assignment for setup mode. /// @@ -46,7 +40,7 @@ pub type SynthesisResult<T = ()> = Result<T, ark_r1cs::SynthesisError>; /// some mocking is required and this function can not be used directly. #[inline] pub fn empty<T>() -> SynthesisResult<T> { - Err(ark_r1cs::SynthesisError::AssignmentMissing) + Err(SynthesisError::AssignmentMissing) } /// Returns a filled variable assignment. @@ -99,8 +93,8 @@ impl From<PublicOrSecret> for ArkAllocationMode { } } -/// Arkworks Constraint System -pub struct ArkConstraintSystem<F> +/// Arkworks Rank-1 Constraint System +pub struct R1CS<F> where F: Field, { @@ -108,7 +102,7 @@ where pub(crate) cs: ark_r1cs::ConstraintSystemRef<F>, } -impl<F> ArkConstraintSystem<F> +impl<F> R1CS<F> where F: Field, { @@ -132,20 +126,20 @@ where } } -impl<F> ConstraintSystem for ArkConstraintSystem<F> +impl<F> ConstraintSystem for R1CS<F> where F: Field, { type Bool = Boolean<F>; #[inline] - fn assert(&mut self, b: Bool<Self>) { + fn assert(&mut self, b: Self::Bool) { b.enforce_equal(&Boolean::TRUE) .expect("This should never fail."); } } -impl<F> Measure<PublicOrSecret> for ArkConstraintSystem<F> +impl<F> Measure<PublicOrSecret> for R1CS<F> where F: Field, { @@ -163,7 +157,7 @@ where } } -impl<F> Variable<ArkConstraintSystem<F>> for Boolean<F> +impl<F> Variable<R1CS<F>> for Boolean<F> where F: Field, { @@ -172,10 +166,7 @@ where type Mode = ArkAllocationMode; #[inline] - fn new( - cs: &mut ArkConstraintSystem<F>, - allocation: Allocation<Self::Type, Self::Mode>, - ) -> Self { + fn new(cs: &mut R1CS<F>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, ArkAllocationMode::Constant) => { Self::new_constant(ns!(cs.cs, "boolean constant"), this) @@ -197,55 +188,37 @@ where } } -impl<F> HasAllocation<ArkConstraintSystem<F>> for bool -where - F: Field, -{ - type Variable = Boolean<F>; - type Mode = ArkAllocationMode; -} - -impl<F> Equal<ArkConstraintSystem<F>> for Boolean<F> +impl<F> Equal<R1CS<F>> for Boolean<F> where F: Field, { #[inline] - fn eq(cs: &mut ArkConstraintSystem<F>, lhs: &Self, rhs: &Self) -> Boolean<F> { + fn eq(cs: &mut R1CS<F>, lhs: &Self, rhs: &Self) -> Boolean<F> { let _ = cs; lhs.is_eq(rhs) .expect("Equality checking is not allowed to fail.") } } -/// Prime Field Element -#[derive(derivative::Derivative)] -#[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] -pub struct Fp<F>(F) -where - F: PrimeField; - -impl<F> Variable<ArkConstraintSystem<F>> for FpVar<F> +impl<F> Variable<R1CS<F>> for FpVar<F> where F: PrimeField, { - type Type = Fp<F>; + type Type = F; type Mode = ArkAllocationMode; #[inline] - fn new( - cs: &mut ArkConstraintSystem<F>, - allocation: Allocation<Self::Type, Self::Mode>, - ) -> Self { + fn new(cs: &mut R1CS<F>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, ArkAllocationMode::Constant) => { - Self::new_constant(ns!(cs.cs, "prime field constant"), this.0) + Self::new_constant(ns!(cs.cs, "prime field constant"), this) } Allocation::Known(this, ArkAllocationMode::Public) => { - Self::new_input(ns!(cs.cs, "prime field input"), full(this.0)) + Self::new_input(ns!(cs.cs, "prime field input"), full(this)) } Allocation::Known(this, ArkAllocationMode::Secret) => { - Self::new_witness(ns!(cs.cs, "prime field witness"), full(this.0)) + Self::new_witness(ns!(cs.cs, "prime field witness"), full(this)) } Allocation::Unknown(PublicOrSecret::Public) => { Self::new_input(ns!(cs.cs, "prime field input"), empty::<F>) @@ -258,294 +231,25 @@ where } } -impl<F> HasAllocation<ArkConstraintSystem<F>> for Fp<F> -where - F: PrimeField, -{ - type Variable = FpVar<F>; - type Mode = ArkAllocationMode; -} - -/// Byte Array Variable -#[derive(derivative::Derivative)] -#[derivative(Clone, Debug)] -pub struct ByteArrayVar<F, const N: usize>(Vec<UInt8<F>>) -where - F: Field; - -impl<F, const N: usize> AsRef<[UInt8<F>]> for ByteArrayVar<F, N> -where - F: Field, -{ - #[inline] - fn as_ref(&self) -> &[UInt8<F>] { - &self.0 - } -} - -impl<F, const N: usize> Borrow<[UInt8<F>]> for ByteArrayVar<F, N> -where - F: Field, -{ - #[inline] - fn borrow(&self) -> &[UInt8<F>] { - &self.0 - } -} - -impl<F, const N: usize> Concat for ByteArrayVar<F, N> -where - F: Field, -{ - type Item = UInt8<F>; - - #[inline] - fn concat<A>(&self, accumulator: &mut A) - where - A: ConcatAccumulator<Self::Item> + ?Sized, - { - accumulator.extend(&self.0); - } -} - -impl<F, const N: usize> Variable<ArkConstraintSystem<F>> for ByteArrayVar<F, N> -where - F: PrimeField, -{ - type Type = [u8; N]; - - type Mode = PublicOrSecret; - - #[inline] - fn new( - cs: &mut ArkConstraintSystem<F>, - allocation: Allocation<Self::Type, Self::Mode>, - ) -> Self { - Self( - match allocation { - Allocation::Known(this, PublicOrSecret::Public) => { - UInt8::new_input_vec(ns!(cs.cs, "byte array public input"), this) - } - Allocation::Known(this, PublicOrSecret::Secret) => { - UInt8::new_witness_vec(ns!(cs.cs, "byte array secret witness"), this) - } - Allocation::Unknown(PublicOrSecret::Public) => { - UInt8::new_input_vec(ns!(cs.cs, "byte array public input"), &[0; N]) - } - Allocation::Unknown(PublicOrSecret::Secret) => { - UInt8::new_witness_vec(ns!(cs.cs, "byte array secret witness"), &vec![None; N]) - } - } - .expect("Variable allocation is not allowed to fail."), - ) - } -} - -impl<F, const N: usize> HasAllocation<ArkConstraintSystem<F>> for [u8; N] -where - F: PrimeField, -{ - type Variable = ByteArrayVar<F, N>; - type Mode = PublicOrSecret; -} - -/// Asset Id Variable -#[derive(derivative::Derivative)] -#[derivative(Clone, Debug)] -pub struct AssetIdVar<F> -where - F: PrimeField, -{ - /// Field Point - field_point: FpVar<F>, - - /// Byte Array - bytes: ByteArrayVar<F, { AssetId::SIZE }>, -} - -impl<F> AssetIdVar<F> -where - F: PrimeField, -{ - /// Builds a new [`AssetIdVar`] from `field_point` and `bytes`. - #[inline] - fn new(field_point: FpVar<F>, bytes: ByteArrayVar<F, { AssetId::SIZE }>) -> Self { - Self { field_point, bytes } - } -} - -impl<F> Concat for AssetIdVar<F> -where - F: PrimeField, -{ - type Item = UInt8<F>; - - #[inline] - fn concat<A>(&self, accumulator: &mut A) - where - A: ConcatAccumulator<Self::Item> + ?Sized, - { - self.bytes.concat(accumulator); - } -} - -impl<F> Variable<ArkConstraintSystem<F>> for AssetIdVar<F> -where - F: PrimeField, -{ - type Type = AssetId; - - type Mode = PublicOrSecret; - - #[inline] - fn new( - cs: &mut ArkConstraintSystem<F>, - allocation: Allocation<Self::Type, Self::Mode>, - ) -> Self { - match allocation { - Allocation::Known(this, mode) => Self::new( - Fp(F::from(this.0)).as_known(cs, mode), - this.into_bytes().as_known(cs, mode), - ), - Allocation::Unknown(mode) => Self::new( - Fp::as_unknown(cs, mode), - <[u8; AssetId::SIZE]>::as_unknown(cs, mode), - ), - } - } -} - -impl<F> HasAllocation<ArkConstraintSystem<F>> for AssetId -where - F: PrimeField, -{ - type Variable = AssetIdVar<F>; - type Mode = PublicOrSecret; -} - -impl<F> Equal<ArkConstraintSystem<F>> for AssetIdVar<F> +impl<F> Equal<R1CS<F>> for FpVar<F> where F: PrimeField, { #[inline] - fn eq(cs: &mut ArkConstraintSystem<F>, lhs: &Self, rhs: &Self) -> Boolean<F> { - // TODO: Is `field_point` or `bytes` faster? + fn eq(cs: &mut R1CS<F>, lhs: &Self, rhs: &Self) -> Boolean<F> { let _ = cs; - lhs.field_point - .is_eq(&rhs.field_point) + lhs.is_eq(rhs) .expect("Equality checking is not allowed to fail.") } } -/// Asset Value Variable -#[derive(derivative::Derivative)] -#[derivative(Clone, Debug)] -pub struct AssetValueVar<F> -where - F: PrimeField, -{ - /// Field Point - field_point: FpVar<F>, - - /// Byte Array - bytes: Option<ByteArrayVar<F, { AssetValue::SIZE }>>, -} - -impl<F> AssetValueVar<F> -where - F: PrimeField, -{ - /// Builds a new [`AssetValueVar`] from `field_point` and `bytes`. - #[inline] - fn new(field_point: FpVar<F>, bytes: Option<ByteArrayVar<F, { AssetValue::SIZE }>>) -> Self { - Self { field_point, bytes } - } -} - -impl<F> Concat for AssetValueVar<F> -where - F: PrimeField, -{ - type Item = UInt8<F>; - - #[inline] - fn concat<A>(&self, accumulator: &mut A) - where - A: ConcatAccumulator<Self::Item> + ?Sized, - { - if let Some(bytes) = &self.bytes { - bytes.concat(accumulator); - } - } -} - -impl<F> Variable<ArkConstraintSystem<F>> for AssetValueVar<F> +impl<F> Add<R1CS<F>> for FpVar<F> where F: PrimeField, { - type Type = AssetValue; - - type Mode = PublicOrSecret; - #[inline] - fn new( - cs: &mut ArkConstraintSystem<F>, - allocation: Allocation<Self::Type, Self::Mode>, - ) -> Self { - match allocation { - Allocation::Known(this, mode) => Self::new( - Fp(F::from(this.0)).as_known(cs, mode), - Some(this.into_bytes().as_known(cs, mode)), - ), - Allocation::Unknown(mode) => Self::new( - Fp::as_unknown(cs, mode), - Some(<[u8; AssetValue::SIZE]>::as_unknown(cs, mode)), - ), - } - } -} - -impl<F> HasAllocation<ArkConstraintSystem<F>> for AssetValue -where - F: PrimeField, -{ - type Variable = AssetValueVar<F>; - type Mode = PublicOrSecret; -} - -impl<F> Equal<ArkConstraintSystem<F>> for AssetValueVar<F> -where - F: PrimeField, -{ - #[inline] - fn eq(cs: &mut ArkConstraintSystem<F>, lhs: &Self, rhs: &Self) -> Boolean<F> { - // TODO: Is `field_point` or `bytes` faster? + fn add(cs: &mut R1CS<F>, lhs: Self, rhs: Self) -> Self { let _ = cs; - lhs.field_point - .is_eq(&rhs.field_point) - .expect("Equality checking is not allowed to fail.") - } -} - -impl<F> Add for AssetValueVar<F> -where - F: PrimeField, -{ - type Output = Self; - - #[inline] - fn add(self, rhs: Self) -> Self { - // TODO: This is not a good abstraction. - Self::new(self.field_point + rhs.field_point, None) - } -} - -impl<F> AddAssign for AssetValueVar<F> -where - F: PrimeField, -{ - #[inline] - fn add_assign(&mut self, rhs: Self) { - self.field_point += rhs.field_point; + lhs + rhs } } diff --git a/manta-pay/src/crypto/constraint/arkworks/mod.rs b/manta-pay/src/crypto/constraint/arkworks/mod.rs index 1a7fa9b69..675e55562 100644 --- a/manta-pay/src/crypto/constraint/arkworks/mod.rs +++ b/manta-pay/src/crypto/constraint/arkworks/mod.rs @@ -17,7 +17,7 @@ //! Arkworks Constraint System and Proof System Implementations mod constraint_system; - -pub mod proof_systems; +mod proof_system; pub use constraint_system::*; +pub use proof_system::*; diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs similarity index 60% rename from manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs rename to manta-pay/src/crypto/constraint/arkworks/proof_system.rs index 938007875..053818ab9 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_systems/groth16.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs @@ -14,36 +14,32 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Arkworks Groth16 Implementation - -use crate::{ - accounting::identity::{Root, Utxo, VoidNumber}, - crypto::{ - constraint::arkworks::{constraint_system::SynthesisResult, ArkConstraintSystem}, - merkle_tree::constraint::root_extend_input, +//! Arkworks Proof System Implementations + +use crate::crypto::constraint::arkworks::{constraint_system::SynthesisResult, R1CS}; +use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef}; + +#[cfg(feature = "groth16")] +use { + crate::crypto::constraint::arkworks::constraint_system::SynthesisError, + alloc::vec::Vec, + ark_crypto_primitives::SNARK, + ark_groth16::{Groth16 as ArkGroth16, PreparedVerifyingKey, Proof, ProvingKey}, + core::marker::PhantomData, + manta_crypto::{ + constraint::ProofSystem, + rand::{CryptoRng, RngCore, SizedRng}, }, }; -use alloc::vec::Vec; -use ark_crypto_primitives::SNARK; -use ark_ec::PairingEngine; -use ark_ff::{Field, ToConstraintField}; -use ark_groth16::{Groth16 as ArkGroth16, PreparedVerifyingKey, Proof, ProvingKey}; -use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError}; -use core::marker::PhantomData; -use manta_accounting::asset::{AssetId, AssetValue}; -use manta_crypto::{ - constraint::{Input, ProofSystem}, - rand::{CryptoRng, RngCore, SizedRng}, -}; /// Constraint Synthesizer Wrapper -struct ConstraintSynthesizerWrapper<F>(ArkConstraintSystem<F>) +struct ConstraintSynthesizerWrapper<F>(R1CS<F>) where - F: Field; + F: ark_ff::Field; impl<F> ConstraintSynthesizer<F> for ConstraintSynthesizerWrapper<F> where - F: Field, + F: ark_ff::Field, { #[inline] fn generate_constraints(self, cs: ConstraintSystemRef<F>) -> SynthesisResult { @@ -60,18 +56,21 @@ where } } -/// Arkworks Groth 16 Proof System +/// Arkworks Groth16 Proof System +#[cfg(feature = "groth16")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "groth16")))] #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Groth16<E>(PhantomData<E>) where - E: PairingEngine; + E: ark_ec::PairingEngine; +#[cfg(feature = "groth16")] impl<E> ProofSystem for Groth16<E> where - E: PairingEngine, + E: ark_ec::PairingEngine, { - type ConstraintSystem = ArkConstraintSystem<E::Fr>; + type ConstraintSystem = R1CS<E::Fr>; type ProvingContext = ProvingKey<E>; @@ -135,57 +134,3 @@ where ArkGroth16::verify_with_processed_vk(context, input, proof) } } - -/* TODO: -impl<E> Input<AssetId> for Groth16<E> -where - E: PairingEngine, -{ - #[inline] - fn extend(input: &mut Self::Input, next: &AssetId) { - input.push(next.0.into()); - input.append(&mut next.into_bytes().to_field_elements().unwrap()); - } -} - -impl<E> Input<AssetValue> for Groth16<E> -where - E: PairingEngine, -{ - #[inline] - fn extend(input: &mut Self::Input, next: &AssetValue) { - input.push(next.0.into()); - input.append(&mut next.into_bytes().to_field_elements().unwrap()); - } -} - -impl<E> Input<VoidNumber> for Groth16<E> -where - E: PairingEngine, -{ - #[inline] - fn extend(input: &mut Self::Input, next: &VoidNumber) { - next.extend_input(input); - } -} - -impl<E> Input<Root> for Groth16<E> -where - E: PairingEngine<Fr = ark_ff::Fp256<ark_bls12_381::FrParameters>>, -{ - #[inline] - fn extend(input: &mut Self::Input, next: &Root) { - root_extend_input(next, input); - } -} - -impl<E> Input<Utxo> for Groth16<E> -where - E: PairingEngine, -{ - #[inline] - fn extend(input: &mut Self::Input, next: &Utxo) { - next.extend_input(input); - } -} -*/ diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_systems/mod.rs b/manta-pay/src/crypto/constraint/arkworks/proof_systems/mod.rs deleted file mode 100644 index bd0636544..000000000 --- a/manta-pay/src/crypto/constraint/arkworks/proof_systems/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Arkworks Proof System Implementations - -#[cfg(feature = "arkworks-groth16")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks-groth16")))] -pub mod groth16; diff --git a/manta-pay/src/crypto/constraint/mod.rs b/manta-pay/src/crypto/constraint/mod.rs index b47f181bd..521240148 100644 --- a/manta-pay/src/crypto/constraint/mod.rs +++ b/manta-pay/src/crypto/constraint/mod.rs @@ -16,10 +16,10 @@ //! Constraint System and Proof System Implementations -#[cfg(feature = "arkworks-zkp")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks-zkp")))] +#[cfg(feature = "arkworks")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] pub mod arkworks; -#[cfg(feature = "zk-garage-zkp")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "zk-garage-zkp")))] -pub mod zk_garage; +#[cfg(feature = "plonk")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "plonk")))] +pub mod plonk; diff --git a/manta-pay/src/crypto/constraint/zk_garage/proof_systems/mod.rs b/manta-pay/src/crypto/constraint/plonk.rs similarity index 92% rename from manta-pay/src/crypto/constraint/zk_garage/proof_systems/mod.rs rename to manta-pay/src/crypto/constraint/plonk.rs index d9b543651..b72b8c82b 100644 --- a/manta-pay/src/crypto/constraint/zk_garage/proof_systems/mod.rs +++ b/manta-pay/src/crypto/constraint/plonk.rs @@ -14,4 +14,4 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! ZK-Garage Proof System Implementations +//! PLONK Constraint System and Proof System Implementations diff --git a/manta-pay/src/crypto/constraint/zk_garage/constraint_system.rs b/manta-pay/src/crypto/constraint/zk_garage/constraint_system.rs deleted file mode 100644 index 565a60be7..000000000 --- a/manta-pay/src/crypto/constraint/zk_garage/constraint_system.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! ZK-Garage Constraint System Implementation diff --git a/manta-pay/src/crypto/constraint/zk_garage/mod.rs b/manta-pay/src/crypto/constraint/zk_garage/mod.rs deleted file mode 100644 index 9d9e803c9..000000000 --- a/manta-pay/src/crypto/constraint/zk_garage/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! ZK-Garage Constraint System and Proof System Implementations - -mod constraint_system; - -pub mod proof_systems; - -pub use constraint_system::*; diff --git a/manta-pay/src/crypto/key.rs b/manta-pay/src/crypto/key.rs index bb1513602..aea72d00b 100644 --- a/manta-pay/src/crypto/key.rs +++ b/manta-pay/src/crypto/key.rs @@ -23,7 +23,12 @@ use manta_util::into_array_unchecked; #[cfg(feature = "arkworks")] use { + crate::crypto::constraint::arkworks::R1CS, ark_ec::{AffineCurve, ProjectiveCurve}, + ark_ff::Field, + ark_r1cs_std::fields::fp::FpVar, + ark_r1cs_std::groups::{CurveVar, GroupOpsBounds}, + ark_r1cs_std::ToBitsGadget, manta_crypto::key::KeyAgreementScheme, }; @@ -50,17 +55,25 @@ where } } +/// +#[cfg(feature = "arkworks")] +type ConstraintField<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField; + /// Elliptic Curve Diffie Hellman Protocol #[cfg(feature = "arkworks")] #[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] -pub struct EllipticCurveDiffieHellman<C>(PhantomData<C>) +pub struct EllipticCurveDiffieHellman<C, GG>(PhantomData<(C, GG)>) where - C: ProjectiveCurve; + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; #[cfg(feature = "arkworks")] -impl<C> KeyAgreementScheme for EllipticCurveDiffieHellman<C> +impl<C, GG> KeyAgreementScheme for EllipticCurveDiffieHellman<C, GG> where C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, { type SecretKey = C::ScalarField; @@ -99,3 +112,49 @@ where public_key } } + +#[cfg(feature = "arkworks")] +impl<C, GG> KeyAgreementScheme<R1CS<ConstraintField<C>>> for EllipticCurveDiffieHellman<C, GG> +where + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, +{ + type SecretKey = FpVar<C::ScalarField>; + + type PublicKey = GG; + + type SharedSecret = GG; + + #[inline] + fn derive( + compiler: &mut R1CS<ConstraintField<C>>, + secret_key: &Self::SecretKey, + ) -> Self::PublicKey { + /* TODO: + let _ = compiler; + GG::prime_subgroup_generator().mul(secret_key) + */ + todo!() + } + + #[inline] + fn agree( + compiler: &mut R1CS<ConstraintField<C>>, + secret_key: &Self::SecretKey, + public_key: &Self::PublicKey, + ) -> Self::SharedSecret { + /* TODO: + let _ = compiler; + public_key + .scalar_mul_le( + FpVar::<ConstraintField<C>>::from(secret_key) + .to_bits_le() + .expect("") + .iter(), + ) + .expect("") + */ + todo!() + } +} diff --git a/manta-pay/src/crypto/mod.rs b/manta-pay/src/crypto/mod.rs index 72f2c5740..a9b4d16bf 100644 --- a/manta-pay/src/crypto/mod.rs +++ b/manta-pay/src/crypto/mod.rs @@ -17,7 +17,7 @@ //! Manta Pay Cryptographic Primitives Implementations pub mod commitment; -// TODO: pub mod constraint; +pub mod constraint; pub mod encryption; pub mod key; // TODO: pub mod merkle_tree; diff --git a/manta-pay/src/accounting/key.rs b/manta-pay/src/key.rs similarity index 100% rename from manta-pay/src/accounting/key.rs rename to manta-pay/src/key.rs diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index 39bb223a7..84f3dae57 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -24,9 +24,10 @@ extern crate alloc; pub mod crypto; +pub mod key; + +// TODO[remove]: pub mod accounting; -/* #[cfg(all(feature = "arkworks", feature = "groth16"))] #[cfg_attr(doc_cfg, doc(cfg(all(feature = "arkworks", feature = "groth16"))))] -pub mod accounting; -*/ +pub mod config; From a24f0d752ad393b5eb80c1513f80ea2700c92d95 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 28 Dec 2021 23:58:16 -0500 Subject: [PATCH 147/275] wip: add poseidon in circuit --- manta-pay/src/config.rs | 14 +++---- manta-pay/src/crypto/commitment/poseidon.rs | 41 ++++++++++++++++++++- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index a49da5b06..38ea817fd 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -98,15 +98,14 @@ impl CommitmentScheme for EphemeralKeyCommitmentScheme { } } -/* impl CommitmentScheme<Compiler> for EphemeralKeyCommitmentScheme { - type Parameters = poseidon::Parameters<Self, Compiler, 2>; + type Parameters = poseidon::Parameters<PoseidonSpec<2>, Compiler, 2>; - type Trapdoor = poseidon::Trapdoor<Self, Compiler, 2>; + type Trapdoor = poseidon::Trapdoor<PoseidonSpec<2>, Compiler, 2>; - type Input = Asset<FpVar<Compiler>, FpVar<Compiler>>; + type Input = Asset<FpVar<ConstraintField>, FpVar<ConstraintField>>; - type Output = poseidon::Output<Self, Compiler, 2>; + type Output = poseidon::Output<PoseidonSpec<2>, Compiler, 2>; #[inline] fn commit( @@ -115,15 +114,14 @@ impl CommitmentScheme<Compiler> for EphemeralKeyCommitmentScheme { trapdoor: &Self::Trapdoor, input: &Self::Input, ) -> Self::Output { - poseidon::Commitment::<Self, Compiler, 2>::commit( + poseidon::Commitment::<PoseidonSpec<2>, Compiler, 2>::commit( compiler, parameters, trapdoor, - &[input.id.0.into(), input.value.0.into()], + &[input.id.clone(), input.value.clone()], ) } } -*/ /// pub struct TrapdoorDerivationFunction; diff --git a/manta-pay/src/crypto/commitment/poseidon.rs b/manta-pay/src/crypto/commitment/poseidon.rs index e4a903416..a52505ba7 100644 --- a/manta-pay/src/crypto/commitment/poseidon.rs +++ b/manta-pay/src/crypto/commitment/poseidon.rs @@ -227,12 +227,14 @@ pub type Output<S, J, const ARITY: usize> = #[cfg(feature = "arkworks")] #[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] pub mod arkworks { - use ark_ff::Field; + use crate::crypto::constraint::arkworks::{FpVar, R1CS}; + use ark_ff::{Field, PrimeField}; + use ark_r1cs_std::fields::FieldVar; /// Poseidon Permutation Specification pub trait Specification { /// Field Type - type Field: Field; + type Field: PrimeField; /// Number of Full Rounds /// @@ -277,4 +279,39 @@ pub mod arkworks { *point = point.pow(&[Self::SBOX_EXPONENT, 0, 0, 0]); } } + + impl<S> super::Specification<R1CS<S::Field>> for S + where + S: Specification, + { + type Field = FpVar<S::Field>; + + const FULL_ROUNDS: usize = S::FULL_ROUNDS; + + const PARTIAL_ROUNDS: usize = S::PARTIAL_ROUNDS; + + #[inline] + fn add(compiler: &mut R1CS<S::Field>, lhs: &Self::Field, rhs: &Self::Field) -> Self::Field { + let _ = compiler; + lhs + rhs + } + + #[inline] + fn mul(compiler: &mut R1CS<S::Field>, lhs: &Self::Field, rhs: &Self::Field) -> Self::Field { + let _ = compiler; + lhs * rhs + } + + #[inline] + fn add_assign(compiler: &mut R1CS<S::Field>, lhs: &mut Self::Field, rhs: &Self::Field) { + let _ = compiler; + *lhs += rhs; + } + + #[inline] + fn apply_sbox(compiler: &mut R1CS<S::Field>, point: &mut Self::Field) { + let _ = compiler; + *point = point.pow_by_constant(&[Self::SBOX_EXPONENT]).expect(""); + } + } } From 6a9c634ba05017c5f1092e5f0b1b29e063fd7c81 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 30 Dec 2021 16:06:20 -0500 Subject: [PATCH 148/275] wip: move to new transfer config abstraction --- manta-accounting/src/transfer/batch.rs | 28 +- manta-accounting/src/transfer/mod.rs | 552 ++++++++++---------- manta-accounting/src/wallet/signer.rs | 76 +-- manta-crypto/src/accumulator.rs | 104 ++-- manta-crypto/src/commitment.rs | 49 +- manta-crypto/src/ecc.rs | 44 ++ manta-crypto/src/encryption.rs | 44 +- manta-crypto/src/key.rs | 20 +- manta-crypto/src/lib.rs | 1 + manta-crypto/src/merkle_tree/forest.rs | 10 +- manta-crypto/src/merkle_tree/tree.rs | 22 +- manta-pay/src/config.rs | 152 +++--- manta-pay/src/crypto/commitment/pedersen.rs | 56 +- manta-pay/src/crypto/commitment/poseidon.rs | 108 ++-- manta-pay/src/crypto/key.rs | 217 ++++---- 15 files changed, 739 insertions(+), 744 deletions(-) create mode 100644 manta-crypto/src/ecc.rs diff --git a/manta-accounting/src/transfer/batch.rs b/manta-accounting/src/transfer/batch.rs index 1adf53285..26c8b53b9 100644 --- a/manta-accounting/src/transfer/batch.rs +++ b/manta-accounting/src/transfer/batch.rs @@ -18,10 +18,7 @@ use crate::{ asset::Asset, - transfer::{ - Configuration, EphemeralKeyParameters, PreSender, Receiver, SpendingKey, Utxo, - UtxoCommitmentParameters, VoidNumberCommitmentParameters, - }, + transfer::{Configuration, Parameters, PreSender, Receiver, SpendingKey, Utxo}, }; use alloc::vec::Vec; use manta_crypto::{ @@ -49,9 +46,7 @@ where /// Builds a new [`Join`] for `asset` using `spending_key` and `zero_key`. #[inline] pub fn new<R, const RECEIVERS: usize>( - ephemeral_key_parameters: &EphemeralKeyParameters<C>, - utxo_parameters: &UtxoCommitmentParameters<C>, - void_number_parameters: &VoidNumberCommitmentParameters<C>, + parameters: &Parameters<C>, asset: Asset, spending_key: &SpendingKey<C>, rng: &mut R, @@ -64,22 +59,11 @@ where // let mut receivers = Vec::with_capacity(RECEIVERS); let mut zeroes = Vec::with_capacity(RECEIVERS - 1); - let (receiver, pre_sender) = spending_key.internal_pair( - ephemeral_key_parameters, - utxo_parameters, - void_number_parameters, - rng.gen(), - asset, - ); + let (receiver, pre_sender) = spending_key.internal_pair(parameters, rng.gen(), asset); receivers.push(receiver); for _ in 0..RECEIVERS - 2 { - let (receiver, pre_sender) = spending_key.internal_zero_pair( - ephemeral_key_parameters, - utxo_parameters, - void_number_parameters, - rng.gen(), - asset.id, - ); + let (receiver, pre_sender) = + spending_key.internal_zero_pair(parameters, rng.gen(), asset.id); receivers.push(receiver); zeroes.push(pre_sender); } @@ -90,7 +74,7 @@ where #[inline] pub fn insert_utxos<A>(&self, utxo_set: &mut A) where - A: Accumulator<Item = Utxo<C>, Verifier = C::UtxoSetVerifier>, + A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, { self.pre_sender.insert_utxo(utxo_set); for zero in &self.zeroes { diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index 198c44786..3b9cc26ee 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -20,7 +20,7 @@ use crate::asset::{Asset, AssetId, AssetValue}; use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use manta_crypto::{ - accumulator::{self, Accumulator, MembershipProof, Verifier}, + accumulator::{Accumulator, MembershipProof, Model}, commitment::CommitmentScheme, constraint::{ Add, Allocation, Constant, ConstraintSystem, Derived, Equal, Input as ProofSystemInput, @@ -55,6 +55,12 @@ pub trait Configuration { /// Public Key Type type PublicKey: Clone; + /// Key Agreement Scheme Type + type KeyAgreementScheme: KeyAgreementScheme< + SecretKey = Self::SecretKey, + PublicKey = Self::PublicKey, + >; + /// Secret Key Variable Type type SecretKeyVar: Variable<Self::Compiler, Type = SecretKey<Self>, Mode = Secret>; @@ -62,17 +68,23 @@ pub trait Configuration { type PublicKeyVar: Variable<Self::Compiler, Type = PublicKey<Self>, Mode = Public> + Equal<Self::Compiler>; - /// Key Agreement Scheme Type - type KeyAgreementScheme: KeyAgreementScheme<SecretKey = Self::SecretKey, PublicKey = Self::PublicKey> - + KeyAgreementScheme< + /// + type KeyAgreementSchemeVar: KeyAgreementScheme< Self::Compiler, SecretKey = Self::SecretKeyVar, PublicKey = Self::PublicKeyVar, - >; + > + Variable<Self::Compiler, Type = Self::KeyAgreementScheme, Mode = Constant>; /// Ephemeral Key Trapdoor Type type EphemeralKeyTrapdoor: Sample; + /// Ephemeral Key Commitment Scheme Type + type EphemeralKeyCommitmentScheme: CommitmentScheme< + Trapdoor = Self::EphemeralKeyTrapdoor, + Input = Asset, + Output = Self::SecretKey, + >; + /// Ephemeral Key Trapdoor Variable Type type EphemeralKeyTrapdoorVar: Variable< Self::Compiler, @@ -80,90 +92,65 @@ pub trait Configuration { Mode = Secret, >; - /// Ephemeral Key Commitment Scheme Parameters Variable Type - type EphemeralKeyParametersVar: Variable< - Self::Compiler, - Type = EphemeralKeyParameters<Self>, - Mode = Constant, - >; - - /// Ephemeral Key Commitment Scheme Type - type EphemeralKeyCommitmentScheme: CommitmentScheme< - Trapdoor = Self::EphemeralKeyTrapdoor, - Input = Asset, - Output = Self::SecretKey, - > + CommitmentScheme< + /// + type EphemeralKeyCommitmentSchemeVar: CommitmentScheme< Self::Compiler, - Parameters = Self::EphemeralKeyParametersVar, Trapdoor = Self::EphemeralKeyTrapdoorVar, Input = AssetVar<Self>, Output = Self::SecretKeyVar, - >; + > + Variable<Self::Compiler, Type = Self::EphemeralKeyCommitmentScheme, Mode = Constant>; /// Trapdoor Derivation Function Type - type TrapdoorDerivationFunction: KeyDerivationFunction<Key = SharedSecret<Self>, Output = Trapdoor<Self>> - + KeyDerivationFunction< + type TrapdoorDerivationFunction: KeyDerivationFunction< + Key = SharedSecret<Self>, + Output = Trapdoor<Self>, + >; + + /// + type TrapdoorDerivationFunctionVar: KeyDerivationFunction< Self::Compiler, Key = SharedSecretVar<Self>, Output = TrapdoorVar<Self>, - >; - - /// UTXO Commitment Parameters Variable Type - type UtxoCommitmentParametersVar: Variable< - Self::Compiler, - Type = UtxoCommitmentParameters<Self>, - Mode = Constant, - >; + > + Variable<Self::Compiler, Type = Self::TrapdoorDerivationFunction, Mode = Constant>; /// Unspent Transaction Output Type type Utxo: PartialEq; + /// UTXO Commitment Scheme Type + type UtxoCommitmentScheme: CommitmentScheme<Input = Asset, Output = Self::Utxo>; + /// UTXO Variable Type type UtxoVar: Variable<Self::Compiler, Type = Self::Utxo, Mode = PublicOrSecret> + Equal<Self::Compiler>; - /// UTXO Commitment Scheme Type - type UtxoCommitmentScheme: CommitmentScheme<Input = Asset, Output = Self::Utxo> - + CommitmentScheme< - Self::Compiler, - Parameters = Self::UtxoCommitmentParametersVar, - Input = AssetVar<Self>, - Output = Self::UtxoVar, - >; - - /// Void Number Commitment Scheme Parameters Variable Type - type VoidNumberCommitmentParametersVar: Variable< - Self::Compiler, - Type = VoidNumberCommitmentParameters<Self>, - Mode = Constant, - >; + /// + type UtxoCommitmentSchemeVar: CommitmentScheme<Self::Compiler, Input = AssetVar<Self>, Output = Self::UtxoVar> + + Variable<Self::Compiler, Type = Self::UtxoCommitmentScheme, Mode = Constant>; /// Void Number Type type VoidNumber: PartialEq; + /// Void Number Commitment Scheme Type + type VoidNumberCommitmentScheme: CommitmentScheme< + Trapdoor = Trapdoor<Self>, + Input = Self::SecretKey, + Output = Self::VoidNumber, + >; + /// Void Number Variable Type type VoidNumberVar: Variable<Self::Compiler, Type = Self::VoidNumber, Mode = Public> + Equal<Self::Compiler>; - /// Void Number Commitment Scheme Type - type VoidNumberCommitmentScheme: CommitmentScheme< - Trapdoor = Trapdoor<Self>, - Input = Self::SecretKey, - Output = Self::VoidNumber, - > + CommitmentScheme< + /// + type VoidNumberCommitmentSchemeVar: CommitmentScheme< Self::Compiler, - Parameters = Self::VoidNumberCommitmentParametersVar, Trapdoor = TrapdoorVar<Self>, Input = Self::SecretKeyVar, Output = Self::VoidNumberVar, - >; + > + Variable<Self::Compiler, Type = Self::VoidNumberCommitmentScheme, Mode = Constant>; - /// UTXO Set Parameters Variable Type - type UtxoSetParametersVar: Variable< - Self::Compiler, - Type = UtxoSetParameters<Self>, - Mode = Constant, - >; + /// UTXO Set Model Type + type UtxoSetModel: Model<Item = Self::Utxo, Verification = bool>; /// UTXO Set Witness Variable Type type UtxoSetWitnessVar: Variable<Self::Compiler, Type = UtxoSetWitness<Self>, Mode = Secret>; @@ -171,16 +158,14 @@ pub trait Configuration { /// UTXO Set Output Variable Type type UtxoSetOutputVar: Variable<Self::Compiler, Type = UtxoSetOutput<Self>, Mode = Public>; - /// UTXO Set Verifier Type - type UtxoSetVerifier: Verifier<Item = Self::Utxo, Verification = bool> - + Verifier< + /// + type UtxoSetModelVar: Model< Self::Compiler, - Parameters = Self::UtxoSetParametersVar, Item = Self::UtxoVar, Witness = Self::UtxoSetWitnessVar, Output = Self::UtxoSetOutputVar, Verification = <Self::Compiler as ConstraintSystem>::Bool, - >; + > + Variable<Self::Compiler, Type = Self::UtxoSetModel, Mode = Constant>; /// Asset Id Variable Type type AssetIdVar: Variable<Self::Compiler, Type = AssetId, Mode = PublicOrSecret> @@ -203,142 +188,175 @@ pub trait Configuration { + ProofSystemInput<VoidNumber<Self>> + ProofSystemInput<PublicKey<Self>>; + /// Note Encryption Key Derivation Function + type NoteEncryptionKeyDerivationFunction: Default; + /// Note Encryption Scheme Type type NoteEncryptionScheme: HybridPublicKeyEncryptionScheme< Plaintext = Asset, KeyAgreementScheme = Self::KeyAgreementScheme, + KeyDerivationFunction = Self::NoteEncryptionKeyDerivationFunction, >; /// Generates the ephemeral secret key from `parameters`, `trapdoor`, and `asset`. #[inline] fn ephemeral_secret_key( - parameters: &EphemeralKeyParameters<Self>, + parameters: &Self::EphemeralKeyCommitmentScheme, trapdoor: &EphemeralKeyTrapdoor<Self>, asset: Asset, ) -> SecretKey<Self> { - Self::EphemeralKeyCommitmentScheme::commit(&mut (), parameters, trapdoor, &asset) + parameters.commit(trapdoor, &asset, &mut ()) } /// Generates the ephemeral secret key from `parameters`, `trapdoor`, and `asset`. #[inline] fn ephemeral_secret_key_var( - cs: &mut Self::Compiler, - parameters: &EphemeralKeyParametersVar<Self>, + parameters: &Self::EphemeralKeyCommitmentSchemeVar, trapdoor: &EphemeralKeyTrapdoorVar<Self>, asset: &AssetVar<Self>, + cs: &mut Self::Compiler, ) -> SecretKeyVar<Self> { - Self::EphemeralKeyCommitmentScheme::commit(cs, parameters, trapdoor, asset) + parameters.commit(trapdoor, asset, cs) } /// Derives a public key variable from a secret key variable. #[inline] fn ephemeral_public_key_var( - cs: &mut Self::Compiler, + parameters: &Self::KeyAgreementSchemeVar, secret_key: &SecretKeyVar<Self>, + cs: &mut Self::Compiler, ) -> PublicKeyVar<Self> { - Self::KeyAgreementScheme::derive(cs, secret_key) + parameters.derive(secret_key, cs) } /// Generates the commitment trapdoor associated to `secret_key` and `public_key`. #[inline] - fn trapdoor(secret_key: &SecretKey<Self>, public_key: &PublicKey<Self>) -> Trapdoor<Self> { - let shared_secret = Self::KeyAgreementScheme::agree(&mut (), secret_key, public_key); - Self::TrapdoorDerivationFunction::derive(&mut (), shared_secret) + fn trapdoor( + key_agreement: &Self::KeyAgreementScheme, + trapdoor_derivation_function: &Self::TrapdoorDerivationFunction, + secret_key: &SecretKey<Self>, + public_key: &PublicKey<Self>, + ) -> Trapdoor<Self> { + let shared_secret = key_agreement.agree(secret_key, public_key, &mut ()); + trapdoor_derivation_function.derive(shared_secret, &mut ()) } /// Generates the commitment trapdoor associated to `secret_key` and `public_key`. #[inline] fn trapdoor_var( - cs: &mut Self::Compiler, + key_agreement: &Self::KeyAgreementSchemeVar, + trapdoor_derivation_function: &Self::TrapdoorDerivationFunctionVar, secret_key: &SecretKeyVar<Self>, public_key: &PublicKeyVar<Self>, + cs: &mut Self::Compiler, ) -> TrapdoorVar<Self> { - let shared_secret = Self::KeyAgreementScheme::agree(cs, secret_key, public_key); - Self::TrapdoorDerivationFunction::derive(cs, shared_secret) + let shared_secret = key_agreement.agree(secret_key, public_key, cs); + trapdoor_derivation_function.derive(shared_secret, cs) } /// Generates the UTXO associated to `trapdoor` and `asset` from `parameters`. #[inline] fn utxo( - parameters: &UtxoCommitmentParameters<Self>, + parameters: &Self::UtxoCommitmentScheme, trapdoor: &Trapdoor<Self>, asset: &Asset, ) -> Utxo<Self> { - Self::UtxoCommitmentScheme::commit(&mut (), parameters, trapdoor, asset) + parameters.commit(trapdoor, asset, &mut ()) } /// Generates the UTXO associated to `trapdoor` and `asset` from `parameters`. #[inline] fn utxo_var( - cs: &mut Self::Compiler, - parameters: &UtxoCommitmentParametersVar<Self>, + parameters: &Self::UtxoCommitmentSchemeVar, trapdoor: &TrapdoorVar<Self>, asset: &AssetVar<Self>, + cs: &mut Self::Compiler, ) -> UtxoVar<Self> { - Self::UtxoCommitmentScheme::commit(cs, parameters, trapdoor, asset) + parameters.commit(trapdoor, asset, cs) } /// Generates the trapdoor associated to `secret_key` and `public_key` and then uses it to /// generate the UTXO associated to `asset`. #[inline] fn full_utxo( - parameters: &UtxoCommitmentParameters<Self>, + key_agreement: &Self::KeyAgreementScheme, + trapdoor_derivation_function: &Self::TrapdoorDerivationFunction, + utxo_commitment: &Self::UtxoCommitmentScheme, secret_key: &SecretKey<Self>, public_key: &PublicKey<Self>, asset: &Asset, ) -> Utxo<Self> { - Self::utxo(parameters, &Self::trapdoor(secret_key, public_key), asset) + let trapdoor = Self::trapdoor( + key_agreement, + trapdoor_derivation_function, + secret_key, + public_key, + ); + Self::utxo(utxo_commitment, &trapdoor, asset) } /// Generates the trapdoor associated to `secret_key` and `public_key` and then uses it to /// generate the UTXO associated to `asset`. #[inline] fn full_utxo_var( - cs: &mut Self::Compiler, - parameters: &UtxoCommitmentParametersVar<Self>, + key_agreement: &Self::KeyAgreementSchemeVar, + trapdoor_derivation_function: &Self::TrapdoorDerivationFunctionVar, + utxo_commitment: &Self::UtxoCommitmentSchemeVar, secret_key: &SecretKeyVar<Self>, public_key: &PublicKeyVar<Self>, asset: &AssetVar<Self>, + cs: &mut Self::Compiler, ) -> UtxoVar<Self> { - let trapdoor = Self::trapdoor_var(cs, secret_key, public_key); - Self::utxo_var(cs, parameters, &trapdoor, asset) + let trapdoor = Self::trapdoor_var( + key_agreement, + trapdoor_derivation_function, + secret_key, + public_key, + cs, + ); + Self::utxo_var(utxo_commitment, &trapdoor, asset, cs) } /// Generates the void number associated to `trapdoor` and `secret_key` using `parameters`. #[inline] fn void_number( - parameters: &VoidNumberCommitmentParameters<Self>, + parameters: &Self::VoidNumberCommitmentScheme, trapdoor: &Trapdoor<Self>, secret_key: &SecretKey<Self>, ) -> VoidNumber<Self> { - Self::VoidNumberCommitmentScheme::commit(&mut (), parameters, trapdoor, secret_key) + parameters.commit(trapdoor, secret_key, &mut ()) } /// Generates the void number associated to `trapdoor` and `secret_key` using `parameters`. #[inline] fn void_number_var( - cs: &mut Self::Compiler, - parameters: &VoidNumberCommitmentParametersVar<Self>, + parameters: &Self::VoidNumberCommitmentSchemeVar, trapdoor: &TrapdoorVar<Self>, secret_key: &SecretKeyVar<Self>, + cs: &mut Self::Compiler, ) -> VoidNumberVar<Self> { - Self::VoidNumberCommitmentScheme::commit(cs, parameters, trapdoor, secret_key) + parameters.commit(trapdoor, secret_key, cs) } /// Checks that the `utxo` is correctly constructed from the `secret_key`, `public_key`, and /// `asset`, returning the void number for the asset if so. #[inline] fn check_full_asset( - utxo_parameters: &UtxoCommitmentParameters<Self>, - void_number_parameters: &VoidNumberCommitmentParameters<Self>, + parameters: &Parameters<Self>, secret_key: &SecretKey<Self>, public_key: &PublicKey<Self>, asset: &Asset, utxo: &Utxo<Self>, ) -> Option<VoidNumber<Self>> { - let trapdoor = Self::trapdoor(secret_key, public_key); - (&Self::utxo(utxo_parameters, &trapdoor, asset) == utxo) - .then(move || Self::void_number(void_number_parameters, &trapdoor, secret_key)) + let trapdoor = Self::trapdoor( + &parameters.key_agreement, + &parameters.trapdoor_derivation_function, + secret_key, + public_key, + ); + (&Self::utxo(&parameters.utxo_commitment, &trapdoor, asset) == utxo).then(move || { + Self::void_number(&parameters.void_number_commitment, &trapdoor, secret_key) + }) } } @@ -349,93 +367,66 @@ pub type AssetVar<C> = Asset<<C as Configuration>::AssetIdVar, <C as Configurati pub type SecretKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::SecretKey; /// Secret Key Variable Type -pub type SecretKeyVar<C> = <C as Configuration>::SecretKeyVar; +pub type SecretKeyVar<C> = + <<C as Configuration>::KeyAgreementSchemeVar as KeyAgreementScheme<Compiler<C>>>::SecretKey; /// Public Key Type pub type PublicKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::PublicKey; /// Public Key Variable Type -pub type PublicKeyVar<C> = <C as Configuration>::PublicKeyVar; +pub type PublicKeyVar<C> = + <<C as Configuration>::KeyAgreementSchemeVar as KeyAgreementScheme<Compiler<C>>>::PublicKey; /// Shared Secret Type pub type SharedSecret<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret; /// Shared Secret Variable Type -pub type SharedSecretVar<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme< - <C as Configuration>::Compiler, ->>::SharedSecret; - -/// Ephemeral Key Commitment Scheme Parameters Type -pub type EphemeralKeyParameters<C> = - <<C as Configuration>::EphemeralKeyCommitmentScheme as CommitmentScheme>::Parameters; - -/// Ephemeral Key Commitment Scheme Parameters Variable Type -pub type EphemeralKeyParametersVar<C> = <C as Configuration>::EphemeralKeyParametersVar; +pub type SharedSecretVar<C> = + <<C as Configuration>::KeyAgreementSchemeVar as KeyAgreementScheme<Compiler<C>>>::SharedSecret; /// Ephemeral Key Trapdoor Type pub type EphemeralKeyTrapdoor<C> = <<C as Configuration>::EphemeralKeyCommitmentScheme as CommitmentScheme>::Trapdoor; /// Ephemeral Key Trapdoor Variable Type -pub type EphemeralKeyTrapdoorVar<C> = <C as Configuration>::EphemeralKeyTrapdoorVar; +pub type EphemeralKeyTrapdoorVar<C> = + <<C as Configuration>::EphemeralKeyCommitmentSchemeVar as CommitmentScheme<Compiler<C>>>::Trapdoor; /// Trapdoor Type pub type Trapdoor<C> = <<C as Configuration>::UtxoCommitmentScheme as CommitmentScheme>::Trapdoor; /// Trapdoor Variable Type -pub type TrapdoorVar<C> = <<C as Configuration>::UtxoCommitmentScheme as CommitmentScheme< - <C as Configuration>::Compiler, ->>::Trapdoor; - -/// UTXO Commitment Scheme Parameters Type -pub type UtxoCommitmentParameters<C> = - <<C as Configuration>::UtxoCommitmentScheme as CommitmentScheme>::Parameters; - -/// UTXO Commitment Scheme Parameters Variable Type -pub type UtxoCommitmentParametersVar<C> = <C as Configuration>::UtxoCommitmentParametersVar; +pub type TrapdoorVar<C> = + <<C as Configuration>::UtxoCommitmentSchemeVar as CommitmentScheme<Compiler<C>>>::Trapdoor; /// Unspend Transaction Output Type pub type Utxo<C> = <<C as Configuration>::UtxoCommitmentScheme as CommitmentScheme>::Output; /// Unspent Transaction Output Variable Type -pub type UtxoVar<C> = <C as Configuration>::UtxoVar; - -/// UTXO Set Accumulator Verifier Parameters Type -pub type UtxoSetParameters<C> = <<C as Configuration>::UtxoSetVerifier as Verifier>::Parameters; - -/// UTXO Set Accumulator Verifier Parameters Variable Type -pub type UtxoSetParametersVar<C> = <C as Configuration>::UtxoSetParametersVar; +pub type UtxoVar<C> = + <<C as Configuration>::UtxoCommitmentSchemeVar as CommitmentScheme<Compiler<C>>>::Output; /// UTXO Set Witness Type -pub type UtxoSetWitness<C> = <<C as Configuration>::UtxoSetVerifier as Verifier>::Witness; +pub type UtxoSetWitness<C> = <<C as Configuration>::UtxoSetModel as Model>::Witness; /// UTXO Set Output Type -pub type UtxoSetOutput<C> = <<C as Configuration>::UtxoSetVerifier as Verifier>::Output; +pub type UtxoSetOutput<C> = <<C as Configuration>::UtxoSetModel as Model>::Output; /// UTXO Membership Proof Type -pub type UtxoMembershipProof<C> = MembershipProof<<C as Configuration>::UtxoSetVerifier>; +pub type UtxoMembershipProof<C> = MembershipProof<<C as Configuration>::UtxoSetModel>; /// UTXO Membership Proof Variable Type -pub type UtxoMembershipProofVar<C> = accumulator::MembershipProof< - <C as Configuration>::UtxoSetVerifier, - <C as Configuration>::Compiler, ->; - -/// Void Number Commitment Scheme Parameters Type -pub type VoidNumberCommitmentParameters<C> = - <<C as Configuration>::VoidNumberCommitmentScheme as CommitmentScheme>::Parameters; - -/// Void Number Commitment Scheme Parameters Variable Type -pub type VoidNumberCommitmentParametersVar<C> = - <C as Configuration>::VoidNumberCommitmentParametersVar; +pub type UtxoMembershipProofVar<C> = + MembershipProof<<C as Configuration>::UtxoSetModelVar, Compiler<C>>; /// Void Number Type pub type VoidNumber<C> = <<C as Configuration>::VoidNumberCommitmentScheme as CommitmentScheme>::Output; /// Void Number Variable Type -pub type VoidNumberVar<C> = <C as Configuration>::VoidNumberVar; +pub type VoidNumberVar<C> = + <<C as Configuration>::VoidNumberCommitmentSchemeVar as CommitmentScheme<Compiler<C>>>::Output; /// Encrypted Note Type pub type EncryptedNote<C> = EncryptedMessage<<C as Configuration>::NoteEncryptionScheme>; @@ -443,6 +434,9 @@ pub type EncryptedNote<C> = EncryptedMessage<<C as Configuration>::NoteEncryptio /// Decrypted Note Type pub type Note<C> = DecryptedMessage<<C as Configuration>::NoteEncryptionScheme>; +/// Transfer Configuration Compiler Type +pub type Compiler<C> = <C as Configuration>::Compiler; + /// Transfer Proof System Type type ProofSystemType<C> = <C as Configuration>::ProofSystem; @@ -462,75 +456,85 @@ pub type ProofInput<C> = <<C as Configuration>::ProofSystem as ProofSystem>::Inp pub type Proof<C> = <ProofSystemType<C> as ProofSystem>::Proof; /// Transfer Parameters -pub struct Parameters<'p, C> +pub struct Parameters<C> where - C: Configuration, + C: Configuration + ?Sized, { - /// Ephemeral Key Commitment Scheme Parameters - pub ephemeral_key: &'p EphemeralKeyParameters<C>, + /// Key Agreement Scheme + pub key_agreement: C::KeyAgreementScheme, + + /// Ephemeral Key Commitment Scheme + pub ephemeral_key_commitment: C::EphemeralKeyCommitmentScheme, - /// UTXO Commitment Scheme Parameters - pub utxo_commitment: &'p UtxoCommitmentParameters<C>, + /// Trapdoor Derivation Function + pub trapdoor_derivation_function: C::TrapdoorDerivationFunction, - /// Void Number Commitment Scheme Parameters - pub void_number_commitment: &'p VoidNumberCommitmentParameters<C>, + /// UTXO Commitment Scheme + pub utxo_commitment: C::UtxoCommitmentScheme, - /// UTXO Set Verifier Parameters - pub utxo_set_verifier: &'p UtxoSetParameters<C>, + /// Void Number Commitment Scheme + pub void_number_commitment: C::VoidNumberCommitmentScheme, } -impl<'p, C> Parameters<'p, C> +/// Transfer Full Parameters +pub struct FullParameters<'p, C> where C: Configuration, { - /// Builds a new [`Parameters`] from `ephemeral_key`, `utxo_commitment`, - /// `void_number_commitment`, and `utxo_set_verifier` parameters. + /// Base Parameters + pub base: &'p Parameters<C>, + + /// UTXO Set Model + pub utxo_set_model: &'p C::UtxoSetModel, +} + +impl<'p, C> FullParameters<'p, C> +where + C: Configuration, +{ + /// Builds a new [`FullParameters`] from `base` and `utxo_set_model`. #[inline] - pub fn new( - ephemeral_key: &'p EphemeralKeyParameters<C>, - utxo_commitment: &'p UtxoCommitmentParameters<C>, - void_number_commitment: &'p VoidNumberCommitmentParameters<C>, - utxo_set_verifier: &'p UtxoSetParameters<C>, - ) -> Self { + pub fn new(base: &'p Parameters<C>, utxo_set_model: &'p C::UtxoSetModel) -> Self { Self { - ephemeral_key, - utxo_commitment, - void_number_commitment, - utxo_set_verifier, + base, + utxo_set_model, } } } -/// Transfer Parameters Variables -pub struct ParametersVar<'p, C> +/// Transfer Full Parameters Variables +pub struct FullParametersVar<'p, C> where C: Configuration, { - /// Ephemeral Key Commitment Scheme Parameters - pub ephemeral_key: EphemeralKeyParametersVar<C>, + /// Key Agreement Scheme + pub key_agreement: C::KeyAgreementSchemeVar, + + /// Ephemeral Key Commitment Scheme + pub ephemeral_key_commitment: C::EphemeralKeyCommitmentSchemeVar, - /// UTXO Commitment Scheme Parameters - pub utxo_commitment: UtxoCommitmentParametersVar<C>, + /// Trapdoor Derivation Function + pub trapdoor_derivation_function: C::TrapdoorDerivationFunctionVar, - /// Void Number Commitment Scheme Parameters - pub void_number_commitment: VoidNumberCommitmentParametersVar<C>, + /// UTXO Commitment Scheme + pub utxo_commitment: C::UtxoCommitmentSchemeVar, - /// UTXO Set Verifier Parameters - pub utxo_set_verifier: UtxoSetParametersVar<C>, + /// Void Number Commitment Scheme + pub void_number_commitment: C::VoidNumberCommitmentSchemeVar, + + /// UTXO Set Model + pub utxo_set_model: C::UtxoSetModelVar, /// Type Parameter Marker __: PhantomData<&'p ()>, } -impl<'p, C> Variable<C::Compiler> for ParametersVar<'p, C> +impl<'p, C> Variable<C::Compiler> for FullParametersVar<'p, C> where C: Configuration, - EphemeralKeyParameters<C>: 'p, - UtxoCommitmentParameters<C>: 'p, - VoidNumberCommitmentParameters<C>: 'p, - UtxoSetParameters<C>: 'p, + Parameters<C>: 'p, { - type Type = Parameters<'p, C>; + type Type = FullParameters<'p, C>; type Mode = Constant; @@ -538,10 +542,15 @@ where fn new(cs: &mut C::Compiler, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self { - ephemeral_key: this.ephemeral_key.as_known(cs, mode), - utxo_commitment: this.utxo_commitment.as_known(cs, mode), - void_number_commitment: this.void_number_commitment.as_known(cs, mode), - utxo_set_verifier: this.utxo_set_verifier.as_known(cs, mode), + key_agreement: this.base.key_agreement.as_known(cs, mode), + ephemeral_key_commitment: this.base.ephemeral_key_commitment.as_known(cs, mode), + trapdoor_derivation_function: this + .base + .trapdoor_derivation_function + .as_known(cs, mode), + utxo_commitment: this.base.utxo_commitment.as_known(cs, mode), + void_number_commitment: this.base.void_number_commitment.as_known(cs, mode), + utxo_set_model: this.utxo_set_model.as_known(cs, mode), __: PhantomData, }, _ => unreachable!("Constant variables cannot be unknown."), @@ -575,17 +584,21 @@ where /// Derives the receiving key for `self`. #[inline] - pub fn derive(&self) -> ReceivingKey<C> { + pub fn derive(&self, parameters: &C::KeyAgreementScheme) -> ReceivingKey<C> { ReceivingKey { - spend: C::KeyAgreementScheme::derive(&mut (), &self.spend), - view: C::KeyAgreementScheme::derive(&mut (), &self.view), + spend: parameters.derive(&self.spend, &mut ()), + view: parameters.derive(&self.view, &mut ()), } } /// Tries to decrypt `encrypted_note` with the viewing key associated to `self`. #[inline] - pub fn decrypt(&self, encrypted_note: EncryptedNote<C>) -> Result<Note<C>, EncryptedNote<C>> { - encrypted_note.decrypt(&self.view) + pub fn decrypt( + &self, + key_agreement: &C::KeyAgreementScheme, + encrypted_note: EncryptedNote<C>, + ) -> Result<Note<C>, EncryptedNote<C>> { + encrypted_note.decrypt(key_agreement, &Default::default(), &self.view) } /// Validates the `utxo` against `self` and the given `ephemeral_key` and `asset`, returning @@ -593,53 +606,36 @@ where #[inline] pub fn check_full_asset( &self, - utxo_parameters: &UtxoCommitmentParameters<C>, - void_number_parameters: &VoidNumberCommitmentParameters<C>, + parameters: &Parameters<C>, ephemeral_key: &PublicKey<C>, asset: &Asset, utxo: &Utxo<C>, ) -> Option<VoidNumber<C>> { - C::check_full_asset( - utxo_parameters, - void_number_parameters, - &self.spend, - ephemeral_key, - asset, - utxo, - ) + C::check_full_asset(parameters, &self.spend, ephemeral_key, asset, utxo) } /// Prepares `self` for spending `asset` with the given `ephemeral_key`. #[inline] pub fn sender( &self, - utxo_parameters: &UtxoCommitmentParameters<C>, - void_number_parameters: &VoidNumberCommitmentParameters<C>, + parameters: &Parameters<C>, ephemeral_key: PublicKey<C>, asset: Asset, ) -> PreSender<C> { // TODO: See if this clone is really needed. - PreSender::new( - utxo_parameters, - void_number_parameters, - self.spend.clone(), - ephemeral_key, - asset, - ) + PreSender::new(parameters, self.spend.clone(), ephemeral_key, asset) } /// Prepares `self` for receiving `asset`. #[inline] pub fn receiver( &self, - ephemeral_key_parameters: &EphemeralKeyParameters<C>, - utxo_parameters: &UtxoCommitmentParameters<C>, + parameters: &Parameters<C>, ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, asset: Asset, ) -> Receiver<C> { - self.derive().into_receiver( - ephemeral_key_parameters, - utxo_parameters, + self.derive(&parameters.key_agreement).into_receiver( + parameters, ephemeral_key_trapdoor, asset, ) @@ -649,24 +645,12 @@ where #[inline] pub fn internal_pair( &self, - ephemeral_key_parameters: &EphemeralKeyParameters<C>, - utxo_parameters: &UtxoCommitmentParameters<C>, - void_number_parameters: &VoidNumberCommitmentParameters<C>, + parameters: &Parameters<C>, ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, asset: Asset, ) -> (Receiver<C>, PreSender<C>) { - let receiver = self.receiver( - ephemeral_key_parameters, - utxo_parameters, - ephemeral_key_trapdoor, - asset, - ); - let sender = self.sender( - utxo_parameters, - void_number_parameters, - receiver.ephemeral_public_key().clone(), - asset, - ); + let receiver = self.receiver(parameters, ephemeral_key_trapdoor, asset); + let sender = self.sender(parameters, receiver.ephemeral_public_key().clone(), asset); (receiver, sender) } @@ -674,19 +658,11 @@ where #[inline] pub fn internal_zero_pair( &self, - ephemeral_key_parameters: &EphemeralKeyParameters<C>, - utxo_parameters: &UtxoCommitmentParameters<C>, - void_number_parameters: &VoidNumberCommitmentParameters<C>, + parameters: &Parameters<C>, ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, asset_id: AssetId, ) -> (Receiver<C>, PreSender<C>) { - self.internal_pair( - ephemeral_key_parameters, - utxo_parameters, - void_number_parameters, - ephemeral_key_trapdoor, - Asset::zero(asset_id), - ) + self.internal_pair(parameters, ephemeral_key_trapdoor, Asset::zero(asset_id)) } } @@ -719,14 +695,12 @@ where #[inline] pub fn into_receiver( self, - ephemeral_key_parameters: &EphemeralKeyParameters<C>, - utxo_parameters: &UtxoCommitmentParameters<C>, + parameters: &Parameters<C>, ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, asset: Asset, ) -> Receiver<C> { Receiver::new( - ephemeral_key_parameters, - utxo_parameters, + parameters, ephemeral_key_trapdoor, self.spend, self.view, @@ -763,16 +737,20 @@ where /// Builds a new [`PreSender`] for `spend` to spend `asset` with `ephemeral_public_key`. #[inline] pub fn new( - utxo_parameters: &UtxoCommitmentParameters<C>, - void_number_parameters: &VoidNumberCommitmentParameters<C>, + parameters: &Parameters<C>, spend: SecretKey<C>, ephemeral_public_key: PublicKey<C>, asset: Asset, ) -> Self { - let trapdoor = C::trapdoor(&spend, &ephemeral_public_key); + let trapdoor = C::trapdoor( + &parameters.key_agreement, + &parameters.trapdoor_derivation_function, + &spend, + &ephemeral_public_key, + ); Self { - utxo: C::utxo(utxo_parameters, &trapdoor, &asset), - void_number: C::void_number(void_number_parameters, &trapdoor, &spend), + utxo: C::utxo(&parameters.utxo_commitment, &trapdoor, &asset), + void_number: C::void_number(&parameters.void_number_commitment, &trapdoor, &spend), spend, ephemeral_public_key, asset, @@ -784,7 +762,7 @@ where #[inline] pub fn insert_utxo<S>(&self, utxo_set: &mut S) -> bool where - S: Accumulator<Item = Utxo<C>, Verifier = C::UtxoSetVerifier>, + S: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, { utxo_set.insert(&self.utxo) } @@ -794,7 +772,7 @@ where #[inline] pub fn get_proof<S>(&self, utxo_set: &S) -> Option<SenderProof<C>> where - S: Accumulator<Item = Utxo<C>, Verifier = C::UtxoSetVerifier>, + S: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, { Some(SenderProof { utxo_membership_proof: utxo_set.prove(&self.utxo)?, @@ -818,7 +796,7 @@ where #[inline] pub fn try_upgrade<S>(self, utxo_set: &S) -> Option<Sender<C>> where - S: Accumulator<Item = Utxo<C>, Verifier = C::UtxoSetVerifier>, + S: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, { Some(self.get_proof(utxo_set)?.upgrade(self)) } @@ -936,22 +914,26 @@ where #[inline] pub fn get_well_formed_asset( self, - parameters: &ParametersVar<C>, + parameters: &FullParametersVar<C>, cs: &mut C::Compiler, ) -> AssetVar<C> { - let trapdoor = C::trapdoor_var(cs, &self.spend, &self.ephemeral_public_key); - let utxo = C::utxo_var(cs, &parameters.utxo_commitment, &trapdoor, &self.asset); - let is_valid_proof = self.utxo_membership_proof.verify_with_compiler( - &parameters.utxo_set_verifier, - &utxo, + let trapdoor = C::trapdoor_var( + &parameters.key_agreement, + &parameters.trapdoor_derivation_function, + &self.spend, + &self.ephemeral_public_key, cs, ); + let utxo = C::utxo_var(&parameters.utxo_commitment, &trapdoor, &self.asset, cs); + let is_valid_proof = + self.utxo_membership_proof + .verify_with_compiler(&parameters.utxo_set_model, &utxo, cs); cs.assert(is_valid_proof); let void_number = C::void_number_var( - cs, &parameters.void_number_commitment, &trapdoor, &self.spend, + cs, ); cs.assert_eq(&self.void_number, &void_number); self.asset @@ -1011,7 +993,7 @@ where /// called before [`is_unspent`](Self::is_unspent) and /// [`has_matching_utxo_set_output`](Self::has_matching_utxo_set_output). /// - /// [`S::Output`]: Verifier::Output + /// [`S::Output`]: Model::Output type ValidUtxoSetOutput; /// Super Posting Key @@ -1157,18 +1139,33 @@ where /// Builds a new [`Receiver`] for `spend` to receive `asset` with `ephemeral_secret_key`. #[inline] pub fn new( - ephemeral_key_parameters: &EphemeralKeyParameters<C>, - utxo_parameters: &UtxoCommitmentParameters<C>, + parameters: &Parameters<C>, ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, spend: PublicKey<C>, view: PublicKey<C>, asset: Asset, ) -> Self { - let ephemeral_secret_key = - C::ephemeral_secret_key(ephemeral_key_parameters, &ephemeral_key_trapdoor, asset); + let ephemeral_secret_key = C::ephemeral_secret_key( + &parameters.ephemeral_key_commitment, + &ephemeral_key_trapdoor, + asset, + ); Self { - utxo: C::full_utxo(utxo_parameters, &ephemeral_secret_key, &spend, &asset), - note: EncryptedMessage::new(&view, &ephemeral_secret_key, asset), + utxo: C::full_utxo( + &parameters.key_agreement, + &parameters.trapdoor_derivation_function, + &parameters.utxo_commitment, + &ephemeral_secret_key, + &spend, + &asset, + ), + note: EncryptedMessage::new( + &parameters.key_agreement, + &Default::default(), + &view, + &ephemeral_secret_key, + asset, + ), ephemeral_key_trapdoor, spend, asset, @@ -1221,23 +1218,26 @@ where #[inline] pub fn get_well_formed_asset( self, - parameters: &ParametersVar<C>, + parameters: &FullParametersVar<C>, cs: &mut C::Compiler, ) -> AssetVar<C> { let ephemeral_secret_key = C::ephemeral_secret_key_var( - cs, - &parameters.ephemeral_key, + &parameters.ephemeral_key_commitment, &self.ephemeral_key_trapdoor, &self.asset, + cs, ); - let ephemeral_public_key = C::ephemeral_public_key_var(cs, &ephemeral_secret_key); + let ephemeral_public_key = + C::ephemeral_public_key_var(&parameters.key_agreement, &ephemeral_secret_key, cs); cs.assert_eq(&self.ephemeral_public_key, &ephemeral_public_key); let utxo = C::full_utxo_var( - cs, + &parameters.key_agreement, + &parameters.trapdoor_derivation_function, &parameters.utxo_commitment, &ephemeral_secret_key, &self.spend, &self.asset, + cs, ); cs.assert_eq(&self.utxo, &utxo); self.asset @@ -1502,7 +1502,7 @@ where /// Generates a proving and verifying context for this transfer shape. #[inline] pub fn generate_context<R>( - parameters: Parameters<C>, + parameters: FullParameters<C>, rng: &mut R, ) -> Result<(ProvingContext<C>, VerifyingContext<C>), ProofSystemError<C>> where @@ -1516,9 +1516,9 @@ where /// Converts `self` into its ledger post. #[inline] - pub fn into_post<'p, R>( + pub fn into_post<R>( self, - parameters: Parameters<'p, C>, + parameters: FullParameters<C>, context: &ProvingContext<C>, rng: &mut R, ) -> Result<TransferPost<C>, ProofSystemError<C>> @@ -1579,7 +1579,7 @@ where { /// Builds constraints for the [`Transfer`] validity proof. #[inline] - fn build_validity_constraints(self, parameters: &ParametersVar<C>, cs: &mut C::Compiler) { + fn build_validity_constraints(self, parameters: &FullParametersVar<C>, cs: &mut C::Compiler) { let mut secret_asset_ids = Vec::with_capacity(SENDERS + RECEIVERS); let input_sum = Self::value_sum( diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index c6cd6e3c2..61ef90bf1 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -32,10 +32,9 @@ use crate::{ canonical::{ Mint, PrivateTransfer, PrivateTransferShape, Reclaim, Selection, Shape, Transaction, }, - EncryptedNote, EphemeralKeyParameters, Parameters, PreSender, ProofSystemError, - ProvingContext, PublicKey, Receiver, ReceivingKey, SecretKey, Sender, SpendingKey, - Transfer, TransferPost, Utxo, UtxoCommitmentParameters, VoidNumber, - VoidNumberCommitmentParameters, + EncryptedNote, FullParameters, Parameters, PreSender, ProofSystemError, ProvingContext, + PublicKey, Receiver, ReceivingKey, SecretKey, Sender, SpendingKey, Transfer, TransferPost, + Utxo, VoidNumber, }, }; use alloc::{vec, vec::Vec}; @@ -46,7 +45,6 @@ use core::{ use manta_crypto::{ accumulator::{ Accumulator, ConstantCapacityAccumulator, ExactSizeAccumulator, OptimizedAccumulator, - Verifier, }, encryption::DecryptedMessage, rand::{CryptoRng, Rand, RngCore}, @@ -210,10 +208,8 @@ pub trait Configuration: transfer::Configuration { >; /// [`Utxo`] Accumulator Type - type UtxoSet: Accumulator< - Item = <Self::UtxoSetVerifier as Verifier>::Item, - Verifier = Self::UtxoSetVerifier, - > + ConstantCapacityAccumulator + type UtxoSet: Accumulator<Item = Self::Utxo, Model = Self::UtxoSetModel> + + ConstantCapacityAccumulator + ExactSizeAccumulator + OptimizedAccumulator + Rollback; @@ -255,14 +251,8 @@ where /// Proving Context proving_context: ProvingContext<C>, - /// Ephemeral Key Parameters - ephemeral_key_parameters: EphemeralKeyParameters<C>, - - /// UTXO Commitment Scheme Parameters - utxo_commitment_parameters: UtxoCommitmentParameters<C>, - - /// Void Number Commitment Scheme Parameters - void_number_commitment_parameters: VoidNumberCommitmentParameters<C>, + /// Parameters + parameters: Parameters<C>, /// UTXO Set utxo_set: C::UtxoSet, @@ -279,14 +269,11 @@ where C: Configuration, { /// Builds a new [`Signer`]. - #[allow(clippy::too_many_arguments)] // FIXME: Find a better way to construct this. #[inline] fn new_inner( account_table: AccountTable<C>, proving_context: ProvingContext<C>, - ephemeral_key_parameters: EphemeralKeyParameters<C>, - utxo_commitment_parameters: UtxoCommitmentParameters<C>, - void_number_commitment_parameters: VoidNumberCommitmentParameters<C>, + parameters: Parameters<C>, utxo_set: C::UtxoSet, assets: C::AssetMap, rng: C::Rng, @@ -294,9 +281,7 @@ where Self { account_table, proving_context, - ephemeral_key_parameters, - utxo_commitment_parameters, - void_number_commitment_parameters, + parameters, utxo_set, assets, rng, @@ -313,18 +298,14 @@ where pub fn new( account_table: AccountTable<C>, proving_context: ProvingContext<C>, - ephemeral_key_parameters: EphemeralKeyParameters<C>, - utxo_commitment_parameters: UtxoCommitmentParameters<C>, - void_number_commitment_parameters: VoidNumberCommitmentParameters<C>, + parameters: Parameters<C>, utxo_set: C::UtxoSet, rng: C::Rng, ) -> Self { Self::new_inner( account_table, proving_context, - ephemeral_key_parameters, - utxo_commitment_parameters, - void_number_commitment_parameters, + parameters, utxo_set, Default::default(), rng, @@ -359,12 +340,11 @@ where }, }) = self .account() - .find_index(move |k| finder.decrypt(k)) + .find_index(|k| finder.decrypt(&self.parameters.key_agreement, &Default::default(), k)) .map_err(key::Error::KeyDerivationError)? { if let Some(void_number) = C::check_full_asset( - &self.utxo_commitment_parameters, - &self.void_number_commitment_parameters, + &self.parameters, &keypair.spend, &ephemeral_public_key, &asset, @@ -447,8 +427,7 @@ where ) -> Result<PreSender<C>, SignerError<C>> { let (spend_index, ephemeral_key) = key; Ok(PreSender::new( - &self.utxo_commitment_parameters, - &self.void_number_commitment_parameters, + &self.parameters, self.account().spend_key(spend_index)?, ephemeral_key, asset, @@ -463,8 +442,7 @@ where .default_keypair() .map_err(key::Error::KeyDerivationError)?; Ok(SpendingKey::new(keypair.spend, keypair.view).receiver( - &self.ephemeral_key_parameters, - &self.utxo_commitment_parameters, + &self.parameters, self.rng.gen(), asset, )) @@ -480,9 +458,7 @@ where .default_keypair() .map_err(key::Error::KeyDerivationError)?; let (receiver, pre_sender) = SpendingKey::new(keypair.spend, keypair.view).internal_pair( - &self.ephemeral_key_parameters, - &self.utxo_commitment_parameters, - &self.void_number_commitment_parameters, + &self.parameters, self.rng.gen(), asset, ); @@ -514,12 +490,7 @@ where ) -> Result<TransferPost<C>, SignerError<C>> { transfer .into_post( - Parameters::new( - &self.ephemeral_key_parameters, - &self.utxo_commitment_parameters, - &self.void_number_commitment_parameters, - self.utxo_set.parameters(), - ), + FullParameters::new(&self.parameters, self.utxo_set.model()), &self.proving_context, &mut self.rng, ) @@ -539,9 +510,7 @@ where .default_keypair() .map_err(key::Error::KeyDerivationError)?; Ok(Join::new( - &self.ephemeral_key_parameters, - &self.utxo_commitment_parameters, - &self.void_number_commitment_parameters, + &self.parameters, asset_id.with(total), &SpendingKey::new(keypair.spend, keypair.view), &mut self.rng, @@ -637,12 +606,7 @@ where /// Prepares a given [`ReceivingKey`] for receiving `asset`. #[inline] pub fn prepare_receiver(&mut self, asset: Asset, receiver: ReceivingKey<C>) -> Receiver<C> { - receiver.into_receiver( - &self.ephemeral_key_parameters, - &self.utxo_commitment_parameters, - self.rng.gen(), - asset, - ) + receiver.into_receiver(&self.parameters, self.rng.gen(), asset) } /// Signs a withdraw transaction for `asset` sent to `receiver`. @@ -699,7 +663,7 @@ where index: Index<C>, ) -> ReceivingKeyResult<C::HierarchicalKeyDerivationScheme, C, Self> { let keypair = self.account().keypair(index)?; - Ok(SpendingKey::new(keypair.spend, keypair.view).derive()) + Ok(SpendingKey::new(keypair.spend, keypair.view).derive(&self.parameters.key_agreement)) } } diff --git a/manta-crypto/src/accumulator.rs b/manta-crypto/src/accumulator.rs index 04f83a913..a4951b935 100644 --- a/manta-crypto/src/accumulator.rs +++ b/manta-crypto/src/accumulator.rs @@ -18,11 +18,8 @@ // TODO: See if we can modify `Accumulator` so that it can extend the `Verifier` trait directly. -/// Accumulator Membership Verifier -pub trait Verifier<J = ()> { - /// Parameters Type - type Parameters; - +/// Accumulator Membership Model +pub trait Model<COM = ()> { /// Item Type type Item: ?Sized; @@ -40,30 +37,27 @@ pub trait Verifier<J = ()> { /// Verifies that `item` is stored in a known accumulator with accumulated `output` and /// membership `witness`. fn verify( - compiler: &mut J, - parameters: &Self::Parameters, + &self, item: &Self::Item, witness: &Self::Witness, output: &Self::Output, + compiler: &mut COM, ) -> Self::Verification; } -/// Accumulator Parameters Type -pub type Parameters<A> = <<A as Accumulator>::Verifier as Verifier>::Parameters; - /// Accumulator Output Type -pub type Output<A> = <<A as Accumulator>::Verifier as Verifier>::Output; +pub type Output<A> = <<A as Accumulator>::Model as Model>::Output; /// Accumulator pub trait Accumulator { /// Item Type type Item: ?Sized; - /// Verifier Type - type Verifier: Verifier<Item = Self::Item> + ?Sized; + /// Model Type + type Model: Model<Item = Self::Item> + ?Sized; - /// Returns the parameters associated with the verifier attached to `self`. - fn parameters(&self) -> &Parameters<Self>; + /// Returns the model associated with `self`. + fn model(&self) -> &Self::Model; /// Inserts `item` into `self` with the guarantee that `self` can later return a valid /// membership proof for `item` with a call to [`prove`](Self::prove). This method returns @@ -80,7 +74,7 @@ pub trait Accumulator { } /// Returns a membership proof for `item` if it is contained in `self`. - fn prove(&self, item: &Self::Item) -> Option<MembershipProof<Self::Verifier>>; + fn prove(&self, item: &Self::Item) -> Option<MembershipProof<Self::Model>>; /// Returns `true` if `item` is stored in `self`. /// @@ -102,11 +96,11 @@ where { type Item = A::Item; - type Verifier = A::Verifier; + type Model = A::Model; #[inline] - fn parameters(&self) -> &Parameters<Self> { - (**self).parameters() + fn model(&self) -> &Self::Model { + (**self).model() } #[inline] @@ -115,7 +109,7 @@ where } #[inline] - fn prove(&self, item: &Self::Item) -> Option<MembershipProof<Self::Verifier>> { + fn prove(&self, item: &Self::Item) -> Option<MembershipProof<Self::Model>> { (**self).prove(item) } @@ -182,54 +176,53 @@ pub trait OptimizedAccumulator: Accumulator { } /// Accumulator Membership Proof -pub struct MembershipProof<V, J = ()> +pub struct MembershipProof<M, COM = ()> where - V: Verifier<J> + ?Sized, + M: Model<COM> + ?Sized, { /// Secret Membership Witness - witness: V::Witness, + witness: M::Witness, /// Accumulator Output - output: V::Output, + output: M::Output, } -impl<V, J> MembershipProof<V, J> +impl<M, COM> MembershipProof<M, COM> where - V: Verifier<J> + ?Sized, + M: Model<COM> + ?Sized, { /// Builds a new [`MembershipProof`] from `witness` and `output`. #[inline] - pub fn new(witness: V::Witness, output: V::Output) -> Self { + pub fn new(witness: M::Witness, output: M::Output) -> Self { Self { witness, output } } - /// Returns the accumulated output part of `self`, dropping the - /// [`V::Witness`](Verifier::Witness). + /// Returns the accumulated output part of `self`, dropping the [`M::Witness`](Model::Witness). #[inline] - pub fn into_output(self) -> V::Output { + pub fn into_output(self) -> M::Output { self.output } - /// Verifies that `item` is stored in a known accumulator using `parameters`. + /// Verifies that `item` is stored in a known accumulator using `model`. #[inline] pub fn verify_with_compiler( &self, - parameters: &V::Parameters, - item: &V::Item, - compiler: &mut J, - ) -> V::Verification { - V::verify(compiler, parameters, item, &self.witness, &self.output) + model: &M, + item: &M::Item, + compiler: &mut COM, + ) -> M::Verification { + model.verify(item, &self.witness, &self.output, compiler) } } -impl<V> MembershipProof<V> +impl<M> MembershipProof<M> where - V: Verifier + ?Sized, + M: Model + ?Sized, { - /// Verifies that `item` is stored in a known accumulator using `parameters`. + /// Verifies that `item` is stored in a known accumulator using `model`. #[inline] - pub fn verify(&self, parameters: &V::Parameters, item: &V::Item) -> V::Verification { - self.verify_with_compiler(parameters, item, &mut ()) + pub fn verify(&self, model: &M, item: &M::Item) -> M::Verification { + self.verify_with_compiler(model, item, &mut ()) } } @@ -286,29 +279,32 @@ pub mod constraint { type Unknown = MembershipProofModeEntry<WitnessMode::Unknown, OutputMode::Unknown>; } - impl<V, J> Variable<J> for MembershipProof<V, J> + impl<M, COM> Variable<COM> for MembershipProof<M, COM> where - V: Verifier + Verifier<J>, - <V as Verifier<J>>::Witness: Variable<J, Type = <V as Verifier>::Witness>, - <V as Verifier<J>>::Output: Variable<J, Type = <V as Verifier>::Output>, + M: Model<COM> + Variable<COM>, + <M as Variable<COM>>::Type: Model, + <M as Model<COM>>::Witness: + Variable<COM, Type = <<M as Variable<COM>>::Type as Model>::Witness>, + <M as Model<COM>>::Output: + Variable<COM, Type = <<M as Variable<COM>>::Type as Model>::Output>, { - type Type = MembershipProof<V>; + type Type = MembershipProof<<M as Variable<COM>>::Type>; type Mode = MembershipProofMode< - <<V as Verifier<J>>::Witness as Variable<J>>::Mode, - <<V as Verifier<J>>::Output as Variable<J>>::Mode, + <<M as Model<COM>>::Witness as Variable<COM>>::Mode, + <<M as Model<COM>>::Output as Variable<COM>>::Mode, >; #[inline] - fn new(cs: &mut J, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + fn new(cs: &mut COM, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self::new( this.witness.as_known(cs, mode.witness), this.output.as_known(cs, mode.output), ), Allocation::Unknown(mode) => Self::new( - <V as Verifier<J>>::Witness::new_unknown(cs, mode.witness), - <V as Verifier<J>>::Output::new_unknown(cs, mode.output), + <M as Model<COM>>::Witness::new_unknown(cs, mode.witness), + <M as Model<COM>>::Output::new_unknown(cs, mode.output), ), } } @@ -328,7 +324,7 @@ pub mod test { pub fn assert_provable_membership<A>(accumulator: &mut A, item: &A::Item) -> Output<A> where A: Accumulator, - A::Verifier: Verifier<Verification = bool>, + A::Model: Model<Verification = bool>, { assert!( accumulator.insert(item), @@ -340,7 +336,7 @@ pub mod test { ); if let Some(proof) = accumulator.prove(item) { assert!( - proof.verify(accumulator.parameters(), item), + proof.verify(accumulator.model(), item), "Invalid proof returned for inserted item." ); proof.into_output() @@ -356,7 +352,7 @@ pub mod test { where A: Accumulator, A::Item: 'i, - A::Verifier: Verifier<Verification = bool>, + A::Model: Model<Verification = bool>, Output<A>: Debug + PartialEq, I: IntoIterator<Item = &'i A::Item>, { diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index e154e5d24..72899b676 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -19,10 +19,7 @@ use core::{fmt::Debug, hash::Hash}; /// Commitment Scheme -pub trait CommitmentScheme<J = ()> { - /// Parameters Type - type Parameters; - +pub trait CommitmentScheme<COM = ()> { /// Trapdoor Type type Trapdoor; @@ -32,24 +29,21 @@ pub trait CommitmentScheme<J = ()> { /// Output Type type Output; - /// Commits to the `input` value using `parameters` and randomness `trapdoor`. + /// Commits to the `input` value using randomness `trapdoor`. fn commit( - compiler: &mut J, - parameters: &Self::Parameters, + &self, trapdoor: &Self::Trapdoor, input: &Self::Input, + compiler: &mut COM, ) -> Self::Output; /// Starts a new [`Builder`] for extended commitments. #[inline] - fn start<'c>( - parameters: &'c Self::Parameters, - trapdoor: &'c Self::Trapdoor, - ) -> Builder<'c, Self, J> + fn start<'c>(&'c self, trapdoor: &'c Self::Trapdoor) -> Builder<'c, Self, COM> where Self::Input: Default, { - Builder::new(parameters, trapdoor) + Builder::new(self, trapdoor) } } @@ -67,18 +61,18 @@ where #[derivative( Clone(bound = "C::Input: Clone"), Copy(bound = "C::Input: Copy"), - Debug(bound = "C::Parameters: Debug, C::Trapdoor: Debug, C::Input: Debug"), - Eq(bound = "C::Parameters: Eq, C::Trapdoor: Eq, C::Input: Eq"), - Hash(bound = "C::Parameters: Hash, C::Trapdoor: Hash, C::Input: Hash"), - PartialEq(bound = "C::Parameters: PartialEq, C::Trapdoor: PartialEq, C::Input: PartialEq") + Debug(bound = "C: Debug, C::Trapdoor: Debug, C::Input: Debug"), + Eq(bound = "C: Eq, C::Trapdoor: Eq, C::Input: Eq"), + Hash(bound = "C: Hash, C::Trapdoor: Hash, C::Input: Hash"), + PartialEq(bound = "C: PartialEq, C::Trapdoor: PartialEq, C::Input: PartialEq") )] -pub struct Builder<'c, C, J = ()> +pub struct Builder<'c, C, COM = ()> where - C: CommitmentScheme<J> + ?Sized, + C: CommitmentScheme<COM> + ?Sized, C::Input: Default, { - /// Commitment Parameters - parameters: &'c C::Parameters, + /// Commitment Scheme + commitment_scheme: &'c C, /// Commitment Trapdoor trapdoor: &'c C::Trapdoor, @@ -87,16 +81,16 @@ where input: C::Input, } -impl<'c, C, J> Builder<'c, C, J> +impl<'c, C, COM> Builder<'c, C, COM> where - C: CommitmentScheme<J> + ?Sized, + C: CommitmentScheme<COM> + ?Sized, C::Input: Default, { - /// Returns a new [`Builder`] with fixed `parameters` and `trapdoor`. + /// Returns a new [`Builder`] with fixed `commitment_scheme` and `trapdoor`. #[inline] - pub fn new(parameters: &'c C::Parameters, trapdoor: &'c C::Trapdoor) -> Self { + pub fn new(commitment_scheme: &'c C, trapdoor: &'c C::Trapdoor) -> Self { Self { - parameters, + commitment_scheme, trapdoor, input: Default::default(), } @@ -131,8 +125,9 @@ where /// Commits to the input stored in the builder. #[inline] - pub fn commit_with_compiler(self, compiler: &mut J) -> C::Output { - C::commit(compiler, self.parameters, self.trapdoor, &self.input) + pub fn commit_with_compiler(self, compiler: &mut COM) -> C::Output { + self.commitment_scheme + .commit(self.trapdoor, &self.input, compiler) } } diff --git a/manta-crypto/src/ecc.rs b/manta-crypto/src/ecc.rs new file mode 100644 index 000000000..945860b0c --- /dev/null +++ b/manta-crypto/src/ecc.rs @@ -0,0 +1,44 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Elliptic Curve Cryptography + +// TODO: Improve ECC abstractions over arkworks. + +/// +pub trait Coordinates<C, J = ()> +where + C: Curve<J>, +{ +} + +/// Elliptic Curve +pub trait Curve<J = ()> { + /// Base Field + type BaseField; + + /// Scalar Field + type ScalarField; +} + +/// Embedded Elliptic Curve +pub trait EmbeddedCurve<C, J = ()>: Curve<J, BaseField = C::ScalarField> +where + C: Curve<J>, +{ + /// + fn lift_scalar(compiler: &mut J, scalar: Self::ScalarField) -> C::ScalarField; +} diff --git a/manta-crypto/src/encryption.rs b/manta-crypto/src/encryption.rs index eb1d7e641..7448ed09c 100644 --- a/manta-crypto/src/encryption.rs +++ b/manta-crypto/src/encryption.rs @@ -68,11 +68,13 @@ pub trait HybridPublicKeyEncryptionScheme: SymmetricKeyEncryptionScheme { /// This method is an optimization path for calling [`KeyAgreementScheme::agree`] and then /// [`KeyDerivationFunction::derive`]. #[inline] - fn agree_derive(secret_key: &SecretKey<Self>, public_key: &PublicKey<Self>) -> Self::Key { - Self::KeyDerivationFunction::derive( - &mut (), - Self::KeyAgreementScheme::agree(&mut (), secret_key, public_key), - ) + fn agree_derive( + agreement: &Self::KeyAgreementScheme, + derivation: &Self::KeyDerivationFunction, + secret_key: &SecretKey<Self>, + public_key: &PublicKey<Self>, + ) -> Self::Key { + derivation.derive(agreement.agree(secret_key, public_key, &mut ()), &mut ()) } } @@ -159,13 +161,18 @@ where /// and an `ephemeral_secret_key`. #[inline] pub fn new( + agreement: &H::KeyAgreementScheme, + derivation: &H::KeyDerivationFunction, public_key: &PublicKey<H>, ephemeral_secret_key: &SecretKey<H>, plaintext: H::Plaintext, ) -> Self { Self { - ciphertext: H::encrypt(H::agree_derive(ephemeral_secret_key, public_key), plaintext), - ephemeral_public_key: H::KeyAgreementScheme::derive(&mut (), ephemeral_secret_key), + ciphertext: H::encrypt( + H::agree_derive(agreement, derivation, ephemeral_secret_key, public_key), + plaintext, + ), + ephemeral_public_key: agreement.derive(ephemeral_secret_key, &mut ()), } } @@ -178,9 +185,19 @@ where /// Tries to decrypt `self` using `secret_key`, returning back `Err(self)` if the `secret_key` /// was unable to decrypt the message. #[inline] - pub fn decrypt(self, secret_key: &SecretKey<H>) -> Result<DecryptedMessage<H>, Self> { + pub fn decrypt( + self, + agreement: &H::KeyAgreementScheme, + derivation: &H::KeyDerivationFunction, + secret_key: &SecretKey<H>, + ) -> Result<DecryptedMessage<H>, Self> { match H::decrypt( - H::agree_derive(secret_key, &self.ephemeral_public_key), + H::agree_derive( + agreement, + derivation, + secret_key, + &self.ephemeral_public_key, + ), &self.ciphertext, ) { Some(plaintext) => Ok(DecryptedMessage::new(plaintext, self.ephemeral_public_key)), @@ -251,9 +268,14 @@ where /// Tries to decrypt with `secret_key`, if `self` still contains a message. #[inline] - pub fn decrypt(&mut self, secret_key: &SecretKey<H>) -> Option<DecryptedMessage<H>> { + pub fn decrypt( + &mut self, + agreement: &H::KeyAgreementScheme, + derivation: &H::KeyDerivationFunction, + secret_key: &SecretKey<H>, + ) -> Option<DecryptedMessage<H>> { if let Some(message) = self.encrypted_message.take() { - match message.decrypt(secret_key) { + match message.decrypt(agreement, derivation, secret_key) { Ok(decrypted_message) => return Some(decrypted_message), Err(message) => self.encrypted_message = Some(message), } diff --git a/manta-crypto/src/key.rs b/manta-crypto/src/key.rs index ffe7a7897..93dfa2e44 100644 --- a/manta-crypto/src/key.rs +++ b/manta-crypto/src/key.rs @@ -17,7 +17,7 @@ //! Cryptographic Key Primitives /// Key Derivation Function -pub trait KeyDerivationFunction<J = ()> { +pub trait KeyDerivationFunction<COM = ()> { /// Input Key Type type Key; @@ -25,7 +25,7 @@ pub trait KeyDerivationFunction<J = ()> { type Output; /// Derives an output key from `secret` computed from a cryptographic agreement scheme. - fn derive(compiler: &mut J, secret: Self::Key) -> Self::Output; + fn derive(&self, secret: Self::Key, compiler: &mut COM) -> Self::Output; } /// Key Agreement Scheme @@ -43,7 +43,7 @@ pub trait KeyDerivationFunction<J = ()> { /// ``` /// This ensures that both parties in the shared computation will arrive at the same conclusion /// about the value of the [`SharedSecret`](Self::SharedSecret). -pub trait KeyAgreementScheme<J = ()> { +pub trait KeyAgreementScheme<COM = ()> { /// Secret Key Type type SecretKey; @@ -55,7 +55,7 @@ pub trait KeyAgreementScheme<J = ()> { /// Derives a public key corresponding to `secret_key`. This public key should be sent to the /// other party involved in the shared computation. - fn derive(compiler: &mut J, secret_key: &Self::SecretKey) -> Self::PublicKey; + fn derive(&self, secret_key: &Self::SecretKey, compiler: &mut COM) -> Self::PublicKey; /// Derives a public key corresponding to `secret_key`. This public key should be sent to the /// other party involved in the shared computation. @@ -68,15 +68,16 @@ pub trait KeyAgreementScheme<J = ()> { /// /// [`derive`]: Self::derive #[inline] - fn derive_owned(compiler: &mut J, secret_key: Self::SecretKey) -> Self::PublicKey { - Self::derive(compiler, &secret_key) + fn derive_owned(&self, secret_key: Self::SecretKey, compiler: &mut COM) -> Self::PublicKey { + self.derive(&secret_key, compiler) } /// Computes the shared secret given the known `secret_key` and the given `public_key`. fn agree( - compiler: &mut J, + &self, secret_key: &Self::SecretKey, public_key: &Self::PublicKey, + compiler: &mut COM, ) -> Self::SharedSecret; /// Computes the shared secret given the known `secret_key` and the given `public_key`. @@ -90,10 +91,11 @@ pub trait KeyAgreementScheme<J = ()> { /// [`agree`]: Self::agree #[inline] fn agree_owned( - compiler: &mut J, + &self, secret_key: Self::SecretKey, public_key: Self::PublicKey, + compiler: &mut COM, ) -> Self::SharedSecret { - Self::agree(compiler, &secret_key, &public_key) + self.agree(&secret_key, &public_key, compiler) } } diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index b6b76bb00..c0e205a23 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -26,6 +26,7 @@ extern crate alloc; pub mod accumulator; pub mod commitment; pub mod constraint; +pub mod ecc; pub mod encryption; pub mod key; pub mod merkle_tree; diff --git a/manta-crypto/src/merkle_tree/forest.rs b/manta-crypto/src/merkle_tree/forest.rs index 0eb559615..5f4154b14 100644 --- a/manta-crypto/src/merkle_tree/forest.rs +++ b/manta-crypto/src/merkle_tree/forest.rs @@ -22,11 +22,11 @@ use crate::{ accumulator::{ - self, Accumulator, ConstantCapacityAccumulator, ExactSizeAccumulator, MembershipProof, + Accumulator, ConstantCapacityAccumulator, ExactSizeAccumulator, MembershipProof, OptimizedAccumulator, }, merkle_tree::{ - tree::{self, Leaf, Parameters, Tree, Verifier}, + tree::{self, Leaf, Parameters, Tree}, WithProofs, }, }; @@ -247,10 +247,10 @@ where { type Item = Leaf<C>; - type Verifier = Verifier<C>; + type Model = Parameters<C>; #[inline] - fn parameters(&self) -> &accumulator::Parameters<Self> { + fn model(&self) -> &Self::Model { self.parameters() } @@ -267,7 +267,7 @@ where } #[inline] - fn prove(&self, item: &Self::Item) -> Option<MembershipProof<Self::Verifier>> { + fn prove(&self, item: &Self::Item) -> Option<MembershipProof<Self::Model>> { let tree = self.forest.get_tree(item); Some(MembershipProof::new( tree.path( diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index b672f4df7..70d17c7f0 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -480,18 +480,10 @@ where /// Merkle Tree Root pub type Root<C> = InnerDigest<C>; -/// Merkle Tree Verifier -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Verifier<C>(PhantomData<C>) -where - C: Configuration + ?Sized; - -impl<C> accumulator::Verifier for Verifier<C> +impl<C> accumulator::Model for Parameters<C> where C: Configuration + ?Sized, { - type Parameters = Parameters<C>; - type Item = Leaf<C>; type Witness = Path<C>; @@ -502,14 +494,14 @@ where #[inline] fn verify( - compiler: &mut (), - parameters: &Self::Parameters, + &self, item: &Self::Item, witness: &Self::Witness, output: &Self::Output, + compiler: &mut (), ) -> Self::Verification { let _ = compiler; - parameters.verify_path(witness, output, item) + self.verify_path(witness, output, item) } } @@ -796,10 +788,10 @@ where { type Item = Leaf<C>; - type Verifier = Verifier<C>; + type Model = Parameters<C>; #[inline] - fn parameters(&self) -> &accumulator::Parameters<Self> { + fn model(&self) -> &Self::Model { self.parameters() } @@ -809,7 +801,7 @@ where } #[inline] - fn prove(&self, item: &Self::Item) -> Option<MembershipProof<Self::Verifier>> { + fn prove(&self, item: &Self::Item) -> Option<MembershipProof<Self::Model>> { Some(MembershipProof::new( self.path(self.position(&self.parameters.digest(item))?) .ok()?, diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 38ea817fd..f73474ab1 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -20,7 +20,12 @@ use crate::crypto::{ commitment::{pedersen, poseidon}, constraint::arkworks::{FpVar, Groth16, R1CS}, encryption::AesGcm, - key::{Blake2sKdf, EllipticCurveDiffieHellman}, + key::{arkworks::EllipticCurveDiffieHellman, Blake2sKdf}, +}; +use ark_ec::ProjectiveCurve; +use bls12_381::Bls12_381; +use bls12_381_ed::{ + constraints::EdwardsVar as Bls12_381_EdwardsVar, EdwardsProjective as Bls12_381_Edwards, }; use manta_accounting::{asset::Asset, transfer}; use manta_crypto::{ @@ -36,26 +41,29 @@ pub use ark_bls12_381 as bls12_381; #[doc(inline)] pub use ark_ed_on_bls12_381 as bls12_381_ed; -/// BLS12-381 Pairing Engine -pub use bls12_381::Bls12_381; +/// +pub type Curve = Bls12_381; /// -pub use bls12_381_ed::EdwardsProjective as Bls12_381_Edwards; +pub type EmbeddedCurve = Bls12_381_Edwards; /// -pub use bls12_381_ed::constraints::EdwardsVar as Bls12_381_EdwardsVar; +pub type EmbeddedCurveVar = Bls12_381_EdwardsVar; /// Constraint Field pub type ConstraintField = bls12_381::Fr; -/// -pub type KeyAgreement = EllipticCurveDiffieHellman<Bls12_381_Edwards, Bls12_381_EdwardsVar>; +/// Constraint Field Variable +pub type ConstraintFieldVar = FpVar<ConstraintField>; /// Constraint Compiler pub type Compiler = R1CS<ConstraintField>; /// Proof System -pub type ProofSystem = Groth16<Bls12_381>; +pub type ProofSystem = Groth16<Curve>; + +/// +pub type KeyAgreement = EllipticCurveDiffieHellman<EmbeddedCurve>; /// pub struct PoseidonSpec<const ARITY: usize>; @@ -68,14 +76,12 @@ impl poseidon::arkworks::Specification for PoseidonSpec<2> { } /// -pub type PedersenSpec = pedersen::arkworks::Specification<Bls12_381_Edwards>; +pub type PedersenSpec = pedersen::arkworks::Specification<EmbeddedCurve>; /// -pub struct EphemeralKeyCommitmentScheme; +pub struct EphemeralKeyCommitmentScheme(pub poseidon::Commitment<PoseidonSpec<2>, (), 2>); impl CommitmentScheme for EphemeralKeyCommitmentScheme { - type Parameters = poseidon::Parameters<PoseidonSpec<2>, (), 2>; - type Trapdoor = poseidon::Trapdoor<PoseidonSpec<2>, (), 2>; type Input = Asset; @@ -84,23 +90,23 @@ impl CommitmentScheme for EphemeralKeyCommitmentScheme { #[inline] fn commit( - compiler: &mut (), - parameters: &Self::Parameters, + &self, trapdoor: &Self::Trapdoor, input: &Self::Input, + compiler: &mut (), ) -> Self::Output { - poseidon::Commitment::<PoseidonSpec<2>, (), 2>::commit( - compiler, - parameters, + self.0.commit( trapdoor, &[input.id.0.into(), input.value.0.into()], + compiler, ) } } -impl CommitmentScheme<Compiler> for EphemeralKeyCommitmentScheme { - type Parameters = poseidon::Parameters<PoseidonSpec<2>, Compiler, 2>; +/// +pub struct EphemeralKeyCommitmentSchemeVar(pub poseidon::Commitment<PoseidonSpec<2>, Compiler, 2>); +impl CommitmentScheme<Compiler> for EphemeralKeyCommitmentSchemeVar { type Trapdoor = poseidon::Trapdoor<PoseidonSpec<2>, Compiler, 2>; type Input = Asset<FpVar<ConstraintField>, FpVar<ConstraintField>>; @@ -109,118 +115,116 @@ impl CommitmentScheme<Compiler> for EphemeralKeyCommitmentScheme { #[inline] fn commit( - compiler: &mut Compiler, - parameters: &Self::Parameters, + &self, trapdoor: &Self::Trapdoor, input: &Self::Input, + compiler: &mut Compiler, ) -> Self::Output { - poseidon::Commitment::<PoseidonSpec<2>, Compiler, 2>::commit( - compiler, - parameters, - trapdoor, - &[input.id.clone(), input.value.clone()], - ) + self.0 + .commit(trapdoor, &[input.id.clone(), input.value.clone()], compiler) } } /// -pub struct TrapdoorDerivationFunction; +pub struct TrapdoorDerivationFunction(pub poseidon::Commitment<PoseidonSpec<2>, (), 2>); impl KeyDerivationFunction for TrapdoorDerivationFunction { - type Key = (); + type Key = EmbeddedCurve; - type Output = (); + type Output = ConstraintField; #[inline] - fn derive(compiler: &mut (), secret: Self::Key) -> Self::Output { - /* TODO: - poseidon::Commitment::<PoseidonSpec<2>, (), 2>::commit( - compiler, - parameters, - Default::default(), - &[secret.x.into(), secret.y.into()], - ) - */ - todo!() + fn derive(&self, secret: Self::Key, compiler: &mut ()) -> Self::Output { + let affine = <Self::Key as ProjectiveCurve>::Affine::from(secret); + self.0 + .commit(&Default::default(), &[affine.x, affine.y], compiler) } } -impl KeyDerivationFunction<Compiler> for TrapdoorDerivationFunction { - type Key = (); +/// +pub struct TrapdoorDerivationFunctionVar(pub poseidon::Commitment<PoseidonSpec<2>, Compiler, 2>); + +impl KeyDerivationFunction<Compiler> for TrapdoorDerivationFunctionVar { + type Key = EmbeddedCurveVar; - type Output = (); + type Output = FpVar<ConstraintField>; #[inline] - fn derive(compiler: &mut Compiler, secret: Self::Key) -> Self::Output { + fn derive(&self, secret: Self::Key, compiler: &mut Compiler) -> Self::Output { + /* TODO: + self.0 + .commit(&Default::default(), &[secret.x, secret.y], compiler) + */ todo!() } } /// -pub struct UtxoCommitmentScheme; +pub struct UtxoCommitmentScheme(pub pedersen::Commitment<PedersenSpec, (), 2>); impl CommitmentScheme for UtxoCommitmentScheme { - type Parameters = pedersen::Parameters<PedersenSpec, (), 2>; type Trapdoor = pedersen::Trapdoor<PedersenSpec, (), 2>; type Input = Asset; type Output = pedersen::Output<PedersenSpec, (), 2>; #[inline] fn commit( - compiler: &mut (), - parameters: &Self::Parameters, + &self, trapdoor: &Self::Trapdoor, input: &Self::Input, + compiler: &mut (), ) -> Self::Output { - pedersen::Commitment::<PedersenSpec, (), 2>::commit( - compiler, - parameters, + self.0.commit( trapdoor, &[input.id.0.into(), input.value.0.into()], + compiler, ) } } -/* -impl CommitmentScheme<Compiler> for UtxoCommitmentScheme {} +/* TODO: +/// +pub struct UtxoCommitmentSchemeVar(pub pedersen::Commitment<PedersenSpec, Compiler, 2>); + +impl CommitmentScheme<Compiler> for UtxoCommitmentScheme { + type Trapdoor = pedersen::Trapdoor<PedersenSpec, Compiler, 2>; + type Input = Asset<FpVar<ConstraintField>, FpVar<ConstraintField>>; + type Output = pedersen::Output<PedersenSpec, Compiler, 2>; + + #[inline] + fn commit( + &self, + trapdoor: &Self::Trapdoor, + input: &Self::Input, + compiler: &mut Compiler, + ) -> Self::Output { + self.0.commit(trapdoor, &[input.id.clone(), input.value.clone()], compiler) + } +} */ /// -pub struct VoidNumberCommitmentScheme; +pub struct VoidNumberCommitmentScheme(pub pedersen::Commitment<PedersenSpec, (), 1>); impl CommitmentScheme for VoidNumberCommitmentScheme { - type Parameters = pedersen::Parameters<PedersenSpec, (), 1>; - type Trapdoor = pedersen::Trapdoor<PedersenSpec, (), 2>; + type Trapdoor = pedersen::Trapdoor<PedersenSpec, (), 1>; type Input = <KeyAgreement as KeyAgreementScheme>::SecretKey; type Output = pedersen::Output<PedersenSpec, (), 1>; #[inline] fn commit( - compiler: &mut (), - parameters: &Self::Parameters, + &self, trapdoor: &Self::Trapdoor, input: &Self::Input, + compiler: &mut (), ) -> Self::Output { - pedersen::Commitment::<PedersenSpec, (), 1>::commit( - compiler, - parameters, - trapdoor, - core::array::from_ref(input), - ) + self.0 + .commit(trapdoor, core::array::from_ref(input), compiler) } } /* -impl CommitmentScheme<Compiler> for VoidNumberCommitmentScheme {} -*/ - -/// -pub struct UtxoSetVerifier; - -/* -impl accumulator::Verifier for UtxoSetVerifier {} - -impl accumulator::Verifier<Compiler> for UtxoSetVerifier {} +impl CommitmentScheme<Compiler> for VoidNumberCommitmentSchemeVar {} */ /// Configuration Structure diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs index aef2e011a..f48b3dcc6 100644 --- a/manta-pay/src/crypto/commitment/pedersen.rs +++ b/manta-pay/src/crypto/commitment/pedersen.rs @@ -22,7 +22,7 @@ use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use manta_crypto::commitment::CommitmentScheme; /// Pedersen Commitment Specification -pub trait Specification<J = ()> { +pub trait Specification<COM = ()> { /// Group Type type Group; @@ -30,31 +30,31 @@ pub trait Specification<J = ()> { type Scalar; /// Adds two points of the group together. - fn add(compiler: &mut J, lhs: Self::Group, rhs: Self::Group) -> Self::Group; + fn add(lhs: Self::Group, rhs: Self::Group, compiler: &mut COM) -> Self::Group; /// Multiplies the given `point` with a `scalar` value. - fn scalar_mul(compiler: &mut J, point: &Self::Group, scalar: &Self::Scalar) -> Self::Group; + fn scalar_mul(point: &Self::Group, scalar: &Self::Scalar, compiler: &mut COM) -> Self::Group; /// Computes the Pedersen Commitment with `parameters` over `trapdoor` and `input` in the given /// `compiler`. #[inline] fn commit<const ARITY: usize>( - compiler: &mut J, - parameters: &Parameters<Self, J, ARITY>, + parameters: &Commitment<Self, COM, ARITY>, trapdoor: &Self::Scalar, input: &[Self::Scalar; ARITY], + compiler: &mut COM, ) -> Self::Group { parameters.input_generators.iter().zip(input).fold( - Self::scalar_mul(compiler, &parameters.trapdoor_generator, trapdoor), + Self::scalar_mul(&parameters.trapdoor_generator, trapdoor, compiler), move |acc, (g, i)| { - let point = Self::scalar_mul(compiler, g, i); - Self::add(compiler, acc, point) + let point = Self::scalar_mul(g, i, compiler); + Self::add(acc, point, compiler) }, ) } } -/// Commitment Parameters +/// Commitment Scheme #[derive(derivative::Derivative)] #[derivative( Clone(bound = "S::Group: Clone"), @@ -64,9 +64,9 @@ pub trait Specification<J = ()> { Hash(bound = "S::Group: Hash"), PartialEq(bound = "S::Group: PartialEq") )] -pub struct Parameters<S, J = (), const ARITY: usize = 1> +pub struct Commitment<S, COM = (), const ARITY: usize = 1> where - S: Specification<J> + ?Sized, + S: Specification<COM> + ?Sized, { /// Trapdoor Generator pub trapdoor_generator: S::Group, @@ -75,19 +75,10 @@ where pub input_generators: [S::Group; ARITY], } -/// Commitment Scheme -#[derive(derivative::Derivative)] -#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Commitment<S, J = (), const ARITY: usize = 1>(PhantomData<(J, S)>) -where - S: Specification<J>; - -impl<S, J, const ARITY: usize> CommitmentScheme<J> for Commitment<S, J, ARITY> +impl<S, COM, const ARITY: usize> CommitmentScheme<COM> for Commitment<S, COM, ARITY> where - S: Specification<J>, + S: Specification<COM>, { - type Parameters = Parameters<S, J, ARITY>; - type Trapdoor = S::Scalar; type Input = [S::Scalar; ARITY]; @@ -96,25 +87,26 @@ where #[inline] fn commit( - compiler: &mut J, - parameters: &Self::Parameters, + &self, trapdoor: &Self::Trapdoor, input: &Self::Input, + compiler: &mut COM, ) -> Self::Output { - S::commit(compiler, parameters, trapdoor, input) + S::commit(self, trapdoor, input, compiler) } } /// Pedersen Commitment Trapdoor Type -pub type Trapdoor<S, J, const ARITY: usize> = - <Commitment<S, J, ARITY> as CommitmentScheme<J>>::Trapdoor; +pub type Trapdoor<S, COM, const ARITY: usize> = + <Commitment<S, COM, ARITY> as CommitmentScheme<COM>>::Trapdoor; /// Pedersen Commitment Input Type -pub type Input<S, J, const ARITY: usize> = <Commitment<S, J, ARITY> as CommitmentScheme<J>>::Input; +pub type Input<S, COM, const ARITY: usize> = + <Commitment<S, COM, ARITY> as CommitmentScheme<COM>>::Input; /// Pedersen Commitment Output Type -pub type Output<S, J, const ARITY: usize> = - <Commitment<S, J, ARITY> as CommitmentScheme<J>>::Output; +pub type Output<S, COM, const ARITY: usize> = + <Commitment<S, COM, ARITY> as CommitmentScheme<COM>>::Output; /// Arkworks Backend #[cfg(feature = "arkworks")] @@ -140,12 +132,12 @@ pub mod arkworks { type Scalar = C::ScalarField; #[inline] - fn add(_: &mut (), lhs: Self::Group, rhs: Self::Group) -> Self::Group { + fn add(lhs: Self::Group, rhs: Self::Group, _: &mut ()) -> Self::Group { lhs + rhs } #[inline] - fn scalar_mul(_: &mut (), point: &Self::Group, scalar: &Self::Scalar) -> Self::Group { + fn scalar_mul(point: &Self::Group, scalar: &Self::Scalar, _: &mut ()) -> Self::Group { point.mul(scalar.into_repr()) } } diff --git a/manta-pay/src/crypto/commitment/poseidon.rs b/manta-pay/src/crypto/commitment/poseidon.rs index a52505ba7..d79ccb3bf 100644 --- a/manta-pay/src/crypto/commitment/poseidon.rs +++ b/manta-pay/src/crypto/commitment/poseidon.rs @@ -19,11 +19,11 @@ // TODO: Describe the contract for `Specification`. use alloc::vec::Vec; -use core::{iter, marker::PhantomData, mem}; +use core::{iter, mem}; use manta_crypto::commitment::CommitmentScheme; /// Poseidon Permutation Specification -pub trait Specification<J = ()> { +pub trait Specification<COM = ()> { /// Field Type type Field; @@ -37,34 +37,34 @@ pub trait Specification<J = ()> { const PARTIAL_ROUNDS: usize; /// Adds two field elements together. - fn add(compiler: &mut J, lhs: &Self::Field, rhs: &Self::Field) -> Self::Field; + fn add(lhs: &Self::Field, rhs: &Self::Field, compiler: &mut COM) -> Self::Field; /// Multiplies two field elements together. - fn mul(compiler: &mut J, lhs: &Self::Field, rhs: &Self::Field) -> Self::Field; + fn mul(lhs: &Self::Field, rhs: &Self::Field, compiler: &mut COM) -> Self::Field; /// Adds the `rhs` field element to `self`, storing the value in `self`. - fn add_assign(compiler: &mut J, lhs: &mut Self::Field, rhs: &Self::Field); + fn add_assign(lhs: &mut Self::Field, rhs: &Self::Field, compiler: &mut COM); /// Applies the S-BOX to `point`. - fn apply_sbox(compiler: &mut J, point: &mut Self::Field); + fn apply_sbox(point: &mut Self::Field, compiler: &mut COM); } /// Internal State Vector -type State<S, J> = Vec<<S as Specification<J>>::Field>; +type State<S, COM> = Vec<<S as Specification<COM>>::Field>; /// Returns the total number of rounds in a Poseidon permutation. #[inline] -pub fn rounds<S, J>() -> usize +pub fn rounds<S, COM>() -> usize where - S: Specification<J>, + S: Specification<COM>, { 2 * S::FULL_ROUNDS + S::PARTIAL_ROUNDS } -/// Poseidon Permutation Parameters -pub struct Parameters<S, J = (), const ARITY: usize = 1> +/// Poseidon Commitment +pub struct Commitment<S, COM = (), const ARITY: usize = 1> where - S: Specification<J>, + S: Specification<COM>, { /// Additive Round Keys additive_round_keys: Vec<S::Field>, @@ -73,11 +73,11 @@ where mds_matrix: Vec<S::Field>, } -impl<S, J, const ARITY: usize> Parameters<S, J, ARITY> +impl<S, COM, const ARITY: usize> Commitment<S, COM, ARITY> where - S: Specification<J>, + S: Specification<COM>, { - /// Builds a new [`Parameters`] form `additive_round_keys` and `mds_matrix`. + /// Builds a new [`Commitment`] form `additive_round_keys` and `mds_matrix`. /// /// # Panics /// @@ -87,7 +87,7 @@ where pub fn new(additive_round_keys: Vec<S::Field>, mds_matrix: Vec<S::Field>) -> Self { assert_eq!( additive_round_keys.len(), - rounds::<S, J>() * (ARITY + 1), + rounds::<S, COM>() * (ARITY + 1), "Additive Rounds Keys are not the correct size." ); assert_eq!( @@ -111,7 +111,7 @@ where /// Computes the MDS matrix multiplication against the `state`. #[inline] - fn mds_matrix_multiply(&self, state: &mut State<S, J>, compiler: &mut J) { + fn mds_matrix_multiply(&self, state: &mut State<S, COM>, compiler: &mut COM) { let width = ARITY + 1; let mut next = Vec::with_capacity(width); for i in 0..width { @@ -119,12 +119,12 @@ where let linear_combination = state .iter() .enumerate() - .map(|(j, elem)| S::mul(compiler, elem, &self.mds_matrix[width * i + j])) + .map(|(j, elem)| S::mul(elem, &self.mds_matrix[width * i + j], compiler)) .collect::<Vec<_>>(); next.push( linear_combination .into_iter() - .reduce(|acc, next| S::add(compiler, &acc, &next)) + .reduce(|acc, next| S::add(&acc, &next, compiler)) .unwrap(), ); } @@ -137,12 +137,12 @@ where &self, trapdoor: &S::Field, input: &[S::Field; ARITY], - compiler: &mut J, - ) -> State<S, J> { + compiler: &mut COM, + ) -> State<S, COM> { let mut state = Vec::with_capacity(ARITY + 1); for (i, point) in iter::once(trapdoor).chain(input).enumerate() { - let mut elem = S::add(compiler, point, &self.additive_round_keys[i]); - S::apply_sbox(compiler, &mut elem); + let mut elem = S::add(point, &self.additive_round_keys[i], compiler); + S::apply_sbox(&mut elem, compiler); state.push(elem); } self.mds_matrix_multiply(&mut state, compiler); @@ -151,38 +151,31 @@ where /// Computes a full round at the given `round` index on the internal permutation `state`. #[inline] - fn full_round(&self, round: usize, state: &mut State<S, J>, compiler: &mut J) { + fn full_round(&self, round: usize, state: &mut State<S, COM>, compiler: &mut COM) { let keys = self.additive_keys(round); for (i, elem) in state.iter_mut().enumerate() { - S::add_assign(compiler, elem, &keys[i]); - S::apply_sbox(compiler, elem); + S::add_assign(elem, &keys[i], compiler); + S::apply_sbox(elem, compiler); } self.mds_matrix_multiply(state, compiler); } /// Computes a partial round at the given `round` index on the internal permutation `state`. #[inline] - fn partial_round(&self, round: usize, state: &mut State<S, J>, compiler: &mut J) { + fn partial_round(&self, round: usize, state: &mut State<S, COM>, compiler: &mut COM) { let keys = self.additive_keys(round); for (i, elem) in state.iter_mut().enumerate() { - S::add_assign(compiler, elem, &keys[i]); + S::add_assign(elem, &keys[i], compiler); } - S::apply_sbox(compiler, &mut state[0]); + S::apply_sbox(&mut state[0], compiler); self.mds_matrix_multiply(state, compiler); } } -/// Poseidon Commitment Scheme -pub struct Commitment<S, J = (), const ARITY: usize = 1>(PhantomData<(S, J)>) +impl<S, COM, const ARITY: usize> CommitmentScheme<COM> for Commitment<S, COM, ARITY> where - S: Specification<J>; - -impl<S, J, const ARITY: usize> CommitmentScheme<J> for Commitment<S, J, ARITY> -where - S: Specification<J>, + S: Specification<COM>, { - type Parameters = Parameters<S, J, ARITY>; - type Trapdoor = S::Field; type Input = [S::Field; ARITY]; @@ -191,21 +184,21 @@ where #[inline] fn commit( - compiler: &mut J, - parameters: &Self::Parameters, + &self, trapdoor: &Self::Trapdoor, input: &Self::Input, + compiler: &mut COM, ) -> Self::Output { - let mut state = parameters.first_round(trapdoor, input, compiler); + let mut state = self.first_round(trapdoor, input, compiler); for round in 1..S::FULL_ROUNDS { - parameters.full_round(round, &mut state, compiler); + self.full_round(round, &mut state, compiler); } for round in S::FULL_ROUNDS..(S::FULL_ROUNDS + S::PARTIAL_ROUNDS) { - parameters.partial_round(round, &mut state, compiler); + self.partial_round(round, &mut state, compiler); } for round in (S::FULL_ROUNDS + S::PARTIAL_ROUNDS)..(2 * S::FULL_ROUNDS + S::PARTIAL_ROUNDS) { - parameters.full_round(round, &mut state, compiler); + self.full_round(round, &mut state, compiler); } state.truncate(1); state.remove(0) @@ -213,15 +206,16 @@ where } /// Poseidon Commitment Trapdoor Type -pub type Trapdoor<S, J, const ARITY: usize> = - <Commitment<S, J, ARITY> as CommitmentScheme<J>>::Trapdoor; +pub type Trapdoor<S, COM, const ARITY: usize> = + <Commitment<S, COM, ARITY> as CommitmentScheme<COM>>::Trapdoor; /// Poseidon Commitment Input Type -pub type Input<S, J, const ARITY: usize> = <Commitment<S, J, ARITY> as CommitmentScheme<J>>::Input; +pub type Input<S, COM, const ARITY: usize> = + <Commitment<S, COM, ARITY> as CommitmentScheme<COM>>::Input; /// Poseidon Commitment Output Type -pub type Output<S, J, const ARITY: usize> = - <Commitment<S, J, ARITY> as CommitmentScheme<J>>::Output; +pub type Output<S, COM, const ARITY: usize> = + <Commitment<S, COM, ARITY> as CommitmentScheme<COM>>::Output; /// Arkworks Backend #[cfg(feature = "arkworks")] @@ -260,22 +254,22 @@ pub mod arkworks { const PARTIAL_ROUNDS: usize = S::PARTIAL_ROUNDS; #[inline] - fn add(_: &mut (), lhs: &Self::Field, rhs: &Self::Field) -> Self::Field { + fn add(lhs: &Self::Field, rhs: &Self::Field, _: &mut ()) -> Self::Field { *lhs + *rhs } #[inline] - fn mul(_: &mut (), lhs: &Self::Field, rhs: &Self::Field) -> Self::Field { + fn mul(lhs: &Self::Field, rhs: &Self::Field, _: &mut ()) -> Self::Field { *lhs * *rhs } #[inline] - fn add_assign(_: &mut (), lhs: &mut Self::Field, rhs: &Self::Field) { + fn add_assign(lhs: &mut Self::Field, rhs: &Self::Field, _: &mut ()) { *lhs += rhs; } #[inline] - fn apply_sbox(_: &mut (), point: &mut Self::Field) { + fn apply_sbox(point: &mut Self::Field, _: &mut ()) { *point = point.pow(&[Self::SBOX_EXPONENT, 0, 0, 0]); } } @@ -291,25 +285,25 @@ pub mod arkworks { const PARTIAL_ROUNDS: usize = S::PARTIAL_ROUNDS; #[inline] - fn add(compiler: &mut R1CS<S::Field>, lhs: &Self::Field, rhs: &Self::Field) -> Self::Field { + fn add(lhs: &Self::Field, rhs: &Self::Field, compiler: &mut R1CS<S::Field>) -> Self::Field { let _ = compiler; lhs + rhs } #[inline] - fn mul(compiler: &mut R1CS<S::Field>, lhs: &Self::Field, rhs: &Self::Field) -> Self::Field { + fn mul(lhs: &Self::Field, rhs: &Self::Field, compiler: &mut R1CS<S::Field>) -> Self::Field { let _ = compiler; lhs * rhs } #[inline] - fn add_assign(compiler: &mut R1CS<S::Field>, lhs: &mut Self::Field, rhs: &Self::Field) { + fn add_assign(lhs: &mut Self::Field, rhs: &Self::Field, compiler: &mut R1CS<S::Field>) { let _ = compiler; *lhs += rhs; } #[inline] - fn apply_sbox(compiler: &mut R1CS<S::Field>, point: &mut Self::Field) { + fn apply_sbox(point: &mut Self::Field, compiler: &mut R1CS<S::Field>) { let _ = compiler; *point = point.pow_by_constant(&[Self::SBOX_EXPONENT]).expect(""); } diff --git a/manta-pay/src/crypto/key.rs b/manta-pay/src/crypto/key.rs index aea72d00b..68aa98a71 100644 --- a/manta-pay/src/crypto/key.rs +++ b/manta-pay/src/crypto/key.rs @@ -21,18 +21,9 @@ use core::marker::PhantomData; use manta_crypto::key::KeyDerivationFunction; use manta_util::into_array_unchecked; -#[cfg(feature = "arkworks")] -use { - crate::crypto::constraint::arkworks::R1CS, - ark_ec::{AffineCurve, ProjectiveCurve}, - ark_ff::Field, - ark_r1cs_std::fields::fp::FpVar, - ark_r1cs_std::groups::{CurveVar, GroupOpsBounds}, - ark_r1cs_std::ToBitsGadget, - manta_crypto::key::KeyAgreementScheme, -}; - /// Blake2s KDF +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Blake2sKdf<T>(PhantomData<T>) where T: AsRef<[u8]>; @@ -46,7 +37,7 @@ where type Output = [u8; 32]; #[inline] - fn derive(compiler: &mut (), secret: Self::Key) -> Self::Output { + fn derive(&self, secret: Self::Key, compiler: &mut ()) -> Self::Output { let _ = compiler; let mut hasher = Blake2s::new(); hasher.update(secret.as_ref()); @@ -57,104 +48,118 @@ where /// #[cfg(feature = "arkworks")] -type ConstraintField<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField; - -/// Elliptic Curve Diffie Hellman Protocol -#[cfg(feature = "arkworks")] #[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] -pub struct EllipticCurveDiffieHellman<C, GG>(PhantomData<(C, GG)>) -where - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>; - -#[cfg(feature = "arkworks")] -impl<C, GG> KeyAgreementScheme for EllipticCurveDiffieHellman<C, GG> -where - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, -{ - type SecretKey = C::ScalarField; - - type PublicKey = C; - - type SharedSecret = C; - - #[inline] - fn derive(compiler: &mut (), secret_key: &Self::SecretKey) -> Self::PublicKey { - Self::derive_owned(compiler, *secret_key) +pub mod arkworks { + use super::*; + use crate::crypto::constraint::arkworks::R1CS; + use ark_ec::{AffineCurve, ProjectiveCurve}; + use ark_ff::{Field, PrimeField}; + use ark_r1cs_std::{ + fields::fp::FpVar, + groups::{CurveVar, GroupOpsBounds}, + ToBitsGadget, + }; + use manta_crypto::key::KeyAgreementScheme; + + /// + type ConstraintField<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField; + + /// Elliptic Curve Diffie Hellman Protocol + pub struct EllipticCurveDiffieHellman<C> + where + C: ProjectiveCurve, + { + /// + pub generator: C, } - #[inline] - fn derive_owned(compiler: &mut (), secret_key: Self::SecretKey) -> Self::PublicKey { - let _ = compiler; - C::Affine::prime_subgroup_generator().mul(secret_key) - } - - #[inline] - fn agree( - compiler: &mut (), - secret_key: &Self::SecretKey, - public_key: &Self::PublicKey, - ) -> Self::SharedSecret { - Self::agree_owned(compiler, *secret_key, *public_key) + impl<C> KeyAgreementScheme for EllipticCurveDiffieHellman<C> + where + C: ProjectiveCurve, + { + type SecretKey = C::ScalarField; + + type PublicKey = C; + + type SharedSecret = C; + + #[inline] + fn derive(&self, secret_key: &Self::SecretKey, compiler: &mut ()) -> Self::PublicKey { + self.derive_owned(*secret_key, compiler) + } + + #[inline] + fn derive_owned(&self, secret_key: Self::SecretKey, compiler: &mut ()) -> Self::PublicKey { + let _ = compiler; + self.generator.mul(secret_key.into_repr()) + } + + #[inline] + fn agree( + &self, + secret_key: &Self::SecretKey, + public_key: &Self::PublicKey, + compiler: &mut (), + ) -> Self::SharedSecret { + self.agree_owned(*secret_key, *public_key, compiler) + } + + #[inline] + fn agree_owned( + &self, + secret_key: Self::SecretKey, + mut public_key: Self::PublicKey, + compiler: &mut (), + ) -> Self::SharedSecret { + let _ = compiler; + public_key *= secret_key; + public_key + } } - #[inline] - fn agree_owned( - compiler: &mut (), - secret_key: Self::SecretKey, - mut public_key: Self::PublicKey, - ) -> Self::SharedSecret { - let _ = compiler; - public_key *= secret_key; - public_key - } -} - -#[cfg(feature = "arkworks")] -impl<C, GG> KeyAgreementScheme<R1CS<ConstraintField<C>>> for EllipticCurveDiffieHellman<C, GG> -where - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - for<'g> &'g GG: GroupOpsBounds<'g, C, GG>, -{ - type SecretKey = FpVar<C::ScalarField>; - - type PublicKey = GG; - - type SharedSecret = GG; - - #[inline] - fn derive( - compiler: &mut R1CS<ConstraintField<C>>, - secret_key: &Self::SecretKey, - ) -> Self::PublicKey { - /* TODO: - let _ = compiler; - GG::prime_subgroup_generator().mul(secret_key) - */ - todo!() - } - - #[inline] - fn agree( - compiler: &mut R1CS<ConstraintField<C>>, - secret_key: &Self::SecretKey, - public_key: &Self::PublicKey, - ) -> Self::SharedSecret { - /* TODO: - let _ = compiler; - public_key - .scalar_mul_le( - FpVar::<ConstraintField<C>>::from(secret_key) - .to_bits_le() - .expect("") - .iter(), - ) - .expect("") - */ - todo!() + /* TODO: + impl<C, GG> KeyAgreementScheme<R1CS<ConstraintField<C>>> for EllipticCurveDiffieHellman<C, GG> + where + C: ProjectiveCurve, + GG: CurveVar<C, ConstraintField<C>>, + { + type SecretKey = FpVar<C::ScalarField>; + + type PublicKey = GG; + + type SharedSecret = GG; + + #[inline] + fn derive( + compiler: &mut R1CS<ConstraintField<C>>, + secret_key: &Self::SecretKey, + ) -> Self::PublicKey { + /* TODO: + let _ = compiler; + GG::prime_subgroup_generator().mul(secret_key) + */ + todo!() + } + + #[inline] + fn agree( + compiler: &mut R1CS<ConstraintField<C>>, + secret_key: &Self::SecretKey, + public_key: &Self::PublicKey, + ) -> Self::SharedSecret { + /* TODO: + let _ = compiler; + public_key + .scalar_mul_le( + FpVar::<ConstraintField<C>>::from(secret_key) + .to_bits_le() + .expect("") + .iter(), + ) + .expect("") + */ + todo!() + } } + */ } From 318a193753e3974e820c67bc8e0719e450a430a6 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 30 Dec 2021 17:31:11 -0500 Subject: [PATCH 149/275] wip: add arkworks implementations --- manta-pay/src/config.rs | 152 ++++++++-------- manta-pay/src/crypto/commitment/pedersen.rs | 87 ++++++++- manta-pay/src/crypto/commitment/poseidon.rs | 47 ++++- manta-pay/src/crypto/key.rs | 165 ------------------ .../key/elliptic_curve_diffie_hellman.rs | 159 +++++++++++++++++ manta-pay/src/crypto/key/mod.rs | 49 ++++++ 6 files changed, 414 insertions(+), 245 deletions(-) delete mode 100644 manta-pay/src/crypto/key.rs create mode 100644 manta-pay/src/crypto/key/elliptic_curve_diffie_hellman.rs create mode 100644 manta-pay/src/crypto/key/mod.rs diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index f73474ab1..6e1aa7271 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -20,19 +20,22 @@ use crate::crypto::{ commitment::{pedersen, poseidon}, constraint::arkworks::{FpVar, Groth16, R1CS}, encryption::AesGcm, - key::{arkworks::EllipticCurveDiffieHellman, Blake2sKdf}, + key::{elliptic_curve_diffie_hellman, Blake2sKdf}, }; use ark_ec::ProjectiveCurve; use bls12_381::Bls12_381; use bls12_381_ed::{ constraints::EdwardsVar as Bls12_381_EdwardsVar, EdwardsProjective as Bls12_381_Edwards, }; -use manta_accounting::{asset::Asset, transfer}; +use manta_accounting::{ + asset::{Asset, AssetId, AssetValue}, + transfer::{self, Utxo, VoidNumber}, +}; use manta_crypto::{ accumulator, commitment::CommitmentScheme, - encryption, - key::{KeyAgreementScheme, KeyDerivationFunction}, + constraint, encryption, + key::{self, KeyDerivationFunction}, merkle_tree, }; @@ -63,7 +66,8 @@ pub type Compiler = R1CS<ConstraintField>; pub type ProofSystem = Groth16<Curve>; /// -pub type KeyAgreement = EllipticCurveDiffieHellman<EmbeddedCurve>; +pub type KeyAgreementSpec = + elliptic_curve_diffie_hellman::arkworks::Specification<EmbeddedCurve, EmbeddedCurveVar>; /// pub struct PoseidonSpec<const ARITY: usize>; @@ -76,7 +80,14 @@ impl poseidon::arkworks::Specification for PoseidonSpec<2> { } /// -pub type PedersenSpec = pedersen::arkworks::Specification<EmbeddedCurve>; +pub type PedersenSpec = pedersen::arkworks::Specification<EmbeddedCurve, EmbeddedCurveVar>; + +/// +pub type KeyAgreementScheme = elliptic_curve_diffie_hellman::KeyAgreement<KeyAgreementSpec>; + +/// +pub type KeyAgreementSchemeVar = + elliptic_curve_diffie_hellman::KeyAgreement<KeyAgreementSpec, Compiler>; /// pub struct EphemeralKeyCommitmentScheme(pub poseidon::Commitment<PoseidonSpec<2>, (), 2>); @@ -182,11 +193,10 @@ impl CommitmentScheme for UtxoCommitmentScheme { } } -/* TODO: /// pub struct UtxoCommitmentSchemeVar(pub pedersen::Commitment<PedersenSpec, Compiler, 2>); -impl CommitmentScheme<Compiler> for UtxoCommitmentScheme { +impl CommitmentScheme<Compiler> for UtxoCommitmentSchemeVar { type Trapdoor = pedersen::Trapdoor<PedersenSpec, Compiler, 2>; type Input = Asset<FpVar<ConstraintField>, FpVar<ConstraintField>>; type Output = pedersen::Output<PedersenSpec, Compiler, 2>; @@ -198,17 +208,17 @@ impl CommitmentScheme<Compiler> for UtxoCommitmentScheme { input: &Self::Input, compiler: &mut Compiler, ) -> Self::Output { - self.0.commit(trapdoor, &[input.id.clone(), input.value.clone()], compiler) + self.0 + .commit(trapdoor, &[input.id.clone(), input.value.clone()], compiler) } } -*/ /// pub struct VoidNumberCommitmentScheme(pub pedersen::Commitment<PedersenSpec, (), 1>); impl CommitmentScheme for VoidNumberCommitmentScheme { type Trapdoor = pedersen::Trapdoor<PedersenSpec, (), 1>; - type Input = <KeyAgreement as KeyAgreementScheme>::SecretKey; + type Input = <KeyAgreementScheme as key::KeyAgreementScheme>::SecretKey; type Output = pedersen::Output<PedersenSpec, (), 1>; #[inline] @@ -223,9 +233,25 @@ impl CommitmentScheme for VoidNumberCommitmentScheme { } } -/* -impl CommitmentScheme<Compiler> for VoidNumberCommitmentSchemeVar {} -*/ +/// +pub struct VoidNumberCommitmentSchemeVar(pub pedersen::Commitment<PedersenSpec, Compiler, 1>); + +impl CommitmentScheme<Compiler> for VoidNumberCommitmentSchemeVar { + type Trapdoor = pedersen::Trapdoor<PedersenSpec, Compiler, 1>; + type Input = <KeyAgreementSchemeVar as key::KeyAgreementScheme<Compiler>>::SecretKey; + type Output = pedersen::Output<PedersenSpec, Compiler, 1>; + + #[inline] + fn commit( + &self, + trapdoor: &Self::Trapdoor, + input: &Self::Input, + compiler: &mut Compiler, + ) -> Self::Output { + self.0 + .commit(trapdoor, core::array::from_ref(input), compiler) + } +} /// Configuration Structure #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -233,105 +259,95 @@ pub struct Config; /* impl transfer::Configuration for Config { - type SecretKey = <Self::KeyAgreementScheme as KeyAgreementScheme>::SecretKey; - type SecretKeyVar = <Self::KeyAgreementScheme as KeyAgreementScheme<Self::Compiler>>::SecretKey; - type PublicKey = <Self::KeyAgreementScheme as KeyAgreementScheme>::PublicKey; - type PublicKeyVar = <Self::KeyAgreementScheme as KeyAgreementScheme<Self::Compiler>>::PublicKey; - type KeyAgreementScheme = KeyAgreement; - + /* TODO: + type SecretKey = <Self::KeyAgreementScheme as key::KeyAgreementScheme>::SecretKey; + type PublicKey = <Self::KeyAgreementScheme as key::KeyAgreementScheme>::PublicKey; + type KeyAgreementScheme = KeyAgreementScheme; + type SecretKeyVar = + <Self::KeyAgreementSchemeVar as key::KeyAgreementScheme<Self::Compiler>>::SecretKey; + type PublicKeyVar = + <Self::KeyAgreementSchemeVar as key::KeyAgreementScheme<Self::Compiler>>::PublicKey; + type KeyAgreementSchemeVar = KeyAgreementSchemeVar; + */ + + /* type EphemeralKeyTrapdoor = <Self::EphemeralKeyCommitmentScheme as CommitmentScheme>::Trapdoor; - type EphemeralKeyTrapdoorVar = - <Self::EphemeralKeyCommitmentScheme as CommitmentScheme<Self::Compiler>>::Trapdoor; - type EphemeralKeyParametersVar = - <Self::EphemeralKeyCommitmentScheme as CommitmentScheme<Self::Compiler>>::Parameters; type EphemeralKeyCommitmentScheme = EphemeralKeyCommitmentScheme; + type EphemeralKeyTrapdoorVar = + <Self::EphemeralKeyCommitmentSchemeVar as CommitmentScheme<Self::Compiler>>::Trapdoor; + type EphemeralKeyCommitmentSchemeVar = EphemeralKeyCommitmentSchemeVar; + */ type TrapdoorDerivationFunction = TrapdoorDerivationFunction; + type TrapdoorDerivationFunctionVar = TrapdoorDerivationFunctionVar; - type UtxoCommitmentParametersVar = - <Self::UtxoCommitmentScheme as CommitmentScheme<Self::Compiler>>::Parameters; type Utxo = <Self::UtxoCommitmentScheme as CommitmentScheme>::Output; type UtxoCommitmentScheme = UtxoCommitmentScheme; + type UtxoVar = <Self::UtxoCommitmentSchemeVar as CommitmentScheme<Self::Compiler>>::Output; + type UtxoCommitmentSchemeVar = UtxoCommitmentSchemeVar; - type VoidNumberCommitmentParametersVar = - <Self::VoidNumberCommitmentScheme as CommitmentScheme<Self::Compiler>>::Parameters; type VoidNumber = <Self::VoidNumberCommitmentScheme as CommitmentScheme>::Output; type VoidNumberCommitmentScheme = VoidNumberCommitmentScheme; + type VoidNumberVar = + <Self::VoidNumberCommitmentSchemeVar as CommitmentScheme<Self::Compiler>>::Output; + type VoidNumberCommitmentSchemeVar = VoidNumberCommitmentSchemeVar; - type UtxoSetParametersVar = - <Self::UtxoSetVerifier as accumulator::Verifier<Self::Compiler>>::Parameters; - type UtxoSetWitnessVar = - <Self::UtxoSetVerifier as accumulator::Verifier<Self::Compiler>>::Witness; - type UtxoSetOutputVar = - <Self::UtxoSetVerifier as accumulator::Verifier<Self::Compiler>>::Output; - type UtxoSetVerifier = UtxoSetVerifier; + /* TODO: + type UtxoSetModel = merkle_tree::Parameters<()>; + type UtxoSetWitnessVar = <Self::UtxoSetModelVar as accumulator::Model<Self::Compiler>>::Witness; + type UtxoSetOutputVar = <Self::UtxoSetModelVar as accumulator::Model<Self::Compiler>>::Output; + type UtxoSetModelVar = (); + */ - type AssetIdVar = FpVar<ConstraintField>; - type AssetValueVar = FpVar<ConstraintField>; + type AssetIdVar = ConstraintFieldVar; + type AssetValueVar = ConstraintFieldVar; type Compiler = Compiler; type ProofSystem = ProofSystem; + /* TODO: + type NoteEncryptionKeyDerivationFunction = + Blake2sKdf<<Self::KeyAgreementScheme as key::KeyAgreementScheme>::SharedSecret>; + type NoteEncryptionScheme = encryption::Hybrid< Self::KeyAgreementScheme, AesGcm<Asset, { Asset::SIZE }>, - Blake2sKdf<<Self::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret>, + Self::NoteEncryptionKeyDerivationFunction, >; + */ } */ -/* TODO: -impl<E> Input<AssetId> for Groth16<E> -where - E: PairingEngine, -{ +impl constraint::Input<AssetId> for ProofSystem { #[inline] fn extend(input: &mut Self::Input, next: &AssetId) { input.push(next.0.into()); - input.append(&mut next.into_bytes().to_field_elements().unwrap()); } } -impl<E> Input<AssetValue> for Groth16<E> -where - E: PairingEngine, -{ +impl constraint::Input<AssetValue> for ProofSystem { #[inline] fn extend(input: &mut Self::Input, next: &AssetValue) { input.push(next.0.into()); - input.append(&mut next.into_bytes().to_field_elements().unwrap()); } } -impl<E> Input<VoidNumber> for Groth16<E> -where - E: PairingEngine, -{ +impl constraint::Input<EmbeddedCurve> for ProofSystem { #[inline] - fn extend(input: &mut Self::Input, next: &VoidNumber) { - next.extend_input(input); + fn extend(input: &mut Self::Input, next: &EmbeddedCurve) { + // TODO: next.extend_input(input); + todo!() } } -impl<E> Input<Root> for Groth16<E> -where - E: PairingEngine<Fr = ark_ff::Fp256<ark_bls12_381::FrParameters>>, +/* TODO: +impl constraint::Input<Root> for ProofSystem { #[inline] fn extend(input: &mut Self::Input, next: &Root) { root_extend_input(next, input); } } - -impl<E> Input<Utxo> for Groth16<E> -where - E: PairingEngine, -{ - #[inline] - fn extend(input: &mut Self::Input, next: &Utxo) { - next.extend_input(input); - } -} */ /* TODO: diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs index f48b3dcc6..da46c2121 100644 --- a/manta-pay/src/crypto/commitment/pedersen.rs +++ b/manta-pay/src/crypto/commitment/pedersen.rs @@ -18,7 +18,7 @@ // TODO: Describe contract for `Specification`. -use core::{fmt::Debug, hash::Hash, marker::PhantomData}; +use core::{fmt::Debug, hash::Hash}; use manta_crypto::commitment::CommitmentScheme; /// Pedersen Commitment Specification @@ -112,20 +112,33 @@ pub type Output<S, COM, const ARITY: usize> = #[cfg(feature = "arkworks")] #[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] pub mod arkworks { - use super::*; + use crate::crypto::constraint::arkworks::{FpVar, R1CS}; use ark_ec::ProjectiveCurve; - use ark_ff::PrimeField; + use ark_ff::{Field, PrimeField}; + use ark_r1cs_std::{groups::CurveVar, ToBitsGadget}; + use ark_relations::ns; + use core::marker::PhantomData; + use manta_crypto::constraint::{Allocation, Constant, Variable}; + use manta_util::fallible_array_map; + + /// Constraint Field Type + type ConstraintField<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField; + + /// Compiler Type + type Compiler<C> = R1CS<ConstraintField<C>>; /// Pedersen Commitment Specification #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct Specification<C>(PhantomData<C>) + pub struct Specification<C, CV>(PhantomData<(C, CV)>) where - C: ProjectiveCurve; + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>; - impl<C> super::Specification for Specification<C> + impl<C, CV> super::Specification for Specification<C, CV> where C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>, { type Group = C; @@ -141,4 +154,66 @@ pub mod arkworks { point.mul(scalar.into_repr()) } } + + impl<C, CV> super::Specification<Compiler<C>> for Specification<C, CV> + where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>, + { + type Group = CV; + + type Scalar = FpVar<ConstraintField<C>>; + + #[inline] + fn add(lhs: Self::Group, rhs: Self::Group, compiler: &mut Compiler<C>) -> Self::Group { + let _ = compiler; + lhs + rhs + } + + #[inline] + fn scalar_mul( + point: &Self::Group, + scalar: &Self::Scalar, + compiler: &mut Compiler<C>, + ) -> Self::Group { + let _ = compiler; + point + .scalar_mul_le( + scalar + .to_bits_le() + .expect("Bit decomposition is not allowed to fail.") + .iter(), + ) + .expect("Scalar multiplication is not allowed to fail.") + } + } + + impl<C, CV, const ARITY: usize> Variable<Compiler<C>> + for super::Commitment<Specification<C, CV>, Compiler<C>, ARITY> + where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>, + { + type Type = super::Commitment<Specification<C, CV>, (), ARITY>; + + type Mode = Constant; + + #[inline] + fn new(cs: &mut Compiler<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + match allocation { + Allocation::Known(this, _) => Self { + trapdoor_generator: CV::new_constant( + ns!(cs.cs, "group element"), + this.trapdoor_generator, + ) + .expect("Variable allocation is not allowed to fail."), + input_generators: fallible_array_map(this.input_generators, |g| { + CV::new_constant(ns!(cs.cs, "group element"), g) + }) + .expect("Variable allocation is not allowed to fail."), + }, + _ => unreachable!(), + } + } + } } diff --git a/manta-pay/src/crypto/commitment/poseidon.rs b/manta-pay/src/crypto/commitment/poseidon.rs index d79ccb3bf..1a12fa4d9 100644 --- a/manta-pay/src/crypto/commitment/poseidon.rs +++ b/manta-pay/src/crypto/commitment/poseidon.rs @@ -223,7 +223,12 @@ pub type Output<S, COM, const ARITY: usize> = pub mod arkworks { use crate::crypto::constraint::arkworks::{FpVar, R1CS}; use ark_ff::{Field, PrimeField}; - use ark_r1cs_std::fields::FieldVar; + use ark_r1cs_std::{alloc::AllocVar, fields::FieldVar}; + use ark_relations::ns; + use manta_crypto::constraint::{Allocation, Constant, Variable}; + + /// Compiler Type + type Compiler<S> = R1CS<<S as Specification>::Field>; /// Poseidon Permutation Specification pub trait Specification { @@ -274,7 +279,7 @@ pub mod arkworks { } } - impl<S> super::Specification<R1CS<S::Field>> for S + impl<S> super::Specification<Compiler<S>> for S where S: Specification, { @@ -285,27 +290,57 @@ pub mod arkworks { const PARTIAL_ROUNDS: usize = S::PARTIAL_ROUNDS; #[inline] - fn add(lhs: &Self::Field, rhs: &Self::Field, compiler: &mut R1CS<S::Field>) -> Self::Field { + fn add(lhs: &Self::Field, rhs: &Self::Field, compiler: &mut Compiler<S>) -> Self::Field { let _ = compiler; lhs + rhs } #[inline] - fn mul(lhs: &Self::Field, rhs: &Self::Field, compiler: &mut R1CS<S::Field>) -> Self::Field { + fn mul(lhs: &Self::Field, rhs: &Self::Field, compiler: &mut Compiler<S>) -> Self::Field { let _ = compiler; lhs * rhs } #[inline] - fn add_assign(lhs: &mut Self::Field, rhs: &Self::Field, compiler: &mut R1CS<S::Field>) { + fn add_assign(lhs: &mut Self::Field, rhs: &Self::Field, compiler: &mut Compiler<S>) { let _ = compiler; *lhs += rhs; } #[inline] - fn apply_sbox(point: &mut Self::Field, compiler: &mut R1CS<S::Field>) { + fn apply_sbox(point: &mut Self::Field, compiler: &mut Compiler<S>) { let _ = compiler; *point = point.pow_by_constant(&[Self::SBOX_EXPONENT]).expect(""); } } + + impl<S, const ARITY: usize> Variable<Compiler<S>> for super::Commitment<S, Compiler<S>, ARITY> + where + S: Specification, + { + type Type = super::Commitment<S, (), ARITY>; + + type Mode = Constant; + + #[inline] + fn new(cs: &mut Compiler<S>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + match allocation { + Allocation::Known(this, _) => Self { + additive_round_keys: this + .additive_round_keys + .iter() + .map(|k| FpVar::new_constant(ns!(cs.cs, ""), k)) + .collect::<Result<Vec<_>, _>>() + .expect("Variable allocation is not allowed to fail."), + mds_matrix: this + .mds_matrix + .iter() + .map(|k| FpVar::new_constant(ns!(cs.cs, ""), k)) + .collect::<Result<Vec<_>, _>>() + .expect("Variable allocation is not allowed to fail."), + }, + _ => unreachable!(), + } + } + } } diff --git a/manta-pay/src/crypto/key.rs b/manta-pay/src/crypto/key.rs deleted file mode 100644 index 68aa98a71..000000000 --- a/manta-pay/src/crypto/key.rs +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Cryptographic Key Primitive Implementations - -use blake2::{Blake2s, Digest}; -use core::marker::PhantomData; -use manta_crypto::key::KeyDerivationFunction; -use manta_util::into_array_unchecked; - -/// Blake2s KDF -#[derive(derivative::Derivative)] -#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Blake2sKdf<T>(PhantomData<T>) -where - T: AsRef<[u8]>; - -impl<T> KeyDerivationFunction for Blake2sKdf<T> -where - T: AsRef<[u8]>, -{ - type Key = T; - - type Output = [u8; 32]; - - #[inline] - fn derive(&self, secret: Self::Key, compiler: &mut ()) -> Self::Output { - let _ = compiler; - let mut hasher = Blake2s::new(); - hasher.update(secret.as_ref()); - hasher.update(b"manta kdf instantiated with blake2s hash function"); - into_array_unchecked(hasher.finalize()) - } -} - -/// -#[cfg(feature = "arkworks")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] -pub mod arkworks { - use super::*; - use crate::crypto::constraint::arkworks::R1CS; - use ark_ec::{AffineCurve, ProjectiveCurve}; - use ark_ff::{Field, PrimeField}; - use ark_r1cs_std::{ - fields::fp::FpVar, - groups::{CurveVar, GroupOpsBounds}, - ToBitsGadget, - }; - use manta_crypto::key::KeyAgreementScheme; - - /// - type ConstraintField<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField; - - /// Elliptic Curve Diffie Hellman Protocol - pub struct EllipticCurveDiffieHellman<C> - where - C: ProjectiveCurve, - { - /// - pub generator: C, - } - - impl<C> KeyAgreementScheme for EllipticCurveDiffieHellman<C> - where - C: ProjectiveCurve, - { - type SecretKey = C::ScalarField; - - type PublicKey = C; - - type SharedSecret = C; - - #[inline] - fn derive(&self, secret_key: &Self::SecretKey, compiler: &mut ()) -> Self::PublicKey { - self.derive_owned(*secret_key, compiler) - } - - #[inline] - fn derive_owned(&self, secret_key: Self::SecretKey, compiler: &mut ()) -> Self::PublicKey { - let _ = compiler; - self.generator.mul(secret_key.into_repr()) - } - - #[inline] - fn agree( - &self, - secret_key: &Self::SecretKey, - public_key: &Self::PublicKey, - compiler: &mut (), - ) -> Self::SharedSecret { - self.agree_owned(*secret_key, *public_key, compiler) - } - - #[inline] - fn agree_owned( - &self, - secret_key: Self::SecretKey, - mut public_key: Self::PublicKey, - compiler: &mut (), - ) -> Self::SharedSecret { - let _ = compiler; - public_key *= secret_key; - public_key - } - } - - /* TODO: - impl<C, GG> KeyAgreementScheme<R1CS<ConstraintField<C>>> for EllipticCurveDiffieHellman<C, GG> - where - C: ProjectiveCurve, - GG: CurveVar<C, ConstraintField<C>>, - { - type SecretKey = FpVar<C::ScalarField>; - - type PublicKey = GG; - - type SharedSecret = GG; - - #[inline] - fn derive( - compiler: &mut R1CS<ConstraintField<C>>, - secret_key: &Self::SecretKey, - ) -> Self::PublicKey { - /* TODO: - let _ = compiler; - GG::prime_subgroup_generator().mul(secret_key) - */ - todo!() - } - - #[inline] - fn agree( - compiler: &mut R1CS<ConstraintField<C>>, - secret_key: &Self::SecretKey, - public_key: &Self::PublicKey, - ) -> Self::SharedSecret { - /* TODO: - let _ = compiler; - public_key - .scalar_mul_le( - FpVar::<ConstraintField<C>>::from(secret_key) - .to_bits_le() - .expect("") - .iter(), - ) - .expect("") - */ - todo!() - } - } - */ -} diff --git a/manta-pay/src/crypto/key/elliptic_curve_diffie_hellman.rs b/manta-pay/src/crypto/key/elliptic_curve_diffie_hellman.rs new file mode 100644 index 000000000..22051d293 --- /dev/null +++ b/manta-pay/src/crypto/key/elliptic_curve_diffie_hellman.rs @@ -0,0 +1,159 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Elliptic Curve Diffie Hellman + +use manta_crypto::key::KeyAgreementScheme; + +/// Elliptic Curve Diffie Hellman Specification +pub trait Specification<COM = ()> { + /// Group Type + type Group; + + /// Scalar Type + type Scalar; + + /// Multiplies `point` by `scalar`. + fn scalar_mul(point: &Self::Group, scalar: &Self::Scalar, compiler: &mut COM) -> Self::Group; +} + +/// Key Agreement Protocol +pub struct KeyAgreement<S, COM = ()> +where + S: Specification<COM>, +{ + /// Base Generator + pub generator: S::Group, +} + +impl<S, COM> KeyAgreementScheme<COM> for KeyAgreement<S, COM> +where + S: Specification<COM>, +{ + type SecretKey = S::Scalar; + + type PublicKey = S::Group; + + type SharedSecret = S::Group; + + #[inline] + fn derive(&self, secret_key: &Self::SecretKey, compiler: &mut COM) -> Self::PublicKey { + S::scalar_mul(&self.generator, secret_key, compiler) + } + + #[inline] + fn agree( + &self, + secret_key: &Self::SecretKey, + public_key: &Self::PublicKey, + compiler: &mut COM, + ) -> Self::SharedSecret { + S::scalar_mul(public_key, secret_key, compiler) + } +} + +/// Arkworks Backend +#[cfg(feature = "arkworks")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] +pub mod arkworks { + use crate::crypto::constraint::arkworks::R1CS; + use ark_ec::ProjectiveCurve; + use ark_ff::{Field, PrimeField}; + use ark_r1cs_std::{fields::fp::FpVar, groups::CurveVar, ToBitsGadget}; + use ark_relations::ns; + use core::marker::PhantomData; + use manta_crypto::constraint::{Allocation, Constant, Variable}; + + /// Constraint Field Type + type ConstraintField<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField; + + /// Compiler Type + type Compiler<C> = R1CS<ConstraintField<C>>; + + /// Specification + pub struct Specification<C, CV>(PhantomData<(C, CV)>) + where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>; + + impl<C, CV> super::Specification for Specification<C, CV> + where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>, + { + type Group = C; + + type Scalar = C::ScalarField; + + #[inline] + fn scalar_mul( + point: &Self::Group, + scalar: &Self::Scalar, + compiler: &mut (), + ) -> Self::Group { + let _ = compiler; + point.mul(scalar.into_repr()) + } + } + + impl<C, CV> super::Specification<Compiler<C>> for Specification<C, CV> + where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>, + { + type Group = CV; + + type Scalar = FpVar<ConstraintField<C>>; + + #[inline] + fn scalar_mul( + point: &Self::Group, + scalar: &Self::Scalar, + compiler: &mut Compiler<C>, + ) -> Self::Group { + let _ = compiler; + point + .scalar_mul_le( + scalar + .to_bits_le() + .expect("Bit decomposition is not allowed to fail.") + .iter(), + ) + .expect("Scalar multiplication is not allowed to fail.") + } + } + + impl<C, CV> Variable<Compiler<C>> for super::KeyAgreement<Specification<C, CV>, Compiler<C>> + where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>, + { + type Type = super::KeyAgreement<Specification<C, CV>, ()>; + + type Mode = Constant; + + #[inline] + fn new(cs: &mut Compiler<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + match allocation { + Allocation::Known(this, _) => Self { + generator: CV::new_constant(ns!(cs.cs, "group element"), this.generator) + .expect("Variable allocation is not allowed to fail."), + }, + _ => unreachable!(), + } + } + } +} diff --git a/manta-pay/src/crypto/key/mod.rs b/manta-pay/src/crypto/key/mod.rs new file mode 100644 index 000000000..bc2d9316c --- /dev/null +++ b/manta-pay/src/crypto/key/mod.rs @@ -0,0 +1,49 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Cryptographic Key Primitive Implementations + +use blake2::{Blake2s, Digest}; +use core::marker::PhantomData; +use manta_crypto::key::KeyDerivationFunction; +use manta_util::into_array_unchecked; + +pub mod elliptic_curve_diffie_hellman; + +/// Blake2s KDF +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Blake2sKdf<T>(PhantomData<T>) +where + T: AsRef<[u8]>; + +impl<T> KeyDerivationFunction for Blake2sKdf<T> +where + T: AsRef<[u8]>, +{ + type Key = T; + + type Output = [u8; 32]; + + #[inline] + fn derive(&self, secret: Self::Key, compiler: &mut ()) -> Self::Output { + let _ = compiler; + let mut hasher = Blake2s::new(); + hasher.update(secret.as_ref()); + hasher.update(b"manta kdf instantiated with blake2s hash function"); + into_array_unchecked(hasher.finalize()) + } +} From ccd3f52597a41481402b0efc1e3ddaea2fe8a2b6 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sat, 1 Jan 2022 19:18:18 -0500 Subject: [PATCH 150/275] fix: move to new spec, improve some ergonomics --- manta-accounting/src/transfer/mod.rs | 355 ++++++------------ manta-accounting/src/wallet/signer.rs | 2 +- manta-crypto/src/commitment.rs | 99 +---- manta-crypto/src/ecc.rs | 132 ++++++- manta-crypto/src/encryption.rs | 64 +++- manta-crypto/src/hash.rs | 60 +++ manta-crypto/src/key.rs | 35 +- manta-crypto/src/lib.rs | 2 + manta-crypto/src/util.rs | 99 +++++ manta-pay/src/config.rs | 2 + ...lliptic_curve_diffie_hellman.rs => ecc.rs} | 53 +-- manta-pay/src/crypto/encryption.rs | 21 +- manta-pay/src/crypto/{key/mod.rs => key.rs} | 19 +- manta-pay/src/crypto/mod.rs | 1 + manta-pay/src/lib.rs | 2 + 15 files changed, 503 insertions(+), 443 deletions(-) create mode 100644 manta-crypto/src/hash.rs create mode 100644 manta-crypto/src/util.rs rename manta-pay/src/crypto/{key/elliptic_curve_diffie_hellman.rs => ecc.rs} (75%) rename manta-pay/src/crypto/{key/mod.rs => key.rs} (76%) diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index 3b9cc26ee..9fc702b99 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -27,7 +27,8 @@ use manta_crypto::{ ProofSystem, Public, PublicOrSecret, Secret, Variable, VariableSource, }, encryption::{DecryptedMessage, EncryptedMessage, HybridPublicKeyEncryptionScheme}, - key::{KeyAgreementScheme, KeyDerivationFunction}, + hash::{HashFunction, Input as HashFunctionInput}, + key::KeyAgreementScheme, rand::{CryptoRng, RngCore, Sample}, }; use manta_util::from_variant_impl; @@ -50,7 +51,7 @@ pub const fn has_public_participants( /// Transfer Configuration pub trait Configuration { /// Secret Key Type - type SecretKey: Clone; + type SecretKey: Clone + Sample; /// Public Key Type type PublicKey: Clone; @@ -68,72 +69,46 @@ pub trait Configuration { type PublicKeyVar: Variable<Self::Compiler, Type = PublicKey<Self>, Mode = Public> + Equal<Self::Compiler>; - /// + /// Key Agreement Scheme Variable Type type KeyAgreementSchemeVar: KeyAgreementScheme< Self::Compiler, SecretKey = Self::SecretKeyVar, PublicKey = Self::PublicKeyVar, > + Variable<Self::Compiler, Type = Self::KeyAgreementScheme, Mode = Constant>; - /// Ephemeral Key Trapdoor Type - type EphemeralKeyTrapdoor: Sample; - - /// Ephemeral Key Commitment Scheme Type - type EphemeralKeyCommitmentScheme: CommitmentScheme< - Trapdoor = Self::EphemeralKeyTrapdoor, - Input = Asset, - Output = Self::SecretKey, - >; - - /// Ephemeral Key Trapdoor Variable Type - type EphemeralKeyTrapdoorVar: Variable< - Self::Compiler, - Type = Self::EphemeralKeyTrapdoor, - Mode = Secret, - >; - - /// - type EphemeralKeyCommitmentSchemeVar: CommitmentScheme< - Self::Compiler, - Trapdoor = Self::EphemeralKeyTrapdoorVar, - Input = AssetVar<Self>, - Output = Self::SecretKeyVar, - > + Variable<Self::Compiler, Type = Self::EphemeralKeyCommitmentScheme, Mode = Constant>; - - /// Trapdoor Derivation Function Type - type TrapdoorDerivationFunction: KeyDerivationFunction< - Key = SharedSecret<Self>, - Output = Trapdoor<Self>, - >; - - /// - type TrapdoorDerivationFunctionVar: KeyDerivationFunction< - Self::Compiler, - Key = SharedSecretVar<Self>, - Output = TrapdoorVar<Self>, - > + Variable<Self::Compiler, Type = Self::TrapdoorDerivationFunction, Mode = Constant>; - /// Unspent Transaction Output Type type Utxo: PartialEq; /// UTXO Commitment Scheme Type - type UtxoCommitmentScheme: CommitmentScheme<Input = Asset, Output = Self::Utxo>; + type UtxoCommitmentScheme: CommitmentScheme< + Trapdoor = Trapdoor<Self>, + Input = Asset, + Output = Self::Utxo, + >; /// UTXO Variable Type type UtxoVar: Variable<Self::Compiler, Type = Self::Utxo, Mode = PublicOrSecret> + Equal<Self::Compiler>; - /// - type UtxoCommitmentSchemeVar: CommitmentScheme<Self::Compiler, Input = AssetVar<Self>, Output = Self::UtxoVar> - + Variable<Self::Compiler, Type = Self::UtxoCommitmentScheme, Mode = Constant>; + /// UTXO Commitment Scheme Variable Type + type UtxoCommitmentSchemeVar: CommitmentScheme< + Self::Compiler, + Trapdoor = TrapdoorVar<Self>, + Input = AssetVar<Self>, + Output = Self::UtxoVar, + > + Variable<Self::Compiler, Type = Self::UtxoCommitmentScheme, Mode = Constant>; /// Void Number Type type VoidNumber: PartialEq; - /// Void Number Commitment Scheme Type - type VoidNumberCommitmentScheme: CommitmentScheme< - Trapdoor = Trapdoor<Self>, - Input = Self::SecretKey, + /// Void Number Hash Function Input Type + type VoidNumberHashFunctionInput: Default + + HashFunctionInput<Self::Utxo> + + HashFunctionInput<Self::SecretKey>; + + /// Void Number Hash Function Type + type VoidNumberHashFunction: HashFunction< + Input = Self::VoidNumberHashFunctionInput, Output = Self::VoidNumber, >; @@ -141,13 +116,17 @@ pub trait Configuration { type VoidNumberVar: Variable<Self::Compiler, Type = Self::VoidNumber, Mode = Public> + Equal<Self::Compiler>; - /// - type VoidNumberCommitmentSchemeVar: CommitmentScheme< + /// Void Number Hash Function Input Variable Type + type VoidNumberHashFunctionInputVar: Default + + HashFunctionInput<Self::UtxoVar> + + HashFunctionInput<Self::SecretKeyVar>; + + /// Void Number Hash Function Variable Type + type VoidNumberHashFunctionVar: HashFunction< Self::Compiler, - Trapdoor = TrapdoorVar<Self>, - Input = Self::SecretKeyVar, + Input = Self::VoidNumberHashFunctionInputVar, Output = Self::VoidNumberVar, - > + Variable<Self::Compiler, Type = Self::VoidNumberCommitmentScheme, Mode = Constant>; + > + Variable<Self::Compiler, Type = Self::VoidNumberHashFunction, Mode = Constant>; /// UTXO Set Model Type type UtxoSetModel: Model<Item = Self::Utxo, Verification = bool>; @@ -158,7 +137,7 @@ pub trait Configuration { /// UTXO Set Output Variable Type type UtxoSetOutputVar: Variable<Self::Compiler, Type = UtxoSetOutput<Self>, Mode = Public>; - /// + /// UTXO Set Model Variable Type type UtxoSetModelVar: Model< Self::Compiler, Item = Self::UtxoVar, @@ -188,37 +167,12 @@ pub trait Configuration { + ProofSystemInput<VoidNumber<Self>> + ProofSystemInput<PublicKey<Self>>; - /// Note Encryption Key Derivation Function - type NoteEncryptionKeyDerivationFunction: Default; - /// Note Encryption Scheme Type type NoteEncryptionScheme: HybridPublicKeyEncryptionScheme< Plaintext = Asset, KeyAgreementScheme = Self::KeyAgreementScheme, - KeyDerivationFunction = Self::NoteEncryptionKeyDerivationFunction, >; - /// Generates the ephemeral secret key from `parameters`, `trapdoor`, and `asset`. - #[inline] - fn ephemeral_secret_key( - parameters: &Self::EphemeralKeyCommitmentScheme, - trapdoor: &EphemeralKeyTrapdoor<Self>, - asset: Asset, - ) -> SecretKey<Self> { - parameters.commit(trapdoor, &asset, &mut ()) - } - - /// Generates the ephemeral secret key from `parameters`, `trapdoor`, and `asset`. - #[inline] - fn ephemeral_secret_key_var( - parameters: &Self::EphemeralKeyCommitmentSchemeVar, - trapdoor: &EphemeralKeyTrapdoorVar<Self>, - asset: &AssetVar<Self>, - cs: &mut Self::Compiler, - ) -> SecretKeyVar<Self> { - parameters.commit(trapdoor, asset, cs) - } - /// Derives a public key variable from a secret key variable. #[inline] fn ephemeral_public_key_var( @@ -233,109 +187,75 @@ pub trait Configuration { #[inline] fn trapdoor( key_agreement: &Self::KeyAgreementScheme, - trapdoor_derivation_function: &Self::TrapdoorDerivationFunction, secret_key: &SecretKey<Self>, public_key: &PublicKey<Self>, ) -> Trapdoor<Self> { - let shared_secret = key_agreement.agree(secret_key, public_key, &mut ()); - trapdoor_derivation_function.derive(shared_secret, &mut ()) + key_agreement.agree(secret_key, public_key, &mut ()) } /// Generates the commitment trapdoor associated to `secret_key` and `public_key`. #[inline] fn trapdoor_var( key_agreement: &Self::KeyAgreementSchemeVar, - trapdoor_derivation_function: &Self::TrapdoorDerivationFunctionVar, secret_key: &SecretKeyVar<Self>, public_key: &PublicKeyVar<Self>, cs: &mut Self::Compiler, ) -> TrapdoorVar<Self> { - let shared_secret = key_agreement.agree(secret_key, public_key, cs); - trapdoor_derivation_function.derive(shared_secret, cs) - } - - /// Generates the UTXO associated to `trapdoor` and `asset` from `parameters`. - #[inline] - fn utxo( - parameters: &Self::UtxoCommitmentScheme, - trapdoor: &Trapdoor<Self>, - asset: &Asset, - ) -> Utxo<Self> { - parameters.commit(trapdoor, asset, &mut ()) - } - - /// Generates the UTXO associated to `trapdoor` and `asset` from `parameters`. - #[inline] - fn utxo_var( - parameters: &Self::UtxoCommitmentSchemeVar, - trapdoor: &TrapdoorVar<Self>, - asset: &AssetVar<Self>, - cs: &mut Self::Compiler, - ) -> UtxoVar<Self> { - parameters.commit(trapdoor, asset, cs) + key_agreement.agree(secret_key, public_key, cs) } /// Generates the trapdoor associated to `secret_key` and `public_key` and then uses it to /// generate the UTXO associated to `asset`. #[inline] - fn full_utxo( + fn utxo( key_agreement: &Self::KeyAgreementScheme, - trapdoor_derivation_function: &Self::TrapdoorDerivationFunction, utxo_commitment: &Self::UtxoCommitmentScheme, secret_key: &SecretKey<Self>, public_key: &PublicKey<Self>, asset: &Asset, ) -> Utxo<Self> { - let trapdoor = Self::trapdoor( - key_agreement, - trapdoor_derivation_function, - secret_key, - public_key, - ); - Self::utxo(utxo_commitment, &trapdoor, asset) + let trapdoor = Self::trapdoor(key_agreement, secret_key, public_key); + utxo_commitment.commit(&trapdoor, asset, &mut ()) } /// Generates the trapdoor associated to `secret_key` and `public_key` and then uses it to /// generate the UTXO associated to `asset`. #[inline] - fn full_utxo_var( + fn utxo_var( key_agreement: &Self::KeyAgreementSchemeVar, - trapdoor_derivation_function: &Self::TrapdoorDerivationFunctionVar, utxo_commitment: &Self::UtxoCommitmentSchemeVar, secret_key: &SecretKeyVar<Self>, public_key: &PublicKeyVar<Self>, asset: &AssetVar<Self>, cs: &mut Self::Compiler, ) -> UtxoVar<Self> { - let trapdoor = Self::trapdoor_var( - key_agreement, - trapdoor_derivation_function, - secret_key, - public_key, - cs, - ); - Self::utxo_var(utxo_commitment, &trapdoor, asset, cs) + let trapdoor = Self::trapdoor_var(key_agreement, secret_key, public_key, cs); + utxo_commitment.commit(&trapdoor, asset, cs) } - /// Generates the void number associated to `trapdoor` and `secret_key` using `parameters`. + /// Generates the void number associated to `utxo` and `secret_key` using `parameters`. #[inline] fn void_number( - parameters: &Self::VoidNumberCommitmentScheme, - trapdoor: &Trapdoor<Self>, + parameters: &Self::VoidNumberHashFunction, + utxo: &Utxo<Self>, secret_key: &SecretKey<Self>, ) -> VoidNumber<Self> { - parameters.commit(trapdoor, secret_key, &mut ()) + parameters.start().update(utxo).update(secret_key).hash() } - /// Generates the void number associated to `trapdoor` and `secret_key` using `parameters`. + /// Generates the void number associated to `utxo` and `secret_key` using `parameters`. #[inline] fn void_number_var( - parameters: &Self::VoidNumberCommitmentSchemeVar, - trapdoor: &TrapdoorVar<Self>, + parameters: &Self::VoidNumberHashFunctionVar, + utxo: &UtxoVar<Self>, secret_key: &SecretKeyVar<Self>, cs: &mut Self::Compiler, ) -> VoidNumberVar<Self> { - parameters.commit(trapdoor, secret_key, cs) + parameters + .start() + .update(utxo) + .update(secret_key) + .hash_with_compiler(cs) } /// Checks that the `utxo` is correctly constructed from the `secret_key`, `public_key`, and @@ -348,15 +268,14 @@ pub trait Configuration { asset: &Asset, utxo: &Utxo<Self>, ) -> Option<VoidNumber<Self>> { - let trapdoor = Self::trapdoor( + (&Self::utxo( &parameters.key_agreement, - &parameters.trapdoor_derivation_function, + &parameters.utxo_commitment, secret_key, public_key, - ); - (&Self::utxo(&parameters.utxo_commitment, &trapdoor, asset) == utxo).then(move || { - Self::void_number(&parameters.void_number_commitment, &trapdoor, secret_key) - }) + asset, + ) == utxo) + .then(move || Self::void_number(&parameters.void_number_hash, utxo, secret_key)) } } @@ -377,28 +296,13 @@ pub type PublicKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreemen pub type PublicKeyVar<C> = <<C as Configuration>::KeyAgreementSchemeVar as KeyAgreementScheme<Compiler<C>>>::PublicKey; -/// Shared Secret Type -pub type SharedSecret<C> = +/// +pub type Trapdoor<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret; -/// Shared Secret Variable Type -pub type SharedSecretVar<C> = - <<C as Configuration>::KeyAgreementSchemeVar as KeyAgreementScheme<Compiler<C>>>::SharedSecret; - -/// Ephemeral Key Trapdoor Type -pub type EphemeralKeyTrapdoor<C> = - <<C as Configuration>::EphemeralKeyCommitmentScheme as CommitmentScheme>::Trapdoor; - -/// Ephemeral Key Trapdoor Variable Type -pub type EphemeralKeyTrapdoorVar<C> = - <<C as Configuration>::EphemeralKeyCommitmentSchemeVar as CommitmentScheme<Compiler<C>>>::Trapdoor; - -/// Trapdoor Type -pub type Trapdoor<C> = <<C as Configuration>::UtxoCommitmentScheme as CommitmentScheme>::Trapdoor; - -/// Trapdoor Variable Type +/// pub type TrapdoorVar<C> = - <<C as Configuration>::UtxoCommitmentSchemeVar as CommitmentScheme<Compiler<C>>>::Trapdoor; + <<C as Configuration>::KeyAgreementSchemeVar as KeyAgreementScheme<Compiler<C>>>::SharedSecret; /// Unspend Transaction Output Type pub type Utxo<C> = <<C as Configuration>::UtxoCommitmentScheme as CommitmentScheme>::Output; @@ -407,6 +311,13 @@ pub type Utxo<C> = <<C as Configuration>::UtxoCommitmentScheme as CommitmentSche pub type UtxoVar<C> = <<C as Configuration>::UtxoCommitmentSchemeVar as CommitmentScheme<Compiler<C>>>::Output; +/// Void Number Type +pub type VoidNumber<C> = <<C as Configuration>::VoidNumberHashFunction as HashFunction>::Output; + +/// Void Number Variable Type +pub type VoidNumberVar<C> = + <<C as Configuration>::VoidNumberHashFunctionVar as HashFunction<Compiler<C>>>::Output; + /// UTXO Set Witness Type pub type UtxoSetWitness<C> = <<C as Configuration>::UtxoSetModel as Model>::Witness; @@ -420,14 +331,6 @@ pub type UtxoMembershipProof<C> = MembershipProof<<C as Configuration>::UtxoSetM pub type UtxoMembershipProofVar<C> = MembershipProof<<C as Configuration>::UtxoSetModelVar, Compiler<C>>; -/// Void Number Type -pub type VoidNumber<C> = - <<C as Configuration>::VoidNumberCommitmentScheme as CommitmentScheme>::Output; - -/// Void Number Variable Type -pub type VoidNumberVar<C> = - <<C as Configuration>::VoidNumberCommitmentSchemeVar as CommitmentScheme<Compiler<C>>>::Output; - /// Encrypted Note Type pub type EncryptedNote<C> = EncryptedMessage<<C as Configuration>::NoteEncryptionScheme>; @@ -463,17 +366,11 @@ where /// Key Agreement Scheme pub key_agreement: C::KeyAgreementScheme, - /// Ephemeral Key Commitment Scheme - pub ephemeral_key_commitment: C::EphemeralKeyCommitmentScheme, - - /// Trapdoor Derivation Function - pub trapdoor_derivation_function: C::TrapdoorDerivationFunction, - /// UTXO Commitment Scheme pub utxo_commitment: C::UtxoCommitmentScheme, - /// Void Number Commitment Scheme - pub void_number_commitment: C::VoidNumberCommitmentScheme, + /// Void Number Hash Function + pub void_number_hash: C::VoidNumberHashFunction, } /// Transfer Full Parameters @@ -510,17 +407,11 @@ where /// Key Agreement Scheme pub key_agreement: C::KeyAgreementSchemeVar, - /// Ephemeral Key Commitment Scheme - pub ephemeral_key_commitment: C::EphemeralKeyCommitmentSchemeVar, - - /// Trapdoor Derivation Function - pub trapdoor_derivation_function: C::TrapdoorDerivationFunctionVar, - /// UTXO Commitment Scheme pub utxo_commitment: C::UtxoCommitmentSchemeVar, - /// Void Number Commitment Scheme - pub void_number_commitment: C::VoidNumberCommitmentSchemeVar, + /// Void Number Hash Function + pub void_number_hash: C::VoidNumberHashFunctionVar, /// UTXO Set Model pub utxo_set_model: C::UtxoSetModelVar, @@ -543,13 +434,8 @@ where match allocation { Allocation::Known(this, mode) => Self { key_agreement: this.base.key_agreement.as_known(cs, mode), - ephemeral_key_commitment: this.base.ephemeral_key_commitment.as_known(cs, mode), - trapdoor_derivation_function: this - .base - .trapdoor_derivation_function - .as_known(cs, mode), utxo_commitment: this.base.utxo_commitment.as_known(cs, mode), - void_number_commitment: this.base.void_number_commitment.as_known(cs, mode), + void_number_hash: this.base.void_number_hash.as_known(cs, mode), utxo_set_model: this.utxo_set_model.as_known(cs, mode), __: PhantomData, }, @@ -595,10 +481,10 @@ where #[inline] pub fn decrypt( &self, - key_agreement: &C::KeyAgreementScheme, + parameters: &C::KeyAgreementScheme, encrypted_note: EncryptedNote<C>, ) -> Result<Note<C>, EncryptedNote<C>> { - encrypted_note.decrypt(key_agreement, &Default::default(), &self.view) + encrypted_note.decrypt(parameters, &self.view) } /// Validates the `utxo` against `self` and the given `ephemeral_key` and `asset`, returning @@ -631,14 +517,11 @@ where pub fn receiver( &self, parameters: &Parameters<C>, - ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, + ephemeral_key: SecretKey<C>, asset: Asset, ) -> Receiver<C> { - self.derive(&parameters.key_agreement).into_receiver( - parameters, - ephemeral_key_trapdoor, - asset, - ) + self.derive(&parameters.key_agreement) + .into_receiver(parameters, ephemeral_key, asset) } /// Returns an receiver-sender pair for internal transactions. @@ -646,10 +529,10 @@ where pub fn internal_pair( &self, parameters: &Parameters<C>, - ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, + ephemeral_key: SecretKey<C>, asset: Asset, ) -> (Receiver<C>, PreSender<C>) { - let receiver = self.receiver(parameters, ephemeral_key_trapdoor, asset); + let receiver = self.receiver(parameters, ephemeral_key, asset); let sender = self.sender(parameters, receiver.ephemeral_public_key().clone(), asset); (receiver, sender) } @@ -659,10 +542,10 @@ where pub fn internal_zero_pair( &self, parameters: &Parameters<C>, - ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, + ephemeral_key: SecretKey<C>, asset_id: AssetId, ) -> (Receiver<C>, PreSender<C>) { - self.internal_pair(parameters, ephemeral_key_trapdoor, Asset::zero(asset_id)) + self.internal_pair(parameters, ephemeral_key, Asset::zero(asset_id)) } } @@ -696,16 +579,10 @@ where pub fn into_receiver( self, parameters: &Parameters<C>, - ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, + ephemeral_key: SecretKey<C>, asset: Asset, ) -> Receiver<C> { - Receiver::new( - parameters, - ephemeral_key_trapdoor, - self.spend, - self.view, - asset, - ) + Receiver::new(parameters, ephemeral_key, self.spend, self.view, asset) } } @@ -742,18 +619,19 @@ where ephemeral_public_key: PublicKey<C>, asset: Asset, ) -> Self { - let trapdoor = C::trapdoor( + let utxo = C::utxo( &parameters.key_agreement, - &parameters.trapdoor_derivation_function, + &parameters.utxo_commitment, &spend, &ephemeral_public_key, + &asset, ); Self { - utxo: C::utxo(&parameters.utxo_commitment, &trapdoor, &asset), - void_number: C::void_number(&parameters.void_number_commitment, &trapdoor, &spend), + void_number: C::void_number(&parameters.void_number_hash, &utxo, &spend), spend, ephemeral_public_key, asset, + utxo, } } @@ -917,24 +795,19 @@ where parameters: &FullParametersVar<C>, cs: &mut C::Compiler, ) -> AssetVar<C> { - let trapdoor = C::trapdoor_var( + let utxo = C::utxo_var( &parameters.key_agreement, - &parameters.trapdoor_derivation_function, + &parameters.utxo_commitment, &self.spend, &self.ephemeral_public_key, + &self.asset, cs, ); - let utxo = C::utxo_var(&parameters.utxo_commitment, &trapdoor, &self.asset, cs); let is_valid_proof = self.utxo_membership_proof .verify_with_compiler(&parameters.utxo_set_model, &utxo, cs); cs.assert(is_valid_proof); - let void_number = C::void_number_var( - &parameters.void_number_commitment, - &trapdoor, - &self.spend, - cs, - ); + let void_number = C::void_number_var(&parameters.void_number_hash, &utxo, &self.spend, cs); cs.assert_eq(&self.void_number, &void_number); self.asset } @@ -1116,8 +989,8 @@ pub struct Receiver<C> where C: Configuration, { - /// Ephemeral Secret Spend Key Trapdoor - ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, + /// Ephemeral Secret Spend Key + ephemeral_secret_key: SecretKey<C>, /// Public Spend Key spend: PublicKey<C>, @@ -1140,20 +1013,14 @@ where #[inline] pub fn new( parameters: &Parameters<C>, - ephemeral_key_trapdoor: EphemeralKeyTrapdoor<C>, + ephemeral_secret_key: SecretKey<C>, spend: PublicKey<C>, view: PublicKey<C>, asset: Asset, ) -> Self { - let ephemeral_secret_key = C::ephemeral_secret_key( - &parameters.ephemeral_key_commitment, - &ephemeral_key_trapdoor, - asset, - ); Self { - utxo: C::full_utxo( + utxo: C::utxo( &parameters.key_agreement, - &parameters.trapdoor_derivation_function, &parameters.utxo_commitment, &ephemeral_secret_key, &spend, @@ -1161,12 +1028,11 @@ where ), note: EncryptedMessage::new( &parameters.key_agreement, - &Default::default(), &view, &ephemeral_secret_key, asset, ), - ephemeral_key_trapdoor, + ephemeral_secret_key, spend, asset, } @@ -1193,8 +1059,8 @@ pub struct ReceiverVar<C> where C: Configuration, { - /// Ephemeral Secret Spend Key Trapdoor - ephemeral_key_trapdoor: EphemeralKeyTrapdoorVar<C>, + /// Ephemeral Secret Spend Key + ephemeral_secret_key: SecretKeyVar<C>, /// Ephemeral Public Spend Key ephemeral_public_key: PublicKeyVar<C>, @@ -1221,20 +1087,13 @@ where parameters: &FullParametersVar<C>, cs: &mut C::Compiler, ) -> AssetVar<C> { - let ephemeral_secret_key = C::ephemeral_secret_key_var( - &parameters.ephemeral_key_commitment, - &self.ephemeral_key_trapdoor, - &self.asset, - cs, - ); let ephemeral_public_key = - C::ephemeral_public_key_var(&parameters.key_agreement, &ephemeral_secret_key, cs); + C::ephemeral_public_key_var(&parameters.key_agreement, &self.ephemeral_secret_key, cs); cs.assert_eq(&self.ephemeral_public_key, &ephemeral_public_key); - let utxo = C::full_utxo_var( + let utxo = C::utxo_var( &parameters.key_agreement, - &parameters.trapdoor_derivation_function, &parameters.utxo_commitment, - &ephemeral_secret_key, + &self.ephemeral_secret_key, &self.spend, &self.asset, cs, @@ -1256,14 +1115,14 @@ where fn new(cs: &mut C::Compiler, allocation: Allocation<Self::Type, Self::Mode>) -> Self { match allocation { Allocation::Known(this, mode) => Self { - ephemeral_key_trapdoor: this.ephemeral_key_trapdoor.as_known(cs, mode), + ephemeral_secret_key: this.ephemeral_secret_key.as_known(cs, mode), ephemeral_public_key: this.ephemeral_public_key().as_known(cs, mode), spend: this.spend.as_known(cs, mode), asset: this.asset.as_known(cs, mode), utxo: this.utxo.as_known(cs, Public), }, Allocation::Unknown(mode) => Self { - ephemeral_key_trapdoor: EphemeralKeyTrapdoorVar::<C>::new_unknown(cs, mode), + ephemeral_secret_key: SecretKeyVar::<C>::new_unknown(cs, mode), ephemeral_public_key: PublicKeyVar::<C>::new_unknown(cs, mode), spend: PublicKeyVar::<C>::new_unknown(cs, mode), asset: AssetVar::<C>::new_unknown(cs, mode), diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 61ef90bf1..5472feba2 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -340,7 +340,7 @@ where }, }) = self .account() - .find_index(|k| finder.decrypt(&self.parameters.key_agreement, &Default::default(), k)) + .find_index(|k| finder.decrypt(&self.parameters.key_agreement, k)) .map_err(key::Error::KeyDerivationError)? { if let Some(void_number) = C::check_full_asset( diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index 72899b676..ba1e4d975 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -16,7 +16,7 @@ //! Commitment Schemes -use core::{fmt::Debug, hash::Hash}; +pub use crate::util::{Builder, Input}; /// Commitment Scheme pub trait CommitmentScheme<COM = ()> { @@ -39,7 +39,10 @@ pub trait CommitmentScheme<COM = ()> { /// Starts a new [`Builder`] for extended commitments. #[inline] - fn start<'c>(&'c self, trapdoor: &'c Self::Trapdoor) -> Builder<'c, Self, COM> + fn start<'c>( + &'c self, + trapdoor: &'c Self::Trapdoor, + ) -> Builder<'c, Self, Self::Input, Self::Trapdoor> where Self::Input: Default, { @@ -47,98 +50,22 @@ pub trait CommitmentScheme<COM = ()> { } } -/// Commitment Extended Input -pub trait Input<T> -where - T: ?Sized, -{ - /// Extends `self` with input data `next`. - fn extend(&mut self, next: &T); -} - -/// Commitment Builder -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "C::Input: Clone"), - Copy(bound = "C::Input: Copy"), - Debug(bound = "C: Debug, C::Trapdoor: Debug, C::Input: Debug"), - Eq(bound = "C: Eq, C::Trapdoor: Eq, C::Input: Eq"), - Hash(bound = "C: Hash, C::Trapdoor: Hash, C::Input: Hash"), - PartialEq(bound = "C: PartialEq, C::Trapdoor: PartialEq, C::Input: PartialEq") -)] -pub struct Builder<'c, C, COM = ()> -where - C: CommitmentScheme<COM> + ?Sized, - C::Input: Default, -{ - /// Commitment Scheme - commitment_scheme: &'c C, - - /// Commitment Trapdoor - trapdoor: &'c C::Trapdoor, - - /// Commitment Input Accumulator - input: C::Input, -} - -impl<'c, C, COM> Builder<'c, C, COM> -where - C: CommitmentScheme<COM> + ?Sized, - C::Input: Default, -{ - /// Returns a new [`Builder`] with fixed `commitment_scheme` and `trapdoor`. +impl<'c, C, I, T> Builder<'c, C, I, T> { + /// Commits to the input stored in the builder against the given `compiler`. #[inline] - pub fn new(commitment_scheme: &'c C, trapdoor: &'c C::Trapdoor) -> Self { - Self { - commitment_scheme, - trapdoor, - input: Default::default(), - } - } - - /// Updates the builder with the `next` input. - #[inline] - #[must_use] - pub fn update<T>(mut self, next: &T) -> Self + pub fn commit_with_compiler<COM>(self, compiler: &mut COM) -> C::Output where - T: ?Sized, - C::Input: Input<T>, + C: CommitmentScheme<COM, Trapdoor = T, Input = I>, { - self.input.extend(next); - self + self.base.commit(self.args, &self.input, compiler) } - /// Updates the builder with each item in `iter`. + /// Commits to the input stored in the builder. #[inline] - #[must_use] - pub fn update_all<'t, T, I>(mut self, iter: I) -> Self + pub fn commit(self) -> C::Output where - T: 't + ?Sized, - I: IntoIterator<Item = &'t T>, - C::Input: Input<T>, + C: CommitmentScheme<Trapdoor = T, Input = I>, { - for next in iter { - self.input.extend(next); - } - self - } - - /// Commits to the input stored in the builder. - #[inline] - pub fn commit_with_compiler(self, compiler: &mut COM) -> C::Output { - self.commitment_scheme - .commit(self.trapdoor, &self.input, compiler) - } -} - -impl<'c, C> Builder<'c, C> -where - C: CommitmentScheme + ?Sized, - C::Input: Default, -{ - /// Commits to the input stored in the builder. - #[inline] - pub fn commit(self) -> C::Output { self.commit_with_compiler(&mut ()) } } diff --git a/manta-crypto/src/ecc.rs b/manta-crypto/src/ecc.rs index 945860b0c..23754d129 100644 --- a/manta-crypto/src/ecc.rs +++ b/manta-crypto/src/ecc.rs @@ -18,15 +18,22 @@ // TODO: Improve ECC abstractions over arkworks. -/// -pub trait Coordinates<C, J = ()> +use crate::{ + constraint::{Allocation, AllocationMode, Constant, Public, Variable, VariableSource}, + key::KeyAgreementScheme, +}; +use core::marker::PhantomData; + +/* TODO: +/// Elliptic Curve Coordinate System +pub trait Coordinates<C, COM = ()> where - C: Curve<J>, + C: Curve<COM>, { } /// Elliptic Curve -pub trait Curve<J = ()> { +pub trait Curve<COM = ()> { /// Base Field type BaseField; @@ -35,10 +42,121 @@ pub trait Curve<J = ()> { } /// Embedded Elliptic Curve -pub trait EmbeddedCurve<C, J = ()>: Curve<J, BaseField = C::ScalarField> +pub trait EmbeddedCurve<C, COM = ()>: Curve<COM, BaseField = C::ScalarField> where - C: Curve<J>, + C: Curve<COM>, { /// - fn lift_scalar(compiler: &mut J, scalar: Self::ScalarField) -> C::ScalarField; + fn lift_scalar(scalar: Self::ScalarField, compiler: &mut COM) -> C::ScalarField; +} +*/ + +/// Elliptic Curve Group +pub trait Group<COM = ()>: Sized { + /// Scalar Field + type Scalar; + + /// Adds `rhs` to `self` in `compiler`, returning a new group element. + #[must_use] + fn add(&self, rhs: &Self, compiler: &mut COM) -> Self; + + /// Adds an owned `rhs` value to `self` in `compiler`, returning a new group element. + /// + /// # Implementation Note + /// + /// This method is an optimization path for [`add`](Self::add) whenever operating with owned + /// values is more efficient than with shared references. + #[inline] + #[must_use] + fn add_owned(self, rhs: Self, compiler: &mut COM) -> Self { + self.add(&rhs, compiler) + } + + /// Multiplies `self` by `scalar` in `compiler`, returning a new group element. + #[must_use] + fn scalar_mul(&self, scalar: &Self::Scalar, compiler: &mut COM) -> Self; +} + +/// Elliptic-Curve Diffie Hellman Key Exchange +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +pub struct DiffieHellman<G, COM = ()> +where + G: Group<COM>, +{ + /// Base Generator + generator: G, + + /// Type Parameter Marker + __: PhantomData<COM>, +} + +impl<G, COM> DiffieHellman<G, COM> +where + G: Group<COM>, +{ + /// Builds a new [`DiffieHellman`] protocol structure from `generator`. + #[inline] + pub fn new(generator: G) -> Self { + Self { + generator, + __: PhantomData, + } + } + + /// Returns a shared reference to the generator for this protocol. + #[inline] + pub fn generator(&self) -> &G { + &self.generator + } + + /// Converts `self` into its underlying generator. + #[inline] + pub fn into_generator(self) -> G { + self.generator + } +} + +impl<G, COM> KeyAgreementScheme<COM> for DiffieHellman<G, COM> +where + G: Group<COM>, +{ + type SecretKey = G::Scalar; + + type PublicKey = G; + + type SharedSecret = G; + + #[inline] + fn derive(&self, secret_key: &Self::SecretKey, compiler: &mut COM) -> Self::PublicKey { + self.agree(secret_key, &self.generator, compiler) + } + + #[inline] + fn agree( + &self, + secret_key: &Self::SecretKey, + public_key: &Self::PublicKey, + compiler: &mut COM, + ) -> Self::SharedSecret { + public_key.scalar_mul(secret_key, compiler) + } +} + +impl<G, COM> Variable<COM> for DiffieHellman<G, COM> +where + G: Group<COM> + Variable<COM>, + <G::Mode as AllocationMode>::Known: From<Public>, +{ + type Type = G::Type; + + type Mode = Constant; + + #[inline] + fn new(cs: &mut COM, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + match allocation { + Allocation::Known(this, _) => Self::new(this.as_known(cs, Public)), + _ => unreachable!("Constants are never unknown."), + } + } } diff --git a/manta-crypto/src/encryption.rs b/manta-crypto/src/encryption.rs index 7448ed09c..247ec0c09 100644 --- a/manta-crypto/src/encryption.rs +++ b/manta-crypto/src/encryption.rs @@ -49,6 +49,43 @@ pub trait SymmetricKeyEncryptionScheme { fn decrypt(key: Self::Key, ciphertext: &Self::Ciphertext) -> Option<Self::Plaintext>; } +/// Constant-Size Symmetric-Key Encryption Scheme +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct ConstantSizeSymmetricKeyEncryption<const SIZE: usize, S, P = [u8; SIZE], C = [u8; SIZE]> +where + S: SymmetricKeyEncryptionScheme<Plaintext = [u8; SIZE], Ciphertext = [u8; SIZE]>, + P: Into<[u8; SIZE]> + TryFrom<[u8; SIZE]>, + C: AsRef<[u8; SIZE]> + From<[u8; SIZE]>, +{ + /// Type Parameter Marker + __: PhantomData<(S, P, C)>, +} + +impl<const SIZE: usize, S, P, C> SymmetricKeyEncryptionScheme + for ConstantSizeSymmetricKeyEncryption<SIZE, S, P, C> +where + S: SymmetricKeyEncryptionScheme<Plaintext = [u8; SIZE], Ciphertext = [u8; SIZE]>, + P: Into<[u8; SIZE]> + TryFrom<[u8; SIZE]>, + C: AsRef<[u8; SIZE]> + From<[u8; SIZE]>, +{ + type Key = S::Key; + + type Plaintext = P; + + type Ciphertext = C; + + #[inline] + fn encrypt(key: Self::Key, plaintext: Self::Plaintext) -> Self::Ciphertext { + S::encrypt(key, plaintext.into()).into() + } + + #[inline] + fn decrypt(key: Self::Key, ciphertext: &Self::Ciphertext) -> Option<Self::Plaintext> { + S::decrypt(key, ciphertext.as_ref()).and_then(move |p| p.try_into().ok()) + } +} + /// Hybrid Public Key Encryption Scheme pub trait HybridPublicKeyEncryptionScheme: SymmetricKeyEncryptionScheme { /// Key Agreement Scheme Type @@ -69,12 +106,11 @@ pub trait HybridPublicKeyEncryptionScheme: SymmetricKeyEncryptionScheme { /// [`KeyDerivationFunction::derive`]. #[inline] fn agree_derive( - agreement: &Self::KeyAgreementScheme, - derivation: &Self::KeyDerivationFunction, + parameters: &Self::KeyAgreementScheme, secret_key: &SecretKey<Self>, public_key: &PublicKey<Self>, ) -> Self::Key { - derivation.derive(agreement.agree(secret_key, public_key, &mut ()), &mut ()) + Self::KeyDerivationFunction::derive(&parameters.agree(secret_key, public_key, &mut ())) } } @@ -161,18 +197,17 @@ where /// and an `ephemeral_secret_key`. #[inline] pub fn new( - agreement: &H::KeyAgreementScheme, - derivation: &H::KeyDerivationFunction, + parameters: &H::KeyAgreementScheme, public_key: &PublicKey<H>, ephemeral_secret_key: &SecretKey<H>, plaintext: H::Plaintext, ) -> Self { Self { ciphertext: H::encrypt( - H::agree_derive(agreement, derivation, ephemeral_secret_key, public_key), + H::agree_derive(parameters, ephemeral_secret_key, public_key), plaintext, ), - ephemeral_public_key: agreement.derive(ephemeral_secret_key, &mut ()), + ephemeral_public_key: parameters.derive(ephemeral_secret_key, &mut ()), } } @@ -187,17 +222,11 @@ where #[inline] pub fn decrypt( self, - agreement: &H::KeyAgreementScheme, - derivation: &H::KeyDerivationFunction, + parameters: &H::KeyAgreementScheme, secret_key: &SecretKey<H>, ) -> Result<DecryptedMessage<H>, Self> { match H::decrypt( - H::agree_derive( - agreement, - derivation, - secret_key, - &self.ephemeral_public_key, - ), + H::agree_derive(parameters, secret_key, &self.ephemeral_public_key), &self.ciphertext, ) { Some(plaintext) => Ok(DecryptedMessage::new(plaintext, self.ephemeral_public_key)), @@ -270,12 +299,11 @@ where #[inline] pub fn decrypt( &mut self, - agreement: &H::KeyAgreementScheme, - derivation: &H::KeyDerivationFunction, + parameters: &H::KeyAgreementScheme, secret_key: &SecretKey<H>, ) -> Option<DecryptedMessage<H>> { if let Some(message) = self.encrypted_message.take() { - match message.decrypt(agreement, derivation, secret_key) { + match message.decrypt(parameters, secret_key) { Ok(decrypted_message) => return Some(decrypted_message), Err(message) => self.encrypted_message = Some(message), } diff --git a/manta-crypto/src/hash.rs b/manta-crypto/src/hash.rs new file mode 100644 index 000000000..da719440e --- /dev/null +++ b/manta-crypto/src/hash.rs @@ -0,0 +1,60 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Hash Functions + +pub use crate::util::{Builder, Input}; + +/// Hash Function +pub trait HashFunction<COM = ()> { + /// Input Type + type Input: ?Sized; + + /// Output Type + type Output; + + /// Performs a hash over `input` in the `compiler`. + fn hash(&self, input: &Self::Input, compiler: &mut COM) -> Self::Output; + + /// Starts a new [`Builder`] for extended hashes. + #[inline] + fn start(&self) -> Builder<Self, Self::Input> + where + Self::Input: Default, + { + Builder::new(self, &()) + } +} + +impl<'h, H, I> Builder<'h, H, I> { + /// Hashes the input stored in the builder against the given `compiler`. + #[inline] + pub fn hash_with_compiler<COM>(self, compiler: &mut COM) -> H::Output + where + H: HashFunction<COM, Input = I>, + { + self.base.hash(&self.input, compiler) + } + + /// Hashes the input stored in the builder. + #[inline] + pub fn hash(self) -> H::Output + where + H: HashFunction<Input = I>, + { + self.hash_with_compiler(&mut ()) + } +} diff --git a/manta-crypto/src/key.rs b/manta-crypto/src/key.rs index 93dfa2e44..232bcffdd 100644 --- a/manta-crypto/src/key.rs +++ b/manta-crypto/src/key.rs @@ -16,16 +16,45 @@ //! Cryptographic Key Primitives +use core::marker::PhantomData; + /// Key Derivation Function -pub trait KeyDerivationFunction<COM = ()> { +pub trait KeyDerivationFunction { /// Input Key Type - type Key; + type Key: ?Sized; /// Output Key Type type Output; /// Derives an output key from `secret` computed from a cryptographic agreement scheme. - fn derive(&self, secret: Self::Key, compiler: &mut COM) -> Self::Output; + fn derive(secret: &Self::Key) -> Self::Output; +} + +/// Key-Bytes Derivation Function +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct KeyBytesDerivationFunction<T, F> +where + T: AsRef<[u8]>, + F: KeyDerivationFunction<Key = [u8]>, +{ + /// Type Parameter Marker + __: PhantomData<(T, F)>, +} + +impl<T, F> KeyDerivationFunction for KeyBytesDerivationFunction<T, F> +where + T: AsRef<[u8]>, + F: KeyDerivationFunction<Key = [u8]>, +{ + type Key = T; + + type Output = F::Output; + + #[inline] + fn derive(secret: &Self::Key) -> Self::Output { + F::derive(secret.as_ref()) + } } /// Key Agreement Scheme diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index c0e205a23..20b7b0114 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -28,6 +28,8 @@ pub mod commitment; pub mod constraint; pub mod ecc; pub mod encryption; +pub mod hash; pub mod key; pub mod merkle_tree; pub mod rand; +pub mod util; diff --git a/manta-crypto/src/util.rs b/manta-crypto/src/util.rs new file mode 100644 index 000000000..ed285cb0a --- /dev/null +++ b/manta-crypto/src/util.rs @@ -0,0 +1,99 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Utilities + +use core::{fmt::Debug, hash::Hash}; + +/// Extended Input +pub trait Input<T> +where + T: ?Sized, +{ + /// Extends `self` with input data `next`. + fn extend(&mut self, next: &T); +} + +/// Extended Input Builder +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "I: Clone"), + Copy(bound = "I: Copy"), + Debug(bound = "F: Debug, I: Debug, Args: Debug"), + Eq(bound = "F: Eq, I: Eq, Args: Eq"), + Hash(bound = "F: Hash, I: Hash, Args: Hash"), + PartialEq(bound = "F: PartialEq, I: PartialEq, Args: PartialEq") +)] +pub struct Builder<'f, F, I, Args = ()> +where + F: ?Sized, + Args: ?Sized, +{ + /// Base Construction + pub(crate) base: &'f F, + + /// Stored Arguments + pub(crate) args: &'f Args, + + /// Input Data + pub(crate) input: I, +} + +impl<'f, F, I, Args> Builder<'f, F, I, Args> +where + F: ?Sized, + Args: ?Sized, +{ + /// Returns a new [`Builder`] for the given `base`. + #[inline] + pub(crate) fn new(base: &'f F, args: &'f Args) -> Self + where + I: Default, + { + Self { + base, + args, + input: Default::default(), + } + } + + /// Updates the builder with the `next` input. + #[inline] + #[must_use] + pub fn update<T>(mut self, next: &T) -> Self + where + T: ?Sized, + I: Input<T>, + { + self.input.extend(next); + self + } + + /// Updates the builder with each item in `iter`. + #[inline] + #[must_use] + pub fn update_all<'t, T, Iter>(mut self, iter: Iter) -> Self + where + T: 't + ?Sized, + Iter: IntoIterator<Item = &'t T>, + I: Input<T>, + { + for next in iter { + self.input.extend(next); + } + self + } +} diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 6e1aa7271..d745a9f1e 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -146,6 +146,7 @@ impl KeyDerivationFunction for TrapdoorDerivationFunction { #[inline] fn derive(&self, secret: Self::Key, compiler: &mut ()) -> Self::Output { + // FIXME: We need to truncate the field element to get it to fit into an embedded scalar. let affine = <Self::Key as ProjectiveCurve>::Affine::from(secret); self.0 .commit(&Default::default(), &[affine.x, affine.y], compiler) @@ -162,6 +163,7 @@ impl KeyDerivationFunction<Compiler> for TrapdoorDerivationFunctionVar { #[inline] fn derive(&self, secret: Self::Key, compiler: &mut Compiler) -> Self::Output { + // FIXME: We need to truncate the field element to get it to fit into an embedded scalar. /* TODO: self.0 .commit(&Default::default(), &[secret.x, secret.y], compiler) diff --git a/manta-pay/src/crypto/key/elliptic_curve_diffie_hellman.rs b/manta-pay/src/crypto/ecc.rs similarity index 75% rename from manta-pay/src/crypto/key/elliptic_curve_diffie_hellman.rs rename to manta-pay/src/crypto/ecc.rs index 22051d293..b3f65dda6 100644 --- a/manta-pay/src/crypto/key/elliptic_curve_diffie_hellman.rs +++ b/manta-pay/src/crypto/ecc.rs @@ -14,56 +14,7 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Elliptic Curve Diffie Hellman - -use manta_crypto::key::KeyAgreementScheme; - -/// Elliptic Curve Diffie Hellman Specification -pub trait Specification<COM = ()> { - /// Group Type - type Group; - - /// Scalar Type - type Scalar; - - /// Multiplies `point` by `scalar`. - fn scalar_mul(point: &Self::Group, scalar: &Self::Scalar, compiler: &mut COM) -> Self::Group; -} - -/// Key Agreement Protocol -pub struct KeyAgreement<S, COM = ()> -where - S: Specification<COM>, -{ - /// Base Generator - pub generator: S::Group, -} - -impl<S, COM> KeyAgreementScheme<COM> for KeyAgreement<S, COM> -where - S: Specification<COM>, -{ - type SecretKey = S::Scalar; - - type PublicKey = S::Group; - - type SharedSecret = S::Group; - - #[inline] - fn derive(&self, secret_key: &Self::SecretKey, compiler: &mut COM) -> Self::PublicKey { - S::scalar_mul(&self.generator, secret_key, compiler) - } - - #[inline] - fn agree( - &self, - secret_key: &Self::SecretKey, - public_key: &Self::PublicKey, - compiler: &mut COM, - ) -> Self::SharedSecret { - S::scalar_mul(public_key, secret_key, compiler) - } -} +//! Elliptic Curve Primitives /// Arkworks Backend #[cfg(feature = "arkworks")] @@ -83,6 +34,7 @@ pub mod arkworks { /// Compiler Type type Compiler<C> = R1CS<ConstraintField<C>>; + /* /// Specification pub struct Specification<C, CV>(PhantomData<(C, CV)>) where @@ -156,4 +108,5 @@ pub mod arkworks { } } } + */ } diff --git a/manta-pay/src/crypto/encryption.rs b/manta-pay/src/crypto/encryption.rs index 32d3144e8..9276ffaf4 100644 --- a/manta-pay/src/crypto/encryption.rs +++ b/manta-pay/src/crypto/encryption.rs @@ -17,37 +17,27 @@ //! Encryption Implementations // FIXME: Don't use raw bytes as encryption/decryption key. -// FIXME: Make sure secret keys are protected. use aes_gcm::{ aead::{Aead, NewAead}, Aes256Gcm, Nonce, }; -use core::marker::PhantomData; use generic_array::GenericArray; use manta_crypto::encryption::SymmetricKeyEncryptionScheme; use manta_util::into_array_unchecked; /// AES Galois Counter Mode -pub struct AesGcm<T, const SIZE: usize>(PhantomData<T>) -where - T: Into<[u8; SIZE]> + From<[u8; SIZE]>; +pub struct AesGcm<const SIZE: usize>; -impl<T, const SIZE: usize> AesGcm<T, SIZE> -where - T: Into<[u8; SIZE]> + From<[u8; SIZE]>, -{ +impl<const SIZE: usize> AesGcm<SIZE> { /// Encryption/Decryption Nonce const NONCE: &'static [u8] = b"manta rocks!"; } -impl<T, const SIZE: usize> SymmetricKeyEncryptionScheme for AesGcm<T, SIZE> -where - T: Into<[u8; SIZE]> + From<[u8; SIZE]>, -{ +impl<const SIZE: usize> SymmetricKeyEncryptionScheme for AesGcm<SIZE> { type Key = [u8; 32]; - type Plaintext = T; + type Plaintext = [u8; SIZE]; type Ciphertext = [u8; SIZE]; @@ -56,7 +46,7 @@ where // SAFETY: Using a deterministic nonce is ok since we never reuse keys. into_array_unchecked( Aes256Gcm::new(GenericArray::from_slice(&key)) - .encrypt(Nonce::from_slice(Self::NONCE), plaintext.into().as_ref()) + .encrypt(Nonce::from_slice(Self::NONCE), plaintext.as_ref()) .expect("Symmetric encryption is not allowed to fail."), ) } @@ -68,6 +58,5 @@ where .decrypt(Nonce::from_slice(Self::NONCE), ciphertext.as_ref()) .ok() .map(into_array_unchecked) - .map(Into::into) } } diff --git a/manta-pay/src/crypto/key/mod.rs b/manta-pay/src/crypto/key.rs similarity index 76% rename from manta-pay/src/crypto/key/mod.rs rename to manta-pay/src/crypto/key.rs index bc2d9316c..bc4b817db 100644 --- a/manta-pay/src/crypto/key/mod.rs +++ b/manta-pay/src/crypto/key.rs @@ -17,32 +17,23 @@ //! Cryptographic Key Primitive Implementations use blake2::{Blake2s, Digest}; -use core::marker::PhantomData; use manta_crypto::key::KeyDerivationFunction; use manta_util::into_array_unchecked; -pub mod elliptic_curve_diffie_hellman; - /// Blake2s KDF #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Blake2sKdf<T>(PhantomData<T>) -where - T: AsRef<[u8]>; +pub struct Blake2sKdf; -impl<T> KeyDerivationFunction for Blake2sKdf<T> -where - T: AsRef<[u8]>, -{ - type Key = T; +impl KeyDerivationFunction for Blake2sKdf { + type Key = [u8]; type Output = [u8; 32]; #[inline] - fn derive(&self, secret: Self::Key, compiler: &mut ()) -> Self::Output { - let _ = compiler; + fn derive(secret: &Self::Key) -> Self::Output { let mut hasher = Blake2s::new(); - hasher.update(secret.as_ref()); + hasher.update(secret); hasher.update(b"manta kdf instantiated with blake2s hash function"); into_array_unchecked(hasher.finalize()) } diff --git a/manta-pay/src/crypto/mod.rs b/manta-pay/src/crypto/mod.rs index a9b4d16bf..4e31bacd0 100644 --- a/manta-pay/src/crypto/mod.rs +++ b/manta-pay/src/crypto/mod.rs @@ -18,6 +18,7 @@ pub mod commitment; pub mod constraint; +pub mod ecc; pub mod encryption; pub mod key; // TODO: pub mod merkle_tree; diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index 84f3dae57..6a79ffe71 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -28,6 +28,8 @@ pub mod key; // TODO[remove]: pub mod accounting; +/* TODO: #[cfg(all(feature = "arkworks", feature = "groth16"))] #[cfg_attr(doc_cfg, doc(cfg(all(feature = "arkworks", feature = "groth16"))))] pub mod config; +*/ From 95b06d03a442ba9b03100250b72f5ee177818af2 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 2 Jan 2022 22:25:45 -0500 Subject: [PATCH 151/275] wip: move to new protocol --- manta-accounting/src/transfer/mod.rs | 76 ++++++++++++++-------------- manta-crypto/src/hash.rs | 17 ++++++- 2 files changed, 53 insertions(+), 40 deletions(-) diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index 9fc702b99..271a57320 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -27,7 +27,7 @@ use manta_crypto::{ ProofSystem, Public, PublicOrSecret, Secret, Variable, VariableSource, }, encryption::{DecryptedMessage, EncryptedMessage, HybridPublicKeyEncryptionScheme}, - hash::{HashFunction, Input as HashFunctionInput}, + hash::BinaryHashFunction, key::KeyAgreementScheme, rand::{CryptoRng, RngCore, Sample}, }; @@ -101,14 +101,10 @@ pub trait Configuration { /// Void Number Type type VoidNumber: PartialEq; - /// Void Number Hash Function Input Type - type VoidNumberHashFunctionInput: Default - + HashFunctionInput<Self::Utxo> - + HashFunctionInput<Self::SecretKey>; - /// Void Number Hash Function Type - type VoidNumberHashFunction: HashFunction< - Input = Self::VoidNumberHashFunctionInput, + type VoidNumberHashFunction: BinaryHashFunction< + Left = Self::Utxo, + Right = Self::SecretKey, Output = Self::VoidNumber, >; @@ -116,15 +112,11 @@ pub trait Configuration { type VoidNumberVar: Variable<Self::Compiler, Type = Self::VoidNumber, Mode = Public> + Equal<Self::Compiler>; - /// Void Number Hash Function Input Variable Type - type VoidNumberHashFunctionInputVar: Default - + HashFunctionInput<Self::UtxoVar> - + HashFunctionInput<Self::SecretKeyVar>; - /// Void Number Hash Function Variable Type - type VoidNumberHashFunctionVar: HashFunction< + type VoidNumberHashFunctionVar: BinaryHashFunction< Self::Compiler, - Input = Self::VoidNumberHashFunctionInputVar, + Left = Self::UtxoVar, + Right = Self::SecretKeyVar, Output = Self::VoidNumberVar, > + Variable<Self::Compiler, Type = Self::VoidNumberHashFunction, Mode = Constant>; @@ -240,7 +232,7 @@ pub trait Configuration { utxo: &Utxo<Self>, secret_key: &SecretKey<Self>, ) -> VoidNumber<Self> { - parameters.start().update(utxo).update(secret_key).hash() + parameters.hash(utxo, secret_key, &mut ()) } /// Generates the void number associated to `utxo` and `secret_key` using `parameters`. @@ -251,11 +243,7 @@ pub trait Configuration { secret_key: &SecretKeyVar<Self>, cs: &mut Self::Compiler, ) -> VoidNumberVar<Self> { - parameters - .start() - .update(utxo) - .update(secret_key) - .hash_with_compiler(cs) + parameters.hash(utxo, secret_key, cs) } /// Checks that the `utxo` is correctly constructed from the `secret_key`, `public_key`, and @@ -296,11 +284,11 @@ pub type PublicKey<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreemen pub type PublicKeyVar<C> = <<C as Configuration>::KeyAgreementSchemeVar as KeyAgreementScheme<Compiler<C>>>::PublicKey; -/// +/// UTXO Trapdoor Type pub type Trapdoor<C> = <<C as Configuration>::KeyAgreementScheme as KeyAgreementScheme>::SharedSecret; -/// +/// UTXO Trapdoor Variable Type pub type TrapdoorVar<C> = <<C as Configuration>::KeyAgreementSchemeVar as KeyAgreementScheme<Compiler<C>>>::SharedSecret; @@ -312,11 +300,12 @@ pub type UtxoVar<C> = <<C as Configuration>::UtxoCommitmentSchemeVar as CommitmentScheme<Compiler<C>>>::Output; /// Void Number Type -pub type VoidNumber<C> = <<C as Configuration>::VoidNumberHashFunction as HashFunction>::Output; +pub type VoidNumber<C> = + <<C as Configuration>::VoidNumberHashFunction as BinaryHashFunction>::Output; /// Void Number Variable Type pub type VoidNumberVar<C> = - <<C as Configuration>::VoidNumberHashFunctionVar as HashFunction<Compiler<C>>>::Output; + <<C as Configuration>::VoidNumberHashFunctionVar as BinaryHashFunction<Compiler<C>>>::Output; /// UTXO Set Witness Type pub type UtxoSetWitness<C> = <<C as Configuration>::UtxoSetModel as Model>::Witness; @@ -373,6 +362,25 @@ where pub void_number_hash: C::VoidNumberHashFunction, } +impl<C> Parameters<C> +where + C: Configuration + ?Sized, +{ + /// Builds a new [`Parameters`]. + #[inline] + pub fn new( + key_agreement: C::KeyAgreementScheme, + utxo_commitment: C::UtxoCommitmentScheme, + void_number_hash: C::VoidNumberHashFunction, + ) -> Self { + Self { + key_agreement, + utxo_commitment, + void_number_hash, + } + } +} + /// Transfer Full Parameters pub struct FullParameters<'p, C> where @@ -405,16 +413,16 @@ where C: Configuration, { /// Key Agreement Scheme - pub key_agreement: C::KeyAgreementSchemeVar, + key_agreement: C::KeyAgreementSchemeVar, /// UTXO Commitment Scheme - pub utxo_commitment: C::UtxoCommitmentSchemeVar, + utxo_commitment: C::UtxoCommitmentSchemeVar, /// Void Number Hash Function - pub void_number_hash: C::VoidNumberHashFunctionVar, + void_number_hash: C::VoidNumberHashFunctionVar, /// UTXO Set Model - pub utxo_set_model: C::UtxoSetModelVar, + utxo_set_model: C::UtxoSetModelVar, /// Type Parameter Marker __: PhantomData<&'p ()>, @@ -477,16 +485,6 @@ where } } - /// Tries to decrypt `encrypted_note` with the viewing key associated to `self`. - #[inline] - pub fn decrypt( - &self, - parameters: &C::KeyAgreementScheme, - encrypted_note: EncryptedNote<C>, - ) -> Result<Note<C>, EncryptedNote<C>> { - encrypted_note.decrypt(parameters, &self.view) - } - /// Validates the `utxo` against `self` and the given `ephemeral_key` and `asset`, returning /// the void number if the `utxo` is valid. #[inline] diff --git a/manta-crypto/src/hash.rs b/manta-crypto/src/hash.rs index da719440e..6ee102671 100644 --- a/manta-crypto/src/hash.rs +++ b/manta-crypto/src/hash.rs @@ -26,7 +26,7 @@ pub trait HashFunction<COM = ()> { /// Output Type type Output; - /// Performs a hash over `input` in the `compiler`. + /// Performs a hash over `input` in the given `compiler`. fn hash(&self, input: &Self::Input, compiler: &mut COM) -> Self::Output; /// Starts a new [`Builder`] for extended hashes. @@ -39,6 +39,21 @@ pub trait HashFunction<COM = ()> { } } +/// Binary Hash Function +pub trait BinaryHashFunction<COM = ()> { + /// Left Input Type + type Left: ?Sized; + + /// Right Input Type + type Right: ?Sized; + + /// Output Type + type Output; + + /// Performs a hash over `lhs` and `rhs` in the given `compiler`. + fn hash(&self, lhs: &Self::Left, rhs: &Self::Right, compiler: &mut COM) -> Self::Output; +} + impl<'h, H, I> Builder<'h, H, I> { /// Hashes the input stored in the builder against the given `compiler`. #[inline] From 78277c63228ab30a749e917ef4df77dd5701a6a5 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 3 Jan 2022 23:47:52 -0500 Subject: [PATCH 152/275] fix: update to new spec --- manta-crypto/src/encryption.rs | 16 +- manta-crypto/src/key.rs | 75 +++-- manta-pay/Cargo.toml | 2 +- manta-pay/src/config.rs | 275 +++++++++--------- manta-pay/src/crypto/commitment/mod.rs | 1 - manta-pay/src/crypto/ecc.rs | 122 +++++--- manta-pay/src/crypto/hash/mod.rs | 19 ++ .../crypto/{commitment => hash}/poseidon.rs | 66 ++--- manta-pay/src/crypto/mod.rs | 3 +- manta-pay/src/lib.rs | 2 - 10 files changed, 326 insertions(+), 255 deletions(-) create mode 100644 manta-pay/src/crypto/hash/mod.rs rename manta-pay/src/crypto/{commitment => hash}/poseidon.rs (87%) diff --git a/manta-crypto/src/encryption.rs b/manta-crypto/src/encryption.rs index 247ec0c09..e2e384f42 100644 --- a/manta-crypto/src/encryption.rs +++ b/manta-crypto/src/encryption.rs @@ -52,37 +52,35 @@ pub trait SymmetricKeyEncryptionScheme { /// Constant-Size Symmetric-Key Encryption Scheme #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct ConstantSizeSymmetricKeyEncryption<const SIZE: usize, S, P = [u8; SIZE], C = [u8; SIZE]> +pub struct ConstantSizeSymmetricKeyEncryption<const SIZE: usize, S, P = [u8; SIZE]> where S: SymmetricKeyEncryptionScheme<Plaintext = [u8; SIZE], Ciphertext = [u8; SIZE]>, P: Into<[u8; SIZE]> + TryFrom<[u8; SIZE]>, - C: AsRef<[u8; SIZE]> + From<[u8; SIZE]>, { /// Type Parameter Marker - __: PhantomData<(S, P, C)>, + __: PhantomData<(S, P)>, } -impl<const SIZE: usize, S, P, C> SymmetricKeyEncryptionScheme - for ConstantSizeSymmetricKeyEncryption<SIZE, S, P, C> +impl<const SIZE: usize, S, P> SymmetricKeyEncryptionScheme + for ConstantSizeSymmetricKeyEncryption<SIZE, S, P> where S: SymmetricKeyEncryptionScheme<Plaintext = [u8; SIZE], Ciphertext = [u8; SIZE]>, P: Into<[u8; SIZE]> + TryFrom<[u8; SIZE]>, - C: AsRef<[u8; SIZE]> + From<[u8; SIZE]>, { type Key = S::Key; type Plaintext = P; - type Ciphertext = C; + type Ciphertext = [u8; SIZE]; #[inline] fn encrypt(key: Self::Key, plaintext: Self::Plaintext) -> Self::Ciphertext { - S::encrypt(key, plaintext.into()).into() + S::encrypt(key, plaintext.into()) } #[inline] fn decrypt(key: Self::Key, ciphertext: &Self::Ciphertext) -> Option<Self::Plaintext> { - S::decrypt(key, ciphertext.as_ref()).and_then(move |p| p.try_into().ok()) + S::decrypt(key, ciphertext).and_then(move |p| p.try_into().ok()) } } diff --git a/manta-crypto/src/key.rs b/manta-crypto/src/key.rs index 232bcffdd..386b1143f 100644 --- a/manta-crypto/src/key.rs +++ b/manta-crypto/src/key.rs @@ -30,30 +30,61 @@ pub trait KeyDerivationFunction { fn derive(secret: &Self::Key) -> Self::Output; } -/// Key-Bytes Derivation Function -#[derive(derivative::Derivative)] -#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct KeyBytesDerivationFunction<T, F> -where - T: AsRef<[u8]>, - F: KeyDerivationFunction<Key = [u8]>, -{ - /// Type Parameter Marker - __: PhantomData<(T, F)>, -} - -impl<T, F> KeyDerivationFunction for KeyBytesDerivationFunction<T, F> -where - T: AsRef<[u8]>, - F: KeyDerivationFunction<Key = [u8]>, -{ - type Key = T; +/// Key Derivation Function Adapter +pub mod kdf { + use super::*; + use alloc::vec::Vec; + + /// From Byte Slice Reference Adapter + #[derive(derivative::Derivative)] + #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct FromByteSliceRef<T, F>(PhantomData<(T, F)>) + where + T: AsRef<[u8]>, + F: KeyDerivationFunction<Key = [u8]>; + + impl<T, F> KeyDerivationFunction for FromByteSliceRef<T, F> + where + T: AsRef<[u8]>, + F: KeyDerivationFunction<Key = [u8]>, + { + type Key = T; + + type Output = F::Output; + + #[inline] + fn derive(secret: &Self::Key) -> Self::Output { + F::derive(secret.as_ref()) + } + } - type Output = F::Output; + /// Byte Conversion Trait + pub trait AsBytes { + /// Returns an owned byte representation of `self`. + fn as_bytes(&self) -> Vec<u8>; + } - #[inline] - fn derive(secret: &Self::Key) -> Self::Output { - F::derive(secret.as_ref()) + /// From Byte Vector Adapter + #[derive(derivative::Derivative)] + #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct FromByteVector<T, F>(PhantomData<(T, F)>) + where + T: AsBytes, + F: KeyDerivationFunction<Key = [u8]>; + + impl<T, F> KeyDerivationFunction for FromByteVector<T, F> + where + T: AsBytes, + F: KeyDerivationFunction<Key = [u8]>, + { + type Key = T; + + type Output = F::Output; + + #[inline] + fn derive(secret: &Self::Key) -> Self::Output { + F::derive(&secret.as_bytes()) + } } } diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 809994e9b..77ed24fd0 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -65,7 +65,7 @@ ark-relations = { version = "0.3.0", optional = true, default-features = false } bip32 = { version = "0.2.2", default-features = false, features = ["bip39", "secp256k1"] } blake2 = { version = "0.10.0", default-features = false } derivative = { version = "2.2.0", default-features = false } -zk-garage-plonk = { package = "ark-plonk", git = "https://github.com/zk-garage/plonk", optional = true, default-features = false } +zk-garage-plonk = { package = "plonk", git = "https://github.com/zk-garage/plonk", optional = true, default-features = false } generic-array = { version = "0.14.4", default-features = false } manta-accounting = { path = "../manta-accounting" } manta-crypto = { path = "../manta-crypto" } diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index d745a9f1e..df3e47e60 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -17,24 +17,29 @@ //! Manta-Pay Configuration use crate::crypto::{ - commitment::{pedersen, poseidon}, constraint::arkworks::{FpVar, Groth16, R1CS}, + ecc, encryption::AesGcm, - key::{elliptic_curve_diffie_hellman, Blake2sKdf}, + hash::poseidon, + key::Blake2sKdf, }; use ark_ec::ProjectiveCurve; +use ark_ff::{BigInteger, PrimeField}; use bls12_381::Bls12_381; use bls12_381_ed::{ constraints::EdwardsVar as Bls12_381_EdwardsVar, EdwardsProjective as Bls12_381_Edwards, }; use manta_accounting::{ asset::{Asset, AssetId, AssetValue}, - transfer::{self, Utxo, VoidNumber}, + transfer, }; use manta_crypto::{ accumulator, commitment::CommitmentScheme, - constraint, encryption, + constraint::{self, Allocation, Constant, Secret, Variable, VariableSource}, + ecc::DiffieHellman, + encryption, + hash::{BinaryHashFunction, HashFunction}, key::{self, KeyDerivationFunction}, merkle_tree, }; @@ -45,13 +50,13 @@ pub use ark_bls12_381 as bls12_381; pub use ark_ed_on_bls12_381 as bls12_381_ed; /// -pub type Curve = Bls12_381; +pub type PairingCurve = Bls12_381; /// -pub type EmbeddedCurve = Bls12_381_Edwards; +pub type Group = ecc::arkworks::Group<Bls12_381_Edwards>; /// -pub type EmbeddedCurveVar = Bls12_381_EdwardsVar; +pub type GroupVar = ecc::arkworks::GroupVar<Bls12_381_Edwards, Bls12_381_EdwardsVar>; /// Constraint Field pub type ConstraintField = bls12_381::Fr; @@ -63,11 +68,7 @@ pub type ConstraintFieldVar = FpVar<ConstraintField>; pub type Compiler = R1CS<ConstraintField>; /// Proof System -pub type ProofSystem = Groth16<Curve>; - -/// -pub type KeyAgreementSpec = - elliptic_curve_diffie_hellman::arkworks::Specification<EmbeddedCurve, EmbeddedCurveVar>; +pub type ProofSystem = Groth16<PairingCurve>; /// pub struct PoseidonSpec<const ARITY: usize>; @@ -79,25 +80,26 @@ impl poseidon::arkworks::Specification for PoseidonSpec<2> { const SBOX_EXPONENT: u64 = 5; } -/// -pub type PedersenSpec = pedersen::arkworks::Specification<EmbeddedCurve, EmbeddedCurveVar>; +impl poseidon::arkworks::Specification for PoseidonSpec<4> { + type Field = ConstraintField; + const FULL_ROUNDS: usize = 10; + const PARTIAL_ROUNDS: usize = 10; + const SBOX_EXPONENT: u64 = 5; +} /// -pub type KeyAgreementScheme = elliptic_curve_diffie_hellman::KeyAgreement<KeyAgreementSpec>; +pub type KeyAgreementScheme = DiffieHellman<Group>; /// -pub type KeyAgreementSchemeVar = - elliptic_curve_diffie_hellman::KeyAgreement<KeyAgreementSpec, Compiler>; +pub type KeyAgreementSchemeVar = DiffieHellman<GroupVar, Compiler>; /// -pub struct EphemeralKeyCommitmentScheme(pub poseidon::Commitment<PoseidonSpec<2>, (), 2>); - -impl CommitmentScheme for EphemeralKeyCommitmentScheme { - type Trapdoor = poseidon::Trapdoor<PoseidonSpec<2>, (), 2>; +pub struct UtxoCommitmentScheme(pub poseidon::Hash<PoseidonSpec<4>, (), 4>); +impl CommitmentScheme for UtxoCommitmentScheme { + type Trapdoor = Group; type Input = Asset; - - type Output = poseidon::Output<PoseidonSpec<2>, (), 2>; + type Output = poseidon::Output<PoseidonSpec<4>, (), 4>; #[inline] fn commit( @@ -106,23 +108,27 @@ impl CommitmentScheme for EphemeralKeyCommitmentScheme { input: &Self::Input, compiler: &mut (), ) -> Self::Output { - self.0.commit( - trapdoor, - &[input.id.0.into(), input.value.0.into()], + // NOTE: The group is in projective form, so we need to convert it first. + let trapdoor = trapdoor.0.into_affine(); + self.0.hash( + &[ + trapdoor.x, + trapdoor.y, + input.id.0.into(), + input.value.0.into(), + ], compiler, ) } } /// -pub struct EphemeralKeyCommitmentSchemeVar(pub poseidon::Commitment<PoseidonSpec<2>, Compiler, 2>); +pub struct UtxoCommitmentSchemeVar(pub poseidon::Hash<PoseidonSpec<4>, Compiler, 4>); -impl CommitmentScheme<Compiler> for EphemeralKeyCommitmentSchemeVar { - type Trapdoor = poseidon::Trapdoor<PoseidonSpec<2>, Compiler, 2>; - - type Input = Asset<FpVar<ConstraintField>, FpVar<ConstraintField>>; - - type Output = poseidon::Output<PoseidonSpec<2>, Compiler, 2>; +impl CommitmentScheme<Compiler> for UtxoCommitmentSchemeVar { + type Trapdoor = GroupVar; + type Input = Asset<AssetIdVar, AssetValueVar>; + type Output = poseidon::Output<PoseidonSpec<4>, Compiler, 4>; #[inline] fn commit( @@ -131,127 +137,127 @@ impl CommitmentScheme<Compiler> for EphemeralKeyCommitmentSchemeVar { input: &Self::Input, compiler: &mut Compiler, ) -> Self::Output { - self.0 - .commit(trapdoor, &[input.id.clone(), input.value.clone()], compiler) + // NOTE: The group is already in affine form, so we can extract `x` and `y`. + self.0.hash( + &[ + trapdoor.0.x.clone(), + trapdoor.0.y.clone(), + input.id.0.clone(), + input.value.0.clone(), + ], + compiler, + ) } } -/// -pub struct TrapdoorDerivationFunction(pub poseidon::Commitment<PoseidonSpec<2>, (), 2>); - -impl KeyDerivationFunction for TrapdoorDerivationFunction { - type Key = EmbeddedCurve; +impl Variable<Compiler> for UtxoCommitmentSchemeVar { + type Type = UtxoCommitmentScheme; - type Output = ConstraintField; + type Mode = Constant; #[inline] - fn derive(&self, secret: Self::Key, compiler: &mut ()) -> Self::Output { - // FIXME: We need to truncate the field element to get it to fit into an embedded scalar. - let affine = <Self::Key as ProjectiveCurve>::Affine::from(secret); - self.0 - .commit(&Default::default(), &[affine.x, affine.y], compiler) + fn new(cs: &mut Compiler, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + match allocation { + Allocation::Known(this, mode) => Self(this.0.as_known(cs, mode)), + _ => unreachable!("Constants cannot be unknown."), + } } } /// -pub struct TrapdoorDerivationFunctionVar(pub poseidon::Commitment<PoseidonSpec<2>, Compiler, 2>); - -impl KeyDerivationFunction<Compiler> for TrapdoorDerivationFunctionVar { - type Key = EmbeddedCurveVar; +pub struct VoidNumberHashFunction(pub poseidon::Hash<PoseidonSpec<2>, (), 2>); - type Output = FpVar<ConstraintField>; +impl BinaryHashFunction for VoidNumberHashFunction { + type Left = <UtxoCommitmentScheme as CommitmentScheme>::Output; + type Right = <KeyAgreementScheme as key::KeyAgreementScheme>::SecretKey; + type Output = poseidon::Output<PoseidonSpec<2>, (), 2>; #[inline] - fn derive(&self, secret: Self::Key, compiler: &mut Compiler) -> Self::Output { - // FIXME: We need to truncate the field element to get it to fit into an embedded scalar. - /* TODO: - self.0 - .commit(&Default::default(), &[secret.x, secret.y], compiler) - */ - todo!() + fn hash(&self, left: &Self::Left, right: &Self::Right, compiler: &mut ()) -> Self::Output { + self.0.hash( + &[ + *left, + // FIXME: This is the lift from inner scalar to outer scalar and only exists in some + // cases! We need a better abstraction for this. + ConstraintField::from_le_bytes_mod_order(&right.into_repr().to_bytes_le()), + ], + compiler, + ) } } /// -pub struct UtxoCommitmentScheme(pub pedersen::Commitment<PedersenSpec, (), 2>); +pub struct VoidNumberHashFunctionVar(pub poseidon::Hash<PoseidonSpec<2>, Compiler, 2>); -impl CommitmentScheme for UtxoCommitmentScheme { - type Trapdoor = pedersen::Trapdoor<PedersenSpec, (), 2>; - type Input = Asset; - type Output = pedersen::Output<PedersenSpec, (), 2>; +impl BinaryHashFunction<Compiler> for VoidNumberHashFunctionVar { + type Left = <UtxoCommitmentSchemeVar as CommitmentScheme<Compiler>>::Output; + type Right = <KeyAgreementSchemeVar as key::KeyAgreementScheme<Compiler>>::SecretKey; + type Output = poseidon::Output<PoseidonSpec<2>, Compiler, 1>; #[inline] - fn commit( + fn hash( &self, - trapdoor: &Self::Trapdoor, - input: &Self::Input, - compiler: &mut (), + left: &Self::Left, + right: &Self::Right, + compiler: &mut Compiler, ) -> Self::Output { - self.0.commit( - trapdoor, - &[input.id.0.into(), input.value.0.into()], - compiler, - ) + self.0.hash(&[left.clone(), right.clone()], compiler) } } -/// -pub struct UtxoCommitmentSchemeVar(pub pedersen::Commitment<PedersenSpec, Compiler, 2>); +impl Variable<Compiler> for VoidNumberHashFunctionVar { + type Type = VoidNumberHashFunction; -impl CommitmentScheme<Compiler> for UtxoCommitmentSchemeVar { - type Trapdoor = pedersen::Trapdoor<PedersenSpec, Compiler, 2>; - type Input = Asset<FpVar<ConstraintField>, FpVar<ConstraintField>>; - type Output = pedersen::Output<PedersenSpec, Compiler, 2>; + type Mode = Constant; #[inline] - fn commit( - &self, - trapdoor: &Self::Trapdoor, - input: &Self::Input, - compiler: &mut Compiler, - ) -> Self::Output { - self.0 - .commit(trapdoor, &[input.id.clone(), input.value.clone()], compiler) + fn new(cs: &mut Compiler, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + match allocation { + Allocation::Known(this, mode) => Self(this.0.as_known(cs, mode)), + _ => unreachable!("Constants cannot be unknown."), + } } } /// -pub struct VoidNumberCommitmentScheme(pub pedersen::Commitment<PedersenSpec, (), 1>); +pub struct AssetIdVar(ConstraintFieldVar); + +impl Variable<Compiler> for AssetIdVar { + type Type = AssetId; -impl CommitmentScheme for VoidNumberCommitmentScheme { - type Trapdoor = pedersen::Trapdoor<PedersenSpec, (), 1>; - type Input = <KeyAgreementScheme as key::KeyAgreementScheme>::SecretKey; - type Output = pedersen::Output<PedersenSpec, (), 1>; + type Mode = Secret; #[inline] - fn commit( - &self, - trapdoor: &Self::Trapdoor, - input: &Self::Input, - compiler: &mut (), - ) -> Self::Output { - self.0 - .commit(trapdoor, core::array::from_ref(input), compiler) + fn new(cs: &mut Compiler, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + Self(match allocation { + Allocation::Known(this, mode) => { + ConstraintFieldVar::new(cs, Allocation::Known(&this.0.into(), mode.into())) + } + Allocation::Unknown(mode) => { + ConstraintFieldVar::new(cs, Allocation::Unknown(mode.into())) + } + }) } } /// -pub struct VoidNumberCommitmentSchemeVar(pub pedersen::Commitment<PedersenSpec, Compiler, 1>); +pub struct AssetValueVar(ConstraintFieldVar); -impl CommitmentScheme<Compiler> for VoidNumberCommitmentSchemeVar { - type Trapdoor = pedersen::Trapdoor<PedersenSpec, Compiler, 1>; - type Input = <KeyAgreementSchemeVar as key::KeyAgreementScheme<Compiler>>::SecretKey; - type Output = pedersen::Output<PedersenSpec, Compiler, 1>; +impl Variable<Compiler> for AssetValueVar { + type Type = AssetValue; + + type Mode = Secret; #[inline] - fn commit( - &self, - trapdoor: &Self::Trapdoor, - input: &Self::Input, - compiler: &mut Compiler, - ) -> Self::Output { - self.0 - .commit(trapdoor, core::array::from_ref(input), compiler) + fn new(cs: &mut Compiler, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + Self(match allocation { + Allocation::Known(this, mode) => { + ConstraintFieldVar::new(cs, Allocation::Known(&this.0.into(), mode.into())) + } + Allocation::Unknown(mode) => { + ConstraintFieldVar::new(cs, Allocation::Unknown(mode.into())) + } + }) } } @@ -261,7 +267,6 @@ pub struct Config; /* impl transfer::Configuration for Config { - /* TODO: type SecretKey = <Self::KeyAgreementScheme as key::KeyAgreementScheme>::SecretKey; type PublicKey = <Self::KeyAgreementScheme as key::KeyAgreementScheme>::PublicKey; type KeyAgreementScheme = KeyAgreementScheme; @@ -270,29 +275,17 @@ impl transfer::Configuration for Config { type PublicKeyVar = <Self::KeyAgreementSchemeVar as key::KeyAgreementScheme<Self::Compiler>>::PublicKey; type KeyAgreementSchemeVar = KeyAgreementSchemeVar; - */ - - /* - type EphemeralKeyTrapdoor = <Self::EphemeralKeyCommitmentScheme as CommitmentScheme>::Trapdoor; - type EphemeralKeyCommitmentScheme = EphemeralKeyCommitmentScheme; - type EphemeralKeyTrapdoorVar = - <Self::EphemeralKeyCommitmentSchemeVar as CommitmentScheme<Self::Compiler>>::Trapdoor; - type EphemeralKeyCommitmentSchemeVar = EphemeralKeyCommitmentSchemeVar; - */ - - type TrapdoorDerivationFunction = TrapdoorDerivationFunction; - type TrapdoorDerivationFunctionVar = TrapdoorDerivationFunctionVar; type Utxo = <Self::UtxoCommitmentScheme as CommitmentScheme>::Output; type UtxoCommitmentScheme = UtxoCommitmentScheme; type UtxoVar = <Self::UtxoCommitmentSchemeVar as CommitmentScheme<Self::Compiler>>::Output; type UtxoCommitmentSchemeVar = UtxoCommitmentSchemeVar; - type VoidNumber = <Self::VoidNumberCommitmentScheme as CommitmentScheme>::Output; - type VoidNumberCommitmentScheme = VoidNumberCommitmentScheme; + type VoidNumber = <Self::VoidNumberHashFunction as BinaryHashFunction>::Output; + type VoidNumberHashFunction = VoidNumberHashFunction; type VoidNumberVar = - <Self::VoidNumberCommitmentSchemeVar as CommitmentScheme<Self::Compiler>>::Output; - type VoidNumberCommitmentSchemeVar = VoidNumberCommitmentSchemeVar; + <Self::VoidNumberHashFunctionVar as BinaryHashFunction<Self::Compiler>>::Output; + type VoidNumberHashFunctionVar = VoidNumberHashFunctionVar; /* TODO: type UtxoSetModel = merkle_tree::Parameters<()>; @@ -301,22 +294,24 @@ impl transfer::Configuration for Config { type UtxoSetModelVar = (); */ - type AssetIdVar = ConstraintFieldVar; - type AssetValueVar = ConstraintFieldVar; + type AssetIdVar = AssetIdVar; + type AssetValueVar = AssetValueVar; type Compiler = Compiler; type ProofSystem = ProofSystem; - /* TODO: - type NoteEncryptionKeyDerivationFunction = - Blake2sKdf<<Self::KeyAgreementScheme as key::KeyAgreementScheme>::SharedSecret>; - type NoteEncryptionScheme = encryption::Hybrid< Self::KeyAgreementScheme, - AesGcm<Asset, { Asset::SIZE }>, - Self::NoteEncryptionKeyDerivationFunction, + encryption::ConstantSizeSymmetricKeyEncryption< + { Asset::SIZE }, + AesGcm<{ Asset::SIZE }>, + Asset, + >, + key::kdf::FromByteVector< + <Self::KeyAgreementScheme as key::KeyAgreementScheme>::SharedSecret, + Blake2sKdf, + >, >; - */ } */ @@ -334,9 +329,9 @@ impl constraint::Input<AssetValue> for ProofSystem { } } -impl constraint::Input<EmbeddedCurve> for ProofSystem { +impl constraint::Input<Group> for ProofSystem { #[inline] - fn extend(input: &mut Self::Input, next: &EmbeddedCurve) { + fn extend(input: &mut Self::Input, next: &Group) { // TODO: next.extend_input(input); todo!() } diff --git a/manta-pay/src/crypto/commitment/mod.rs b/manta-pay/src/crypto/commitment/mod.rs index 84f470644..3e8cff1b6 100644 --- a/manta-pay/src/crypto/commitment/mod.rs +++ b/manta-pay/src/crypto/commitment/mod.rs @@ -17,4 +17,3 @@ //! Commitment Scheme Implementations pub mod pedersen; -pub mod poseidon; diff --git a/manta-pay/src/crypto/ecc.rs b/manta-pay/src/crypto/ecc.rs index b3f65dda6..c4c8f017b 100644 --- a/manta-pay/src/crypto/ecc.rs +++ b/manta-pay/src/crypto/ecc.rs @@ -20,13 +20,18 @@ #[cfg(feature = "arkworks")] #[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] pub mod arkworks { - use crate::crypto::constraint::arkworks::R1CS; + use crate::crypto::constraint::arkworks::{empty, full, ArkAllocationMode, R1CS}; + use alloc::vec::Vec; use ark_ec::ProjectiveCurve; use ark_ff::{Field, PrimeField}; use ark_r1cs_std::{fields::fp::FpVar, groups::CurveVar, ToBitsGadget}; use ark_relations::ns; use core::marker::PhantomData; - use manta_crypto::constraint::{Allocation, Constant, Variable}; + use manta_crypto::{ + constraint::{Allocation, PublicOrSecret, Variable}, + ecc, + key::kdf, + }; /// Constraint Field Type type ConstraintField<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField; @@ -34,79 +39,108 @@ pub mod arkworks { /// Compiler Type type Compiler<C> = R1CS<ConstraintField<C>>; - /* - /// Specification - pub struct Specification<C, CV>(PhantomData<(C, CV)>) + /// Elliptic Curve Group Element + #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct Group<C>(pub(crate) C) where - C: ProjectiveCurve, - CV: CurveVar<C, ConstraintField<C>>; + C: ProjectiveCurve; - impl<C, CV> super::Specification for Specification<C, CV> + impl<C> kdf::AsBytes for Group<C> where C: ProjectiveCurve, - CV: CurveVar<C, ConstraintField<C>>, { - type Group = C; + #[inline] + fn as_bytes(&self) -> Vec<u8> { + ark_ff::to_bytes!(&self.0).expect("Byte conversion does not fail.") + } + } + impl<C> ecc::Group for Group<C> + where + C: ProjectiveCurve, + { type Scalar = C::ScalarField; #[inline] - fn scalar_mul( - point: &Self::Group, - scalar: &Self::Scalar, - compiler: &mut (), - ) -> Self::Group { - let _ = compiler; - point.mul(scalar.into_repr()) + fn add(&self, rhs: &Self, _: &mut ()) -> Self { + Self(self.0 + rhs.0) + } + + #[inline] + fn scalar_mul(&self, scalar: &Self::Scalar, _: &mut ()) -> Self { + Self(self.0.mul(scalar.into_repr())) } } - impl<C, CV> super::Specification<Compiler<C>> for Specification<C, CV> + /// Elliptic Curve Group Element Variable + pub struct GroupVar<C, CV>(pub(crate) CV, PhantomData<C>) + where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>; + + impl<C, CV> ecc::Group<Compiler<C>> for GroupVar<C, CV> where C: ProjectiveCurve, CV: CurveVar<C, ConstraintField<C>>, { - type Group = CV; - + // FIXME: This should be a "subtype" of this field (whenever we have an actual injection). type Scalar = FpVar<ConstraintField<C>>; #[inline] - fn scalar_mul( - point: &Self::Group, - scalar: &Self::Scalar, - compiler: &mut Compiler<C>, - ) -> Self::Group { + fn add(&self, rhs: &Self, compiler: &mut Compiler<C>) -> Self { + let _ = compiler; + Self(self.0.clone() + &rhs.0, PhantomData) + } + + #[inline] + fn scalar_mul(&self, scalar: &Self::Scalar, compiler: &mut Compiler<C>) -> Self { let _ = compiler; - point - .scalar_mul_le( - scalar - .to_bits_le() - .expect("Bit decomposition is not allowed to fail.") - .iter(), - ) - .expect("Scalar multiplication is not allowed to fail.") + Self( + self.0 + .scalar_mul_le( + scalar + .to_bits_le() + .expect("Bit decomposition is not allowed to fail.") + .iter(), + ) + .expect("Scalar multiplication is not allowed to fail."), + PhantomData, + ) } } - impl<C, CV> Variable<Compiler<C>> for super::KeyAgreement<Specification<C, CV>, Compiler<C>> + impl<C, CV> Variable<Compiler<C>> for GroupVar<C, CV> where C: ProjectiveCurve, CV: CurveVar<C, ConstraintField<C>>, { - type Type = super::KeyAgreement<Specification<C, CV>, ()>; + type Type = Group<C>; - type Mode = Constant; + type Mode = ArkAllocationMode; #[inline] fn new(cs: &mut Compiler<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - match allocation { - Allocation::Known(this, _) => Self { - generator: CV::new_constant(ns!(cs.cs, "group element"), this.generator) - .expect("Variable allocation is not allowed to fail."), - }, - _ => unreachable!(), - } + Self( + match allocation { + Allocation::Known(this, ArkAllocationMode::Constant) => { + CV::new_constant(ns!(cs.cs, ""), this.0) + } + Allocation::Known(this, ArkAllocationMode::Public) => { + CV::new_input(ns!(cs.cs, ""), full(this.0)) + } + Allocation::Known(this, ArkAllocationMode::Secret) => { + CV::new_witness(ns!(cs.cs, ""), full(this.0)) + } + Allocation::Unknown(PublicOrSecret::Public) => { + CV::new_input(ns!(cs.cs, ""), empty::<C>) + } + Allocation::Unknown(PublicOrSecret::Secret) => { + CV::new_witness(ns!(cs.cs, ""), empty::<C>) + } + } + .expect("Variable allocation is not allowed to fail."), + PhantomData, + ) } } - */ } diff --git a/manta-pay/src/crypto/hash/mod.rs b/manta-pay/src/crypto/hash/mod.rs new file mode 100644 index 000000000..e35eed6a6 --- /dev/null +++ b/manta-pay/src/crypto/hash/mod.rs @@ -0,0 +1,19 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Hash Function Implementations + +pub mod poseidon; diff --git a/manta-pay/src/crypto/commitment/poseidon.rs b/manta-pay/src/crypto/hash/poseidon.rs similarity index 87% rename from manta-pay/src/crypto/commitment/poseidon.rs rename to manta-pay/src/crypto/hash/poseidon.rs index 1a12fa4d9..159229308 100644 --- a/manta-pay/src/crypto/commitment/poseidon.rs +++ b/manta-pay/src/crypto/hash/poseidon.rs @@ -14,13 +14,13 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Poseidon Commitment +//! Poseidon Hash Function // TODO: Describe the contract for `Specification`. use alloc::vec::Vec; use core::{iter, mem}; -use manta_crypto::commitment::CommitmentScheme; +use manta_crypto::hash::{BinaryHashFunction, HashFunction}; /// Poseidon Permutation Specification pub trait Specification<COM = ()> { @@ -36,6 +36,9 @@ pub trait Specification<COM = ()> { /// Number of Partial Rounds const PARTIAL_ROUNDS: usize; + /// Returns the additive identity of the field. + fn zero(compiler: &mut COM) -> Self::Field; + /// Adds two field elements together. fn add(lhs: &Self::Field, rhs: &Self::Field, compiler: &mut COM) -> Self::Field; @@ -61,8 +64,8 @@ where 2 * S::FULL_ROUNDS + S::PARTIAL_ROUNDS } -/// Poseidon Commitment -pub struct Commitment<S, COM = (), const ARITY: usize = 1> +/// Poseidon Hash +pub struct Hash<S, COM = (), const ARITY: usize = 1> where S: Specification<COM>, { @@ -73,11 +76,11 @@ where mds_matrix: Vec<S::Field>, } -impl<S, COM, const ARITY: usize> Commitment<S, COM, ARITY> +impl<S, COM, const ARITY: usize> Hash<S, COM, ARITY> where S: Specification<COM>, { - /// Builds a new [`Commitment`] form `additive_round_keys` and `mds_matrix`. + /// Builds a new [`Hash`](self::Hash) form `additive_round_keys` and `mds_matrix`. /// /// # Panics /// @@ -133,14 +136,9 @@ where /// Computes the first round of the Poseidon permutation from `trapdoor` and `input`. #[inline] - fn first_round( - &self, - trapdoor: &S::Field, - input: &[S::Field; ARITY], - compiler: &mut COM, - ) -> State<S, COM> { + fn first_round(&self, input: &[S::Field; ARITY], compiler: &mut COM) -> State<S, COM> { let mut state = Vec::with_capacity(ARITY + 1); - for (i, point) in iter::once(trapdoor).chain(input).enumerate() { + for (i, point) in iter::once(&S::zero(compiler)).chain(input).enumerate() { let mut elem = S::add(point, &self.additive_round_keys[i], compiler); S::apply_sbox(&mut elem, compiler); state.push(elem); @@ -172,24 +170,17 @@ where } } -impl<S, COM, const ARITY: usize> CommitmentScheme<COM> for Commitment<S, COM, ARITY> +impl<S, COM, const ARITY: usize> HashFunction<COM> for Hash<S, COM, ARITY> where S: Specification<COM>, { - type Trapdoor = S::Field; - type Input = [S::Field; ARITY]; type Output = S::Field; #[inline] - fn commit( - &self, - trapdoor: &Self::Trapdoor, - input: &Self::Input, - compiler: &mut COM, - ) -> Self::Output { - let mut state = self.first_round(trapdoor, input, compiler); + fn hash(&self, input: &Self::Input, compiler: &mut COM) -> Self::Output { + let mut state = self.first_round(input, compiler); for round in 1..S::FULL_ROUNDS { self.full_round(round, &mut state, compiler); } @@ -205,17 +196,11 @@ where } } -/// Poseidon Commitment Trapdoor Type -pub type Trapdoor<S, COM, const ARITY: usize> = - <Commitment<S, COM, ARITY> as CommitmentScheme<COM>>::Trapdoor; - -/// Poseidon Commitment Input Type -pub type Input<S, COM, const ARITY: usize> = - <Commitment<S, COM, ARITY> as CommitmentScheme<COM>>::Input; +/// Poseidon Hash Input Type +pub type Input<S, COM, const ARITY: usize> = <Hash<S, COM, ARITY> as HashFunction<COM>>::Input; /// Poseidon Commitment Output Type -pub type Output<S, COM, const ARITY: usize> = - <Commitment<S, COM, ARITY> as CommitmentScheme<COM>>::Output; +pub type Output<S, COM, const ARITY: usize> = <Hash<S, COM, ARITY> as HashFunction<COM>>::Output; /// Arkworks Backend #[cfg(feature = "arkworks")] @@ -258,6 +243,11 @@ pub mod arkworks { const PARTIAL_ROUNDS: usize = S::PARTIAL_ROUNDS; + #[inline] + fn zero(_: &mut ()) -> Self::Field { + Default::default() + } + #[inline] fn add(lhs: &Self::Field, rhs: &Self::Field, _: &mut ()) -> Self::Field { *lhs + *rhs @@ -289,6 +279,12 @@ pub mod arkworks { const PARTIAL_ROUNDS: usize = S::PARTIAL_ROUNDS; + #[inline] + fn zero(compiler: &mut Compiler<S>) -> Self::Field { + let _ = compiler; + Self::Field::zero() + } + #[inline] fn add(lhs: &Self::Field, rhs: &Self::Field, compiler: &mut Compiler<S>) -> Self::Field { let _ = compiler; @@ -314,11 +310,11 @@ pub mod arkworks { } } - impl<S, const ARITY: usize> Variable<Compiler<S>> for super::Commitment<S, Compiler<S>, ARITY> + impl<S, const ARITY: usize> Variable<Compiler<S>> for super::Hash<S, Compiler<S>, ARITY> where S: Specification, { - type Type = super::Commitment<S, (), ARITY>; + type Type = super::Hash<S, (), ARITY>; type Mode = Constant; @@ -339,7 +335,7 @@ pub mod arkworks { .collect::<Result<Vec<_>, _>>() .expect("Variable allocation is not allowed to fail."), }, - _ => unreachable!(), + _ => unreachable!("Constant variables cannot be unknown."), } } } diff --git a/manta-pay/src/crypto/mod.rs b/manta-pay/src/crypto/mod.rs index 4e31bacd0..c21b34b48 100644 --- a/manta-pay/src/crypto/mod.rs +++ b/manta-pay/src/crypto/mod.rs @@ -16,9 +16,10 @@ //! Manta Pay Cryptographic Primitives Implementations -pub mod commitment; +// TODO[remove]: pub mod commitment; pub mod constraint; pub mod ecc; pub mod encryption; +pub mod hash; pub mod key; // TODO: pub mod merkle_tree; diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index 6a79ffe71..84f3dae57 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -28,8 +28,6 @@ pub mod key; // TODO[remove]: pub mod accounting; -/* TODO: #[cfg(all(feature = "arkworks", feature = "groth16"))] #[cfg_attr(doc_cfg, doc(cfg(all(feature = "arkworks", feature = "groth16"))))] pub mod config; -*/ From 14da558501b6313762cab793ae11a596a4c9760c Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 4 Jan 2022 17:59:06 -0500 Subject: [PATCH 153/275] wip: make merkle tree circuit-friendly --- manta-crypto/src/constraint.rs | 17 ++ manta-crypto/src/merkle_tree/forest.rs | 6 +- manta-crypto/src/merkle_tree/fork.rs | 49 ++++- manta-crypto/src/merkle_tree/full.rs | 16 +- manta-crypto/src/merkle_tree/inner_tree.rs | 2 + manta-crypto/src/merkle_tree/partial.rs | 21 ++- manta-crypto/src/merkle_tree/path.rs | 161 +++++++++++++--- manta-crypto/src/merkle_tree/single_path.rs | 4 +- manta-crypto/src/merkle_tree/test.rs | 5 +- manta-crypto/src/merkle_tree/tree.rs | 197 +++++++++++++++----- manta-pay/src/config.rs | 47 ++++- manta-pay/src/crypto/merkle_tree.rs | 47 ++--- manta-pay/src/crypto/mod.rs | 2 +- 13 files changed, 439 insertions(+), 135 deletions(-) diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index 9b4ca4856..cfc561a69 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -191,6 +191,19 @@ where } } +/// Native Compiler Marker Trait +/// +/// This trait is only implemented for `()`, the only native compiler. +pub trait Native { + /// Returns the native compiler. + fn compiler() -> Self; +} + +impl Native for () { + #[inline] + fn compiler() -> Self {} +} + /// Variable Allocation Trait pub trait Variable<C>: Sized where @@ -297,6 +310,7 @@ where } } +/* TODO[remove]: impl<T, C> reflection::HasAllocation<C> for PhantomData<T> where T: ?Sized, @@ -305,6 +319,7 @@ where type Variable = PhantomData<T>; type Mode = (); } +*/ /// Allocates a new known variable into `cs` with the given `mode`. #[inline] @@ -886,6 +901,7 @@ pub mod measure { } } +/* TODO[remove]: /// Opt-In Compile-Time Reflection Capabilities /// /// See [`HasAllocation`] and [`HasVariable`] for more information. @@ -1107,3 +1123,4 @@ pub mod types { /// Pointer-Sized Unsigned Integer Variable Type pub type Usize<C> = Var<usize, C>; } +*/ diff --git a/manta-crypto/src/merkle_tree/forest.rs b/manta-crypto/src/merkle_tree/forest.rs index 5f4154b14..9e55067bb 100644 --- a/manta-crypto/src/merkle_tree/forest.rs +++ b/manta-crypto/src/merkle_tree/forest.rs @@ -27,7 +27,7 @@ use crate::{ }, merkle_tree::{ tree::{self, Leaf, Parameters, Tree}, - WithProofs, + InnerDigest, WithProofs, }, }; use alloc::vec::Vec; @@ -244,6 +244,7 @@ where C: Configuration + ?Sized, F: Forest<C>, F::Tree: WithProofs<C>, + InnerDigest<C>: PartialEq, { type Item = Leaf<C>; @@ -292,6 +293,7 @@ where C: Configuration + ?Sized, F: ConstantWidthForest<C>, F::Tree: WithProofs<C>, + InnerDigest<C>: PartialEq, { #[inline] fn capacity() -> usize { @@ -304,6 +306,7 @@ where C: Configuration + ?Sized, F: Forest<C>, F::Tree: WithProofs<C>, + InnerDigest<C>: PartialEq, { #[inline] fn len(&self) -> usize { @@ -321,6 +324,7 @@ where C: Configuration + ?Sized, F: Forest<C>, F::Tree: WithProofs<C>, + InnerDigest<C>: PartialEq, { #[inline] fn insert_nonprovable(&mut self, item: &Self::Item) -> bool { diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs index 9cac559b1..fa40729c0 100644 --- a/manta-crypto/src/merkle_tree/fork.rs +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -81,6 +81,7 @@ where pub fn fork<M>(&self, parameters: &Parameters<C>) -> Fork<C, T, P, M> where M: Default + InnerMap<C>, + LeafDigest<C>: Default, { Fork::new(parameters, self) } @@ -91,6 +92,7 @@ where pub fn attach<M>(&self, parameters: &Parameters<C>, fork: &mut Fork<C, T, P, M>) -> bool where M: Default + InnerMap<C>, + LeafDigest<C>: Default, { fork.attach(parameters, self) } @@ -115,6 +117,7 @@ where ) -> Result<(), Fork<C, T, P, M>> where M: Default + InnerMap<C>, + LeafDigest<C>: Default, { match fork.get_attached_base(self) { Some(base) => { @@ -136,6 +139,7 @@ where branch: Partial<C, M>, ) where M: InnerMap<C> + Default, + LeafDigest<C>: Default, { self.base = Some(fork_base); let mut base = P::claim(mem::take(&mut self.base).unwrap()); @@ -252,7 +256,10 @@ where { /// Builds a new [`Fork`] from `trunk`. #[inline] - pub fn new(parameters: &Parameters<C>, trunk: &Trunk<C, T, P>) -> Self { + pub fn new(parameters: &Parameters<C>, trunk: &Trunk<C, T, P>) -> Self + where + LeafDigest<C>: Default, + { Self::with_leaves(parameters, trunk, Default::default()).unwrap() } @@ -263,7 +270,10 @@ where parameters: &Parameters<C>, trunk: &Trunk<C, T, P>, leaf_digests: Vec<LeafDigest<C>>, - ) -> Option<Self> { + ) -> Option<Self> + where + LeafDigest<C>: Default, + { let (base_contribution, branch) = Self::new_branch(parameters, trunk.borrow_base().as_ref(), leaf_digests)?; Some(Self { @@ -279,7 +289,10 @@ where parameters: &Parameters<C>, base: &T, leaf_digests: Vec<LeafDigest<C>>, - ) -> Option<(BaseContribution, Partial<C, M>)> { + ) -> Option<(BaseContribution, Partial<C, M>)> + where + LeafDigest<C>: Default, + { if leaf_digests.len() + base.len() >= capacity::<C>() { return None; } @@ -293,7 +306,10 @@ where parameters: &Parameters<C>, base: &T, leaf_digests: Vec<LeafDigest<C>>, - ) -> (BaseContribution, Partial<C, M>) { + ) -> (BaseContribution, Partial<C, M>) + where + LeafDigest<C>: Default, + { let (base_contribution, base_inner_digest, base_leaf_digests, inner_path) = Self::generate_branch_setup(parameters, base); let mut partial = Partial::new_unchecked( @@ -317,7 +333,10 @@ where InnerDigest<C>, Vec<LeafDigest<C>>, CurrentInnerPath<C>, - ) { + ) + where + LeafDigest<C>: Default, + { if base.is_empty() { ( BaseContribution::Empty, @@ -350,7 +369,10 @@ where fn extract_leaves( base_contribution: BaseContribution, branch: Partial<C, M>, - ) -> Vec<LeafDigest<C>> { + ) -> Vec<LeafDigest<C>> + where + LeafDigest<C>: Default, + { let mut leaf_digests = branch.into_leaves(); mem::drop(leaf_digests.drain(0..base_contribution as usize)); leaf_digests @@ -363,7 +385,10 @@ where base: &T, base_contribution: &mut BaseContribution, branch: &mut Partial<C, M>, - ) -> bool { + ) -> bool + where + LeafDigest<C>: Default, + { if branch.len() + base.len() - (*base_contribution as usize) >= capacity::<C>() { return false; } @@ -380,7 +405,10 @@ where /// Tries to attach this fork to a new `trunk`, returning `false` if `self` has too many leaves /// to fit in `trunk`. #[inline] - pub fn attach(&mut self, parameters: &Parameters<C>, trunk: &Trunk<C, T, P>) -> bool { + pub fn attach(&mut self, parameters: &Parameters<C>, trunk: &Trunk<C, T, P>) -> bool + where + LeafDigest<C>: Default, + { if !Self::try_rebase( parameters, trunk.borrow_base().as_ref(), @@ -437,7 +465,10 @@ where /// Returns `None` if this fork has been detached from its trunk. Use [`attach`](Self::attach) /// to re-associate a trunk to this fork. #[inline] - pub fn push(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> Option<bool> { + pub fn push(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> Option<bool> + where + LeafDigest<C>: Default, + { let _ = P::upgrade(&self.base)?; Some(self.branch.push(parameters, leaf)) } diff --git a/manta-crypto/src/merkle_tree/full.rs b/manta-crypto/src/merkle_tree/full.rs index c1b5a7589..a774e91a0 100644 --- a/manta-crypto/src/merkle_tree/full.rs +++ b/manta-crypto/src/merkle_tree/full.rs @@ -33,9 +33,9 @@ pub type FullMerkleTree<C, M = BTreeMap<C>> = MerkleTree<C, Full<C, M>>; /// Full Merkle Tree Backing Structure #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "LeafDigest<C>: Clone, M: Clone"), + Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone, M: Clone"), Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug, M: Debug"), - Default(bound = "M: Default"), + Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default, M: Default"), Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq, M: Eq"), Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash, M: Hash"), PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq, M: PartialEq") @@ -107,7 +107,7 @@ where #[inline] fn get_owned_leaf_sibling(&self, index: Node) -> LeafDigest<C> where - LeafDigest<C>: Clone, + LeafDigest<C>: Clone + Default, { self.get_leaf_sibling(index).cloned().unwrap_or_default() } @@ -119,7 +119,9 @@ where parameters: &Parameters<C>, leaf_index: Node, leaf_digest: LeafDigest<C>, - ) { + ) where + LeafDigest<C>: Default, + { self.inner_digests.insert( parameters, leaf_index, @@ -138,7 +140,8 @@ impl<C, M> Tree<C> for Full<C, M> where C: Configuration + ?Sized, M: InnerMap<C> + Default, - LeafDigest<C>: Clone, + LeafDigest<C>: Clone + Default, + InnerDigest<C>: PartialEq, { #[inline] fn new(parameters: &Parameters<C>) -> Self { @@ -189,7 +192,8 @@ impl<C, M> WithProofs<C> for Full<C, M> where C: Configuration + ?Sized, M: Default + InnerMap<C>, - LeafDigest<C>: Clone + PartialEq, + LeafDigest<C>: Clone + Default + PartialEq, + InnerDigest<C>: PartialEq, { #[inline] fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>> { diff --git a/manta-crypto/src/merkle_tree/inner_tree.rs b/manta-crypto/src/merkle_tree/inner_tree.rs index 8fb9415f8..b01b7a41e 100644 --- a/manta-crypto/src/merkle_tree/inner_tree.rs +++ b/manta-crypto/src/merkle_tree/inner_tree.rs @@ -551,6 +551,7 @@ impl<C, M> InnerTree<C, M, Sentinel<C>> where C: Configuration + ?Sized, M: InnerMap<C>, + InnerDigest<C>: PartialEq, { /// Returns the path at `leaf_index`, assuming that `leaf_index` is the right-most index, /// so that the return value is a valid [`CurrentInnerPath`]. @@ -745,6 +746,7 @@ impl<C, M> PartialInnerTree<C, M, Sentinel<C>> where C: Configuration + ?Sized, M: InnerMap<C>, + InnerDigest<C>: PartialEq, { /// Returns the path at `leaf_index`, assuming that `leaf_index` is the right-most index, /// so that the return value is a valid [`CurrentInnerPath`]. diff --git a/manta-crypto/src/merkle_tree/partial.rs b/manta-crypto/src/merkle_tree/partial.rs index d1a131d94..0e56ef9f5 100644 --- a/manta-crypto/src/merkle_tree/partial.rs +++ b/manta-crypto/src/merkle_tree/partial.rs @@ -33,9 +33,9 @@ pub type PartialMerkleTree<C, M = BTreeMap<C>> = MerkleTree<C, Partial<C, M>>; /// Partial Merkle Tree Backing Structure #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "LeafDigest<C>: Clone, M: Clone"), + Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone, M: Clone"), Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug, M: Debug"), - Default(bound = "M: Default"), + Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default, M: Default"), Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq, M: Eq"), Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash, M: Hash"), PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq, M: PartialEq") @@ -128,7 +128,7 @@ where #[inline] fn get_owned_leaf_sibling(&self, index: Node) -> LeafDigest<C> where - LeafDigest<C>: Clone, + LeafDigest<C>: Clone + Default, { self.get_leaf_sibling(index).cloned().unwrap_or_default() } @@ -141,7 +141,9 @@ where parameters: &Parameters<C>, leaf_index: Node, leaf_digest: LeafDigest<C>, - ) { + ) where + LeafDigest<C>: Default, + { self.inner_digests.insert( parameters, leaf_index, @@ -158,7 +160,10 @@ where /// Appends a `leaf` to the tree using `parameters`. // FIXME: Remove pub(super) on this once we have a better interface for `fork`. #[inline] - pub(super) fn push(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> bool { + pub(super) fn push(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> bool + where + LeafDigest<C>: Default, + { let len = self.len(); if len >= capacity::<C>() { return false; @@ -172,7 +177,8 @@ impl<C, M> Tree<C> for Partial<C, M> where C: Configuration + ?Sized, M: InnerMap<C> + Default, - LeafDigest<C>: Clone, + LeafDigest<C>: Clone + Default, + InnerDigest<C>: PartialEq, { #[inline] fn new(parameters: &Parameters<C>) -> Self { @@ -224,7 +230,8 @@ impl<C, M> WithProofs<C> for Partial<C, M> where C: Configuration + ?Sized, M: Default + InnerMap<C>, - LeafDigest<C>: Clone + PartialEq, + LeafDigest<C>: Clone + Default + PartialEq, + InnerDigest<C>: PartialEq, { #[inline] fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>> { diff --git a/manta-crypto/src/merkle_tree/path.rs b/manta-crypto/src/merkle_tree/path.rs index 77529cbf5..7dfbe4798 100644 --- a/manta-crypto/src/merkle_tree/path.rs +++ b/manta-crypto/src/merkle_tree/path.rs @@ -41,7 +41,7 @@ pub(super) mod prelude { /// Merkle Tree Inner Path #[derive(derivative::Derivative)] #[derivative( - Clone(bound = ""), + Clone(bound = "InnerDigest<C>: Clone"), Debug(bound = "InnerDigest<C>: Debug"), Eq(bound = "InnerDigest<C>: Eq"), Hash(bound = "InnerDigest<C>: Hash"), @@ -77,14 +77,20 @@ where /// Checks if `self` could represent the [`CurrentInnerPath`] of some tree. #[inline] - pub fn is_current(&self) -> bool { + pub fn is_current(&self) -> bool + where + InnerDigest<C>: PartialEq, + { self.is_current_with(&Default::default()) } /// Checks if `self` could represent the [`CurrentInnerPath`] of some tree, using `default` /// as the sentinel value. #[inline] - pub fn is_current_with(&self, default: &InnerDigest<C>) -> bool { + pub fn is_current_with(&self, default: &InnerDigest<C>) -> bool + where + InnerDigest<C>: PartialEq, + { InnerNodeIter::from_leaf::<C>(self.leaf_index) .zip(self.path.iter()) .all(move |(node, d)| match node.parity() { @@ -124,7 +130,10 @@ where root: &Root<C>, leaf_digest: &LeafDigest<C>, sibling_digest: &LeafDigest<C>, - ) -> bool { + ) -> bool + where + InnerDigest<C>: PartialEq, + { root == &self.root(parameters, leaf_digest, sibling_digest) } @@ -214,9 +223,9 @@ where /// Merkle Tree Current Inner Path #[derive(derivative::Derivative)] #[derivative( - Clone(bound = ""), + Clone(bound = "InnerDigest<C>: Clone"), Debug(bound = "InnerDigest<C>: Debug"), - Default(bound = ""), + Default(bound = "InnerDigest<C>: Default"), Eq(bound = "InnerDigest<C>: Eq"), Hash(bound = "InnerDigest<C>: Hash"), PartialEq(bound = "InnerDigest<C>: PartialEq") @@ -256,14 +265,20 @@ where /// Builds a new [`CurrentInnerPath`] from an [`InnerPath`] without checking that `path` /// satisfies [`InnerPath::is_current`]. #[inline] - pub fn from_path_unchecked(path: InnerPath<C>) -> Self { + pub fn from_path_unchecked(path: InnerPath<C>) -> Self + where + InnerDigest<C>: PartialEq, + { Self::from_path_unchecked_with(path, &Default::default()) } /// Builds a new [`CurrentInnerPath`] from an [`InnerPath`] without checking that `path` /// satisfies [`InnerPath::is_current_with`] against `default`. #[inline] - pub fn from_path_unchecked_with(mut path: InnerPath<C>, default: &InnerDigest<C>) -> Self { + pub fn from_path_unchecked_with(mut path: InnerPath<C>, default: &InnerDigest<C>) -> Self + where + InnerDigest<C>: PartialEq, + { path.path.retain(|d| d != default); Self::new(path.leaf_index, path.path) } @@ -306,7 +321,10 @@ where root: &Root<C>, leaf_digest: &LeafDigest<C>, sibling_digest: &LeafDigest<C>, - ) -> bool { + ) -> bool + where + InnerDigest<C>: PartialEq, + { root == &self.root(parameters, leaf_digest, sibling_digest) } @@ -366,7 +384,10 @@ where leaf_digest: &mut LeafDigest<C>, sibling_digest: &mut LeafDigest<C>, next_leaf_digest: LeafDigest<C>, - ) -> Root<C> { + ) -> Root<C> + where + LeafDigest<C>: Default, + { let mut last_index = self.leaf_index; let mut index = self.leaf_index + 1; self.leaf_index = index; @@ -428,6 +449,7 @@ where impl<C> TryFrom<InnerPath<C>> for CurrentInnerPath<C> where C: Configuration + ?Sized, + InnerDigest<C>: PartialEq, { type Error = InnerPath<C>; @@ -552,9 +574,9 @@ impl<C> FusedIterator for CurrentInnerPathNodeIter<C> where C: Configuration + ? /// Merkle Tree Path #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "LeafDigest<C>: Clone"), + Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), - Default(bound = ""), + Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default"), Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") @@ -601,14 +623,20 @@ where /// Checks if `self` could represent the [`CurrentPath`] of some tree. #[inline] - pub fn is_current(&self) -> bool { + pub fn is_current(&self) -> bool + where + InnerDigest<C>: PartialEq, + { self.is_current_with(&Default::default()) } /// Checks if `self` could represent the [`CurrentPath`] of some tree, using `default` as the /// sentinel value. #[inline] - pub fn is_current_with(&self, default: &InnerDigest<C>) -> bool { + pub fn is_current_with(&self, default: &InnerDigest<C>) -> bool + where + InnerDigest<C>: PartialEq, + { self.inner_path.is_current_with(default) } @@ -627,7 +655,10 @@ where parameters: &Parameters<C>, root: &Root<C>, leaf_digest: &LeafDigest<C>, - ) -> bool { + ) -> bool + where + InnerDigest<C>: PartialEq, + { self.inner_path .verify_digest(parameters, root, leaf_digest, &self.sibling_digest) } @@ -635,7 +666,10 @@ where /// Returns `true` if `self` is a witness to the fact that `leaf` is stored in a merkle tree /// with the given `root`. #[inline] - pub fn verify(&self, parameters: &Parameters<C>, root: &Root<C>, leaf: &Leaf<C>) -> bool { + pub fn verify(&self, parameters: &Parameters<C>, root: &Root<C>, leaf: &Leaf<C>) -> bool + where + InnerDigest<C>: PartialEq, + { self.verify_digest(parameters, root, &parameters.digest(leaf)) } } @@ -679,7 +713,7 @@ where #[derivative( Clone(bound = "LeafDigest<C>: Clone"), Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), - Default(bound = ""), + Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default"), Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") @@ -721,14 +755,20 @@ where /// Builds a new [`CurrentPath`] from a [`Path`] without checking that `path` satisfies /// [`Path::is_current`]. #[inline] - pub fn from_path_unchecked(path: Path<C>) -> Self { + pub fn from_path_unchecked(path: Path<C>) -> Self + where + InnerDigest<C>: PartialEq, + { Self::from_path_unchecked_with(path, &Default::default()) } /// Builds a new [`CurrentPath`] from a [`Path`] without checking that `path` satisfies /// [`Path::is_current_with`] against `default`. #[inline] - pub fn from_path_unchecked_with(path: Path<C>, default: &InnerDigest<C>) -> Self { + pub fn from_path_unchecked_with(path: Path<C>, default: &InnerDigest<C>) -> Self + where + InnerDigest<C>: PartialEq, + { Self::from_inner( path.sibling_digest, CurrentInnerPath::from_path_unchecked_with(path.inner_path, default), @@ -756,7 +796,10 @@ where parameters: &Parameters<C>, root: &Root<C>, leaf_digest: &LeafDigest<C>, - ) -> bool { + ) -> bool + where + InnerDigest<C>: PartialEq, + { self.inner_path .verify_digest(parameters, root, leaf_digest, &self.sibling_digest) } @@ -764,7 +807,10 @@ where /// Returns `true` if `self` is a witness to the fact that `leaf` is stored in a merkle tree /// with the given `root`. #[inline] - pub fn verify(&self, parameters: &Parameters<C>, root: &Root<C>, leaf: &Leaf<C>) -> bool { + pub fn verify(&self, parameters: &Parameters<C>, root: &Root<C>, leaf: &Leaf<C>) -> bool + where + InnerDigest<C>: PartialEq, + { self.verify_digest(parameters, root, &parameters.digest(leaf)) } @@ -775,7 +821,10 @@ where parameters: &Parameters<C>, current: &mut LeafDigest<C>, next: LeafDigest<C>, - ) -> Root<C> { + ) -> Root<C> + where + LeafDigest<C>: Default, + { self.inner_path .update(parameters, current, &mut self.sibling_digest, next) } @@ -784,6 +833,7 @@ where impl<C> TryFrom<Path<C>> for CurrentPath<C> where C: Configuration + ?Sized, + InnerDigest<C>: PartialEq, { type Error = Path<C>; @@ -801,4 +851,69 @@ where } /// Constraint System Gadgets -pub mod constraint {} +pub mod constraint { + use super::*; + use crate::constraint::{Allocation, AllocationMode, Secret, Variable, VariableSource}; + + /// + pub struct InnerPathVar<C, COM> + where + C: Configuration<COM> + ?Sized, + { + /// Digest Indices + pub indices: Vec<bool>, + + /// Inner Digest Path + /// + /// Inner digests are stored from leaf to root, not including the root. + pub path: Vec<InnerDigest<C, COM>>, + } + + impl<C, COM> Variable<COM> for InnerPathVar<C, COM> + where + C: Configuration<COM> + Variable<COM> + ?Sized, + C::Type: Configuration, + { + type Type = InnerPath<C::Type>; + type Mode = Secret; + + #[inline] + fn new(compiler: &mut COM, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + todo!() + } + } + + /// + pub struct PathVar<C, COM> + where + C: Configuration<COM> + ?Sized, + { + /// Sibling Digest + pub sibling_digest: LeafDigest<C, COM>, + + /// Inner Path + pub inner_path: InnerPathVar<C, COM>, + } + + impl<C, COM> Variable<COM> for PathVar<C, COM> + where + C: Configuration<COM> + Variable<COM> + ?Sized, + C::Type: Configuration, + LeafDigest<C, COM>: Variable<COM, Type = LeafDigest<C::Type>>, + <<LeafDigest<C, COM> as Variable<COM>>::Mode as AllocationMode>::Known: From<Secret>, + { + type Type = Path<C::Type>; + type Mode = Secret; + + #[inline] + fn new(compiler: &mut COM, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + match allocation { + Allocation::Known(this, mode) => Self { + sibling_digest: this.sibling_digest.as_known(compiler, mode), + inner_path: this.inner_path.as_known(compiler, mode), + }, + _ => todo!(), + } + } + } +} diff --git a/manta-crypto/src/merkle_tree/single_path.rs b/manta-crypto/src/merkle_tree/single_path.rs index 1961f6c9a..4ad5e161a 100644 --- a/manta-crypto/src/merkle_tree/single_path.rs +++ b/manta-crypto/src/merkle_tree/single_path.rs @@ -43,7 +43,7 @@ pub type SinglePathMerkleTree<C> = MerkleTree<C, SinglePath<C>>; /// Single Path Merkle Tree Backing Structure #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "LeafDigest<C>: Clone"), + Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default"), Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), @@ -120,7 +120,7 @@ where impl<C> Tree<C> for SinglePath<C> where C: Configuration + ?Sized, - LeafDigest<C>: Clone, + LeafDigest<C>: Clone + Default, { #[inline] fn new(parameters: &Parameters<C>) -> Self { diff --git a/manta-crypto/src/merkle_tree/test.rs b/manta-crypto/src/merkle_tree/test.rs index 01ce19da3..88ea09c32 100644 --- a/manta-crypto/src/merkle_tree/test.rs +++ b/manta-crypto/src/merkle_tree/test.rs @@ -18,8 +18,8 @@ use crate::{ merkle_tree::{ - Configuration, HashConfiguration, InnerHashParameters, Leaf, LeafHashParameters, - MerkleTree, Parameters, Tree, WithProofs, + Configuration, HashConfiguration, InnerDigest, InnerHashParameters, Leaf, + LeafHashParameters, MerkleTree, Parameters, Tree, WithProofs, }, rand::{CryptoRng, RngCore, Sample}, }; @@ -130,6 +130,7 @@ pub fn assert_valid_path<C, T>(tree: &MerkleTree<C, T>, index: usize, leaf: &Lea where C: Configuration + ?Sized, T: Tree<C> + WithProofs<C>, + InnerDigest<C>: PartialEq, { assert!( tree.path(index) diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index 70d17c7f0..5940bebe3 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -26,6 +26,7 @@ use crate::{ self, Accumulator, ConstantCapacityAccumulator, ExactSizeAccumulator, MembershipProof, OptimizedAccumulator, }, + constraint::Native, merkle_tree::{ fork::Trunk, path::{CurrentPath, Path}, @@ -35,7 +36,7 @@ use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use manta_util::pointer::PointerFamily; /// Merkle Tree Leaf Hash -pub trait LeafHash { +pub trait LeafHash<COM = ()> { /// Leaf Type type Leaf: ?Sized; @@ -43,51 +44,88 @@ pub trait LeafHash { type Parameters; /// Leaf Hash Output Type - type Output: Default; + type Output; + + /// Computes the digest of the `leaf` using `parameters` inside the given `compiler`. + fn digest_in( + parameters: &Self::Parameters, + leaf: &Self::Leaf, + compiler: &mut COM, + ) -> Self::Output; /// Computes the digest of the `leaf` using `parameters`. - fn digest(parameters: &Self::Parameters, leaf: &Self::Leaf) -> Self::Output; + #[inline] + fn digest(parameters: &Self::Parameters, leaf: &Self::Leaf) -> Self::Output + where + COM: Native, + { + Self::digest_in(parameters, leaf, &mut COM::compiler()) + } } /// Merkle Tree Inner Hash -pub trait InnerHash { +pub trait InnerHash<COM = ()> { /// Leaf Hash Type - type LeafHash: LeafHash; + type LeafHash: LeafHash<COM>; /// Inner Hash Parameters Type type Parameters; /// Inner Hash Output Type - type Output: Clone + Default + PartialEq; + type Output: Clone + Default; - /// Returns `true` if `digest` is the default inner hash output value. + /// Combines two inner digests into a new inner digest using `parameters` inside the given + /// `compiler`. + fn join_in( + parameters: &Self::Parameters, + lhs: &Self::Output, + rhs: &Self::Output, + compiler: &mut COM, + ) -> Self::Output; + + /// Combines two inner digests into a new inner digest using `parameters`. #[inline] - fn is_default(digest: &Self::Output) -> bool { - digest == &Default::default() + fn join(parameters: &Self::Parameters, lhs: &Self::Output, rhs: &Self::Output) -> Self::Output + where + COM: Native, + { + Self::join_in(parameters, lhs, rhs, &mut COM::compiler()) } - /// Combines two inner digests into a new inner digest using `parameters`. - fn join(parameters: &Self::Parameters, lhs: &Self::Output, rhs: &Self::Output) -> Self::Output; + /// Combines two [`LeafHash`](Self::LeafHash) digests into an inner digest inside the given + /// `compiler`. + fn join_leaves_in( + parameters: &Self::Parameters, + lhs: &<Self::LeafHash as LeafHash<COM>>::Output, + rhs: &<Self::LeafHash as LeafHash<COM>>::Output, + compiler: &mut COM, + ) -> Self::Output; /// Combines two [`LeafHash`](Self::LeafHash) digests into an inner digest. + #[inline] fn join_leaves( parameters: &Self::Parameters, - lhs: &<Self::LeafHash as LeafHash>::Output, - rhs: &<Self::LeafHash as LeafHash>::Output, - ) -> Self::Output; + lhs: &<Self::LeafHash as LeafHash<COM>>::Output, + rhs: &<Self::LeafHash as LeafHash<COM>>::Output, + ) -> Self::Output + where + COM: Native, + { + Self::join_leaves_in(parameters, lhs, rhs, &mut COM::compiler()) + } } /// Merkle Tree Hash Configuration -pub trait HashConfiguration { +pub trait HashConfiguration<COM = ()> { /// Leaf Hash Type - type LeafHash: LeafHash; + type LeafHash: LeafHash<COM>; /// Inner Hash Type - type InnerHash: InnerHash<LeafHash = Self::LeafHash>; + type InnerHash: InnerHash<COM, LeafHash = Self::LeafHash>; } /// Merkle Tree Configuration -pub trait Configuration: HashConfiguration { +pub trait Configuration<COM = ()>: HashConfiguration<COM> { /// Fixed Height of the Merkle Tree /// /// # Contract @@ -106,39 +144,43 @@ pub trait Configuration: HashConfiguration { /// Since this `struct` is meant to be used as a type parameter, any values of this type have no /// meaning, just like values of type [`HashConfiguration`] or [`Configuration`]. #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Config<C, const HEIGHT: usize>(PhantomData<C>) +pub struct Config<C, const HEIGHT: usize, COM = ()>(PhantomData<(COM, C)>) where - C: HashConfiguration + ?Sized; + C: HashConfiguration<COM> + ?Sized; -impl<C, const HEIGHT: usize> HashConfiguration for Config<C, HEIGHT> +impl<C, const HEIGHT: usize, COM> HashConfiguration<COM> for Config<C, HEIGHT, COM> where - C: HashConfiguration + ?Sized, + C: HashConfiguration<COM> + ?Sized, { type LeafHash = C::LeafHash; type InnerHash = C::InnerHash; } -impl<C, const HEIGHT: usize> Configuration for Config<C, HEIGHT> +impl<C, const HEIGHT: usize, COM> Configuration<COM> for Config<C, HEIGHT, COM> where - C: HashConfiguration + ?Sized, + C: HashConfiguration<COM> + ?Sized, { const HEIGHT: usize = HEIGHT; } /// Leaf Type -pub type Leaf<C> = <<C as HashConfiguration>::LeafHash as LeafHash>::Leaf; +pub type Leaf<C, COM = ()> = <<C as HashConfiguration<COM>>::LeafHash as LeafHash<COM>>::Leaf; /// Leaf Hash Parameters Type -pub type LeafHashParameters<C> = <<C as HashConfiguration>::LeafHash as LeafHash>::Parameters; +pub type LeafHashParameters<C, COM = ()> = + <<C as HashConfiguration<COM>>::LeafHash as LeafHash<COM>>::Parameters; /// Leaf Hash Digest Type -pub type LeafDigest<C> = <<C as HashConfiguration>::LeafHash as LeafHash>::Output; +pub type LeafDigest<C, COM = ()> = + <<C as HashConfiguration<COM>>::LeafHash as LeafHash<COM>>::Output; /// Inner Hash Parameters Type -pub type InnerHashParameters<C> = <<C as HashConfiguration>::InnerHash as InnerHash>::Parameters; +pub type InnerHashParameters<C, COM = ()> = + <<C as HashConfiguration<COM>>::InnerHash as InnerHash<COM>>::Parameters; /// Inner Hash Digest Type -pub type InnerDigest<C> = <<C as HashConfiguration>::InnerHash as InnerHash>::Output; +pub type InnerDigest<C, COM = ()> = + <<C as HashConfiguration<COM>>::InnerHash as InnerHash<COM>>::Output; /// Returns the capacity of the merkle tree with the given [`C::HEIGHT`](Configuration::HEIGHT) /// parameter. @@ -149,6 +191,19 @@ pub type InnerDigest<C> = <<C as HashConfiguration>::InnerHash as InnerHash>::Ou pub fn capacity<C>() -> usize where C: Configuration + ?Sized, +{ + capacity_in::<C, _>() +} + +/// Returns the capacity of the merkle tree with the given [`C::HEIGHT`](Configuration::HEIGHT) +/// parameter. +/// +/// The capacity of a merkle tree with height `H` is `2^(H-1)`. +#[inline] +#[must_use] +pub fn capacity_in<C, COM>() -> usize +where + C: Configuration<COM> + ?Sized, { 1_usize << (C::HEIGHT - 1) } @@ -162,6 +217,19 @@ where pub fn path_length<C>() -> usize where C: Configuration + ?Sized, +{ + path_length_in::<C, _>() +} + +/// Returns the path length of the merkle tree with the given [`C::HEIGHT`](Configuration::HEIGHT) +/// parameter. +/// +/// The path length of a merkle tree with height `H` is `H - 2`. +#[inline] +#[must_use] +pub fn path_length_in<C, COM>() -> usize +where + C: Configuration<COM> + ?Sized, { C::HEIGHT - 2 } @@ -419,35 +487,70 @@ where /// Merkle Tree Parameters #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "LeafHashParameters<C>: Clone, InnerHashParameters<C>: Clone"), - Copy(bound = "LeafHashParameters<C>: Copy, InnerHashParameters<C>: Copy"), - Debug(bound = "LeafHashParameters<C>: Debug, InnerHashParameters<C>: Debug"), - Default(bound = "LeafHashParameters<C>: Default, InnerHashParameters<C>: Default"), - Eq(bound = "LeafHashParameters<C>: Eq, InnerHashParameters<C>: Eq"), - Hash(bound = "LeafHashParameters<C>: Hash, InnerHashParameters<C>: Hash"), - PartialEq(bound = "LeafHashParameters<C>: PartialEq, InnerHashParameters<C>: PartialEq") + Clone(bound = "LeafHashParameters<C, COM>: Clone, InnerHashParameters<C, COM>: Clone"), + Copy(bound = "LeafHashParameters<C, COM>: Copy, InnerHashParameters<C, COM>: Copy"), + Debug(bound = "LeafHashParameters<C, COM>: Debug, InnerHashParameters<C, COM>: Debug"), + Default(bound = "LeafHashParameters<C, COM>: Default, InnerHashParameters<C, COM>: Default"), + Eq(bound = "LeafHashParameters<C, COM>: Eq, InnerHashParameters<C, COM>: Eq"), + Hash(bound = "LeafHashParameters<C, COM>: Hash, InnerHashParameters<C, COM>: Hash"), + PartialEq( + bound = "LeafHashParameters<C, COM>: PartialEq, InnerHashParameters<C, COM>: PartialEq" + ) )] -pub struct Parameters<C> +pub struct Parameters<C, COM = ()> where - C: HashConfiguration + ?Sized, + C: HashConfiguration<COM> + ?Sized, { /// Leaf Hash Parameters - pub leaf: LeafHashParameters<C>, + pub leaf: LeafHashParameters<C, COM>, /// Inner Hash Parameters - pub inner: InnerHashParameters<C>, + pub inner: InnerHashParameters<C, COM>, } -impl<C> Parameters<C> +impl<C, COM> Parameters<C, COM> where - C: HashConfiguration + ?Sized, + C: HashConfiguration<COM> + ?Sized, { /// Builds a new [`Parameters`] from `leaf` and `inner` parameters. #[inline] - pub fn new(leaf: LeafHashParameters<C>, inner: InnerHashParameters<C>) -> Self { + pub fn new(leaf: LeafHashParameters<C, COM>, inner: InnerHashParameters<C, COM>) -> Self { Self { leaf, inner } } + /// Computes the leaf digest of `leaf` using `self`. + #[inline] + pub fn digest_in(&self, leaf: &Leaf<C, COM>, compiler: &mut COM) -> LeafDigest<C, COM> { + C::LeafHash::digest_in(&self.leaf, leaf, compiler) + } + + /// Combines two inner digests into a new inner digest using `self`. + #[inline] + pub fn join_in( + &self, + lhs: &InnerDigest<C, COM>, + rhs: &InnerDigest<C, COM>, + compiler: &mut COM, + ) -> InnerDigest<C, COM> { + C::InnerHash::join_in(&self.inner, lhs, rhs, compiler) + } + + /// Combines two leaf digests into a new inner digest using `self`. + #[inline] + pub fn join_leaves_in( + &self, + lhs: &LeafDigest<C, COM>, + rhs: &LeafDigest<C, COM>, + compiler: &mut COM, + ) -> InnerDigest<C, COM> { + C::InnerHash::join_leaves_in(&self.inner, lhs, rhs, compiler) + } +} + +impl<C> Parameters<C> +where + C: HashConfiguration + ?Sized, +{ /// Computes the leaf digest of `leaf` using `self`. #[inline] pub fn digest(&self, leaf: &Leaf<C>) -> LeafDigest<C> { @@ -472,17 +575,19 @@ where pub fn verify_path(&self, path: &Path<C>, root: &Root<C>, leaf: &Leaf<C>) -> bool where C: Configuration, + InnerDigest<C>: PartialEq, { path.verify(self, root, leaf) } } /// Merkle Tree Root -pub type Root<C> = InnerDigest<C>; +pub type Root<C, COM = ()> = InnerDigest<C, COM>; impl<C> accumulator::Model for Parameters<C> where C: Configuration + ?Sized, + InnerDigest<C>: PartialEq, { type Item = Leaf<C>; @@ -785,6 +890,7 @@ impl<C, T> Accumulator for MerkleTree<C, T> where C: Configuration + ?Sized, T: Tree<C> + WithProofs<C>, + InnerDigest<C>: PartialEq, { type Item = Leaf<C>; @@ -819,6 +925,7 @@ impl<C, T> ConstantCapacityAccumulator for MerkleTree<C, T> where C: Configuration + ?Sized, T: Tree<C> + WithProofs<C>, + InnerDigest<C>: PartialEq, { #[inline] fn capacity() -> usize { @@ -830,6 +937,7 @@ impl<C, T> ExactSizeAccumulator for MerkleTree<C, T> where C: Configuration + ?Sized, T: Tree<C> + WithProofs<C>, + InnerDigest<C>: PartialEq, { #[inline] fn len(&self) -> usize { @@ -846,6 +954,7 @@ impl<C, T> OptimizedAccumulator for MerkleTree<C, T> where C: Configuration + ?Sized, T: Tree<C> + WithProofs<C>, + InnerDigest<C>: PartialEq, { #[inline] fn insert_nonprovable(&mut self, item: &Self::Item) -> bool { diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index df3e47e60..3ad7fca3c 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -93,13 +93,16 @@ pub type KeyAgreementScheme = DiffieHellman<Group>; /// pub type KeyAgreementSchemeVar = DiffieHellman<GroupVar, Compiler>; +/// +pub type Utxo = poseidon::Output<PoseidonSpec<4>, (), 4>; + /// pub struct UtxoCommitmentScheme(pub poseidon::Hash<PoseidonSpec<4>, (), 4>); impl CommitmentScheme for UtxoCommitmentScheme { type Trapdoor = Group; type Input = Asset; - type Output = poseidon::Output<PoseidonSpec<4>, (), 4>; + type Output = Utxo; #[inline] fn commit( @@ -122,13 +125,16 @@ impl CommitmentScheme for UtxoCommitmentScheme { } } +/// +pub type UtxoVar = poseidon::Output<PoseidonSpec<4>, Compiler, 4>; + /// pub struct UtxoCommitmentSchemeVar(pub poseidon::Hash<PoseidonSpec<4>, Compiler, 4>); impl CommitmentScheme<Compiler> for UtxoCommitmentSchemeVar { type Trapdoor = GroupVar; type Input = Asset<AssetIdVar, AssetValueVar>; - type Output = poseidon::Output<PoseidonSpec<4>, Compiler, 4>; + type Output = UtxoVar; #[inline] fn commit( @@ -164,13 +170,16 @@ impl Variable<Compiler> for UtxoCommitmentSchemeVar { } } +/// +pub type VoidNumber = poseidon::Output<PoseidonSpec<2>, (), 2>; + /// pub struct VoidNumberHashFunction(pub poseidon::Hash<PoseidonSpec<2>, (), 2>); impl BinaryHashFunction for VoidNumberHashFunction { - type Left = <UtxoCommitmentScheme as CommitmentScheme>::Output; + type Left = Utxo; type Right = <KeyAgreementScheme as key::KeyAgreementScheme>::SecretKey; - type Output = poseidon::Output<PoseidonSpec<2>, (), 2>; + type Output = VoidNumber; #[inline] fn hash(&self, left: &Self::Left, right: &Self::Right, compiler: &mut ()) -> Self::Output { @@ -224,7 +233,6 @@ pub struct AssetIdVar(ConstraintFieldVar); impl Variable<Compiler> for AssetIdVar { type Type = AssetId; - type Mode = Secret; #[inline] @@ -245,7 +253,6 @@ pub struct AssetValueVar(ConstraintFieldVar); impl Variable<Compiler> for AssetValueVar { type Type = AssetValue; - type Mode = Secret; #[inline] @@ -261,6 +268,34 @@ impl Variable<Compiler> for AssetValueVar { } } +/// +pub struct LeafHash; + +impl merkle_tree::LeafHash for LeafHash { + type Leaf = Utxo; + type Parameters = (); + type Output = Utxo; + + #[inline] + fn digest_in(_: &Self::Parameters, leaf: &Self::Leaf, _: &mut ()) -> Self::Output { + *leaf + } +} + +/// +pub struct LeafHashVar; + +impl merkle_tree::LeafHash<Compiler> for LeafHashVar { + type Leaf = UtxoVar; + type Parameters = (); + type Output = UtxoVar; + + #[inline] + fn digest_in(_: &Self::Parameters, leaf: &Self::Leaf, _: &mut Compiler) -> Self::Output { + *leaf + } +} + /// Configuration Structure #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Config; diff --git a/manta-pay/src/crypto/merkle_tree.rs b/manta-pay/src/crypto/merkle_tree.rs index b7d6d2d8a..055d3cc5e 100644 --- a/manta-pay/src/crypto/merkle_tree.rs +++ b/manta-pay/src/crypto/merkle_tree.rs @@ -31,9 +31,12 @@ use manta_crypto::{ }; use manta_util::{as_bytes, Concat}; +/* #[cfg(feature = "test")] use manta_crypto::rand::Standard; +*/ +/* /// Arkworks Leaf Hash Converter #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -183,9 +186,8 @@ where type Leaf = C::Leaf; type LeafHash = C::LeafHash; type InnerHash = C::InnerHash; - type Height = C::Height; - const HEIGHT: Self::Height = C::HEIGHT; + const HEIGHT: usize = C::HEIGHT; } impl<C> merkle_tree::HashConfiguration for ConfigConverter<C> @@ -200,9 +202,7 @@ impl<C> merkle_tree::Configuration for ConfigConverter<C> where C: Configuration, { - type Height = C::Height; - - const HEIGHT: Self::Height = C::HEIGHT; + const HEIGHT: usize = C::HEIGHT; } impl<C> Config for ConfigConverter<C> @@ -247,11 +247,13 @@ where <ConfigConverter<C> as merkle_tree::HashConfiguration>::InnerHash::sample_parameters(rng) } } +*/ +/* TODO: /// Merkle Tree Constraint System Variables pub mod constraint { use super::*; - use crate::crypto::constraint::arkworks::{empty, full, ArkConstraintSystem}; + use crate::crypto::constraint::arkworks::{empty, full, R1CS}; use ark_crypto_primitives::{ crh::constraints::{CRHGadget, TwoToOneCRHGadget}, merkle_tree::{constraints::PathVar as ArkPathVar, Path as ArkPath}, @@ -260,8 +262,8 @@ pub mod constraint { use ark_r1cs_std::{alloc::AllocVar, boolean::Boolean, eq::EqGadget, uint8::UInt8}; use ark_relations::ns; use manta_crypto::{ - accumulator::Verifier, - constraint::{reflection::HasAllocation, Allocation, Constant, Public, Secret, Variable}, + accumulator::Model, + constraint::{Allocation, Constant, Public, Secret, Variable}, merkle_tree::{Parameters, Path, Root}, }; @@ -281,7 +283,7 @@ pub mod constraint { pub type ConstraintField<C> = <C as Configuration>::ConstraintField; /// Constraint System Type - pub type ContraintSystem<C> = ArkConstraintSystem<ConstraintField<C>>; + pub type ContraintSystem<C> = R1CS<ConstraintField<C>>; /// Leaf Hash Type pub type LeafHashVar<C> = <C as Configuration>::LeafHashVar; @@ -354,7 +356,7 @@ pub mod constraint { } } - impl<C> Verifier for ParametersVar<C> + impl<C> Model<R1CS<ConstraintField<C>>> for ParametersVar<C> where C: Configuration, { @@ -406,14 +408,6 @@ pub mod constraint { } } - impl<C> HasAllocation<ContraintSystem<C>> for Parameters<ConfigConverter<C>> - where - C: Configuration, - { - type Variable = ParametersVar<C>; - type Mode = Constant; - } - /// Merkle Tree Root Inner Type type RootInnerType<C> = <<C as super::Configuration>::InnerHash as TwoToOneCRH>::Output; @@ -459,14 +453,6 @@ pub mod constraint { } } - impl<C> HasAllocation<ContraintSystem<C>> for Root<ConfigConverter<C>> - where - C: Configuration, - { - type Variable = RootVar<C>; - type Mode = Public; - } - /// Extends the `input` vector by constraint field elements that make up `root`. #[inline] pub fn root_extend_input<C>( @@ -549,12 +535,5 @@ pub mod constraint { ) } } - - impl<C> HasAllocation<ContraintSystem<C>> for Path<ConfigConverter<C>> - where - C: Configuration, - { - type Variable = PathVar<C>; - type Mode = Secret; - } } +*/ diff --git a/manta-pay/src/crypto/mod.rs b/manta-pay/src/crypto/mod.rs index c21b34b48..5b18ee453 100644 --- a/manta-pay/src/crypto/mod.rs +++ b/manta-pay/src/crypto/mod.rs @@ -22,4 +22,4 @@ pub mod ecc; pub mod encryption; pub mod hash; pub mod key; -// TODO: pub mod merkle_tree; +pub mod merkle_tree; From d04d7b790a597823b9171d71f74e7143baa45d1a Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 5 Jan 2022 00:07:37 -0500 Subject: [PATCH 154/275] feat: add merkle tree path variable verification --- manta-crypto/src/constraint.rs | 39 ++++++ manta-crypto/src/merkle_tree/path.rs | 200 ++++++++++++++++++++++++++- manta-crypto/src/merkle_tree/tree.rs | 21 --- 3 files changed, 232 insertions(+), 28 deletions(-) diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index cfc561a69..32fec8ac4 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -436,6 +436,24 @@ pub trait ConstraintSystem { V::assert_all_eq(self, iter); } + /// Selects `lhs` if `bit == 0` and `rhs` if `bit == 1`. + #[inline] + fn conditional_select<V>(&mut self, bit: &Self::Bool, lhs: &V, rhs: &V) -> V + where + V: ConditionalSelect<Self>, + { + V::select(bit, lhs, rhs, self) + } + + /// Swaps `lhs` and `rhs` if `bit == 1`. + #[inline] + fn conditional_swap<V>(&mut self, bit: &Self::Bool, lhs: &V, rhs: &V) -> (V, V) + where + V: ConditionalSelect<Self>, + { + V::swap(bit, lhs, rhs, self) + } + /// Returns proving and verifying contexts for the constraints contained in `self`. #[inline] fn generate_context<P, R>( @@ -502,6 +520,27 @@ where } } +/// Conditional Selection +pub trait ConditionalSelect<C> +where + C: ConstraintSystem + ?Sized, +{ + /// Selects `lhs` if `bit == 0` and `rhs` if `bit == 1`. + fn select(bit: &C::Bool, lhs: &Self, rhs: &Self, cs: &mut C) -> Self; + + /// Swaps `lhs` and `rhs` if `bit == 1`. + #[inline] + fn swap(bit: &C::Bool, lhs: &Self, rhs: &Self, cs: &mut C) -> (Self, Self) + where + Self: Sized, + { + ( + Self::select(bit, lhs, rhs, cs), + Self::select(bit, lhs, rhs, cs), + ) + } +} + /// Addition Trait pub trait Add<C> where diff --git a/manta-crypto/src/merkle_tree/path.rs b/manta-crypto/src/merkle_tree/path.rs index 7dfbe4798..88dce19dd 100644 --- a/manta-crypto/src/merkle_tree/path.rs +++ b/manta-crypto/src/merkle_tree/path.rs @@ -17,6 +17,7 @@ //! Merkle Tree Paths // TODO: Move some methods to a `raw` module for paths. +// TODO: Move to a uniform interface for native and circuit paths. use crate::merkle_tree::{ inner_tree::{InnerNode, InnerNodeIter}, @@ -853,15 +854,25 @@ where /// Constraint System Gadgets pub mod constraint { use super::*; - use crate::constraint::{Allocation, AllocationMode, Secret, Variable, VariableSource}; - - /// + use crate::{ + constraint::{ + Allocation, AllocationMode, ConditionalSelect, ConstraintSystem, Equal, Secret, + Variable, VariableSource, + }, + merkle_tree::path_length_in, + }; + + /// Inner Path Variable pub struct InnerPathVar<C, COM> where C: Configuration<COM> + ?Sized, + COM: ConstraintSystem, { + /// Leaf Index + pub leaf_index: COM::Bool, + /// Digest Indices - pub indices: Vec<bool>, + pub inner_indices: Vec<COM::Bool>, /// Inner Digest Path /// @@ -869,24 +880,131 @@ pub mod constraint { pub path: Vec<InnerDigest<C, COM>>, } + impl<C, COM> InnerPathVar<C, COM> + where + C: Configuration<COM> + ?Sized, + COM: ConstraintSystem, + InnerDigest<C, COM>: ConditionalSelect<COM>, + { + /// Computes the root of the merkle tree relative to `base` using `parameters`. + #[inline] + pub fn root_from_base( + &self, + parameters: &Parameters<C, COM>, + base: InnerDigest<C, COM>, + compiler: &mut COM, + ) -> Root<C, COM> { + Self::fold( + parameters, + base, + self.inner_indices.iter().zip(self.path.iter()), + compiler, + ) + } + + /// Computes the root of the merkle tree relative to `leaf_digest` and its `sibling_digest` + /// using `parameters`. + #[inline] + pub fn root( + &self, + parameters: &Parameters<C, COM>, + leaf_digest: &LeafDigest<C, COM>, + sibling_digest: &LeafDigest<C, COM>, + compiler: &mut COM, + ) -> Root<C, COM> + where + LeafDigest<C, COM>: ConditionalSelect<COM>, + { + let (lhs, rhs) = + compiler.conditional_swap(&self.leaf_index, leaf_digest, sibling_digest); + self.root_from_base( + parameters, + parameters.join_leaves_in(&lhs, &rhs, compiler), + compiler, + ) + } + + /// Returns the folding algorithm for a path with `index` as its starting index. + #[inline] + fn fold_fn<'d>( + parameters: &'d Parameters<C, COM>, + compiler: &'d mut COM, + ) -> impl 'd + + FnMut( + InnerDigest<C, COM>, + (&'d COM::Bool, &'d InnerDigest<C, COM>), + ) -> InnerDigest<C, COM> { + move |acc, (b, d)| { + let (lhs, rhs) = compiler.conditional_swap(b, &acc, d); + parameters.join_in(&lhs, &rhs, compiler) + } + } + + /// Folds `iter` into a root using the path folding algorithm for [`InnerPath`]. + #[inline] + fn fold<'i, I>( + parameters: &'i Parameters<C, COM>, + base: InnerDigest<C, COM>, + iter: I, + compiler: &'i mut COM, + ) -> Root<C, COM> + where + InnerDigest<C, COM>: 'i, + I: IntoIterator<Item = (&'i COM::Bool, &'i InnerDigest<C, COM>)>, + { + iter.into_iter() + .fold(base, Self::fold_fn(parameters, compiler)) + } + } + impl<C, COM> Variable<COM> for InnerPathVar<C, COM> where + COM: ConstraintSystem, + <<COM::Bool as Variable<COM>>::Mode as AllocationMode>::Known: From<Secret>, + <<COM::Bool as Variable<COM>>::Mode as AllocationMode>::Unknown: From<Secret>, C: Configuration<COM> + Variable<COM> + ?Sized, C::Type: Configuration, + InnerDigest<C, COM>: Variable<COM, Type = InnerDigest<C::Type>>, + <<InnerDigest<C, COM> as Variable<COM>>::Mode as AllocationMode>::Known: From<Secret>, + <<InnerDigest<C, COM> as Variable<COM>>::Mode as AllocationMode>::Unknown: From<Secret>, { type Type = InnerPath<C::Type>; type Mode = Secret; #[inline] fn new(compiler: &mut COM, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - todo!() + match allocation { + Allocation::Known(this, mode) => Self { + leaf_index: this.leaf_index.is_right().as_known(compiler, mode), + inner_indices: this + .leaf_index + .parents() + .map(|i| i.is_right().as_known(compiler, mode)) + .collect(), + path: this + .path + .iter() + .map(|d| d.as_known(compiler, mode)) + .collect(), + }, + Allocation::Unknown(mode) => Self { + leaf_index: bool::as_unknown(compiler, mode), + inner_indices: (0..path_length_in::<C, _>()) + .map(|_| bool::as_unknown(compiler, mode)) + .collect(), + path: (0..path_length_in::<C, _>()) + .map(|_| InnerDigest::<C::Type>::as_unknown(compiler, mode)) + .collect(), + }, + } } } - /// + /// Path Variable pub struct PathVar<C, COM> where C: Configuration<COM> + ?Sized, + COM: ConstraintSystem, { /// Sibling Digest pub sibling_digest: LeafDigest<C, COM>, @@ -895,12 +1013,77 @@ pub mod constraint { pub inner_path: InnerPathVar<C, COM>, } + impl<C, COM> PathVar<C, COM> + where + C: Configuration<COM> + ?Sized, + COM: ConstraintSystem, + InnerDigest<C, COM>: ConditionalSelect<COM>, + LeafDigest<C, COM>: ConditionalSelect<COM>, + { + /// Computes the root of the merkle tree relative to `leaf_digest` using `parameters`. + #[inline] + pub fn root( + &self, + parameters: &Parameters<C, COM>, + leaf_digest: &LeafDigest<C, COM>, + compiler: &mut COM, + ) -> Root<C, COM> { + self.inner_path + .root(parameters, leaf_digest, &self.sibling_digest, compiler) + } + + /// Returns `true` if `self` is a witness to the fact that `leaf_digest` is stored in a + /// merkle tree with the given `root`. + #[inline] + pub fn verify_digest( + &self, + parameters: &Parameters<C, COM>, + root: &Root<C, COM>, + leaf_digest: &LeafDigest<C, COM>, + compiler: &mut COM, + ) -> COM::Bool + where + Root<C, COM>: Equal<COM>, + { + let computed_root = self.root(parameters, leaf_digest, compiler); + compiler.eq(root, &computed_root) + } + + /// Returns `true` if `self` is a witness to the fact that `leaf` is stored in a merkle tree + /// with the given `root`. + #[inline] + pub fn verify( + &self, + parameters: &Parameters<C, COM>, + root: &Root<C, COM>, + leaf: &Leaf<C, COM>, + compiler: &mut COM, + ) -> COM::Bool + where + Root<C, COM>: Equal<COM>, + { + self.verify_digest( + parameters, + root, + &parameters.digest_in(leaf, compiler), + compiler, + ) + } + } + impl<C, COM> Variable<COM> for PathVar<C, COM> where + COM: ConstraintSystem, + <<COM::Bool as Variable<COM>>::Mode as AllocationMode>::Known: From<Secret>, + <<COM::Bool as Variable<COM>>::Mode as AllocationMode>::Unknown: From<Secret>, C: Configuration<COM> + Variable<COM> + ?Sized, C::Type: Configuration, LeafDigest<C, COM>: Variable<COM, Type = LeafDigest<C::Type>>, <<LeafDigest<C, COM> as Variable<COM>>::Mode as AllocationMode>::Known: From<Secret>, + <<LeafDigest<C, COM> as Variable<COM>>::Mode as AllocationMode>::Unknown: From<Secret>, + InnerDigest<C, COM>: Variable<COM, Type = InnerDigest<C::Type>>, + <<InnerDigest<C, COM> as Variable<COM>>::Mode as AllocationMode>::Known: From<Secret>, + <<InnerDigest<C, COM> as Variable<COM>>::Mode as AllocationMode>::Unknown: From<Secret>, { type Type = Path<C::Type>; type Mode = Secret; @@ -912,7 +1095,10 @@ pub mod constraint { sibling_digest: this.sibling_digest.as_known(compiler, mode), inner_path: this.inner_path.as_known(compiler, mode), }, - _ => todo!(), + Allocation::Unknown(mode) => Self { + sibling_digest: LeafDigest::<C::Type>::as_unknown(compiler, mode), + inner_path: InnerPath::<C::Type>::as_unknown(compiler, mode), + }, } } } diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index 5940bebe3..44062963b 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -463,27 +463,6 @@ where } } -/// Digest Type -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "LeafDigest<C>: Clone"), - Copy(bound = "LeafDigest<C>: Copy, InnerDigest<C>: Copy"), - Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), - Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), - Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), - PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") -)] -pub enum Digest<C> -where - C: Configuration + ?Sized, -{ - /// Leaf Digest - Leaf(LeafDigest<C>), - - /// Inner Digest - Inner(InnerDigest<C>), -} - /// Merkle Tree Parameters #[derive(derivative::Derivative)] #[derivative( From 1e4af94af17d1b7e23cd689ea174df87de10b39b Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 5 Jan 2022 00:08:56 -0500 Subject: [PATCH 155/275] fix: correct conditional select order for swap --- manta-crypto/src/constraint.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index 32fec8ac4..8cf1766ef 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -536,7 +536,7 @@ where { ( Self::select(bit, lhs, rhs, cs), - Self::select(bit, lhs, rhs, cs), + Self::select(bit, rhs, lhs, cs), ) } } From 132e09850ac362edac7edf9070cfa907deba59a7 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 5 Jan 2022 00:33:01 -0500 Subject: [PATCH 156/275] feat: add in-circuit accumulator model construction for MT --- manta-crypto/src/merkle_tree/tree.rs | 78 +++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 7 deletions(-) diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index 44062963b..eb2855f21 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -26,10 +26,12 @@ use crate::{ self, Accumulator, ConstantCapacityAccumulator, ExactSizeAccumulator, MembershipProof, OptimizedAccumulator, }, - constraint::Native, + constraint::{ + Allocation, ConditionalSelect, Constant, ConstraintSystem, Equal, Native, Variable, + }, merkle_tree::{ fork::Trunk, - path::{CurrentPath, Path}, + path::{constraint::PathVar, CurrentPath, Path}, }, }; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; @@ -524,6 +526,25 @@ where ) -> InnerDigest<C, COM> { C::InnerHash::join_leaves_in(&self.inner, lhs, rhs, compiler) } + + /// Verify that `path` witnesses the fact that `leaf` is a member of a merkle tree with the + /// given `root`. + #[inline] + pub fn verify_path_in( + &self, + path: &PathVar<C, COM>, + root: &Root<C, COM>, + leaf: &Leaf<C, COM>, + compiler: &mut COM, + ) -> COM::Bool + where + C: Configuration<COM>, + COM: ConstraintSystem, + InnerDigest<C, COM>: ConditionalSelect<COM> + Equal<COM>, + LeafDigest<C, COM>: ConditionalSelect<COM>, + { + path.verify(self, root, leaf, compiler) + } } impl<C> Parameters<C> @@ -560,6 +581,29 @@ where } } +use crate::constraint::VariableSource; + +impl<C, COM> Variable<COM> for Parameters<C, COM> +where + C: HashConfiguration<COM> + Variable<COM> + ?Sized, + C::Type: HashConfiguration, + LeafHashParameters<C, COM>: Variable<COM, Type = LeafHashParameters<C::Type>, Mode = Constant>, + InnerHashParameters<C, COM>: + Variable<COM, Type = InnerHashParameters<C::Type>, Mode = Constant>, +{ + type Type = Parameters<C::Type>; + type Mode = Constant; + + #[inline] + fn new(compiler: &mut COM, allocation: Allocation<Self::Type, Self::Mode>) -> Self { + let (this, mode) = allocation.into_known(); + Self::new( + this.leaf.as_known(compiler, mode), + this.inner.as_known(compiler, mode), + ) + } +} + /// Merkle Tree Root pub type Root<C, COM = ()> = InnerDigest<C, COM>; @@ -569,11 +613,8 @@ where InnerDigest<C>: PartialEq, { type Item = Leaf<C>; - type Witness = Path<C>; - type Output = Root<C>; - type Verification = bool; #[inline] @@ -582,13 +623,36 @@ where item: &Self::Item, witness: &Self::Witness, output: &Self::Output, - compiler: &mut (), + _: &mut (), ) -> Self::Verification { - let _ = compiler; self.verify_path(witness, output, item) } } +impl<C, COM> accumulator::Model<COM> for Parameters<C, COM> +where + C: Configuration<COM> + ?Sized, + COM: ConstraintSystem, + InnerDigest<C, COM>: ConditionalSelect<COM> + Equal<COM>, + LeafDigest<C, COM>: ConditionalSelect<COM>, +{ + type Item = Leaf<C, COM>; + type Witness = PathVar<C, COM>; + type Output = Root<C, COM>; + type Verification = COM::Bool; + + #[inline] + fn verify( + &self, + item: &Self::Item, + witness: &Self::Witness, + output: &Self::Output, + compiler: &mut COM, + ) -> Self::Verification { + self.verify_path_in(witness, output, item, compiler) + } +} + /// Merkle Tree #[derive(derivative::Derivative)] #[derivative( From b1879b8ad4b95e5433fc17e939eaa5eff13f14ee Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 5 Jan 2022 13:42:01 -0500 Subject: [PATCH 157/275] wip: improve merkle-tree hash interfaces --- manta-crypto/src/constraint.rs | 20 +++++++ manta-crypto/src/merkle_tree/tree.rs | 47 ++++++++++++--- manta-pay/src/config.rs | 83 ++++++++++++++++++++------- manta-pay/src/crypto/hash/poseidon.rs | 19 +++--- 4 files changed, 130 insertions(+), 39 deletions(-) diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index 8cf1766ef..dd37ec7d7 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -454,6 +454,15 @@ pub trait ConstraintSystem { V::swap(bit, lhs, rhs, self) } + /// Swaps `lhs` and `rhs` in-place if `bit == 1`. + #[inline] + fn conditional_swap_in_place<V>(&mut self, bit: &Self::Bool, lhs: &mut V, rhs: &mut V) + where + V: ConditionalSelect<Self>, + { + V::swap_in_place(bit, lhs, rhs, self) + } + /// Returns proving and verifying contexts for the constraints contained in `self`. #[inline] fn generate_context<P, R>( @@ -539,6 +548,17 @@ where Self::select(bit, rhs, lhs, cs), ) } + + /// Swaps `lhs` and `rhs` in-place if `bit == 1`. + #[inline] + fn swap_in_place(bit: &C::Bool, lhs: &mut Self, rhs: &mut Self, cs: &mut C) + where + Self: Sized, + { + let (swapped_lhs, swapped_rhs) = Self::swap(bit, lhs, rhs, cs); + *lhs = swapped_lhs; + *rhs = swapped_rhs; + } } /// Addition Trait diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index eb2855f21..0d45fa49c 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -65,10 +65,39 @@ pub trait LeafHash<COM = ()> { } } +/// Identity Leaf Hash +/// +/// # Safety +/// +/// This implementation of [`LeafHash`] should only be used when users cannot control the value of a +/// leaf itself, otherwise, using this implementation may not be safe. +pub struct IdentityLeafHash<L, COM = ()>(PhantomData<(L, COM)>) +where + L: Clone; + +impl<L, COM> LeafHash<COM> for IdentityLeafHash<L, COM> +where + L: Clone, +{ + type Leaf = L; + type Parameters = (); + type Output = L; + + #[inline] + fn digest_in( + parameters: &Self::Parameters, + leaf: &Self::Leaf, + compiler: &mut COM, + ) -> Self::Output { + let _ = (parameters, compiler); + leaf.clone() + } +} + /// Merkle Tree Inner Hash pub trait InnerHash<COM = ()> { - /// Leaf Hash Type - type LeafHash: LeafHash<COM>; + /// Leaf Digest Type + type LeafDigest; /// Inner Hash Parameters Type type Parameters; @@ -94,21 +123,21 @@ pub trait InnerHash<COM = ()> { Self::join_in(parameters, lhs, rhs, &mut COM::compiler()) } - /// Combines two [`LeafHash`](Self::LeafHash) digests into an inner digest inside the given + /// Combines two [`LeafDigest`](Self::LeafDigest) values into an inner digest inside the given /// `compiler`. fn join_leaves_in( parameters: &Self::Parameters, - lhs: &<Self::LeafHash as LeafHash<COM>>::Output, - rhs: &<Self::LeafHash as LeafHash<COM>>::Output, + lhs: &Self::LeafDigest, + rhs: &Self::LeafDigest, compiler: &mut COM, ) -> Self::Output; - /// Combines two [`LeafHash`](Self::LeafHash) digests into an inner digest. + /// Combines two [`LeafDigest`](Self::LeafDigest) values into an inner digest. #[inline] fn join_leaves( parameters: &Self::Parameters, - lhs: &<Self::LeafHash as LeafHash<COM>>::Output, - rhs: &<Self::LeafHash as LeafHash<COM>>::Output, + lhs: &Self::LeafDigest, + rhs: &Self::LeafDigest, ) -> Self::Output where COM: Native, @@ -123,7 +152,7 @@ pub trait HashConfiguration<COM = ()> { type LeafHash: LeafHash<COM>; /// Inner Hash Type - type InnerHash: InnerHash<COM, LeafHash = Self::LeafHash>; + type InnerHash: InnerHash<COM, LeafDigest = <Self::LeafHash as LeafHash<COM>>::Output>; } /// Merkle Tree Configuration diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 3ad7fca3c..7e0bdc6cc 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -29,6 +29,7 @@ use bls12_381::Bls12_381; use bls12_381_ed::{ constraints::EdwardsVar as Bls12_381_EdwardsVar, EdwardsProjective as Bls12_381_Edwards, }; +use core::marker::PhantomData; use manta_accounting::{ asset::{Asset, AssetId, AssetValue}, transfer, @@ -94,10 +95,10 @@ pub type KeyAgreementScheme = DiffieHellman<Group>; pub type KeyAgreementSchemeVar = DiffieHellman<GroupVar, Compiler>; /// -pub type Utxo = poseidon::Output<PoseidonSpec<4>, (), 4>; +pub type Utxo = poseidon::Output<PoseidonSpec<4>, 4>; /// -pub struct UtxoCommitmentScheme(pub poseidon::Hash<PoseidonSpec<4>, (), 4>); +pub struct UtxoCommitmentScheme(pub poseidon::Hash<PoseidonSpec<4>, 4>); impl CommitmentScheme for UtxoCommitmentScheme { type Trapdoor = Group; @@ -126,10 +127,10 @@ impl CommitmentScheme for UtxoCommitmentScheme { } /// -pub type UtxoVar = poseidon::Output<PoseidonSpec<4>, Compiler, 4>; +pub type UtxoVar = poseidon::Output<PoseidonSpec<4>, 4, Compiler>; /// -pub struct UtxoCommitmentSchemeVar(pub poseidon::Hash<PoseidonSpec<4>, Compiler, 4>); +pub struct UtxoCommitmentSchemeVar(pub poseidon::Hash<PoseidonSpec<4>, 4, Compiler>); impl CommitmentScheme<Compiler> for UtxoCommitmentSchemeVar { type Trapdoor = GroupVar; @@ -171,10 +172,10 @@ impl Variable<Compiler> for UtxoCommitmentSchemeVar { } /// -pub type VoidNumber = poseidon::Output<PoseidonSpec<2>, (), 2>; +pub type VoidNumber = poseidon::Output<PoseidonSpec<2>, 2>; /// -pub struct VoidNumberHashFunction(pub poseidon::Hash<PoseidonSpec<2>, (), 2>); +pub struct VoidNumberHashFunction(pub poseidon::Hash<PoseidonSpec<2>, 2>); impl BinaryHashFunction for VoidNumberHashFunction { type Left = Utxo; @@ -196,12 +197,12 @@ impl BinaryHashFunction for VoidNumberHashFunction { } /// -pub struct VoidNumberHashFunctionVar(pub poseidon::Hash<PoseidonSpec<2>, Compiler, 2>); +pub struct VoidNumberHashFunctionVar(pub poseidon::Hash<PoseidonSpec<2>, 2, Compiler>); impl BinaryHashFunction<Compiler> for VoidNumberHashFunctionVar { type Left = <UtxoCommitmentSchemeVar as CommitmentScheme<Compiler>>::Output; type Right = <KeyAgreementSchemeVar as key::KeyAgreementScheme<Compiler>>::SecretKey; - type Output = poseidon::Output<PoseidonSpec<2>, Compiler, 1>; + type Output = poseidon::Output<PoseidonSpec<2>, 2, Compiler>; #[inline] fn hash( @@ -269,32 +270,70 @@ impl Variable<Compiler> for AssetValueVar { } /// -pub struct LeafHash; +pub type LeafHash = merkle_tree::IdentityLeafHash<Utxo>; -impl merkle_tree::LeafHash for LeafHash { - type Leaf = Utxo; - type Parameters = (); - type Output = Utxo; +/// +pub type LeafHashVar = merkle_tree::IdentityLeafHash<UtxoVar, Compiler>; + +/// +pub struct InnerHash; + +impl merkle_tree::InnerHash for InnerHash { + type LeafDigest = Utxo; + type Parameters = poseidon::Hash<PoseidonSpec<2>, 2>; + type Output = poseidon::Output<PoseidonSpec<2>, 2>; #[inline] - fn digest_in(_: &Self::Parameters, leaf: &Self::Leaf, _: &mut ()) -> Self::Output { - *leaf + fn join_in( + parameters: &Self::Parameters, + lhs: &Self::Output, + rhs: &Self::Output, + compiler: &mut (), + ) -> Self::Output { + parameters.hash(&[*lhs, *rhs], compiler) + } + + #[inline] + fn join_leaves_in( + parameters: &Self::Parameters, + lhs: &Self::LeafDigest, + rhs: &Self::LeafDigest, + compiler: &mut (), + ) -> Self::Output { + parameters.hash(&[*lhs, *rhs], compiler) } } +/* /// -pub struct LeafHashVar; +pub struct InnerHashVar; -impl merkle_tree::LeafHash<Compiler> for LeafHashVar { - type Leaf = UtxoVar; - type Parameters = (); - type Output = UtxoVar; +impl merkle_tree::InnerHash<Compiler> for InnerHash { + type LeafDigest = UtxoVar; + type Parameters = poseidon::Hash<PoseidonSpec<2>, 2, Compiler>; + type Output = poseidon::Output<PoseidonSpec<2>, 2, Compiler>; + + #[inline] + fn join_in( + parameters: &Self::Parameters, + lhs: &Self::Output, + rhs: &Self::Output, + compiler: &mut COM, + ) -> Self::Output { + parameters.hash(&[*lhs, *rhs], compiler) + } #[inline] - fn digest_in(_: &Self::Parameters, leaf: &Self::Leaf, _: &mut Compiler) -> Self::Output { - *leaf + fn join_leaves_in( + parameters: &Self::Parameters, + lhs: &Self::LeafDigest, + rhs: &Self::LeafDigest, + compiler: &mut COM, + ) -> Self::Output { + parameters.hash(&[*lhs, *rhs], compiler) } } +*/ /// Configuration Structure #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] diff --git a/manta-pay/src/crypto/hash/poseidon.rs b/manta-pay/src/crypto/hash/poseidon.rs index 159229308..64124fed6 100644 --- a/manta-pay/src/crypto/hash/poseidon.rs +++ b/manta-pay/src/crypto/hash/poseidon.rs @@ -65,7 +65,7 @@ where } /// Poseidon Hash -pub struct Hash<S, COM = (), const ARITY: usize = 1> +pub struct Hash<S, const ARITY: usize = 1, COM = ()> where S: Specification<COM>, { @@ -76,7 +76,7 @@ where mds_matrix: Vec<S::Field>, } -impl<S, COM, const ARITY: usize> Hash<S, COM, ARITY> +impl<S, const ARITY: usize, COM> Hash<S, ARITY, COM> where S: Specification<COM>, { @@ -170,7 +170,7 @@ where } } -impl<S, COM, const ARITY: usize> HashFunction<COM> for Hash<S, COM, ARITY> +impl<S, const ARITY: usize, COM> HashFunction<COM> for Hash<S, ARITY, COM> where S: Specification<COM>, { @@ -197,10 +197,11 @@ where } /// Poseidon Hash Input Type -pub type Input<S, COM, const ARITY: usize> = <Hash<S, COM, ARITY> as HashFunction<COM>>::Input; +pub type Input<S, const ARITY: usize, COM = ()> = <Hash<S, ARITY, COM> as HashFunction<COM>>::Input; /// Poseidon Commitment Output Type -pub type Output<S, COM, const ARITY: usize> = <Hash<S, COM, ARITY> as HashFunction<COM>>::Output; +pub type Output<S, const ARITY: usize, COM = ()> = + <Hash<S, ARITY, COM> as HashFunction<COM>>::Output; /// Arkworks Backend #[cfg(feature = "arkworks")] @@ -306,15 +307,17 @@ pub mod arkworks { #[inline] fn apply_sbox(point: &mut Self::Field, compiler: &mut Compiler<S>) { let _ = compiler; - *point = point.pow_by_constant(&[Self::SBOX_EXPONENT]).expect(""); + *point = point + .pow_by_constant(&[Self::SBOX_EXPONENT]) + .expect("Exponentiation is not allowed to fail."); } } - impl<S, const ARITY: usize> Variable<Compiler<S>> for super::Hash<S, Compiler<S>, ARITY> + impl<S, const ARITY: usize> Variable<Compiler<S>> for super::Hash<S, ARITY, Compiler<S>> where S: Specification, { - type Type = super::Hash<S, (), ARITY>; + type Type = super::Hash<S, ARITY>; type Mode = Constant; From be24899f59111cb5fa90e17d707d2090f67f5674 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 5 Jan 2022 17:35:06 -0500 Subject: [PATCH 158/275] wip: finish merkle tree related trait impls --- manta-accounting/src/transfer/mod.rs | 8 +- manta-crypto/src/commitment.rs | 37 +- manta-crypto/src/hash.rs | 60 +- manta-crypto/src/lib.rs | 2 +- manta-crypto/src/merkle_tree/forest.rs | 8 +- manta-crypto/src/merkle_tree/fork.rs | 9 + manta-crypto/src/merkle_tree/full.rs | 18 +- manta-crypto/src/merkle_tree/inner_tree.rs | 18 +- manta-crypto/src/merkle_tree/partial.rs | 22 +- manta-crypto/src/merkle_tree/path.rs | 60 +- manta-crypto/src/merkle_tree/single_path.rs | 6 +- manta-crypto/src/merkle_tree/tree.rs | 16 +- manta-pay/src/accounting/transfer.rs | 127 ----- manta-pay/src/config.rs | 355 +++++------- manta-pay/src/crypto/commitment/mod.rs | 19 - manta-pay/src/crypto/commitment/pedersen.rs | 219 ------- .../constraint/arkworks/constraint_system.rs | 20 +- manta-pay/src/crypto/hash/poseidon.rs | 16 +- manta-pay/src/crypto/merkle_tree.rs | 539 ------------------ manta-pay/src/crypto/mod.rs | 2 - manta-pay/src/{accounting => }/ledger.rs | 0 manta-pay/src/lib.rs | 6 +- manta-pay/src/test.rs | 87 +++ 23 files changed, 398 insertions(+), 1256 deletions(-) delete mode 100644 manta-pay/src/accounting/transfer.rs delete mode 100644 manta-pay/src/crypto/commitment/mod.rs delete mode 100644 manta-pay/src/crypto/commitment/pedersen.rs delete mode 100644 manta-pay/src/crypto/merkle_tree.rs rename manta-pay/src/{accounting => }/ledger.rs (100%) create mode 100644 manta-pay/src/test.rs diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index 271a57320..03c947d69 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -207,7 +207,7 @@ pub trait Configuration { asset: &Asset, ) -> Utxo<Self> { let trapdoor = Self::trapdoor(key_agreement, secret_key, public_key); - utxo_commitment.commit(&trapdoor, asset, &mut ()) + utxo_commitment.commit(&trapdoor, asset) } /// Generates the trapdoor associated to `secret_key` and `public_key` and then uses it to @@ -222,7 +222,7 @@ pub trait Configuration { cs: &mut Self::Compiler, ) -> UtxoVar<Self> { let trapdoor = Self::trapdoor_var(key_agreement, secret_key, public_key, cs); - utxo_commitment.commit(&trapdoor, asset, cs) + utxo_commitment.commit_in(&trapdoor, asset, cs) } /// Generates the void number associated to `utxo` and `secret_key` using `parameters`. @@ -232,7 +232,7 @@ pub trait Configuration { utxo: &Utxo<Self>, secret_key: &SecretKey<Self>, ) -> VoidNumber<Self> { - parameters.hash(utxo, secret_key, &mut ()) + parameters.hash(utxo, secret_key) } /// Generates the void number associated to `utxo` and `secret_key` using `parameters`. @@ -243,7 +243,7 @@ pub trait Configuration { secret_key: &SecretKeyVar<Self>, cs: &mut Self::Compiler, ) -> VoidNumberVar<Self> { - parameters.hash(utxo, secret_key, cs) + parameters.hash_in(utxo, secret_key, cs) } /// Checks that the `utxo` is correctly constructed from the `secret_key`, `public_key`, and diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index ba1e4d975..c1b10e758 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -16,7 +16,7 @@ //! Commitment Schemes -pub use crate::util::{Builder, Input}; +use crate::constraint::Native; /// Commitment Scheme pub trait CommitmentScheme<COM = ()> { @@ -29,43 +29,20 @@ pub trait CommitmentScheme<COM = ()> { /// Output Type type Output; - /// Commits to the `input` value using randomness `trapdoor`. - fn commit( + /// Commits to the `input` value using the randomness `trapdoor` inside the `compiler`. + fn commit_in( &self, trapdoor: &Self::Trapdoor, input: &Self::Input, compiler: &mut COM, ) -> Self::Output; - /// Starts a new [`Builder`] for extended commitments. + /// Commits to the `input` value using the randomness `trapdoor`. #[inline] - fn start<'c>( - &'c self, - trapdoor: &'c Self::Trapdoor, - ) -> Builder<'c, Self, Self::Input, Self::Trapdoor> + fn commit(&self, trapdoor: &Self::Trapdoor, input: &Self::Input) -> Self::Output where - Self::Input: Default, + COM: Native, { - Builder::new(self, trapdoor) - } -} - -impl<'c, C, I, T> Builder<'c, C, I, T> { - /// Commits to the input stored in the builder against the given `compiler`. - #[inline] - pub fn commit_with_compiler<COM>(self, compiler: &mut COM) -> C::Output - where - C: CommitmentScheme<COM, Trapdoor = T, Input = I>, - { - self.base.commit(self.args, &self.input, compiler) - } - - /// Commits to the input stored in the builder. - #[inline] - pub fn commit(self) -> C::Output - where - C: CommitmentScheme<Trapdoor = T, Input = I>, - { - self.commit_with_compiler(&mut ()) + self.commit_in(trapdoor, input, &mut COM::compiler()) } } diff --git a/manta-crypto/src/hash.rs b/manta-crypto/src/hash.rs index 6ee102671..e7223d0ff 100644 --- a/manta-crypto/src/hash.rs +++ b/manta-crypto/src/hash.rs @@ -16,26 +16,47 @@ //! Hash Functions -pub use crate::util::{Builder, Input}; +use crate::constraint::Native; /// Hash Function -pub trait HashFunction<COM = ()> { +pub trait HashFunction<const ARITY: usize = 1, COM = ()> { /// Input Type type Input: ?Sized; /// Output Type type Output; - /// Performs a hash over `input` in the given `compiler`. - fn hash(&self, input: &Self::Input, compiler: &mut COM) -> Self::Output; + /// Computes the hash over `input` in the given `compiler`. + fn hash_in(&self, input: [&Self::Input; ARITY], compiler: &mut COM) -> Self::Output; - /// Starts a new [`Builder`] for extended hashes. + /// Computes the hash over `input`. #[inline] - fn start(&self) -> Builder<Self, Self::Input> + fn hash(&self, input: [&Self::Input; ARITY]) -> Self::Output where - Self::Input: Default, + COM: Native, { - Builder::new(self, &()) + self.hash_in(input, &mut COM::compiler()) + } +} + +/// Unary Hash Function +pub trait UnaryHashFunction<COM = ()> { + /// Input Type + type Input: ?Sized; + + /// Output Type + type Output; + + /// Computes the hash over `input` in the given `compiler`. + fn hash_in(&self, input: &Self::Input, compiler: &mut COM) -> Self::Output; + + /// Computes the hash over `input`. + #[inline] + fn hash(&self, input: &Self::Input) -> Self::Output + where + COM: Native, + { + self.hash_in(input, &mut COM::compiler()) } } @@ -50,26 +71,15 @@ pub trait BinaryHashFunction<COM = ()> { /// Output Type type Output; - /// Performs a hash over `lhs` and `rhs` in the given `compiler`. - fn hash(&self, lhs: &Self::Left, rhs: &Self::Right, compiler: &mut COM) -> Self::Output; -} - -impl<'h, H, I> Builder<'h, H, I> { - /// Hashes the input stored in the builder against the given `compiler`. - #[inline] - pub fn hash_with_compiler<COM>(self, compiler: &mut COM) -> H::Output - where - H: HashFunction<COM, Input = I>, - { - self.base.hash(&self.input, compiler) - } + /// Computes the hash over `lhs` and `rhs` in the given `compiler`. + fn hash_in(&self, lhs: &Self::Left, rhs: &Self::Right, compiler: &mut COM) -> Self::Output; - /// Hashes the input stored in the builder. + /// Computes the hash over `lhs` and `rhs`. #[inline] - pub fn hash(self) -> H::Output + fn hash(&self, lhs: &Self::Left, rhs: &Self::Right) -> Self::Output where - H: HashFunction<Input = I>, + COM: Native, { - self.hash_with_compiler(&mut ()) + self.hash_in(lhs, rhs, &mut COM::compiler()) } } diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index 20b7b0114..e0faad28d 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -32,4 +32,4 @@ pub mod hash; pub mod key; pub mod merkle_tree; pub mod rand; -pub mod util; +// TODO[remove]: pub mod util; diff --git a/manta-crypto/src/merkle_tree/forest.rs b/manta-crypto/src/merkle_tree/forest.rs index 9e55067bb..71d3711af 100644 --- a/manta-crypto/src/merkle_tree/forest.rs +++ b/manta-crypto/src/merkle_tree/forest.rs @@ -244,7 +244,7 @@ where C: Configuration + ?Sized, F: Forest<C>, F::Tree: WithProofs<C>, - InnerDigest<C>: PartialEq, + InnerDigest<C>: Clone + PartialEq, { type Item = Leaf<C>; @@ -293,7 +293,7 @@ where C: Configuration + ?Sized, F: ConstantWidthForest<C>, F::Tree: WithProofs<C>, - InnerDigest<C>: PartialEq, + InnerDigest<C>: Clone + PartialEq, { #[inline] fn capacity() -> usize { @@ -306,7 +306,7 @@ where C: Configuration + ?Sized, F: Forest<C>, F::Tree: WithProofs<C>, - InnerDigest<C>: PartialEq, + InnerDigest<C>: Clone + PartialEq, { #[inline] fn len(&self) -> usize { @@ -324,7 +324,7 @@ where C: Configuration + ?Sized, F: Forest<C>, F::Tree: WithProofs<C>, - InnerDigest<C>: PartialEq, + InnerDigest<C>: Clone + PartialEq, { #[inline] fn insert_nonprovable(&mut self, item: &Self::Item) -> bool { diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs index fa40729c0..75cb9eb3d 100644 --- a/manta-crypto/src/merkle_tree/fork.rs +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -82,6 +82,7 @@ where where M: Default + InnerMap<C>, LeafDigest<C>: Default, + InnerDigest<C>: Default, { Fork::new(parameters, self) } @@ -93,6 +94,7 @@ where where M: Default + InnerMap<C>, LeafDigest<C>: Default, + InnerDigest<C>: Default, { fork.attach(parameters, self) } @@ -259,6 +261,7 @@ where pub fn new(parameters: &Parameters<C>, trunk: &Trunk<C, T, P>) -> Self where LeafDigest<C>: Default, + InnerDigest<C>: Default, { Self::with_leaves(parameters, trunk, Default::default()).unwrap() } @@ -273,6 +276,7 @@ where ) -> Option<Self> where LeafDigest<C>: Default, + InnerDigest<C>: Default, { let (base_contribution, branch) = Self::new_branch(parameters, trunk.borrow_base().as_ref(), leaf_digests)?; @@ -292,6 +296,7 @@ where ) -> Option<(BaseContribution, Partial<C, M>)> where LeafDigest<C>: Default, + InnerDigest<C>: Default, { if leaf_digests.len() + base.len() >= capacity::<C>() { return None; @@ -309,6 +314,7 @@ where ) -> (BaseContribution, Partial<C, M>) where LeafDigest<C>: Default, + InnerDigest<C>: Default, { let (base_contribution, base_inner_digest, base_leaf_digests, inner_path) = Self::generate_branch_setup(parameters, base); @@ -336,6 +342,7 @@ where ) where LeafDigest<C>: Default, + InnerDigest<C>: Default, { if base.is_empty() { ( @@ -388,6 +395,7 @@ where ) -> bool where LeafDigest<C>: Default, + InnerDigest<C>: Default, { if branch.len() + base.len() - (*base_contribution as usize) >= capacity::<C>() { return false; @@ -408,6 +416,7 @@ where pub fn attach(&mut self, parameters: &Parameters<C>, trunk: &Trunk<C, T, P>) -> bool where LeafDigest<C>: Default, + InnerDigest<C>: Default, { if !Self::try_rebase( parameters, diff --git a/manta-crypto/src/merkle_tree/full.rs b/manta-crypto/src/merkle_tree/full.rs index a774e91a0..f2d30e894 100644 --- a/manta-crypto/src/merkle_tree/full.rs +++ b/manta-crypto/src/merkle_tree/full.rs @@ -141,7 +141,7 @@ where C: Configuration + ?Sized, M: InnerMap<C> + Default, LeafDigest<C>: Clone + Default, - InnerDigest<C>: PartialEq, + InnerDigest<C>: Clone + Default + PartialEq, { #[inline] fn new(parameters: &Parameters<C>) -> Self { @@ -193,7 +193,7 @@ where C: Configuration + ?Sized, M: Default + InnerMap<C>, LeafDigest<C>: Clone + Default + PartialEq, - InnerDigest<C>: PartialEq, + InnerDigest<C>: Clone + Default + PartialEq, { #[inline] fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>> { @@ -220,9 +220,9 @@ where #[inline] fn path(&self, parameters: &Parameters<C>, index: usize) -> Result<Path<C>, PathError> { let _ = parameters; - let len = self.len(); - if index > 0 && index >= len { - return Err(PathError::IndexTooLarge(len)); + let length = self.len(); + if index > 0 && index >= length { + return Err(PathError::IndexTooLarge { length }); } let leaf_index = Node(index); Ok(Path::from_inner( @@ -230,4 +230,12 @@ where self.inner_digests.path(leaf_index), )) } + + #[inline] + fn remove_path(&mut self, index: usize) -> bool { + // NOTE: This method cannot be implemented, since this violates the semantics of this tree, + // which is supposed to keep all of its nodes forever. + let _ = index; + false + } } diff --git a/manta-crypto/src/merkle_tree/inner_tree.rs b/manta-crypto/src/merkle_tree/inner_tree.rs index b01b7a41e..5a7a35ef4 100644 --- a/manta-crypto/src/merkle_tree/inner_tree.rs +++ b/manta-crypto/src/merkle_tree/inner_tree.rs @@ -314,10 +314,10 @@ where /// Sentinel Source for a Single Sentinel Value #[derive(derivative::Derivative)] #[derivative( - Clone(bound = ""), + Clone(bound = "InnerDigest<C>: Clone"), Copy(bound = "InnerDigest<C>: Copy"), Debug(bound = "InnerDigest<C>: Debug"), - Default(bound = ""), + Default(bound = "InnerDigest<C>: Default"), Eq(bound = "InnerDigest<C>: Eq"), Hash(bound = "InnerDigest<C>: Hash"), PartialEq(bound = "InnerDigest<C>: PartialEq") @@ -539,7 +539,10 @@ where /// Returns the path at `leaf_index`. #[inline] - pub fn path(&self, leaf_index: Node) -> InnerPath<C> { + pub fn path(&self, leaf_index: Node) -> InnerPath<C> + where + InnerDigest<C>: Clone, + { InnerPath::new( leaf_index, self.path_iter_for_leaf(leaf_index).cloned().collect(), @@ -551,7 +554,7 @@ impl<C, M> InnerTree<C, M, Sentinel<C>> where C: Configuration + ?Sized, M: InnerMap<C>, - InnerDigest<C>: PartialEq, + InnerDigest<C>: Clone + PartialEq, { /// Returns the path at `leaf_index`, assuming that `leaf_index` is the right-most index, /// so that the return value is a valid [`CurrentInnerPath`]. @@ -737,7 +740,10 @@ where /// Returns the path at `leaf_index` without checking if `leaf_index` is later than the /// starting index of this tree. #[inline] - pub fn path_unchecked(&self, leaf_index: Node) -> InnerPath<C> { + pub fn path_unchecked(&self, leaf_index: Node) -> InnerPath<C> + where + InnerDigest<C>: Clone, + { self.inner_tree.path(leaf_index) } } @@ -746,7 +752,7 @@ impl<C, M> PartialInnerTree<C, M, Sentinel<C>> where C: Configuration + ?Sized, M: InnerMap<C>, - InnerDigest<C>: PartialEq, + InnerDigest<C>: Clone + PartialEq, { /// Returns the path at `leaf_index`, assuming that `leaf_index` is the right-most index, /// so that the return value is a valid [`CurrentInnerPath`]. diff --git a/manta-crypto/src/merkle_tree/partial.rs b/manta-crypto/src/merkle_tree/partial.rs index 0e56ef9f5..24121b4bc 100644 --- a/manta-crypto/src/merkle_tree/partial.rs +++ b/manta-crypto/src/merkle_tree/partial.rs @@ -178,7 +178,7 @@ where C: Configuration + ?Sized, M: InnerMap<C> + Default, LeafDigest<C>: Clone + Default, - InnerDigest<C>: PartialEq, + InnerDigest<C>: Clone + Default + PartialEq, { #[inline] fn new(parameters: &Parameters<C>) -> Self { @@ -231,7 +231,7 @@ where C: Configuration + ?Sized, M: Default + InnerMap<C>, LeafDigest<C>: Clone + Default + PartialEq, - InnerDigest<C>: PartialEq, + InnerDigest<C>: Clone + Default + PartialEq, { #[inline] fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>> { @@ -257,22 +257,20 @@ where #[inline] fn path(&self, parameters: &Parameters<C>, index: usize) -> Result<Path<C>, PathError> { - // FIXME: Implement this: - // TODO: Make sure we don't query paths too far to the left. - /* + // FIXME: Check that this is implemented properly. let _ = parameters; - if index > 0 && index >= self.leaf_digests.len() { - return Err(()); + let length = self.len(); + if index > 0 && index >= length { + return Err(PathError::IndexTooLarge { length }); + } + if index < self.starting_leaf_index().0 { + return Err(PathError::MissingPath); } let leaf_index = Node(index); - Ok(Path::new( - leaf_index, + Ok(Path::from_inner( self.get_owned_leaf_sibling(leaf_index), self.inner_digests.path_unchecked(leaf_index), )) - */ - let _ = (parameters, index); - todo!() } #[inline] diff --git a/manta-crypto/src/merkle_tree/path.rs b/manta-crypto/src/merkle_tree/path.rs index 88dce19dd..d8e20f245 100644 --- a/manta-crypto/src/merkle_tree/path.rs +++ b/manta-crypto/src/merkle_tree/path.rs @@ -80,7 +80,7 @@ where #[inline] pub fn is_current(&self) -> bool where - InnerDigest<C>: PartialEq, + InnerDigest<C>: Default + PartialEq, { self.is_current_with(&Default::default()) } @@ -167,6 +167,7 @@ where impl<C> Default for InnerPath<C> where C: Configuration + ?Sized, + InnerDigest<C>: Default, { #[inline] fn default() -> Self { @@ -190,6 +191,7 @@ where impl<C> From<CurrentInnerPath<C>> for InnerPath<C> where C: Configuration + ?Sized, + InnerDigest<C>: Default, { #[inline] fn from(path: CurrentInnerPath<C>) -> Self { @@ -268,7 +270,7 @@ where #[inline] pub fn from_path_unchecked(path: InnerPath<C>) -> Self where - InnerDigest<C>: PartialEq, + InnerDigest<C>: Default + PartialEq, { Self::from_path_unchecked_with(path, &Default::default()) } @@ -286,7 +288,10 @@ where /// Computes the root of the merkle tree relative to `base` using `parameters`. #[inline] - pub fn root_from_base(&self, parameters: &Parameters<C>, base: InnerDigest<C>) -> Root<C> { + pub fn root_from_base(&self, parameters: &Parameters<C>, base: InnerDigest<C>) -> Root<C> + where + InnerDigest<C>: Default, + { Self::fold( parameters, 0, @@ -305,7 +310,10 @@ where parameters: &Parameters<C>, leaf_digest: &LeafDigest<C>, sibling_digest: &LeafDigest<C>, - ) -> Root<C> { + ) -> Root<C> + where + InnerDigest<C>: Default, + { self.root_from_base( parameters, self.leaf_index @@ -324,7 +332,7 @@ where sibling_digest: &LeafDigest<C>, ) -> bool where - InnerDigest<C>: PartialEq, + InnerDigest<C>: Default + PartialEq, { root == &self.root(parameters, leaf_digest, sibling_digest) } @@ -388,6 +396,7 @@ where ) -> Root<C> where LeafDigest<C>: Default, + InnerDigest<C>: Default, { let mut last_index = self.leaf_index; let mut index = self.leaf_index + 1; @@ -450,7 +459,7 @@ where impl<C> TryFrom<InnerPath<C>> for CurrentInnerPath<C> where C: Configuration + ?Sized, - InnerDigest<C>: PartialEq, + InnerDigest<C>: Default + PartialEq, { type Error = InnerPath<C>; @@ -468,6 +477,7 @@ where impl<C> IntoIterator for CurrentInnerPath<C> where C: Configuration + ?Sized, + InnerDigest<C>: Default, { type Item = InnerDigest<C>; @@ -497,6 +507,7 @@ where impl<C> Iterator for CurrentInnerPathIntoIter<C> where C: Configuration + ?Sized, + InnerDigest<C>: Default, { type Item = InnerDigest<C>; @@ -516,9 +527,19 @@ where } } -impl<C> ExactSizeIterator for CurrentInnerPathIntoIter<C> where C: Configuration + ?Sized {} +impl<C> ExactSizeIterator for CurrentInnerPathIntoIter<C> +where + C: Configuration + ?Sized, + InnerDigest<C>: Default, +{ +} -impl<C> FusedIterator for CurrentInnerPathIntoIter<C> where C: Configuration + ?Sized {} +impl<C> FusedIterator for CurrentInnerPathIntoIter<C> +where + C: Configuration + ?Sized, + InnerDigest<C>: Default, +{ +} /// [`InnerNode`] Iterator for [`CurrentInnerPath`] pub struct CurrentInnerPathNodeIter<C> @@ -626,7 +647,7 @@ where #[inline] pub fn is_current(&self) -> bool where - InnerDigest<C>: PartialEq, + InnerDigest<C>: Default + PartialEq, { self.is_current_with(&Default::default()) } @@ -636,7 +657,7 @@ where #[inline] pub fn is_current_with(&self, default: &InnerDigest<C>) -> bool where - InnerDigest<C>: PartialEq, + InnerDigest<C>: Default + PartialEq, { self.inner_path.is_current_with(default) } @@ -678,6 +699,7 @@ where impl<C> From<CurrentPath<C>> for Path<C> where C: Configuration + ?Sized, + InnerDigest<C>: Default, { #[inline] fn from(path: CurrentPath<C>) -> Self { @@ -712,7 +734,7 @@ where /// Merkle Tree Current Path #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "LeafDigest<C>: Clone"), + Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default"), Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), @@ -758,7 +780,7 @@ where #[inline] pub fn from_path_unchecked(path: Path<C>) -> Self where - InnerDigest<C>: PartialEq, + InnerDigest<C>: Default + PartialEq, { Self::from_path_unchecked_with(path, &Default::default()) } @@ -768,7 +790,7 @@ where #[inline] pub fn from_path_unchecked_with(path: Path<C>, default: &InnerDigest<C>) -> Self where - InnerDigest<C>: PartialEq, + InnerDigest<C>: Default + PartialEq, { Self::from_inner( path.sibling_digest, @@ -784,7 +806,10 @@ where /// Computes the root of the merkle tree relative to `leaf_digest` using `parameters`. #[inline] - pub fn root(&self, parameters: &Parameters<C>, leaf_digest: &LeafDigest<C>) -> Root<C> { + pub fn root(&self, parameters: &Parameters<C>, leaf_digest: &LeafDigest<C>) -> Root<C> + where + InnerDigest<C>: Default, + { self.inner_path .root(parameters, leaf_digest, &self.sibling_digest) } @@ -799,7 +824,7 @@ where leaf_digest: &LeafDigest<C>, ) -> bool where - InnerDigest<C>: PartialEq, + InnerDigest<C>: Default + PartialEq, { self.inner_path .verify_digest(parameters, root, leaf_digest, &self.sibling_digest) @@ -810,7 +835,7 @@ where #[inline] pub fn verify(&self, parameters: &Parameters<C>, root: &Root<C>, leaf: &Leaf<C>) -> bool where - InnerDigest<C>: PartialEq, + InnerDigest<C>: Default + PartialEq, { self.verify_digest(parameters, root, &parameters.digest(leaf)) } @@ -825,6 +850,7 @@ where ) -> Root<C> where LeafDigest<C>: Default, + InnerDigest<C>: Default, { self.inner_path .update(parameters, current, &mut self.sibling_digest, next) @@ -834,7 +860,7 @@ where impl<C> TryFrom<Path<C>> for CurrentPath<C> where C: Configuration + ?Sized, - InnerDigest<C>: PartialEq, + InnerDigest<C>: Default + PartialEq, { type Error = Path<C>; diff --git a/manta-crypto/src/merkle_tree/single_path.rs b/manta-crypto/src/merkle_tree/single_path.rs index 4ad5e161a..563b920ab 100644 --- a/manta-crypto/src/merkle_tree/single_path.rs +++ b/manta-crypto/src/merkle_tree/single_path.rs @@ -111,7 +111,10 @@ where /// Computes the root of the tree under the assumption that `self.leaf_digest.is_some()` /// evaluates to `true`. #[inline] - fn compute_root(&self, parameters: &Parameters<C>) -> Root<C> { + fn compute_root(&self, parameters: &Parameters<C>) -> Root<C> + where + InnerDigest<C>: Default, + { self.current_path .root(parameters, self.leaf_digest().unwrap()) } @@ -121,6 +124,7 @@ impl<C> Tree<C> for SinglePath<C> where C: Configuration + ?Sized, LeafDigest<C>: Clone + Default, + InnerDigest<C>: Clone + Default, { #[inline] fn new(parameters: &Parameters<C>) -> Self { diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index 0d45fa49c..709f3ece2 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -103,7 +103,7 @@ pub trait InnerHash<COM = ()> { type Parameters; /// Inner Hash Output Type - type Output: Clone + Default; + type Output; /// Combines two inner digests into a new inner digest using `parameters` inside the given /// `compiler`. @@ -411,10 +411,10 @@ pub enum PathError { MissingPath, /// Given index exceeded the length of the tree - IndexTooLarge( + IndexTooLarge { /// Length of the tree - usize, - ), + length: usize, + }, } /// Merkle Tree Membership Proof Mixin @@ -962,7 +962,7 @@ impl<C, T> Accumulator for MerkleTree<C, T> where C: Configuration + ?Sized, T: Tree<C> + WithProofs<C>, - InnerDigest<C>: PartialEq, + InnerDigest<C>: Clone + PartialEq, { type Item = Leaf<C>; @@ -997,7 +997,7 @@ impl<C, T> ConstantCapacityAccumulator for MerkleTree<C, T> where C: Configuration + ?Sized, T: Tree<C> + WithProofs<C>, - InnerDigest<C>: PartialEq, + InnerDigest<C>: Clone + PartialEq, { #[inline] fn capacity() -> usize { @@ -1009,7 +1009,7 @@ impl<C, T> ExactSizeAccumulator for MerkleTree<C, T> where C: Configuration + ?Sized, T: Tree<C> + WithProofs<C>, - InnerDigest<C>: PartialEq, + InnerDigest<C>: Clone + PartialEq, { #[inline] fn len(&self) -> usize { @@ -1026,7 +1026,7 @@ impl<C, T> OptimizedAccumulator for MerkleTree<C, T> where C: Configuration + ?Sized, T: Tree<C> + WithProofs<C>, - InnerDigest<C>: PartialEq, + InnerDigest<C>: Clone + PartialEq, { #[inline] fn insert_nonprovable(&mut self, item: &Self::Item) -> bool { diff --git a/manta-pay/src/accounting/transfer.rs b/manta-pay/src/accounting/transfer.rs deleted file mode 100644 index 71a09dd84..000000000 --- a/manta-pay/src/accounting/transfer.rs +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Transfer Implementations - -use crate::crypto::merkle_tree::ConfigConverter; -use manta_accounting::{ - identity, transfer, - transfer::{self as transfer, canonical}, -}; -use manta_crypto::merkle_tree::{self, full::Full}; - -/// Unspent Transaction Output -pub type Utxo = transfer::Utxo<Configuration>; - -/// Void Number -pub type VoidNumber = transfer::VoidNumber<Configuration>; - -/// UTXO Set Parameters -pub type Parameters = merkle_tree::Parameters<ConfigConverter<Configuration>>; - -/// UTXO Set Root -pub type Root = merkle_tree::Root<ConfigConverter<Configuration>>; - -/// UTXO Set Path -pub type Path = merkle_tree::Path<ConfigConverter<Configuration>>; - -/// Mint Transaction Type -pub type Mint = canonical::Mint<Configuration>; - -/// Private Transfer Transaction Type -pub type PrivateTransfer = canonical::PrivateTransfer<Configuration>; - -/// Reclaim Transaction Type -pub type Reclaim = canonical::Reclaim<Configuration>; - -/// Transfer Post Type -pub type TransferPost = transfer::TransferPost<Configuration>; - -/// Testing Suite -#[cfg(test)] -mod test { - use crate::accounting::{ - config, - identity::UtxoSet, - transfer::{Mint, PrivateTransfer, Reclaim}, - }; - use manta_crypto::{ - constraint::{measure::Measure, ProofSystem}, - rand::Rand, - }; - use rand::thread_rng; - - /// Tests the generation of proving/verifying contexts for [`Mint`]. - #[test] - fn sample_mint_context() { - let mut rng = thread_rng(); - let cs = Mint::sample_unknown_constraints(&mut rng); - println!("Mint: {:?}", cs.measure()); - config::ProofSystem::generate_context(cs, &mut rng).unwrap(); - } - - /// Tests the generation of proving/verifying contexts for [`PrivateTransfer`]. - #[test] - fn sample_private_transfer_context() { - let mut rng = thread_rng(); - let cs = PrivateTransfer::sample_unknown_constraints(&mut rng); - println!("PrivateTransfer: {:?}", cs.measure()); - config::ProofSystem::generate_context(cs, &mut rng).unwrap(); - } - - /// Tests the generation of proving/verifying contexts for [`Reclaim`]. - #[test] - fn sample_reclaim_context() { - let mut rng = thread_rng(); - let cs = Reclaim::sample_unknown_constraints(&mut rng); - println!("Reclaim: {:?}", cs.measure()); - config::ProofSystem::generate_context(cs, &mut rng).unwrap(); - } - - /// Tests the generation of a [`Mint`]. - #[test] - fn mint() { - let mut rng = thread_rng(); - assert!(matches!( - Mint::sample_and_check_proof(&rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng), - Ok(true) - )); - } - - /// Tests the generation of a [`PrivateTransfer`]. - #[test] - fn private_transfer() { - let mut rng = thread_rng(); - assert!(matches!( - PrivateTransfer::sample_and_check_proof( - &rng.gen(), - &mut UtxoSet::new(rng.gen()), - &mut rng - ), - Ok(true) - )); - } - - /// Tests the generation of a [`Reclaim`]. - #[test] - fn reclaim() { - let mut rng = thread_rng(); - assert!(matches!( - Reclaim::sample_and_check_proof(&rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng), - Ok(true) - )); - } -} diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 7e0bdc6cc..6b0e8e5cc 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -29,7 +29,6 @@ use bls12_381::Bls12_381; use bls12_381_ed::{ constraints::EdwardsVar as Bls12_381_EdwardsVar, EdwardsProjective as Bls12_381_Edwards, }; -use core::marker::PhantomData; use manta_accounting::{ asset::{Asset, AssetId, AssetValue}, transfer, @@ -41,8 +40,7 @@ use manta_crypto::{ ecc::DiffieHellman, encryption, hash::{BinaryHashFunction, HashFunction}, - key::{self, KeyDerivationFunction}, - merkle_tree, + key, merkle_tree, }; #[doc(inline)] @@ -50,13 +48,13 @@ pub use ark_bls12_381 as bls12_381; #[doc(inline)] pub use ark_ed_on_bls12_381 as bls12_381_ed; -/// +/// Pairing Curve Type pub type PairingCurve = Bls12_381; -/// +/// Embedded Group Type pub type Group = ecc::arkworks::Group<Bls12_381_Edwards>; -/// +/// Embedded Group Variable Type pub type GroupVar = ecc::arkworks::GroupVar<Bls12_381_Edwards, Bls12_381_EdwardsVar>; /// Constraint Field @@ -71,7 +69,7 @@ pub type Compiler = R1CS<ConstraintField>; /// Proof System pub type ProofSystem = Groth16<PairingCurve>; -/// +/// Poseidon Specification pub struct PoseidonSpec<const ARITY: usize>; impl poseidon::arkworks::Specification for PoseidonSpec<2> { @@ -88,16 +86,16 @@ impl poseidon::arkworks::Specification for PoseidonSpec<4> { const SBOX_EXPONENT: u64 = 5; } -/// +/// Key Agreement Scheme Type pub type KeyAgreementScheme = DiffieHellman<Group>; -/// +/// Key Agreement Scheme Variable Type pub type KeyAgreementSchemeVar = DiffieHellman<GroupVar, Compiler>; -/// +/// Unspent Transaction Output Type pub type Utxo = poseidon::Output<PoseidonSpec<4>, 4>; -/// +/// UTXO Commitment Scheme pub struct UtxoCommitmentScheme(pub poseidon::Hash<PoseidonSpec<4>, 4>); impl CommitmentScheme for UtxoCommitmentScheme { @@ -106,30 +104,27 @@ impl CommitmentScheme for UtxoCommitmentScheme { type Output = Utxo; #[inline] - fn commit( + fn commit_in( &self, trapdoor: &Self::Trapdoor, input: &Self::Input, - compiler: &mut (), + _: &mut (), ) -> Self::Output { // NOTE: The group is in projective form, so we need to convert it first. let trapdoor = trapdoor.0.into_affine(); - self.0.hash( - &[ - trapdoor.x, - trapdoor.y, - input.id.0.into(), - input.value.0.into(), - ], - compiler, - ) + self.0.hash([ + &trapdoor.x, + &trapdoor.y, + &input.id.0.into(), + &input.value.0.into(), + ]) } } -/// +/// Unspent Transaction Output Variable Type pub type UtxoVar = poseidon::Output<PoseidonSpec<4>, 4, Compiler>; -/// +/// UTXO Commitment Scheme Variable pub struct UtxoCommitmentSchemeVar(pub poseidon::Hash<PoseidonSpec<4>, 4, Compiler>); impl CommitmentScheme<Compiler> for UtxoCommitmentSchemeVar { @@ -138,20 +133,15 @@ impl CommitmentScheme<Compiler> for UtxoCommitmentSchemeVar { type Output = UtxoVar; #[inline] - fn commit( + fn commit_in( &self, trapdoor: &Self::Trapdoor, input: &Self::Input, compiler: &mut Compiler, ) -> Self::Output { // NOTE: The group is already in affine form, so we can extract `x` and `y`. - self.0.hash( - &[ - trapdoor.0.x.clone(), - trapdoor.0.y.clone(), - input.id.0.clone(), - input.value.0.clone(), - ], + self.0.hash_in( + [&trapdoor.0.x, &trapdoor.0.y, &input.id.0, &input.value.0], compiler, ) } @@ -159,22 +149,19 @@ impl CommitmentScheme<Compiler> for UtxoCommitmentSchemeVar { impl Variable<Compiler> for UtxoCommitmentSchemeVar { type Type = UtxoCommitmentScheme; - type Mode = Constant; #[inline] fn new(cs: &mut Compiler, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - match allocation { - Allocation::Known(this, mode) => Self(this.0.as_known(cs, mode)), - _ => unreachable!("Constants cannot be unknown."), - } + let (this, mode) = allocation.into_known(); + Self(this.0.as_known(cs, mode)) } } -/// +/// Void Number Type pub type VoidNumber = poseidon::Output<PoseidonSpec<2>, 2>; -/// +/// Void Number Hash Function pub struct VoidNumberHashFunction(pub poseidon::Hash<PoseidonSpec<2>, 2>); impl BinaryHashFunction for VoidNumberHashFunction { @@ -183,20 +170,20 @@ impl BinaryHashFunction for VoidNumberHashFunction { type Output = VoidNumber; #[inline] - fn hash(&self, left: &Self::Left, right: &Self::Right, compiler: &mut ()) -> Self::Output { - self.0.hash( - &[ - *left, - // FIXME: This is the lift from inner scalar to outer scalar and only exists in some - // cases! We need a better abstraction for this. - ConstraintField::from_le_bytes_mod_order(&right.into_repr().to_bytes_le()), - ], - compiler, - ) + fn hash_in(&self, left: &Self::Left, right: &Self::Right, _: &mut ()) -> Self::Output { + self.0.hash([ + left, + // FIXME: This is the lift from inner scalar to outer scalar and only exists in some + // cases! We need a better abstraction for this. + &ConstraintField::from_le_bytes_mod_order(&right.into_repr().to_bytes_le()), + ]) } } -/// +/// Void Number Variable Type +pub type VoidNumberVar = poseidon::Output<PoseidonSpec<2>, 2, Compiler>; + +/// Void Number Hash Function Variable pub struct VoidNumberHashFunctionVar(pub poseidon::Hash<PoseidonSpec<2>, 2, Compiler>); impl BinaryHashFunction<Compiler> for VoidNumberHashFunctionVar { @@ -205,31 +192,28 @@ impl BinaryHashFunction<Compiler> for VoidNumberHashFunctionVar { type Output = poseidon::Output<PoseidonSpec<2>, 2, Compiler>; #[inline] - fn hash( + fn hash_in( &self, left: &Self::Left, right: &Self::Right, compiler: &mut Compiler, ) -> Self::Output { - self.0.hash(&[left.clone(), right.clone()], compiler) + self.0.hash_in([left, right], compiler) } } impl Variable<Compiler> for VoidNumberHashFunctionVar { type Type = VoidNumberHashFunction; - type Mode = Constant; #[inline] fn new(cs: &mut Compiler, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - match allocation { - Allocation::Known(this, mode) => Self(this.0.as_known(cs, mode)), - _ => unreachable!("Constants cannot be unknown."), - } + let (this, mode) = allocation.into_known(); + Self(this.0.as_known(cs, mode)) } } -/// +/// Asset ID Variable pub struct AssetIdVar(ConstraintFieldVar); impl Variable<Compiler> for AssetIdVar { @@ -249,7 +233,7 @@ impl Variable<Compiler> for AssetIdVar { } } -/// +/// Asset Value Variable pub struct AssetValueVar(ConstraintFieldVar); impl Variable<Compiler> for AssetValueVar { @@ -269,13 +253,13 @@ impl Variable<Compiler> for AssetValueVar { } } -/// +/// Leaf Hash Configuration Type pub type LeafHash = merkle_tree::IdentityLeafHash<Utxo>; -/// +/// Leaf Hash Variable Configuration Type pub type LeafHashVar = merkle_tree::IdentityLeafHash<UtxoVar, Compiler>; -/// +/// Inner Hash Configuration pub struct InnerHash; impl merkle_tree::InnerHash for InnerHash { @@ -288,9 +272,9 @@ impl merkle_tree::InnerHash for InnerHash { parameters: &Self::Parameters, lhs: &Self::Output, rhs: &Self::Output, - compiler: &mut (), + _: &mut (), ) -> Self::Output { - parameters.hash(&[*lhs, *rhs], compiler) + parameters.hash([lhs, rhs]) } #[inline] @@ -298,17 +282,16 @@ impl merkle_tree::InnerHash for InnerHash { parameters: &Self::Parameters, lhs: &Self::LeafDigest, rhs: &Self::LeafDigest, - compiler: &mut (), + _: &mut (), ) -> Self::Output { - parameters.hash(&[*lhs, *rhs], compiler) + parameters.hash([lhs, rhs]) } } -/* -/// +/// Inner Hash Variable Configuration pub struct InnerHashVar; -impl merkle_tree::InnerHash<Compiler> for InnerHash { +impl merkle_tree::InnerHash<Compiler> for InnerHashVar { type LeafDigest = UtxoVar; type Parameters = poseidon::Hash<PoseidonSpec<2>, 2, Compiler>; type Output = poseidon::Output<PoseidonSpec<2>, 2, Compiler>; @@ -318,9 +301,9 @@ impl merkle_tree::InnerHash<Compiler> for InnerHash { parameters: &Self::Parameters, lhs: &Self::Output, rhs: &Self::Output, - compiler: &mut COM, + compiler: &mut Compiler, ) -> Self::Output { - parameters.hash(&[*lhs, *rhs], compiler) + parameters.hash_in([lhs, rhs], compiler) } #[inline] @@ -328,66 +311,35 @@ impl merkle_tree::InnerHash<Compiler> for InnerHash { parameters: &Self::Parameters, lhs: &Self::LeafDigest, rhs: &Self::LeafDigest, - compiler: &mut COM, + compiler: &mut Compiler, ) -> Self::Output { - parameters.hash(&[*lhs, *rhs], compiler) + parameters.hash_in([lhs, rhs], compiler) } } -*/ - -/// Configuration Structure -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Config; -/* -impl transfer::Configuration for Config { - type SecretKey = <Self::KeyAgreementScheme as key::KeyAgreementScheme>::SecretKey; - type PublicKey = <Self::KeyAgreementScheme as key::KeyAgreementScheme>::PublicKey; - type KeyAgreementScheme = KeyAgreementScheme; - type SecretKeyVar = - <Self::KeyAgreementSchemeVar as key::KeyAgreementScheme<Self::Compiler>>::SecretKey; - type PublicKeyVar = - <Self::KeyAgreementSchemeVar as key::KeyAgreementScheme<Self::Compiler>>::PublicKey; - type KeyAgreementSchemeVar = KeyAgreementSchemeVar; +/// Merkle Tree Configuration +pub struct MerkleTreeConfiguration; - type Utxo = <Self::UtxoCommitmentScheme as CommitmentScheme>::Output; - type UtxoCommitmentScheme = UtxoCommitmentScheme; - type UtxoVar = <Self::UtxoCommitmentSchemeVar as CommitmentScheme<Self::Compiler>>::Output; - type UtxoCommitmentSchemeVar = UtxoCommitmentSchemeVar; +impl merkle_tree::HashConfiguration for MerkleTreeConfiguration { + type LeafHash = LeafHash; + type InnerHash = InnerHash; +} - type VoidNumber = <Self::VoidNumberHashFunction as BinaryHashFunction>::Output; - type VoidNumberHashFunction = VoidNumberHashFunction; - type VoidNumberVar = - <Self::VoidNumberHashFunctionVar as BinaryHashFunction<Self::Compiler>>::Output; - type VoidNumberHashFunctionVar = VoidNumberHashFunctionVar; +impl merkle_tree::Configuration for MerkleTreeConfiguration { + const HEIGHT: usize = 20; +} - /* TODO: - type UtxoSetModel = merkle_tree::Parameters<()>; - type UtxoSetWitnessVar = <Self::UtxoSetModelVar as accumulator::Model<Self::Compiler>>::Witness; - type UtxoSetOutputVar = <Self::UtxoSetModelVar as accumulator::Model<Self::Compiler>>::Output; - type UtxoSetModelVar = (); - */ +/// Merkle Tree Variable Configuration +pub struct MerkleTreeConfigurationVar; - type AssetIdVar = AssetIdVar; - type AssetValueVar = AssetValueVar; - - type Compiler = Compiler; - type ProofSystem = ProofSystem; +impl merkle_tree::HashConfiguration<Compiler> for MerkleTreeConfigurationVar { + type LeafHash = LeafHashVar; + type InnerHash = InnerHashVar; +} - type NoteEncryptionScheme = encryption::Hybrid< - Self::KeyAgreementScheme, - encryption::ConstantSizeSymmetricKeyEncryption< - { Asset::SIZE }, - AesGcm<{ Asset::SIZE }>, - Asset, - >, - key::kdf::FromByteVector< - <Self::KeyAgreementScheme as key::KeyAgreementScheme>::SharedSecret, - Blake2sKdf, - >, - >; +impl merkle_tree::Configuration<Compiler> for MerkleTreeConfigurationVar { + const HEIGHT: usize = <MerkleTreeConfiguration as merkle_tree::Configuration>::HEIGHT; } -*/ impl constraint::Input<AssetId> for ProofSystem { #[inline] @@ -403,130 +355,83 @@ impl constraint::Input<AssetValue> for ProofSystem { } } -impl constraint::Input<Group> for ProofSystem { +impl constraint::Input<ConstraintField> for ProofSystem { #[inline] - fn extend(input: &mut Self::Input, next: &Group) { - // TODO: next.extend_input(input); - todo!() + fn extend(input: &mut Self::Input, next: &ConstraintField) { + input.push(*next); } } -/* TODO: -impl constraint::Input<Root> for ProofSystem -{ +impl constraint::Input<Group> for ProofSystem { #[inline] - fn extend(input: &mut Self::Input, next: &Root) { - root_extend_input(next, input); + fn extend(input: &mut Self::Input, next: &Group) { + // FIXME: Make sure we can type check the coordinate system here. + let affine = next.0.into_affine(); + input.push(affine.x); + input.push(affine.y); } } -*/ - -/* TODO: -/// Pedersen Window Parameters -#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct PedersenCommitmentWindowParameters; - -impl PedersenWindow for PedersenCommitmentWindowParameters { - const WINDOW_SIZE: usize = 4; - const NUM_WINDOWS: usize = 256; -} - -/// Pedersen Commitment Projective Curve -pub type PedersenCommitmentProjectiveCurve = EdwardsProjective; - -/// Pedersen Commitment Projective Curve -pub type PedersenCommitmentProjectiveCurveVar = EdwardsVar; -/// Pedersen Commitment Scheme -pub type PedersenCommitment = pedersen::constraint::PedersenCommitmentWrapper< - PedersenCommitmentWindowParameters, - PedersenCommitmentProjectiveCurve, - PedersenCommitmentProjectiveCurveVar, +/// Note Encryption Scheme +pub type NoteEncryptionScheme = encryption::Hybrid< + KeyAgreementScheme, + encryption::ConstantSizeSymmetricKeyEncryption<{ Asset::SIZE }, AesGcm<{ Asset::SIZE }>, Asset>, + key::kdf::FromByteVector< + <KeyAgreementScheme as key::KeyAgreementScheme>::SharedSecret, + Blake2sKdf, + >, >; -/// Pedersen Commitment Scheme Variable -pub type PedersenCommitmentVar = pedersen::constraint::PedersenCommitmentVar< - PedersenCommitmentWindowParameters, - PedersenCommitmentProjectiveCurve, - PedersenCommitmentProjectiveCurveVar, ->; - -/// Arkworks Pedersen Commitment Scheme -type ArkPedersenCommitment = - CRH<PedersenCommitmentProjectiveCurve, PedersenCommitmentWindowParameters>; - -/// Constraint Field -pub type ConstraintField = Fq; - -/// Constraint System -pub type ConstraintSystem = ArkConstraintSystem<ConstraintField>; +/// Transfer Configuration +pub struct TransferConfiguration; -/// Proof System -pub type ProofSystem = Groth16<Bls12_381>; +/* +impl transfer::Configuration for TransferConfiguration { + type SecretKey = <Self::KeyAgreementScheme as key::KeyAgreementScheme>::SecretKey; + type PublicKey = <Self::KeyAgreementScheme as key::KeyAgreementScheme>::PublicKey; + type KeyAgreementScheme = KeyAgreementScheme; + type SecretKeyVar = + <Self::KeyAgreementSchemeVar as key::KeyAgreementScheme<Self::Compiler>>::SecretKey; + type PublicKeyVar = + <Self::KeyAgreementSchemeVar as key::KeyAgreementScheme<Self::Compiler>>::PublicKey; + type KeyAgreementSchemeVar = KeyAgreementSchemeVar; -impl ArkMerkleTreeConfiguration for Configuration { - type Leaf = Utxo; - type LeafHash = ArkPedersenCommitment; - type InnerHash = ArkPedersenCommitment; - type Height = u8; + type Utxo = <Self::UtxoCommitmentScheme as CommitmentScheme>::Output; + type UtxoCommitmentScheme = UtxoCommitmentScheme; + type UtxoVar = <Self::UtxoCommitmentSchemeVar as CommitmentScheme<Self::Compiler>>::Output; + type UtxoCommitmentSchemeVar = UtxoCommitmentSchemeVar; - const HEIGHT: Self::Height = 20; -} + type VoidNumber = <Self::VoidNumberHashFunction as BinaryHashFunction>::Output; + type VoidNumberHashFunction = VoidNumberHashFunction; + type VoidNumberVar = + <Self::VoidNumberHashFunctionVar as BinaryHashFunction<Self::Compiler>>::Output; + type VoidNumberHashFunctionVar = VoidNumberHashFunctionVar; -impl merkle_tree::HashConfiguration for Configuration { - type LeafHash = - <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::HashConfiguration>::LeafHash; - type InnerHash = - <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::HashConfiguration>::InnerHash; -} + type UtxoSetModel = merkle_tree::Parameters<MerkleTreeConfiguration>; + type UtxoSetWitnessVar = <Self::UtxoSetModelVar as accumulator::Model<Self::Compiler>>::Witness; + type UtxoSetOutputVar = <Self::UtxoSetModelVar as accumulator::Model<Self::Compiler>>::Output; + type UtxoSetModelVar = merkle_tree::Parameters<MerkleTreeConfigurationVar, Compiler>; -impl merkle_tree::Configuration for Configuration { - type Height = - <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::Configuration>::Height; + type AssetIdVar = AssetIdVar; + type AssetValueVar = AssetValueVar; - const HEIGHT: Self::Height = - <ArkMerkleTreeConfigConverter<Configuration> as merkle_tree::Configuration>::HEIGHT; -} + type Compiler = Compiler; + type ProofSystem = ProofSystem; -impl merkle_tree_constraint::Configuration for Configuration { - type ConstraintField = ConstraintField; - type LeafHashVar = CRHGadget< - PedersenCommitmentProjectiveCurve, - PedersenCommitmentProjectiveCurveVar, - PedersenCommitmentWindowParameters, - >; - type InnerHashVar = CRHGadget< - PedersenCommitmentProjectiveCurve, - PedersenCommitmentProjectiveCurveVar, - PedersenCommitmentWindowParameters, - >; + type NoteEncryptionScheme = NoteEncryptionScheme; } +*/ -impl identity::Configuration for Configuration { - type Asset = Asset; - type KeyAgreementScheme = EllipticCurveDiffieHellman<PedersenCommitmentProjectiveCurve>; - type CommitmentScheme = PedersenCommitment; -} +/* TODO: +/// Mint Transfer Type +pub struct Mint = transfer::canonical::Mint<TransferConfiguration>; -/* -/// Transfer Constraint Configuration Structure -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct TransferConstraintConfiguration; - -impl identity::Configuration for TransferConstraintConfiguration { - type Asset = AssetVar; - type KeyAgreementScheme = (); - type CommitmentScheme = (); -} +/// Private Transfer Type +pub struct PrivateTransfer = transfer::canonical::PrivateTransfer<TransferConfiguration>; -impl transfer::ConstraintConfiguration<ConstraintSystem> for TransferConstraintConfiguration {} +/// Reclaim Transfer Type +pub struct Reclaim = transfer::canonical::Reclaim<TransferConfiguration>; -impl transfer::Configuration for Configuration { - type EncryptionScheme = (); - type UtxoSetVerifier = (); - type ConstraintSystem = ConstraintSystem; - type ConstraintConfiguration = TransferConstraintConfiguration; - type ProofSystem = ProofSystem; -} -*/ +/// Transfer Post Type +pub struct TransferPost = transfer::TransferPost<TransferConfiguration>; */ diff --git a/manta-pay/src/crypto/commitment/mod.rs b/manta-pay/src/crypto/commitment/mod.rs deleted file mode 100644 index 3e8cff1b6..000000000 --- a/manta-pay/src/crypto/commitment/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Commitment Scheme Implementations - -pub mod pedersen; diff --git a/manta-pay/src/crypto/commitment/pedersen.rs b/manta-pay/src/crypto/commitment/pedersen.rs deleted file mode 100644 index da46c2121..000000000 --- a/manta-pay/src/crypto/commitment/pedersen.rs +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Pedersen Commitments - -// TODO: Describe contract for `Specification`. - -use core::{fmt::Debug, hash::Hash}; -use manta_crypto::commitment::CommitmentScheme; - -/// Pedersen Commitment Specification -pub trait Specification<COM = ()> { - /// Group Type - type Group; - - /// Scalar Field Type - type Scalar; - - /// Adds two points of the group together. - fn add(lhs: Self::Group, rhs: Self::Group, compiler: &mut COM) -> Self::Group; - - /// Multiplies the given `point` with a `scalar` value. - fn scalar_mul(point: &Self::Group, scalar: &Self::Scalar, compiler: &mut COM) -> Self::Group; - - /// Computes the Pedersen Commitment with `parameters` over `trapdoor` and `input` in the given - /// `compiler`. - #[inline] - fn commit<const ARITY: usize>( - parameters: &Commitment<Self, COM, ARITY>, - trapdoor: &Self::Scalar, - input: &[Self::Scalar; ARITY], - compiler: &mut COM, - ) -> Self::Group { - parameters.input_generators.iter().zip(input).fold( - Self::scalar_mul(&parameters.trapdoor_generator, trapdoor, compiler), - move |acc, (g, i)| { - let point = Self::scalar_mul(g, i, compiler); - Self::add(acc, point, compiler) - }, - ) - } -} - -/// Commitment Scheme -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "S::Group: Clone"), - Copy(bound = "S::Group: Copy"), - Debug(bound = "S::Group: Debug"), - Eq(bound = "S::Group: Eq"), - Hash(bound = "S::Group: Hash"), - PartialEq(bound = "S::Group: PartialEq") -)] -pub struct Commitment<S, COM = (), const ARITY: usize = 1> -where - S: Specification<COM> + ?Sized, -{ - /// Trapdoor Generator - pub trapdoor_generator: S::Group, - - /// Input Generators - pub input_generators: [S::Group; ARITY], -} - -impl<S, COM, const ARITY: usize> CommitmentScheme<COM> for Commitment<S, COM, ARITY> -where - S: Specification<COM>, -{ - type Trapdoor = S::Scalar; - - type Input = [S::Scalar; ARITY]; - - type Output = S::Group; - - #[inline] - fn commit( - &self, - trapdoor: &Self::Trapdoor, - input: &Self::Input, - compiler: &mut COM, - ) -> Self::Output { - S::commit(self, trapdoor, input, compiler) - } -} - -/// Pedersen Commitment Trapdoor Type -pub type Trapdoor<S, COM, const ARITY: usize> = - <Commitment<S, COM, ARITY> as CommitmentScheme<COM>>::Trapdoor; - -/// Pedersen Commitment Input Type -pub type Input<S, COM, const ARITY: usize> = - <Commitment<S, COM, ARITY> as CommitmentScheme<COM>>::Input; - -/// Pedersen Commitment Output Type -pub type Output<S, COM, const ARITY: usize> = - <Commitment<S, COM, ARITY> as CommitmentScheme<COM>>::Output; - -/// Arkworks Backend -#[cfg(feature = "arkworks")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] -pub mod arkworks { - use crate::crypto::constraint::arkworks::{FpVar, R1CS}; - use ark_ec::ProjectiveCurve; - use ark_ff::{Field, PrimeField}; - use ark_r1cs_std::{groups::CurveVar, ToBitsGadget}; - use ark_relations::ns; - use core::marker::PhantomData; - use manta_crypto::constraint::{Allocation, Constant, Variable}; - use manta_util::fallible_array_map; - - /// Constraint Field Type - type ConstraintField<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField; - - /// Compiler Type - type Compiler<C> = R1CS<ConstraintField<C>>; - - /// Pedersen Commitment Specification - #[derive(derivative::Derivative)] - #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct Specification<C, CV>(PhantomData<(C, CV)>) - where - C: ProjectiveCurve, - CV: CurveVar<C, ConstraintField<C>>; - - impl<C, CV> super::Specification for Specification<C, CV> - where - C: ProjectiveCurve, - CV: CurveVar<C, ConstraintField<C>>, - { - type Group = C; - - type Scalar = C::ScalarField; - - #[inline] - fn add(lhs: Self::Group, rhs: Self::Group, _: &mut ()) -> Self::Group { - lhs + rhs - } - - #[inline] - fn scalar_mul(point: &Self::Group, scalar: &Self::Scalar, _: &mut ()) -> Self::Group { - point.mul(scalar.into_repr()) - } - } - - impl<C, CV> super::Specification<Compiler<C>> for Specification<C, CV> - where - C: ProjectiveCurve, - CV: CurveVar<C, ConstraintField<C>>, - { - type Group = CV; - - type Scalar = FpVar<ConstraintField<C>>; - - #[inline] - fn add(lhs: Self::Group, rhs: Self::Group, compiler: &mut Compiler<C>) -> Self::Group { - let _ = compiler; - lhs + rhs - } - - #[inline] - fn scalar_mul( - point: &Self::Group, - scalar: &Self::Scalar, - compiler: &mut Compiler<C>, - ) -> Self::Group { - let _ = compiler; - point - .scalar_mul_le( - scalar - .to_bits_le() - .expect("Bit decomposition is not allowed to fail.") - .iter(), - ) - .expect("Scalar multiplication is not allowed to fail.") - } - } - - impl<C, CV, const ARITY: usize> Variable<Compiler<C>> - for super::Commitment<Specification<C, CV>, Compiler<C>, ARITY> - where - C: ProjectiveCurve, - CV: CurveVar<C, ConstraintField<C>>, - { - type Type = super::Commitment<Specification<C, CV>, (), ARITY>; - - type Mode = Constant; - - #[inline] - fn new(cs: &mut Compiler<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - match allocation { - Allocation::Known(this, _) => Self { - trapdoor_generator: CV::new_constant( - ns!(cs.cs, "group element"), - this.trapdoor_generator, - ) - .expect("Variable allocation is not allowed to fail."), - input_generators: fallible_array_map(this.input_generators, |g| { - CV::new_constant(ns!(cs.cs, "group element"), g) - }) - .expect("Variable allocation is not allowed to fail."), - }, - _ => unreachable!(), - } - } - } -} diff --git a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs index aefbf1a79..0189e308e 100644 --- a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs @@ -17,11 +17,13 @@ //! Arkworks Constraint System Implementation use ark_ff::{fields::Field, PrimeField}; -use ark_r1cs_std::{alloc::AllocVar, bits::boolean::Boolean, eq::EqGadget}; +use ark_r1cs_std::{ + alloc::AllocVar, bits::boolean::Boolean, eq::EqGadget, select::CondSelectGadget, +}; use ark_relations::{ns, r1cs as ark_r1cs}; use manta_crypto::constraint::{ - measure::Measure, Add, Allocation, AllocationMode, ConstraintSystem, Equal, Public, - PublicOrSecret, Secret, Variable, + measure::Measure, Add, Allocation, AllocationMode, ConditionalSelect, ConstraintSystem, Equal, + Public, PublicOrSecret, Secret, Variable, }; pub use ark_r1cs::SynthesisError; @@ -243,6 +245,18 @@ where } } +impl<F> ConditionalSelect<R1CS<F>> for FpVar<F> +where + F: PrimeField, +{ + #[inline] + fn select(bit: &Boolean<F>, lhs: &Self, rhs: &Self, compiler: &mut R1CS<F>) -> Self { + let _ = compiler; + Self::conditionally_select(bit, lhs, rhs) + .expect("Conditionally selecting from two values is not allowed to fail.") + } +} + impl<F> Add<R1CS<F>> for FpVar<F> where F: PrimeField, diff --git a/manta-pay/src/crypto/hash/poseidon.rs b/manta-pay/src/crypto/hash/poseidon.rs index 64124fed6..ca6c3d20b 100644 --- a/manta-pay/src/crypto/hash/poseidon.rs +++ b/manta-pay/src/crypto/hash/poseidon.rs @@ -17,10 +17,11 @@ //! Poseidon Hash Function // TODO: Describe the contract for `Specification`. +// TODO: Add more methods to the `Specification` trait for optimization. use alloc::vec::Vec; use core::{iter, mem}; -use manta_crypto::hash::{BinaryHashFunction, HashFunction}; +use manta_crypto::hash::HashFunction; /// Poseidon Permutation Specification pub trait Specification<COM = ()> { @@ -136,7 +137,7 @@ where /// Computes the first round of the Poseidon permutation from `trapdoor` and `input`. #[inline] - fn first_round(&self, input: &[S::Field; ARITY], compiler: &mut COM) -> State<S, COM> { + fn first_round(&self, input: [&S::Field; ARITY], compiler: &mut COM) -> State<S, COM> { let mut state = Vec::with_capacity(ARITY + 1); for (i, point) in iter::once(&S::zero(compiler)).chain(input).enumerate() { let mut elem = S::add(point, &self.additive_round_keys[i], compiler); @@ -170,16 +171,16 @@ where } } -impl<S, const ARITY: usize, COM> HashFunction<COM> for Hash<S, ARITY, COM> +impl<S, const ARITY: usize, COM> HashFunction<ARITY, COM> for Hash<S, ARITY, COM> where S: Specification<COM>, { - type Input = [S::Field; ARITY]; + type Input = S::Field; type Output = S::Field; #[inline] - fn hash(&self, input: &Self::Input, compiler: &mut COM) -> Self::Output { + fn hash_in(&self, input: [&Self::Input; ARITY], compiler: &mut COM) -> Self::Output { let mut state = self.first_round(input, compiler); for round in 1..S::FULL_ROUNDS { self.full_round(round, &mut state, compiler); @@ -197,11 +198,12 @@ where } /// Poseidon Hash Input Type -pub type Input<S, const ARITY: usize, COM = ()> = <Hash<S, ARITY, COM> as HashFunction<COM>>::Input; +pub type Input<S, const ARITY: usize, COM = ()> = + <Hash<S, ARITY, COM> as HashFunction<ARITY, COM>>::Input; /// Poseidon Commitment Output Type pub type Output<S, const ARITY: usize, COM = ()> = - <Hash<S, ARITY, COM> as HashFunction<COM>>::Output; + <Hash<S, ARITY, COM> as HashFunction<ARITY, COM>>::Output; /// Arkworks Backend #[cfg(feature = "arkworks")] diff --git a/manta-pay/src/crypto/merkle_tree.rs b/manta-pay/src/crypto/merkle_tree.rs deleted file mode 100644 index 055d3cc5e..000000000 --- a/manta-pay/src/crypto/merkle_tree.rs +++ /dev/null @@ -1,539 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Arkworks Merkle Tree Wrappers - -// TODO: Move as much constraint code to `manta_crypto` as possible. - -use alloc::{vec, vec::Vec}; -use ark_crypto_primitives::{ - crh::{TwoToOneCRH, CRH}, - merkle_tree::Config, -}; -use ark_ff::{to_bytes, ToBytes}; -use core::marker::PhantomData; -use manta_crypto::{ - merkle_tree::{self, InnerHash, LeafHash}, - rand::{CryptoRng, RngCore, SizedRng}, -}; -use manta_util::{as_bytes, Concat}; - -/* -#[cfg(feature = "test")] -use manta_crypto::rand::Standard; -*/ - -/* -/// Arkworks Leaf Hash Converter -#[derive(derivative::Derivative)] -#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct LeafHashConverter<L, LH>(PhantomData<L>, PhantomData<LH>) -where - L: Concat<Item = u8> + ?Sized, - LH: CRH; - -impl<L, LH> LeafHashConverter<L, LH> -where - L: Concat<Item = u8> + ?Sized, - LH: CRH, -{ - /// Sample leaf hash parameters using `rng`. - #[inline] - pub fn sample_parameters<R>(rng: &mut R) -> LH::Parameters - where - R: CryptoRng + RngCore + ?Sized, - { - LH::setup(&mut SizedRng(rng)) - .expect("Leaf hash parameter generation is not allowed to fail.") - } -} - -impl<L, LH> LeafHash for LeafHashConverter<L, LH> -where - L: Concat<Item = u8> + ?Sized, - LH: CRH, -{ - type Leaf = L; - - type Parameters = LH::Parameters; - - type Output = LH::Output; - - #[inline] - fn digest(parameters: &Self::Parameters, leaf: &Self::Leaf) -> Self::Output { - LH::evaluate(parameters, &as_bytes!(leaf)) - .expect("Leaf digest computation is not allowed to fail.") - } -} - -/// Arkworks Inner Hash Converter -#[derive(derivative::Derivative)] -#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct InnerHashConverter<L, LH, IH>(PhantomData<L>, PhantomData<(LH, IH)>) -where - L: Concat<Item = u8> + ?Sized, - LH: CRH, - IH: TwoToOneCRH; - -impl<L, LH, IH> InnerHashConverter<L, LH, IH> -where - L: Concat<Item = u8> + ?Sized, - LH: CRH, - IH: TwoToOneCRH, -{ - /// Sample inner hash parameters using `rng`. - #[inline] - pub fn sample_parameters<R>(rng: &mut R) -> IH::Parameters - where - R: CryptoRng + RngCore + ?Sized, - { - IH::setup(&mut SizedRng(rng)) - .expect("Inner hash parameter generation is not allowed to fail.") - } - - /// Evaluates the inner hash function for `IH` using `parameters`. - #[inline] - fn evaluate<T>(parameters: &IH::Parameters, lhs: &T, rhs: &T) -> IH::Output - where - T: ToBytes, - { - IH::evaluate( - parameters, - &to_bytes!(lhs).expect("Conversion to bytes is not allowed to fail."), - &to_bytes!(rhs).expect("Conversion to bytes is not allowed to fail."), - ) - .expect("Inner digest computation is not allowed to fail.") - } -} - -impl<L, LH, IH> InnerHash for InnerHashConverter<L, LH, IH> -where - L: Concat<Item = u8> + ?Sized, - LH: CRH, - IH: TwoToOneCRH, -{ - type LeafHash = LeafHashConverter<L, LH>; - - type Parameters = IH::Parameters; - - type Output = IH::Output; - - #[inline] - fn join(parameters: &Self::Parameters, lhs: &Self::Output, rhs: &Self::Output) -> Self::Output { - Self::evaluate(parameters, lhs, rhs) - } - - #[inline] - fn join_leaves( - parameters: &Self::Parameters, - lhs: &<Self::LeafHash as LeafHash>::Output, - rhs: &<Self::LeafHash as LeafHash>::Output, - ) -> Self::Output { - Self::evaluate(parameters, lhs, rhs) - } -} - -/// Arkworks Merkle Tree Configuration -pub trait Configuration { - /// Leaf Type - type Leaf: Concat<Item = u8> + ?Sized; - - /// Leaf Hash Type - type LeafHash: CRH; - - /// Inner Hash Type - type InnerHash: TwoToOneCRH; - - /// Merkle Tree Height Type - type Height: Copy + Into<usize>; - - /// Merkle Tree Height - const HEIGHT: Self::Height; -} - -/// Configuration Converter -/// -/// Given any `L` and [`C: Configuration`](Configuration), this struct can be used as -/// `ConfigConverter<L, C>` instead of `C` in places where we need an implementation of the -/// `arkworks` [`Config`] trait or the `manta_crypto` [`Configuration`](merkle_tree::Configuration) -/// trait. -/// -/// This `struct` is meant only to be used in place of the type `C`, so any values of this `struct` -/// have no meaning. -#[derive(derivative::Derivative)] -#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct ConfigConverter<C>(PhantomData<C>) -where - C: Configuration; - -impl<C> Configuration for ConfigConverter<C> -where - C: Configuration, -{ - type Leaf = C::Leaf; - type LeafHash = C::LeafHash; - type InnerHash = C::InnerHash; - - const HEIGHT: usize = C::HEIGHT; -} - -impl<C> merkle_tree::HashConfiguration for ConfigConverter<C> -where - C: Configuration, -{ - type LeafHash = LeafHashConverter<C::Leaf, C::LeafHash>; - type InnerHash = InnerHashConverter<C::Leaf, C::LeafHash, C::InnerHash>; -} - -impl<C> merkle_tree::Configuration for ConfigConverter<C> -where - C: Configuration, -{ - const HEIGHT: usize = C::HEIGHT; -} - -impl<C> Config for ConfigConverter<C> -where - C: Configuration, -{ - type LeafHash = C::LeafHash; - type TwoToOneHash = C::InnerHash; -} - -#[cfg(feature = "test")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] -impl<C> merkle_tree::test::HashParameterSampling for ConfigConverter<C> -where - C: Configuration, -{ - type LeafHashParameterDistribution = Standard; - - type InnerHashParameterDistribution = Standard; - - #[inline] - fn sample_leaf_hash_parameters<R>( - distribution: Self::LeafHashParameterDistribution, - rng: &mut R, - ) -> merkle_tree::LeafHashParameters<Self> - where - R: CryptoRng + RngCore + ?Sized, - { - let _ = distribution; - <ConfigConverter<C> as merkle_tree::HashConfiguration>::LeafHash::sample_parameters(rng) - } - - #[inline] - fn sample_inner_hash_parameters<R>( - distribution: Self::InnerHashParameterDistribution, - rng: &mut R, - ) -> merkle_tree::InnerHashParameters<Self> - where - R: CryptoRng + RngCore + ?Sized, - { - let _ = distribution; - <ConfigConverter<C> as merkle_tree::HashConfiguration>::InnerHash::sample_parameters(rng) - } -} -*/ - -/* TODO: -/// Merkle Tree Constraint System Variables -pub mod constraint { - use super::*; - use crate::crypto::constraint::arkworks::{empty, full, R1CS}; - use ark_crypto_primitives::{ - crh::constraints::{CRHGadget, TwoToOneCRHGadget}, - merkle_tree::{constraints::PathVar as ArkPathVar, Path as ArkPath}, - }; - use ark_ff::{Field, ToConstraintField}; - use ark_r1cs_std::{alloc::AllocVar, boolean::Boolean, eq::EqGadget, uint8::UInt8}; - use ark_relations::ns; - use manta_crypto::{ - accumulator::Model, - constraint::{Allocation, Constant, Public, Secret, Variable}, - merkle_tree::{Parameters, Path, Root}, - }; - - /// Merkle Tree Constraint System Configuration - pub trait Configuration: super::Configuration { - /// Constraint Field Type - type ConstraintField: Field; - - /// Leaf Hash Variable Type - type LeafHashVar: CRHGadget<Self::LeafHash, Self::ConstraintField>; - - /// Inner Hash Variable Type - type InnerHashVar: TwoToOneCRHGadget<Self::InnerHash, Self::ConstraintField>; - } - - /// Constraint Field Type - pub type ConstraintField<C> = <C as Configuration>::ConstraintField; - - /// Constraint System Type - pub type ContraintSystem<C> = R1CS<ConstraintField<C>>; - - /// Leaf Hash Type - pub type LeafHashVar<C> = <C as Configuration>::LeafHashVar; - - /// Inner Hash Type - pub type InnerHashVar<C> = <C as Configuration>::InnerHashVar; - - /// Leaf Hash Parameters Type - pub type LeafHashParametersVar<C> = <LeafHashVar<C> as CRHGadget< - <C as super::Configuration>::LeafHash, - ConstraintField<C>, - >>::ParametersVar; - - /// Inner Hash Parameters Type - pub type InnerHashParametersVar<C> = <InnerHashVar<C> as TwoToOneCRHGadget< - <C as super::Configuration>::InnerHash, - ConstraintField<C>, - >>::ParametersVar; - - /// Merkle Tree Parameters Variable - #[derive(derivative::Derivative)] - #[derivative(Clone(bound = ""))] - pub struct ParametersVar<C> - where - C: Configuration, - { - /// Leaf Hash Parameters Variable - pub leaf: LeafHashParametersVar<C>, - - /// Inner Hash Parameters Variable - pub inner: InnerHashParametersVar<C>, - } - - impl<C> ParametersVar<C> - where - C: Configuration, - { - /// Builds a new [`ParametersVar`] from `leaf` and `inner` parameters. - #[inline] - pub fn new(leaf: LeafHashParametersVar<C>, inner: InnerHashParametersVar<C>) -> Self { - Self { leaf, inner } - } - - /// Verifies that `path` constitutes a proof that `leaf` is contained in the Merkle Tree - /// with the given `root`. - #[inline] - pub fn verify( - &self, - root: &RootVar<C>, - path: &PathVar<C>, - leaf: &[UInt8<ConstraintField<C>>], - ) -> Boolean<ConstraintField<C>> { - path.0 - .verify_membership(&self.leaf, &self.inner, &root.0, &leaf) - .expect("This is not allowed to fail.") - } - - /// Asserts that `path` constitutes a proof that `leaf` is contained in the Merkle Tree - /// with the given `root`. - #[inline] - pub fn assert_verified( - &self, - root: &RootVar<C>, - path: &PathVar<C>, - leaf: &[UInt8<ConstraintField<C>>], - ) { - self.verify(root, path, leaf) - .enforce_equal(&Boolean::TRUE) - .expect("This is not allowed to fail."); - } - } - - impl<C> Model<R1CS<ConstraintField<C>>> for ParametersVar<C> - where - C: Configuration, - { - type Item = [UInt8<ConstraintField<C>>]; - - type Witness = PathVar<C>; - - type Output = RootVar<C>; - - type Verification = Boolean<ConstraintField<C>>; - - #[inline] - fn verify( - &self, - item: &Self::Item, - witness: &Self::Witness, - output: &Self::Output, - ) -> Self::Verification { - self.verify(output, witness, item) - } - } - - impl<C> Variable<ContraintSystem<C>> for ParametersVar<C> - where - C: Configuration, - { - type Type = Parameters<ConfigConverter<C>>; - - type Mode = Constant; - - #[inline] - fn new( - cs: &mut ContraintSystem<C>, - allocation: Allocation<Self::Type, Self::Mode>, - ) -> Self { - let (this, _) = allocation.into_known(); - ParametersVar::new( - LeafHashParametersVar::<C>::new_constant( - ns!(cs.cs, "leaf hash parameter constant"), - &this.leaf, - ) - .expect("Variable allocation is not allowed to fail."), - InnerHashParametersVar::<C>::new_constant( - ns!(cs.cs, "two-to-one hash parameter constant"), - &this.inner, - ) - .expect("Variable allocation is not allowed to fail."), - ) - } - } - - /// Merkle Tree Root Inner Type - type RootInnerType<C> = <<C as super::Configuration>::InnerHash as TwoToOneCRH>::Output; - - /// Merkle Tree Root Variable Inner Type - type RootVarInnerType<C> = <InnerHashVar<C> as TwoToOneCRHGadget< - <C as super::Configuration>::InnerHash, - ConstraintField<C>, - >>::OutputVar; - - /// Merkle Tree Root Variable - #[derive(derivative::Derivative)] - #[derivative(Clone)] - pub struct RootVar<C>(RootVarInnerType<C>) - where - C: Configuration; - - impl<C> Variable<ContraintSystem<C>> for RootVar<C> - where - C: Configuration, - { - type Type = Root<ConfigConverter<C>>; - - type Mode = Public; - - #[inline] - fn new( - cs: &mut ContraintSystem<C>, - allocation: Allocation<Self::Type, Self::Mode>, - ) -> Self { - RootVar( - match allocation.known() { - Some((this, _)) => AllocVar::<RootInnerType<C>, _>::new_input( - ns!(cs.cs, "merkle tree root public input"), - full(&this.0), - ), - _ => AllocVar::<RootInnerType<C>, _>::new_input( - ns!(cs.cs, "merkle tree root public input"), - empty::<RootInnerType<C>>, - ), - } - .expect("Variable allocation is not allowed to fail."), - ) - } - } - - /// Extends the `input` vector by constraint field elements that make up `root`. - #[inline] - pub fn root_extend_input<C>( - root: &Root<ConfigConverter<C>>, - input: &mut Vec<ConstraintField<C>>, - ) where - C: Configuration, - RootInnerType<C>: ToConstraintField<ConstraintField<C>>, - { - input.append( - &mut root - .0 - .to_field_elements() - .expect("Conversion to constraint field elements is not allowed to fail."), - ); - } - - /// Merkle Tree Path Inner Type - type PathInnerType<C> = ArkPath<ConfigConverter<C>>; - - /// Merkle Tree Path Variable Inner Type - type PathVarInnerType<C> = - ArkPathVar<ConfigConverter<C>, LeafHashVar<C>, InnerHashVar<C>, ConstraintField<C>>; - - /// Merkle Tree Path Variable - pub struct PathVar<C>(PathVarInnerType<C>) - where - C: Configuration; - - impl<C> PathVar<C> - where - C: Configuration, - { - /// Converts a [`Path`] to a [`PathInnerType`]. - #[inline] - fn convert_path(path: &Path<ConfigConverter<C>>) -> PathInnerType<C> { - PathInnerType { - leaf_sibling_hash: path.sibling_digest.clone(), - auth_path: path.inner_path.path.iter().rev().cloned().collect(), - leaf_index: path.inner_path.leaf_index.0, - } - } - - /// Builds a default [`PathInnerType`] for use as an unknown variable value. - #[inline] - fn default_path() -> PathInnerType<C> { - PathInnerType { - leaf_sibling_hash: Default::default(), - auth_path: vec![Default::default(); C::HEIGHT.into() - 2], - leaf_index: Default::default(), - } - } - } - - impl<C> Variable<ContraintSystem<C>> for PathVar<C> - where - C: Configuration, - { - type Type = Path<ConfigConverter<C>>; - - type Mode = Secret; - - #[inline] - fn new( - cs: &mut ContraintSystem<C>, - allocation: Allocation<Self::Type, Self::Mode>, - ) -> Self { - PathVar( - match allocation.known() { - Some((this, _)) => PathVarInnerType::new_witness( - ns!(cs.cs, "path variable secret witness"), - full(&Self::convert_path(this)), - ), - _ => PathVarInnerType::new_witness( - ns!(cs.cs, "path variable secret witness"), - full(&Self::default_path()), - ), - } - .expect("Variable allocation is not allowed to fail."), - ) - } - } -} -*/ diff --git a/manta-pay/src/crypto/mod.rs b/manta-pay/src/crypto/mod.rs index 5b18ee453..35907a000 100644 --- a/manta-pay/src/crypto/mod.rs +++ b/manta-pay/src/crypto/mod.rs @@ -16,10 +16,8 @@ //! Manta Pay Cryptographic Primitives Implementations -// TODO[remove]: pub mod commitment; pub mod constraint; pub mod ecc; pub mod encryption; pub mod hash; pub mod key; -pub mod merkle_tree; diff --git a/manta-pay/src/accounting/ledger.rs b/manta-pay/src/ledger.rs similarity index 100% rename from manta-pay/src/accounting/ledger.rs rename to manta-pay/src/ledger.rs diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index 84f3dae57..ab8cc6d3d 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -23,10 +23,12 @@ extern crate alloc; +#[cfg(test)] +mod test; + pub mod crypto; pub mod key; - -// TODO[remove]: pub mod accounting; +// TODO: pub mod ledger; #[cfg(all(feature = "arkworks", feature = "groth16"))] #[cfg_attr(doc_cfg, doc(cfg(all(feature = "arkworks", feature = "groth16"))))] diff --git a/manta-pay/src/test.rs b/manta-pay/src/test.rs new file mode 100644 index 000000000..748c127cc --- /dev/null +++ b/manta-pay/src/test.rs @@ -0,0 +1,87 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Manta Pay Testing + +/* TODO: +use crate::accounting::{ + config, + identity::UtxoSet, + transfer::{Mint, PrivateTransfer, Reclaim}, +}; +use manta_crypto::{ + constraint::{measure::Measure, ProofSystem}, + rand::Rand, +}; +use rand::thread_rng; + +/// Tests the generation of proving/verifying contexts for [`Mint`]. +#[test] +fn sample_mint_context() { + let mut rng = thread_rng(); + let cs = Mint::sample_unknown_constraints(&mut rng); + println!("Mint: {:?}", cs.measure()); + config::ProofSystem::generate_context(cs, &mut rng).unwrap(); +} + +/// Tests the generation of proving/verifying contexts for [`PrivateTransfer`]. +#[test] +fn sample_private_transfer_context() { + let mut rng = thread_rng(); + let cs = PrivateTransfer::sample_unknown_constraints(&mut rng); + println!("PrivateTransfer: {:?}", cs.measure()); + config::ProofSystem::generate_context(cs, &mut rng).unwrap(); +} + +/// Tests the generation of proving/verifying contexts for [`Reclaim`]. +#[test] +fn sample_reclaim_context() { + let mut rng = thread_rng(); + let cs = Reclaim::sample_unknown_constraints(&mut rng); + println!("Reclaim: {:?}", cs.measure()); + config::ProofSystem::generate_context(cs, &mut rng).unwrap(); +} + +/// Tests the generation of a [`Mint`]. +#[test] +fn mint() { + let mut rng = thread_rng(); + assert!(matches!( + Mint::sample_and_check_proof(&rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng), + Ok(true) + )); +} + +/// Tests the generation of a [`PrivateTransfer`]. +#[test] +fn private_transfer() { + let mut rng = thread_rng(); + assert!(matches!( + PrivateTransfer::sample_and_check_proof(&rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng), + Ok(true) + )); +} + +/// Tests the generation of a [`Reclaim`]. +#[test] +fn reclaim() { + let mut rng = thread_rng(); + assert!(matches!( + Reclaim::sample_and_check_proof(&rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng), + Ok(true) + )); +} +*/ From 359f571bc505befe7082c64ef00cc2c154cd2362 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 6 Jan 2022 02:20:18 -0500 Subject: [PATCH 159/275] feat: move to more efficient constraint system abstraction --- manta-accounting/src/asset.rs | 25 +- manta-accounting/src/transfer/mod.rs | 317 ++--- manta-crypto/src/accumulator.rs | 99 +- manta-crypto/src/constraint.rs | 1054 +++-------------- manta-crypto/src/ecc.rs | 19 +- manta-crypto/src/lib.rs | 1 - manta-crypto/src/merkle_tree/path.rs | 100 +- manta-crypto/src/merkle_tree/tree.rs | 28 +- manta-crypto/src/util.rs | 99 -- manta-pay/src/config.rs | 64 +- .../constraint/arkworks/constraint_system.rs | 212 ++-- .../constraint/arkworks/proof_system.rs | 4 +- manta-pay/src/crypto/ecc.rs | 101 +- manta-pay/src/crypto/hash/poseidon.rs | 38 +- 14 files changed, 693 insertions(+), 1468 deletions(-) delete mode 100644 manta-crypto/src/util.rs diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 4888503a6..0c15ef271 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -34,7 +34,7 @@ use derive_more::{ Add, AddAssign, Display, Div, DivAssign, From, Mul, MulAssign, Product, Sub, SubAssign, Sum, }; use manta_crypto::{ - constraint::{Allocation, PublicOrSecret, Secret, Variable, VariableSource}, + constraint::{Allocator, Secret, ValueSource, Variable}, rand::{CryptoRng, Rand, RngCore, Sample, Standard}, }; use manta_util::{into_array_unchecked, Concat, ConcatAccumulator}; @@ -529,26 +529,21 @@ where } } -impl<C, I, V> Variable<C> for Asset<I, V> +impl<COM, I, V> Variable<Secret, COM> for Asset<I, V> where - C: ?Sized, - I: Variable<C, Type = AssetId, Mode = PublicOrSecret>, - V: Variable<C, Type = AssetValue, Mode = PublicOrSecret>, + I: Variable<Secret, COM, Type = AssetId>, + V: Variable<Secret, COM, Type = AssetValue>, { type Type = Asset; - type Mode = Secret; + #[inline] + fn new_known(this: &Self::Type, compiler: &mut COM) -> Self { + Self::new(this.id.as_known(compiler), this.value.as_known(compiler)) + } #[inline] - fn new(cs: &mut C, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - match allocation { - Allocation::Known(this, mode) => { - Self::new(this.id.as_known(cs, mode), this.value.as_known(cs, mode)) - } - Allocation::Unknown(mode) => { - Self::new(I::new_unknown(cs, mode), V::new_unknown(cs, mode)) - } - } + fn new_unknown(compiler: &mut COM) -> Self { + Self::new(compiler.allocate_unknown(), compiler.allocate_unknown()) } } diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index 03c947d69..eae17049b 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -23,8 +23,8 @@ use manta_crypto::{ accumulator::{Accumulator, MembershipProof, Model}, commitment::CommitmentScheme, constraint::{ - Add, Allocation, Constant, ConstraintSystem, Derived, Equal, Input as ProofSystemInput, - ProofSystem, Public, PublicOrSecret, Secret, Variable, VariableSource, + Add, Allocator, Constant, ConstraintSystem, Derived, Equal, Input as ProofSystemInput, + ProofSystem, Public, Secret, ValueSource, Variable, }, encryption::{DecryptedMessage, EncryptedMessage, HybridPublicKeyEncryptionScheme}, hash::BinaryHashFunction, @@ -63,10 +63,10 @@ pub trait Configuration { >; /// Secret Key Variable Type - type SecretKeyVar: Variable<Self::Compiler, Type = SecretKey<Self>, Mode = Secret>; + type SecretKeyVar: Variable<Secret, Self::Compiler, Type = SecretKey<Self>>; /// Public Key Variable Type - type PublicKeyVar: Variable<Self::Compiler, Type = PublicKey<Self>, Mode = Public> + type PublicKeyVar: Variable<Public, Self::Compiler, Type = PublicKey<Self>> + Equal<Self::Compiler>; /// Key Agreement Scheme Variable Type @@ -74,7 +74,7 @@ pub trait Configuration { Self::Compiler, SecretKey = Self::SecretKeyVar, PublicKey = Self::PublicKeyVar, - > + Variable<Self::Compiler, Type = Self::KeyAgreementScheme, Mode = Constant>; + > + Constant<Self::Compiler, Type = Self::KeyAgreementScheme>; /// Unspent Transaction Output Type type Utxo: PartialEq; @@ -87,7 +87,8 @@ pub trait Configuration { >; /// UTXO Variable Type - type UtxoVar: Variable<Self::Compiler, Type = Self::Utxo, Mode = PublicOrSecret> + type UtxoVar: Variable<Public, Self::Compiler, Type = Self::Utxo> + + Variable<Secret, Self::Compiler, Type = Self::Utxo> + Equal<Self::Compiler>; /// UTXO Commitment Scheme Variable Type @@ -96,7 +97,7 @@ pub trait Configuration { Trapdoor = TrapdoorVar<Self>, Input = AssetVar<Self>, Output = Self::UtxoVar, - > + Variable<Self::Compiler, Type = Self::UtxoCommitmentScheme, Mode = Constant>; + > + Constant<Self::Compiler, Type = Self::UtxoCommitmentScheme>; /// Void Number Type type VoidNumber: PartialEq; @@ -109,7 +110,7 @@ pub trait Configuration { >; /// Void Number Variable Type - type VoidNumberVar: Variable<Self::Compiler, Type = Self::VoidNumber, Mode = Public> + type VoidNumberVar: Variable<Public, Self::Compiler, Type = Self::VoidNumber> + Equal<Self::Compiler>; /// Void Number Hash Function Variable Type @@ -118,16 +119,16 @@ pub trait Configuration { Left = Self::UtxoVar, Right = Self::SecretKeyVar, Output = Self::VoidNumberVar, - > + Variable<Self::Compiler, Type = Self::VoidNumberHashFunction, Mode = Constant>; + > + Constant<Self::Compiler, Type = Self::VoidNumberHashFunction>; /// UTXO Set Model Type type UtxoSetModel: Model<Item = Self::Utxo, Verification = bool>; /// UTXO Set Witness Variable Type - type UtxoSetWitnessVar: Variable<Self::Compiler, Type = UtxoSetWitness<Self>, Mode = Secret>; + type UtxoSetWitnessVar: Variable<Secret, Self::Compiler, Type = UtxoSetWitness<Self>>; /// UTXO Set Output Variable Type - type UtxoSetOutputVar: Variable<Self::Compiler, Type = UtxoSetOutput<Self>, Mode = Public>; + type UtxoSetOutputVar: Variable<Public, Self::Compiler, Type = UtxoSetOutput<Self>>; /// UTXO Set Model Variable Type type UtxoSetModelVar: Model< @@ -136,16 +137,18 @@ pub trait Configuration { Witness = Self::UtxoSetWitnessVar, Output = Self::UtxoSetOutputVar, Verification = <Self::Compiler as ConstraintSystem>::Bool, - > + Variable<Self::Compiler, Type = Self::UtxoSetModel, Mode = Constant>; + > + Constant<Self::Compiler, Type = Self::UtxoSetModel>; /// Asset Id Variable Type - type AssetIdVar: Variable<Self::Compiler, Type = AssetId, Mode = PublicOrSecret> + type AssetIdVar: Variable<Public, Self::Compiler, Type = AssetId> + + Variable<Secret, Self::Compiler, Type = AssetId> + Equal<Self::Compiler>; /// Asset Value Variable Type - type AssetValueVar: Variable<Self::Compiler, Type = AssetValue, Mode = PublicOrSecret> - + Equal<Self::Compiler> - + Add<Self::Compiler>; + type AssetValueVar: Variable<Public, Self::Compiler, Type = AssetValue> + + Variable<Secret, Self::Compiler, Type = AssetValue> + + Add<Self::Compiler> + + Equal<Self::Compiler>; /// Constraint System Type type Compiler: ConstraintSystem; @@ -170,9 +173,9 @@ pub trait Configuration { fn ephemeral_public_key_var( parameters: &Self::KeyAgreementSchemeVar, secret_key: &SecretKeyVar<Self>, - cs: &mut Self::Compiler, + compiler: &mut Self::Compiler, ) -> PublicKeyVar<Self> { - parameters.derive(secret_key, cs) + parameters.derive(secret_key, compiler) } /// Generates the commitment trapdoor associated to `secret_key` and `public_key`. @@ -191,9 +194,9 @@ pub trait Configuration { key_agreement: &Self::KeyAgreementSchemeVar, secret_key: &SecretKeyVar<Self>, public_key: &PublicKeyVar<Self>, - cs: &mut Self::Compiler, + compiler: &mut Self::Compiler, ) -> TrapdoorVar<Self> { - key_agreement.agree(secret_key, public_key, cs) + key_agreement.agree(secret_key, public_key, compiler) } /// Generates the trapdoor associated to `secret_key` and `public_key` and then uses it to @@ -219,10 +222,10 @@ pub trait Configuration { secret_key: &SecretKeyVar<Self>, public_key: &PublicKeyVar<Self>, asset: &AssetVar<Self>, - cs: &mut Self::Compiler, + compiler: &mut Self::Compiler, ) -> UtxoVar<Self> { - let trapdoor = Self::trapdoor_var(key_agreement, secret_key, public_key, cs); - utxo_commitment.commit_in(&trapdoor, asset, cs) + let trapdoor = Self::trapdoor_var(key_agreement, secret_key, public_key, compiler); + utxo_commitment.commit_in(&trapdoor, asset, compiler) } /// Generates the void number associated to `utxo` and `secret_key` using `parameters`. @@ -241,9 +244,9 @@ pub trait Configuration { parameters: &Self::VoidNumberHashFunctionVar, utxo: &UtxoVar<Self>, secret_key: &SecretKeyVar<Self>, - cs: &mut Self::Compiler, + compiler: &mut Self::Compiler, ) -> VoidNumberVar<Self> { - parameters.hash_in(utxo, secret_key, cs) + parameters.hash_in(utxo, secret_key, compiler) } /// Checks that the `utxo` is correctly constructed from the `secret_key`, `public_key`, and @@ -428,26 +431,21 @@ where __: PhantomData<&'p ()>, } -impl<'p, C> Variable<C::Compiler> for FullParametersVar<'p, C> +impl<'p, C> Constant<C::Compiler> for FullParametersVar<'p, C> where C: Configuration, Parameters<C>: 'p, { type Type = FullParameters<'p, C>; - type Mode = Constant; - #[inline] - fn new(cs: &mut C::Compiler, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - match allocation { - Allocation::Known(this, mode) => Self { - key_agreement: this.base.key_agreement.as_known(cs, mode), - utxo_commitment: this.base.utxo_commitment.as_known(cs, mode), - void_number_hash: this.base.void_number_hash.as_known(cs, mode), - utxo_set_model: this.utxo_set_model.as_known(cs, mode), - __: PhantomData, - }, - _ => unreachable!("Constant variables cannot be unknown."), + fn new_constant(this: &Self::Type, compiler: &mut C::Compiler) -> Self { + Self { + key_agreement: this.base.key_agreement.as_constant(compiler), + utxo_commitment: this.base.utxo_commitment.as_constant(compiler), + void_number_hash: this.base.void_number_hash.as_constant(compiler), + utxo_set_model: this.utxo_set_model.as_constant(compiler), + __: PhantomData, } } } @@ -786,12 +784,12 @@ where C: Configuration, { /// Returns the asset for `self`, checking if `self` is well-formed in the given constraint - /// system `cs`. + /// system `compiler`. #[inline] pub fn get_well_formed_asset( self, parameters: &FullParametersVar<C>, - cs: &mut C::Compiler, + compiler: &mut C::Compiler, ) -> AssetVar<C> { let utxo = C::utxo_var( &parameters.key_agreement, @@ -799,43 +797,44 @@ where &self.spend, &self.ephemeral_public_key, &self.asset, - cs, + compiler, ); let is_valid_proof = self.utxo_membership_proof - .verify_with_compiler(&parameters.utxo_set_model, &utxo, cs); - cs.assert(is_valid_proof); - let void_number = C::void_number_var(&parameters.void_number_hash, &utxo, &self.spend, cs); - cs.assert_eq(&self.void_number, &void_number); + .verify_in(&parameters.utxo_set_model, &utxo, compiler); + compiler.assert(is_valid_proof); + let void_number = + C::void_number_var(&parameters.void_number_hash, &utxo, &self.spend, compiler); + compiler.assert_eq(&self.void_number, &void_number); self.asset } } -impl<C> Variable<C::Compiler> for SenderVar<C> +impl<C> Variable<Derived, C::Compiler> for SenderVar<C> where C: Configuration, { type Type = Sender<C>; - type Mode = Derived; + #[inline] + fn new_known(this: &Self::Type, compiler: &mut C::Compiler) -> Self { + Self { + spend: this.spend.as_known(compiler), + ephemeral_public_key: this.ephemeral_public_key.as_known(compiler), + asset: this.asset.as_known(compiler), + utxo_membership_proof: this.utxo_membership_proof.as_known(compiler), + void_number: this.void_number.as_known(compiler), + } + } #[inline] - fn new(cs: &mut C::Compiler, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - match allocation { - Allocation::Known(this, mode) => Self { - spend: this.spend.as_known(cs, mode), - ephemeral_public_key: this.ephemeral_public_key.as_known(cs, mode), - asset: this.asset.as_known(cs, mode), - utxo_membership_proof: this.utxo_membership_proof.as_known(cs, mode), - void_number: this.void_number.as_known(cs, Public), - }, - Allocation::Unknown(mode) => Self { - spend: SecretKeyVar::<C>::new_unknown(cs, mode), - ephemeral_public_key: PublicKeyVar::<C>::new_unknown(cs, mode), - asset: AssetVar::<C>::new_unknown(cs, mode), - utxo_membership_proof: UtxoMembershipProofVar::<C>::new_unknown(cs, mode), - void_number: VoidNumberVar::<C>::new_unknown(cs, Public), - }, + fn new_unknown(compiler: &mut C::Compiler) -> Self { + Self { + spend: compiler.allocate_unknown(), + ephemeral_public_key: compiler.allocate_unknown(), + asset: compiler.allocate_unknown(), + utxo_membership_proof: compiler.allocate_unknown(), + void_number: compiler.allocate_unknown(), } } } @@ -1078,54 +1077,57 @@ where C: Configuration, { /// Returns the asset for `self`, checking if `self` is well-formed in the given constraint - /// system `cs`. + /// system `compiler`. #[inline] pub fn get_well_formed_asset( self, parameters: &FullParametersVar<C>, - cs: &mut C::Compiler, + compiler: &mut C::Compiler, ) -> AssetVar<C> { - let ephemeral_public_key = - C::ephemeral_public_key_var(&parameters.key_agreement, &self.ephemeral_secret_key, cs); - cs.assert_eq(&self.ephemeral_public_key, &ephemeral_public_key); + let ephemeral_public_key = C::ephemeral_public_key_var( + &parameters.key_agreement, + &self.ephemeral_secret_key, + compiler, + ); + compiler.assert_eq(&self.ephemeral_public_key, &ephemeral_public_key); let utxo = C::utxo_var( &parameters.key_agreement, &parameters.utxo_commitment, &self.ephemeral_secret_key, &self.spend, &self.asset, - cs, + compiler, ); - cs.assert_eq(&self.utxo, &utxo); + compiler.assert_eq(&self.utxo, &utxo); self.asset } } -impl<C> Variable<C::Compiler> for ReceiverVar<C> +impl<C> Variable<Derived, C::Compiler> for ReceiverVar<C> where C: Configuration, { type Type = Receiver<C>; - type Mode = Derived; + #[inline] + fn new_known(this: &Self::Type, compiler: &mut C::Compiler) -> Self { + Self { + ephemeral_secret_key: this.ephemeral_secret_key.as_known(compiler), + ephemeral_public_key: this.ephemeral_public_key().as_known(compiler), + spend: this.spend.as_known(compiler), + asset: this.asset.as_known(compiler), + utxo: this.utxo.as_known::<Public, _>(compiler), + } + } #[inline] - fn new(cs: &mut C::Compiler, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - match allocation { - Allocation::Known(this, mode) => Self { - ephemeral_secret_key: this.ephemeral_secret_key.as_known(cs, mode), - ephemeral_public_key: this.ephemeral_public_key().as_known(cs, mode), - spend: this.spend.as_known(cs, mode), - asset: this.asset.as_known(cs, mode), - utxo: this.utxo.as_known(cs, Public), - }, - Allocation::Unknown(mode) => Self { - ephemeral_secret_key: SecretKeyVar::<C>::new_unknown(cs, mode), - ephemeral_public_key: PublicKeyVar::<C>::new_unknown(cs, mode), - spend: PublicKeyVar::<C>::new_unknown(cs, mode), - asset: AssetVar::<C>::new_unknown(cs, mode), - utxo: UtxoVar::<C>::new_unknown(cs, Public), - }, + fn new_unknown(compiler: &mut C::Compiler) -> Self { + Self { + ephemeral_secret_key: compiler.allocate_unknown(), + ephemeral_public_key: compiler.allocate_unknown(), + spend: compiler.allocate_unknown(), + asset: compiler.allocate_unknown(), + utxo: compiler.allocate_unknown::<Public, _>(), } } } @@ -1365,10 +1367,10 @@ where where R: CryptoRng + RngCore + ?Sized, { - let mut cs = C::ProofSystem::for_unknown(); - TransferVar::<C, SOURCES, SENDERS, RECEIVERS, SINKS>::new_unknown(&mut cs, Derived) - .build_validity_constraints(&parameters.as_known(&mut cs, Public), &mut cs); - cs.generate_context::<C::ProofSystem, _>(rng) + let mut compiler = C::ProofSystem::for_unknown(); + TransferVar::<C, SOURCES, SENDERS, RECEIVERS, SINKS>::new_unknown(&mut compiler) + .build_validity_constraints(&parameters.as_constant(&mut compiler), &mut compiler); + C::ProofSystem::generate_context(compiler, rng) } /// Converts `self` into its ledger post. @@ -1384,11 +1386,14 @@ where { Ok(TransferPost { validity_proof: { - let mut cs = C::ProofSystem::for_known(); + let mut compiler = C::ProofSystem::for_known(); let transfer: TransferVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> = - self.as_known(&mut cs, Derived); - transfer.build_validity_constraints(&parameters.as_known(&mut cs, Public), &mut cs); - cs.prove::<C::ProofSystem, _>(context, rng)? + self.as_known(&mut compiler); + transfer.build_validity_constraints( + &parameters.as_constant(&mut compiler), + &mut compiler, + ); + C::ProofSystem::prove(compiler, context, rng)? }, asset_id: self.asset_id, sources: self.sources.into(), @@ -1436,110 +1441,114 @@ where { /// Builds constraints for the [`Transfer`] validity proof. #[inline] - fn build_validity_constraints(self, parameters: &FullParametersVar<C>, cs: &mut C::Compiler) { + fn build_validity_constraints( + self, + parameters: &FullParametersVar<C>, + compiler: &mut C::Compiler, + ) { let mut secret_asset_ids = Vec::with_capacity(SENDERS + RECEIVERS); let input_sum = Self::value_sum( self.senders .into_iter() .map(|s| { - let asset = s.get_well_formed_asset(parameters, cs); + let asset = s.get_well_formed_asset(parameters, compiler); secret_asset_ids.push(asset.id); asset.value }) .chain(self.sources) .collect::<Vec<_>>(), - cs, + compiler, ); let output_sum = Self::value_sum( self.receivers .into_iter() .map(|r| { - let asset = r.get_well_formed_asset(parameters, cs); + let asset = r.get_well_formed_asset(parameters, compiler); secret_asset_ids.push(asset.id); asset.value }) .chain(self.sinks) .collect::<Vec<_>>(), - cs, + compiler, ); - cs.assert_eq(&input_sum, &output_sum); + compiler.assert_eq(&input_sum, &output_sum); match self.asset_id { - Some(asset_id) => cs.assert_all_eq_to_base(&asset_id, secret_asset_ids.iter()), - _ => cs.assert_all_eq(secret_asset_ids.iter()), + Some(asset_id) => compiler.assert_all_eq_to_base(&asset_id, secret_asset_ids.iter()), + _ => compiler.assert_all_eq(secret_asset_ids.iter()), } } - /// Computes the sum of the asset values over `iter` inside of `cs`. + /// Computes the sum of the asset values over `iter` inside of `compiler`. #[inline] - fn value_sum<I>(iter: I, cs: &mut C::Compiler) -> C::AssetValueVar + fn value_sum<I>(iter: I, compiler: &mut C::Compiler) -> C::AssetValueVar where I: IntoIterator<Item = C::AssetValueVar>, { iter.into_iter() - .reduce(move |l, r| Add::add(cs, l, r)) + .reduce(move |l, r| Add::add(l, r, compiler)) .unwrap() } } impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> - Variable<C::Compiler> for TransferVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> + Variable<Derived, C::Compiler> for TransferVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> where C: Configuration, { type Type = Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>; - type Mode = Derived; + #[inline] + fn new_known(this: &Self::Type, compiler: &mut C::Compiler) -> Self { + Self { + asset_id: this.asset_id.map(|id| id.as_known::<Public, _>(compiler)), + sources: this + .sources + .iter() + .map(|source| source.as_known::<Public, _>(compiler)) + .collect(), + senders: this + .senders + .iter() + .map(|sender| sender.as_known(compiler)) + .collect(), + receivers: this + .receivers + .iter() + .map(|receiver| receiver.as_known(compiler)) + .collect(), + sinks: this + .sinks + .iter() + .map(|sink| sink.as_known::<Public, _>(compiler)) + .collect(), + } + } #[inline] - fn new(cs: &mut C::Compiler, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - match allocation { - Allocation::Known(this, mode) => Self { - asset_id: this.asset_id.map(|id| id.as_known(cs, Public)), - sources: this - .sources - .iter() - .map(|source| source.as_known(cs, Public)) - .collect(), - senders: this - .senders - .iter() - .map(|sender| sender.as_known(cs, mode)) - .collect(), - receivers: this - .receivers - .iter() - .map(|receiver| receiver.as_known(cs, mode)) - .collect(), - sinks: this - .sinks - .iter() - .map(|sink| sink.as_known(cs, Public)) - .collect(), - }, - Allocation::Unknown(mode) => Self { - asset_id: has_public_participants(SOURCES, SENDERS, RECEIVERS, SINKS) - .then(|| C::AssetIdVar::new_unknown(cs, Public)), - sources: (0..SOURCES) - .into_iter() - .map(|_| C::AssetValueVar::new_unknown(cs, Public)) - .collect(), - senders: (0..SENDERS) - .into_iter() - .map(|_| SenderVar::<C>::new_unknown(cs, mode)) - .collect(), - receivers: (0..RECEIVERS) - .into_iter() - .map(|_| ReceiverVar::<C>::new_unknown(cs, mode)) - .collect(), - sinks: (0..SINKS) - .into_iter() - .map(|_| C::AssetValueVar::new_unknown(cs, Public)) - .collect(), - }, + fn new_unknown(compiler: &mut C::Compiler) -> Self { + Self { + asset_id: has_public_participants(SOURCES, SENDERS, RECEIVERS, SINKS) + .then(|| compiler.allocate_unknown::<Public, _>()), + sources: (0..SOURCES) + .into_iter() + .map(|_| compiler.allocate_unknown::<Public, _>()) + .collect(), + senders: (0..SENDERS) + .into_iter() + .map(|_| compiler.allocate_unknown()) + .collect(), + receivers: (0..RECEIVERS) + .into_iter() + .map(|_| compiler.allocate_unknown()) + .collect(), + sinks: (0..SINKS) + .into_iter() + .map(|_| compiler.allocate_unknown::<Public, _>()) + .collect(), } } } diff --git a/manta-crypto/src/accumulator.rs b/manta-crypto/src/accumulator.rs index a4951b935..92249c9ba 100644 --- a/manta-crypto/src/accumulator.rs +++ b/manta-crypto/src/accumulator.rs @@ -16,7 +16,7 @@ //! Dynamic Cryptographic Accumulators -// TODO: See if we can modify `Accumulator` so that it can extend the `Verifier` trait directly. +use crate::constraint::Native; /// Accumulator Membership Model pub trait Model<COM = ()> { @@ -35,14 +35,29 @@ pub trait Model<COM = ()> { type Verification; /// Verifies that `item` is stored in a known accumulator with accumulated `output` and - /// membership `witness`. - fn verify( + /// membership `witness` inside the given `compiler`. + fn verify_in( &self, item: &Self::Item, witness: &Self::Witness, output: &Self::Output, compiler: &mut COM, ) -> Self::Verification; + + /// Verifies that `item` is stored in a known accumulator with accumulated `output` and + /// membership `witness`. + #[inline] + fn verify( + &self, + item: &Self::Item, + witness: &Self::Witness, + output: &Self::Output, + ) -> Self::Verification + where + COM: Native, + { + self.verify_in(item, witness, output, &mut COM::compiler()) + } } /// Accumulator Output Type @@ -203,33 +218,26 @@ where self.output } - /// Verifies that `item` is stored in a known accumulator using `model`. + /// Verifies that `item` is stored in a known accumulator using `model` inside the `compiler`. #[inline] - pub fn verify_with_compiler( - &self, - model: &M, - item: &M::Item, - compiler: &mut COM, - ) -> M::Verification { - model.verify(item, &self.witness, &self.output, compiler) + pub fn verify_in(&self, model: &M, item: &M::Item, compiler: &mut COM) -> M::Verification { + model.verify_in(item, &self.witness, &self.output, compiler) } -} -impl<M> MembershipProof<M> -where - M: Model + ?Sized, -{ /// Verifies that `item` is stored in a known accumulator using `model`. #[inline] - pub fn verify(&self, model: &M, item: &M::Item) -> M::Verification { - self.verify_with_compiler(model, item, &mut ()) + pub fn verify(&self, model: &M, item: &M::Item) -> M::Verification + where + COM: Native, + { + model.verify(item, &self.witness, &self.output) } } /// Constraint System Gadgets pub mod constraint { use super::*; - use crate::constraint::{Allocation, AllocationMode, Derived, Variable, VariableSource}; + use crate::constraint::{Allocator, Constant, Derived, ValueSource, Variable}; use core::marker::PhantomData; /// Membership Proof Allocation Mode Entry @@ -265,48 +273,29 @@ pub mod constraint { /// Membership Proof Allocation Mode #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct MembershipProofMode<WitnessMode, OutputMode>(PhantomData<(WitnessMode, OutputMode)>) - where - WitnessMode: AllocationMode, - OutputMode: AllocationMode; + pub struct MembershipProofMode<WitnessMode, OutputMode>(PhantomData<(WitnessMode, OutputMode)>); - impl<WitnessMode, OutputMode> AllocationMode for MembershipProofMode<WitnessMode, OutputMode> + impl<M, WitnessMode, OutputMode, COM> + Variable<MembershipProofMode<WitnessMode, OutputMode>, COM> for MembershipProof<M, COM> where - WitnessMode: AllocationMode, - OutputMode: AllocationMode, + M: Model<COM> + Constant<COM>, + M::Type: Model, + M::Witness: Variable<WitnessMode, COM, Type = <M::Type as Model>::Witness>, + M::Output: Variable<OutputMode, COM, Type = <M::Type as Model>::Output>, { - type Known = MembershipProofModeEntry<WitnessMode::Known, OutputMode::Known>; - type Unknown = MembershipProofModeEntry<WitnessMode::Unknown, OutputMode::Unknown>; - } - - impl<M, COM> Variable<COM> for MembershipProof<M, COM> - where - M: Model<COM> + Variable<COM>, - <M as Variable<COM>>::Type: Model, - <M as Model<COM>>::Witness: - Variable<COM, Type = <<M as Variable<COM>>::Type as Model>::Witness>, - <M as Model<COM>>::Output: - Variable<COM, Type = <<M as Variable<COM>>::Type as Model>::Output>, - { - type Type = MembershipProof<<M as Variable<COM>>::Type>; + type Type = MembershipProof<M::Type>; - type Mode = MembershipProofMode< - <<M as Model<COM>>::Witness as Variable<COM>>::Mode, - <<M as Model<COM>>::Output as Variable<COM>>::Mode, - >; + #[inline] + fn new_known(this: &Self::Type, compiler: &mut COM) -> Self { + Self::new( + this.witness.as_known(compiler), + this.output.as_known(compiler), + ) + } #[inline] - fn new(cs: &mut COM, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - match allocation { - Allocation::Known(this, mode) => Self::new( - this.witness.as_known(cs, mode.witness), - this.output.as_known(cs, mode.output), - ), - Allocation::Unknown(mode) => Self::new( - <M as Model<COM>>::Witness::new_unknown(cs, mode.witness), - <M as Model<COM>>::Output::new_unknown(cs, mode.output), - ), - } + fn new_unknown(compiler: &mut COM) -> Self { + Self::new(compiler.allocate_unknown(), compiler.allocate_unknown()) } } } diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index dd37ec7d7..0423e1096 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -16,380 +16,200 @@ //! Constraint Systems and Proof Systems -// TODO: Add derive macros to all the enums/structs here. -// TODO: Add derive trait to implement `HasAllocation` for structs (and enums?). -// TODO: Add more convenience functions for allocating unknown variables. // FIXME: Leverage the type system to constrain allocation to only unknown modes for verifier // generation and only known modes for proof generation, instead of relying on the `for_*` // methods to "do the right thing". -use core::{ - convert::{Infallible, TryFrom}, - fmt::Debug, - hash::Hash, - marker::PhantomData, -}; +use core::{fmt::Debug, hash::Hash, marker::PhantomData}; +use manta_util::{create_seal, seal}; use rand_core::{CryptoRng, RngCore}; -/// Allocation Mode -pub trait AllocationMode { - /// Known Allocation Mode - type Known; +create_seal! {} - /// Unknown Allocation Mode - type Unknown; -} - -impl AllocationMode for Infallible { - type Known = Self; - type Unknown = Self; -} - -impl AllocationMode for () { - type Known = Self; - type Unknown = Self; -} +/// Generic Derived Allocation Mode +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Derived; -/// Allocation Entry -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "Mode::Known: Clone, Mode::Unknown: Clone"), - Copy(bound = "Mode::Known: Copy, Mode::Unknown: Copy"), - Debug(bound = "T: Debug, Mode::Known: Debug, Mode::Unknown: Debug"), - Eq(bound = "T: Eq, Mode::Known: Eq, Mode::Unknown: Eq"), - Hash(bound = "T: Hash, Mode::Known: Hash, Mode::Unknown: Hash"), - PartialEq(bound = "T: PartialEq, Mode::Known: PartialEq, Mode::Unknown: PartialEq") -)] -pub enum Allocation<'t, T, Mode> -where - T: ?Sized, - Mode: AllocationMode, -{ - /// Known Value - Known( - /// Allocation Value - &'t T, - /// Allocation Mode - Mode::Known, - ), - /// Unknown Value - Unknown( - /// Allocation Mode - Mode::Unknown, - ), -} +/// Public Allocation Mode +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Public; -impl<'t, T, Mode> From<(&'t T, Mode::Known)> for Allocation<'t, T, Mode> -where - T: ?Sized, - Mode: AllocationMode, -{ +impl From<Derived> for Public { #[inline] - fn from((value, mode): (&'t T, Mode::Known)) -> Self { - Self::Known(value, mode) + fn from(d: Derived) -> Self { + let _ = d; + Self } } -impl<'t, T, Mode> Allocation<'t, T, Mode> -where - T: ?Sized, - Mode: AllocationMode, -{ - /// Returns `true` if `self` represents a known value and mode. - #[inline] - pub fn is_known(&self) -> bool { - matches!(self, Self::Known(..)) - } - - /// Returns `true` if `self` represents an unknown value mode. - #[inline] - pub fn is_unknown(&self) -> bool { - matches!(self, Self::Unknown(..)) - } - - /// Converts `self` into a possibly known value and mode. - #[inline] - pub fn known(self) -> Option<(&'t T, Mode::Known)> { - match self { - Self::Known(value, mode) => Some((value, mode)), - _ => None, - } - } +/// Secret Allocation Mode +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Secret; - /// Converts `self` into a possibly unknown mode. +impl From<Derived> for Secret { #[inline] - pub fn unknown(self) -> Option<Mode::Unknown> { - match self { - Self::Unknown(mode) => Some(mode), - _ => None, - } + fn from(d: Derived) -> Self { + let _ = d; + Self } +} - /// Converts `self` into a known value and mode whenever its unknown mode is [`Infallible`]. - #[inline] - pub fn into_known(self) -> (&'t T, Mode::Known) - where - Mode: AllocationMode<Unknown = Infallible>, - { - match self { - Self::Known(value, mode) => (value, mode), - _ => unreachable!("Values of infallible types cannot be constructed."), - } - } +/// Constant Type Alias +pub type Const<C, COM> = <C as Constant<COM>>::Type; - /// Converts `self` into an unknown mode whenever its known mode is [`Infallible`]. - #[inline] - pub fn into_unknown(self) -> Mode::Unknown - where - Mode: AllocationMode<Known = Infallible>, - { - match self { - Self::Unknown(mode) => mode, - _ => unreachable!("Values of infallible types cannot be constructed."), - } - } +/// Compiler Constant +pub trait Constant<COM> +where + COM: ?Sized, +{ + /// Underlying Type + type Type; - /// Maps over the possible value stored in `self`. - #[inline] - pub fn map<'u, U, N, F>(self, f: F) -> Allocation<'u, U, N> - where - Mode::Known: Into<N::Known>, - Mode::Unknown: Into<N::Unknown>, - N: AllocationMode, - F: FnOnce(&'t T) -> &'u U, - { - match self { - Self::Known(value, mode) => Allocation::Known(f(value), mode.into()), - Self::Unknown(mode) => Allocation::Unknown(mode.into()), - } - } + /// Allocates a new constant from `this` into the `compiler`. + fn new_constant(this: &Self::Type, compiler: &mut COM) -> Self; +} - /// Allocates a variable with `self` as the allocation entry into `cs`. - #[inline] - pub fn allocate<C, V>(self, cs: &mut C) -> V - where - C: ?Sized, - V: Variable<C, Type = T, Mode = Mode>, - { - V::new(cs, self) - } +impl<T, COM> Constant<COM> for PhantomData<T> +where + COM: ?Sized, +{ + type Type = PhantomData<T>; - /// Allocates a variable into `cs` after mapping over `self`. #[inline] - pub fn map_allocate<C, V, F>(self, cs: &mut C, f: F) -> V - where - Mode::Known: Into<<V::Mode as AllocationMode>::Known>, - Mode::Unknown: Into<<V::Mode as AllocationMode>::Unknown>, - C: ?Sized, - V: Variable<C>, - F: FnOnce(&'t T) -> V::Type, - { - match self { - Self::Known(value, mode) => V::new_known(cs, &f(value), mode), - Self::Unknown(mode) => V::new_unknown(cs, mode), - } + fn new_constant(this: &Self::Type, compiler: &mut COM) -> Self { + let _ = (this, compiler); + PhantomData } } -/// Native Compiler Marker Trait -/// -/// This trait is only implemented for `()`, the only native compiler. -pub trait Native { - /// Returns the native compiler. - fn compiler() -> Self; -} - -impl Native for () { - #[inline] - fn compiler() -> Self {} -} +/// Variable Type Alias +pub type Var<V, M, COM> = <V as Variable<M, COM>>::Type; -/// Variable Allocation Trait -pub trait Variable<C>: Sized +/// Compiler Variable +pub trait Variable<M, COM> where - C: ?Sized, + COM: ?Sized, { - /// Origin Type of the Variable + /// Underlying Type type Type; - /// Allocation Mode - type Mode: AllocationMode; + /// Allocates a new known value from `this` into the `compiler`. + fn new_known(this: &Self::Type, compiler: &mut COM) -> Self; - /// Allocates a new variable into `cs` with the given `allocation`. - fn new(cs: &mut C, allocation: Allocation<Self::Type, Self::Mode>) -> Self; - - /// Allocates a new known variable into `cs` with the given `mode`. - #[inline] - fn new_known( - cs: &mut C, - value: &Self::Type, - mode: impl Into<<Self::Mode as AllocationMode>::Known>, - ) -> Self { - Self::new(cs, Allocation::Known(value, mode.into())) - } + /// Allocates a new unknown value into the `compiler`. + fn new_unknown(compiler: &mut COM) -> Self; +} - /// Allocates a new unknown variable into `cs` with the given `mode`. - #[inline] - fn new_unknown(cs: &mut C, mode: impl Into<<Self::Mode as AllocationMode>::Unknown>) -> Self { - Self::new(cs, Allocation::Unknown(mode.into())) - } +impl<T, M, COM> Variable<M, COM> for PhantomData<T> +where + COM: ?Sized, +{ + type Type = PhantomData<T>; - /// Allocates a new known variable into `cs` with the given `mode` which holds the default - /// value of [`Self::Type`]. #[inline] - fn from_default(cs: &mut C, mode: impl Into<<Self::Mode as AllocationMode>::Known>) -> Self - where - Self::Type: Default, - { - Self::new_known(cs, &Default::default(), mode) + fn new_known(this: &Self::Type, compiler: &mut COM) -> Self { + let _ = (this, compiler); + PhantomData } - /// Allocates a new known variable into `cs` with the given `mode` which holds the default - /// value of [`&Self::Type`](Self::Type). #[inline] - fn from_default_ref<'t>( - cs: &mut C, - mode: impl Into<<Self::Mode as AllocationMode>::Known>, - ) -> Self - where - Self::Type: 't, - &'t Self::Type: Default, - { - Self::new_known(cs, Default::default(), mode) + fn new_unknown(compiler: &mut COM) -> Self { + let _ = compiler; + PhantomData } } -/// Variable Source -pub trait VariableSource { - /// Allocates a new variable into `cs` with the given `allocation`. +/// Value Source Auto-Trait +pub trait ValueSource<COM> +where + COM: ?Sized, +{ + /// Allocates `self` as a constant in `compiler`. #[inline] - fn as_variable<C, V>(cs: &mut C, allocation: Allocation<Self, V::Mode>) -> V + fn as_constant<C>(&self, compiler: &mut COM) -> C where - C: ?Sized, - V: Variable<C, Type = Self>, + C: Constant<COM, Type = Self>, { - V::new(cs, allocation) + C::new_constant(self, compiler) } - /// Allocates a new known variable into `cs` with the given `mode`. + /// Allocates `self` as a known value in `compiler`. #[inline] - fn as_known<C, V>(&self, cs: &mut C, mode: impl Into<<V::Mode as AllocationMode>::Known>) -> V + fn as_known<M, V>(&self, compiler: &mut COM) -> V where - C: ?Sized, - V: Variable<C, Type = Self>, + V: Variable<M, COM, Type = Self>, { - V::new_known(cs, self, mode) + V::new_known(self, compiler) } - /// Allocates a new unknown variable into `cs` with the given `mode`. + /// Allocates an unknown value of type `Self` into `compiler`. #[inline] - fn as_unknown<C, V>(cs: &mut C, mode: impl Into<<V::Mode as AllocationMode>::Unknown>) -> V + fn as_unknown<M, V>(compiler: &mut COM) -> V where - C: ?Sized, - V: Variable<C, Type = Self>, + V: Variable<M, COM, Type = Self>, { - V::new_unknown(cs, mode) + V::new_unknown(compiler) } } -impl<T> VariableSource for T where T: ?Sized {} - -impl<T, C> Variable<C> for PhantomData<T> -where - T: ?Sized, - C: ?Sized, -{ - type Type = PhantomData<T>; - - type Mode = (); +impl<COM, T> ValueSource<COM> for T where T: ?Sized {} +/// Allocator Auto-Trait +pub trait Allocator { + /// Allocates a constant with the given `value` into `self`. #[inline] - fn new(cs: &mut C, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - let _ = (cs, allocation); - PhantomData - } -} - -/* TODO[remove]: -impl<T, C> reflection::HasAllocation<C> for PhantomData<T> -where - T: ?Sized, - C: ?Sized, -{ - type Variable = PhantomData<T>; - type Mode = (); -} -*/ - -/// Allocates a new known variable into `cs` with the given `mode`. -#[inline] -pub fn known<C, V>( - cs: &mut C, - value: &V::Type, - mode: impl Into<<V::Mode as AllocationMode>::Known>, -) -> V -where - C: ?Sized, - V: Variable<C>, -{ - V::new_known(cs, value, mode) -} - -/// Allocates a new unknown variable into `cs` with the given `mode`. -#[inline] -pub fn unknown<C, V>(cs: &mut C, mode: impl Into<<V::Mode as AllocationMode>::Unknown>) -> V -where - C: ?Sized, - V: Variable<C>, -{ - V::new_unknown(cs, mode) -} - -/// Allocation System -pub trait AllocationSystem { - /// Allocates a new variable into `self` with the given `allocation`. - #[inline] - fn allocate<V>(&mut self, allocation: Allocation<V::Type, V::Mode>) -> V + fn allocate_constant<C>(&mut self, value: &C::Type) -> C where - V: Variable<Self>, + C: Constant<Self>, { - V::new(self, allocation) + C::new_constant(value, self) } - /// Allocates a new known variable into `self` with the given `mode`. + /// Allocates a known variable with the given `value` into `self`. #[inline] - fn allocate_known<V>( - &mut self, - value: &V::Type, - mode: impl Into<<V::Mode as AllocationMode>::Known>, - ) -> V + fn allocate_known<M, V>(&mut self, value: &V::Type) -> V where - V: Variable<Self>, + V: Variable<M, Self>, { - known(self, value, mode) + V::new_known(value, self) } - /// Allocates a new unknown variable into `self` with the given `mode`. + /// Allocates an unknown variable into `self`. #[inline] - fn allocate_unknown<V>(&mut self, mode: impl Into<<V::Mode as AllocationMode>::Unknown>) -> V + fn allocate_unknown<M, V>(&mut self) -> V where - V: Variable<Self>, + V: Variable<M, Self>, { - unknown(self, mode) + V::new_unknown(self) } } -impl<C> AllocationSystem for C where C: ?Sized {} +impl<COM> Allocator for COM where COM: ?Sized {} + +/// Native Compiler Marker Trait +/// +/// This trait is only implemented for `()`, the only native compiler. +pub trait Native: sealed::Sealed { + /// Returns the native compiler. + fn compiler() -> Self; +} + +seal! { () } + +impl Native for () { + #[inline] + fn compiler() -> Self {} +} /// Constraint System pub trait ConstraintSystem { /// Boolean Variable Type - type Bool: Variable<Self, Type = bool>; + type Bool; - /// Asserts that `b` is `true`. + /// Asserts that `b == 1`. fn assert(&mut self, b: Self::Bool); - /// Asserts that all the booleans in `iter` are `true`. + /// Asserts that all the booleans in `iter` are equal to `1`. #[inline] fn assert_all<I>(&mut self, iter: I) where @@ -404,7 +224,7 @@ pub trait ConstraintSystem { where V: Equal<Self>, { - V::eq(self, lhs, rhs) + V::eq(lhs, rhs, self) } /// Asserts that `lhs` and `rhs` are equal. @@ -413,7 +233,7 @@ pub trait ConstraintSystem { where V: Equal<Self>, { - V::assert_eq(self, lhs, rhs); + V::assert_eq(lhs, rhs, self); } /// Asserts that all the elements in `iter` are equal to some `base` element. @@ -423,7 +243,7 @@ pub trait ConstraintSystem { V: 't + Equal<Self>, I: IntoIterator<Item = &'t V>, { - V::assert_all_eq_to_base(self, base, iter); + V::assert_all_eq_to_base(base, iter, self); } /// Asserts that all the elements in `iter` are equal. @@ -433,7 +253,7 @@ pub trait ConstraintSystem { V: 't + Equal<Self>, I: IntoIterator<Item = &'t V>, { - V::assert_all_eq(self, iter); + V::assert_all_eq(iter, self); } /// Selects `lhs` if `bit == 0` and `rhs` if `bit == 1`. @@ -462,112 +282,107 @@ pub trait ConstraintSystem { { V::swap_in_place(bit, lhs, rhs, self) } +} - /// Returns proving and verifying contexts for the constraints contained in `self`. - #[inline] - fn generate_context<P, R>( - self, - rng: &mut R, - ) -> Result<(P::ProvingContext, P::VerifyingContext), P::Error> - where - Self: Sized, - P: ProofSystem<ConstraintSystem = Self>, - R: CryptoRng + RngCore + ?Sized, - { - P::generate_context(self, rng) - } +/* TODO: Can we safely implement this? +impl ConstraintSystem for () { + type Bool = bool; - /// Returns a proof that the constraint system `self` is consistent. #[inline] - fn prove<P, R>(self, context: &P::ProvingContext, rng: &mut R) -> Result<P::Proof, P::Error> - where - Self: Sized, - P: ProofSystem<ConstraintSystem = Self>, - R: CryptoRng + RngCore + ?Sized, - { - P::prove(self, context, rng) + fn assert(&mut self, b: Self::Bool) { + assert!(b, "Native Constraint System assertion."); } } +*/ /// Equality Trait -pub trait Equal<C> +pub trait Equal<COM> where - C: ConstraintSystem + ?Sized, + COM: ConstraintSystem + ?Sized, { /// Generates a boolean that represents the fact that `lhs` and `rhs` may be equal. - fn eq(cs: &mut C, lhs: &Self, rhs: &Self) -> C::Bool; + fn eq(lhs: &Self, rhs: &Self, compiler: &mut COM) -> COM::Bool; /// Asserts that `lhs` and `rhs` are equal. #[inline] - fn assert_eq(cs: &mut C, lhs: &Self, rhs: &Self) { - let boolean = Self::eq(cs, lhs, rhs); - cs.assert(boolean); + fn assert_eq(lhs: &Self, rhs: &Self, compiler: &mut COM) { + let boolean = Self::eq(lhs, rhs, compiler); + compiler.assert(boolean); } /// Asserts that all the elements in `iter` are equal to some `base` element. #[inline] - fn assert_all_eq_to_base<'t, I>(cs: &mut C, base: &'t Self, iter: I) + fn assert_all_eq_to_base<'t, I>(base: &'t Self, iter: I, compiler: &mut COM) where I: IntoIterator<Item = &'t Self>, { for item in iter { - Self::assert_eq(cs, base, item); + Self::assert_eq(base, item, compiler); } } /// Asserts that all the elements in `iter` are equal. #[inline] - fn assert_all_eq<'t, I>(cs: &mut C, iter: I) + fn assert_all_eq<'t, I>(iter: I, compiler: &mut COM) where Self: 't, I: IntoIterator<Item = &'t Self>, { let mut iter = iter.into_iter(); if let Some(base) = iter.next() { - Self::assert_all_eq_to_base(cs, base, iter); + Self::assert_all_eq_to_base(base, iter, compiler); } } } /// Conditional Selection -pub trait ConditionalSelect<C> +pub trait ConditionalSelect<COM> where - C: ConstraintSystem + ?Sized, + COM: ConstraintSystem + ?Sized, { /// Selects `lhs` if `bit == 0` and `rhs` if `bit == 1`. - fn select(bit: &C::Bool, lhs: &Self, rhs: &Self, cs: &mut C) -> Self; + fn select(bit: &COM::Bool, lhs: &Self, rhs: &Self, compiler: &mut COM) -> Self; /// Swaps `lhs` and `rhs` if `bit == 1`. #[inline] - fn swap(bit: &C::Bool, lhs: &Self, rhs: &Self, cs: &mut C) -> (Self, Self) + fn swap(bit: &COM::Bool, lhs: &Self, rhs: &Self, compiler: &mut COM) -> (Self, Self) where Self: Sized, { ( - Self::select(bit, lhs, rhs, cs), - Self::select(bit, rhs, lhs, cs), + Self::select(bit, lhs, rhs, compiler), + Self::select(bit, rhs, lhs, compiler), ) } /// Swaps `lhs` and `rhs` in-place if `bit == 1`. #[inline] - fn swap_in_place(bit: &C::Bool, lhs: &mut Self, rhs: &mut Self, cs: &mut C) + fn swap_in_place(bit: &COM::Bool, lhs: &mut Self, rhs: &mut Self, compiler: &mut COM) where Self: Sized, { - let (swapped_lhs, swapped_rhs) = Self::swap(bit, lhs, rhs, cs); + let (swapped_lhs, swapped_rhs) = Self::swap(bit, lhs, rhs, compiler); *lhs = swapped_lhs; *rhs = swapped_rhs; } } -/// Addition Trait -pub trait Add<C> +/// Addition +pub trait Add<COM> +where + COM: ?Sized, +{ + /// Adds `lhs` and `rhs` inside of `compiler`. + fn add(lhs: Self, rhs: Self, compiler: &mut COM) -> Self; +} + +/// Subtraction +pub trait Sub<COM> where - C: ConstraintSystem + ?Sized, + COM: ?Sized, { - /// Adds `lhs` to `rhs` inside of `cs`, returning the sum. - fn add(cs: &mut C, lhs: Self, rhs: Self) -> Self; + /// Subtracts `rhs` from `lhs` inside of `compiler`. + fn sub(lhs: Self, rhs: Self, compiler: &mut COM) -> Self; } /// Proof System @@ -603,17 +418,17 @@ pub trait ProofSystem { #[must_use] fn for_known() -> Self::ConstraintSystem; - /// Returns proving and verifying contexts for the constraints contained in `cs`. + /// Returns proving and verifying contexts for the constraints contained in `compiler`. fn generate_context<R>( - cs: Self::ConstraintSystem, + compiler: Self::ConstraintSystem, rng: &mut R, ) -> Result<(Self::ProvingContext, Self::VerifyingContext), Self::Error> where R: CryptoRng + RngCore + ?Sized; - /// Returns a proof that the constraint system `cs` is consistent. + /// Returns a proof that the constraint system `compiler` is consistent. fn prove<R>( - cs: Self::ConstraintSystem, + compiler: Self::ConstraintSystem, context: &Self::ProvingContext, rng: &mut R, ) -> Result<Self::Proof, Self::Error> @@ -637,549 +452,58 @@ where fn extend(input: &mut Self::Input, next: &T); } -/// Derived Allocation Mode -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Derived; - -impl AllocationMode for Derived { - type Known = Self; - type Unknown = Self; -} - -impl From<Derived> for () { - #[inline] - fn from(d: Derived) -> Self { - let _ = d; - } -} - -/// Always Public Allocation Mode -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Public; - -impl AllocationMode for Public { - type Known = Self; - type Unknown = Self; -} - -impl From<Derived> for Public { - #[inline] - fn from(d: Derived) -> Self { - let _ = d; - Self - } -} - -/// Always Secret Allocation Mode -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Secret; - -impl AllocationMode for Secret { - type Known = Self; - type Unknown = Self; -} - -impl From<Derived> for Secret { - #[inline] - fn from(d: Derived) -> Self { - let _ = d; - Self - } -} - -/// Constant Allocation Mode -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Constant<T = Public>( - /// Underyling Allocation Mode - pub T, -) -where - T: AllocationMode; - -impl<T> AllocationMode for Constant<T> -where - T: AllocationMode, -{ - type Known = T::Known; - type Unknown = Infallible; -} - -impl<T> From<Derived> for Constant<T> -where - T: AllocationMode + From<Derived>, -{ - #[inline] - fn from(d: Derived) -> Self { - Self(d.into()) - } -} - -/// Public/Secret Allocation Mode -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum PublicOrSecret { - /// Public Variable Mode - Public, - - /// Secret Variable Mode - Secret, -} - -impl PublicOrSecret { - /// Returns `true` if this mode is for public variables. - #[inline] - pub const fn is_public(&self) -> bool { - matches!(self, Self::Public) - } - - /// Converts [`PublicOrSecret`] into `Option<Public>`. - #[inline] - pub const fn public(self) -> Option<Public> { - match self { - Self::Public => Some(Public), - Self::Secret => None, - } - } - - /// Returns `true` if this mode is for secret variables. - #[inline] - pub const fn is_secret(&self) -> bool { - matches!(self, Self::Secret) - } - - /// Converts [`PublicOrSecret`] into `Option<Secret>`. - #[inline] - pub const fn secret(self) -> Option<Secret> { - match self { - Self::Secret => Some(Secret), - Self::Public => None, - } - } -} - -impl AllocationMode for PublicOrSecret { - type Known = Self; - type Unknown = Self; -} - -impl Default for PublicOrSecret { - #[inline] - fn default() -> Self { - Self::Secret - } -} - -impl From<Public> for PublicOrSecret { - #[inline] - fn from(p: Public) -> Self { - let _ = p; - Self::Public - } -} - -impl TryFrom<PublicOrSecret> for Public { - type Error = Secret; - - #[inline] - fn try_from(pos: PublicOrSecret) -> Result<Self, Self::Error> { - pos.public().ok_or(Secret) - } -} - -impl From<Secret> for PublicOrSecret { - #[inline] - fn from(s: Secret) -> Self { - let _ = s; - Self::Secret - } -} - -impl TryFrom<PublicOrSecret> for Secret { - type Error = Public; - - #[inline] - fn try_from(pos: PublicOrSecret) -> Result<Self, Self::Error> { - pos.secret().ok_or(Public) - } -} - -impl<T> From<Constant<T>> for PublicOrSecret -where - T: AllocationMode + Into<PublicOrSecret>, -{ - #[inline] - fn from(c: Constant<T>) -> Self { - c.0.into() - } -} - -impl<T> TryFrom<PublicOrSecret> for Constant<T> -where - T: AllocationMode + TryFrom<PublicOrSecret>, -{ - type Error = T::Error; - - #[inline] - fn try_from(pos: PublicOrSecret) -> Result<Self, Self::Error> { - T::try_from(pos).map(Self) - } -} - /// Constraint System Measurement pub mod measure { use super::*; /// Constraint System Measurement - pub trait Measure<M> - where - M: ?Sized, - { + pub trait Measure { /// Returns the number of constraints stored in `self`. fn constraint_count(&self) -> usize; - /// Returns the number of variables allocated with the given `mode`. - fn variable_count(&self, mode: M) -> usize; - - /// Returns the number of constraints and number and kind of variables stored in `self`. + /// Returns the number of allocated constants. #[inline] - fn measure(&self) -> SizeReport<M> - where - M: MeasureVariables, - { - M::measure(self) + fn constant_count(&self) -> Option<usize> { + None } - } - - /// Constraint System Variable Allocation Measurement - pub trait MeasureVariables { - /// Measurement Type - type Measurement; - - /// Counts the number of constraints and the number of variables allocated with all of the - /// possible modes in `Self`, returning a [`SizeReport`]. - fn measure<C>(cs: &C) -> SizeReport<Self> - where - C: Measure<Self> + ?Sized; - } - - /// Constraint System Size Measurement Report - #[derive(derivative::Derivative)] - #[derivative( - Clone(bound = "M::Measurement: Clone"), - Copy(bound = "M::Measurement: Copy"), - Debug(bound = "M::Measurement: Debug"), - Default(bound = "M::Measurement: Default"), - Eq(bound = "M::Measurement: Eq"), - Hash(bound = "M::Measurement: Hash"), - PartialEq(bound = "M::Measurement: PartialEq") - )] - pub struct SizeReport<M> - where - M: MeasureVariables + ?Sized, - { - /// Number of constraints - pub constraint_count: usize, - /// Number of variables - pub variable_count: M::Measurement, - } - - impl<M> SizeReport<M> - where - M: MeasureVariables + ?Sized, - { - /// Builds a new [`SizeReport`] from `constraint_count` and `variable_count`. - #[inline] - pub fn new(constraint_count: usize, variable_count: M::Measurement) -> Self { - Self { - constraint_count, - variable_count, - } - } - - /// Builds a new [`SizeReport`] with the given `cs` to measure its constraint count and - /// `variable_count` as the number of variables in the resulting report. + /// Returns the number of allocated public variables. #[inline] - pub fn with<C>(cs: &C, variable_count: M::Measurement) -> Self - where - C: Measure<M> + ?Sized, - { - Self::new(cs.constraint_count(), variable_count) + fn public_variable_count(&self) -> Option<usize> { + None } - } - - impl MeasureVariables for Public { - type Measurement = usize; - - #[inline] - fn measure<C>(cs: &C) -> SizeReport<Self> - where - C: Measure<Self> + ?Sized, - { - SizeReport::with(cs, cs.variable_count(Public)) - } - } - - impl MeasureVariables for Secret { - type Measurement = usize; + /// Returns the number of allocated secret variables. #[inline] - fn measure<C>(cs: &C) -> SizeReport<Self> - where - C: Measure<Self> + ?Sized, - { - SizeReport::with(cs, cs.variable_count(Secret)) + fn secret_variable_count(&self) -> Option<usize> { + None } - } - - impl MeasureVariables for PublicOrSecret { - type Measurement = PublicOrSecretMeasurement; + /// Returns a [`SizeReport`] with the number of constraints and variables of each kind. #[inline] - fn measure<C>(cs: &C) -> SizeReport<Self> - where - C: Measure<Self> + ?Sized, - { - SizeReport::with( - cs, - PublicOrSecretMeasurement { - public: cs.variable_count(PublicOrSecret::Public), - secret: cs.variable_count(PublicOrSecret::Secret), - }, - ) + fn measure(&self) -> SizeReport { + SizeReport { + constraint_count: self.constraint_count(), + constant_count: self.constant_count(), + public_variable_count: self.public_variable_count(), + secret_variable_count: self.secret_variable_count(), + } } } - /// [`PublicOrSecret`] Measurement + /// Constraint System Size Measurement Report #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] - pub struct PublicOrSecretMeasurement { - /// Public Variable Count - pub public: usize, - - /// Secret Variable Count - pub secret: usize, - } -} - -/* TODO[remove]: -/// Opt-In Compile-Time Reflection Capabilities -/// -/// See [`HasAllocation`] and [`HasVariable`] for more information. -/// -/// [`HasAllocation`]: reflection::HasAllocation -/// [`HasVariable`]: reflection::HasVariable -pub mod reflection { - use super::*; - - /// Variable Type - /// - /// Requires a [`HasAllocation`] implementation for `T`. - pub type Var<T, C> = <C as HasVariable<T>>::Variable; - - /// Allocation Mode Type - /// - /// Requires a [`HasAllocation`] implementation for `T`. - pub type Mode<T, C> = <Var<T, C> as Variable<C>>::Mode; - - /// Known Allocation Mode Type - /// - /// Requires a [`HasAllocation`] implementation for `T`. - pub type KnownMode<T, C> = <Mode<T, C> as AllocationMode>::Known; - - /// Known Allocation Mode Type - /// - /// Requires a [`HasAllocation`] implementation for `T`. - pub type UnknownMode<T, C> = <Mode<T, C> as AllocationMode>::Unknown; - - /// Allocation Entry Type - /// - /// Requires a [`HasAllocation`] implementation for `T`. - pub type Alloc<'t, T, C> = Allocation<'t, T, Mode<T, C>>; - - /// Variable Existence Reflection Trait - /// - /// This trait can be optionally implemented by any type `T` which has an existing variable - /// type that implements [`Variable<C, Type = T>`](Variable). Implementing this trait unlocks - /// all of the reflection capabilities in this module. - /// - /// Whenever possible, library authors should implement [`HasAllocation`] on their types which - /// have associated variables but should minimize their use of [`HasVariable`] so that users - /// can take advantage of as much of a library as possible while implementing as little as - /// possible. - pub trait HasAllocation<C> - where - C: ?Sized, - { - /// Variable Object Type - type Variable: Variable<C, Mode = Self::Mode, Type = Self>; - - /// Allocation Mode - type Mode: AllocationMode; - - /// Allocates a new variable into `cs` with the given `allocation`. - #[inline] - fn variable(cs: &mut C, allocation: Allocation<Self, Self::Mode>) -> Self::Variable { - Self::Variable::new(cs, allocation) - } - - /// Allocates a new known variable into `cs` with the given `mode`. - #[inline] - fn known( - &self, - cs: &mut C, - mode: impl Into<<Self::Mode as AllocationMode>::Known>, - ) -> Self::Variable { - Self::Variable::new_known(cs, self, mode) - } - - /// Allocates a new unknown variable into `cs` with the given `mode`. - #[inline] - fn unknown( - cs: &mut C, - mode: impl Into<<Self::Mode as AllocationMode>::Unknown>, - ) -> Self::Variable { - Self::Variable::new_unknown(cs, mode) - } - } - - /// Variable Existence Reflection Trait - /// - /// This trait is automatically implemented for all types [`T: HasAllocation`](HasAllocation) - /// and it activates all the reflection features in this module. See that trait for more - /// information on activating reflection. - pub trait HasVariable<T> - where - T: ?Sized, - { - /// Variable Object Type - type Variable: Variable<Self, Mode = Self::Mode, Type = T>; - - /// Allocation Mode - type Mode: AllocationMode; - - /// Allocates a new variable into `self` with the given `allocation`. - #[inline] - fn new_allocation(&mut self, allocation: Allocation<T, Self::Mode>) -> Self::Variable { - Self::Variable::new(self, allocation) - } - - /// Allocates a new known variable into `self` with the given `mode`. - #[inline] - fn new_known_allocation( - &mut self, - value: &T, - mode: impl Into<<Self::Mode as AllocationMode>::Known>, - ) -> Self::Variable { - Self::Variable::new_known(self, value, mode) - } - - /// Allocates a new unknown variable into `self` with the given `mode`. - #[inline] - fn new_unknown_allocation( - &mut self, - mode: impl Into<<Self::Mode as AllocationMode>::Unknown>, - ) -> Self::Variable { - Self::Variable::new_unknown(self, mode) - } - } - - impl<C, T> HasVariable<T> for C - where - C: ?Sized, - T: HasAllocation<C> + ?Sized, - { - type Variable = T::Variable; - type Mode = T::Mode; - } + pub struct SizeReport { + /// Number of Constraints + pub constraint_count: usize, - /// - pub trait HasEqual<V>: ConstraintSystem { - /// - fn eq(&mut self, lhs: &V, rhs: &V) -> Self::Bool; - } + /// Number of Constants + pub constant_count: Option<usize>, - impl<C, V> Equal<C> for V - where - C: HasEqual<V>, - { - #[inline] - fn eq(cs: &mut C, lhs: &Self, rhs: &Self) -> C::Bool { - HasEqual::eq(cs, lhs, rhs) - } - } + /// Number of Public Variables + pub public_variable_count: Option<usize>, - /// Allocates a new unknown variable into `cs` with the given `mode`. - #[inline] - pub fn known<T, C>(cs: &mut C, value: &T, mode: KnownMode<T, C>) -> Var<T, C> - where - T: ?Sized, - C: HasVariable<T> + ?Sized, - { - cs.new_known_allocation(value, mode) - } - - /// Allocates a new unknown variable into `cs` with the given `mode`. - #[inline] - pub fn unknown<T, C>(cs: &mut C, mode: UnknownMode<T, C>) -> Var<T, C> - where - T: ?Sized, - C: HasVariable<T> + ?Sized, - { - cs.new_unknown_allocation(mode) + /// Number of Secret Variables + pub secret_variable_count: Option<usize>, } } - -/// Type Aliases -/// -/// All of these types depend on reflection capabilities. See [`reflection`] for more information. -pub mod types { - use super::reflection::Var; - - /// Boolean Variable Type - pub type Bool<C> = Var<bool, C>; - - /// Character Variable Type - pub type Char<C> = Var<char, C>; - - /// 32-bit Floating Point Variable Type - pub type F32<C> = Var<f32, C>; - - /// 64-bit Floating Point Variable Type - pub type F64<C> = Var<f64, C>; - - /// Signed 8-bit Integer Variable Type - pub type I8<C> = Var<i8, C>; - - /// Signed 16-bit Integer Variable Type - pub type I16<C> = Var<i16, C>; - - /// Signed 32-bit Integer Variable Type - pub type I32<C> = Var<i32, C>; - - /// Signed 64-bit Integer Variable Type - pub type I64<C> = Var<i64, C>; - - /// Signed 128-bit Integer Variable Type - pub type I128<C> = Var<i128, C>; - - /// Pointer-Sized Integer Variable Type - pub type Isize<C> = Var<isize, C>; - - /// Unsigned 8-bit Integer Variable Type - pub type U8<C> = Var<u8, C>; - - /// Unsigned 16-bit Integer Variable Type - pub type U16<C> = Var<u16, C>; - - /// Unsigned 32-bit Integer Variable Type - pub type U32<C> = Var<u32, C>; - - /// Unsigned 64-bit Integer Variable Type - pub type U64<C> = Var<u64, C>; - - /// Unsigned 128-bit Integer Variable Type - pub type U128<C> = Var<u128, C>; - - /// Pointer-Sized Unsigned Integer Variable Type - pub type Usize<C> = Var<usize, C>; -} -*/ diff --git a/manta-crypto/src/ecc.rs b/manta-crypto/src/ecc.rs index 23754d129..f591671f4 100644 --- a/manta-crypto/src/ecc.rs +++ b/manta-crypto/src/ecc.rs @@ -18,10 +18,7 @@ // TODO: Improve ECC abstractions over arkworks. -use crate::{ - constraint::{Allocation, AllocationMode, Constant, Public, Variable, VariableSource}, - key::KeyAgreementScheme, -}; +use crate::{constraint::Constant, key::KeyAgreementScheme}; use core::marker::PhantomData; /* TODO: @@ -143,20 +140,14 @@ where } } -impl<G, COM> Variable<COM> for DiffieHellman<G, COM> +impl<G, COM> Constant<COM> for DiffieHellman<G, COM> where - G: Group<COM> + Variable<COM>, - <G::Mode as AllocationMode>::Known: From<Public>, + G: Group<COM> + Constant<COM>, { type Type = G::Type; - type Mode = Constant; - #[inline] - fn new(cs: &mut COM, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - match allocation { - Allocation::Known(this, _) => Self::new(this.as_known(cs, Public)), - _ => unreachable!("Constants are never unknown."), - } + fn new_constant(value: &Self::Type, compiler: &mut COM) -> Self { + Self::new(G::new_constant(value, compiler)) } } diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index e0faad28d..d4b86713b 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -32,4 +32,3 @@ pub mod hash; pub mod key; pub mod merkle_tree; pub mod rand; -// TODO[remove]: pub mod util; diff --git a/manta-crypto/src/merkle_tree/path.rs b/manta-crypto/src/merkle_tree/path.rs index d8e20f245..9066ae309 100644 --- a/manta-crypto/src/merkle_tree/path.rs +++ b/manta-crypto/src/merkle_tree/path.rs @@ -882,8 +882,8 @@ pub mod constraint { use super::*; use crate::{ constraint::{ - Allocation, AllocationMode, ConditionalSelect, ConstraintSystem, Equal, Secret, - Variable, VariableSource, + Allocator, ConditionalSelect, Constant, ConstraintSystem, Equal, Secret, ValueSource, + Variable, }, merkle_tree::path_length_in, }; @@ -983,45 +983,39 @@ pub mod constraint { } } - impl<C, COM> Variable<COM> for InnerPathVar<C, COM> + impl<C, COM> Variable<Secret, COM> for InnerPathVar<C, COM> where COM: ConstraintSystem, - <<COM::Bool as Variable<COM>>::Mode as AllocationMode>::Known: From<Secret>, - <<COM::Bool as Variable<COM>>::Mode as AllocationMode>::Unknown: From<Secret>, - C: Configuration<COM> + Variable<COM> + ?Sized, + COM::Bool: Variable<Secret, COM, Type = bool>, + C: Configuration<COM> + Constant<COM> + ?Sized, C::Type: Configuration, - InnerDigest<C, COM>: Variable<COM, Type = InnerDigest<C::Type>>, - <<InnerDigest<C, COM> as Variable<COM>>::Mode as AllocationMode>::Known: From<Secret>, - <<InnerDigest<C, COM> as Variable<COM>>::Mode as AllocationMode>::Unknown: From<Secret>, + InnerDigest<C, COM>: Variable<Secret, COM, Type = InnerDigest<C::Type>>, { type Type = InnerPath<C::Type>; - type Mode = Secret; #[inline] - fn new(compiler: &mut COM, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - match allocation { - Allocation::Known(this, mode) => Self { - leaf_index: this.leaf_index.is_right().as_known(compiler, mode), - inner_indices: this - .leaf_index - .parents() - .map(|i| i.is_right().as_known(compiler, mode)) - .collect(), - path: this - .path - .iter() - .map(|d| d.as_known(compiler, mode)) - .collect(), - }, - Allocation::Unknown(mode) => Self { - leaf_index: bool::as_unknown(compiler, mode), - inner_indices: (0..path_length_in::<C, _>()) - .map(|_| bool::as_unknown(compiler, mode)) - .collect(), - path: (0..path_length_in::<C, _>()) - .map(|_| InnerDigest::<C::Type>::as_unknown(compiler, mode)) - .collect(), - }, + fn new_known(this: &Self::Type, compiler: &mut COM) -> Self { + Self { + leaf_index: this.leaf_index.is_right().as_known(compiler), + inner_indices: this + .leaf_index + .parents() + .map(|i| i.is_right().as_known(compiler)) + .collect(), + path: this.path.iter().map(|d| d.as_known(compiler)).collect(), + } + } + + #[inline] + fn new_unknown(compiler: &mut COM) -> Self { + Self { + leaf_index: compiler.allocate_unknown(), + inner_indices: (0..path_length_in::<C, _>()) + .map(|_| compiler.allocate_unknown()) + .collect(), + path: (0..path_length_in::<C, _>()) + .map(|_| compiler.allocate_unknown()) + .collect(), } } } @@ -1097,34 +1091,30 @@ pub mod constraint { } } - impl<C, COM> Variable<COM> for PathVar<C, COM> + impl<C, COM> Variable<Secret, COM> for PathVar<C, COM> where COM: ConstraintSystem, - <<COM::Bool as Variable<COM>>::Mode as AllocationMode>::Known: From<Secret>, - <<COM::Bool as Variable<COM>>::Mode as AllocationMode>::Unknown: From<Secret>, - C: Configuration<COM> + Variable<COM> + ?Sized, + COM::Bool: Variable<Secret, COM, Type = bool>, + C: Configuration<COM> + Constant<COM> + ?Sized, C::Type: Configuration, - LeafDigest<C, COM>: Variable<COM, Type = LeafDigest<C::Type>>, - <<LeafDigest<C, COM> as Variable<COM>>::Mode as AllocationMode>::Known: From<Secret>, - <<LeafDigest<C, COM> as Variable<COM>>::Mode as AllocationMode>::Unknown: From<Secret>, - InnerDigest<C, COM>: Variable<COM, Type = InnerDigest<C::Type>>, - <<InnerDigest<C, COM> as Variable<COM>>::Mode as AllocationMode>::Known: From<Secret>, - <<InnerDigest<C, COM> as Variable<COM>>::Mode as AllocationMode>::Unknown: From<Secret>, + InnerDigest<C, COM>: Variable<Secret, COM, Type = InnerDigest<C::Type>>, + LeafDigest<C, COM>: Variable<Secret, COM, Type = LeafDigest<C::Type>>, { type Type = Path<C::Type>; - type Mode = Secret; #[inline] - fn new(compiler: &mut COM, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - match allocation { - Allocation::Known(this, mode) => Self { - sibling_digest: this.sibling_digest.as_known(compiler, mode), - inner_path: this.inner_path.as_known(compiler, mode), - }, - Allocation::Unknown(mode) => Self { - sibling_digest: LeafDigest::<C::Type>::as_unknown(compiler, mode), - inner_path: InnerPath::<C::Type>::as_unknown(compiler, mode), - }, + fn new_known(this: &Self::Type, compiler: &mut COM) -> Self { + Self { + sibling_digest: this.sibling_digest.as_known(compiler), + inner_path: this.inner_path.as_known(compiler), + } + } + + #[inline] + fn new_unknown(compiler: &mut COM) -> Self { + Self { + sibling_digest: compiler.allocate_unknown(), + inner_path: compiler.allocate_unknown(), } } } diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index 709f3ece2..5a18b96bf 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -20,15 +20,14 @@ // implementations for the trivial tree sizes? // TODO: Add "copy-on-write" adapters for `Root` and `Path`, and see if we can incorporate them // into `Tree`. +// TODO: Use uniform construction for `Path` and `PathVar`. use crate::{ accumulator::{ self, Accumulator, ConstantCapacityAccumulator, ExactSizeAccumulator, MembershipProof, OptimizedAccumulator, }, - constraint::{ - Allocation, ConditionalSelect, Constant, ConstraintSystem, Equal, Native, Variable, - }, + constraint::{ConditionalSelect, Constant, ConstraintSystem, Equal, Native, ValueSource}, merkle_tree::{ fork::Trunk, path::{constraint::PathVar, CurrentPath, Path}, @@ -610,25 +609,20 @@ where } } -use crate::constraint::VariableSource; - -impl<C, COM> Variable<COM> for Parameters<C, COM> +impl<C, COM> Constant<COM> for Parameters<C, COM> where - C: HashConfiguration<COM> + Variable<COM> + ?Sized, + C: HashConfiguration<COM> + Constant<COM> + ?Sized, C::Type: HashConfiguration, - LeafHashParameters<C, COM>: Variable<COM, Type = LeafHashParameters<C::Type>, Mode = Constant>, - InnerHashParameters<C, COM>: - Variable<COM, Type = InnerHashParameters<C::Type>, Mode = Constant>, + LeafHashParameters<C, COM>: Constant<COM, Type = LeafHashParameters<C::Type>>, + InnerHashParameters<C, COM>: Constant<COM, Type = InnerHashParameters<C::Type>>, { type Type = Parameters<C::Type>; - type Mode = Constant; #[inline] - fn new(compiler: &mut COM, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - let (this, mode) = allocation.into_known(); + fn new_constant(this: &Self::Type, compiler: &mut COM) -> Self { Self::new( - this.leaf.as_known(compiler, mode), - this.inner.as_known(compiler, mode), + this.leaf.as_constant(compiler), + this.inner.as_constant(compiler), ) } } @@ -647,7 +641,7 @@ where type Verification = bool; #[inline] - fn verify( + fn verify_in( &self, item: &Self::Item, witness: &Self::Witness, @@ -671,7 +665,7 @@ where type Verification = COM::Bool; #[inline] - fn verify( + fn verify_in( &self, item: &Self::Item, witness: &Self::Witness, diff --git a/manta-crypto/src/util.rs b/manta-crypto/src/util.rs deleted file mode 100644 index ed285cb0a..000000000 --- a/manta-crypto/src/util.rs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Utilities - -use core::{fmt::Debug, hash::Hash}; - -/// Extended Input -pub trait Input<T> -where - T: ?Sized, -{ - /// Extends `self` with input data `next`. - fn extend(&mut self, next: &T); -} - -/// Extended Input Builder -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "I: Clone"), - Copy(bound = "I: Copy"), - Debug(bound = "F: Debug, I: Debug, Args: Debug"), - Eq(bound = "F: Eq, I: Eq, Args: Eq"), - Hash(bound = "F: Hash, I: Hash, Args: Hash"), - PartialEq(bound = "F: PartialEq, I: PartialEq, Args: PartialEq") -)] -pub struct Builder<'f, F, I, Args = ()> -where - F: ?Sized, - Args: ?Sized, -{ - /// Base Construction - pub(crate) base: &'f F, - - /// Stored Arguments - pub(crate) args: &'f Args, - - /// Input Data - pub(crate) input: I, -} - -impl<'f, F, I, Args> Builder<'f, F, I, Args> -where - F: ?Sized, - Args: ?Sized, -{ - /// Returns a new [`Builder`] for the given `base`. - #[inline] - pub(crate) fn new(base: &'f F, args: &'f Args) -> Self - where - I: Default, - { - Self { - base, - args, - input: Default::default(), - } - } - - /// Updates the builder with the `next` input. - #[inline] - #[must_use] - pub fn update<T>(mut self, next: &T) -> Self - where - T: ?Sized, - I: Input<T>, - { - self.input.extend(next); - self - } - - /// Updates the builder with each item in `iter`. - #[inline] - #[must_use] - pub fn update_all<'t, T, Iter>(mut self, iter: Iter) -> Self - where - T: 't + ?Sized, - Iter: IntoIterator<Item = &'t T>, - I: Input<T>, - { - for next in iter { - self.input.extend(next); - } - self - } -} diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 6b0e8e5cc..e3c63f96c 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -36,7 +36,7 @@ use manta_accounting::{ use manta_crypto::{ accumulator, commitment::CommitmentScheme, - constraint::{self, Allocation, Constant, Secret, Variable, VariableSource}, + constraint::{Allocator, Constant, Input as ProofSystemInput, Secret, ValueSource, Variable}, ecc::DiffieHellman, encryption, hash::{BinaryHashFunction, HashFunction}, @@ -147,14 +147,12 @@ impl CommitmentScheme<Compiler> for UtxoCommitmentSchemeVar { } } -impl Variable<Compiler> for UtxoCommitmentSchemeVar { +impl Constant<Compiler> for UtxoCommitmentSchemeVar { type Type = UtxoCommitmentScheme; - type Mode = Constant; #[inline] - fn new(cs: &mut Compiler, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - let (this, mode) = allocation.into_known(); - Self(this.0.as_known(cs, mode)) + fn new_constant(this: &Self::Type, compiler: &mut Compiler) -> Self { + Self(this.0.as_constant(compiler)) } } @@ -202,54 +200,46 @@ impl BinaryHashFunction<Compiler> for VoidNumberHashFunctionVar { } } -impl Variable<Compiler> for VoidNumberHashFunctionVar { +impl Constant<Compiler> for VoidNumberHashFunctionVar { type Type = VoidNumberHashFunction; - type Mode = Constant; #[inline] - fn new(cs: &mut Compiler, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - let (this, mode) = allocation.into_known(); - Self(this.0.as_known(cs, mode)) + fn new_constant(this: &Self::Type, compiler: &mut Compiler) -> Self { + Self(this.0.as_constant(compiler)) } } /// Asset ID Variable pub struct AssetIdVar(ConstraintFieldVar); -impl Variable<Compiler> for AssetIdVar { +impl Variable<Secret, Compiler> for AssetIdVar { type Type = AssetId; - type Mode = Secret; #[inline] - fn new(cs: &mut Compiler, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - Self(match allocation { - Allocation::Known(this, mode) => { - ConstraintFieldVar::new(cs, Allocation::Known(&this.0.into(), mode.into())) - } - Allocation::Unknown(mode) => { - ConstraintFieldVar::new(cs, Allocation::Unknown(mode.into())) - } - }) + fn new_known(this: &Self::Type, compiler: &mut Compiler) -> Self { + Self(ConstraintField::from(this.0).as_known::<Secret, _>(compiler)) + } + + #[inline] + fn new_unknown(compiler: &mut Compiler) -> Self { + Self(compiler.allocate_unknown::<Secret, _>()) } } /// Asset Value Variable pub struct AssetValueVar(ConstraintFieldVar); -impl Variable<Compiler> for AssetValueVar { +impl Variable<Secret, Compiler> for AssetValueVar { type Type = AssetValue; - type Mode = Secret; #[inline] - fn new(cs: &mut Compiler, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - Self(match allocation { - Allocation::Known(this, mode) => { - ConstraintFieldVar::new(cs, Allocation::Known(&this.0.into(), mode.into())) - } - Allocation::Unknown(mode) => { - ConstraintFieldVar::new(cs, Allocation::Unknown(mode.into())) - } - }) + fn new_known(this: &Self::Type, compiler: &mut Compiler) -> Self { + Self(ConstraintField::from(this.0).as_known::<Secret, _>(compiler)) + } + + #[inline] + fn new_unknown(compiler: &mut Compiler) -> Self { + Self(compiler.allocate_unknown::<Secret, _>()) } } @@ -341,28 +331,28 @@ impl merkle_tree::Configuration<Compiler> for MerkleTreeConfigurationVar { const HEIGHT: usize = <MerkleTreeConfiguration as merkle_tree::Configuration>::HEIGHT; } -impl constraint::Input<AssetId> for ProofSystem { +impl ProofSystemInput<AssetId> for ProofSystem { #[inline] fn extend(input: &mut Self::Input, next: &AssetId) { input.push(next.0.into()); } } -impl constraint::Input<AssetValue> for ProofSystem { +impl ProofSystemInput<AssetValue> for ProofSystem { #[inline] fn extend(input: &mut Self::Input, next: &AssetValue) { input.push(next.0.into()); } } -impl constraint::Input<ConstraintField> for ProofSystem { +impl ProofSystemInput<ConstraintField> for ProofSystem { #[inline] fn extend(input: &mut Self::Input, next: &ConstraintField) { input.push(*next); } } -impl constraint::Input<Group> for ProofSystem { +impl ProofSystemInput<Group> for ProofSystem { #[inline] fn extend(input: &mut Self::Input, next: &Group) { // FIXME: Make sure we can type check the coordinate system here. diff --git a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs index 0189e308e..9cd2a2982 100644 --- a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs @@ -16,14 +16,14 @@ //! Arkworks Constraint System Implementation -use ark_ff::{fields::Field, PrimeField}; +use ark_ff::PrimeField; use ark_r1cs_std::{ alloc::AllocVar, bits::boolean::Boolean, eq::EqGadget, select::CondSelectGadget, }; use ark_relations::{ns, r1cs as ark_r1cs}; use manta_crypto::constraint::{ - measure::Measure, Add, Allocation, AllocationMode, ConditionalSelect, ConstraintSystem, Equal, - Public, PublicOrSecret, Secret, Variable, + measure::Measure, Add, ConditionalSelect, Constant, ConstraintSystem, Equal, Public, Secret, + Variable, }; pub use ark_r1cs::SynthesisError; @@ -51,54 +51,10 @@ pub fn full<T>(t: T) -> impl FnOnce() -> SynthesisResult<T> { move || Ok(t) } -/// Arkworks Allocation Mode -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub enum ArkAllocationMode { - /// Allocates a Constant Variable - Constant, - - /// Allocates a Public Input Variable - Public, - - /// Allocates a Secret Witness Variable - Secret, -} - -impl AllocationMode for ArkAllocationMode { - type Known = Self; - type Unknown = PublicOrSecret; -} - -impl From<Public> for ArkAllocationMode { - #[inline] - fn from(p: Public) -> Self { - let _ = p; - Self::Public - } -} - -impl From<Secret> for ArkAllocationMode { - #[inline] - fn from(s: Secret) -> Self { - let _ = s; - Self::Secret - } -} - -impl From<PublicOrSecret> for ArkAllocationMode { - #[inline] - fn from(pos: PublicOrSecret) -> Self { - match pos { - PublicOrSecret::Public => Self::Public, - PublicOrSecret::Secret => Self::Secret, - } - } -} - /// Arkworks Rank-1 Constraint System pub struct R1CS<F> where - F: Field, + F: PrimeField, { /// Constraint System pub(crate) cs: ark_r1cs::ConstraintSystemRef<F>, @@ -106,7 +62,7 @@ where impl<F> R1CS<F> where - F: Field, + F: PrimeField, { /// Constructs a new constraint system which is ready for unknown variables. #[inline] @@ -130,7 +86,7 @@ where impl<F> ConstraintSystem for R1CS<F> where - F: Field, + F: PrimeField, { type Bool = Boolean<F>; @@ -141,9 +97,9 @@ where } } -impl<F> Measure<PublicOrSecret> for R1CS<F> +impl<F> Measure for R1CS<F> where - F: Field, + F: PrimeField, { #[inline] fn constraint_count(&self) -> usize { @@ -151,85 +107,127 @@ where } #[inline] - fn variable_count(&self, mode: PublicOrSecret) -> usize { - match mode { - PublicOrSecret::Public => self.cs.num_instance_variables(), - PublicOrSecret::Secret => self.cs.num_witness_variables(), - } + fn public_variable_count(&self) -> Option<usize> { + Some(self.cs.num_instance_variables()) + } + + #[inline] + fn secret_variable_count(&self) -> Option<usize> { + Some(self.cs.num_witness_variables()) + } +} + +impl<F> Constant<R1CS<F>> for Boolean<F> +where + F: PrimeField, +{ + type Type = bool; + + #[inline] + fn new_constant(this: &Self::Type, compiler: &mut R1CS<F>) -> Self { + AllocVar::new_constant(ns!(compiler.cs, "boolean constant"), this) + .expect("Variable allocation is not allowed to fail.") + } +} + +impl<F> Variable<Public, R1CS<F>> for Boolean<F> +where + F: PrimeField, +{ + type Type = bool; + + #[inline] + fn new_known(this: &Self::Type, compiler: &mut R1CS<F>) -> Self { + Self::new_input(ns!(compiler.cs, "boolean public input"), full(this)) + .expect("Variable allocation is not allowed to fail.") + } + + #[inline] + fn new_unknown(compiler: &mut R1CS<F>) -> Self { + Self::new_input(ns!(compiler.cs, "boolean public input"), empty::<bool>) + .expect("Variable allocation is not allowed to fail.") } } -impl<F> Variable<R1CS<F>> for Boolean<F> +impl<F> Variable<Secret, R1CS<F>> for Boolean<F> where - F: Field, + F: PrimeField, { type Type = bool; - type Mode = ArkAllocationMode; + #[inline] + fn new_known(this: &Self::Type, compiler: &mut R1CS<F>) -> Self { + Self::new_witness(ns!(compiler.cs, "boolean secret witness"), full(this)) + .expect("Variable allocation is not allowed to fail.") + } #[inline] - fn new(cs: &mut R1CS<F>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - match allocation { - Allocation::Known(this, ArkAllocationMode::Constant) => { - Self::new_constant(ns!(cs.cs, "boolean constant"), this) - } - Allocation::Known(this, ArkAllocationMode::Public) => { - Self::new_input(ns!(cs.cs, "boolean input"), full(this)) - } - Allocation::Known(this, ArkAllocationMode::Secret) => { - Self::new_witness(ns!(cs.cs, "boolean witness"), full(this)) - } - Allocation::Unknown(PublicOrSecret::Public) => { - Self::new_input(ns!(cs.cs, "boolean input"), empty::<bool>) - } - Allocation::Unknown(PublicOrSecret::Secret) => { - Self::new_witness(ns!(cs.cs, "boolean witness"), empty::<bool>) - } - } - .expect("Variable allocation is not allowed to fail.") + fn new_unknown(compiler: &mut R1CS<F>) -> Self { + Self::new_witness(ns!(compiler.cs, "boolean secret witness"), empty::<bool>) + .expect("Variable allocation is not allowed to fail.") } } impl<F> Equal<R1CS<F>> for Boolean<F> where - F: Field, + F: PrimeField, { #[inline] - fn eq(cs: &mut R1CS<F>, lhs: &Self, rhs: &Self) -> Boolean<F> { - let _ = cs; + fn eq(lhs: &Self, rhs: &Self, compiler: &mut R1CS<F>) -> Boolean<F> { + let _ = compiler; lhs.is_eq(rhs) .expect("Equality checking is not allowed to fail.") } } -impl<F> Variable<R1CS<F>> for FpVar<F> +impl<F> Constant<R1CS<F>> for FpVar<F> +where + F: PrimeField, +{ + type Type = F; + + #[inline] + fn new_constant(this: &Self::Type, compiler: &mut R1CS<F>) -> Self { + AllocVar::new_constant(ns!(compiler.cs, "field constant"), this) + .expect("Variable allocation is not allowed to fail.") + } +} + +impl<F> Variable<Public, R1CS<F>> for FpVar<F> where F: PrimeField, { type Type = F; - type Mode = ArkAllocationMode; + #[inline] + fn new_known(this: &Self::Type, compiler: &mut R1CS<F>) -> Self { + Self::new_input(ns!(compiler.cs, "field public input"), full(this)) + .expect("Variable allocation is not allowed to fail.") + } #[inline] - fn new(cs: &mut R1CS<F>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - match allocation { - Allocation::Known(this, ArkAllocationMode::Constant) => { - Self::new_constant(ns!(cs.cs, "prime field constant"), this) - } - Allocation::Known(this, ArkAllocationMode::Public) => { - Self::new_input(ns!(cs.cs, "prime field input"), full(this)) - } - Allocation::Known(this, ArkAllocationMode::Secret) => { - Self::new_witness(ns!(cs.cs, "prime field witness"), full(this)) - } - Allocation::Unknown(PublicOrSecret::Public) => { - Self::new_input(ns!(cs.cs, "prime field input"), empty::<F>) - } - Allocation::Unknown(PublicOrSecret::Secret) => { - Self::new_witness(ns!(cs.cs, "prime field witness"), empty::<F>) - } - } - .expect("Variable allocation is not allowed to fail.") + fn new_unknown(compiler: &mut R1CS<F>) -> Self { + Self::new_input(ns!(compiler.cs, "field public input"), empty::<F>) + .expect("Variable allocation is not allowed to fail.") + } +} + +impl<F> Variable<Secret, R1CS<F>> for FpVar<F> +where + F: PrimeField, +{ + type Type = F; + + #[inline] + fn new_known(this: &Self::Type, compiler: &mut R1CS<F>) -> Self { + Self::new_witness(ns!(compiler.cs, "field secret witness"), full(this)) + .expect("Variable allocation is not allowed to fail.") + } + + #[inline] + fn new_unknown(compiler: &mut R1CS<F>) -> Self { + Self::new_witness(ns!(compiler.cs, "field secret witness"), empty::<F>) + .expect("Variable allocation is not allowed to fail.") } } @@ -238,8 +236,8 @@ where F: PrimeField, { #[inline] - fn eq(cs: &mut R1CS<F>, lhs: &Self, rhs: &Self) -> Boolean<F> { - let _ = cs; + fn eq(lhs: &Self, rhs: &Self, compiler: &mut R1CS<F>) -> Boolean<F> { + let _ = compiler; lhs.is_eq(rhs) .expect("Equality checking is not allowed to fail.") } @@ -262,8 +260,8 @@ where F: PrimeField, { #[inline] - fn add(cs: &mut R1CS<F>, lhs: Self, rhs: Self) -> Self { - let _ = cs; + fn add(lhs: Self, rhs: Self, compiler: &mut R1CS<F>) -> Self { + let _ = compiler; lhs + rhs } } diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs index 053818ab9..24b374766 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs @@ -35,11 +35,11 @@ use { /// Constraint Synthesizer Wrapper struct ConstraintSynthesizerWrapper<F>(R1CS<F>) where - F: ark_ff::Field; + F: ark_ff::PrimeField; impl<F> ConstraintSynthesizer<F> for ConstraintSynthesizerWrapper<F> where - F: ark_ff::Field, + F: ark_ff::PrimeField, { #[inline] fn generate_constraints(self, cs: ConstraintSystemRef<F>) -> SynthesisResult { diff --git a/manta-pay/src/crypto/ecc.rs b/manta-pay/src/crypto/ecc.rs index c4c8f017b..b893b2363 100644 --- a/manta-pay/src/crypto/ecc.rs +++ b/manta-pay/src/crypto/ecc.rs @@ -20,7 +20,7 @@ #[cfg(feature = "arkworks")] #[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] pub mod arkworks { - use crate::crypto::constraint::arkworks::{empty, full, ArkAllocationMode, R1CS}; + use crate::crypto::constraint::arkworks::{empty, full, R1CS}; use alloc::vec::Vec; use ark_ec::ProjectiveCurve; use ark_ff::{Field, PrimeField}; @@ -28,7 +28,7 @@ pub mod arkworks { use ark_relations::ns; use core::marker::PhantomData; use manta_crypto::{ - constraint::{Allocation, PublicOrSecret, Variable}, + constraint::{Constant, Public, Secret, Variable}, ecc, key::kdf, }; @@ -78,6 +78,18 @@ pub mod arkworks { C: ProjectiveCurve, CV: CurveVar<C, ConstraintField<C>>; + impl<C, CV> GroupVar<C, CV> + where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>, + { + /// Builds a new [`GroupVar`] from a given `point`. + #[inline] + pub fn new(point: CV) -> Self { + Self(point, PhantomData) + } + } + impl<C, CV> ecc::Group<Compiler<C>> for GroupVar<C, CV> where C: ProjectiveCurve, @@ -109,37 +121,78 @@ pub mod arkworks { } } - impl<C, CV> Variable<Compiler<C>> for GroupVar<C, CV> + impl<C, CV> Constant<Compiler<C>> for GroupVar<C, CV> where C: ProjectiveCurve, CV: CurveVar<C, ConstraintField<C>>, { type Type = Group<C>; - type Mode = ArkAllocationMode; + #[inline] + fn new_constant(this: &Self::Type, compiler: &mut Compiler<C>) -> Self { + Self::new( + CV::new_constant(ns!(compiler.cs, "embedded curve point constant"), this.0) + .expect("Variable allocation is not allowed to fail."), + ) + } + } + + impl<C, CV> Variable<Public, Compiler<C>> for GroupVar<C, CV> + where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>, + { + type Type = Group<C>; #[inline] - fn new(cs: &mut Compiler<C>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - Self( - match allocation { - Allocation::Known(this, ArkAllocationMode::Constant) => { - CV::new_constant(ns!(cs.cs, ""), this.0) - } - Allocation::Known(this, ArkAllocationMode::Public) => { - CV::new_input(ns!(cs.cs, ""), full(this.0)) - } - Allocation::Known(this, ArkAllocationMode::Secret) => { - CV::new_witness(ns!(cs.cs, ""), full(this.0)) - } - Allocation::Unknown(PublicOrSecret::Public) => { - CV::new_input(ns!(cs.cs, ""), empty::<C>) - } - Allocation::Unknown(PublicOrSecret::Secret) => { - CV::new_witness(ns!(cs.cs, ""), empty::<C>) - } - } + fn new_known(this: &Self::Type, compiler: &mut Compiler<C>) -> Self { + Self::new( + CV::new_input( + ns!(compiler.cs, "embedded curve point public input"), + full(this.0), + ) + .expect("Variable allocation is not allowed to fail."), + ) + } + + #[inline] + fn new_unknown(compiler: &mut Compiler<C>) -> Self { + Self::new( + CV::new_input( + ns!(compiler.cs, "embedded curve point public input"), + empty::<C>, + ) + .expect("Variable allocation is not allowed to fail."), + ) + } + } + + impl<C, CV> Variable<Secret, Compiler<C>> for GroupVar<C, CV> + where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>, + { + type Type = Group<C>; + + #[inline] + fn new_known(this: &Self::Type, compiler: &mut Compiler<C>) -> Self { + Self::new( + CV::new_witness( + ns!(compiler.cs, "embedded curve point secret witness"), + full(this.0), + ) + .expect("Variable allocation is not allowed to fail."), + ) + } + + #[inline] + fn new_unknown(compiler: &mut Compiler<C>) -> Self { + Self::new( + CV::new_witness( + ns!(compiler.cs, "embedded curve point secret witness"), + empty::<C>, + ) .expect("Variable allocation is not allowed to fail."), - PhantomData, ) } } diff --git a/manta-pay/src/crypto/hash/poseidon.rs b/manta-pay/src/crypto/hash/poseidon.rs index ca6c3d20b..032dcd0d4 100644 --- a/manta-pay/src/crypto/hash/poseidon.rs +++ b/manta-pay/src/crypto/hash/poseidon.rs @@ -211,9 +211,8 @@ pub type Output<S, const ARITY: usize, COM = ()> = pub mod arkworks { use crate::crypto::constraint::arkworks::{FpVar, R1CS}; use ark_ff::{Field, PrimeField}; - use ark_r1cs_std::{alloc::AllocVar, fields::FieldVar}; - use ark_relations::ns; - use manta_crypto::constraint::{Allocation, Constant, Variable}; + use ark_r1cs_std::fields::FieldVar; + use manta_crypto::constraint::{Constant, ValueSource}; /// Compiler Type type Compiler<S> = R1CS<<S as Specification>::Field>; @@ -315,32 +314,25 @@ pub mod arkworks { } } - impl<S, const ARITY: usize> Variable<Compiler<S>> for super::Hash<S, ARITY, Compiler<S>> + impl<S, const ARITY: usize> Constant<Compiler<S>> for super::Hash<S, ARITY, Compiler<S>> where S: Specification, { type Type = super::Hash<S, ARITY>; - type Mode = Constant; - #[inline] - fn new(cs: &mut Compiler<S>, allocation: Allocation<Self::Type, Self::Mode>) -> Self { - match allocation { - Allocation::Known(this, _) => Self { - additive_round_keys: this - .additive_round_keys - .iter() - .map(|k| FpVar::new_constant(ns!(cs.cs, ""), k)) - .collect::<Result<Vec<_>, _>>() - .expect("Variable allocation is not allowed to fail."), - mds_matrix: this - .mds_matrix - .iter() - .map(|k| FpVar::new_constant(ns!(cs.cs, ""), k)) - .collect::<Result<Vec<_>, _>>() - .expect("Variable allocation is not allowed to fail."), - }, - _ => unreachable!("Constant variables cannot be unknown."), + fn new_constant(this: &Self::Type, compiler: &mut Compiler<S>) -> Self { + Self { + additive_round_keys: this + .additive_round_keys + .iter() + .map(|k| k.as_constant(compiler)) + .collect(), + mds_matrix: this + .mds_matrix + .iter() + .map(|k| k.as_constant(compiler)) + .collect(), } } } From 562cd49d7175e136a9879f57d87c056a79545ce1 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 6 Jan 2022 11:26:27 -0500 Subject: [PATCH 160/275] wip: prepare for final configuration --- manta-crypto/src/constraint.rs | 29 ++++++++ manta-crypto/src/ecc.rs | 5 +- manta-pay/src/config.rs | 68 +++++++++++++++++-- .../constraint/arkworks/constraint_system.rs | 6 +- manta-pay/src/crypto/ecc.rs | 20 +++++- 5 files changed, 115 insertions(+), 13 deletions(-) diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index 0423e1096..3010a8bb5 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -69,6 +69,18 @@ where fn new_constant(this: &Self::Type, compiler: &mut COM) -> Self; } +impl<COM> Constant<COM> for () +where + COM: ?Sized, +{ + type Type = (); + + #[inline] + fn new_constant(this: &Self::Type, compiler: &mut COM) -> Self { + let _ = (this, compiler); + } +} + impl<T, COM> Constant<COM> for PhantomData<T> where COM: ?Sized, @@ -100,6 +112,23 @@ where fn new_unknown(compiler: &mut COM) -> Self; } +impl<M, COM> Variable<M, COM> for () +where + COM: ?Sized, +{ + type Type = (); + + #[inline] + fn new_known(this: &Self::Type, compiler: &mut COM) -> Self { + let _ = (this, compiler); + } + + #[inline] + fn new_unknown(compiler: &mut COM) -> Self { + let _ = compiler; + } +} + impl<T, M, COM> Variable<M, COM> for PhantomData<T> where COM: ?Sized, diff --git a/manta-crypto/src/ecc.rs b/manta-crypto/src/ecc.rs index f591671f4..1fdacd752 100644 --- a/manta-crypto/src/ecc.rs +++ b/manta-crypto/src/ecc.rs @@ -143,11 +143,12 @@ where impl<G, COM> Constant<COM> for DiffieHellman<G, COM> where G: Group<COM> + Constant<COM>, + G::Type: Group, { - type Type = G::Type; + type Type = DiffieHellman<G::Type>; #[inline] fn new_constant(value: &Self::Type, compiler: &mut COM) -> Self { - Self::new(G::new_constant(value, compiler)) + Self::new(G::new_constant(&value.generator, compiler)) } } diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index e3c63f96c..abad2e02d 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -17,7 +17,7 @@ //! Manta-Pay Configuration use crate::crypto::{ - constraint::arkworks::{FpVar, Groth16, R1CS}, + constraint::arkworks::{Boolean, FpVar, Groth16, R1CS}, ecc, encryption::AesGcm, hash::poseidon, @@ -36,7 +36,10 @@ use manta_accounting::{ use manta_crypto::{ accumulator, commitment::CommitmentScheme, - constraint::{Allocator, Constant, Input as ProofSystemInput, Secret, ValueSource, Variable}, + constraint::{ + Add, Allocator, Constant, Equal, Input as ProofSystemInput, Public, Secret, ValueSource, + Variable, + }, ecc::DiffieHellman, encryption, hash::{BinaryHashFunction, HashFunction}, @@ -212,6 +215,27 @@ impl Constant<Compiler> for VoidNumberHashFunctionVar { /// Asset ID Variable pub struct AssetIdVar(ConstraintFieldVar); +impl Equal<Compiler> for AssetIdVar { + #[inline] + fn eq(lhs: &Self, rhs: &Self, compiler: &mut Compiler) -> Boolean<ConstraintField> { + ConstraintFieldVar::eq(&lhs.0, &rhs.0, compiler) + } +} + +impl Variable<Public, Compiler> for AssetIdVar { + type Type = AssetId; + + #[inline] + fn new_known(this: &Self::Type, compiler: &mut Compiler) -> Self { + Self(ConstraintField::from(this.0).as_known::<Public, _>(compiler)) + } + + #[inline] + fn new_unknown(compiler: &mut Compiler) -> Self { + Self(compiler.allocate_unknown::<Public, _>()) + } +} + impl Variable<Secret, Compiler> for AssetIdVar { type Type = AssetId; @@ -229,6 +253,34 @@ impl Variable<Secret, Compiler> for AssetIdVar { /// Asset Value Variable pub struct AssetValueVar(ConstraintFieldVar); +impl Add<Compiler> for AssetValueVar { + #[inline] + fn add(lhs: Self, rhs: Self, compiler: &mut Compiler) -> Self { + Self(ConstraintFieldVar::add(lhs.0, rhs.0, compiler)) + } +} + +impl Equal<Compiler> for AssetValueVar { + #[inline] + fn eq(lhs: &Self, rhs: &Self, compiler: &mut Compiler) -> Boolean<ConstraintField> { + ConstraintFieldVar::eq(&lhs.0, &rhs.0, compiler) + } +} + +impl Variable<Public, Compiler> for AssetValueVar { + type Type = AssetValue; + + #[inline] + fn new_known(this: &Self::Type, compiler: &mut Compiler) -> Self { + Self(ConstraintField::from(this.0).as_known::<Public, _>(compiler)) + } + + #[inline] + fn new_unknown(compiler: &mut Compiler) -> Self { + Self(compiler.allocate_unknown::<Public, _>()) + } +} + impl Variable<Secret, Compiler> for AssetValueVar { type Type = AssetValue; @@ -331,6 +383,16 @@ impl merkle_tree::Configuration<Compiler> for MerkleTreeConfigurationVar { const HEIGHT: usize = <MerkleTreeConfiguration as merkle_tree::Configuration>::HEIGHT; } +impl Constant<Compiler> for MerkleTreeConfigurationVar { + type Type = MerkleTreeConfiguration; + + #[inline] + fn new_constant(this: &Self::Type, compiler: &mut Compiler) -> Self { + let _ = (this, compiler); + Self + } +} + impl ProofSystemInput<AssetId> for ProofSystem { #[inline] fn extend(input: &mut Self::Input, next: &AssetId) { @@ -375,7 +437,6 @@ pub type NoteEncryptionScheme = encryption::Hybrid< /// Transfer Configuration pub struct TransferConfiguration; -/* impl transfer::Configuration for TransferConfiguration { type SecretKey = <Self::KeyAgreementScheme as key::KeyAgreementScheme>::SecretKey; type PublicKey = <Self::KeyAgreementScheme as key::KeyAgreementScheme>::PublicKey; @@ -410,7 +471,6 @@ impl transfer::Configuration for TransferConfiguration { type NoteEncryptionScheme = NoteEncryptionScheme; } -*/ /* TODO: /// Mint Transfer Type diff --git a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs index 9cd2a2982..6f4381adb 100644 --- a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs @@ -17,9 +17,7 @@ //! Arkworks Constraint System Implementation use ark_ff::PrimeField; -use ark_r1cs_std::{ - alloc::AllocVar, bits::boolean::Boolean, eq::EqGadget, select::CondSelectGadget, -}; +use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget, select::CondSelectGadget}; use ark_relations::{ns, r1cs as ark_r1cs}; use manta_crypto::constraint::{ measure::Measure, Add, ConditionalSelect, Constant, ConstraintSystem, Equal, Public, Secret, @@ -27,7 +25,7 @@ use manta_crypto::constraint::{ }; pub use ark_r1cs::SynthesisError; -pub use ark_r1cs_std::fields::fp::FpVar; +pub use ark_r1cs_std::{bits::boolean::Boolean, fields::fp::FpVar}; /// Synthesis Result pub type SynthesisResult<T = ()> = Result<T, SynthesisError>; diff --git a/manta-pay/src/crypto/ecc.rs b/manta-pay/src/crypto/ecc.rs index b893b2363..c53955099 100644 --- a/manta-pay/src/crypto/ecc.rs +++ b/manta-pay/src/crypto/ecc.rs @@ -20,15 +20,15 @@ #[cfg(feature = "arkworks")] #[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] pub mod arkworks { - use crate::crypto::constraint::arkworks::{empty, full, R1CS}; + use crate::crypto::constraint::arkworks::{empty, full, Boolean, FpVar, R1CS}; use alloc::vec::Vec; use ark_ec::ProjectiveCurve; use ark_ff::{Field, PrimeField}; - use ark_r1cs_std::{fields::fp::FpVar, groups::CurveVar, ToBitsGadget}; + use ark_r1cs_std::{groups::CurveVar, ToBitsGadget}; use ark_relations::ns; use core::marker::PhantomData; use manta_crypto::{ - constraint::{Constant, Public, Secret, Variable}, + constraint::{Constant, Equal, Public, Secret, Variable}, ecc, key::kdf, }; @@ -121,6 +121,20 @@ pub mod arkworks { } } + impl<C, CV> Equal<Compiler<C>> for GroupVar<C, CV> + where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>, + { + #[inline] + fn eq(lhs: &Self, rhs: &Self, compiler: &mut Compiler<C>) -> Boolean<ConstraintField<C>> { + let _ = compiler; + lhs.0 + .is_eq(&rhs.0) + .expect("Equality check is not allowed to fail.") + } + } + impl<C, CV> Constant<Compiler<C>> for GroupVar<C, CV> where C: ProjectiveCurve, From 724d29363fbe43d389ad9c86268ba5b07a168e99 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 6 Jan 2022 13:37:33 -0500 Subject: [PATCH 161/275] feat: finish transfer configuration --- manta-pay/src/config.rs | 18 ++--- manta-pay/src/crypto/ecc.rs | 157 +++++++++++++++++++++++++++++++++--- 2 files changed, 155 insertions(+), 20 deletions(-) diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index abad2e02d..ebca6198b 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -18,13 +18,11 @@ use crate::crypto::{ constraint::arkworks::{Boolean, FpVar, Groth16, R1CS}, - ecc, + ecc::{self, arkworks::ProjectiveCurve}, encryption::AesGcm, hash::poseidon, key::Blake2sKdf, }; -use ark_ec::ProjectiveCurve; -use ark_ff::{BigInteger, PrimeField}; use bls12_381::Bls12_381; use bls12_381_ed::{ constraints::EdwardsVar as Bls12_381_EdwardsVar, EdwardsProjective as Bls12_381_Edwards, @@ -176,7 +174,7 @@ impl BinaryHashFunction for VoidNumberHashFunction { left, // FIXME: This is the lift from inner scalar to outer scalar and only exists in some // cases! We need a better abstraction for this. - &ConstraintField::from_le_bytes_mod_order(&right.into_repr().to_bytes_le()), + &ecc::arkworks::lift_embedded_scalar::<Bls12_381_Edwards>(right), ]) } } @@ -199,7 +197,7 @@ impl BinaryHashFunction<Compiler> for VoidNumberHashFunctionVar { right: &Self::Right, compiler: &mut Compiler, ) -> Self::Output { - self.0.hash_in([left, right], compiler) + self.0.hash_in([left, &right.0], compiler) } } @@ -472,16 +470,14 @@ impl transfer::Configuration for TransferConfiguration { type NoteEncryptionScheme = NoteEncryptionScheme; } -/* TODO: /// Mint Transfer Type -pub struct Mint = transfer::canonical::Mint<TransferConfiguration>; +pub type Mint = transfer::canonical::Mint<TransferConfiguration>; /// Private Transfer Type -pub struct PrivateTransfer = transfer::canonical::PrivateTransfer<TransferConfiguration>; +pub type PrivateTransfer = transfer::canonical::PrivateTransfer<TransferConfiguration>; /// Reclaim Transfer Type -pub struct Reclaim = transfer::canonical::Reclaim<TransferConfiguration>; +pub type Reclaim = transfer::canonical::Reclaim<TransferConfiguration>; /// Transfer Post Type -pub struct TransferPost = transfer::TransferPost<TransferConfiguration>; -*/ +pub type TransferPost = transfer::TransferPost<TransferConfiguration>; diff --git a/manta-pay/src/crypto/ecc.rs b/manta-pay/src/crypto/ecc.rs index c53955099..b619301e7 100644 --- a/manta-pay/src/crypto/ecc.rs +++ b/manta-pay/src/crypto/ecc.rs @@ -22,23 +22,90 @@ pub mod arkworks { use crate::crypto::constraint::arkworks::{empty, full, Boolean, FpVar, R1CS}; use alloc::vec::Vec; - use ark_ec::ProjectiveCurve; - use ark_ff::{Field, PrimeField}; - use ark_r1cs_std::{groups::CurveVar, ToBitsGadget}; + use ark_ff::{BigInteger, Field, FpParameters, PrimeField, UniformRand}; + use ark_r1cs_std::ToBitsGadget; use ark_relations::ns; use core::marker::PhantomData; use manta_crypto::{ - constraint::{Constant, Equal, Public, Secret, Variable}, + constraint::{Allocator, Constant, Equal, Public, Secret, ValueSource, Variable}, ecc, key::kdf, + rand::{CryptoRng, RngCore, Sample, Standard}, }; + pub use ark_ec::ProjectiveCurve; + pub use ark_r1cs_std::groups::CurveVar; + /// Constraint Field Type type ConstraintField<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField; /// Compiler Type type Compiler<C> = R1CS<ConstraintField<C>>; + /// Converts `scalar` to the bit representation of `O`. + #[inline] + pub fn convert_bits<T, O>(scalar: T) -> O::BigInt + where + T: BigInteger, + O: PrimeField, + { + O::BigInt::from_bits_le(&scalar.to_bits_le()) + } + + /// Checks that the modulus of `A` is smaller than that of `B`. + #[inline] + pub fn modulus_is_smaller<A, B>() -> bool + where + A: PrimeField, + B: PrimeField, + { + let modulus_a = A::Params::MODULUS; + let modulus_b = B::Params::MODULUS; + if modulus_a.num_bits() <= modulus_b.num_bits() { + convert_bits::<_, B>(modulus_a) < modulus_b + } else { + modulus_a < convert_bits::<_, A>(modulus_b) + } + } + + /// Lifts an embedded scalar to an outer scalar. + /// + /// # Safety + /// + /// This can only be used whenver the embedded scalar field is **smaller** than the outer scalar + /// field. + #[inline] + pub fn lift_embedded_scalar<C>(scalar: &Scalar<C>) -> ConstraintField<C> + where + C: ProjectiveCurve, + { + assert!( + modulus_is_smaller::<C::ScalarField, ConstraintField<C>>(), + "The modulus of the embedded scalar field is larger than that of the constraint field." + ); + ConstraintField::<C>::from_le_bytes_mod_order(&scalar.0.into_repr().to_bytes_le()) + } + + /// Elliptic Curve Scalar Element + #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct Scalar<C>(C::ScalarField) + where + C: ProjectiveCurve; + + impl<C> Sample for Scalar<C> + where + C: ProjectiveCurve, + { + #[inline] + fn sample<R>(distribution: Standard, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + let _ = distribution; + Self(C::ScalarField::rand(rng)) + } + } + /// Elliptic Curve Group Element #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Group<C>(pub(crate) C) @@ -59,7 +126,7 @@ pub mod arkworks { where C: ProjectiveCurve, { - type Scalar = C::ScalarField; + type Scalar = Scalar<C>; #[inline] fn add(&self, rhs: &Self, _: &mut ()) -> Self { @@ -68,7 +135,79 @@ pub mod arkworks { #[inline] fn scalar_mul(&self, scalar: &Self::Scalar, _: &mut ()) -> Self { - Self(self.0.mul(scalar.into_repr())) + Self(self.0.mul(scalar.0.into_repr())) + } + } + + /// Elliptic Curve Scalar Element Variable + /// + /// # Safety + /// + /// This type can only be used whenever the embedded scalar field is **smaller** than the + /// outer scalar field. + pub struct ScalarVar<C, CV>(pub(crate) FpVar<ConstraintField<C>>, PhantomData<CV>) + where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>; + + impl<C, CV> ScalarVar<C, CV> + where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>, + { + /// Builds a new [`ScalarVar`] from a given `scalar`. + #[inline] + fn new(scalar: FpVar<ConstraintField<C>>) -> Self { + Self(scalar, PhantomData) + } + } + + impl<C, CV> Constant<Compiler<C>> for ScalarVar<C, CV> + where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>, + { + type Type = Scalar<C>; + + #[inline] + fn new_constant(this: &Self::Type, compiler: &mut Compiler<C>) -> Self { + Self::new(lift_embedded_scalar::<C>(this).as_constant(compiler)) + } + } + + impl<C, CV> Variable<Public, Compiler<C>> for ScalarVar<C, CV> + where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>, + { + type Type = Scalar<C>; + + #[inline] + fn new_known(this: &Self::Type, compiler: &mut Compiler<C>) -> Self { + Self::new(lift_embedded_scalar::<C>(this).as_known::<Public, _>(compiler)) + } + + #[inline] + fn new_unknown(compiler: &mut Compiler<C>) -> Self { + Self::new(compiler.allocate_unknown::<Public, _>()) + } + } + + impl<C, CV> Variable<Secret, Compiler<C>> for ScalarVar<C, CV> + where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>, + { + type Type = Scalar<C>; + + #[inline] + fn new_known(this: &Self::Type, compiler: &mut Compiler<C>) -> Self { + Self::new(lift_embedded_scalar::<C>(this).as_known::<Secret, _>(compiler)) + } + + #[inline] + fn new_unknown(compiler: &mut Compiler<C>) -> Self { + Self::new(compiler.allocate_unknown::<Secret, _>()) } } @@ -85,7 +224,7 @@ pub mod arkworks { { /// Builds a new [`GroupVar`] from a given `point`. #[inline] - pub fn new(point: CV) -> Self { + fn new(point: CV) -> Self { Self(point, PhantomData) } } @@ -95,8 +234,7 @@ pub mod arkworks { C: ProjectiveCurve, CV: CurveVar<C, ConstraintField<C>>, { - // FIXME: This should be a "subtype" of this field (whenever we have an actual injection). - type Scalar = FpVar<ConstraintField<C>>; + type Scalar = ScalarVar<C, CV>; #[inline] fn add(&self, rhs: &Self, compiler: &mut Compiler<C>) -> Self { @@ -111,6 +249,7 @@ pub mod arkworks { self.0 .scalar_mul_le( scalar + .0 .to_bits_le() .expect("Bit decomposition is not allowed to fail.") .iter(), From 407f33398109de0bea09826016eac03ade6a4da9 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 6 Jan 2022 15:51:34 -0500 Subject: [PATCH 162/275] feat: setup proving/verifying key generation tests --- manta-accounting/src/transfer/mod.rs | 43 +++++-- manta-accounting/src/transfer/test.rs | 54 ++++++++ manta-crypto/src/ecc.rs | 60 ++++----- manta-crypto/src/rand.rs | 75 ++++++++++- manta-pay/src/config.rs | 121 ++++++++++++++---- .../constraint/arkworks/constraint_system.rs | 41 ++++-- manta-pay/src/crypto/ecc.rs | 29 ++++- manta-pay/src/crypto/hash/poseidon.rs | 89 +++++++++++-- manta-pay/src/test.rs | 14 +- 9 files changed, 412 insertions(+), 114 deletions(-) create mode 100644 manta-accounting/src/transfer/test.rs diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index eae17049b..13113299f 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -36,6 +36,10 @@ use manta_util::from_variant_impl; pub mod batch; pub mod canonical; +#[cfg(feature = "test")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] +pub mod test; + /// Returns `true` if the transfer with this shape would have public participants. #[inline] pub const fn has_public_participants( @@ -1358,6 +1362,25 @@ where } } + /// Builds a constraint system which asserts constraints against unknown variables. + #[inline] + pub fn unknown_constraints(parameters: FullParameters<C>) -> C::Compiler { + let mut compiler = C::ProofSystem::for_unknown(); + TransferVar::<C, SOURCES, SENDERS, RECEIVERS, SINKS>::new_unknown(&mut compiler) + .build_validity_constraints(&parameters.as_constant(&mut compiler), &mut compiler); + compiler + } + + /// Builds a constraint system which asserts constraints against known variables. + #[inline] + pub fn known_constraints(&self, parameters: FullParameters<C>) -> C::Compiler { + let mut compiler = C::ProofSystem::for_known(); + let transfer: TransferVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> = + self.as_known(&mut compiler); + transfer.build_validity_constraints(&parameters.as_constant(&mut compiler), &mut compiler); + compiler + } + /// Generates a proving and verifying context for this transfer shape. #[inline] pub fn generate_context<R>( @@ -1367,10 +1390,7 @@ where where R: CryptoRng + RngCore + ?Sized, { - let mut compiler = C::ProofSystem::for_unknown(); - TransferVar::<C, SOURCES, SENDERS, RECEIVERS, SINKS>::new_unknown(&mut compiler) - .build_validity_constraints(&parameters.as_constant(&mut compiler), &mut compiler); - C::ProofSystem::generate_context(compiler, rng) + C::ProofSystem::generate_context(Self::unknown_constraints(parameters), rng) } /// Converts `self` into its ledger post. @@ -1385,16 +1405,11 @@ where R: CryptoRng + RngCore + ?Sized, { Ok(TransferPost { - validity_proof: { - let mut compiler = C::ProofSystem::for_known(); - let transfer: TransferVar<C, SOURCES, SENDERS, RECEIVERS, SINKS> = - self.as_known(&mut compiler); - transfer.build_validity_constraints( - &parameters.as_constant(&mut compiler), - &mut compiler, - ); - C::ProofSystem::prove(compiler, context, rng)? - }, + validity_proof: C::ProofSystem::prove( + self.known_constraints(parameters), + context, + rng, + )?, asset_id: self.asset_id, sources: self.sources.into(), sender_posts: IntoIterator::into_iter(self.senders) diff --git a/manta-accounting/src/transfer/test.rs b/manta-accounting/src/transfer/test.rs new file mode 100644 index 000000000..1cb293614 --- /dev/null +++ b/manta-accounting/src/transfer/test.rs @@ -0,0 +1,54 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Transfer Protocol Testing Framework + +use crate::transfer::{Configuration, Parameters}; +use manta_crypto::rand::{CryptoRng, Rand, RngCore, Sample, Standard}; + +/// Parameters Distribution +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +pub struct ParametersDistribution<K = Standard, U = Standard, V = Standard> { + /// Key Agreement Scheme Distribution + pub key_agreement: K, + + /// UTXO Commitment Scheme Distribution + pub utxo_commitment: U, + + /// Void Number Hash Function Distribution + pub void_number_hash: V, +} + +impl<K, U, V, C> Sample<ParametersDistribution<K, U, V>> for Parameters<C> +where + C: Configuration + ?Sized, + C::KeyAgreementScheme: Sample<K>, + C::UtxoCommitmentScheme: Sample<U>, + C::VoidNumberHashFunction: Sample<V>, +{ + #[inline] + fn sample<R>(distribution: ParametersDistribution<K, U, V>, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + Parameters::new( + rng.sample(distribution.key_agreement), + rng.sample(distribution.utxo_commitment), + rng.sample(distribution.void_number_hash), + ) + } +} diff --git a/manta-crypto/src/ecc.rs b/manta-crypto/src/ecc.rs index 1fdacd752..c4720ff58 100644 --- a/manta-crypto/src/ecc.rs +++ b/manta-crypto/src/ecc.rs @@ -18,36 +18,13 @@ // TODO: Improve ECC abstractions over arkworks. -use crate::{constraint::Constant, key::KeyAgreementScheme}; +use crate::{ + constraint::Constant, + key::KeyAgreementScheme, + rand::{CryptoRng, RngCore, Sample}, +}; use core::marker::PhantomData; -/* TODO: -/// Elliptic Curve Coordinate System -pub trait Coordinates<C, COM = ()> -where - C: Curve<COM>, -{ -} - -/// Elliptic Curve -pub trait Curve<COM = ()> { - /// Base Field - type BaseField; - - /// Scalar Field - type ScalarField; -} - -/// Embedded Elliptic Curve -pub trait EmbeddedCurve<C, COM = ()>: Curve<COM, BaseField = C::ScalarField> -where - C: Curve<COM>, -{ - /// - fn lift_scalar(scalar: Self::ScalarField, compiler: &mut COM) -> C::ScalarField; -} -*/ - /// Elliptic Curve Group pub trait Group<COM = ()>: Sized { /// Scalar Field @@ -114,6 +91,19 @@ where } } +impl<G, COM> Constant<COM> for DiffieHellman<G, COM> +where + G: Group<COM> + Constant<COM>, + G::Type: Group, +{ + type Type = DiffieHellman<G::Type>; + + #[inline] + fn new_constant(value: &Self::Type, compiler: &mut COM) -> Self { + Self::new(G::new_constant(&value.generator, compiler)) + } +} + impl<G, COM> KeyAgreementScheme<COM> for DiffieHellman<G, COM> where G: Group<COM>, @@ -140,15 +130,15 @@ where } } -impl<G, COM> Constant<COM> for DiffieHellman<G, COM> +impl<D, G, COM> Sample<D> for DiffieHellman<G, COM> where - G: Group<COM> + Constant<COM>, - G::Type: Group, + G: Group<COM> + Sample<D>, { - type Type = DiffieHellman<G::Type>; - #[inline] - fn new_constant(value: &Self::Type, compiler: &mut COM) -> Self { - Self::new(G::new_constant(&value.generator, compiler)) + fn sample<R>(distribution: D, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + Self::new(G::sample(distribution, rng)) } } diff --git a/manta-crypto/src/rand.rs b/manta-crypto/src/rand.rs index 228315080..6c67feea6 100644 --- a/manta-crypto/src/rand.rs +++ b/manta-crypto/src/rand.rs @@ -16,6 +16,8 @@ //! Random Number Generators +// TODO: Add a `Sample` derive trait. + use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash, iter::repeat, marker::PhantomData}; use manta_util::into_array_unchecked; @@ -252,14 +254,65 @@ where R: CryptoRng + RngCore + ?Sized, { into_array_unchecked( - repeat(distribution) - .take(N) - .map(move |d| T::sample(d, rng)) + rng.sample_iter(repeat(distribution).take(N)) .collect::<Vec<_>>(), ) } } +/// Distribution Iterator +pub struct DistIter<'r, D, T, R> +where + D: Iterator, + T: Sample<D::Item>, + R: CryptoRng + RngCore + ?Sized, +{ + /// Distribution Iterator + iter: D, + + /// Random Number Generator + rng: &'r mut R, + + /// Type Parameter Marker + __: PhantomData<T>, +} + +impl<'r, D, T, R> DistIter<'r, D, T, R> +where + D: Iterator, + T: Sample<D::Item>, + R: CryptoRng + RngCore + ?Sized, +{ + /// Builds a new [`DistIter`] from `iter` and `rng`. + #[inline] + fn new(iter: D, rng: &'r mut R) -> Self { + Self { + iter, + rng, + __: PhantomData, + } + } +} + +impl<'r, D, T, R> Iterator for DistIter<'r, D, T, R> +where + D: Iterator, + T: Sample<D::Item>, + R: CryptoRng + RngCore + ?Sized, +{ + type Item = T; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + self.iter.next().map(|d| self.rng.sample(d)) + } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + self.iter.size_hint() + } +} + /// Fallible Sampling Trait pub trait TrySample<D = Standard>: Sized { /// Error Type @@ -286,7 +339,7 @@ pub trait TrySample<D = Standard>: Sized { /// Random Number Generator pub trait Rand: CryptoRng + RngCore { /// Returns a random value of type `Self`, sampled according to the given `distribution`, - /// generated from the `rng`. + /// generated from `self`. #[inline] fn sample<D, T>(&mut self, distribution: D) -> T where @@ -295,8 +348,18 @@ pub trait Rand: CryptoRng + RngCore { T::sample(distribution, self) } + /// Returns an iterator over `iter` which samples from `self`. + #[inline] + fn sample_iter<D, T>(&mut self, iter: D) -> DistIter<D, T, Self> + where + D: Iterator, + T: Sample<D::Item>, + { + DistIter::new(iter, self) + } + /// Tries to return a random value of type `Self`, sampled according to the given - /// `distribution`, generated from the `rng`. + /// `distribution`, generated from `self`. #[inline] fn try_sample<D, T>(&mut self, distribution: D) -> Result<T, T::Error> where @@ -306,7 +369,7 @@ pub trait Rand: CryptoRng + RngCore { } /// Returns a random value of type `Self`, sampled according to the default distribution of - /// type `D`, generated from the `rng`. + /// type `D`, generated from `rng`. #[inline] fn gen<D, T>(&mut self) -> T where diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index ebca6198b..8d4b5bbdc 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -17,7 +17,7 @@ //! Manta-Pay Configuration use crate::crypto::{ - constraint::arkworks::{Boolean, FpVar, Groth16, R1CS}, + constraint::arkworks::{Boolean, Fp, FpVar, Groth16, R1CS}, ecc::{self, arkworks::ProjectiveCurve}, encryption::AesGcm, hash::poseidon, @@ -44,6 +44,9 @@ use manta_crypto::{ key, merkle_tree, }; +#[cfg(test)] +use manta_crypto::rand::{CryptoRng, Rand, RngCore, Sample, Standard}; + #[doc(inline)] pub use ark_bls12_381 as bls12_381; #[doc(inline)] @@ -73,6 +76,12 @@ pub type ProofSystem = Groth16<PairingCurve>; /// Poseidon Specification pub struct PoseidonSpec<const ARITY: usize>; +/// Poseidon-2 Hash Parameters +pub type Poseidon2 = poseidon::Hash<PoseidonSpec<2>, 2>; + +/// Poseidon-2 Hash Parameters Variable +pub type Poseidon2Var = poseidon::Hash<PoseidonSpec<2>, 2, Compiler>; + impl poseidon::arkworks::Specification for PoseidonSpec<2> { type Field = ConstraintField; const FULL_ROUNDS: usize = 10; @@ -80,6 +89,12 @@ impl poseidon::arkworks::Specification for PoseidonSpec<2> { const SBOX_EXPONENT: u64 = 5; } +/// Poseidon-4 Hash Parameters +pub type Poseidon4 = poseidon::Hash<PoseidonSpec<4>, 4>; + +/// Poseidon-4 Hash Parameters Variable +pub type Poseidon4Var = poseidon::Hash<PoseidonSpec<4>, 4, Compiler>; + impl poseidon::arkworks::Specification for PoseidonSpec<4> { type Field = ConstraintField; const FULL_ROUNDS: usize = 10; @@ -94,10 +109,10 @@ pub type KeyAgreementScheme = DiffieHellman<Group>; pub type KeyAgreementSchemeVar = DiffieHellman<GroupVar, Compiler>; /// Unspent Transaction Output Type -pub type Utxo = poseidon::Output<PoseidonSpec<4>, 4>; +pub type Utxo = Fp<ConstraintField>; /// UTXO Commitment Scheme -pub struct UtxoCommitmentScheme(pub poseidon::Hash<PoseidonSpec<4>, 4>); +pub struct UtxoCommitmentScheme(pub Poseidon4); impl CommitmentScheme for UtxoCommitmentScheme { type Trapdoor = Group; @@ -114,19 +129,30 @@ impl CommitmentScheme for UtxoCommitmentScheme { // NOTE: The group is in projective form, so we need to convert it first. let trapdoor = trapdoor.0.into_affine(); self.0.hash([ - &trapdoor.x, - &trapdoor.y, - &input.id.0.into(), - &input.value.0.into(), + &Fp(trapdoor.x), + &Fp(trapdoor.y), + &Fp(input.id.0.into()), + &Fp(input.value.0.into()), ]) } } +#[cfg(test)] // NOTE: This is only safe in a test. +impl Sample for UtxoCommitmentScheme { + #[inline] + fn sample<R>(distribution: Standard, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + rng.sample(distribution) + } +} + /// Unspent Transaction Output Variable Type -pub type UtxoVar = poseidon::Output<PoseidonSpec<4>, 4, Compiler>; +pub type UtxoVar = ConstraintFieldVar; /// UTXO Commitment Scheme Variable -pub struct UtxoCommitmentSchemeVar(pub poseidon::Hash<PoseidonSpec<4>, 4, Compiler>); +pub struct UtxoCommitmentSchemeVar(pub Poseidon4Var); impl CommitmentScheme<Compiler> for UtxoCommitmentSchemeVar { type Trapdoor = GroupVar; @@ -158,10 +184,10 @@ impl Constant<Compiler> for UtxoCommitmentSchemeVar { } /// Void Number Type -pub type VoidNumber = poseidon::Output<PoseidonSpec<2>, 2>; +pub type VoidNumber = Fp<ConstraintField>; /// Void Number Hash Function -pub struct VoidNumberHashFunction(pub poseidon::Hash<PoseidonSpec<2>, 2>); +pub struct VoidNumberHashFunction(pub Poseidon2); impl BinaryHashFunction for VoidNumberHashFunction { type Left = Utxo; @@ -179,16 +205,27 @@ impl BinaryHashFunction for VoidNumberHashFunction { } } +#[cfg(test)] // NOTE: This is only safe in a test. +impl Sample for VoidNumberHashFunction { + #[inline] + fn sample<R>(distribution: Standard, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + rng.sample(distribution) + } +} + /// Void Number Variable Type -pub type VoidNumberVar = poseidon::Output<PoseidonSpec<2>, 2, Compiler>; +pub type VoidNumberVar = ConstraintFieldVar; /// Void Number Hash Function Variable -pub struct VoidNumberHashFunctionVar(pub poseidon::Hash<PoseidonSpec<2>, 2, Compiler>); +pub struct VoidNumberHashFunctionVar(pub Poseidon2Var); impl BinaryHashFunction<Compiler> for VoidNumberHashFunctionVar { type Left = <UtxoCommitmentSchemeVar as CommitmentScheme<Compiler>>::Output; type Right = <KeyAgreementSchemeVar as key::KeyAgreementScheme<Compiler>>::SecretKey; - type Output = poseidon::Output<PoseidonSpec<2>, 2, Compiler>; + type Output = ConstraintFieldVar; #[inline] fn hash_in( @@ -225,7 +262,7 @@ impl Variable<Public, Compiler> for AssetIdVar { #[inline] fn new_known(this: &Self::Type, compiler: &mut Compiler) -> Self { - Self(ConstraintField::from(this.0).as_known::<Public, _>(compiler)) + Self(Fp(ConstraintField::from(this.0)).as_known::<Public, _>(compiler)) } #[inline] @@ -239,7 +276,7 @@ impl Variable<Secret, Compiler> for AssetIdVar { #[inline] fn new_known(this: &Self::Type, compiler: &mut Compiler) -> Self { - Self(ConstraintField::from(this.0).as_known::<Secret, _>(compiler)) + Self(Fp(ConstraintField::from(this.0)).as_known::<Secret, _>(compiler)) } #[inline] @@ -270,7 +307,7 @@ impl Variable<Public, Compiler> for AssetValueVar { #[inline] fn new_known(this: &Self::Type, compiler: &mut Compiler) -> Self { - Self(ConstraintField::from(this.0).as_known::<Public, _>(compiler)) + Self(Fp(ConstraintField::from(this.0)).as_known::<Public, _>(compiler)) } #[inline] @@ -284,7 +321,7 @@ impl Variable<Secret, Compiler> for AssetValueVar { #[inline] fn new_known(this: &Self::Type, compiler: &mut Compiler) -> Self { - Self(ConstraintField::from(this.0).as_known::<Secret, _>(compiler)) + Self(Fp(ConstraintField::from(this.0)).as_known::<Secret, _>(compiler)) } #[inline] @@ -304,8 +341,8 @@ pub struct InnerHash; impl merkle_tree::InnerHash for InnerHash { type LeafDigest = Utxo; - type Parameters = poseidon::Hash<PoseidonSpec<2>, 2>; - type Output = poseidon::Output<PoseidonSpec<2>, 2>; + type Parameters = Poseidon2; + type Output = Fp<ConstraintField>; #[inline] fn join_in( @@ -333,8 +370,8 @@ pub struct InnerHashVar; impl merkle_tree::InnerHash<Compiler> for InnerHashVar { type LeafDigest = UtxoVar; - type Parameters = poseidon::Hash<PoseidonSpec<2>, 2, Compiler>; - type Output = poseidon::Output<PoseidonSpec<2>, 2, Compiler>; + type Parameters = Poseidon2Var; + type Output = ConstraintFieldVar; #[inline] fn join_in( @@ -369,6 +406,34 @@ impl merkle_tree::Configuration for MerkleTreeConfiguration { const HEIGHT: usize = 20; } +#[cfg(test)] +impl merkle_tree::test::HashParameterSampling for MerkleTreeConfiguration { + type LeafHashParameterDistribution = Standard; + type InnerHashParameterDistribution = Standard; + + #[inline] + fn sample_leaf_hash_parameters<R>( + distribution: Self::LeafHashParameterDistribution, + rng: &mut R, + ) -> merkle_tree::LeafHashParameters<Self> + where + R: CryptoRng + RngCore + ?Sized, + { + let _ = (distribution, rng); + } + + #[inline] + fn sample_inner_hash_parameters<R>( + distribution: Self::InnerHashParameterDistribution, + rng: &mut R, + ) -> merkle_tree::InnerHashParameters<Self> + where + R: CryptoRng + RngCore + ?Sized, + { + rng.sample(distribution) + } +} + /// Merkle Tree Variable Configuration pub struct MerkleTreeConfigurationVar; @@ -405,10 +470,10 @@ impl ProofSystemInput<AssetValue> for ProofSystem { } } -impl ProofSystemInput<ConstraintField> for ProofSystem { +impl ProofSystemInput<Fp<ConstraintField>> for ProofSystem { #[inline] - fn extend(input: &mut Self::Input, next: &ConstraintField) { - input.push(*next); + fn extend(input: &mut Self::Input, next: &Fp<ConstraintField>) { + input.push(next.0); } } @@ -470,6 +535,12 @@ impl transfer::Configuration for TransferConfiguration { type NoteEncryptionScheme = NoteEncryptionScheme; } +/// Transfer Parameters +pub type Parameters = transfer::Parameters<TransferConfiguration>; + +/// Full Transfer Parameters +pub type FullParameters<'p> = transfer::FullParameters<'p, TransferConfiguration>; + /// Mint Transfer Type pub type Mint = transfer::canonical::Mint<TransferConfiguration>; diff --git a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs index 6f4381adb..36771d89e 100644 --- a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs @@ -19,14 +19,37 @@ use ark_ff::PrimeField; use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget, select::CondSelectGadget}; use ark_relations::{ns, r1cs as ark_r1cs}; -use manta_crypto::constraint::{ - measure::Measure, Add, ConditionalSelect, Constant, ConstraintSystem, Equal, Public, Secret, - Variable, +use manta_crypto::{ + constraint::{ + measure::Measure, Add, ConditionalSelect, Constant, ConstraintSystem, Equal, Public, + Secret, Variable, + }, + rand::{CryptoRng, RngCore, Sample, Standard}, }; pub use ark_r1cs::SynthesisError; pub use ark_r1cs_std::{bits::boolean::Boolean, fields::fp::FpVar}; +/// Prime Field Element +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Fp<F>(pub F) +where + F: PrimeField; + +impl<F> Sample for Fp<F> +where + F: PrimeField, +{ + #[inline] + fn sample<R>(distribution: Standard, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + let _ = distribution; + Self(F::rand(rng)) + } +} + /// Synthesis Result pub type SynthesisResult<T = ()> = Result<T, SynthesisError>; @@ -182,11 +205,11 @@ impl<F> Constant<R1CS<F>> for FpVar<F> where F: PrimeField, { - type Type = F; + type Type = Fp<F>; #[inline] fn new_constant(this: &Self::Type, compiler: &mut R1CS<F>) -> Self { - AllocVar::new_constant(ns!(compiler.cs, "field constant"), this) + AllocVar::new_constant(ns!(compiler.cs, "field constant"), this.0) .expect("Variable allocation is not allowed to fail.") } } @@ -195,11 +218,11 @@ impl<F> Variable<Public, R1CS<F>> for FpVar<F> where F: PrimeField, { - type Type = F; + type Type = Fp<F>; #[inline] fn new_known(this: &Self::Type, compiler: &mut R1CS<F>) -> Self { - Self::new_input(ns!(compiler.cs, "field public input"), full(this)) + Self::new_input(ns!(compiler.cs, "field public input"), full(this.0)) .expect("Variable allocation is not allowed to fail.") } @@ -214,11 +237,11 @@ impl<F> Variable<Secret, R1CS<F>> for FpVar<F> where F: PrimeField, { - type Type = F; + type Type = Fp<F>; #[inline] fn new_known(this: &Self::Type, compiler: &mut R1CS<F>) -> Self { - Self::new_witness(ns!(compiler.cs, "field secret witness"), full(this)) + Self::new_witness(ns!(compiler.cs, "field secret witness"), full(this.0)) .expect("Variable allocation is not allowed to fail.") } diff --git a/manta-pay/src/crypto/ecc.rs b/manta-pay/src/crypto/ecc.rs index b619301e7..2b8a64c1b 100644 --- a/manta-pay/src/crypto/ecc.rs +++ b/manta-pay/src/crypto/ecc.rs @@ -20,9 +20,9 @@ #[cfg(feature = "arkworks")] #[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] pub mod arkworks { - use crate::crypto::constraint::arkworks::{empty, full, Boolean, FpVar, R1CS}; + use crate::crypto::constraint::arkworks::{empty, full, Boolean, Fp, FpVar, R1CS}; use alloc::vec::Vec; - use ark_ff::{BigInteger, Field, FpParameters, PrimeField, UniformRand}; + use ark_ff::{BigInteger, Field, FpParameters, PrimeField}; use ark_r1cs_std::ToBitsGadget; use ark_relations::ns; use core::marker::PhantomData; @@ -42,6 +42,9 @@ pub mod arkworks { /// Compiler Type type Compiler<C> = R1CS<ConstraintField<C>>; + /// Scalar Field Element + pub type Scalar<C> = Fp<<C as ProjectiveCurve>::ScalarField>; + /// Converts `scalar` to the bit representation of `O`. #[inline] pub fn convert_bits<T, O>(scalar: T) -> O::BigInt @@ -75,7 +78,7 @@ pub mod arkworks { /// This can only be used whenver the embedded scalar field is **smaller** than the outer scalar /// field. #[inline] - pub fn lift_embedded_scalar<C>(scalar: &Scalar<C>) -> ConstraintField<C> + pub fn lift_embedded_scalar<C>(scalar: &Scalar<C>) -> Fp<ConstraintField<C>> where C: ProjectiveCurve, { @@ -83,9 +86,12 @@ pub mod arkworks { modulus_is_smaller::<C::ScalarField, ConstraintField<C>>(), "The modulus of the embedded scalar field is larger than that of the constraint field." ); - ConstraintField::<C>::from_le_bytes_mod_order(&scalar.0.into_repr().to_bytes_le()) + Fp(ConstraintField::<C>::from_le_bytes_mod_order( + &scalar.0.into_repr().to_bytes_le(), + )) } + /* TODO[remove]: /// Elliptic Curve Scalar Element #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Scalar<C>(C::ScalarField) @@ -105,6 +111,7 @@ pub mod arkworks { Self(C::ScalarField::rand(rng)) } } + */ /// Elliptic Curve Group Element #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -139,6 +146,20 @@ pub mod arkworks { } } + impl<C> Sample for Group<C> + where + C: ProjectiveCurve, + { + #[inline] + fn sample<R>(distribution: Standard, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + let _ = distribution; + Self(C::rand(rng)) + } + } + /// Elliptic Curve Scalar Element Variable /// /// # Safety diff --git a/manta-pay/src/crypto/hash/poseidon.rs b/manta-pay/src/crypto/hash/poseidon.rs index 032dcd0d4..446eb52b8 100644 --- a/manta-pay/src/crypto/hash/poseidon.rs +++ b/manta-pay/src/crypto/hash/poseidon.rs @@ -23,6 +23,12 @@ use alloc::vec::Vec; use core::{iter, mem}; use manta_crypto::hash::HashFunction; +#[cfg(test)] +use { + core::iter::repeat, + manta_crypto::rand::{CryptoRng, Rand, RngCore, Sample}, +}; + /// Poseidon Permutation Specification pub trait Specification<COM = ()> { /// Field Type @@ -81,7 +87,10 @@ impl<S, const ARITY: usize, COM> Hash<S, ARITY, COM> where S: Specification<COM>, { - /// Builds a new [`Hash`](self::Hash) form `additive_round_keys` and `mds_matrix`. + /// Width of the State Buffer + pub const WIDTH: usize = ARITY + 1; + + /// Builds a new [`Hash`](self::Hash) from `additive_round_keys` and `mds_matrix`. /// /// # Panics /// @@ -91,24 +100,51 @@ where pub fn new(additive_round_keys: Vec<S::Field>, mds_matrix: Vec<S::Field>) -> Self { assert_eq!( additive_round_keys.len(), - rounds::<S, COM>() * (ARITY + 1), + Self::additive_round_keys_len(), "Additive Rounds Keys are not the correct size." ); assert_eq!( mds_matrix.len(), - (ARITY + 1).pow(2), + Self::mds_matrix_size(), "MDS Matrix is not the correct size." ); + Self::new_unchecked(additive_round_keys, mds_matrix) + } + + /// Builds a new [`Hash`](self::Hash) from `additive_round_keys` and `mds_matrix` without + /// checking their sizes. + #[inline] + fn new_unchecked(additive_round_keys: Vec<S::Field>, mds_matrix: Vec<S::Field>) -> Self { Self { additive_round_keys, mds_matrix, } } + /// Returns the total number of rounds for this Poseidon hash function specification. + #[inline] + pub fn rounds() -> usize { + rounds::<S, COM>() + } + + /// Returns the number of additive round keys. + #[inline] + pub fn additive_round_keys_len() -> usize { + Self::rounds() * Self::WIDTH + } + + /// Returns the size of the MDS matrix. + /// + /// This is the square of the [`WIDTH`](Self::WIDTH). + #[inline] + pub fn mds_matrix_size() -> usize { + Self::WIDTH.pow(2) + } + /// Returns the additive keys for the given `round`. #[inline] fn additive_keys(&self, round: usize) -> &[S::Field] { - let width = ARITY + 1; + let width = Self::WIDTH; let start = round * width; &self.additive_round_keys[start..start + width] } @@ -116,7 +152,7 @@ where /// Computes the MDS matrix multiplication against the `state`. #[inline] fn mds_matrix_multiply(&self, state: &mut State<S, COM>, compiler: &mut COM) { - let width = ARITY + 1; + let width = Self::WIDTH; let mut next = Vec::with_capacity(width); for i in 0..width { #[allow(clippy::needless_collect)] // NOTE: Clippy is wrong here, we need `&mut` access. @@ -138,7 +174,7 @@ where /// Computes the first round of the Poseidon permutation from `trapdoor` and `input`. #[inline] fn first_round(&self, input: [&S::Field; ARITY], compiler: &mut COM) -> State<S, COM> { - let mut state = Vec::with_capacity(ARITY + 1); + let mut state = Vec::with_capacity(Self::WIDTH); for (i, point) in iter::once(&S::zero(compiler)).chain(input).enumerate() { let mut elem = S::add(point, &self.additive_round_keys[i], compiler); S::apply_sbox(&mut elem, compiler); @@ -197,6 +233,35 @@ where } } +#[cfg(test)] // NOTE: This is only safe in a test. +impl<D, S, const ARITY: usize, COM> Sample<D> for Hash<S, ARITY, COM> +where + D: Clone, + S: Specification<COM>, + S::Field: Sample<D>, +{ + /// Samples random Poseidon parameters. + /// + /// # Warning + /// + /// This method samples the individual field elements of the parameters set, instead of + /// producing an actually correct/safe set of additive round keys and MDS matrix. + #[inline] + fn sample<R>(distribution: D, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + Self { + additive_round_keys: rng + .sample_iter(repeat(distribution.clone()).take(Self::additive_round_keys_len())) + .collect(), + mds_matrix: rng + .sample_iter(repeat(distribution).take(Self::mds_matrix_size())) + .collect(), + } + } +} + /// Poseidon Hash Input Type pub type Input<S, const ARITY: usize, COM = ()> = <Hash<S, ARITY, COM> as HashFunction<ARITY, COM>>::Input; @@ -209,7 +274,7 @@ pub type Output<S, const ARITY: usize, COM = ()> = #[cfg(feature = "arkworks")] #[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] pub mod arkworks { - use crate::crypto::constraint::arkworks::{FpVar, R1CS}; + use crate::crypto::constraint::arkworks::{Fp, FpVar, R1CS}; use ark_ff::{Field, PrimeField}; use ark_r1cs_std::fields::FieldVar; use manta_crypto::constraint::{Constant, ValueSource}; @@ -239,7 +304,7 @@ pub mod arkworks { where S: Specification, { - type Field = S::Field; + type Field = Fp<S::Field>; const FULL_ROUNDS: usize = S::FULL_ROUNDS; @@ -252,22 +317,22 @@ pub mod arkworks { #[inline] fn add(lhs: &Self::Field, rhs: &Self::Field, _: &mut ()) -> Self::Field { - *lhs + *rhs + Fp(lhs.0 + rhs.0) } #[inline] fn mul(lhs: &Self::Field, rhs: &Self::Field, _: &mut ()) -> Self::Field { - *lhs * *rhs + Fp(lhs.0 * rhs.0) } #[inline] fn add_assign(lhs: &mut Self::Field, rhs: &Self::Field, _: &mut ()) { - *lhs += rhs; + lhs.0 += rhs.0; } #[inline] fn apply_sbox(point: &mut Self::Field, _: &mut ()) { - *point = point.pow(&[Self::SBOX_EXPONENT, 0, 0, 0]); + point.0 = point.0.pow(&[Self::SBOX_EXPONENT, 0, 0, 0]); } } diff --git a/manta-pay/src/test.rs b/manta-pay/src/test.rs index 748c127cc..118b56d6a 100644 --- a/manta-pay/src/test.rs +++ b/manta-pay/src/test.rs @@ -16,12 +16,7 @@ //! Manta Pay Testing -/* TODO: -use crate::accounting::{ - config, - identity::UtxoSet, - transfer::{Mint, PrivateTransfer, Reclaim}, -}; +use crate::config::{self, FullParameters, Mint, PrivateTransfer, Reclaim}; use manta_crypto::{ constraint::{measure::Measure, ProofSystem}, rand::Rand, @@ -32,7 +27,7 @@ use rand::thread_rng; #[test] fn sample_mint_context() { let mut rng = thread_rng(); - let cs = Mint::sample_unknown_constraints(&mut rng); + let cs = Mint::unknown_constraints(FullParameters::new(&rng.gen(), &rng.gen())); println!("Mint: {:?}", cs.measure()); config::ProofSystem::generate_context(cs, &mut rng).unwrap(); } @@ -41,7 +36,7 @@ fn sample_mint_context() { #[test] fn sample_private_transfer_context() { let mut rng = thread_rng(); - let cs = PrivateTransfer::sample_unknown_constraints(&mut rng); + let cs = PrivateTransfer::unknown_constraints(FullParameters::new(&rng.gen(), &rng.gen())); println!("PrivateTransfer: {:?}", cs.measure()); config::ProofSystem::generate_context(cs, &mut rng).unwrap(); } @@ -50,11 +45,12 @@ fn sample_private_transfer_context() { #[test] fn sample_reclaim_context() { let mut rng = thread_rng(); - let cs = Reclaim::sample_unknown_constraints(&mut rng); + let cs = Reclaim::unknown_constraints(FullParameters::new(&rng.gen(), &rng.gen())); println!("Reclaim: {:?}", cs.measure()); config::ProofSystem::generate_context(cs, &mut rng).unwrap(); } +/* /// Tests the generation of a [`Mint`]. #[test] fn mint() { From 0ef3b28544c516632fad7dd6872239d0758f4b92 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 6 Jan 2022 17:15:59 -0500 Subject: [PATCH 163/275] wip: add proof verification tests --- manta-accounting/src/transfer/mod.rs | 2 + manta-accounting/src/transfer/test.rs | 160 +++++++++++++++++++++++++- manta-pay/src/config.rs | 4 +- 3 files changed, 161 insertions(+), 5 deletions(-) diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index 13113299f..00d20d3fe 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -389,6 +389,8 @@ where } /// Transfer Full Parameters +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""), Copy(bound = ""))] pub struct FullParameters<'p, C> where C: Configuration, diff --git a/manta-accounting/src/transfer/test.rs b/manta-accounting/src/transfer/test.rs index 1cb293614..e60278e93 100644 --- a/manta-accounting/src/transfer/test.rs +++ b/manta-accounting/src/transfer/test.rs @@ -16,8 +16,63 @@ //! Transfer Protocol Testing Framework -use crate::transfer::{Configuration, Parameters}; -use manta_crypto::rand::{CryptoRng, Rand, RngCore, Sample, Standard}; +use crate::{ + asset::{Asset, AssetId, AssetValue, AssetValueType}, + transfer::{ + has_public_participants, Configuration, FullParameters, Parameters, ProofSystemError, + Receiver, Sender, Transfer, Utxo, + }, +}; +use alloc::vec::Vec; +use core::marker::PhantomData; +use manta_crypto::{ + accumulator::Accumulator, + constraint::ProofSystem, + rand::{CryptoRng, Rand, RngCore, Sample, Standard}, +}; +use manta_util::into_array_unchecked; + +/// Samples a distribution over `count`-many values summing to `total`. +/// +/// # Warning +/// +/// This is a naive algorithm and should only be used for testing purposes. +#[inline] +pub fn value_distribution<R>(count: usize, total: AssetValue, rng: &mut R) -> Vec<AssetValue> +where + R: CryptoRng + RngCore + ?Sized, +{ + if count == 0 { + return Default::default(); + } + let mut result = Vec::with_capacity(count + 1); + result.push(AssetValue(0)); + for _ in 1..count { + result.push(AssetValue(AssetValueType::gen(rng) % total.0)); + } + result.push(total); + result.sort_unstable(); + for i in 0..count { + result[i] = result[i + 1] - result[i]; + } + result + .pop() + .expect("There's always at least one element in this vector."); + result +} + +/// Samples asset values from `rng`. +/// +/// # Warning +/// +/// This is a naive algorithm and should only be used for testing purposes. +#[inline] +pub fn sample_asset_values<R, const N: usize>(total: AssetValue, rng: &mut R) -> [AssetValue; N] +where + R: CryptoRng + RngCore + ?Sized, +{ + into_array_unchecked(value_distribution(N, total, rng)) +} /// Parameters Distribution #[derive(derivative::Derivative)] @@ -35,7 +90,7 @@ pub struct ParametersDistribution<K = Standard, U = Standard, V = Standard> { impl<K, U, V, C> Sample<ParametersDistribution<K, U, V>> for Parameters<C> where - C: Configuration + ?Sized, + C: Configuration, C::KeyAgreementScheme: Sample<K>, C::UtxoCommitmentScheme: Sample<U>, C::VoidNumberHashFunction: Sample<V>, @@ -52,3 +107,102 @@ where ) } } + +/// +pub struct TransferDistribution<C> +where + C: Configuration, +{ + /// + __: PhantomData<C>, +} + +impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> + Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS> +where + C: Configuration, +{ + /// + #[inline] + pub fn sample_and_check_proof<A, R>( + parameters: &Parameters<C>, + utxo_set: A, + rng: &mut R, + ) -> Result<bool, ProofSystemError<C>> + where + A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + R: CryptoRng + RngCore + ?Sized, + { + let full_parameters = FullParameters::new(parameters, utxo_set.model()); + let (proving_context, verifying_context) = Self::generate_context(full_parameters, rng)?; + let post = Self::sample(TransferDistribution { __: PhantomData }, rng).into_post( + full_parameters, + &proving_context, + rng, + )?; + C::ProofSystem::verify( + &post.generate_proof_input(), + &post.validity_proof, + &verifying_context, + ) + } +} + +/// +#[inline] +fn sample_senders_and_receivers<C>( + senders: &[AssetValue], + receivers: &[AssetValue], +) -> (Vec<Sender<C>>, Vec<Receiver<C>>) +where + C: Configuration, +{ + todo!() +} + +impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> + Sample<TransferDistribution<C>> for Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS> +where + C: Configuration, +{ + #[inline] + fn sample<R>(distribution: TransferDistribution<C>, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + let asset: Asset = rng.gen(); + + let mut input = value_distribution(SOURCES + SENDERS, asset.value, rng); + let mut output = value_distribution(RECEIVERS + SINKS, asset.value, rng); + let secret_input = input.split_off(SOURCES); + let public_output = output.split_off(RECEIVERS); + + let (senders, receivers) = sample_senders_and_receivers(&secret_input, &output); + + Self::new( + has_public_participants(SOURCES, SENDERS, RECEIVERS, SINKS).then(|| asset.id), + into_array_unchecked(input), + into_array_unchecked(senders), + into_array_unchecked(receivers), + into_array_unchecked(public_output), + ) + + /* + Ok(Self { + public: PublicTransfer::new( + asset.id, + into_array_unchecked(input), + into_array_unchecked(public_output), + ), + secret: distribution::FixedSecretTransfer::try_sample_custom_distribution( + asset.id, + into_array_unchecked(secret_input), + into_array_unchecked(output), + distribution.base.commitment_scheme, + distribution.base.utxo_set, + rng, + )?, + }) + */ + } +} diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 8d4b5bbdc..248925a49 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -144,7 +144,7 @@ impl Sample for UtxoCommitmentScheme { where R: CryptoRng + RngCore + ?Sized, { - rng.sample(distribution) + Self(rng.sample(distribution)) } } @@ -212,7 +212,7 @@ impl Sample for VoidNumberHashFunction { where R: CryptoRng + RngCore + ?Sized, { - rng.sample(distribution) + Self(rng.sample(distribution)) } } From 49c80166b3b8be22fcdddb09c1f1bf0649e9e2be Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 6 Jan 2022 19:34:34 -0500 Subject: [PATCH 164/275] fix: make sure padding is counted in encryption ciphertext size --- manta-accounting/src/transfer/test.rs | 131 +++++++++++++++++--------- manta-crypto/src/encryption.rs | 70 +++++++------- manta-pay/src/config.rs | 7 +- manta-pay/src/crypto/encryption.rs | 82 +++++++++------- manta-pay/src/test.rs | 14 ++- manta-util/src/array.rs | 5 +- 6 files changed, 187 insertions(+), 122 deletions(-) diff --git a/manta-accounting/src/transfer/test.rs b/manta-accounting/src/transfer/test.rs index e60278e93..4a1e135c9 100644 --- a/manta-accounting/src/transfer/test.rs +++ b/manta-accounting/src/transfer/test.rs @@ -19,15 +19,15 @@ use crate::{ asset::{Asset, AssetId, AssetValue, AssetValueType}, transfer::{ - has_public_participants, Configuration, FullParameters, Parameters, ProofSystemError, - Receiver, Sender, Transfer, Utxo, + has_public_participants, Configuration, FullParameters, Parameters, PreSender, + ProofSystemError, Receiver, Sender, Transfer, Utxo, }, }; use alloc::vec::Vec; -use core::marker::PhantomData; use manta_crypto::{ accumulator::Accumulator, constraint::ProofSystem, + key::KeyAgreementScheme, rand::{CryptoRng, Rand, RngCore, Sample, Standard}, }; use manta_util::into_array_unchecked; @@ -108,13 +108,17 @@ where } } -/// -pub struct TransferDistribution<C> +/// Transfer Distribution +pub struct TransferDistribution<'p, C, A> where C: Configuration, + A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, { - /// - __: PhantomData<C>, + /// Parameters + pub parameters: &'p Parameters<C>, + + /// UTXO Set + pub utxo_set: &'p mut A, } impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> @@ -122,24 +126,28 @@ impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, cons where C: Configuration, { - /// + /// Samples a new [`Transfer`] and builds a correctness proof for it, checking if it was + /// validated. #[inline] pub fn sample_and_check_proof<A, R>( parameters: &Parameters<C>, - utxo_set: A, + utxo_set: &mut A, rng: &mut R, ) -> Result<bool, ProofSystemError<C>> where A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, R: CryptoRng + RngCore + ?Sized, { + let transfer = Self::sample( + TransferDistribution { + parameters, + utxo_set, + }, + rng, + ); let full_parameters = FullParameters::new(parameters, utxo_set.model()); let (proving_context, verifying_context) = Self::generate_context(full_parameters, rng)?; - let post = Self::sample(TransferDistribution { __: PhantomData }, rng).into_post( - full_parameters, - &proving_context, - rng, - )?; + let post = transfer.into_post(full_parameters, &proving_context, rng)?; C::ProofSystem::verify( &post.generate_proof_input(), &post.validity_proof, @@ -148,37 +156,92 @@ where } } -/// +/// Samples a set of senders and receivers. #[inline] -fn sample_senders_and_receivers<C>( +fn sample_senders_and_receivers<C, A, R>( + parameters: &Parameters<C>, + asset_id: AssetId, senders: &[AssetValue], receivers: &[AssetValue], + utxo_set: &mut A, + rng: &mut R, ) -> (Vec<Sender<C>>, Vec<Receiver<C>>) where C: Configuration, + A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + R: CryptoRng + RngCore + ?Sized, { - todo!() + ( + senders + .iter() + .map(|v| { + let sender = PreSender::new( + parameters, + rng.gen(), + C::KeyAgreementScheme::derive_owned( + &parameters.key_agreement, + rng.gen(), + &mut (), + ), + asset_id.with(*v), + ); + sender.insert_utxo(utxo_set); + sender.try_upgrade(utxo_set).unwrap() + }) + .collect(), + receivers + .iter() + .map(|v| { + Receiver::new( + parameters, + rng.gen(), + C::KeyAgreementScheme::derive_owned( + &parameters.key_agreement, + rng.gen(), + &mut (), + ), + C::KeyAgreementScheme::derive_owned( + &parameters.key_agreement, + rng.gen(), + &mut (), + ), + asset_id.with(*v), + ) + }) + .collect(), + ) } -impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> - Sample<TransferDistribution<C>> for Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS> +impl< + C, + A, + const SOURCES: usize, + const SENDERS: usize, + const RECEIVERS: usize, + const SINKS: usize, + > Sample<TransferDistribution<'_, C, A>> for Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS> where C: Configuration, + A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, { #[inline] - fn sample<R>(distribution: TransferDistribution<C>, rng: &mut R) -> Self + fn sample<R>(distribution: TransferDistribution<'_, C, A>, rng: &mut R) -> Self where R: CryptoRng + RngCore + ?Sized, { - let asset: Asset = rng.gen(); - + let asset = Asset::gen(rng); let mut input = value_distribution(SOURCES + SENDERS, asset.value, rng); let mut output = value_distribution(RECEIVERS + SINKS, asset.value, rng); let secret_input = input.split_off(SOURCES); let public_output = output.split_off(RECEIVERS); - - let (senders, receivers) = sample_senders_and_receivers(&secret_input, &output); - + let (senders, receivers) = sample_senders_and_receivers( + distribution.parameters, + asset.id, + &secret_input, + &output, + distribution.utxo_set, + rng, + ); Self::new( has_public_participants(SOURCES, SENDERS, RECEIVERS, SINKS).then(|| asset.id), into_array_unchecked(input), @@ -186,23 +249,5 @@ where into_array_unchecked(receivers), into_array_unchecked(public_output), ) - - /* - Ok(Self { - public: PublicTransfer::new( - asset.id, - into_array_unchecked(input), - into_array_unchecked(public_output), - ), - secret: distribution::FixedSecretTransfer::try_sample_custom_distribution( - asset.id, - into_array_unchecked(secret_input), - into_array_unchecked(output), - distribution.base.commitment_scheme, - distribution.base.utxo_set, - rng, - )?, - }) - */ } } diff --git a/manta-crypto/src/encryption.rs b/manta-crypto/src/encryption.rs index e2e384f42..7591aa8f3 100644 --- a/manta-crypto/src/encryption.rs +++ b/manta-crypto/src/encryption.rs @@ -49,38 +49,38 @@ pub trait SymmetricKeyEncryptionScheme { fn decrypt(key: Self::Key, ciphertext: &Self::Ciphertext) -> Option<Self::Plaintext>; } -/// Constant-Size Symmetric-Key Encryption Scheme -#[derive(derivative::Derivative)] -#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct ConstantSizeSymmetricKeyEncryption<const SIZE: usize, S, P = [u8; SIZE]> -where - S: SymmetricKeyEncryptionScheme<Plaintext = [u8; SIZE], Ciphertext = [u8; SIZE]>, - P: Into<[u8; SIZE]> + TryFrom<[u8; SIZE]>, -{ - /// Type Parameter Marker - __: PhantomData<(S, P)>, -} - -impl<const SIZE: usize, S, P> SymmetricKeyEncryptionScheme - for ConstantSizeSymmetricKeyEncryption<SIZE, S, P> -where - S: SymmetricKeyEncryptionScheme<Plaintext = [u8; SIZE], Ciphertext = [u8; SIZE]>, - P: Into<[u8; SIZE]> + TryFrom<[u8; SIZE]>, -{ - type Key = S::Key; - - type Plaintext = P; - - type Ciphertext = [u8; SIZE]; - - #[inline] - fn encrypt(key: Self::Key, plaintext: Self::Plaintext) -> Self::Ciphertext { - S::encrypt(key, plaintext.into()) - } +/// Symmetric Encryption +pub mod symmetric { + use super::*; + + /// Mapped Symmetric Encryption Scheme + #[derive(derivative::Derivative)] + #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct Map<S, P = <S as SymmetricKeyEncryptionScheme>::Plaintext>(PhantomData<(S, P)>) + where + S: SymmetricKeyEncryptionScheme, + P: Into<S::Plaintext> + TryFrom<S::Plaintext>; + + impl<S, P> SymmetricKeyEncryptionScheme for Map<S, P> + where + S: SymmetricKeyEncryptionScheme, + P: Into<S::Plaintext> + TryFrom<S::Plaintext>, + { + type Key = S::Key; + + type Plaintext = P; + + type Ciphertext = S::Ciphertext; + + #[inline] + fn encrypt(key: Self::Key, plaintext: Self::Plaintext) -> Self::Ciphertext { + S::encrypt(key, plaintext.into()) + } - #[inline] - fn decrypt(key: Self::Key, ciphertext: &Self::Ciphertext) -> Option<Self::Plaintext> { - S::decrypt(key, ciphertext).and_then(move |p| p.try_into().ok()) + #[inline] + fn decrypt(key: Self::Key, ciphertext: &Self::Ciphertext) -> Option<Self::Plaintext> { + S::decrypt(key, ciphertext).and_then(move |p| p.try_into().ok()) + } } } @@ -132,15 +132,11 @@ pub type PublicKey<H> = /// [`agree_derive`]: HybridPublicKeyEncryptionScheme::agree_derive #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Hybrid<K, S, F> +pub struct Hybrid<K, S, F>(PhantomData<(K, S, F)>) where K: KeyAgreementScheme, S: SymmetricKeyEncryptionScheme, - F: KeyDerivationFunction<Key = K::SharedSecret, Output = S::Key>, -{ - /// Type Parameter Marker - __: PhantomData<(K, S, F)>, -} + F: KeyDerivationFunction<Key = K::SharedSecret, Output = S::Key>; impl<K, S, F> SymmetricKeyEncryptionScheme for Hybrid<K, S, F> where diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 248925a49..9d23c89ee 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -19,7 +19,7 @@ use crate::crypto::{ constraint::arkworks::{Boolean, Fp, FpVar, Groth16, R1CS}, ecc::{self, arkworks::ProjectiveCurve}, - encryption::AesGcm, + encryption::aes::{self, AesGcm}, hash::poseidon, key::Blake2sKdf, }; @@ -490,7 +490,10 @@ impl ProofSystemInput<Group> for ProofSystem { /// Note Encryption Scheme pub type NoteEncryptionScheme = encryption::Hybrid< KeyAgreementScheme, - encryption::ConstantSizeSymmetricKeyEncryption<{ Asset::SIZE }, AesGcm<{ Asset::SIZE }>, Asset>, + encryption::symmetric::Map< + AesGcm<{ Asset::SIZE }, { aes::ciphertext_size(Asset::SIZE) }>, + Asset, + >, key::kdf::FromByteVector< <KeyAgreementScheme as key::KeyAgreementScheme>::SharedSecret, Blake2sKdf, diff --git a/manta-pay/src/crypto/encryption.rs b/manta-pay/src/crypto/encryption.rs index 9276ffaf4..3e78e793a 100644 --- a/manta-pay/src/crypto/encryption.rs +++ b/manta-pay/src/crypto/encryption.rs @@ -18,45 +18,59 @@ // FIXME: Don't use raw bytes as encryption/decryption key. -use aes_gcm::{ - aead::{Aead, NewAead}, - Aes256Gcm, Nonce, -}; -use generic_array::GenericArray; -use manta_crypto::encryption::SymmetricKeyEncryptionScheme; -use manta_util::into_array_unchecked; - -/// AES Galois Counter Mode -pub struct AesGcm<const SIZE: usize>; - -impl<const SIZE: usize> AesGcm<SIZE> { - /// Encryption/Decryption Nonce - const NONCE: &'static [u8] = b"manta rocks!"; -} +/// AES Encryption Implementation +pub mod aes { + use aes_gcm::{ + aead::{Aead, NewAead}, + Aes256Gcm, Nonce, + }; + use generic_array::GenericArray; + use manta_crypto::encryption::SymmetricKeyEncryptionScheme; + use manta_util::into_array_unchecked; -impl<const SIZE: usize> SymmetricKeyEncryptionScheme for AesGcm<SIZE> { - type Key = [u8; 32]; + /// AES-GCM Authentication Tag Size + #[allow(clippy::cast_possible_truncation)] // NOTE: GCM Tag Size should be smaller than `2^32`. + const TAG_SIZE: usize = (aes_gcm::C_MAX - aes_gcm::P_MAX) as usize; - type Plaintext = [u8; SIZE]; + /// Computes the size of the ciphertext corresponding to a plaintext of the given + /// `plaintext_size`. + #[inline] + pub const fn ciphertext_size(plaintext_size: usize) -> usize { + plaintext_size + TAG_SIZE + } - type Ciphertext = [u8; SIZE]; + /// AES Galois Counter Mode + pub struct AesGcm<const P: usize, const C: usize>; - #[inline] - fn encrypt(key: Self::Key, plaintext: Self::Plaintext) -> Self::Ciphertext { - // SAFETY: Using a deterministic nonce is ok since we never reuse keys. - into_array_unchecked( - Aes256Gcm::new(GenericArray::from_slice(&key)) - .encrypt(Nonce::from_slice(Self::NONCE), plaintext.as_ref()) - .expect("Symmetric encryption is not allowed to fail."), - ) + impl<const P: usize, const C: usize> AesGcm<P, C> { + /// Encryption/Decryption Nonce + const NONCE: &'static [u8] = b"manta rocks!"; } - #[inline] - fn decrypt(key: Self::Key, ciphertext: &Self::Ciphertext) -> Option<Self::Plaintext> { - // SAFETY: Using a deterministic nonce is ok since we never reuse keys. - Aes256Gcm::new(GenericArray::from_slice(&key)) - .decrypt(Nonce::from_slice(Self::NONCE), ciphertext.as_ref()) - .ok() - .map(into_array_unchecked) + impl<const P: usize, const C: usize> SymmetricKeyEncryptionScheme for AesGcm<P, C> { + type Key = [u8; 32]; + + type Plaintext = [u8; P]; + + type Ciphertext = [u8; C]; + + #[inline] + fn encrypt(key: Self::Key, plaintext: Self::Plaintext) -> Self::Ciphertext { + // SAFETY: Using a deterministic nonce is ok since we never reuse keys. + into_array_unchecked( + Aes256Gcm::new(GenericArray::from_slice(&key)) + .encrypt(Nonce::from_slice(Self::NONCE), plaintext.as_ref()) + .expect("Symmetric encryption is not allowed to fail."), + ) + } + + #[inline] + fn decrypt(key: Self::Key, ciphertext: &Self::Ciphertext) -> Option<Self::Plaintext> { + // SAFETY: Using a deterministic nonce is ok since we never reuse keys. + Aes256Gcm::new(GenericArray::from_slice(&key)) + .decrypt(Nonce::from_slice(Self::NONCE), ciphertext.as_ref()) + .ok() + .map(into_array_unchecked) + } } } diff --git a/manta-pay/src/test.rs b/manta-pay/src/test.rs index 118b56d6a..d82874791 100644 --- a/manta-pay/src/test.rs +++ b/manta-pay/src/test.rs @@ -19,10 +19,14 @@ use crate::config::{self, FullParameters, Mint, PrivateTransfer, Reclaim}; use manta_crypto::{ constraint::{measure::Measure, ProofSystem}, + merkle_tree, rand::Rand, }; use rand::thread_rng; +type UtxoSet = merkle_tree::full::FullMerkleTree<config::MerkleTreeConfiguration>; + +/* /// Tests the generation of proving/verifying contexts for [`Mint`]. #[test] fn sample_mint_context() { @@ -49,18 +53,18 @@ fn sample_reclaim_context() { println!("Reclaim: {:?}", cs.measure()); config::ProofSystem::generate_context(cs, &mut rng).unwrap(); } +*/ -/* /// Tests the generation of a [`Mint`]. #[test] fn mint() { let mut rng = thread_rng(); - assert!(matches!( - Mint::sample_and_check_proof(&rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng), - Ok(true) - )); + let result = Mint::sample_and_check_proof(&rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng); + println!("Mint: {:?}", result); + assert!(matches!(result, Ok(true))); } +/* /// Tests the generation of a [`PrivateTransfer`]. #[test] fn private_transfer() { diff --git a/manta-util/src/array.rs b/manta-util/src/array.rs index 474b3b92a..3b14e69e0 100644 --- a/manta-util/src/array.rs +++ b/manta-util/src/array.rs @@ -27,7 +27,10 @@ where { match v.try_into() { Ok(array) => array, - _ => unreachable!("User was supposed to ensure that this branch is never reached."), + _ => unreachable!( + "Input did not have the correct length to match the output slice of length {:?}.", + N, + ), } } From 40bef055b3bac02774eb8f14eb62a7afbe800e8c Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 6 Jan 2022 22:36:05 -0500 Subject: [PATCH 165/275] wip: fixing circuit issues --- manta-accounting/src/transfer/mod.rs | 17 +++--- manta-crypto/src/constraint.rs | 2 + manta-crypto/src/merkle_tree/path.rs | 1 + manta-crypto/src/merkle_tree/tree.rs | 12 +++-- manta-pay/src/config.rs | 5 +- .../constraint/arkworks/proof_system.rs | 2 + manta-pay/src/test.rs | 52 +++++++++++++++---- 7 files changed, 66 insertions(+), 25 deletions(-) diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index 00d20d3fe..df7b87fcc 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -71,6 +71,7 @@ pub trait Configuration { /// Public Key Variable Type type PublicKeyVar: Variable<Public, Self::Compiler, Type = PublicKey<Self>> + + Variable<Secret, Self::Compiler, Type = PublicKey<Self>> + Equal<Self::Compiler>; /// Key Agreement Scheme Variable Type @@ -805,10 +806,12 @@ where &self.asset, compiler, ); + /* FIXME: let is_valid_proof = self.utxo_membership_proof .verify_in(&parameters.utxo_set_model, &utxo, compiler); compiler.assert(is_valid_proof); + */ let void_number = C::void_number_var(&parameters.void_number_hash, &utxo, &self.spend, compiler); compiler.assert_eq(&self.void_number, &void_number); @@ -826,7 +829,7 @@ where fn new_known(this: &Self::Type, compiler: &mut C::Compiler) -> Self { Self { spend: this.spend.as_known(compiler), - ephemeral_public_key: this.ephemeral_public_key.as_known(compiler), + ephemeral_public_key: this.ephemeral_public_key.as_known::<Secret, _>(compiler), asset: this.asset.as_known(compiler), utxo_membership_proof: this.utxo_membership_proof.as_known(compiler), void_number: this.void_number.as_known(compiler), @@ -837,7 +840,7 @@ where fn new_unknown(compiler: &mut C::Compiler) -> Self { Self { spend: compiler.allocate_unknown(), - ephemeral_public_key: compiler.allocate_unknown(), + ephemeral_public_key: compiler.allocate_unknown::<Secret, _>(), asset: compiler.allocate_unknown(), utxo_membership_proof: compiler.allocate_unknown(), void_number: compiler.allocate_unknown(), @@ -1119,8 +1122,8 @@ where fn new_known(this: &Self::Type, compiler: &mut C::Compiler) -> Self { Self { ephemeral_secret_key: this.ephemeral_secret_key.as_known(compiler), - ephemeral_public_key: this.ephemeral_public_key().as_known(compiler), - spend: this.spend.as_known(compiler), + ephemeral_public_key: this.ephemeral_public_key().as_known::<Public, _>(compiler), + spend: this.spend.as_known::<Secret, _>(compiler), asset: this.asset.as_known(compiler), utxo: this.utxo.as_known::<Public, _>(compiler), } @@ -1130,8 +1133,8 @@ where fn new_unknown(compiler: &mut C::Compiler) -> Self { Self { ephemeral_secret_key: compiler.allocate_unknown(), - ephemeral_public_key: compiler.allocate_unknown(), - spend: compiler.allocate_unknown(), + ephemeral_public_key: compiler.allocate_unknown::<Public, _>(), + spend: compiler.allocate_unknown::<Secret, _>(), asset: compiler.allocate_unknown(), utxo: compiler.allocate_unknown::<Public, _>(), } @@ -1212,8 +1215,8 @@ where pub fn extend_input(&self, input: &mut ProofInput<C>) { // TODO: Add a "public part" trait that extracts the public part of `Receiver` (using // `ReceiverVar` to determine the types), then generate this method automatically. - C::ProofSystem::extend(input, &self.utxo); C::ProofSystem::extend(input, self.ephemeral_public_key()); + C::ProofSystem::extend(input, &self.utxo); } /// Validates `self` on the receiver `ledger`. diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index 3010a8bb5..af5a95f27 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -19,6 +19,8 @@ // FIXME: Leverage the type system to constrain allocation to only unknown modes for verifier // generation and only known modes for proof generation, instead of relying on the `for_*` // methods to "do the right thing". +// TODO: Find ways to enforce public input structure, since it's very easy to extend the input +// vector by the wrong amount. use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use manta_util::{create_seal, seal}; diff --git a/manta-crypto/src/merkle_tree/path.rs b/manta-crypto/src/merkle_tree/path.rs index 9066ae309..e0402469c 100644 --- a/manta-crypto/src/merkle_tree/path.rs +++ b/manta-crypto/src/merkle_tree/path.rs @@ -1000,6 +1000,7 @@ pub mod constraint { inner_indices: this .leaf_index .parents() + .take(path_length_in::<C, _>()) .map(|i| i.is_right().as_known(compiler)) .collect(), path: this.path.iter().map(|d| d.as_known(compiler)).collect(), diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index 5a18b96bf..dd531a75b 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -16,11 +16,13 @@ //! Merkle Tree Abstractions -// TODO: Should we get rid of the `H > 2` requirement, and find a way to give correct -// implementations for the trivial tree sizes? -// TODO: Add "copy-on-write" adapters for `Root` and `Path`, and see if we can incorporate them -// into `Tree`. -// TODO: Use uniform construction for `Path` and `PathVar`. +// FIXME: Enforce that `Configuration` and `Configuration<COM>` use the same HEIGHT when used +// together. +// TODO: Should we get rid of the `H > 2` requirement, and find a way to give correct +// implementations for the trivial tree sizes? +// TODO: Add "copy-on-write" adapters for `Root` and `Path`, and see if we can incorporate them +// into `Tree`. +// TODO: Use uniform construction for `Path` and `PathVar`. use crate::{ accumulator::{ diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 9d23c89ee..8fb7e1236 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -23,6 +23,7 @@ use crate::crypto::{ hash::poseidon, key::Blake2sKdf, }; +use ark_ff::ToConstraintField; use bls12_381::Bls12_381; use bls12_381_ed::{ constraints::EdwardsVar as Bls12_381_EdwardsVar, EdwardsProjective as Bls12_381_Edwards, @@ -481,9 +482,7 @@ impl ProofSystemInput<Group> for ProofSystem { #[inline] fn extend(input: &mut Self::Input, next: &Group) { // FIXME: Make sure we can type check the coordinate system here. - let affine = next.0.into_affine(); - input.push(affine.x); - input.push(affine.y); + input.append(&mut next.0.into_affine().to_field_elements().unwrap()); } } diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs index 24b374766..9d289f892 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs @@ -106,6 +106,7 @@ where ConstraintSynthesizerWrapper(cs), &mut SizedRng(rng), )?; + println!("VERIFYINGKEY: {:?}", verifying_key.gamma_abc_g1.len()); Ok((proving_key, ArkGroth16::process_vk(&verifying_key)?)) } @@ -131,6 +132,7 @@ where proof: &Self::Proof, context: &Self::VerifyingContext, ) -> Result<Self::Verification, Self::Error> { + println!("INPUT: {:?}", input.len()); ArkGroth16::verify_with_processed_vk(context, input, proof) } } diff --git a/manta-pay/src/test.rs b/manta-pay/src/test.rs index d82874791..67514703f 100644 --- a/manta-pay/src/test.rs +++ b/manta-pay/src/test.rs @@ -35,7 +35,9 @@ fn sample_mint_context() { println!("Mint: {:?}", cs.measure()); config::ProofSystem::generate_context(cs, &mut rng).unwrap(); } +*/ +/* /// Tests the generation of proving/verifying contexts for [`PrivateTransfer`]. #[test] fn sample_private_transfer_context() { @@ -44,7 +46,9 @@ fn sample_private_transfer_context() { println!("PrivateTransfer: {:?}", cs.measure()); config::ProofSystem::generate_context(cs, &mut rng).unwrap(); } +*/ +/* /// Tests the generation of proving/verifying contexts for [`Reclaim`]. #[test] fn sample_reclaim_context() { @@ -55,6 +59,36 @@ fn sample_reclaim_context() { } */ +/* +#[test] +fn testing() { + let mut rng = thread_rng(); + + use manta_accounting::transfer::test::TransferDistribution; + use manta_crypto::{accumulator::Accumulator, rand::Sample}; + + let parameters = rng.gen(); + let mut utxo_set = UtxoSet::new(rng.gen()); + let transfer = Reclaim::sample( + TransferDistribution { + parameters: &parameters, + utxo_set: &mut utxo_set, + }, + &mut rng, + ); + let full_parameters = FullParameters::new(&parameters, utxo_set.model()); + + println!( + "Unknown {:?}", + Reclaim::unknown_constraints(full_parameters).measure() + ); + println!( + "Known {:?}", + transfer.known_constraints(full_parameters).measure() + ); +} +*/ + /// Tests the generation of a [`Mint`]. #[test] fn mint() { @@ -64,24 +98,22 @@ fn mint() { assert!(matches!(result, Ok(true))); } -/* /// Tests the generation of a [`PrivateTransfer`]. #[test] fn private_transfer() { let mut rng = thread_rng(); - assert!(matches!( - PrivateTransfer::sample_and_check_proof(&rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng), - Ok(true) - )); + let result = + PrivateTransfer::sample_and_check_proof(&rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng); + println!("PrivateTransfer: {:?}", result); + assert!(matches!(result, Ok(true))); } /// Tests the generation of a [`Reclaim`]. #[test] fn reclaim() { let mut rng = thread_rng(); - assert!(matches!( - Reclaim::sample_and_check_proof(&rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng), - Ok(true) - )); + let result = + Reclaim::sample_and_check_proof(&rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng); + println!("Reclaim: {:?}", result); + assert!(matches!(result, Ok(true))); } -*/ From 702a33fdc79ed78d1257bc72662019352e38fb40 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 6 Jan 2022 22:51:43 -0500 Subject: [PATCH 166/275] fix: correct conditional swap parity issue --- manta-accounting/src/transfer/mod.rs | 2 -- manta-crypto/src/constraint.rs | 12 +++---- .../constraint/arkworks/constraint_system.rs | 9 +++-- .../constraint/arkworks/proof_system.rs | 2 -- manta-pay/src/test.rs | 36 ------------------- 5 files changed, 13 insertions(+), 48 deletions(-) diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index df7b87fcc..72e36de3b 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -806,12 +806,10 @@ where &self.asset, compiler, ); - /* FIXME: let is_valid_proof = self.utxo_membership_proof .verify_in(&parameters.utxo_set_model, &utxo, compiler); compiler.assert(is_valid_proof); - */ let void_number = C::void_number_var(&parameters.void_number_hash, &utxo, &self.spend, compiler); compiler.assert_eq(&self.void_number, &void_number); diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index af5a95f27..2155cf02f 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -287,13 +287,13 @@ pub trait ConstraintSystem { V::assert_all_eq(iter, self); } - /// Selects `lhs` if `bit == 0` and `rhs` if `bit == 1`. + /// Selects `true_value` when `bit == 1` and `false_value` when `bit == 0`. #[inline] - fn conditional_select<V>(&mut self, bit: &Self::Bool, lhs: &V, rhs: &V) -> V + fn conditional_select<V>(&mut self, bit: &Self::Bool, true_value: &V, false_value: &V) -> V where V: ConditionalSelect<Self>, { - V::select(bit, lhs, rhs, self) + V::select(bit, true_value, false_value, self) } /// Swaps `lhs` and `rhs` if `bit == 1`. @@ -371,8 +371,8 @@ pub trait ConditionalSelect<COM> where COM: ConstraintSystem + ?Sized, { - /// Selects `lhs` if `bit == 0` and `rhs` if `bit == 1`. - fn select(bit: &COM::Bool, lhs: &Self, rhs: &Self, compiler: &mut COM) -> Self; + /// Selects `true_value` when `bit == 1` and `false_value` when `bit == 0`. + fn select(bit: &COM::Bool, true_value: &Self, false_value: &Self, compiler: &mut COM) -> Self; /// Swaps `lhs` and `rhs` if `bit == 1`. #[inline] @@ -381,8 +381,8 @@ where Self: Sized, { ( - Self::select(bit, lhs, rhs, compiler), Self::select(bit, rhs, lhs, compiler), + Self::select(bit, lhs, rhs, compiler), ) } diff --git a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs index 36771d89e..8e060843e 100644 --- a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs @@ -269,9 +269,14 @@ where F: PrimeField, { #[inline] - fn select(bit: &Boolean<F>, lhs: &Self, rhs: &Self, compiler: &mut R1CS<F>) -> Self { + fn select( + bit: &Boolean<F>, + true_value: &Self, + false_value: &Self, + compiler: &mut R1CS<F>, + ) -> Self { let _ = compiler; - Self::conditionally_select(bit, lhs, rhs) + Self::conditionally_select(bit, true_value, false_value) .expect("Conditionally selecting from two values is not allowed to fail.") } } diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs index 9d289f892..24b374766 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs @@ -106,7 +106,6 @@ where ConstraintSynthesizerWrapper(cs), &mut SizedRng(rng), )?; - println!("VERIFYINGKEY: {:?}", verifying_key.gamma_abc_g1.len()); Ok((proving_key, ArkGroth16::process_vk(&verifying_key)?)) } @@ -132,7 +131,6 @@ where proof: &Self::Proof, context: &Self::VerifyingContext, ) -> Result<Self::Verification, Self::Error> { - println!("INPUT: {:?}", input.len()); ArkGroth16::verify_with_processed_vk(context, input, proof) } } diff --git a/manta-pay/src/test.rs b/manta-pay/src/test.rs index 67514703f..fd6947101 100644 --- a/manta-pay/src/test.rs +++ b/manta-pay/src/test.rs @@ -26,7 +26,6 @@ use rand::thread_rng; type UtxoSet = merkle_tree::full::FullMerkleTree<config::MerkleTreeConfiguration>; -/* /// Tests the generation of proving/verifying contexts for [`Mint`]. #[test] fn sample_mint_context() { @@ -35,9 +34,7 @@ fn sample_mint_context() { println!("Mint: {:?}", cs.measure()); config::ProofSystem::generate_context(cs, &mut rng).unwrap(); } -*/ -/* /// Tests the generation of proving/verifying contexts for [`PrivateTransfer`]. #[test] fn sample_private_transfer_context() { @@ -46,9 +43,7 @@ fn sample_private_transfer_context() { println!("PrivateTransfer: {:?}", cs.measure()); config::ProofSystem::generate_context(cs, &mut rng).unwrap(); } -*/ -/* /// Tests the generation of proving/verifying contexts for [`Reclaim`]. #[test] fn sample_reclaim_context() { @@ -57,37 +52,6 @@ fn sample_reclaim_context() { println!("Reclaim: {:?}", cs.measure()); config::ProofSystem::generate_context(cs, &mut rng).unwrap(); } -*/ - -/* -#[test] -fn testing() { - let mut rng = thread_rng(); - - use manta_accounting::transfer::test::TransferDistribution; - use manta_crypto::{accumulator::Accumulator, rand::Sample}; - - let parameters = rng.gen(); - let mut utxo_set = UtxoSet::new(rng.gen()); - let transfer = Reclaim::sample( - TransferDistribution { - parameters: &parameters, - utxo_set: &mut utxo_set, - }, - &mut rng, - ); - let full_parameters = FullParameters::new(&parameters, utxo_set.model()); - - println!( - "Unknown {:?}", - Reclaim::unknown_constraints(full_parameters).measure() - ); - println!( - "Known {:?}", - transfer.known_constraints(full_parameters).measure() - ); -} -*/ /// Tests the generation of a [`Mint`]. #[test] From 4b71f02e6d9aef4bf98cb69c5ceb392b73fcf427 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 6 Jan 2022 22:59:47 -0500 Subject: [PATCH 167/275] wip: prepare to implement concrete signer/ledger --- manta-pay/src/ledger.rs | 5 ++--- manta-pay/src/lib.rs | 3 ++- manta-pay/src/signer.rs | 17 +++++++++++++++++ 3 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 manta-pay/src/signer.rs diff --git a/manta-pay/src/ledger.rs b/manta-pay/src/ledger.rs index 04e4a9ea2..33cc103d2 100644 --- a/manta-pay/src/ledger.rs +++ b/manta-pay/src/ledger.rs @@ -16,9 +16,7 @@ //! Ledger Implementation -// FIXME: Migrate this to new ledger abstraction. This will most likely go to `wallet` since it -// represents the "native" ledger rather than the blockchain ledger. - +/* use crate::{ accounting::{ config::Configuration, @@ -114,3 +112,4 @@ impl UtxoSetLedger { true } } +*/ diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index ab8cc6d3d..802125c2b 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -28,7 +28,8 @@ mod test; pub mod crypto; pub mod key; -// TODO: pub mod ledger; +pub mod ledger; +pub mod signer; #[cfg(all(feature = "arkworks", feature = "groth16"))] #[cfg_attr(doc_cfg, doc(cfg(all(feature = "arkworks", feature = "groth16"))))] diff --git a/manta-pay/src/signer.rs b/manta-pay/src/signer.rs new file mode 100644 index 000000000..a1352206b --- /dev/null +++ b/manta-pay/src/signer.rs @@ -0,0 +1,17 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Manta Pay Base Signer Implementation From 64e6b007dfec37fe51bdc46235e954be4b7a58d8 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 7 Jan 2022 02:31:56 -0500 Subject: [PATCH 168/275] wip: start building concrete wallet --- manta-accounting/src/asset.rs | 2 +- manta-pay/Cargo.toml | 2 +- manta-pay/src/config.rs | 18 +++++++++--------- manta-pay/src/lib.rs | 2 +- manta-pay/src/{signer.rs => wallet.rs} | 24 +++++++++++++++++++++++- 5 files changed, 35 insertions(+), 13 deletions(-) rename manta-pay/src/{signer.rs => wallet.rs} (54%) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 0c15ef271..694afd5ef 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -607,7 +607,7 @@ pub trait AssetMap: Default { } } -/// +/// Implements [`AssetMap`] for map types. macro_rules! impl_asset_map_for_maps_body { ($k:tt) => { type Key = $k; diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 77ed24fd0..e3443e96f 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -50,7 +50,7 @@ plonk = ["zk-garage-plonk"] test = ["manta-accounting/test", "manta-crypto/test"] # Standard Library -std = [] +std = ["manta-accounting/std"] [dependencies] aes-gcm = { version = "0.9.4", default-features = false, features = ["aes", "alloc"] } diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 8fb7e1236..d9c982033 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -499,10 +499,10 @@ pub type NoteEncryptionScheme = encryption::Hybrid< >, >; -/// Transfer Configuration -pub struct TransferConfiguration; +/// Base Configuration +pub struct Config; -impl transfer::Configuration for TransferConfiguration { +impl transfer::Configuration for Config { type SecretKey = <Self::KeyAgreementScheme as key::KeyAgreementScheme>::SecretKey; type PublicKey = <Self::KeyAgreementScheme as key::KeyAgreementScheme>::PublicKey; type KeyAgreementScheme = KeyAgreementScheme; @@ -538,19 +538,19 @@ impl transfer::Configuration for TransferConfiguration { } /// Transfer Parameters -pub type Parameters = transfer::Parameters<TransferConfiguration>; +pub type Parameters = transfer::Parameters<Config>; /// Full Transfer Parameters -pub type FullParameters<'p> = transfer::FullParameters<'p, TransferConfiguration>; +pub type FullParameters<'p> = transfer::FullParameters<'p, Config>; /// Mint Transfer Type -pub type Mint = transfer::canonical::Mint<TransferConfiguration>; +pub type Mint = transfer::canonical::Mint<Config>; /// Private Transfer Type -pub type PrivateTransfer = transfer::canonical::PrivateTransfer<TransferConfiguration>; +pub type PrivateTransfer = transfer::canonical::PrivateTransfer<Config>; /// Reclaim Transfer Type -pub type Reclaim = transfer::canonical::Reclaim<TransferConfiguration>; +pub type Reclaim = transfer::canonical::Reclaim<Config>; /// Transfer Post Type -pub type TransferPost = transfer::TransferPost<TransferConfiguration>; +pub type TransferPost = transfer::TransferPost<Config>; diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index 802125c2b..8f72dfd95 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -29,7 +29,7 @@ mod test; pub mod crypto; pub mod key; pub mod ledger; -pub mod signer; +pub mod wallet; #[cfg(all(feature = "arkworks", feature = "groth16"))] #[cfg_attr(doc_cfg, doc(cfg(all(feature = "arkworks", feature = "groth16"))))] diff --git a/manta-pay/src/signer.rs b/manta-pay/src/wallet.rs similarity index 54% rename from manta-pay/src/signer.rs rename to manta-pay/src/wallet.rs index a1352206b..617d52904 100644 --- a/manta-pay/src/signer.rs +++ b/manta-pay/src/wallet.rs @@ -14,4 +14,26 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Manta Pay Base Signer Implementation +//! Manta Pay Base Wallet Implementation + +use crate::{ + config::{Config, MerkleTreeConfiguration}, + key::TestnetKeySecret, +}; +use manta_accounting::{ + asset::{Asset, HashAssetMap}, + wallet::signer::{self, AssetMapKey}, +}; +use manta_crypto::merkle_tree; + +/* TODO: +impl signer::Configuration for Config { + type HierarchicalKeyDerivationScheme = TestnetKeySecret; + type UtxoSet = merkle_tree::full::FullMerkleTree<MerkleTreeConfiguration>; + type AssetMap = HashAssetMap<AssetMapKey<Self>>; + type Rng = rand_chacha::ChaCha20Rng; +} + +/// +pub type Signer = signer::Signer<Config>; +*/ From 81a898902cd728f7c1afc93bcc27995198a011d2 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 7 Jan 2022 14:07:02 -0500 Subject: [PATCH 169/275] wip: setup new signer/wallet --- manta-accounting/src/key.rs | 55 ++++- manta-accounting/src/wallet/signer.rs | 13 +- manta-crypto/src/merkle_tree/forest.rs | 30 ++- manta-crypto/src/merkle_tree/fork.rs | 225 ++++++++++++++++++-- manta-crypto/src/merkle_tree/full.rs | 4 +- manta-crypto/src/merkle_tree/partial.rs | 55 +++-- manta-crypto/src/merkle_tree/single_path.rs | 4 +- manta-crypto/src/merkle_tree/test.rs | 19 ++ manta-crypto/src/merkle_tree/tree.rs | 28 ++- manta-pay/src/config.rs | 12 +- manta-pay/src/crypto/ecc.rs | 22 -- manta-pay/src/crypto/hash/poseidon.rs | 10 +- manta-pay/src/test/merkle_tree.rs | 90 ++++++++ manta-pay/src/test/mod.rs | 20 ++ manta-pay/src/{test.rs => test/transfer.rs} | 2 +- manta-pay/src/wallet.rs | 68 +++++- manta-util/src/lib.rs | 16 ++ 17 files changed, 581 insertions(+), 92 deletions(-) create mode 100644 manta-pay/src/test/merkle_tree.rs create mode 100644 manta-pay/src/test/mod.rs rename manta-pay/src/{test.rs => test/transfer.rs} (98%) diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index 7e948a1c3..574167fa4 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -17,7 +17,8 @@ //! Hierarchical Key Derivation Schemes use alloc::vec::Vec; -use core::{fmt::Debug, hash::Hash}; +use core::{fmt::Debug, hash::Hash, marker::PhantomData}; +use manta_crypto::key::KeyDerivationFunction; /// Hierarchical Key Derivation Parameter pub trait HierarchicalKeyDerivationParameter: Copy + Default + PartialOrd { @@ -101,6 +102,58 @@ where } } +/// Mapping Hierarchical Key Derivation Scheme +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +pub struct Map<H, K> +where + H: HierarchicalKeyDerivationScheme, + K: KeyDerivationFunction<Key = H::SecretKey>, +{ + /// Base Derivation Scheme + base: H, + + /// Type Parameter Marker + __: PhantomData<K>, +} + +impl<H, K> HierarchicalKeyDerivationScheme for Map<H, K> +where + H: HierarchicalKeyDerivationScheme, + K: KeyDerivationFunction<Key = H::SecretKey>, +{ + type Account = H::Account; + + type Index = H::Index; + + type SecretKey = K::Output; + + type Error = H::Error; + + #[inline] + fn derive_spend( + &self, + account: Self::Account, + spend: Self::Index, + ) -> Result<Self::SecretKey, Self::Error> { + self.base + .derive_spend(account, spend) + .map(move |k| K::derive(&k)) + } + + #[inline] + fn derive_view( + &self, + account: Self::Account, + spend: Self::Index, + view: Self::Index, + ) -> Result<Self::SecretKey, Self::Error> { + self.base + .derive_view(account, spend, view) + .map(move |k| K::derive(&k)) + } +} + /// Hierarchical Key Derivation Secret Key Pair pub struct SecretKeyPair<H> where diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 5472feba2..00c6caf76 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -49,18 +49,7 @@ use manta_crypto::{ encryption::DecryptedMessage, rand::{CryptoRng, Rand, RngCore}, }; -use manta_util::{fallible_array_map, into_array_unchecked, iter::IteratorExt}; - -/// Rollback Trait -pub trait Rollback { - /// Rolls back `self` to the previous state. - /// - /// # Implementation Note - /// - /// Rolling back to the previous state must be idempotent, i.e. two consecutive calls to - /// [`rollback`](Self::rollback) should do the same as one call. - fn rollback(&mut self); -} +use manta_util::{fallible_array_map, into_array_unchecked, iter::IteratorExt, Rollback}; /// Signer Connection pub trait Connection<H, C> diff --git a/manta-crypto/src/merkle_tree/forest.rs b/manta-crypto/src/merkle_tree/forest.rs index 71d3711af..dd26e4faa 100644 --- a/manta-crypto/src/merkle_tree/forest.rs +++ b/manta-crypto/src/merkle_tree/forest.rs @@ -26,13 +26,15 @@ use crate::{ OptimizedAccumulator, }, merkle_tree::{ + fork::ForkedTree, + inner_tree::InnerMap, tree::{self, Leaf, Parameters, Tree}, - InnerDigest, WithProofs, + InnerDigest, LeafDigest, WithProofs, }, }; use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; -use manta_util::into_array_unchecked; +use manta_util::{into_array_unchecked, pointer::PointerFamily, Rollback}; /// Merkle Forest Configuration pub trait Configuration: tree::Configuration { @@ -53,12 +55,15 @@ pub trait Configuration: tree::Configuration { /// # Contract /// /// For a type to be a fixed index, the number of possible values must be known at compile time. In -/// this case, `N` must be the the number of values. If this type is used as an +/// this case, `N` must be the number of distinct values. If this type is used as an /// [`Index`](Configuration::Index) for a merkle forest configuration, the /// [`tree_index`](Configuration::tree_index) method must return values from a distribution over /// exactly `N` values. pub trait FixedIndex<const N: usize>: Into<usize> {} +impl FixedIndex<256> for u8 {} +impl FixedIndex<65536> for u16 {} + /// Merkle Forest Structure pub trait Forest<C> where @@ -445,3 +450,22 @@ where } } } + +impl<C, T, P, M, const N: usize> Rollback + for MerkleForest<C, TreeArray<C, ForkedTree<C, T, P, M>, N>> +where + C: Configuration + ?Sized, + C::Index: FixedIndex<N>, + T: Tree<C>, + P: PointerFamily<T>, + M: Default + InnerMap<C>, + LeafDigest<C>: Clone + Default, + InnerDigest<C>: Default, +{ + #[inline] + fn rollback(&mut self) { + for tree in &mut self.forest.array { + tree.reset_fork(&self.parameters); + } + } +} diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs index 75cb9eb3d..fecd602b6 100644 --- a/manta-crypto/src/merkle_tree/fork.rs +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -24,7 +24,8 @@ use crate::merkle_tree::{ inner_tree::{BTreeMap, InnerMap, PartialInnerTree}, partial::Partial, path::CurrentInnerPath, - Configuration, InnerDigest, Leaf, LeafDigest, Node, Parameters, Parity, Tree, + Configuration, CurrentPath, InnerDigest, Leaf, LeafDigest, Node, Parameters, Parity, Path, + PathError, Root, Tree, WithProofs, }; use alloc::{vec, vec::Vec}; use core::{borrow::Borrow, fmt::Debug, hash::Hash, marker::PhantomData, mem, ops::Deref}; @@ -81,7 +82,7 @@ where pub fn fork<M>(&self, parameters: &Parameters<C>) -> Fork<C, T, P, M> where M: Default + InnerMap<C>, - LeafDigest<C>: Default, + LeafDigest<C>: Clone + Default, InnerDigest<C>: Default, { Fork::new(parameters, self) @@ -93,7 +94,7 @@ where pub fn attach<M>(&self, parameters: &Parameters<C>, fork: &mut Fork<C, T, P, M>) -> bool where M: Default + InnerMap<C>, - LeafDigest<C>: Default, + LeafDigest<C>: Clone + Default, InnerDigest<C>: Default, { fork.attach(parameters, self) @@ -260,7 +261,7 @@ where #[inline] pub fn new(parameters: &Parameters<C>, trunk: &Trunk<C, T, P>) -> Self where - LeafDigest<C>: Default, + LeafDigest<C>: Clone + Default, InnerDigest<C>: Default, { Self::with_leaves(parameters, trunk, Default::default()).unwrap() @@ -275,7 +276,7 @@ where leaf_digests: Vec<LeafDigest<C>>, ) -> Option<Self> where - LeafDigest<C>: Default, + LeafDigest<C>: Clone + Default, InnerDigest<C>: Default, { let (base_contribution, branch) = @@ -295,7 +296,7 @@ where leaf_digests: Vec<LeafDigest<C>>, ) -> Option<(BaseContribution, Partial<C, M>)> where - LeafDigest<C>: Default, + LeafDigest<C>: Clone + Default, InnerDigest<C>: Default, { if leaf_digests.len() + base.len() >= capacity::<C>() { @@ -313,7 +314,7 @@ where leaf_digests: Vec<LeafDigest<C>>, ) -> (BaseContribution, Partial<C, M>) where - LeafDigest<C>: Default, + LeafDigest<C>: Clone + Default, InnerDigest<C>: Default, { let (base_contribution, base_inner_digest, base_leaf_digests, inner_path) = @@ -341,7 +342,7 @@ where CurrentInnerPath<C>, ) where - LeafDigest<C>: Default, + LeafDigest<C>: Clone + Default, InnerDigest<C>: Default, { if base.is_empty() { @@ -352,19 +353,19 @@ where base.current_path(parameters).inner_path, ) } else { - let current_leaf = base.current_leaf(); + let current_leaf = base.current_leaf().unwrap(); let current_path = base.current_path(parameters); match current_path.leaf_index().parity() { Parity::Left => ( BaseContribution::LeftLeaf, - parameters.join_leaves(&current_leaf, &current_path.sibling_digest), - vec![current_leaf], + parameters.join_leaves(current_leaf, &current_path.sibling_digest), + vec![current_leaf.clone()], current_path.inner_path, ), Parity::Right => ( BaseContribution::BothLeaves, - parameters.join_leaves(&current_path.sibling_digest, &current_leaf), - vec![current_path.sibling_digest, current_leaf], + parameters.join_leaves(&current_path.sibling_digest, current_leaf), + vec![current_path.sibling_digest, current_leaf.clone()], current_path.inner_path, ), } @@ -394,7 +395,7 @@ where branch: &mut Partial<C, M>, ) -> bool where - LeafDigest<C>: Default, + LeafDigest<C>: Clone + Default, InnerDigest<C>: Default, { if branch.len() + base.len() - (*base_contribution as usize) >= capacity::<C>() { @@ -415,7 +416,7 @@ where #[inline] pub fn attach(&mut self, parameters: &Parameters<C>, trunk: &Trunk<C, T, P>) -> bool where - LeafDigest<C>: Default, + LeafDigest<C>: Clone + Default, InnerDigest<C>: Default, { if !Self::try_rebase( @@ -469,6 +470,31 @@ where self.branch.root() } + /// + #[inline] + pub fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>> { + self.branch.get_leaf_digest(index) + } + + /// + #[inline] + pub fn position(&self, leaf_digest: &LeafDigest<C>) -> Option<usize> { + todo!() + } + + /// + #[inline] + pub fn current_leaf(&self) -> Option<&LeafDigest<C>> { + // self.branch.current_leaf() + todo!() + } + + /// + #[inline] + pub fn current_path(&self, parameters: &Parameters<C>) -> CurrentPath<C> { + todo!() + } + /// Appends a new `leaf` onto this fork. /// /// Returns `None` if this fork has been detached from its trunk. Use [`attach`](Self::attach) @@ -481,4 +507,173 @@ where let _ = P::upgrade(&self.base)?; Some(self.branch.push(parameters, leaf)) } + + /// Appends a new `leaf_digest` onto this fork. + #[inline] + fn maybe_push_digest<F>(&mut self, parameters: &Parameters<C>, leaf_digest: F) -> Option<bool> + where + F: FnOnce() -> Option<LeafDigest<C>>, + LeafDigest<C>: Default, + { + let _ = P::upgrade(&self.base)?; + self.branch.maybe_push_digest(parameters, leaf_digest) + } +} + +/// Forked Tree +pub struct ForkedTree<C, T, P = pointer::SingleThreaded, M = BTreeMap<C>> +where + C: Configuration + ?Sized, + T: Tree<C>, + P: PointerFamily<T>, + M: Default + InnerMap<C>, +{ + /// Base Trunk + trunk: Trunk<C, T, P>, + + /// Fork + fork: Fork<C, T, P, M>, +} + +impl<C, T, P, M> ForkedTree<C, T, P, M> +where + C: Configuration + ?Sized, + T: Tree<C>, + P: PointerFamily<T>, + M: Default + InnerMap<C>, +{ + /// Builds a new [`ForkedTree`] for `tree`. + #[inline] + pub fn new(tree: T, parameters: &Parameters<C>) -> Self + where + LeafDigest<C>: Clone + Default, + InnerDigest<C>: Default, + { + let trunk = Trunk::new(tree); + Self { + fork: trunk.fork(parameters), + trunk, + } + } + + /// Returns a shared reference to the trunk of this tree. + #[inline] + pub fn trunk(&self) -> &Trunk<C, T, P> { + &self.trunk + } + + /// Returns a shared reference to the fork of this tree. + #[inline] + pub fn fork(&self) -> &Fork<C, T, P, M> { + &self.fork + } + + /// Returns a mutable reference to the trunk of this tree. + #[inline] + pub fn fork_mut(&mut self) -> &mut Fork<C, T, P, M> { + &mut self.fork + } + + /// Resets the fork of this tree back to the trunk. + #[inline] + pub fn reset_fork(&mut self, parameters: &Parameters<C>) + where + LeafDigest<C>: Clone + Default, + InnerDigest<C>: Default, + { + self.fork = self.trunk.fork(parameters); + } + + /// Converts `self` back into its inner [`Tree`]. + /// + /// # Safety + /// + /// This method automatically detaches all of the forks associated to the trunk of this tree. + #[inline] + pub fn into_tree(self) -> T { + self.trunk.into_tree() + } +} + +impl<C, T, P, M> Tree<C> for ForkedTree<C, T, P, M> +where + C: Configuration + ?Sized, + T: Tree<C>, + P: PointerFamily<T>, + M: Default + InnerMap<C>, + LeafDigest<C>: Clone + Default, + InnerDigest<C>: Default, +{ + #[inline] + fn new(parameters: &Parameters<C>) -> Self { + Self::new(T::new(parameters), parameters) + } + + #[inline] + fn len(&self) -> usize { + self.fork.len() + } + + #[inline] + fn current_leaf(&self) -> Option<&LeafDigest<C>> { + self.fork.current_leaf() + } + + #[inline] + fn root(&self) -> &Root<C> { + self.fork.root() + } + + #[inline] + fn current_path(&self, parameters: &Parameters<C>) -> CurrentPath<C> { + // TODO: self.fork.current_path(parameters) + todo!() + } + + #[inline] + fn maybe_push_digest<F>(&mut self, parameters: &Parameters<C>, leaf_digest: F) -> Option<bool> + where + F: FnOnce() -> Option<LeafDigest<C>>, + { + self.fork.maybe_push_digest(parameters, leaf_digest) + } +} + +impl<C, T, P, M> WithProofs<C> for ForkedTree<C, T, P, M> +where + C: Configuration + ?Sized, + T: Tree<C> + WithProofs<C>, + P: PointerFamily<T>, + M: Default + InnerMap<C>, + LeafDigest<C>: Clone + Default, + InnerDigest<C>: Default, +{ + #[inline] + fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>> { + self.fork.leaf_digest(index) + } + + #[inline] + fn position(&self, leaf_digest: &LeafDigest<C>) -> Option<usize> { + // self.fork.position(leaf_digest) + todo!() + } + + #[inline] + fn maybe_push_provable_digest<F>( + &mut self, + parameters: &Parameters<C>, + leaf_digest: F, + ) -> Option<bool> + where + F: FnOnce() -> Option<LeafDigest<C>>, + { + self.fork.maybe_push_digest(parameters, leaf_digest) + } + + #[inline] + fn path(&self, parameters: &Parameters<C>, index: usize) -> Result<Path<C>, PathError> { + // self.fork.path(parameters, index) + todo!() + } } diff --git a/manta-crypto/src/merkle_tree/full.rs b/manta-crypto/src/merkle_tree/full.rs index f2d30e894..f30dde0ba 100644 --- a/manta-crypto/src/merkle_tree/full.rs +++ b/manta-crypto/src/merkle_tree/full.rs @@ -155,8 +155,8 @@ where } #[inline] - fn current_leaf(&self) -> LeafDigest<C> { - self.leaf_digests.last().cloned().unwrap_or_default() + fn current_leaf(&self) -> Option<&LeafDigest<C>> { + self.leaf_digests.last() } #[inline] diff --git a/manta-crypto/src/merkle_tree/partial.rs b/manta-crypto/src/merkle_tree/partial.rs index 24121b4bc..aa5221d89 100644 --- a/manta-crypto/src/merkle_tree/partial.rs +++ b/manta-crypto/src/merkle_tree/partial.rs @@ -117,16 +117,22 @@ where self.inner_digests.root() } + /// + #[inline] + pub fn get_leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>> { + self.leaf_digests.get(index - self.starting_leaf_index().0) + } + /// Returns the sibling leaf node to `index`. #[inline] - fn get_leaf_sibling(&self, index: Node) -> Option<&LeafDigest<C>> { + pub fn get_leaf_sibling(&self, index: Node) -> Option<&LeafDigest<C>> { self.leaf_digests .get((index - self.starting_leaf_index().0).sibling().0) } /// Returns an owned sibling leaf node to `index`. #[inline] - fn get_owned_leaf_sibling(&self, index: Node) -> LeafDigest<C> + pub fn get_owned_leaf_sibling(&self, index: Node) -> LeafDigest<C> where LeafDigest<C>: Clone + Default, { @@ -134,9 +140,8 @@ where } /// Appends a `leaf_digest` with index given by `leaf_index` into the tree. - // FIXME: Remove pub(super) on this once we have a better interface for `fork`. #[inline] - pub(super) fn push_leaf_digest( + pub fn push_leaf_digest( &mut self, parameters: &Parameters<C>, leaf_index: Node, @@ -158,9 +163,8 @@ where } /// Appends a `leaf` to the tree using `parameters`. - // FIXME: Remove pub(super) on this once we have a better interface for `fork`. #[inline] - pub(super) fn push(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> bool + pub fn push(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> bool where LeafDigest<C>: Default, { @@ -171,6 +175,26 @@ where self.push_leaf_digest(parameters, Node(len), parameters.digest(leaf)); true } + + /// Appends `leaf_digest` to the tree using `parameters`. + #[inline] + pub fn maybe_push_digest<F>( + &mut self, + parameters: &Parameters<C>, + leaf_digest: F, + ) -> Option<bool> + where + F: FnOnce() -> Option<LeafDigest<C>>, + LeafDigest<C>: Default, + { + // TODO: Push without keeping unnecessary proof. + let len = self.len(); + if len >= capacity::<C>() { + return Some(false); + } + self.push_leaf_digest(parameters, Node(len), leaf_digest()?); + Some(true) + } } impl<C, M> Tree<C> for Partial<C, M> @@ -192,8 +216,8 @@ where } #[inline] - fn current_leaf(&self) -> LeafDigest<C> { - self.leaf_digests.last().cloned().unwrap_or_default() + fn current_leaf(&self) -> Option<&LeafDigest<C>> { + self.leaf_digests.last() } #[inline] @@ -216,13 +240,7 @@ where where F: FnOnce() -> Option<LeafDigest<C>>, { - // TODO: Push without keeping unnecessary proof. - let len = self.len(); - if len >= capacity::<C>() { - return Some(false); - } - self.push_leaf_digest(parameters, Node(len), leaf_digest()?); - Some(true) + self.maybe_push_digest(parameters, leaf_digest) } } @@ -235,12 +253,15 @@ where { #[inline] fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>> { - self.leaf_digests.get(index) + self.get_leaf_digest(index) } #[inline] fn position(&self, leaf_digest: &LeafDigest<C>) -> Option<usize> { - self.leaf_digests.iter().position(move |d| d == leaf_digest) + self.leaf_digests + .iter() + .position(move |d| d == leaf_digest) + .map(move |i| i + self.starting_leaf_index().0) } #[inline] diff --git a/manta-crypto/src/merkle_tree/single_path.rs b/manta-crypto/src/merkle_tree/single_path.rs index 563b920ab..b517354a7 100644 --- a/manta-crypto/src/merkle_tree/single_path.rs +++ b/manta-crypto/src/merkle_tree/single_path.rs @@ -143,8 +143,8 @@ where } #[inline] - fn current_leaf(&self) -> LeafDigest<C> { - self.leaf_digest.as_ref().cloned().unwrap_or_default() + fn current_leaf(&self) -> Option<&LeafDigest<C>> { + self.leaf_digest.as_ref() } #[inline] diff --git a/manta-crypto/src/merkle_tree/test.rs b/manta-crypto/src/merkle_tree/test.rs index 88ea09c32..fef9b5620 100644 --- a/manta-crypto/src/merkle_tree/test.rs +++ b/manta-crypto/src/merkle_tree/test.rs @@ -139,3 +139,22 @@ where "Path returned from tree was not valid." ); } + +/// Tests path construction for multiple insertions. This is an extension of the +/// [`assert_valid_path`] test. +#[inline] +pub fn assert_valid_paths<C, T>(tree: &mut MerkleTree<C, T>, leaves: &[Leaf<C>]) +where + C: Configuration + ?Sized, + T: Tree<C> + WithProofs<C>, + InnerDigest<C>: PartialEq, + Leaf<C>: Sized, +{ + let starting_index = tree.len(); + for (i, leaf) in leaves.iter().enumerate() { + tree.push(leaf); + for (j, previous_leaf) in leaves.iter().enumerate().take(i + 1) { + assert_valid_path(tree, starting_index + j, previous_leaf); + } + } +} diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index dd531a75b..74c841825 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -31,12 +31,13 @@ use crate::{ }, constraint::{ConditionalSelect, Constant, ConstraintSystem, Equal, Native, ValueSource}, merkle_tree::{ - fork::Trunk, + fork::{ForkedTree, Trunk}, + inner_tree::InnerMap, path::{constraint::PathVar, CurrentPath, Path}, }, }; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; -use manta_util::pointer::PointerFamily; +use manta_util::{pointer::PointerFamily, Rollback}; /// Merkle Tree Leaf Hash pub trait LeafHash<COM = ()> { @@ -72,6 +73,8 @@ pub trait LeafHash<COM = ()> { /// /// This implementation of [`LeafHash`] should only be used when users cannot control the value of a /// leaf itself, otherwise, using this implementation may not be safe. +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct IdentityLeafHash<L, COM = ()>(PhantomData<(L, COM)>) where L: Clone; @@ -305,8 +308,8 @@ where self.len() == 0 } - /// Returns the current (i.e. right-most) leaf. - fn current_leaf(&self) -> LeafDigest<C>; + /// Returns the current (i.e. right-most) leaf if the tree is not empty. + fn current_leaf(&self) -> Option<&LeafDigest<C>>; /// Returns the [`Root`] of the merkle tree. fn root(&self) -> &Root<C>; @@ -792,7 +795,7 @@ where /// /// See [`Tree::current_leaf`] for more. #[inline] - pub fn current_leaf(&self) -> LeafDigest<C> { + pub fn current_leaf(&self) -> Option<&LeafDigest<C>> { self.tree.current_leaf() } @@ -1037,3 +1040,18 @@ where .unwrap_or(false) } } + +impl<C, T, P, M> Rollback for MerkleTree<C, ForkedTree<C, T, P, M>> +where + C: Configuration + ?Sized, + T: Tree<C>, + P: PointerFamily<T>, + M: Default + InnerMap<C>, + LeafDigest<C>: Clone + Default, + InnerDigest<C>: Default, +{ + #[inline] + fn rollback(&mut self) { + self.tree.reset_fork(&self.parameters); + } +} diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index d9c982033..eb5f72e68 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -25,9 +25,7 @@ use crate::crypto::{ }; use ark_ff::ToConstraintField; use bls12_381::Bls12_381; -use bls12_381_ed::{ - constraints::EdwardsVar as Bls12_381_EdwardsVar, EdwardsProjective as Bls12_381_Edwards, -}; +use bls12_381_ed::constraints::EdwardsVar as Bls12_381_EdwardsVar; use manta_accounting::{ asset::{Asset, AssetId, AssetValue}, transfer, @@ -53,6 +51,8 @@ pub use ark_bls12_381 as bls12_381; #[doc(inline)] pub use ark_ed_on_bls12_381 as bls12_381_ed; +pub(crate) use bls12_381_ed::EdwardsProjective as Bls12_381_Edwards; + /// Pairing Curve Type pub type PairingCurve = Bls12_381; @@ -106,6 +106,12 @@ impl poseidon::arkworks::Specification for PoseidonSpec<4> { /// Key Agreement Scheme Type pub type KeyAgreementScheme = DiffieHellman<Group>; +/// Secret Key Type +pub type SecretKey = <KeyAgreementScheme as key::KeyAgreementScheme>::SecretKey; + +/// Public Key Type +pub type PublicKey = <KeyAgreementScheme as key::KeyAgreementScheme>::PublicKey; + /// Key Agreement Scheme Variable Type pub type KeyAgreementSchemeVar = DiffieHellman<GroupVar, Compiler>; diff --git a/manta-pay/src/crypto/ecc.rs b/manta-pay/src/crypto/ecc.rs index 2b8a64c1b..4a5df5c2e 100644 --- a/manta-pay/src/crypto/ecc.rs +++ b/manta-pay/src/crypto/ecc.rs @@ -91,28 +91,6 @@ pub mod arkworks { )) } - /* TODO[remove]: - /// Elliptic Curve Scalar Element - #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct Scalar<C>(C::ScalarField) - where - C: ProjectiveCurve; - - impl<C> Sample for Scalar<C> - where - C: ProjectiveCurve, - { - #[inline] - fn sample<R>(distribution: Standard, rng: &mut R) -> Self - where - R: CryptoRng + RngCore + ?Sized, - { - let _ = distribution; - Self(C::ScalarField::rand(rng)) - } - } - */ - /// Elliptic Curve Group Element #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Group<C>(pub(crate) C) diff --git a/manta-pay/src/crypto/hash/poseidon.rs b/manta-pay/src/crypto/hash/poseidon.rs index 446eb52b8..eaac6651e 100644 --- a/manta-pay/src/crypto/hash/poseidon.rs +++ b/manta-pay/src/crypto/hash/poseidon.rs @@ -20,7 +20,7 @@ // TODO: Add more methods to the `Specification` trait for optimization. use alloc::vec::Vec; -use core::{iter, mem}; +use core::{fmt::Debug, iter, mem}; use manta_crypto::hash::HashFunction; #[cfg(test)] @@ -72,6 +72,14 @@ where } /// Poseidon Hash +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "S::Field: Clone"), + Debug(bound = "S::Field: Debug"), + Eq(bound = "S::Field: Eq"), + Hash(bound = "S::Field: core::hash::Hash"), + PartialEq(bound = "S::Field: PartialEq") +)] pub struct Hash<S, const ARITY: usize = 1, COM = ()> where S: Specification<COM>, diff --git a/manta-pay/src/test/merkle_tree.rs b/manta-pay/src/test/merkle_tree.rs new file mode 100644 index 000000000..31e73111f --- /dev/null +++ b/manta-pay/src/test/merkle_tree.rs @@ -0,0 +1,90 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Manta Pay Merkle Tree Testing + +use crate::config::MerkleTreeConfiguration; +use core::iter::repeat; +use manta_crypto::{ + accumulator, + accumulator::Accumulator, + merkle_tree::{self, forest, fork, test, MerkleTree}, + rand::{Rand, Standard}, +}; +use rand::thread_rng; + +/// Base Tree +pub type Base = merkle_tree::full::Full<MerkleTreeConfiguration>; + +/// Forked Base Tree +pub type ForkedBase = fork::ForkedTree<MerkleTreeConfiguration, Base>; + +/// Tree Wrapper for Base +pub type Tree = MerkleTree<MerkleTreeConfiguration, Base>; + +/// Tree Wrapper for Forked Base +pub type ForkedTree = MerkleTree<MerkleTreeConfiguration, ForkedBase>; + +/// Forest Wrapper for Base +pub type Forest = forest::TreeArrayMerkleForest<MerkleTreeConfiguration, Base, 256>; + +/// Forest Wrapper for Forked Base +pub type ForkedForest = forest::TreeArrayMerkleForest<MerkleTreeConfiguration, ForkedBase, 256>; + +#[test] +fn test_suite() { + let mut rng = thread_rng(); + let parameters = rng.gen(); + let parameters = test::push_twice_to_empty_tree_succeeds::<MerkleTreeConfiguration, Base>( + parameters, + &rng.gen(), + &rng.gen(), + ); + + let mut tree = Tree::new(parameters); + + accumulator::test::assert_unique_outputs( + &mut tree, + &rng.sample_iter(repeat(Standard).take(300)) + .collect::<Vec<_>>(), + ); + for _ in 0..30000 { + tree.insert(&rng.gen()); + } + accumulator::test::assert_unique_outputs( + &mut tree, + &rng.sample_iter(repeat(Standard).take(300)) + .collect::<Vec<_>>(), + ); + + let parameters = tree.into_parameters(); + + let mut forest = Forest::new(parameters); + + accumulator::test::assert_unique_outputs( + &mut forest, + &rng.sample_iter(repeat(Standard).take(300)) + .collect::<Vec<_>>(), + ); + for _ in 0..30000 { + forest.insert(&rng.gen()); + } + accumulator::test::assert_unique_outputs( + &mut forest, + &rng.sample_iter(repeat(Standard).take(300)) + .collect::<Vec<_>>(), + ); +} diff --git a/manta-pay/src/test/mod.rs b/manta-pay/src/test/mod.rs new file mode 100644 index 000000000..e35ea0352 --- /dev/null +++ b/manta-pay/src/test/mod.rs @@ -0,0 +1,20 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Manta Pay Testing + +pub mod merkle_tree; +pub mod transfer; diff --git a/manta-pay/src/test.rs b/manta-pay/src/test/transfer.rs similarity index 98% rename from manta-pay/src/test.rs rename to manta-pay/src/test/transfer.rs index fd6947101..b6f6d4707 100644 --- a/manta-pay/src/test.rs +++ b/manta-pay/src/test/transfer.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Manta Pay Testing +//! Manta Pay Transfer Testing use crate::config::{self, FullParameters, Mint, PrivateTransfer, Reclaim}; use manta_crypto::{ diff --git a/manta-pay/src/wallet.rs b/manta-pay/src/wallet.rs index 617d52904..e1b100fe8 100644 --- a/manta-pay/src/wallet.rs +++ b/manta-pay/src/wallet.rs @@ -17,23 +17,75 @@ //! Manta Pay Base Wallet Implementation use crate::{ - config::{Config, MerkleTreeConfiguration}, + config::{Bls12_381_Edwards, Config, MerkleTreeConfiguration, SecretKey}, + crypto::{constraint::arkworks::Fp, ecc::arkworks::ProjectiveCurve}, key::TestnetKeySecret, }; +use ark_ff::PrimeField; +use blake2::{ + digest::{Update, VariableOutput}, + Blake2sVar, +}; use manta_accounting::{ - asset::{Asset, HashAssetMap}, + asset::HashAssetMap, + key::{self, HierarchicalKeyDerivationScheme}, wallet::signer::{self, AssetMapKey}, }; -use manta_crypto::merkle_tree; +use manta_crypto::{key::KeyDerivationFunction, merkle_tree}; + +/// Hierarchical Key Derivation Function +pub struct HierarchicalKeyDerivationFunction; + +impl KeyDerivationFunction for HierarchicalKeyDerivationFunction { + type Key = <TestnetKeySecret as HierarchicalKeyDerivationScheme>::SecretKey; + type Output = SecretKey; + + #[inline] + fn derive(secret_key: &Self::Key) -> Self::Output { + // FIXME: Check that this conversion is logical/safe. + let bytes: [u8; 32] = secret_key + .private_key() + .to_bytes() + .try_into() + .expect("The private key has 32 bytes."); + Fp(<Bls12_381_Edwards as ProjectiveCurve>::ScalarField::from_le_bytes_mod_order(&bytes)) + } +} + +impl merkle_tree::forest::Configuration for MerkleTreeConfiguration { + type Index = u8; + + #[inline] + fn tree_index(leaf: &merkle_tree::Leaf<Self>) -> Self::Index { + let mut hasher = Blake2sVar::new(1).unwrap(); + hasher.update( + &ark_ff::to_bytes!(leaf.0).expect("Converting to bytes is not allowed to fail."), + ); + let mut result = [0]; + hasher + .finalize_variable(&mut result) + .expect("Hashing is not allowed to fail."); + result[0] + } +} + +/// Signer UTXO Set +pub type UtxoSet = merkle_tree::forest::TreeArrayMerkleForest< + MerkleTreeConfiguration, + merkle_tree::fork::ForkedTree< + MerkleTreeConfiguration, + merkle_tree::full::Full<MerkleTreeConfiguration>, + >, + 256, +>; -/* TODO: impl signer::Configuration for Config { - type HierarchicalKeyDerivationScheme = TestnetKeySecret; - type UtxoSet = merkle_tree::full::FullMerkleTree<MerkleTreeConfiguration>; + type HierarchicalKeyDerivationScheme = + key::Map<TestnetKeySecret, HierarchicalKeyDerivationFunction>; + type UtxoSet = UtxoSet; type AssetMap = HashAssetMap<AssetMapKey<Self>>; type Rng = rand_chacha::ChaCha20Rng; } -/// +/// Signer pub type Signer = signer::Signer<Config>; -*/ diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index 65cabbc79..4cbaa7aa9 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -16,6 +16,8 @@ //! Utilities +// TODO: Find a better way to abstract the `Rollback` trait. + #![no_std] #![cfg_attr(doc_cfg, feature(doc_cfg))] #![forbid(rustdoc::broken_intra_doc_links)] @@ -47,3 +49,17 @@ macro_rules! from_variant_impl { } }; } + +/// Rollback Trait +/// +/// This trait should be implemented by strucutres which have a canonical working state which can be +/// discarded easily. +pub trait Rollback { + /// Rolls back `self` to the previous state. + /// + /// # Implementation Note + /// + /// Rolling back to the previous state must be idempotent, i.e. two consecutive calls to + /// [`rollback`](Self::rollback) should do the same as one call. + fn rollback(&mut self); +} From 5179fa0532ac185dee34d4efedcb8330757c3b13 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 7 Jan 2022 23:27:51 -0500 Subject: [PATCH 170/275] fix: solved issues in merkle tree forking mechanism --- manta-accounting/src/wallet/signer.rs | 1 + manta-crypto/Cargo.toml | 4 + manta-crypto/src/merkle_tree/forest.rs | 9 +- manta-crypto/src/merkle_tree/fork.rs | 136 ++++++++++++++--- manta-crypto/src/merkle_tree/full.rs | 6 +- manta-crypto/src/merkle_tree/partial.rs | 91 +++++++++--- manta-crypto/src/merkle_tree/path.rs | 12 +- manta-crypto/src/merkle_tree/single_path.rs | 2 +- manta-crypto/src/merkle_tree/test.rs | 153 +++++++++++++++++++- manta-crypto/src/merkle_tree/tree.rs | 11 +- manta-pay/src/test/merkle_tree.rs | 20 ++- manta-util/src/lib.rs | 11 +- manta-util/src/pointer.rs | 2 +- 13 files changed, 396 insertions(+), 62 deletions(-) diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 00c6caf76..d7a75688c 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -374,6 +374,7 @@ where self.insert_next_item(utxo, encrypted_note, &mut void_numbers, &mut assets)?; } // FIXME: Do we need to check the void numbers which survived the above loop? + self.utxo_set.commit(); Ok(SyncResponse::new(assets)) } diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index af75492df..03da39470 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -38,3 +38,7 @@ test = [] derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } manta-util = { path = "../manta-util" } rand_core = { version = "0.6.3", default-features = false } + +[dev-dependencies] +rand = "0.8.4" + diff --git a/manta-crypto/src/merkle_tree/forest.rs b/manta-crypto/src/merkle_tree/forest.rs index dd26e4faa..fd802f849 100644 --- a/manta-crypto/src/merkle_tree/forest.rs +++ b/manta-crypto/src/merkle_tree/forest.rs @@ -460,7 +460,7 @@ where P: PointerFamily<T>, M: Default + InnerMap<C>, LeafDigest<C>: Clone + Default, - InnerDigest<C>: Default, + InnerDigest<C>: Clone + Default + PartialEq, { #[inline] fn rollback(&mut self) { @@ -468,4 +468,11 @@ where tree.reset_fork(&self.parameters); } } + + #[inline] + fn commit(&mut self) { + for tree in &mut self.forest.array { + tree.merge_fork(&self.parameters); + } + } } diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs index fecd602b6..57e01909f 100644 --- a/manta-crypto/src/merkle_tree/fork.rs +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -17,13 +17,13 @@ //! Merkle Tree Forks // TODO: Implement derive-able traits for these types. -// TODO: See if we can get rid of the smart pointer logic. +// TODO: See if we can get rid of the smart pointer logic (we can for `ForkedTree` at least). use crate::merkle_tree::{ capacity, inner_tree::{BTreeMap, InnerMap, PartialInnerTree}, partial::Partial, - path::CurrentInnerPath, + path::{CurrentInnerPath, InnerPath}, Configuration, CurrentPath, InnerDigest, Leaf, LeafDigest, Node, Parameters, Parity, Path, PathError, Root, Tree, WithProofs, }; @@ -32,6 +32,8 @@ use core::{borrow::Borrow, fmt::Debug, hash::Hash, marker::PhantomData, mem, ops use manta_util::pointer::{self, PointerFamily}; /// Fork-able Merkle Tree +#[derive(derivative::Derivative)] +#[derivative(Debug(bound = "P::Strong: Debug"))] pub struct Trunk<C, T, P = pointer::SingleThreaded> where C: Configuration + ?Sized, @@ -233,6 +235,11 @@ impl Default for BaseContribution { } /// Merkle Tree Fork +#[derive(derivative::Derivative)] +#[derivative( + Debug(bound = "P::Weak: Debug, LeafDigest<C>: Debug, InnerDigest<C>: Debug, M: Debug"), + Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default") +)] pub struct Fork<C, T, P = pointer::SingleThreaded, M = BTreeMap<C>> where C: Configuration + ?Sized, @@ -452,6 +459,13 @@ where } } + /// Checks if `self` is still attached to `trunk`. + #[inline] + fn check_attachment(&self) -> Option<()> { + let _ = P::upgrade(&self.base)?; + Some(()) + } + /// Computes the length of this fork of the tree. #[inline] pub fn len(&self) -> usize { @@ -473,26 +487,87 @@ where /// #[inline] pub fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>> { - self.branch.get_leaf_digest(index) + self.branch.leaf_digest(index) } /// #[inline] - pub fn position(&self, leaf_digest: &LeafDigest<C>) -> Option<usize> { - todo!() + pub fn position(&self, leaf_digest: &LeafDigest<C>) -> Option<usize> + where + LeafDigest<C>: PartialEq, + { + self.branch.position(leaf_digest) } /// #[inline] pub fn current_leaf(&self) -> Option<&LeafDigest<C>> { - // self.branch.current_leaf() - todo!() + self.branch.current_leaf() } /// #[inline] - pub fn current_path(&self, parameters: &Parameters<C>) -> CurrentPath<C> { - todo!() + pub fn current_path(&self) -> CurrentPath<C> + where + LeafDigest<C>: Clone + Default, + InnerDigest<C>: Clone + Default + PartialEq, + { + self.branch.current_path() + } + + /// + #[inline] + pub fn path(&self, parameters: &Parameters<C>, index: usize) -> Result<Path<C>, PathError> + where + T: WithProofs<C>, + LeafDigest<C>: Clone + Default, + InnerDigest<C>: Clone, + { + let length = self.len(); + if index > 0 && index >= length { + return Err(PathError::IndexTooLarge { length }); + } + if index < self.branch.starting_leaf_index() { + match P::upgrade(&self.base) { + Some(base) => { + let base_index = Node(index); + let base_path = base.path(parameters, base_index.0)?; + + let fork_index = self.branch.starting_leaf_node(); + let mut fork_path = self.branch.path_unchecked(fork_index.0); + + if !Node::are_siblings(&base_index, &fork_index) { + let matching_index = base_index + .parents() + .zip(fork_index.parents()) + .position(|(b, f)| Node::are_siblings(&b, &f)) + .unwrap(); + + fork_path.inner_path.path[matching_index] = InnerPath::fold( + parameters, + fork_index, + fork_index.join_leaves( + parameters, + self.leaf_digest(fork_index.0) + .unwrap_or(&Default::default()), + &fork_path.sibling_digest, + ), + &fork_path.inner_path.path[..matching_index], + ); + + fork_path.inner_path.path[..matching_index] + .clone_from_slice(&base_path.inner_path.path[..matching_index]); + } + + fork_path.inner_path.leaf_index = base_path.inner_path.leaf_index; + fork_path.sibling_digest = base_path.sibling_digest; + Ok(fork_path) + } + _ => Err(PathError::MissingPath), + } + } else { + Ok(self.branch.path_unchecked(index)) + } } /// Appends a new `leaf` onto this fork. @@ -504,23 +579,30 @@ where where LeafDigest<C>: Default, { - let _ = P::upgrade(&self.base)?; + self.check_attachment()?; Some(self.branch.push(parameters, leaf)) } /// Appends a new `leaf_digest` onto this fork. + /// + /// Returns `None` if this fork has been detached from its trunk. Use [`attach`](Self::attach) + /// to re-associate a trunk to this fork. #[inline] fn maybe_push_digest<F>(&mut self, parameters: &Parameters<C>, leaf_digest: F) -> Option<bool> where F: FnOnce() -> Option<LeafDigest<C>>, LeafDigest<C>: Default, { - let _ = P::upgrade(&self.base)?; + self.check_attachment()?; self.branch.maybe_push_digest(parameters, leaf_digest) } } /// Forked Tree +#[derive(derivative::Derivative)] +#[derivative(Debug( + bound = "P::Strong: Debug, P::Weak: Debug, LeafDigest<C>: Debug, InnerDigest<C>: Debug, M: Debug" +))] pub struct ForkedTree<C, T, P = pointer::SingleThreaded, M = BTreeMap<C>> where C: Configuration + ?Sized, @@ -584,6 +666,22 @@ where self.fork = self.trunk.fork(parameters); } + /// Merges the fork of this tree back into the trunk returning `true` if it was successful. + #[inline] + pub fn merge_fork(&mut self, parameters: &Parameters<C>) -> bool + where + LeafDigest<C>: Clone + Default, + InnerDigest<C>: Default, + { + if let Err(fork) = self.trunk.merge(parameters, mem::take(&mut self.fork)) { + self.fork = fork; + false + } else { + self.reset_fork(parameters); + true + } + } + /// Converts `self` back into its inner [`Tree`]. /// /// # Safety @@ -602,7 +700,7 @@ where P: PointerFamily<T>, M: Default + InnerMap<C>, LeafDigest<C>: Clone + Default, - InnerDigest<C>: Default, + InnerDigest<C>: Clone + Default + PartialEq, { #[inline] fn new(parameters: &Parameters<C>) -> Self { @@ -626,8 +724,8 @@ where #[inline] fn current_path(&self, parameters: &Parameters<C>) -> CurrentPath<C> { - // TODO: self.fork.current_path(parameters) - todo!() + let _ = parameters; + self.fork.current_path() } #[inline] @@ -645,8 +743,8 @@ where T: Tree<C> + WithProofs<C>, P: PointerFamily<T>, M: Default + InnerMap<C>, - LeafDigest<C>: Clone + Default, - InnerDigest<C>: Default, + LeafDigest<C>: Clone + Default + PartialEq, + InnerDigest<C>: Clone + Default, { #[inline] fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>> { @@ -655,8 +753,7 @@ where #[inline] fn position(&self, leaf_digest: &LeafDigest<C>) -> Option<usize> { - // self.fork.position(leaf_digest) - todo!() + self.fork.position(leaf_digest) } #[inline] @@ -673,7 +770,6 @@ where #[inline] fn path(&self, parameters: &Parameters<C>, index: usize) -> Result<Path<C>, PathError> { - // self.fork.path(parameters, index) - todo!() + self.fork.path(parameters, index) } } diff --git a/manta-crypto/src/merkle_tree/full.rs b/manta-crypto/src/merkle_tree/full.rs index f30dde0ba..204ee217d 100644 --- a/manta-crypto/src/merkle_tree/full.rs +++ b/manta-crypto/src/merkle_tree/full.rs @@ -167,7 +167,11 @@ where #[inline] fn current_path(&self, parameters: &Parameters<C>) -> CurrentPath<C> { let _ = parameters; - let leaf_index = Node(self.len() - 1); + let length = self.len(); + if length == 0 { + return Default::default(); + } + let leaf_index = Node(length - 1); CurrentPath::from_inner( self.get_owned_leaf_sibling(leaf_index), self.inner_digests.current_path_unchecked(leaf_index), diff --git a/manta-crypto/src/merkle_tree/partial.rs b/manta-crypto/src/merkle_tree/partial.rs index aa5221d89..155941c02 100644 --- a/manta-crypto/src/merkle_tree/partial.rs +++ b/manta-crypto/src/merkle_tree/partial.rs @@ -93,16 +93,22 @@ where self.leaf_digests } - /// Returns the starting leaf index for this tree. + /// Returns the starting leaf [`Node`] for this tree. #[inline] - pub fn starting_leaf_index(&self) -> Node { + pub fn starting_leaf_node(&self) -> Node { self.inner_digests.starting_leaf_index() } + /// Returns the starting leaf index for this tree. + #[inline] + pub fn starting_leaf_index(&self) -> usize { + self.starting_leaf_node().0 + } + /// Returns the number of leaves in this tree. #[inline] pub fn len(&self) -> usize { - self.starting_leaf_index().0 + self.leaf_digests.len() + self.starting_leaf_index() + self.leaf_digests.len() } /// Returns `true` if this tree is empty. @@ -119,15 +125,27 @@ where /// #[inline] - pub fn get_leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>> { - self.leaf_digests.get(index - self.starting_leaf_index().0) + pub fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>> { + self.leaf_digests.get(index - self.starting_leaf_index()) + } + + /// + #[inline] + pub fn position(&self, leaf_digest: &LeafDigest<C>) -> Option<usize> + where + LeafDigest<C>: PartialEq, + { + self.leaf_digests + .iter() + .position(move |d| d == leaf_digest) + .map(move |i| i + self.starting_leaf_index()) } /// Returns the sibling leaf node to `index`. #[inline] pub fn get_leaf_sibling(&self, index: Node) -> Option<&LeafDigest<C>> { self.leaf_digests - .get((index - self.starting_leaf_index().0).sibling().0) + .get((index - self.starting_leaf_index()).sibling().0) } /// Returns an owned sibling leaf node to `index`. @@ -139,6 +157,44 @@ where self.get_leaf_sibling(index).cloned().unwrap_or_default() } + /// + #[inline] + pub fn current_leaf(&self) -> Option<&LeafDigest<C>> { + self.leaf_digests.last() + } + + /// + #[inline] + pub fn current_path(&self) -> CurrentPath<C> + where + LeafDigest<C>: Clone + Default, + InnerDigest<C>: Clone + PartialEq, + { + let length = self.len(); + if length == 0 { + return Default::default(); + } + let leaf_index = Node(length - 1); + CurrentPath::from_inner( + self.get_owned_leaf_sibling(leaf_index), + self.inner_digests.current_path_unchecked(leaf_index), + ) + } + + /// + #[inline] + pub fn path_unchecked(&self, index: usize) -> Path<C> + where + LeafDigest<C>: Clone + Default, + InnerDigest<C>: Clone, + { + let leaf_index = Node(index); + Path::from_inner( + self.get_owned_leaf_sibling(leaf_index), + self.inner_digests.path_unchecked(leaf_index), + ) + } + /// Appends a `leaf_digest` with index given by `leaf_index` into the tree. #[inline] pub fn push_leaf_digest( @@ -217,7 +273,7 @@ where #[inline] fn current_leaf(&self) -> Option<&LeafDigest<C>> { - self.leaf_digests.last() + self.current_leaf() } #[inline] @@ -228,11 +284,7 @@ where #[inline] fn current_path(&self, parameters: &Parameters<C>) -> CurrentPath<C> { let _ = parameters; - let leaf_index = Node(self.len() - 1); - CurrentPath::from_inner( - self.get_owned_leaf_sibling(leaf_index), - self.inner_digests.current_path_unchecked(leaf_index), - ) + self.current_path() } #[inline] @@ -253,15 +305,12 @@ where { #[inline] fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>> { - self.get_leaf_digest(index) + self.leaf_digest(index) } #[inline] fn position(&self, leaf_digest: &LeafDigest<C>) -> Option<usize> { - self.leaf_digests - .iter() - .position(move |d| d == leaf_digest) - .map(move |i| i + self.starting_leaf_index().0) + self.position(leaf_digest) } #[inline] @@ -284,14 +333,10 @@ where if index > 0 && index >= length { return Err(PathError::IndexTooLarge { length }); } - if index < self.starting_leaf_index().0 { + if index < self.starting_leaf_index() { return Err(PathError::MissingPath); } - let leaf_index = Node(index); - Ok(Path::from_inner( - self.get_owned_leaf_sibling(leaf_index), - self.inner_digests.path_unchecked(leaf_index), - )) + Ok(self.path_unchecked(index)) } #[inline] diff --git a/manta-crypto/src/merkle_tree/path.rs b/manta-crypto/src/merkle_tree/path.rs index e0402469c..026b2831e 100644 --- a/manta-crypto/src/merkle_tree/path.rs +++ b/manta-crypto/src/merkle_tree/path.rs @@ -149,7 +149,7 @@ where /// Folds `iter` into a root using the path folding algorithm for [`InnerPath`]. #[inline] - fn fold<'i, I>( + pub(crate) fn fold<'i, I>( parameters: &'i Parameters<C>, index: Node, base: InnerDigest<C>, @@ -228,7 +228,7 @@ where #[derivative( Clone(bound = "InnerDigest<C>: Clone"), Debug(bound = "InnerDigest<C>: Debug"), - Default(bound = "InnerDigest<C>: Default"), + Default(bound = ""), Eq(bound = "InnerDigest<C>: Eq"), Hash(bound = "InnerDigest<C>: Hash"), PartialEq(bound = "InnerDigest<C>: PartialEq") @@ -426,9 +426,11 @@ where accumulator = parameters.join(&accumulator, &default_inner_digest); depth += 1; } - mem::drop(self.path.drain(1..i)); - self.path[0] = last_accumulator; + + mem::drop(self.path.drain(0..i)); + self.path.insert(0, last_accumulator); accumulator = parameters.join(&self.path[0], &accumulator); + Self::fold( parameters, depth + 1, @@ -736,7 +738,7 @@ where #[derivative( Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug"), - Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default"), + Default(bound = "LeafDigest<C>: Default"), Eq(bound = "LeafDigest<C>: Eq, InnerDigest<C>: Eq"), Hash(bound = "LeafDigest<C>: Hash, InnerDigest<C>: Hash"), PartialEq(bound = "LeafDigest<C>: PartialEq, InnerDigest<C>: PartialEq") diff --git a/manta-crypto/src/merkle_tree/single_path.rs b/manta-crypto/src/merkle_tree/single_path.rs index b517354a7..4a7715025 100644 --- a/manta-crypto/src/merkle_tree/single_path.rs +++ b/manta-crypto/src/merkle_tree/single_path.rs @@ -83,7 +83,7 @@ where pub fn length_state(&self) -> Length { if self.leaf_digest.is_none() { Length::Empty - } else if self.current_path.leaf_index().0 < capacity::<C>() - 2 { + } else if self.current_path.leaf_index().0 < capacity::<C>() - 1 { Length::CanAccept } else { Length::Full diff --git a/manta-crypto/src/merkle_tree/test.rs b/manta-crypto/src/merkle_tree/test.rs index fef9b5620..dd1b01034 100644 --- a/manta-crypto/src/merkle_tree/test.rs +++ b/manta-crypto/src/merkle_tree/test.rs @@ -18,12 +18,12 @@ use crate::{ merkle_tree::{ - Configuration, HashConfiguration, InnerDigest, InnerHashParameters, Leaf, - LeafHashParameters, MerkleTree, Parameters, Tree, WithProofs, + Configuration, HashConfiguration, IdentityLeafHash, InnerDigest, InnerHash, + InnerHashParameters, Leaf, LeafHashParameters, MerkleTree, Parameters, Tree, WithProofs, }, rand::{CryptoRng, RngCore, Sample}, }; -use core::{fmt::Debug, hash::Hash}; +use core::{fmt::Debug, hash::Hash, marker::PhantomData, ops::BitXor}; /// Hash Parameter Sampling pub trait HashParameterSampling: HashConfiguration { @@ -158,3 +158,150 @@ where } } } + +/// Test Merkle Tree Configuration +/// +/// # Warning +/// +/// This is only meant for testing purposes, and should not be used in production or +/// cryptographically secure environments. +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Test<T = u64, const HEIGHT: usize = 20>(PhantomData<T>) +where + T: Clone + BitXor<Output = T> + Default + PartialEq; + +impl<T, const HEIGHT: usize> InnerHash for Test<T, HEIGHT> +where + T: Clone + BitXor<Output = T> + Default + PartialEq, +{ + type LeafDigest = T; + type Parameters = (); + type Output = T; + + #[inline] + fn join_in( + parameters: &Self::Parameters, + lhs: &Self::Output, + rhs: &Self::Output, + _: &mut (), + ) -> Self::Output { + let _ = parameters; + lhs.clone() ^ rhs.clone() + } + + #[inline] + fn join_leaves_in( + parameters: &Self::Parameters, + lhs: &Self::LeafDigest, + rhs: &Self::LeafDigest, + _: &mut (), + ) -> Self::Output { + let _ = parameters; + lhs.clone() ^ rhs.clone() + } +} + +impl<T, const HEIGHT: usize> HashConfiguration for Test<T, HEIGHT> +where + T: Clone + BitXor<Output = T> + Default + PartialEq, +{ + type LeafHash = IdentityLeafHash<T>; + type InnerHash = Test<T, HEIGHT>; +} + +impl<T, const HEIGHT: usize> Configuration for Test<T, HEIGHT> +where + T: Clone + BitXor<Output = T> + Default + PartialEq, +{ + const HEIGHT: usize = HEIGHT; +} + +/* +#[cfg(test)] +mod complete { + use super::*; + use crate::{ + merkle_tree::{ + self, + fork::{self, ForkedTree, Trunk}, + inner_tree, + }, + rand::{Rand, Standard}, + }; + use core::iter::repeat; + use manta_util::pointer::SingleThreaded; + use rand::thread_rng; + + #[test] + fn test_suite() { + let mut rng = thread_rng(); + let parameters = Default::default(); + + let mut single_path_tree = + merkle_tree::single_path::SinglePathMerkleTree::<Test>::new(parameters); + + let mut full_tree = merkle_tree::full::FullMerkleTree::<Test>::new(parameters); + + let leaves = rng + .sample_iter(repeat(Standard).take(1_000_000)) + .collect::<Vec<u64>>(); + + full_tree.extend(&leaves[0..1000]); + + // println!("{:#?}", full_tree); + + let mut forked_tree = MerkleTree::from_tree( + ForkedTree::<_, _, SingleThreaded, inner_tree::BTreeMap<Test>>::new( + full_tree.clone().tree, + &parameters, + ), + parameters, + ); + + use manta_util::Rollback; + + forked_tree.commit(); + + assert_valid_paths(&mut full_tree, &leaves[1000..3000]); + assert_valid_paths(&mut forked_tree, &leaves[1000..3000]); + + forked_tree.commit(); + + for (i, leaf) in leaves.iter().enumerate().take(1000) { + // println!("{:#?}", full_tree.path(i)); + // println!("{:#?}", forked_tree.path(i)); + // assert_eq!(full_tree.path(i), forked_tree.path(i), "Failed on {:?}", i); + assert_valid_path(&full_tree, i, leaf); + assert_valid_path(&forked_tree, i, leaf); + } + + forked_tree.commit(); + let tree = forked_tree.tree.into_tree(); + + assert_eq!(full_tree.tree, tree); + + /* + let mut trunk = Trunk::<_, _, SingleThreaded>::new(full_tree.clone().tree); + + println!("{:#?}", trunk); + + let mut fork = trunk.fork::<inner_tree::BTreeMap<Test>>(&parameters); + + println!("{:#?}", fork); + + for leaf in leaves.iter().take(1000) { + full_tree.push(leaf); + fork.push(&parameters, leaf); + assert_eq!(full_tree.current_path(), fork.current_path()); + // println!("{:#?}", fork); + // println!("{:#?}", full_tree); + } + + trunk.merge(&parameters, fork).unwrap(); + println!("{:#?}", trunk); + println!("{:#?}", full_tree); + assert_eq!(trunk.into_tree(), full_tree.tree); + */ + } +} +*/ diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index 74c841825..df2289c70 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -698,10 +698,10 @@ where T: Tree<C>, { /// Underlying Tree Structure - tree: T, + pub tree: T, /// Merkle Tree Parameters - parameters: Parameters<C>, + pub parameters: Parameters<C>, } impl<C, T> MerkleTree<C, T> @@ -1048,10 +1048,15 @@ where P: PointerFamily<T>, M: Default + InnerMap<C>, LeafDigest<C>: Clone + Default, - InnerDigest<C>: Default, + InnerDigest<C>: Clone + Default + PartialEq, { #[inline] fn rollback(&mut self) { self.tree.reset_fork(&self.parameters); } + + #[inline] + fn commit(&mut self) { + self.tree.merge_fork(&self.parameters); + } } diff --git a/manta-pay/src/test/merkle_tree.rs b/manta-pay/src/test/merkle_tree.rs index 31e73111f..cb291ddf3 100644 --- a/manta-pay/src/test/merkle_tree.rs +++ b/manta-pay/src/test/merkle_tree.rs @@ -54,6 +54,7 @@ fn test_suite() { &rng.gen(), ); + /* let mut tree = Tree::new(parameters); accumulator::test::assert_unique_outputs( @@ -70,9 +71,7 @@ fn test_suite() { .collect::<Vec<_>>(), ); - let parameters = tree.into_parameters(); - - let mut forest = Forest::new(parameters); + let mut forest = Forest::new(tree.into_parameters()); accumulator::test::assert_unique_outputs( &mut forest, @@ -87,4 +86,19 @@ fn test_suite() { &rng.sample_iter(repeat(Standard).take(300)) .collect::<Vec<_>>(), ); + */ + + /* + let mut forked_tree = ForkedTree::new(parameters); + + println!("{:?}", forked_tree.trunk().len()); + println!("{:?}", forked_tree.fork().len()); + + for _ in 0..1 { + forked_tree.insert(&rng.gen()); + } + + println!("{:?}", forked_tree.trunk().len()); + println!("{:?}", forked_tree.fork().len()); + */ } diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index 4cbaa7aa9..07e2d5a9a 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -60,6 +60,15 @@ pub trait Rollback { /// # Implementation Note /// /// Rolling back to the previous state must be idempotent, i.e. two consecutive calls to - /// [`rollback`](Self::rollback) should do the same as one call. + /// [`rollback`](Self::rollback) should have the same effect as one call. fn rollback(&mut self); + + /// Commits `self` to the current state, preventing a future call to + /// [`rollback`](Self::rollback) from clearing the state. + /// + /// # Implementation Note + /// + /// Commiting to the current state must be idempotent, i.e. two consecutive calls to + /// [`commit`](Self::commit) should have the same effect as one call. + fn commit(&mut self); } diff --git a/manta-util/src/pointer.rs b/manta-util/src/pointer.rs index f50126330..e7b3d62a2 100644 --- a/manta-util/src/pointer.rs +++ b/manta-util/src/pointer.rs @@ -32,7 +32,7 @@ pub trait PointerFamily<T>: sealed::Sealed { type Strong: AsRef<T> + Borrow<T> + Deref<Target = T>; /// Weak Pointer - type Weak; + type Weak: Default; /// Returns a new strong pointer holding `base`. fn new(base: T) -> Self::Strong; From 95c1d5138b9bbe5084671c42eb4776b2adab8b27 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sat, 8 Jan 2022 03:39:52 -0500 Subject: [PATCH 171/275] wip: starting implementing end-to-end internal tests --- manta-accounting/Cargo.toml | 15 +- manta-accounting/src/fs.rs | 448 +++++-------- manta-accounting/src/transfer/canonical.rs | 165 ++++- manta-accounting/src/wallet/signer.rs | 12 +- manta-accounting/src/wallet/state.rs | 19 +- manta-crypto/src/merkle_tree/forest.rs | 16 +- manta-crypto/src/merkle_tree/fork.rs | 12 +- manta-crypto/src/merkle_tree/partial.rs | 11 +- manta-crypto/src/merkle_tree/path.rs | 2 +- manta-crypto/src/merkle_tree/test.rs | 90 --- manta-pay/src/config.rs | 71 +- manta-pay/src/key.rs | 16 +- manta-pay/src/ledger.rs | 219 +++++-- manta-pay/src/lib.rs | 1 + manta-pay/src/simulation/mod.rs | 722 +++++++++++++++++++++ manta-pay/src/test/merkle_tree.rs | 61 +- manta-pay/src/test/mod.rs | 2 +- manta-pay/src/wallet.rs | 48 +- 18 files changed, 1345 insertions(+), 585 deletions(-) create mode 100644 manta-pay/src/simulation/mod.rs diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index c36fe212e..4c0f9fcc8 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -26,22 +26,29 @@ maintenance = { status = "actively-developed" } [features] # Cocoon Filesystem Adapters -cocoon-adapters = ["cocoon/std", "zeroize"] +cocoon-fs = [ + "async-std", + "cocoon/std", + "futures", + "zeroize/alloc", +] # Standard Library -std = ["cocoon-adapters"] +std = ["cocoon/std", "async-std"] # Testing Frameworks test = [] [dependencies] +async-std = { version = "1.10.0", optional = true } cocoon = { version = "0.3.0", optional = true, default-features = false } derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } derive_more = { version = "0.99.16", default-features = false, features = ["add", "add_assign", "display", "from", "mul", "mul_assign", "sum"] } +futures = { version = "0.3.19", optional = true } manta-crypto = { path = "../manta-crypto" } manta-util = { path = "../manta-util" } -rayon = { version = "1.5.1", optional = true } -zeroize = { version = "1.4.3", optional = true, default-features = false, features = ["alloc", "zeroize_derive"] } +rayon = { version = "1.5.1", optional = true, default-features = false } +zeroize = { version = "1.4.3", optional = true, default-features = false } [dev-dependencies] rand = "0.8.4" diff --git a/manta-accounting/src/fs.rs b/manta-accounting/src/fs.rs index 99f495d8c..ca2c8b4be 100644 --- a/manta-accounting/src/fs.rs +++ b/manta-accounting/src/fs.rs @@ -16,80 +16,152 @@ //! Encrypted Filesystem Primitives -// FIXME: Change this to a "payload parsing" scheme, like serdes but ensure it gets encrypted -// before saving and decrypted after loading. So we need something like EncryptedSerialize -// and DecryptedDeserialize. +// FIXME: Add asynchronous streaming interfaces. -/// Filesystem Encrypted Loading -pub trait Load: Sized { - /// Path Type - type Path: ?Sized; +use core::future::Future; - /// Loading Key Type - type LoadingKey: ?Sized; +/// Serialization +pub trait Serialize { + /// Appends representation of `self` in bytes to `buffer`. + fn serialize(&self, buffer: &mut Vec<u8>); - /// Load Error Type - type Error; + /// Converts `self` into a vector of bytes. + #[inline] + fn to_vec(&self) -> Vec<u8> { + let mut buffer = Vec::new(); + self.serialize(&mut buffer); + buffer + } +} - /// Loads an element of type `Self` from `path` unlocking it with the `loading_key`. - fn load<P>(path: P, loading_key: &Self::LoadingKey) -> Result<Self, Self::Error> - where - P: AsRef<Self::Path>; +impl Serialize for u8 { + #[inline] + fn serialize(&self, buffer: &mut Vec<u8>) { + buffer.push(*self); + } } -/// Filesystem Encrypted Loading with Extra Data -pub trait LoadWith<T>: Load { - /// Loads an element of type `Self` along with additional data from `path` unlocking it with - /// the `loading_key`. - fn load_with<P>(path: P, loading_key: &Self::LoadingKey) -> Result<(Self, T), Self::Error> - where - P: AsRef<Self::Path>; +impl<T> Serialize for [T] +where + T: Serialize, +{ + #[inline] + fn serialize(&self, buffer: &mut Vec<u8>) { + for item in self { + item.serialize(buffer); + } + } +} + +impl<T, const N: usize> Serialize for [T; N] +where + T: Serialize, +{ + #[inline] + fn serialize(&self, buffer: &mut Vec<u8>) { + for item in self { + item.serialize(buffer); + } + } +} + +/// Deserialization +pub trait Deserialize: Sized { + /// Error Type + type Error; + + /// Parses the input `buffer` into a concrete value of type `Self` if possible. + fn deserialize(buffer: Vec<u8>) -> Result<Self, Self::Error>; } /// Filesystem Encrypted Saving -pub trait Save { +pub trait SaveEncrypted { /// Path Type - type Path: ?Sized; + type Path; /// Saving Key Type - type SavingKey: ?Sized; + type SavingKey; - /// Save Error Type + /// Saving Error type Error; - /// Saves `self` to `path` locking it with the `saving_key`. - fn save<P>(self, path: P, saving_key: &Self::SavingKey) -> Result<(), Self::Error> + /// Saving Future + type Future: Future<Output = Result<(), Self::Error>>; + + /// Saves the `payload` to `path` using the `saving_key` to encrypt it. + fn save_bytes(path: Self::Path, saving_key: Self::SavingKey, payload: Vec<u8>) -> Self::Future; + + /// Saves the `payload` to `path` after serializing using the `saving_key` to encrypt it. + #[inline] + fn save<S>(path: Self::Path, saving_key: Self::SavingKey, payload: &S) -> Self::Future where - P: AsRef<Self::Path>; + S: Serialize, + { + Self::save_bytes(path, saving_key, payload.to_vec()) + } } -/// Filesystem Encrypted Saving with Extra Data -pub trait SaveWith<T>: Save { - /// Saves `self` along with `additional` data to `path` locking it with the `saving_key`. - fn save_with<P>( - self, - additional: T, - path: P, - saving_key: &Self::SavingKey, - ) -> Result<(), Self::Error> - where - P: AsRef<Self::Path>; +/// Filesystem Decrypted Loading +pub trait LoadDecrypted { + /// Path Type + type Path; + + /// Loading Key Type + type LoadingKey; + + /// Loading Error Type + type Error; + + /// Loading Future + type Future: Future<Output = Result<Vec<u8>, Self::Error>>; + + /// Loads a vector of bytes from `path` using `loading_key` to decrypt them. + fn load_bytes(path: Self::Path, loading_key: Self::LoadingKey) -> Self::Future; } -/// Cocoon [`Load`] and [`Save`] Adapters -#[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +/// Loads a vector of bytes from `path` using `loading_key` to decrypt them, then deserializing +/// the bytes to a concrete value of type `D`. +#[inline] +pub async fn load<L, D>(path: L::Path, loading_key: L::LoadingKey) -> Result<D, LoadError<D, L>> +where + L: LoadDecrypted, + D: Deserialize, +{ + match L::load_bytes(path, loading_key).await { + Ok(bytes) => D::deserialize(bytes).map_err(LoadError::Deserialize), + Err(err) => Err(LoadError::Loading(err)), + } +} + +/// Loading Error +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum LoadError<D, L> +where + D: Deserialize, + L: LoadDecrypted, +{ + /// Deserialization Error + Deserialize(D::Error), + + /// Payload Loading Error + Loading(L::Error), +} + +/// Cocoon Adapters +#[cfg(feature = "cocoon-fs")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "cocoon-fs")))] pub mod cocoon { use super::*; - use cocoon_crate::{Cocoon, Error as CocoonError}; - use core::{ - convert::{Infallible, TryInto}, - fmt, mem, - ops::Drop, + use async_std::{ + fs::OpenOptions, + io::{Error as IoError, ReadExt, WriteExt}, + path::PathBuf, }; + use cocoon_crate::{Cocoon, Error as CocoonError}; + use core::fmt; + use futures::future::LocalBoxFuture; use manta_util::from_variant_impl; - use std::{fs::OpenOptions, io::Error as IoError, path::Path}; - use zeroize::Zeroize; + use zeroize::Zeroizing; /// Cocoon Loading/Saving Error #[derive(Debug)] @@ -114,252 +186,62 @@ pub mod cocoon { } } + #[cfg(feature = "std")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] impl std::error::Error for Error {} - /// Payload Parsing - pub trait FromPayload: Sized { - /// Parsing Error Type - type Error; - - /// Converts the `payload` into an element of type `Self`. - fn from_payload(payload: &[u8]) -> Result<Self, Self::Error>; - } - - impl<const N: usize> FromPayload for [u8; N] { - type Error = core::array::TryFromSliceError; - - #[inline] - fn from_payload(payload: &[u8]) -> Result<Self, Self::Error> { - (*payload).try_into() - } - } - - impl FromPayload for Vec<u8> { - type Error = Infallible; - - #[inline] - fn from_payload(payload: &[u8]) -> Result<Self, Self::Error> { - Ok(payload.to_vec()) - } - } - - /// Owned Payload Parsing - pub trait FromPayloadOwned: Sized { - /// Parsing Error Type - type Error; - - /// Converts the `payload` into an element of type `Self`. - fn from_payload_owned(payload: Vec<u8>) -> Result<Self, Self::Error>; - } - - impl<const N: usize> FromPayloadOwned for [u8; N] { - type Error = Vec<u8>; - - #[inline] - fn from_payload_owned(payload: Vec<u8>) -> Result<Self, Self::Error> { - payload.try_into() - } - } - - impl FromPayloadOwned for Vec<u8> { - type Error = Infallible; - - #[inline] - fn from_payload_owned(payload: Vec<u8>) -> Result<Self, Self::Error> { - Ok(payload) - } - } - - /// Cocoon [`Load`] Adapter - #[derive(Zeroize)] - #[zeroize(drop)] - pub struct Loader(Vec<u8>); - - impl Loader { - /// Parses the loaded data into an element of type `T` by taking a referece to the payload. - #[inline] - pub fn parse<T>(self) -> Result<T, T::Error> - where - T: FromPayload, - { - T::from_payload(&self.0) - } - - /// Parses the loaded data into an element of type `T` by taking ownership of the payload. - #[inline] - pub fn parse_owned<T>(mut self) -> Result<T, T::Error> - where - T: FromPayloadOwned, - { - T::from_payload_owned(mem::take(&mut self.0)) - } - } - - impl Load for Loader { - type Path = Path; - - type LoadingKey = [u8]; + /// Cocoon [`SaveEncrypted`] Adapter + #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct Save; + impl SaveEncrypted for Save { + type Path = PathBuf; + type SavingKey = Vec<u8>; type Error = Error; + type Future = LocalBoxFuture<'static, Result<(), Self::Error>>; #[inline] - fn load<P>(path: P, loading_key: &Self::LoadingKey) -> Result<Self, Self::Error> - where - P: AsRef<Self::Path>, - { - Ok(Self( - Cocoon::new(loading_key).parse(&mut OpenOptions::new().read(true).open(path)?)?, - )) + fn save_bytes( + path: Self::Path, + saving_key: Self::SavingKey, + payload: Vec<u8>, + ) -> Self::Future { + Box::pin(async { + let saving_key = Zeroizing::new(saving_key); + let mut buffer = Zeroizing::new(Vec::new()); + Cocoon::new(&saving_key).dump(payload, &mut buffer.as_mut_slice())?; + OpenOptions::new() + .create(true) + .truncate(true) + .write(true) + .open(path) + .await? + .write_all(&buffer) + .await?; + Ok(()) + }) } } - /// Payload Extraction - pub trait Payload { - /// Extracts a byte vector payload from `self`. - fn payload(&self) -> Vec<u8>; - } - - impl<const N: usize> Payload for [u8; N] { - #[inline] - fn payload(&self) -> Vec<u8> { - (*self).into() - } - } - - impl Payload for &[u8] { - #[inline] - fn payload(&self) -> Vec<u8> { - self.to_vec() - } - } - - impl Payload for Vec<u8> { - #[inline] - fn payload(&self) -> Vec<u8> { - self.clone() - } - } - - /// Cocoon [`Save`] Borrowed Data Adapter - #[derive(Clone, Copy)] - pub struct Saver<'t, T>( - /// Payload Source - pub &'t T, - ) - where - T: Payload; - - impl<'t, T> Save for Saver<'t, T> - where - T: Payload, - { - type Path = Path; - - type SavingKey = [u8]; - - type Error = Error; - - #[inline] - fn save<P>(self, path: P, saving_key: &Self::SavingKey) -> Result<(), Self::Error> - where - P: AsRef<Self::Path>, - { - save_payload(self.0, path, saving_key) - } - } - - /// Cocoon [`Save`] Owned Data Adapter - #[derive(Zeroize)] - pub struct SaverOwned<T>(T) - where - T: Payload + Zeroize; - - impl<T> Drop for SaverOwned<T> - where - T: Payload + Zeroize, - { - #[inline] - fn drop(&mut self) { - self.0.zeroize(); - } - } - - impl<T> Save for SaverOwned<T> - where - T: Payload + Zeroize, - { - type Path = Path; - - type SavingKey = [u8]; + /// Cocoon [`LoadDecrypted`] Adapter + #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct Load; + impl LoadDecrypted for Load { + type Path = PathBuf; + type LoadingKey = Vec<u8>; type Error = Error; + type Future = LocalBoxFuture<'static, Result<Vec<u8>, Self::Error>>; #[inline] - fn save<P>(mut self, path: P, saving_key: &Self::SavingKey) -> Result<(), Self::Error> - where - P: AsRef<Self::Path>, - { - save_payload(&self.0, path, saving_key)?; - self.0.zeroize(); - Ok(()) - } - } - - /// Saves the payload generated from `source` to `path` using the `saving_key`. - #[inline] - fn save_payload<T, P>(source: &T, path: P, saving_key: &[u8]) -> Result<(), Error> - where - T: Payload, - P: AsRef<Path>, - { - // NOTE: We want to check that the file can be opened and that we can write to it - // before we extract the sensitive payload out of `self`. - let mut file = OpenOptions::new() - .create(true) - .truncate(true) - .write(true) - .open(path)?; - Ok(Cocoon::new(saving_key).dump(source.payload(), &mut file)?) - } - - /// Testing Suite - #[cfg(test)] - mod test { - use super::*; - use rand::{thread_rng, RngCore}; - - /// Tests loading some saved data using the [`Saver`] and [`Loader`] adapters. - #[test] - fn load_saved() { - let dir = tempfile::tempdir().expect("Temporary directory should have been created."); - let path = dir.path().join("load_saved.data"); - let mut rng = thread_rng(); - - // Generate random password to save to and load from the file system. - let mut password = [0; 256]; - rng.fill_bytes(&mut password); - - // Generate random payload. - let mut expected = [0; 2048]; - rng.fill_bytes(&mut expected); - - // Save the target payload to the file system. - Saver(&expected) - .save(&path, &password) - .expect("Payload should have been saved."); - - // Load the payload from the file system. - let observed = Loader::load(&path, &password) - .expect("Payload should have been loaded.") - .parse::<[u8; 2048]>() - .expect("Payload should have been parsed properly."); - - // Check that the payload matches. - assert_eq!(expected, observed); - - // Close the testing directory. - dir.close() - .expect("Temporary directory should have closed."); + fn load_bytes(path: Self::Path, loading_key: Self::LoadingKey) -> Self::Future { + Box::pin(async move { + let loading_key = Zeroizing::new(loading_key); + let mut buffer = Zeroizing::new(Vec::new()); + let mut file = OpenOptions::new().read(true).open(path).await?; + file.read_to_end(&mut buffer).await?; + Ok(Cocoon::parse_only(&loading_key).parse(&mut buffer.as_slice())?) + }) } } } diff --git a/manta-accounting/src/transfer/canonical.rs b/manta-accounting/src/transfer/canonical.rs index 69753eef5..42c52303d 100644 --- a/manta-accounting/src/transfer/canonical.rs +++ b/manta-accounting/src/transfer/canonical.rs @@ -14,13 +14,14 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Canonical Transaction Types +//! Canonical Transfer Types use crate::{ asset::{self, Asset, AssetValue}, - transfer::{Configuration, PreSender, Receiver, ReceivingKey, Sender, Transfer}, + transfer::{self, Configuration, PreSender, Receiver, ReceivingKey, Sender, Transfer}, }; use alloc::vec::Vec; +use core::{fmt::Debug, hash::Hash}; use manta_util::{create_seal, seal}; create_seal! {} @@ -77,7 +78,7 @@ macro_rules! transfer_alias { }; } -/// Mint Transaction Shape +/// [`Mint`] Transfer Shape /// /// ```text /// <1, 0, 1, 0> @@ -87,7 +88,7 @@ pub struct MintShape; impl_shape!(MintShape, 1, 0, 1, 0); -/// Mint Transaction +/// [`Mint`] Transfer Type pub type Mint<C> = transfer_alias!(C, MintShape); impl<C> Mint<C> @@ -101,7 +102,7 @@ where } } -/// Private Transfer Transaction Shape +/// [`PrivateTransfer`] Transfer Shape /// /// ```text /// <0, 2, 2, 0> @@ -111,7 +112,7 @@ pub struct PrivateTransferShape; impl_shape!(PrivateTransferShape, 0, 2, 2, 0); -/// Private Transfer Transaction +/// [`PrivateTransfer`] Transfer Type pub type PrivateTransfer<C> = transfer_alias!(C, PrivateTransferShape); impl<C> PrivateTransfer<C> @@ -128,7 +129,7 @@ where } } -/// Reclaim Transaction Shape +/// [`Reclaim`] Transfer Shape /// /// ```text /// <0, 2, 1, 1> @@ -147,7 +148,7 @@ impl_shape!( 1 ); -/// Reclaim Transaction +/// [`Reclaim`] Transfer pub type Reclaim<C> = transfer_alias!(C, ReclaimShape); impl<C> Reclaim<C> @@ -165,6 +166,60 @@ where } } +/// Transfer Shape +pub enum TransferShape { + /// [`Mint`] Transfer + Mint, + + /// [`PrivateTransfer`] Transfer + PrivateTransfer, + + /// [`Reclaim`] Transfer + Reclaim, +} + +impl TransferShape { + /// Selects the [`TransferShape`] for the given shape if it matches a canonical shape. + #[allow(clippy::absurd_extreme_comparisons)] // NOTE: We want these comparisons as values.. + #[inline] + pub fn select( + asset_id_is_some: bool, + sources: usize, + senders: usize, + receivers: usize, + sinks: usize, + ) -> Option<Self> { + const MINT_VISIBLE_ID: bool = MintShape::SOURCES + MintShape::SINKS > 0; + const PRIVATE_TRANSFER_VISIBLE_ID: bool = + PrivateTransferShape::SOURCES + PrivateTransferShape::SINKS > 0; + const RECLAIM_VISIBLE_ID: bool = ReclaimShape::SOURCES + ReclaimShape::SINKS > 0; + match (asset_id_is_some, sources, senders, receivers, sinks) { + ( + MINT_VISIBLE_ID, + MintShape::SOURCES, + MintShape::SENDERS, + MintShape::RECEIVERS, + MintShape::SINKS, + ) => Some(Self::Mint), + ( + PRIVATE_TRANSFER_VISIBLE_ID, + PrivateTransferShape::SOURCES, + PrivateTransferShape::SENDERS, + PrivateTransferShape::RECEIVERS, + PrivateTransferShape::SINKS, + ) => Some(Self::PrivateTransfer), + ( + RECLAIM_VISIBLE_ID, + ReclaimShape::SOURCES, + ReclaimShape::SENDERS, + ReclaimShape::RECEIVERS, + ReclaimShape::SINKS, + ) => Some(Self::Reclaim), + _ => None, + } + } +} + /// Canonical Transaction Type pub enum Transaction<C> where @@ -261,3 +316,97 @@ where )) } } + +/// Canonical Proving Contexts +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "transfer::ProvingContext<C>: Clone"), + Copy(bound = "transfer::ProvingContext<C>: Copy"), + Debug(bound = "transfer::ProvingContext<C>: Debug"), + Default(bound = "transfer::ProvingContext<C>: Default"), + Eq(bound = "transfer::ProvingContext<C>: Eq"), + Hash(bound = "transfer::ProvingContext<C>: Hash"), + PartialEq(bound = "transfer::ProvingContext<C>: PartialEq") +)] +pub struct ProvingContext<C> +where + C: Configuration, +{ + /// Mint Proving Context + pub mint: transfer::ProvingContext<C>, + + /// Private Transfer Proving Context + pub private_transfer: transfer::ProvingContext<C>, + + /// Reclaim Proving Context + pub reclaim: transfer::ProvingContext<C>, +} + +impl<C> ProvingContext<C> +where + C: Configuration, +{ + /// Selects the proving context for the given shape if it matches a canonical shape. + #[inline] + pub fn select( + &self, + asset_id_is_some: bool, + sources: usize, + senders: usize, + receivers: usize, + sinks: usize, + ) -> Option<&transfer::ProvingContext<C>> { + match TransferShape::select(asset_id_is_some, sources, senders, receivers, sinks)? { + TransferShape::Mint => Some(&self.mint), + TransferShape::PrivateTransfer => Some(&self.private_transfer), + TransferShape::Reclaim => Some(&self.reclaim), + } + } +} + +/// Canonical Verifying Contexts +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "transfer::VerifyingContext<C>: Clone"), + Copy(bound = "transfer::VerifyingContext<C>: Copy"), + Debug(bound = "transfer::VerifyingContext<C>: Debug"), + Default(bound = "transfer::VerifyingContext<C>: Default"), + Eq(bound = "transfer::VerifyingContext<C>: Eq"), + Hash(bound = "transfer::VerifyingContext<C>: Hash"), + PartialEq(bound = "transfer::VerifyingContext<C>: PartialEq") +)] +pub struct VerifyingContext<C> +where + C: Configuration, +{ + /// Mint Verifying Context + pub mint: transfer::VerifyingContext<C>, + + /// Private Transfer Verifying Context + pub private_transfer: transfer::VerifyingContext<C>, + + /// Reclaim Verifying Context + pub reclaim: transfer::VerifyingContext<C>, +} + +impl<C> VerifyingContext<C> +where + C: Configuration, +{ + /// Selects the verifying context for the given shape if it matches a canonical shape. + #[inline] + pub fn select( + &self, + asset_id_is_some: bool, + sources: usize, + senders: usize, + receivers: usize, + sinks: usize, + ) -> Option<&transfer::VerifyingContext<C>> { + match TransferShape::select(asset_id_is_some, sources, senders, receivers, sinks)? { + TransferShape::Mint => Some(&self.mint), + TransferShape::PrivateTransfer => Some(&self.private_transfer), + TransferShape::Reclaim => Some(&self.reclaim), + } + } +} diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index d7a75688c..d5ef4eb8f 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -30,11 +30,12 @@ use crate::{ self, batch::Join, canonical::{ - Mint, PrivateTransfer, PrivateTransferShape, Reclaim, Selection, Shape, Transaction, + Mint, PrivateTransfer, PrivateTransferShape, ProvingContext, Reclaim, Selection, Shape, + Transaction, }, - EncryptedNote, FullParameters, Parameters, PreSender, ProofSystemError, ProvingContext, - PublicKey, Receiver, ReceivingKey, SecretKey, Sender, SpendingKey, Transfer, TransferPost, - Utxo, VoidNumber, + EncryptedNote, FullParameters, Parameters, PreSender, ProofSystemError, PublicKey, + Receiver, ReceivingKey, SecretKey, Sender, SpendingKey, Transfer, TransferPost, Utxo, + VoidNumber, }, }; use alloc::{vec, vec::Vec}; @@ -478,6 +479,7 @@ where &mut self, transfer: Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>, ) -> Result<TransferPost<C>, SignerError<C>> { + /* TODO: transfer .into_post( FullParameters::new(&self.parameters, self.utxo_set.model()), @@ -485,6 +487,8 @@ where &mut self.rng, ) .map_err(Error::ProofSystemError) + */ + todo!() } /// Computes the next [`Join`](Join) element for an asset rebalancing round. diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 08b8ac194..cb13d27f3 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -41,7 +41,7 @@ use std::{ }; /// Balance State -pub trait BalanceState { +pub trait BalanceState: Default { /// Returns the current balance associated with this `id`. fn balance(&self, id: AssetId) -> AssetValue; @@ -154,11 +154,20 @@ pub type HashMapBalanceState<S = RandomState> = HashMap<AssetId, AssetValue, S>; #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] impl<S> BalanceState for HashMapBalanceState<S> where - S: BuildHasher, + S: BuildHasher + Default, { impl_balance_state_map_body! { HashMapEntry } } +/// Native Wallet +pub type NativeWallet<C, L, B = BTreeMapBalanceState> = Wallet< + <C as signer::Configuration>::HierarchicalKeyDerivationScheme, + C, + L, + signer::Signer<C>, + B, +>; + /// Wallet pub struct Wallet<H, C, L, S, B = BTreeMapBalanceState> where @@ -204,6 +213,12 @@ where } } + /// Starts a new [`Wallet`] from `signer` and `ledger` connections. + #[inline] + pub fn empty(signer: S, ledger: L) -> Self { + Self::new(signer, ledger, Default::default(), Default::default()) + } + /// Returns the current balance associated with this `id`. #[inline] pub fn balance(&self, id: AssetId) -> AssetValue { diff --git a/manta-crypto/src/merkle_tree/forest.rs b/manta-crypto/src/merkle_tree/forest.rs index fd802f849..0841b29c2 100644 --- a/manta-crypto/src/merkle_tree/forest.rs +++ b/manta-crypto/src/merkle_tree/forest.rs @@ -166,10 +166,10 @@ where F: Forest<C>, { /// Underlying Forest Structure - forest: F, + pub forest: F, /// Merkle Forest Parameters - parameters: Parameters<C>, + pub parameters: Parameters<C>, } impl<C, F> MerkleForest<C, F> @@ -371,6 +371,18 @@ where __: PhantomData<C>, } +impl<C, T, const N: usize> AsRef<[T; N]> for TreeArray<C, T, N> +where + C: Configuration + ?Sized, + C::Index: FixedIndex<N>, + T: Tree<C>, +{ + #[inline] + fn as_ref(&self) -> &[T; N] { + &self.array + } +} + impl<C, T, const N: usize> Default for TreeArray<C, T, N> where C: Configuration + ?Sized, diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs index 57e01909f..6380fbca5 100644 --- a/manta-crypto/src/merkle_tree/fork.rs +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -484,13 +484,13 @@ where self.branch.root() } - /// + /// Returns the leaf digest at the given `index` in the tree. #[inline] pub fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>> { self.branch.leaf_digest(index) } - /// + /// Returns the position of `leaf_digest` in the tree. #[inline] pub fn position(&self, leaf_digest: &LeafDigest<C>) -> Option<usize> where @@ -499,13 +499,13 @@ where self.branch.position(leaf_digest) } - /// + /// Returns the current (right-most) leaf of the tree. #[inline] pub fn current_leaf(&self) -> Option<&LeafDigest<C>> { self.branch.current_leaf() } - /// + /// Returns the current (right-most) path of the tree. #[inline] pub fn current_path(&self) -> CurrentPath<C> where @@ -515,7 +515,7 @@ where self.branch.current_path() } - /// + /// Returns the path at the given `index` in the tree. #[inline] pub fn path(&self, parameters: &Parameters<C>, index: usize) -> Result<Path<C>, PathError> where @@ -523,6 +523,8 @@ where LeafDigest<C>: Clone + Default, InnerDigest<C>: Clone, { + // FIXME: Move this algorithm to `crate::merkle_tree::path`. + let length = self.len(); if index > 0 && index >= length { return Err(PathError::IndexTooLarge { length }); diff --git a/manta-crypto/src/merkle_tree/partial.rs b/manta-crypto/src/merkle_tree/partial.rs index 155941c02..948ff9669 100644 --- a/manta-crypto/src/merkle_tree/partial.rs +++ b/manta-crypto/src/merkle_tree/partial.rs @@ -123,13 +123,13 @@ where self.inner_digests.root() } - /// + /// Returns the leaf digest at the given `index` in the tree. #[inline] pub fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>> { self.leaf_digests.get(index - self.starting_leaf_index()) } - /// + /// Returns the position of `leaf_digest` in the tree. #[inline] pub fn position(&self, leaf_digest: &LeafDigest<C>) -> Option<usize> where @@ -157,13 +157,13 @@ where self.get_leaf_sibling(index).cloned().unwrap_or_default() } - /// + /// Returns the current (right-most) leaf of the tree. #[inline] pub fn current_leaf(&self) -> Option<&LeafDigest<C>> { self.leaf_digests.last() } - /// + /// Returns the current (right-most) path of the tree. #[inline] pub fn current_path(&self) -> CurrentPath<C> where @@ -181,7 +181,7 @@ where ) } - /// + /// Returns the path at `index` without bounds-checking on the index. #[inline] pub fn path_unchecked(&self, index: usize) -> Path<C> where @@ -327,7 +327,6 @@ where #[inline] fn path(&self, parameters: &Parameters<C>, index: usize) -> Result<Path<C>, PathError> { - // FIXME: Check that this is implemented properly. let _ = parameters; let length = self.len(); if index > 0 && index >= length { diff --git a/manta-crypto/src/merkle_tree/path.rs b/manta-crypto/src/merkle_tree/path.rs index 026b2831e..f2b916b9c 100644 --- a/manta-crypto/src/merkle_tree/path.rs +++ b/manta-crypto/src/merkle_tree/path.rs @@ -154,7 +154,7 @@ where index: Node, base: InnerDigest<C>, iter: I, - ) -> Root<C> + ) -> InnerDigest<C> where InnerDigest<C>: 'i, I: IntoIterator<Item = &'i InnerDigest<C>>, diff --git a/manta-crypto/src/merkle_tree/test.rs b/manta-crypto/src/merkle_tree/test.rs index dd1b01034..2d806bd1f 100644 --- a/manta-crypto/src/merkle_tree/test.rs +++ b/manta-crypto/src/merkle_tree/test.rs @@ -215,93 +215,3 @@ where { const HEIGHT: usize = HEIGHT; } - -/* -#[cfg(test)] -mod complete { - use super::*; - use crate::{ - merkle_tree::{ - self, - fork::{self, ForkedTree, Trunk}, - inner_tree, - }, - rand::{Rand, Standard}, - }; - use core::iter::repeat; - use manta_util::pointer::SingleThreaded; - use rand::thread_rng; - - #[test] - fn test_suite() { - let mut rng = thread_rng(); - let parameters = Default::default(); - - let mut single_path_tree = - merkle_tree::single_path::SinglePathMerkleTree::<Test>::new(parameters); - - let mut full_tree = merkle_tree::full::FullMerkleTree::<Test>::new(parameters); - - let leaves = rng - .sample_iter(repeat(Standard).take(1_000_000)) - .collect::<Vec<u64>>(); - - full_tree.extend(&leaves[0..1000]); - - // println!("{:#?}", full_tree); - - let mut forked_tree = MerkleTree::from_tree( - ForkedTree::<_, _, SingleThreaded, inner_tree::BTreeMap<Test>>::new( - full_tree.clone().tree, - &parameters, - ), - parameters, - ); - - use manta_util::Rollback; - - forked_tree.commit(); - - assert_valid_paths(&mut full_tree, &leaves[1000..3000]); - assert_valid_paths(&mut forked_tree, &leaves[1000..3000]); - - forked_tree.commit(); - - for (i, leaf) in leaves.iter().enumerate().take(1000) { - // println!("{:#?}", full_tree.path(i)); - // println!("{:#?}", forked_tree.path(i)); - // assert_eq!(full_tree.path(i), forked_tree.path(i), "Failed on {:?}", i); - assert_valid_path(&full_tree, i, leaf); - assert_valid_path(&forked_tree, i, leaf); - } - - forked_tree.commit(); - let tree = forked_tree.tree.into_tree(); - - assert_eq!(full_tree.tree, tree); - - /* - let mut trunk = Trunk::<_, _, SingleThreaded>::new(full_tree.clone().tree); - - println!("{:#?}", trunk); - - let mut fork = trunk.fork::<inner_tree::BTreeMap<Test>>(&parameters); - - println!("{:#?}", fork); - - for leaf in leaves.iter().take(1000) { - full_tree.push(leaf); - fork.push(&parameters, leaf); - assert_eq!(full_tree.current_path(), fork.current_path()); - // println!("{:#?}", fork); - // println!("{:#?}", full_tree); - } - - trunk.merge(&parameters, fork).unwrap(); - println!("{:#?}", trunk); - println!("{:#?}", full_tree); - assert_eq!(trunk.into_tree(), full_tree.tree); - */ - } -} -*/ diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index eb5f72e68..1dfca1594 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -16,18 +16,26 @@ //! Manta-Pay Configuration -use crate::crypto::{ - constraint::arkworks::{Boolean, Fp, FpVar, Groth16, R1CS}, - ecc::{self, arkworks::ProjectiveCurve}, - encryption::aes::{self, AesGcm}, - hash::poseidon, - key::Blake2sKdf, +use crate::{ + crypto::{ + constraint::arkworks::{Boolean, Fp, FpVar, Groth16, R1CS}, + ecc::{self, arkworks::ProjectiveCurve}, + encryption::aes::{self, AesGcm}, + hash::poseidon, + key::Blake2sKdf, + }, + key::TestnetKeySecret, +}; +use ark_ff::{PrimeField, ToConstraintField}; +use blake2::{ + digest::{Update, VariableOutput}, + Blake2sVar, }; -use ark_ff::ToConstraintField; use bls12_381::Bls12_381; use bls12_381_ed::constraints::EdwardsVar as Bls12_381_EdwardsVar; use manta_accounting::{ asset::{Asset, AssetId, AssetValue}, + key::HierarchicalKeyDerivationScheme, transfer, }; use manta_crypto::{ @@ -40,7 +48,9 @@ use manta_crypto::{ ecc::DiffieHellman, encryption, hash::{BinaryHashFunction, HashFunction}, - key, merkle_tree, + key, + key::KeyDerivationFunction, + merkle_tree, }; #[cfg(test)] @@ -549,6 +559,9 @@ pub type Parameters = transfer::Parameters<Config>; /// Full Transfer Parameters pub type FullParameters<'p> = transfer::FullParameters<'p, Config>; +/// Encrypted Note Type +pub type EncryptedNote = transfer::EncryptedNote<Config>; + /// Mint Transfer Type pub type Mint = transfer::canonical::Mint<Config>; @@ -560,3 +573,45 @@ pub type Reclaim = transfer::canonical::Reclaim<Config>; /// Transfer Post Type pub type TransferPost = transfer::TransferPost<Config>; + +/// Proving Context Type +pub type ProvingContext = transfer::ProvingContext<Config>; + +/// Verifying Context Type +pub type VerifyingContext = transfer::VerifyingContext<Config>; + +/// Hierarchical Key Derivation Function +pub struct HierarchicalKeyDerivationFunction; + +impl KeyDerivationFunction for HierarchicalKeyDerivationFunction { + type Key = <TestnetKeySecret as HierarchicalKeyDerivationScheme>::SecretKey; + type Output = SecretKey; + + #[inline] + fn derive(secret_key: &Self::Key) -> Self::Output { + // FIXME: Check that this conversion is logical/safe. + let bytes: [u8; 32] = secret_key + .private_key() + .to_bytes() + .try_into() + .expect("The private key has 32 bytes."); + Fp(<Bls12_381_Edwards as ProjectiveCurve>::ScalarField::from_le_bytes_mod_order(&bytes)) + } +} + +impl merkle_tree::forest::Configuration for MerkleTreeConfiguration { + type Index = u8; + + #[inline] + fn tree_index(leaf: &merkle_tree::Leaf<Self>) -> Self::Index { + let mut hasher = Blake2sVar::new(1).unwrap(); + hasher.update( + &ark_ff::to_bytes!(leaf.0).expect("Converting to bytes is not allowed to fail."), + ); + let mut result = [0]; + hasher + .finalize_variable(&mut result) + .expect("Hashing is not allowed to fail."); + result[0] + } +} diff --git a/manta-pay/src/key.rs b/manta-pay/src/key.rs index 4ac85189e..dad8ae310 100644 --- a/manta-pay/src/key.rs +++ b/manta-pay/src/key.rs @@ -27,7 +27,9 @@ use alloc::{format, string::String}; use bip32::{Seed, XPrv}; use core::{marker::PhantomData, num::ParseIntError, str::FromStr}; -use manta_accounting::key::{HierarchicalKeyDerivationParameter, HierarchicalKeyDerivationScheme}; +use manta_accounting::key::{ + self, HierarchicalKeyDerivationParameter, HierarchicalKeyDerivationScheme, +}; use manta_util::{create_seal, seal}; pub use bip32::{Error, Mnemonic}; @@ -255,3 +257,15 @@ where ) } } + +/// Account Table Type +pub type AccountTable<C> = key::AccountTable<KeySecret<C>>; + +/// Testnet [`AccountTable`] Type +pub type TestnetAccountTable = AccountTable<Testnet>; + +/// Manta [`AccountTable`] Type +pub type MantaAccountTable = AccountTable<Manta>; + +/// Calamari [`AccountTable`] Type +pub type CalamariAccountTable = AccountTable<Calamari>; diff --git a/manta-pay/src/ledger.rs b/manta-pay/src/ledger.rs index 33cc103d2..8cb891aff 100644 --- a/manta-pay/src/ledger.rs +++ b/manta-pay/src/ledger.rs @@ -16,100 +16,187 @@ //! Ledger Implementation -/* -use crate::{ - accounting::{ - config::Configuration, - identity::{Parameters, Root, Utxo}, +use crate::config::{ + Config, EncryptedNote, MerkleTreeConfiguration, ProofSystem, Utxo, VerifyingContext, VoidNumber, +}; +use manta_accounting::{ + asset::{AssetId, AssetValue}, + transfer::{ + self, InsufficientPublicBalance, Proof, ReceiverLedger, ReceiverPostingKey, SenderLedger, + SenderPostingKey, TransferLedger, TransferLedgerSuperPostingKey, UtxoSetOutput, }, - crypto::merkle_tree::ConfigConverter, }; -use alloc::{collections::BTreeSet, vec, vec::Vec}; -use blake2::{ - digest::{Update, VariableOutput}, - VarBlake2s, +use manta_crypto::{ + constraint::{Input as ProofSystemInput, ProofSystem as _}, + merkle_tree, + merkle_tree::forest::Configuration, }; -use manta_crypto::merkle_tree::{single_path::SinglePath, Tree}; -use manta_util::{as_bytes, into_array_unchecked}; +use std::collections::{HashMap, HashSet}; -/// UTXO Shard -#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] -pub struct UtxoShard { - /// Shard Root - root: Root, +/// UTXO Merkle Forest Type +pub type UtxoMerkleForest = merkle_tree::forest::TreeArrayMerkleForest< + MerkleTreeConfiguration, + merkle_tree::single_path::SinglePath<MerkleTreeConfiguration>, + 256, +>; - /// Unspent Transaction Outputs - utxos: SinglePath<ConfigConverter<Configuration>>, -} +/// Wrap Type +#[derive(Clone, Copy)] +pub struct Wrap<T>(T); + +/// Ledger +pub struct Ledger { + /// Void Numbers + void_numbers: HashSet<VoidNumber>, -/// UTXO Set Ledger -#[derive(Clone)] -pub struct UtxoSetLedger { - /// UTXO Shards - pub shards: [UtxoShard; Self::SHARD_COUNT], + /// UTXOs + utxos: HashSet<Utxo>, - /// UTXO Set - pub utxos: BTreeSet<[u8; 32]>, + /// Shards + shards: HashMap<u8, HashMap<u64, (Utxo, EncryptedNote)>>, - /// Merkle Tree Parameters - pub parameters: Parameters, + /// UTXO Forest + utxo_forest: UtxoMerkleForest, + + /// Verifying Contexts + verifying_context: transfer::canonical::VerifyingContext<Config>, } -impl UtxoSetLedger { - const SHARD_COUNT: usize = 256; +impl SenderLedger<Config> for Ledger { + type ValidVoidNumber = Wrap<VoidNumber>; + type ValidUtxoSetOutput = Wrap<UtxoSetOutput<Config>>; + type SuperPostingKey = (Wrap<()>, ()); - /// Builds a new [`UtxoSetLedger`]. #[inline] - pub fn new(parameters: Parameters) -> Self { - Self { - shards: into_array_unchecked(vec![Default::default(); Self::SHARD_COUNT]), - utxos: Default::default(), - parameters, + fn is_unspent(&self, void_number: VoidNumber) -> Option<Self::ValidVoidNumber> { + if self.void_numbers.contains(&void_number) { + None + } else { + Some(Wrap(void_number)) } } - /// Computes the shard index of this `utxo`. #[inline] - fn shard_index(utxo: &Utxo) -> usize { - let mut hasher = VarBlake2s::new(1).expect("Failed to generate Variable Blake2s hasher."); - hasher.update(&as_bytes!(utxo)); - let mut res: usize = 0; - hasher.finalize_variable(|x| res = x[0] as usize); - res + fn has_matching_utxo_set_output( + &self, + output: UtxoSetOutput<Config>, + ) -> Option<Self::ValidUtxoSetOutput> { + for tree in self.utxo_forest.forest.as_ref() { + if tree.root() == &output { + return Some(Wrap(output)); + } + } + None } - /// Returns a shared reference to the shard which this `utxo` would be stored in. #[inline] - pub fn shard(&self, utxo: &Utxo) -> &UtxoShard { - &self.shards[Self::shard_index(utxo)] + fn spend( + &mut self, + utxo_set_output: Self::ValidUtxoSetOutput, + void_number: Self::ValidVoidNumber, + super_key: &Self::SuperPostingKey, + ) { + let _ = (utxo_set_output, super_key); + self.void_numbers.insert(void_number.0); } +} + +impl ReceiverLedger<Config> for Ledger { + type ValidUtxo = Wrap<Utxo>; + type SuperPostingKey = (Wrap<()>, ()); - /// Returns `true` if the `root` belongs to some shard. #[inline] - pub fn root_exists(&self, root: &Root) -> bool { - self.shards.iter().any(move |s| s.root == *root) + fn is_not_registered(&self, utxo: Utxo) -> Option<Self::ValidUtxo> { + if self.utxos.contains(&utxo) { + None + } else { + Some(Wrap(utxo)) + } } - /// Returns `true` if the `utxo` belongs to the shard it would be stored in. #[inline] - pub fn utxo_exists(&self, utxo: &Utxo) -> bool { - self.utxos.contains(as_bytes!(utxo).as_slice()) + fn register( + &mut self, + utxo: Self::ValidUtxo, + note: EncryptedNote, + super_key: &Self::SuperPostingKey, + ) { + let _ = super_key; + let shard = self + .shards + .get_mut(&MerkleTreeConfiguration::tree_index(&utxo.0)) + .unwrap(); + let len = shard.len(); + shard.insert(len as u64, (utxo.0, note)); } +} + +impl TransferLedger<Config> for Ledger { + type ValidSourceBalance = Wrap<AssetValue>; + type ValidProof = Wrap<()>; + type SuperPostingKey = (); - /// #[inline] - pub fn insert(&mut self, utxo: &Utxo) -> bool { - if self.utxo_exists(utxo) { - return false; - } - if !self.shards[Self::shard_index(utxo)] - .utxos - .push(&self.parameters, utxo) - { - return false; + fn check_source_balances( + &self, + sources: Vec<AssetValue>, + ) -> Result<Vec<Self::ValidSourceBalance>, InsufficientPublicBalance> { + // FIXME: This can only be implemented on the actual ledger. + Ok(sources.into_iter().map(Wrap).collect()) + } + + #[inline] + fn is_valid( + &self, + asset_id: Option<AssetId>, + sources: &[Self::ValidSourceBalance], + senders: &[SenderPostingKey<Config, Self>], + receivers: &[ReceiverPostingKey<Config, Self>], + sinks: &[AssetValue], + proof: Proof<Config>, + ) -> Option<Self::ValidProof> { + let verifying_context = self.verifying_context.select( + asset_id.is_some(), + sources.len(), + senders.len(), + receivers.len(), + sinks.len(), + )?; + + let mut input = Default::default(); + if let Some(asset_id) = asset_id { + ProofSystem::extend(&mut input, &asset_id); } - self.utxos.insert(into_array_unchecked(as_bytes!(utxo))); - true + sources + .iter() + .for_each(|source| ProofSystem::extend(&mut input, &source.0)); + senders.iter().for_each(|sender| { + // ... + todo!() + }); + receivers.iter().for_each(|receiver| { + // ... + todo!() + }); + sinks + .iter() + .for_each(|sink| ProofSystem::extend(&mut input, sink)); + + ProofSystem::verify(&input, &proof, verifying_context) + .ok()? + .then(move || Wrap(())) + } + + #[inline] + fn update_public_balances( + &mut self, + asset_id: AssetId, + sources: Vec<Self::ValidSourceBalance>, + sinks: Vec<AssetValue>, + proof: Self::ValidProof, + super_key: &TransferLedgerSuperPostingKey<Config, Self>, + ) { + // FIXME: This can only be implemented on the real ledger. + let _ = (asset_id, sources, sinks, proof, super_key); } } -*/ diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index 8f72dfd95..14b03824b 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -29,6 +29,7 @@ mod test; pub mod crypto; pub mod key; pub mod ledger; +pub mod simulation; pub mod wallet; #[cfg(all(feature = "arkworks", feature = "groth16"))] diff --git a/manta-pay/src/simulation/mod.rs b/manta-pay/src/simulation/mod.rs new file mode 100644 index 000000000..ee13d5f7e --- /dev/null +++ b/manta-pay/src/simulation/mod.rs @@ -0,0 +1,722 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Manta Pay Simulation + +/* +use clap::{App, Arg}; +use core::cmp::min; +use core::ops::Range; +use indexmap::IndexMap; +use rand::{distributions::Distribution, seq::SliceRandom, thread_rng, Rng, RngCore}; +use serde::{Deserialize, Serialize}; +use statrs::{ + distribution::{Categorical, Discrete, Poisson}, + StatsError, +}; +use std::collections::HashMap; +use std::fs; + +/// Flushes the STDOUT buffer. +#[inline] +fn flush_stdout() { + use std::io::Write; + let _ = std::io::stdout().flush(); +} + +/// Choose `count`-many elements from `vec` randomly and drop the remaining ones. +#[inline] +fn choose_multiple<T, R>(vec: &mut Vec<T>, count: usize, rng: &mut R) +where + R: RngCore + ?Sized, +{ + let drop_count = vec.partial_shuffle(rng, count).1.len(); + vec.drain(0..drop_count); +} + +/// Asset Id +pub type AssetId = u32; + +/// Asset Value +pub type AssetValue = u128; + +/// Asset +#[derive(Clone, Copy, Debug, Default, Deserialize, Hash, Eq, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct Asset { + /// Asset Id + pub id: AssetId, + + /// Asset Value + pub value: AssetValue, +} + +impl Asset { + /// Builds a new [`Asset`] from the given `id` and `value`. + #[inline] + pub fn new(id: AssetId, value: AssetValue) -> Self { + Self { id, value } + } +} + +/// Balance State +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +#[serde(deny_unknown_fields, transparent)] +pub struct BalanceState { + /// Asset Map + map: HashMap<AssetId, AssetValue>, +} + +impl BalanceState { + /// Returns the asset balance associated to the assets with the given `id`. + #[inline] + pub fn balance(&self, id: AssetId) -> AssetValue { + self.map.get(&id).copied().unwrap_or_default() + } + + /// Returns `true` if `self` contains at least `value` amount of the asset with the given `id`. + #[inline] + pub fn contains(&self, id: AssetId, value: AssetValue) -> bool { + self.balance(id) >= value + } + + /// Deposit `asset` into `self`. + #[inline] + pub fn deposit(&mut self, asset: Asset) { + *self.map.entry(asset.id).or_default() += asset.value; + } + + /// Withdraw `asset` from `self`, returning `false` if it would overdraw the balance. + #[inline] + pub fn withdraw(&mut self, asset: Asset) -> bool { + if asset.value == 0 { + true + } else { + self.map + .get_mut(&asset.id) + .map(move |balance| { + if let Some(result) = balance.checked_sub(asset.value) { + *balance = result; + true + } else { + false + } + }) + .unwrap_or(false) + } + } +} + +/// Action Types +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[serde(deny_unknown_fields, tag = "type")] +pub enum Action { + /// No Action + None, + + /// Public Deposit Action + PublicDeposit, + + /// Public Withdraw Action + PublicWithdraw, + + /// Mint Action + Mint, + + /// Private Transfer Action + PrivateTransfer, + + /// Reclaim Action + Reclaim, +} + +/// Action Distribution Probability Mass Function +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] +pub struct ActionDistributionPMF<T = f64> { + /// No Action Weight + pub none: T, + + /// Public Deposit Action Weight + pub public_deposit: T, + + /// Public Withdraw Action Weight + pub public_withdraw: T, + + /// Mint Action Weight + pub mint: T, + + /// Private Transfer Action Weight + pub private_transfer: T, + + /// Reclaim Action Weight + pub reclaim: T, +} + +impl Default for ActionDistributionPMF { + #[inline] + fn default() -> Self { + Self { + none: 1.0, + public_deposit: 1.0, + public_withdraw: 1.0, + mint: 1.0, + private_transfer: 1.0, + reclaim: 1.0, + } + } +} + +impl From<ActionDistribution> for ActionDistributionPMF { + #[inline] + fn from(actions: ActionDistribution) -> Self { + ActionDistributionPMF { + none: actions.distribution.pmf(0), + public_deposit: actions.distribution.pmf(1), + public_withdraw: actions.distribution.pmf(2), + mint: actions.distribution.pmf(3), + private_transfer: actions.distribution.pmf(4), + reclaim: actions.distribution.pmf(5), + } + } +} + +/// Action Distribution +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[serde( + deny_unknown_fields, + into = "ActionDistributionPMF", + try_from = "ActionDistributionPMF" +)] +pub struct ActionDistribution { + /// Distribution over Actions + distribution: Categorical, +} + +impl Default for ActionDistribution { + #[inline] + fn default() -> Self { + Self::try_from(ActionDistributionPMF::default()).unwrap() + } +} + +impl TryFrom<ActionDistributionPMF> for ActionDistribution { + type Error = StatsError; + + #[inline] + fn try_from(pmf: ActionDistributionPMF) -> Result<Self, StatsError> { + Ok(Self { + distribution: Categorical::new(&[ + pmf.none, + pmf.public_deposit, + pmf.public_withdraw, + pmf.mint, + pmf.private_transfer, + pmf.reclaim, + ])?, + }) + } +} + +impl Distribution<Action> for ActionDistribution { + #[inline] + fn sample<R>(&self, rng: &mut R) -> Action + where + R: RngCore + ?Sized, + { + match self.distribution.sample(rng) as usize { + 0 => Action::None, + 1 => Action::PublicDeposit, + 2 => Action::PublicWithdraw, + 3 => Action::Mint, + 4 => Action::PrivateTransfer, + 5 => Action::Reclaim, + _ => unreachable!(), + } + } +} + +/// User Account +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct Account { + /// Public Balances + pub public: BalanceState, + + /// Secret Balances + pub secret: BalanceState, + + /// Action Distribution + pub actions: ActionDistribution, +} + +impl Account { + /// Samples a new account sampled using `config` settings and `rng`. + #[inline] + pub fn sample<R>(config: &Config, rng: &mut R) -> Self + where + R: RngCore + ?Sized, + { + let mut public = BalanceState::default(); + // TODO: Use a better distribution to sample a starting balance. + for _ in 0usize..rng.gen_range(0..50) { + public.deposit(config.sample_asset(rng)); + } + Self { + public, + secret: Default::default(), + actions: ActionDistribution::try_from(ActionDistributionPMF { + none: rng.gen_range(config.action_sampling_ranges.none.clone()), + public_deposit: rng.gen_range(config.action_sampling_ranges.public_deposit.clone()), + public_withdraw: rng + .gen_range(config.action_sampling_ranges.public_withdraw.clone()), + mint: rng.gen_range(config.action_sampling_ranges.mint.clone()), + private_transfer: rng + .gen_range(config.action_sampling_ranges.private_transfer.clone()), + reclaim: rng.gen_range(config.action_sampling_ranges.reclaim.clone()), + }) + .unwrap(), + } + } +} + +/// Simulation Update +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields, tag = "type")] +pub enum Update { + /// Create Account + CreateAccount { account: Account }, + + /// Deposit Public Balance + PublicDeposit { account_index: usize, asset: Asset }, + + /// Withdraw Public Balance + PublicWithdraw { account_index: usize, asset: Asset }, + + /// Mint Asset + Mint { source_index: usize, asset: Asset }, + + /// Private Transfer Asset + PrivateTransfer { + sender_index: usize, + receiver_index: usize, + asset: Asset, + }, + + /// Reclaim Asset + Reclaim { sender_index: usize, asset: Asset }, +} + +/// Simulation Configuration +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct Config { + /// Number of starting accounts + pub starting_account_count: u64, + + /// Number of simulation steps before creating new accounts + pub new_account_sampling_cycle: u64, + + /// [`Poisson`] growth rate of the number of accounts + /// + /// This configuration setting is not used if `new_account_sampling_cycle == 0`. + pub account_count_growth_rate: f64, + + /// Maximum number of accounts + /// + /// If this value is less than `starting_account_count`, the maximum count is ignored. + pub maximum_account_count: u64, + + /// Which assets are allowed to be sampled and the maximum per sample + pub allowed_asset_sampling: IndexMap<AssetId, AssetValue>, + + /// Action Sampling Ranges + /// + /// This is a distribution over an [`ActionDistribution`] which is used to sample an + /// [`ActionDistribution`] for a particular account. + pub action_sampling_ranges: ActionDistributionPMF<Range<f64>>, + + /// Maximum number of updates allowed per step + /// + /// If this value is `0`, it has no effect. + pub maximum_updates_per_step: u32, + + /// Maximum number of total updates + /// + /// If this value is `0`, it has no effect. + pub maximum_total_updates: u32, +} + +impl Config { + /// Returns `true` if `self` has an active account count maximum. + #[inline] + fn has_maximum_account_count(&self) -> bool { + self.maximum_account_count >= self.starting_account_count + } + + /// Returns `true` if `accounts` is equal to the account count maximum, if it is active. + #[inline] + fn maximum_account_count_has_been_reached(&self, accounts: u64) -> bool { + self.has_maximum_account_count() && self.maximum_account_count == accounts + } + + /// Returns `true` if new accounts should be created for the current `step_counter` and an + /// account list with `accounts`-many elements. + #[inline] + fn should_create_new_accounts(&self, step_counter: u64, accounts: u64) -> bool { + self.maximum_account_count != self.starting_account_count + && !self.maximum_account_count_has_been_reached(accounts) + && self.new_account_sampling_cycle != 0 + && step_counter % self.new_account_sampling_cycle == 0 + } + + /// Samples an allowed asset using `rng`. + #[inline] + fn sample_asset<R>(&self, rng: &mut R) -> Asset + where + R: RngCore + ?Sized, + { + let id = self.sample_asset_id(rng); + Asset::new(id, self.sample_asset_value(id, rng)) + } + + /// Samples an allowed asset id using `rng`. + #[inline] + fn sample_asset_id<R>(&self, rng: &mut R) -> AssetId + where + R: RngCore + ?Sized, + { + let mut ids = self.allowed_asset_sampling.keys(); + *ids.nth(rng.gen_range(0..ids.len())).unwrap() + } + + /// Samples an allowed asset value of the given `id` using `rng`. + #[inline] + fn sample_asset_value<R>(&self, id: AssetId, rng: &mut R) -> AssetValue + where + R: RngCore + ?Sized, + { + rng.gen_range(0..=self.allowed_asset_sampling[&id]) + } + + /// Samples an allowed withdraw from `balances`. + #[inline] + fn sample_withdraw<R>(&self, balances: &BalanceState, rng: &mut R) -> Asset + where + R: RngCore + ?Sized, + { + let mut ids = self.allowed_asset_sampling.keys().collect::<Vec<_>>(); + ids.shuffle(rng); + for id in &ids { + let balance = balances.balance(**id); + if balance != 0 { + return Asset::new( + **id, + rng.gen_range(0..=min(balance, self.allowed_asset_sampling[*id])), + ); + } + } + Asset::new(*ids[ids.len() - 1], 0) + } +} + +/// Simulator +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct Simulator { + /// Configuration + config: Config, + + /// Step Counter + step_counter: u64, + + /// Accounts + accounts: Vec<Account>, +} + +impl Simulator { + /// Builds a new [`Simulator`] from the given `config`, sampling from `rng`. + #[inline] + pub fn new<R>(config: Config, rng: &mut R) -> Self + where + R: RngCore + ?Sized, + { + Self { + accounts: (0..config.starting_account_count) + .map(|_| Account::sample(&config, rng)) + .collect(), + step_counter: Default::default(), + config, + } + } + + /// Computes one step of the simulation using `rng`. + #[inline] + pub fn step<R>(&self, rng: &mut R) -> Vec<Update> + where + R: RngCore + ?Sized, + { + let mut updates = Vec::new(); + for (i, account) in self.accounts.iter().enumerate() { + match account.actions.sample(rng) { + Action::None => {} + Action::PublicDeposit => { + updates.push(Update::PublicDeposit { + account_index: i, + asset: self.config.sample_asset(rng), + }); + } + Action::PublicWithdraw => { + updates.push(Update::PublicWithdraw { + account_index: i, + asset: self.config.sample_withdraw(&account.public, rng), + }); + } + Action::Mint => { + updates.push(Update::Mint { + source_index: i, + asset: self.config.sample_withdraw(&account.public, rng), + }); + } + Action::PrivateTransfer => { + updates.push(Update::PrivateTransfer { + sender_index: i, + receiver_index: rng.gen_range(0..self.accounts.len()), + asset: self.config.sample_withdraw(&account.secret, rng), + }); + } + Action::Reclaim => { + updates.push(Update::Reclaim { + sender_index: i, + asset: self.config.sample_withdraw(&account.secret, rng), + }); + } + } + } + let accounts_len = self.accounts.len() as u64; + if self + .config + .should_create_new_accounts(self.step_counter, accounts_len) + { + let mut new_accounts = Poisson::new(self.config.account_count_growth_rate) + .unwrap() + .sample(rng) as u64; + if self.config.has_maximum_account_count() { + new_accounts = + new_accounts.clamp(0, self.config.maximum_account_count - accounts_len); + } + for _ in 0..new_accounts { + updates.push(Update::CreateAccount { + account: Account::sample(&self.config, rng), + }); + } + } + if self.config.maximum_updates_per_step > 0 { + choose_multiple( + &mut updates, + self.config.maximum_updates_per_step as usize, + rng, + ); + } + updates + } + + /// Applies `update` to the internal state of the simulator, returning the update back + /// if an error occured. + #[inline] + pub fn apply(&mut self, update: Update) -> Result<(), Update> { + match &update { + Update::CreateAccount { account } => { + self.accounts.push(account.clone()); + return Ok(()); + } + Update::PublicDeposit { + account_index, + asset, + } => { + if let Some(balances) = self.accounts.get_mut(*account_index) { + balances.public.deposit(*asset); + return Ok(()); + } + } + Update::PublicWithdraw { + account_index, + asset, + } => { + if let Some(balances) = self.accounts.get_mut(*account_index) { + if balances.public.withdraw(*asset) { + return Ok(()); + } + } + } + Update::Mint { + source_index, + asset, + } => { + if let Some(balances) = self.accounts.get_mut(*source_index) { + if balances.public.withdraw(*asset) { + balances.secret.deposit(*asset); + return Ok(()); + } + } + } + Update::PrivateTransfer { + sender_index, + receiver_index, + asset, + } => { + if let Some(sender) = self.accounts.get_mut(*sender_index) { + if sender.secret.withdraw(*asset) { + if let Some(receiver) = self.accounts.get_mut(*receiver_index) { + receiver.secret.deposit(*asset); + return Ok(()); + } + } + } + } + Update::Reclaim { + sender_index, + asset, + } => { + if let Some(balances) = self.accounts.get_mut(*sender_index) { + if balances.secret.withdraw(*asset) { + balances.public.deposit(*asset); + return Ok(()); + } + } + } + } + Err(update) + } + + /// Runs `self` for the given number of `steps`. + #[inline] + pub fn run<R>(&mut self, steps: usize, rng: &mut R) -> Simulation + where + R: RngCore + ?Sized, + { + let initial_accounts = self.accounts.clone(); + let mut updates = Vec::new(); + for _ in 0..steps { + let mut next_updates = self.step(rng); + let update_limit = self.config.maximum_total_updates as usize; + if update_limit > 0 { + match update_limit - updates.len() { + 0 => break, + diff => next_updates.truncate(diff), + } + } + for update in &next_updates { + if let Err(update) = self.apply(update.clone()) { + panic!( + "ERROR: {}\n\n Panicked on the following state:\nSimulation: {:?}\nUpdate: {:?}", + "This is an internal simulation error. Please file a bug.", + self, + update + ); + } + } + updates.append(&mut next_updates); + self.step_counter += 1; + } + Simulation { + config: self.config.clone(), + initial_accounts, + final_accounts: self.accounts.clone(), + updates, + } + } +} + +/// Simulation Final State +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct Simulation { + /// Configuration + pub config: Config, + + /// Initial Account State + pub initial_accounts: Vec<Account>, + + /// Final Account State + pub final_accounts: Vec<Account>, + + /// Updates + pub updates: Vec<Update>, +} + +/// Runs a [`Simulator`] in a CLI. +pub fn main() { + let matches = App::new("Manta Simulation") + .arg( + Arg::with_name("steps") + .help("The number of steps to run the simulation.") + .required(true), + ) + .arg( + Arg::with_name("config") + .short("c") + .long("config") + .value_name("FILE") + .help("Sets a custom config file. By default, `default-config.json` is used.") + .takes_value(true), + ) + .arg( + Arg::with_name("output") + .short("o") + .long("output") + .value_name("FILE") + .help("Sets a custom output file") + .takes_value(true), + ) + .get_matches(); + + let config_path = matches.value_of("config").unwrap_or("default-config.json"); + let config = match fs::read_to_string(&config_path) { + Ok(config) => match serde_json::from_str(&config) { + Ok(config) => config, + err => panic!("ERROR: {:?}", err), + }, + _ => panic!("ERROR: Invalid configuration path: {:?}", config_path), + }; + + let steps = matches + .value_of("steps") + .unwrap() + .parse::<usize>() + .expect("ERROR: Invalid number of simulation steps."); + + let mut rng = thread_rng(); + + print!("INFO: Running simulation ... "); + flush_stdout(); + let simulation = Simulator::new(config, &mut rng).run(steps, &mut rng); + println!("DONE."); + + if let Some(output_path) = matches.value_of("output") { + print!("INFO: Writing simulation to file ... "); + flush_stdout(); + match serde_json::to_writer( + fs::File::create(output_path).expect("ERROR: Unable to create output file."), + &simulation, + ) { + Ok(()) => println!("DONE. Output written to `{}`.", output_path), + err => panic!("ERROR: {:?}", err), + } + } else if let Err(err) = serde_json::to_writer_pretty(std::io::stdout(), &simulation) { + panic!("ERROR: {:?}", err); + } +} +*/ diff --git a/manta-pay/src/test/merkle_tree.rs b/manta-pay/src/test/merkle_tree.rs index cb291ddf3..a5a2530b2 100644 --- a/manta-pay/src/test/merkle_tree.rs +++ b/manta-pay/src/test/merkle_tree.rs @@ -26,6 +26,7 @@ use manta_crypto::{ }; use rand::thread_rng; +/* /// Base Tree pub type Base = merkle_tree::full::Full<MerkleTreeConfiguration>; @@ -43,62 +44,4 @@ pub type Forest = forest::TreeArrayMerkleForest<MerkleTreeConfiguration, Base, 2 /// Forest Wrapper for Forked Base pub type ForkedForest = forest::TreeArrayMerkleForest<MerkleTreeConfiguration, ForkedBase, 256>; - -#[test] -fn test_suite() { - let mut rng = thread_rng(); - let parameters = rng.gen(); - let parameters = test::push_twice_to_empty_tree_succeeds::<MerkleTreeConfiguration, Base>( - parameters, - &rng.gen(), - &rng.gen(), - ); - - /* - let mut tree = Tree::new(parameters); - - accumulator::test::assert_unique_outputs( - &mut tree, - &rng.sample_iter(repeat(Standard).take(300)) - .collect::<Vec<_>>(), - ); - for _ in 0..30000 { - tree.insert(&rng.gen()); - } - accumulator::test::assert_unique_outputs( - &mut tree, - &rng.sample_iter(repeat(Standard).take(300)) - .collect::<Vec<_>>(), - ); - - let mut forest = Forest::new(tree.into_parameters()); - - accumulator::test::assert_unique_outputs( - &mut forest, - &rng.sample_iter(repeat(Standard).take(300)) - .collect::<Vec<_>>(), - ); - for _ in 0..30000 { - forest.insert(&rng.gen()); - } - accumulator::test::assert_unique_outputs( - &mut forest, - &rng.sample_iter(repeat(Standard).take(300)) - .collect::<Vec<_>>(), - ); - */ - - /* - let mut forked_tree = ForkedTree::new(parameters); - - println!("{:?}", forked_tree.trunk().len()); - println!("{:?}", forked_tree.fork().len()); - - for _ in 0..1 { - forked_tree.insert(&rng.gen()); - } - - println!("{:?}", forked_tree.trunk().len()); - println!("{:?}", forked_tree.fork().len()); - */ -} +*/ diff --git a/manta-pay/src/test/mod.rs b/manta-pay/src/test/mod.rs index e35ea0352..6f2847103 100644 --- a/manta-pay/src/test/mod.rs +++ b/manta-pay/src/test/mod.rs @@ -16,5 +16,5 @@ //! Manta Pay Testing -pub mod merkle_tree; +// TODO: pub mod merkle_tree; pub mod transfer; diff --git a/manta-pay/src/wallet.rs b/manta-pay/src/wallet.rs index e1b100fe8..466d6eefd 100644 --- a/manta-pay/src/wallet.rs +++ b/manta-pay/src/wallet.rs @@ -17,57 +17,15 @@ //! Manta Pay Base Wallet Implementation use crate::{ - config::{Bls12_381_Edwards, Config, MerkleTreeConfiguration, SecretKey}, - crypto::{constraint::arkworks::Fp, ecc::arkworks::ProjectiveCurve}, + config::{Config, HierarchicalKeyDerivationFunction, MerkleTreeConfiguration}, key::TestnetKeySecret, }; -use ark_ff::PrimeField; -use blake2::{ - digest::{Update, VariableOutput}, - Blake2sVar, -}; use manta_accounting::{ asset::HashAssetMap, - key::{self, HierarchicalKeyDerivationScheme}, + key, wallet::signer::{self, AssetMapKey}, }; -use manta_crypto::{key::KeyDerivationFunction, merkle_tree}; - -/// Hierarchical Key Derivation Function -pub struct HierarchicalKeyDerivationFunction; - -impl KeyDerivationFunction for HierarchicalKeyDerivationFunction { - type Key = <TestnetKeySecret as HierarchicalKeyDerivationScheme>::SecretKey; - type Output = SecretKey; - - #[inline] - fn derive(secret_key: &Self::Key) -> Self::Output { - // FIXME: Check that this conversion is logical/safe. - let bytes: [u8; 32] = secret_key - .private_key() - .to_bytes() - .try_into() - .expect("The private key has 32 bytes."); - Fp(<Bls12_381_Edwards as ProjectiveCurve>::ScalarField::from_le_bytes_mod_order(&bytes)) - } -} - -impl merkle_tree::forest::Configuration for MerkleTreeConfiguration { - type Index = u8; - - #[inline] - fn tree_index(leaf: &merkle_tree::Leaf<Self>) -> Self::Index { - let mut hasher = Blake2sVar::new(1).unwrap(); - hasher.update( - &ark_ff::to_bytes!(leaf.0).expect("Converting to bytes is not allowed to fail."), - ); - let mut result = [0]; - hasher - .finalize_variable(&mut result) - .expect("Hashing is not allowed to fail."); - result[0] - } -} +use manta_crypto::merkle_tree; /// Signer UTXO Set pub type UtxoSet = merkle_tree::forest::TreeArrayMerkleForest< From 0a03a79e46e423b2c8c41cfec5fca84c9fbbc2b7 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sat, 8 Jan 2022 14:02:36 -0500 Subject: [PATCH 172/275] feat: add ledger implementation --- manta-accounting/src/transfer/mod.rs | 212 ++++++++++++++++++++++----- manta-pay/src/ledger.rs | 167 +++++++++++++++------ 2 files changed, 300 insertions(+), 79 deletions(-) diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index 72e36de3b..87c94084b 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -859,7 +859,7 @@ where /// implementation of [`SenderLedger`]. This is to prevent that [`spend`](Self::spend) is /// called before [`is_unspent`](Self::is_unspent) and /// [`has_matching_utxo_set_output`](Self::has_matching_utxo_set_output). - type ValidVoidNumber; + type ValidVoidNumber: AsRef<VoidNumber<C>>; /// Valid UTXO Set Output Posting Key /// @@ -871,7 +871,7 @@ where /// [`has_matching_utxo_set_output`](Self::has_matching_utxo_set_output). /// /// [`S::Output`]: Model::Output - type ValidUtxoSetOutput; + type ValidUtxoSetOutput: AsRef<UtxoSetOutput<C>>; /// Super Posting Key /// @@ -981,6 +981,13 @@ where C: Configuration, L: SenderLedger<C> + ?Sized, { + /// Extends proof public input with `self`. + #[inline] + pub fn extend_input(&self, input: &mut ProofInput<C>) { + C::ProofSystem::extend(input, self.utxo_set_output.as_ref()); + C::ProofSystem::extend(input, self.void_number.as_ref()); + } + /// Posts `self` to the sender `ledger`. #[inline] pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) { @@ -1151,7 +1158,7 @@ where /// This type must be some wrapper around [`Utxo`] which can only be constructed by this /// implementation of [`ReceiverLedger`]. This is to prevent that [`register`](Self::register) /// is called before [`is_not_registered`](Self::is_not_registered). - type ValidUtxo; + type ValidUtxo: AsRef<Utxo<C>>; /// Super Posting Key /// @@ -1250,6 +1257,19 @@ where C: Configuration, L: ReceiverLedger<C> + ?Sized, { + /// Returns the ephemeral public key associated to `self`. + #[inline] + pub fn ephemeral_public_key(&self) -> &PublicKey<C> { + self.note.ephemeral_public_key() + } + + /// Extends proof public input with `self`. + #[inline] + pub fn extend_input(&self, input: &mut ProofInput<C>) { + C::ProofSystem::extend(input, self.ephemeral_public_key()); + C::ProofSystem::extend(input, self.utxo.as_ref()); + } + /// Posts `self` to the receiver `ledger`. #[inline] pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) { @@ -1577,13 +1597,24 @@ pub trait TransferLedger<C>: SenderLedger<C, SuperPostingKey = (Self::ValidProof where C: Configuration, { - /// Valid [`AssetValue`] for [`TransferPost`] source + /// Account Identifier + type AccountId; + + /// Valid [`AssetValue`] for [`TransferPost`] Source /// /// # Safety /// /// This type must be restricted so that it can only be constructed by this implementation of /// [`TransferLedger`]. - type ValidSourceBalance; + type ValidSourceAccount: AsRef<AssetValue>; + + /// Valid [`AssetValue`] for [`TransferPost`] Sink + /// + /// # Safety + /// + /// This type must be restricted so that it can only be constructed by this implementation of + /// [`TransferLedger`]. + type ValidSinkAccount: AsRef<AssetValue>; /// Valid [`Proof`] Posting Key /// @@ -1592,8 +1623,8 @@ where /// This type must be restricted so that it can only be constructed by this implementation /// of [`TransferLedger`]. This is to prevent that [`SenderPostingKey::post`] and /// [`ReceiverPostingKey::post`] are called before [`SenderPost::validate`], - /// [`ReceiverPost::validate`], [`check_source_balances`](Self::check_source_balances), and - /// [`is_valid`](Self::is_valid). + /// [`ReceiverPost::validate`], [`check_source_accounts`](Self::check_source_accounts), + /// [`check_sink_accounts`](Self::check_sink_accounts) and [`is_valid`](Self::is_valid). type ValidProof: Copy; /// Super Posting Key @@ -1603,20 +1634,30 @@ where /// Checks that the balances associated to the source accounts are sufficient to withdraw the /// amount given in `sources`. - fn check_source_balances( + fn check_source_accounts( &self, + asset_id: Option<AssetId>, + accounts: Vec<Self::AccountId>, sources: Vec<AssetValue>, - ) -> Result<Vec<Self::ValidSourceBalance>, InsufficientPublicBalance>; + ) -> Result<Vec<Self::ValidSourceAccount>, InvalidSourceAccounts<Self::AccountId>>; + + /// Checks that the sink accounts exist. + fn check_sink_accounts( + &self, + asset_id: Option<AssetId>, + accounts: Vec<Self::AccountId>, + sinks: Vec<AssetValue>, + ) -> Result<Vec<Self::ValidSinkAccount>, InvalidSinkAccounts<Self::AccountId>>; /// Checks that the transfer `proof` is valid. #[allow(clippy::too_many_arguments)] // FIXME: Write a better abstraction for this. fn is_valid( &self, asset_id: Option<AssetId>, - sources: &[Self::ValidSourceBalance], + sources: &[SourcePostingKey<C, Self>], senders: &[SenderPostingKey<C, Self>], receivers: &[ReceiverPostingKey<C, Self>], - sinks: &[AssetValue], + sinks: &[SinkPostingKey<C, Self>], proof: Proof<C>, ) -> Option<Self::ValidProof>; @@ -1630,33 +1671,68 @@ where fn update_public_balances( &mut self, asset_id: AssetId, - sources: Vec<Self::ValidSourceBalance>, - sinks: Vec<AssetValue>, + sources: Vec<SourcePostingKey<C, Self>>, + sinks: Vec<SinkPostingKey<C, Self>>, proof: Self::ValidProof, super_key: &TransferLedgerSuperPostingKey<C, Self>, ); } /// Transfer Source Posting Key Type -pub type SourcePostingKey<C, L> = <L as TransferLedger<C>>::ValidSourceBalance; +pub type SourcePostingKey<C, L> = <L as TransferLedger<C>>::ValidSourceAccount; + +/// Transfer Sink Posting Key Type +pub type SinkPostingKey<C, L> = <L as TransferLedger<C>>::ValidSinkAccount; /// Transfer Ledger Super Posting Key Type pub type TransferLedgerSuperPostingKey<C, L> = <L as TransferLedger<C>>::SuperPostingKey; -/// Insufficient Public Balance Error +/// Account Balance +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum AccountBalance { + /// Known Balance + Known(AssetValue), + + /// Unknown Account + UnknownAccount, +} + +/// Invalid Source Accounts /// -/// This `enum` is the error state of the [`TransferLedger::check_source_balances`] method. See its +/// This `enum` is the error state of the [`TransferLedger::check_source_accounts`] method. See its /// documentation for more. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub struct InsufficientPublicBalance { - /// Index of the Public Address - pub index: usize, +pub enum InvalidSourceAccounts<AccountId> { + /// Invalid Transfer Shape + InvalidShape, - /// Current Balance - pub balance: AssetValue, + /// Account is Unknown or would be Overdraw + BadAccount { + /// Account Id + account_id: AccountId, - /// Amount Attempting to Withdraw - pub withdraw: AssetValue, + /// Current Balance if Known + balance: AccountBalance, + + /// Amount Attempting to Withdraw + withdraw: AssetValue, + }, +} + +/// Invalid Sink Accounts +/// +/// This `enum` is the error state of the [`TransferLedger::check_sink_accounts`] method. See its +/// documentation for more. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum InvalidSinkAccounts<AccountId> { + /// Invalid Transfer Shape + InvalidShape, + + /// Account is Unknown + BadAccount { + /// Account Id + account_id: AccountId, + }, } /// Transfer Post Error @@ -1664,9 +1740,12 @@ pub struct InsufficientPublicBalance { /// This `enum` is the error state of the [`TransferPost::validate`] method. See its documentation /// for more. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum TransferPostError { - /// Insufficient Public Balance - InsufficientPublicBalance(InsufficientPublicBalance), +pub enum TransferPostError<AccountId> { + /// Invalid Source Accounts + InvalidSourceAccounts(InvalidSourceAccounts<AccountId>), + + /// Invalid Sink Accounts + InvalidSinkAccounts(InvalidSinkAccounts<AccountId>), /// Sender Post Error Sender(SenderPostError), @@ -1686,8 +1765,33 @@ pub enum TransferPostError { InvalidProof, } -from_variant_impl!(TransferPostError, Sender, SenderPostError); -from_variant_impl!(TransferPostError, Receiver, ReceiverPostError); +impl<AccountId> From<InvalidSourceAccounts<AccountId>> for TransferPostError<AccountId> { + #[inline] + fn from(err: InvalidSourceAccounts<AccountId>) -> Self { + Self::InvalidSourceAccounts(err) + } +} + +impl<AccountId> From<InvalidSinkAccounts<AccountId>> for TransferPostError<AccountId> { + #[inline] + fn from(err: InvalidSinkAccounts<AccountId>) -> Self { + Self::InvalidSinkAccounts(err) + } +} + +impl<AccountId> From<SenderPostError> for TransferPostError<AccountId> { + #[inline] + fn from(err: SenderPostError) -> Self { + Self::Sender(err) + } +} + +impl<AccountId> From<ReceiverPostError> for TransferPostError<AccountId> { + #[inline] + fn from(err: ReceiverPostError) -> Self { + Self::Receiver(err) + } +} /// Transfer Post pub struct TransferPost<C> @@ -1741,13 +1845,19 @@ where /// Validates `self` on the transfer `ledger`. #[inline] - pub fn validate<L>(self, ledger: &L) -> Result<TransferPostingKey<C, L>, TransferPostError> + pub fn validate<L>( + self, + source_accounts: Vec<L::AccountId>, + sink_accounts: Vec<L::AccountId>, + ledger: &L, + ) -> Result<TransferPostingKey<C, L>, TransferPostError<L::AccountId>> where L: TransferLedger<C>, { - let source_posting_keys = ledger - .check_source_balances(self.sources) - .map_err(TransferPostError::InsufficientPublicBalance)?; + let source_posting_keys = + ledger.check_source_accounts(self.asset_id, source_accounts, self.sources)?; + let sink_posting_keys = + ledger.check_sink_accounts(self.asset_id, sink_accounts, self.sinks)?; for (i, p) in self.sender_posts.iter().enumerate() { if self .sender_posts @@ -1784,7 +1894,7 @@ where &source_posting_keys, &sender_posting_keys, &receiver_posting_keys, - &self.sinks, + &sink_posting_keys, self.validity_proof, ) { Some(key) => key, @@ -1794,7 +1904,7 @@ where source_posting_keys, sender_posting_keys, receiver_posting_keys, - sinks: self.sinks, + sink_posting_keys, }) } } @@ -1817,8 +1927,8 @@ where /// Receiver Posting Keys receiver_posting_keys: Vec<ReceiverPostingKey<C, L>>, - /// Sinks - sinks: Vec<AssetValue>, + /// Sink Posting Keys + sink_posting_keys: Vec<SinkPostingKey<C, L>>, /// Validity Proof Posting Key validity_proof: L::ValidProof, @@ -1829,6 +1939,34 @@ where C: Configuration, L: TransferLedger<C>, { + /// Generates the public input for the [`Transfer`] validation proof. + #[inline] + pub fn generate_proof_input( + asset_id: Option<AssetId>, + sources: &[SourcePostingKey<C, L>], + senders: &[SenderPostingKey<C, L>], + receivers: &[ReceiverPostingKey<C, L>], + sinks: &[SinkPostingKey<C, L>], + ) -> ProofInput<C> { + let mut input = Default::default(); + if let Some(asset_id) = asset_id { + C::ProofSystem::extend(&mut input, &asset_id); + } + sources + .iter() + .for_each(|source| C::ProofSystem::extend(&mut input, source.as_ref())); + senders + .iter() + .for_each(|post| post.extend_input(&mut input)); + receivers + .iter() + .for_each(|post| post.extend_input(&mut input)); + sinks + .iter() + .for_each(|sink| C::ProofSystem::extend(&mut input, sink.as_ref())); + input + } + /// Posts `self` to the transfer `ledger`. /// /// # Safety @@ -1849,7 +1987,7 @@ where ledger.update_public_balances( asset_id, self.source_posting_keys, - self.sinks, + self.sink_posting_keys, proof, super_key, ); diff --git a/manta-pay/src/ledger.rs b/manta-pay/src/ledger.rs index 8cb891aff..ee047de02 100644 --- a/manta-pay/src/ledger.rs +++ b/manta-pay/src/ledger.rs @@ -17,20 +17,17 @@ //! Ledger Implementation use crate::config::{ - Config, EncryptedNote, MerkleTreeConfiguration, ProofSystem, Utxo, VerifyingContext, VoidNumber, + Config, EncryptedNote, MerkleTreeConfiguration, ProofSystem, Utxo, VoidNumber, }; use manta_accounting::{ asset::{AssetId, AssetValue}, transfer::{ - self, InsufficientPublicBalance, Proof, ReceiverLedger, ReceiverPostingKey, SenderLedger, - SenderPostingKey, TransferLedger, TransferLedgerSuperPostingKey, UtxoSetOutput, + self, AccountBalance, InvalidSinkAccounts, InvalidSourceAccounts, Proof, ReceiverLedger, + ReceiverPostingKey, SenderLedger, SenderPostingKey, SinkPostingKey, SourcePostingKey, + TransferLedger, TransferLedgerSuperPostingKey, TransferPostingKey, UtxoSetOutput, }, }; -use manta_crypto::{ - constraint::{Input as ProofSystemInput, ProofSystem as _}, - merkle_tree, - merkle_tree::forest::Configuration, -}; +use manta_crypto::{constraint::ProofSystem as _, merkle_tree, merkle_tree::forest::Configuration}; use std::collections::{HashMap, HashSet}; /// UTXO Merkle Forest Type @@ -44,6 +41,24 @@ pub type UtxoMerkleForest = merkle_tree::forest::TreeArrayMerkleForest< #[derive(Clone, Copy)] pub struct Wrap<T>(T); +impl<T> AsRef<T> for Wrap<T> { + #[inline] + fn as_ref(&self) -> &T { + &self.0 + } +} + +/// Wrap Pair Type +#[derive(Clone, Copy)] +pub struct WrapPair<L, R>(L, R); + +impl<L, R> AsRef<R> for WrapPair<L, R> { + #[inline] + fn as_ref(&self) -> &R { + &self.1 + } +} + /// Ledger pub struct Ledger { /// Void Numbers @@ -58,6 +73,9 @@ pub struct Ledger { /// UTXO Forest utxo_forest: UtxoMerkleForest, + /// Account Table + accounts: HashMap<u128, HashMap<AssetId, AssetValue>>, + /// Verifying Contexts verifying_context: transfer::canonical::VerifyingContext<Config>, } @@ -132,27 +150,93 @@ impl ReceiverLedger<Config> for Ledger { } impl TransferLedger<Config> for Ledger { - type ValidSourceBalance = Wrap<AssetValue>; + type AccountId = u128; + type ValidSourceAccount = WrapPair<Self::AccountId, AssetValue>; + type ValidSinkAccount = WrapPair<Self::AccountId, AssetValue>; type ValidProof = Wrap<()>; type SuperPostingKey = (); #[inline] - fn check_source_balances( + fn check_source_accounts( &self, + asset_id: Option<AssetId>, + accounts: Vec<Self::AccountId>, sources: Vec<AssetValue>, - ) -> Result<Vec<Self::ValidSourceBalance>, InsufficientPublicBalance> { - // FIXME: This can only be implemented on the actual ledger. - Ok(sources.into_iter().map(Wrap).collect()) + ) -> Result<Vec<Self::ValidSourceAccount>, InvalidSourceAccounts<Self::AccountId>> { + if let Some(asset_id) = asset_id { + let mut valid_source_accounts = Vec::new(); + for (account_id, withdraw) in accounts.into_iter().zip(sources) { + match self.accounts.get(&account_id) { + Some(map) => match map.get(&asset_id) { + Some(balance) => { + if balance >= &withdraw { + valid_source_accounts.push(WrapPair(account_id, withdraw)); + } else { + return Err(InvalidSourceAccounts::BadAccount { + account_id, + balance: AccountBalance::Known(*balance), + withdraw, + }); + } + } + _ => { + // FIXME: What about zero values in `sources`? + return Err(InvalidSourceAccounts::BadAccount { + account_id, + balance: AccountBalance::Known(AssetValue(0)), + withdraw, + }); + } + }, + _ => { + return Err(InvalidSourceAccounts::BadAccount { + account_id, + balance: AccountBalance::UnknownAccount, + withdraw, + }); + } + } + } + Ok(valid_source_accounts) + } else if accounts.is_empty() && sources.is_empty() { + Ok(Vec::new()) + } else { + Err(InvalidSourceAccounts::InvalidShape) + } + } + + #[inline] + fn check_sink_accounts( + &self, + asset_id: Option<AssetId>, + accounts: Vec<Self::AccountId>, + sinks: Vec<AssetValue>, + ) -> Result<Vec<Self::ValidSinkAccount>, InvalidSinkAccounts<Self::AccountId>> { + if asset_id.is_some() { + let mut valid_sink_accounts = Vec::new(); + for (account_id, deposit) in accounts.into_iter().zip(sinks) { + if self.accounts.contains_key(&account_id) { + valid_sink_accounts.push(WrapPair(account_id, deposit)); + } else { + return Err(InvalidSinkAccounts::BadAccount { account_id }); + } + } + Ok(valid_sink_accounts) + } else if accounts.is_empty() && sinks.is_empty() { + Ok(Vec::new()) + } else { + Err(InvalidSinkAccounts::InvalidShape) + } } #[inline] fn is_valid( &self, asset_id: Option<AssetId>, - sources: &[Self::ValidSourceBalance], + sources: &[SourcePostingKey<Config, Self>], senders: &[SenderPostingKey<Config, Self>], receivers: &[ReceiverPostingKey<Config, Self>], - sinks: &[AssetValue], + sinks: &[SinkPostingKey<Config, Self>], proof: Proof<Config>, ) -> Option<Self::ValidProof> { let verifying_context = self.verifying_context.select( @@ -162,41 +246,40 @@ impl TransferLedger<Config> for Ledger { receivers.len(), sinks.len(), )?; - - let mut input = Default::default(); - if let Some(asset_id) = asset_id { - ProofSystem::extend(&mut input, &asset_id); - } - sources - .iter() - .for_each(|source| ProofSystem::extend(&mut input, &source.0)); - senders.iter().for_each(|sender| { - // ... - todo!() - }); - receivers.iter().for_each(|receiver| { - // ... - todo!() - }); - sinks - .iter() - .for_each(|sink| ProofSystem::extend(&mut input, sink)); - - ProofSystem::verify(&input, &proof, verifying_context) - .ok()? - .then(move || Wrap(())) + ProofSystem::verify( + &TransferPostingKey::generate_proof_input(asset_id, sources, senders, receivers, sinks), + &proof, + verifying_context, + ) + .ok()? + .then(move || Wrap(())) } #[inline] fn update_public_balances( &mut self, asset_id: AssetId, - sources: Vec<Self::ValidSourceBalance>, - sinks: Vec<AssetValue>, + sources: Vec<SourcePostingKey<Config, Self>>, + sinks: Vec<SinkPostingKey<Config, Self>>, proof: Self::ValidProof, super_key: &TransferLedgerSuperPostingKey<Config, Self>, ) { - // FIXME: This can only be implemented on the real ledger. - let _ = (asset_id, sources, sinks, proof, super_key); + let _ = (proof, super_key); + for WrapPair(account_id, withdraw) in sources { + *self + .accounts + .get_mut(&account_id) + .expect("We checked that this account exists.") + .get_mut(&asset_id) + .expect("We checked that this account has enough balance to withdraw.") -= withdraw; + } + for WrapPair(account_id, deposit) in sinks { + *self + .accounts + .get_mut(&account_id) + .expect("We checked that this account exists.") + .entry(asset_id) + .or_default() += deposit; + } } } From d13db743737063f8ebc79a2886e7cceb653e2d0d Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sat, 8 Jan 2022 17:40:00 -0500 Subject: [PATCH 173/275] wip: setup ledger abstraction in pallet-manta-pay --- manta-accounting/Cargo.toml | 1 + manta-accounting/src/asset.rs | 4 ++++ manta-accounting/src/fs.rs | 1 + manta-accounting/src/transfer/mod.rs | 36 +++++++++++++++++----------- manta-pay/Cargo.toml | 6 +++-- manta-pay/src/ledger.rs | 6 +++-- manta-pay/src/lib.rs | 16 +++++++++++-- 7 files changed, 50 insertions(+), 20 deletions(-) diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 4c0f9fcc8..a55b9ff69 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -48,6 +48,7 @@ futures = { version = "0.3.19", optional = true } manta-crypto = { path = "../manta-crypto" } manta-util = { path = "../manta-util" } rayon = { version = "1.5.1", optional = true, default-features = false } +serde = { version = "1.0.133", optional = true, default-features = false, features = ["derive"] } zeroize = { version = "1.4.3", optional = true, default-features = false } [dev-dependencies] diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 694afd5ef..f117d4aa2 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -49,6 +49,7 @@ use std::{ pub type AssetIdType = u32; /// Asset Id Type +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Clone, Copy, Debug, Default, Display, Eq, From, Hash, Ord, PartialEq, PartialOrd)] #[from(forward)] pub struct AssetId( @@ -124,6 +125,7 @@ where pub type AssetValueType = u128; /// Asset Value Type +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive( Add, AddAssign, @@ -265,6 +267,7 @@ impl<'a> Sum<&'a AssetValue> for AssetValue { /// /// This `struct` is created by the [`make_change`](AssetValue::make_change) method on /// [`AssetValue`]. See its documentation for more. +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct Change { /// Base Amount @@ -322,6 +325,7 @@ impl ExactSizeIterator for Change {} impl FusedIterator for Change {} /// Asset +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Clone, Copy, Debug, Default, Display, Eq, From, Hash, PartialEq)] #[display(fmt = "{{id: {}, value: {}}}", id, value)] pub struct Asset<I = AssetId, V = AssetValue> { diff --git a/manta-accounting/src/fs.rs b/manta-accounting/src/fs.rs index ca2c8b4be..6a29f2f21 100644 --- a/manta-accounting/src/fs.rs +++ b/manta-accounting/src/fs.rs @@ -18,6 +18,7 @@ // FIXME: Add asynchronous streaming interfaces. +use alloc::vec::Vec; use core::future::Future; /// Serialization diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index 87c94084b..ea0de2fce 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -31,7 +31,6 @@ use manta_crypto::{ key::KeyAgreementScheme, rand::{CryptoRng, RngCore, Sample}, }; -use manta_util::from_variant_impl; pub mod batch; pub mod canonical; @@ -1600,6 +1599,9 @@ where /// Account Identifier type AccountId; + /// Ledger Event + type Event; + /// Valid [`AssetValue`] for [`TransferPost`] Source /// /// # Safety @@ -1659,7 +1661,7 @@ where receivers: &[ReceiverPostingKey<C, Self>], sinks: &[SinkPostingKey<C, Self>], proof: Proof<C>, - ) -> Option<Self::ValidProof>; + ) -> Option<(Self::ValidProof, Self::Event)>; /// Updates the public balances in the ledger, finishing the transaction. /// @@ -1888,23 +1890,25 @@ where .into_iter() .map(move |r| r.validate(ledger)) .collect::<Result<Vec<_>, _>>()?; + let (validity_proof, event) = match ledger.is_valid( + self.asset_id, + &source_posting_keys, + &sender_posting_keys, + &receiver_posting_keys, + &sink_posting_keys, + self.validity_proof, + ) { + Some((validity_proof, event)) => (validity_proof, event), + _ => return Err(TransferPostError::InvalidProof), + }; Ok(TransferPostingKey { - validity_proof: match ledger.is_valid( - self.asset_id, - &source_posting_keys, - &sender_posting_keys, - &receiver_posting_keys, - &sink_posting_keys, - self.validity_proof, - ) { - Some(key) => key, - _ => return Err(TransferPostError::InvalidProof), - }, asset_id: self.asset_id, source_posting_keys, sender_posting_keys, receiver_posting_keys, sink_posting_keys, + validity_proof, + event, }) } } @@ -1932,6 +1936,9 @@ where /// Validity Proof Posting Key validity_proof: L::ValidProof, + + /// Ledger Event + event: L::Event, } impl<C, L> TransferPostingKey<C, L> @@ -1975,7 +1982,7 @@ where /// [`SenderLedger::spend`] and [`ReceiverLedger::register`] for more information on the /// contract for this method. #[inline] - pub fn post(self, super_key: &TransferLedgerSuperPostingKey<C, L>, ledger: &mut L) { + pub fn post(self, super_key: &TransferLedgerSuperPostingKey<C, L>, ledger: &mut L) -> L::Event { let proof = self.validity_proof; for key in self.sender_posting_keys { key.post(&(proof, *super_key), ledger); @@ -1992,5 +1999,6 @@ where super_key, ); } + self.event } } diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index e3443e96f..0648a3e10 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -62,16 +62,18 @@ ark-ff = { version = "0.3.0", optional = true, default-features = false } ark-groth16 = { version = "0.3.0", optional = true, default-features = false } ark-r1cs-std = { version = "0.3.1", optional = true, default-features = false } ark-relations = { version = "0.3.0", optional = true, default-features = false } -bip32 = { version = "0.2.2", default-features = false, features = ["bip39", "secp256k1"] } +bip32 = { version = "0.3.0", default-features = false, features = ["bip39", "secp256k1"] } blake2 = { version = "0.10.0", default-features = false } derivative = { version = "2.2.0", default-features = false } -zk-garage-plonk = { package = "plonk", git = "https://github.com/zk-garage/plonk", optional = true, default-features = false } generic-array = { version = "0.14.4", default-features = false } manta-accounting = { path = "../manta-accounting" } manta-crypto = { path = "../manta-crypto" } manta-util = { path = "../manta-util" } rand_chacha = { version = "0.3.1", default-features = false } +scale-codec = { package = "parity-scale-codec", version = "2.3.1", optional = true, default-features = false, features = ["derive"] } +serde = { version = "1.0.133", optional = true, default-features = false, features = ["derive"] } x25519-dalek = { git = "https://github.com/Manta-Network/x25519-dalek", default-features = false, features = ["u64_backend"] } +zk-garage-plonk = { package = "plonk", git = "https://github.com/zk-garage/plonk", optional = true, default-features = false } [dev-dependencies] criterion = "0.3.5" diff --git a/manta-pay/src/ledger.rs b/manta-pay/src/ledger.rs index ee047de02..ebeed53ab 100644 --- a/manta-pay/src/ledger.rs +++ b/manta-pay/src/ledger.rs @@ -19,6 +19,7 @@ use crate::config::{ Config, EncryptedNote, MerkleTreeConfiguration, ProofSystem, Utxo, VoidNumber, }; +use alloc::vec::Vec; use manta_accounting::{ asset::{AssetId, AssetValue}, transfer::{ @@ -151,6 +152,7 @@ impl ReceiverLedger<Config> for Ledger { impl TransferLedger<Config> for Ledger { type AccountId = u128; + type Event = (); type ValidSourceAccount = WrapPair<Self::AccountId, AssetValue>; type ValidSinkAccount = WrapPair<Self::AccountId, AssetValue>; type ValidProof = Wrap<()>; @@ -238,7 +240,7 @@ impl TransferLedger<Config> for Ledger { receivers: &[ReceiverPostingKey<Config, Self>], sinks: &[SinkPostingKey<Config, Self>], proof: Proof<Config>, - ) -> Option<Self::ValidProof> { + ) -> Option<(Self::ValidProof, Self::Event)> { let verifying_context = self.verifying_context.select( asset_id.is_some(), sources.len(), @@ -252,7 +254,7 @@ impl TransferLedger<Config> for Ledger { verifying_context, ) .ok()? - .then(move || Wrap(())) + .then(move || (Wrap(()), ())) } #[inline] diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index 14b03824b..ebac2a540 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -28,10 +28,22 @@ mod test; pub mod crypto; pub mod key; -pub mod ledger; pub mod simulation; -pub mod wallet; #[cfg(all(feature = "arkworks", feature = "groth16"))] #[cfg_attr(doc_cfg, doc(cfg(all(feature = "arkworks", feature = "groth16"))))] pub mod config; + +#[cfg(all(feature = "std", feature = "arkworks", feature = "groth16"))] +#[cfg_attr( + doc_cfg, + doc(cfg(all(feature = "std", feature = "arkworks", feature = "groth16"))) +)] +pub mod ledger; + +#[cfg(all(feature = "std", feature = "arkworks", feature = "groth16"))] +#[cfg_attr( + doc_cfg, + doc(cfg(all(feature = "std", feature = "arkworks", feature = "groth16"))) +)] +pub mod wallet; From 3ca4cbde19f75f8ab56a9e6171f6a2b997798dbe Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sat, 8 Jan 2022 18:30:32 -0500 Subject: [PATCH 174/275] wip: move serdes traits to manta-util --- manta-accounting/Cargo.toml | 4 +- manta-accounting/src/fs.rs | 60 ++-------------------- manta-crypto/src/encryption.rs | 24 +++++++-- manta-util/Cargo.toml | 3 ++ manta-util/src/lib.rs | 9 +++- manta-util/src/serde.rs | 94 ++++++++++++++++++++++++++++++++++ 6 files changed, 129 insertions(+), 65 deletions(-) create mode 100644 manta-util/src/serde.rs diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index a55b9ff69..a62a193e3 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -30,7 +30,6 @@ cocoon-fs = [ "async-std", "cocoon/std", "futures", - "zeroize/alloc", ] # Standard Library @@ -46,10 +45,9 @@ derivative = { version = "2.2.0", default-features = false, features = ["use_cor derive_more = { version = "0.99.16", default-features = false, features = ["add", "add_assign", "display", "from", "mul", "mul_assign", "sum"] } futures = { version = "0.3.19", optional = true } manta-crypto = { path = "../manta-crypto" } -manta-util = { path = "../manta-util" } +manta-util = { path = "../manta-util", features = ["zeroize"] } rayon = { version = "1.5.1", optional = true, default-features = false } serde = { version = "1.0.133", optional = true, default-features = false, features = ["derive"] } -zeroize = { version = "1.4.3", optional = true, default-features = false } [dev-dependencies] rand = "0.8.4" diff --git a/manta-accounting/src/fs.rs b/manta-accounting/src/fs.rs index 6a29f2f21..5fb671188 100644 --- a/manta-accounting/src/fs.rs +++ b/manta-accounting/src/fs.rs @@ -20,60 +20,7 @@ use alloc::vec::Vec; use core::future::Future; - -/// Serialization -pub trait Serialize { - /// Appends representation of `self` in bytes to `buffer`. - fn serialize(&self, buffer: &mut Vec<u8>); - - /// Converts `self` into a vector of bytes. - #[inline] - fn to_vec(&self) -> Vec<u8> { - let mut buffer = Vec::new(); - self.serialize(&mut buffer); - buffer - } -} - -impl Serialize for u8 { - #[inline] - fn serialize(&self, buffer: &mut Vec<u8>) { - buffer.push(*self); - } -} - -impl<T> Serialize for [T] -where - T: Serialize, -{ - #[inline] - fn serialize(&self, buffer: &mut Vec<u8>) { - for item in self { - item.serialize(buffer); - } - } -} - -impl<T, const N: usize> Serialize for [T; N] -where - T: Serialize, -{ - #[inline] - fn serialize(&self, buffer: &mut Vec<u8>) { - for item in self { - item.serialize(buffer); - } - } -} - -/// Deserialization -pub trait Deserialize: Sized { - /// Error Type - type Error; - - /// Parses the input `buffer` into a concrete value of type `Self` if possible. - fn deserialize(buffer: Vec<u8>) -> Result<Self, Self::Error>; -} +use manta_util::{Deserialize, Serialize}; /// Filesystem Encrypted Saving pub trait SaveEncrypted { @@ -129,7 +76,7 @@ where D: Deserialize, { match L::load_bytes(path, loading_key).await { - Ok(bytes) => D::deserialize(bytes).map_err(LoadError::Deserialize), + Ok(bytes) => D::from_vec(bytes).map_err(LoadError::Deserialize), Err(err) => Err(LoadError::Loading(err)), } } @@ -161,8 +108,7 @@ pub mod cocoon { use cocoon_crate::{Cocoon, Error as CocoonError}; use core::fmt; use futures::future::LocalBoxFuture; - use manta_util::from_variant_impl; - use zeroize::Zeroizing; + use manta_util::{from_variant_impl, zeroize::Zeroizing}; /// Cocoon Loading/Saving Error #[derive(Debug)] diff --git a/manta-crypto/src/encryption.rs b/manta-crypto/src/encryption.rs index 7591aa8f3..33e37f979 100644 --- a/manta-crypto/src/encryption.rs +++ b/manta-crypto/src/encryption.rs @@ -17,7 +17,7 @@ //! Encryption Primitives use crate::key::{KeyAgreementScheme, KeyDerivationFunction}; -use core::marker::PhantomData; +use core::{fmt::Debug, hash::Hash, marker::PhantomData}; /// Symmetric Key Encryption Scheme /// @@ -172,15 +172,24 @@ where } /// Encrypted Message +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "H::Ciphertext: Clone, PublicKey<H>: Clone"), + Copy(bound = "H::Ciphertext: Copy, PublicKey<H>: Copy"), + Debug(bound = "H::Ciphertext: Debug, PublicKey<H>: Debug"), + Eq(bound = "H::Ciphertext: Eq, PublicKey<H>: Eq"), + Hash(bound = "H::Ciphertext: Hash, PublicKey<H>: Hash"), + PartialEq(bound = "H::Ciphertext: PartialEq, PublicKey<H>: PartialEq") +)] pub struct EncryptedMessage<H> where H: HybridPublicKeyEncryptionScheme, { /// Ciphertext - ciphertext: H::Ciphertext, + pub ciphertext: H::Ciphertext, /// Ephemeral Public Key - ephemeral_public_key: PublicKey<H>, + pub ephemeral_public_key: PublicKey<H>, } impl<H> EncryptedMessage<H> @@ -230,6 +239,15 @@ where } /// Decrypted Message +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "H::Plaintext: Clone, PublicKey<H>: Clone"), + Copy(bound = "H::Plaintext: Copy, PublicKey<H>: Copy"), + Debug(bound = "H::Plaintext: Debug, PublicKey<H>: Debug"), + Eq(bound = "H::Plaintext: Eq, PublicKey<H>: Eq"), + Hash(bound = "H::Plaintext: Hash, PublicKey<H>: Hash"), + PartialEq(bound = "H::Plaintext: PartialEq, PublicKey<H>: PartialEq") +)] pub struct DecryptedMessage<H> where H: HybridPublicKeyEncryptionScheme, diff --git a/manta-util/Cargo.toml b/manta-util/Cargo.toml index c4b62419f..5a55c9555 100644 --- a/manta-util/Cargo.toml +++ b/manta-util/Cargo.toml @@ -23,3 +23,6 @@ rustdoc-args = ["--cfg", "doc_cfg"] is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } + +[dependencies] +zeroize = { version = "1.4.3", optional = true, default-features = false, features = ["alloc"] } diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index 07e2d5a9a..2cd0b77b0 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -16,8 +16,6 @@ //! Utilities -// TODO: Find a better way to abstract the `Rollback` trait. - #![no_std] #![cfg_attr(doc_cfg, feature(doc_cfg))] #![forbid(rustdoc::broken_intra_doc_links)] @@ -28,6 +26,7 @@ extern crate alloc; mod array; mod concat; mod sealed; +mod serde; pub mod iter; pub mod num; @@ -35,6 +34,12 @@ pub mod pointer; pub use array::*; pub use concat::*; +pub use serde::*; + +#[doc(inline)] +#[cfg(feature = "zeroize")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "zeroize")))] +pub use zeroize; /// Implements [`From`]`<$from>` for an enum `$to`, choosing the `$kind` variant. // TODO: add `where` clauses diff --git a/manta-util/src/serde.rs b/manta-util/src/serde.rs new file mode 100644 index 000000000..62a9de145 --- /dev/null +++ b/manta-util/src/serde.rs @@ -0,0 +1,94 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Serialization Utilities + +use alloc::vec::Vec; + +/// Serialization +pub trait Serialize { + /// Appends representation of `self` in bytes to `buffer`. + fn serialize(&self, buffer: &mut Vec<u8>); + + /// Converts `self` into a vector of bytes. + #[inline] + fn to_vec(&self) -> Vec<u8> { + let mut buffer = Vec::new(); + self.serialize(&mut buffer); + buffer + } +} + +impl Serialize for u8 { + #[inline] + fn serialize(&self, buffer: &mut Vec<u8>) { + buffer.push(*self); + } +} + +impl<T> Serialize for [T] +where + T: Serialize, +{ + #[inline] + fn serialize(&self, buffer: &mut Vec<u8>) { + for item in self { + item.serialize(buffer); + } + } +} + +impl<T, const N: usize> Serialize for [T; N] +where + T: Serialize, +{ + #[inline] + fn serialize(&self, buffer: &mut Vec<u8>) { + for item in self { + item.serialize(buffer); + } + } +} + +/// Exact Size Serialization +pub trait SerializeExactSize<const N: usize>: Serialize { + /// Converts `self` into a exactly known byte array. + fn to_array(&self) -> [u8; N]; +} + +/// Deserialization +pub trait Deserialize: Sized { + /// Error Type + type Error; + + /// Parses the input `buffer` into a concrete value of type `Self` if possible. + fn deserialize(buffer: &mut Vec<u8>) -> Result<Self, Self::Error>; + + /// Converts a byte vector into a concrete value of type `Self` if possible. + #[cfg(feature = "zeroize")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "zeroize")))] + #[inline] + fn from_vec(buffer: Vec<u8>) -> Result<Self, Self::Error> { + let mut buffer = zeroize::Zeroizing::new(buffer); + Self::deserialize(&mut buffer) + } +} + +/// Exact Size Deserialization +pub trait DeserializeExactSize<const N: usize>: Deserialize { + /// Converts a fixed-length byte array into a concrete value of type `Self`. + fn from_array(buffer: [u8; N]) -> Self; +} From d1edc3824f90efee331fb4607a13244df8bb96af Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 10 Jan 2022 02:13:38 -0500 Subject: [PATCH 175/275] wip: start moving to more resource-specific optimizations --- Cargo.toml | 2 - manta-accounting/src/asset.rs | 92 ++++++--- manta-accounting/src/transfer/canonical.rs | 103 +++++----- manta-accounting/src/transfer/mod.rs | 14 +- manta-accounting/src/transfer/test.rs | 6 +- manta-accounting/src/wallet/signer.rs | 178 ++++++++++++------ manta-codec/Cargo.toml | 39 ---- manta-codec/LICENSE | 1 - manta-codec/README.md | 1 - manta-codec/src/lib.rs | 136 ------------- manta-crypto/src/constraint.rs | 6 +- manta-pay/src/config.rs | 3 +- .../constraint/arkworks/proof_system.rs | 4 + manta-pay/src/ledger.rs | 9 +- manta-pay/src/test/transfer.rs | 19 +- manta-util/src/cache.rs | 62 ++++++ manta-util/src/future.rs | 26 +++ manta-util/src/lib.rs | 2 + src/lib.rs | 3 - 19 files changed, 370 insertions(+), 336 deletions(-) delete mode 100644 manta-codec/Cargo.toml delete mode 120000 manta-codec/LICENSE delete mode 100644 manta-codec/README.md delete mode 100644 manta-codec/src/lib.rs create mode 100644 manta-util/src/cache.rs create mode 100644 manta-util/src/future.rs diff --git a/Cargo.toml b/Cargo.toml index c3f269f2d..66819f149 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,6 @@ maintenance = { status = "actively-developed" } resolver = "2" members = [ "manta-accounting", - "manta-codec", "manta-crypto", "manta-pay", "manta-util", @@ -46,7 +45,6 @@ test = ["manta-accounting/test", "manta-crypto/test"] [dependencies] manta-accounting = { path = "manta-accounting" } -manta-codec = { path = "manta-codec" } manta-crypto = { path = "manta-crypto" } manta-pay = { path = "manta-pay" } manta-util = { path = "manta-util" } diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index f117d4aa2..b81773ac5 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -21,12 +21,14 @@ // TODO: Implement `Concat` for `AssetId` and `AssetValue`. // TODO: Add implementations for `AssetMap` using sorted vectors and/or max-heaps -use alloc::{collections::BTreeMap, vec::Vec}; +use alloc::{ + collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}, + vec::Vec, +}; use core::{ fmt::Debug, hash::Hash, - iter, - iter::{FusedIterator, Sum}, + iter::{self, FusedIterator, Sum}, ops::{Add, AddAssign, Mul, Sub, SubAssign}, slice, }; @@ -41,7 +43,7 @@ use manta_util::{into_array_unchecked, Concat, ConcatAccumulator}; #[cfg(feature = "std")] use std::{ - collections::hash_map::{HashMap, RandomState}, + collections::hash_map::{Entry as HashMapEntry, HashMap, RandomState}, hash::BuildHasher, }; @@ -108,6 +110,13 @@ impl From<AssetId> for [u8; AssetId::SIZE] { } } +impl PartialEq<AssetIdType> for AssetId { + #[inline] + fn eq(&self, rhs: &AssetIdType) -> bool { + self.0 == *rhs + } +} + impl<D> Sample<D> for AssetId where AssetIdType: Sample<D>, @@ -238,6 +247,13 @@ impl Mul<AssetValue> for AssetValueType { } } +impl PartialEq<AssetValueType> for AssetValue { + #[inline] + fn eq(&self, rhs: &AssetValueType) -> bool { + self.0 == *rhs + } +} + impl<D> Sample<D> for AssetValue where AssetValueType: Sample<D>, @@ -326,7 +342,7 @@ impl FusedIterator for Change {} /// Asset #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -#[derive(Clone, Copy, Debug, Default, Display, Eq, From, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Default, Display, Eq, From, Hash, Ord, PartialEq, PartialOrd)] #[display(fmt = "{{id: {}, value: {}}}", id, value)] pub struct Asset<I = AssetId, V = AssetValue> { /// Asset Id @@ -554,6 +570,10 @@ where /// Asset Map /// /// This trait represents an asset distribution over some [`Key`](Self::Key) type. +/// +/// # Warning +/// +/// It is possible that keys are repeated, as long as the assets associated to them are different. pub trait AssetMap: Default { /// Key Type /// @@ -600,20 +620,21 @@ pub trait AssetMap: Default { } /// Removes the `key` from the map. - fn remove(&mut self, key: Self::Key); + fn remove(&mut self, key: Self::Key, asset: Asset); /// Removes all the keys in `iter` from the map. fn remove_all<I>(&mut self, iter: I) where - I: IntoIterator<Item = Self::Key>, + I: IntoIterator<Item = (Self::Key, Asset)>, { - iter.into_iter().for_each(move |key| self.remove(key)); + iter.into_iter() + .for_each(move |(key, asset)| self.remove(key, asset)); } } /// Implements [`AssetMap`] for map types. macro_rules! impl_asset_map_for_maps_body { - ($k:tt) => { + ($k:tt, $entry:tt) => { type Key = $k; #[inline] @@ -624,11 +645,13 @@ macro_rules! impl_asset_map_for_maps_body { } let mut sum = Asset::zero(asset.id); let mut values = Vec::new(); - for (key, item) in self { - if item.value != AssetValue(0) && sum.try_add_assign(*item) { - values.push((key.clone(), item.value)); - if sum.value >= asset.value { - break; + for (key, assets) in self { + for item in assets { + if item.value != AssetValue(0) && sum.try_add_assign(*item) { + values.push((key.clone(), item.value)); + if sum.value >= asset.value { + break; + } } } } @@ -642,8 +665,11 @@ macro_rules! impl_asset_map_for_maps_body { #[inline] fn zeroes(&self, n: usize, id: AssetId) -> Vec<Self::Key> { self.iter() - .filter_map(move |(k, a)| { - (a.id == id && a.value == AssetValue(0)).then(move || k.clone()) + .filter_map(move |(key, assets)| { + assets + .iter() + .any(move |a| a.id == id && a.value == 0) + .then(move || key.clone()) }) .take(n) .collect() @@ -651,30 +677,48 @@ macro_rules! impl_asset_map_for_maps_body { #[inline] fn insert(&mut self, key: Self::Key, asset: Asset) { - self.insert(key, asset); + match self.entry(key) { + $entry::Vacant(entry) => { + entry.insert(vec![asset]); + } + $entry::Occupied(mut entry) => { + let assets = entry.get_mut(); + if let Err(index) = assets.binary_search(&asset) { + assets.insert(index, asset); + } + } + } } #[inline] - fn remove(&mut self, key: Self::Key) { - self.remove(&key); + fn remove(&mut self, key: Self::Key, asset: Asset) { + if let $entry::Occupied(mut entry) = self.entry(key) { + let assets = entry.get_mut(); + if let Ok(index) = assets.binary_search(&asset) { + assets.remove(index); + } + if assets.is_empty() { + entry.remove(); + } + } } }; } /// B-Tree Map [`AssetMap`] Implementation -pub type BTreeAssetMap<K> = BTreeMap<K, Asset>; +pub type BTreeAssetMap<K> = BTreeMap<K, Vec<Asset>>; impl<K> AssetMap for BTreeAssetMap<K> where K: Clone + Ord, { - impl_asset_map_for_maps_body! { K } + impl_asset_map_for_maps_body! { K, BTreeMapEntry } } /// Hash Map [`AssetMap`] Implementation #[cfg(feature = "std")] #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] -pub type HashAssetMap<K, S = RandomState> = HashMap<K, Asset, S>; +pub type HashAssetMap<K, S = RandomState> = HashMap<K, Vec<Asset>, S>; #[cfg(feature = "std")] #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] @@ -683,7 +727,7 @@ where K: Clone + Hash + Eq, S: BuildHasher + Default, { - impl_asset_map_for_maps_body! { K } + impl_asset_map_for_maps_body! { K, HashMapEntry } } /// Asset Selection @@ -873,7 +917,7 @@ mod test { let n = rng.gen_range(1..0xFFFF); let change = amount.make_change(n).unwrap().collect::<Vec<_>>(); assert_eq!(n, change.len()); - assert_eq!(amount, change.into_iter().sum()); + assert_eq!(amount, change.into_iter().sum::<AssetValue>()); } } } diff --git a/manta-accounting/src/transfer/canonical.rs b/manta-accounting/src/transfer/canonical.rs index 42c52303d..3481e6177 100644 --- a/manta-accounting/src/transfer/canonical.rs +++ b/manta-accounting/src/transfer/canonical.rs @@ -18,7 +18,10 @@ use crate::{ asset::{self, Asset, AssetValue}, - transfer::{self, Configuration, PreSender, Receiver, ReceivingKey, Sender, Transfer}, + transfer::{ + Configuration, PreSender, ProvingContext, Receiver, ReceivingKey, Sender, Transfer, + VerifyingContext, + }, }; use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; @@ -317,96 +320,82 @@ where } } -/// Canonical Proving Contexts +/// Canonical Multi-Proving Contexts #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "transfer::ProvingContext<C>: Clone"), - Copy(bound = "transfer::ProvingContext<C>: Copy"), - Debug(bound = "transfer::ProvingContext<C>: Debug"), - Default(bound = "transfer::ProvingContext<C>: Default"), - Eq(bound = "transfer::ProvingContext<C>: Eq"), - Hash(bound = "transfer::ProvingContext<C>: Hash"), - PartialEq(bound = "transfer::ProvingContext<C>: PartialEq") + Clone(bound = "ProvingContext<C>: Clone"), + Copy(bound = "ProvingContext<C>: Copy"), + Debug(bound = "ProvingContext<C>: Debug"), + Default(bound = "ProvingContext<C>: Default"), + Eq(bound = "ProvingContext<C>: Eq"), + Hash(bound = "ProvingContext<C>: Hash"), + PartialEq(bound = "ProvingContext<C>: PartialEq") )] -pub struct ProvingContext<C> +pub struct MultiProvingContext<C> where - C: Configuration, + C: Configuration + ?Sized, { /// Mint Proving Context - pub mint: transfer::ProvingContext<C>, + pub mint: ProvingContext<C>, /// Private Transfer Proving Context - pub private_transfer: transfer::ProvingContext<C>, + pub private_transfer: ProvingContext<C>, /// Reclaim Proving Context - pub reclaim: transfer::ProvingContext<C>, + pub reclaim: ProvingContext<C>, } -impl<C> ProvingContext<C> +impl<C> MultiProvingContext<C> where - C: Configuration, + C: Configuration + ?Sized, { - /// Selects the proving context for the given shape if it matches a canonical shape. + /// Selects a [`ProvingContext`] based on `shape`. #[inline] - pub fn select( - &self, - asset_id_is_some: bool, - sources: usize, - senders: usize, - receivers: usize, - sinks: usize, - ) -> Option<&transfer::ProvingContext<C>> { - match TransferShape::select(asset_id_is_some, sources, senders, receivers, sinks)? { - TransferShape::Mint => Some(&self.mint), - TransferShape::PrivateTransfer => Some(&self.private_transfer), - TransferShape::Reclaim => Some(&self.reclaim), + pub fn select(&self, shape: TransferShape) -> &ProvingContext<C> { + match shape { + TransferShape::Mint => &self.mint, + TransferShape::PrivateTransfer => &self.private_transfer, + TransferShape::Reclaim => &self.reclaim, } } } -/// Canonical Verifying Contexts +/// Canonical Multi-Verifying Contexts #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "transfer::VerifyingContext<C>: Clone"), - Copy(bound = "transfer::VerifyingContext<C>: Copy"), - Debug(bound = "transfer::VerifyingContext<C>: Debug"), - Default(bound = "transfer::VerifyingContext<C>: Default"), - Eq(bound = "transfer::VerifyingContext<C>: Eq"), - Hash(bound = "transfer::VerifyingContext<C>: Hash"), - PartialEq(bound = "transfer::VerifyingContext<C>: PartialEq") + Clone(bound = "VerifyingContext<C>: Clone"), + Copy(bound = "VerifyingContext<C>: Copy"), + Debug(bound = "VerifyingContext<C>: Debug"), + Default(bound = "VerifyingContext<C>: Default"), + Eq(bound = "VerifyingContext<C>: Eq"), + Hash(bound = "VerifyingContext<C>: Hash"), + PartialEq(bound = "VerifyingContext<C>: PartialEq") )] -pub struct VerifyingContext<C> +pub struct MultiVerifyingContext<C> where - C: Configuration, + C: Configuration + ?Sized, { /// Mint Verifying Context - pub mint: transfer::VerifyingContext<C>, + pub mint: VerifyingContext<C>, /// Private Transfer Verifying Context - pub private_transfer: transfer::VerifyingContext<C>, + pub private_transfer: VerifyingContext<C>, /// Reclaim Verifying Context - pub reclaim: transfer::VerifyingContext<C>, + pub reclaim: VerifyingContext<C>, } -impl<C> VerifyingContext<C> +impl<C> MultiVerifyingContext<C> where - C: Configuration, + C: Configuration + ?Sized, { - /// Selects the verifying context for the given shape if it matches a canonical shape. + /// Selects a [`VerifyingContext`] based on `shape`. #[inline] - pub fn select( - &self, - asset_id_is_some: bool, - sources: usize, - senders: usize, - receivers: usize, - sinks: usize, - ) -> Option<&transfer::VerifyingContext<C>> { - match TransferShape::select(asset_id_is_some, sources, senders, receivers, sinks)? { - TransferShape::Mint => Some(&self.mint), - TransferShape::PrivateTransfer => Some(&self.private_transfer), - TransferShape::Reclaim => Some(&self.reclaim), + pub fn select(&self, shape: TransferShape) -> &VerifyingContext<C> { + match shape { + TransferShape::Mint => &self.mint, + TransferShape::PrivateTransfer => &self.private_transfer, + TransferShape::Reclaim => &self.reclaim, } } } diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index ea0de2fce..e9865d860 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -23,8 +23,8 @@ use manta_crypto::{ accumulator::{Accumulator, MembershipProof, Model}, commitment::CommitmentScheme, constraint::{ - Add, Allocator, Constant, ConstraintSystem, Derived, Equal, Input as ProofSystemInput, - ProofSystem, Public, Secret, ValueSource, Variable, + Add, Allocator, Constant, ConstraintSystem, Derived, Equal, ProofSystem, ProofSystemInput, + Public, Secret, ValueSource, Variable, }, encryption::{DecryptedMessage, EncryptedMessage, HybridPublicKeyEncryptionScheme}, hash::BinaryHashFunction, @@ -342,6 +342,9 @@ type ProofSystemType<C> = <C as Configuration>::ProofSystem; /// Transfer Proof System Error Type pub type ProofSystemError<C> = <ProofSystemType<C> as ProofSystem>::Error; +/// Transfer Proof System Public Parameters Type +pub type ProofSystemPublicParameters<C> = <ProofSystemType<C> as ProofSystem>::PublicParameters; + /// Transfer Proving Context Type pub type ProvingContext<C> = <ProofSystemType<C> as ProofSystem>::ProvingContext; @@ -1406,13 +1409,18 @@ where /// Generates a proving and verifying context for this transfer shape. #[inline] pub fn generate_context<R>( + public_parameters: &ProofSystemPublicParameters<C>, parameters: FullParameters<C>, rng: &mut R, ) -> Result<(ProvingContext<C>, VerifyingContext<C>), ProofSystemError<C>> where R: CryptoRng + RngCore + ?Sized, { - C::ProofSystem::generate_context(Self::unknown_constraints(parameters), rng) + C::ProofSystem::generate_context( + Self::unknown_constraints(parameters), + public_parameters, + rng, + ) } /// Converts `self` into its ledger post. diff --git a/manta-accounting/src/transfer/test.rs b/manta-accounting/src/transfer/test.rs index 4a1e135c9..d7e723fa1 100644 --- a/manta-accounting/src/transfer/test.rs +++ b/manta-accounting/src/transfer/test.rs @@ -20,7 +20,7 @@ use crate::{ asset::{Asset, AssetId, AssetValue, AssetValueType}, transfer::{ has_public_participants, Configuration, FullParameters, Parameters, PreSender, - ProofSystemError, Receiver, Sender, Transfer, Utxo, + ProofSystemError, ProofSystemPublicParameters, Receiver, Sender, Transfer, Utxo, }, }; use alloc::vec::Vec; @@ -130,6 +130,7 @@ where /// validated. #[inline] pub fn sample_and_check_proof<A, R>( + public_parameters: &ProofSystemPublicParameters<C>, parameters: &Parameters<C>, utxo_set: &mut A, rng: &mut R, @@ -146,7 +147,8 @@ where rng, ); let full_parameters = FullParameters::new(parameters, utxo_set.model()); - let (proving_context, verifying_context) = Self::generate_context(full_parameters, rng)?; + let (proving_context, verifying_context) = + Self::generate_context(public_parameters, full_parameters, rng)?; let post = transfer.into_post(full_parameters, &proving_context, rng)?; C::ProofSystem::verify( &post.generate_proof_input(), diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index d5ef4eb8f..af82670d0 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -30,12 +30,12 @@ use crate::{ self, batch::Join, canonical::{ - Mint, PrivateTransfer, PrivateTransferShape, ProvingContext, Reclaim, Selection, Shape, - Transaction, + Mint, MultiProvingContext, PrivateTransfer, PrivateTransferShape, Reclaim, Selection, + Shape, Transaction, }, - EncryptedNote, FullParameters, Parameters, PreSender, ProofSystemError, PublicKey, - Receiver, ReceivingKey, SecretKey, Sender, SpendingKey, Transfer, TransferPost, Utxo, - VoidNumber, + EncryptedNote, FullParameters, Parameters, PreSender, ProofSystemError, ProvingContext, + PublicKey, Receiver, ReceivingKey, SecretKey, Sender, SpendingKey, Transfer, TransferPost, + Utxo, VoidNumber, }, }; use alloc::{vec, vec::Vec}; @@ -50,7 +50,10 @@ use manta_crypto::{ encryption::DecryptedMessage, rand::{CryptoRng, Rand, RngCore}, }; -use manta_util::{fallible_array_map, into_array_unchecked, iter::IteratorExt, Rollback}; +use manta_util::{ + cache::CachedResource, fallible_array_map, future::LocalBoxFuture, into_array_unchecked, + iter::IteratorExt, Rollback, +}; /// Signer Connection pub trait Connection<H, C> @@ -207,6 +210,9 @@ pub trait Configuration: transfer::Configuration { /// Asset Map Type type AssetMap: AssetMap<Key = AssetMapKey<Self>>; + /// Proving Context Cache + type ProvingContextCache: CachedResource<MultiProvingContext<Self>>; + /// Random Number Generator Type type Rng: CryptoRng + RngCore; } @@ -239,7 +245,7 @@ where account_table: AccountTable<C>, /// Proving Context - proving_context: ProvingContext<C>, + proving_context: C::ProvingContextCache, /// Parameters parameters: Parameters<C>, @@ -262,7 +268,7 @@ where #[inline] fn new_inner( account_table: AccountTable<C>, - proving_context: ProvingContext<C>, + proving_context: C::ProvingContextCache, parameters: Parameters<C>, utxo_set: C::UtxoSet, assets: C::AssetMap, @@ -284,10 +290,12 @@ where /// /// This method assumes that `account_table` has never been used before, and does not attempt /// to perform wallet recovery on this table. + // + // FIXME: Check that this warning even makes sense. #[inline] pub fn new( account_table: AccountTable<C>, - proving_context: ProvingContext<C>, + proving_context: C::ProvingContextCache, parameters: Parameters<C>, utxo_set: C::UtxoSet, rng: C::Rng, @@ -345,7 +353,8 @@ where { void_numbers.remove(void_number_index); self.utxo_set.remove_proof(&utxo); - self.assets.remove((index.spend, ephemeral_public_key)); + self.assets + .remove((index.spend, ephemeral_public_key), asset); } else { assets.push(asset); self.assets @@ -476,29 +485,69 @@ where const RECEIVERS: usize, const SINKS: usize, >( - &mut self, + parameters: FullParameters<C>, + proving_context: &ProvingContext<C>, transfer: Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>, + rng: &mut C::Rng, ) -> Result<TransferPost<C>, SignerError<C>> { - /* TODO: transfer - .into_post( - FullParameters::new(&self.parameters, self.utxo_set.model()), - &self.proving_context, - &mut self.rng, - ) + .into_post(parameters, proving_context, rng) .map_err(Error::ProofSystemError) - */ - todo!() + } + + /// Builds a [`TransferPost`] for `mint`. + #[inline] + fn mint_post( + &mut self, + proving_context: &ProvingContext<C>, + mint: Mint<C>, + ) -> Result<TransferPost<C>, SignerError<C>> { + Self::build_post( + FullParameters::new(&self.parameters, self.utxo_set.model()), + proving_context, + mint, + &mut self.rng, + ) + } + + /// Builds a [`TransferPost`] for `private_transfer`. + #[inline] + fn private_transfer_post( + &mut self, + proving_context: &ProvingContext<C>, + private_transfer: PrivateTransfer<C>, + ) -> Result<TransferPost<C>, SignerError<C>> { + Self::build_post( + FullParameters::new(&self.parameters, self.utxo_set.model()), + proving_context, + private_transfer, + &mut self.rng, + ) + } + + /// Builds a [`TransferPost`] for `reclaim`. + #[inline] + fn reclaim_post( + &mut self, + proving_context: &ProvingContext<C>, + reclaim: Reclaim<C>, + ) -> Result<TransferPost<C>, SignerError<C>> { + Self::build_post( + FullParameters::new(&self.parameters, self.utxo_set.model()), + proving_context, + reclaim, + &mut self.rng, + ) } /// Computes the next [`Join`](Join) element for an asset rebalancing round. #[allow(clippy::type_complexity)] // NOTE: Clippy is too harsh here. #[inline] - fn next_join<const RECEIVERS: usize>( + fn next_join( &mut self, asset_id: AssetId, total: AssetValue, - ) -> Result<([Receiver<C>; RECEIVERS], Join<C>), SignerError<C>> { + ) -> Result<([Receiver<C>; PrivateTransferShape::RECEIVERS], Join<C>), SignerError<C>> { let keypair = self .account() .default_keypair() @@ -513,14 +562,15 @@ where /// Prepares the final pre-senders for the last part of the transaction. #[inline] - fn prepare_final_pre_senders<const SENDERS: usize>( + fn prepare_final_pre_senders( &mut self, + proving_context: &MultiProvingContext<C>, asset_id: AssetId, mut new_zeroes: Vec<PreSender<C>>, pre_senders: &mut Vec<PreSender<C>>, posts: &mut Vec<TransferPost<C>>, ) -> Result<(), SignerError<C>> { - let mut needed_zeroes = SENDERS - pre_senders.len(); + let mut needed_zeroes = PrivateTransferShape::SENDERS - pre_senders.len(); if needed_zeroes == 0 { return Ok(()); } @@ -544,7 +594,7 @@ where } for _ in 0..needed_mints { let (mint, pre_sender) = self.mint_zero(asset_id)?; - posts.push(self.build_post(mint)?); + posts.push(self.mint_post(&proving_context.mint, mint)?); pre_senders.push(pre_sender); } Ok(()) @@ -552,34 +602,34 @@ where /// Computes the batched transactions for rebalancing before a final transfer. #[inline] - fn compute_batched_transactions<const SENDERS: usize, const RECEIVERS: usize>( + fn compute_batched_transactions( &mut self, + proving_context: &MultiProvingContext<C>, asset_id: AssetId, mut pre_senders: Vec<PreSender<C>>, posts: &mut Vec<TransferPost<C>>, - ) -> Result<[Sender<C>; SENDERS], SignerError<C>> { - assert!( - (SENDERS >= 2) && (RECEIVERS >= 2), - "The transfer shape must include at least two senders and two receivers." - ); + ) -> Result<[Sender<C>; PrivateTransferShape::SENDERS], SignerError<C>> { assert!( !pre_senders.is_empty(), "The set of initial senders cannot be empty." ); let mut new_zeroes = Vec::new(); - while pre_senders.len() > SENDERS { + while pre_senders.len() > PrivateTransferShape::SENDERS { let mut joins = Vec::new(); - let mut iter = pre_senders.into_iter().chunk_by::<SENDERS>(); + let mut iter = pre_senders + .into_iter() + .chunk_by::<{ PrivateTransferShape::SENDERS }>(); for chunk in &mut iter { let senders = fallible_array_map(chunk, |s| { s.try_upgrade(&self.utxo_set) .ok_or(Error::MissingUtxoMembershipProof) })?; - let (receivers, mut join) = self.next_join::<RECEIVERS>( - asset_id, - senders.iter().map(Sender::asset_value).sum(), - )?; - posts.push(self.build_post(Transfer::new(None, [], senders, receivers, []))?); + let (receivers, mut join) = + self.next_join(asset_id, senders.iter().map(Sender::asset_value).sum())?; + posts.push(self.private_transfer_post( + &proving_context.private_transfer, + PrivateTransfer::build(senders, receivers), + )?); join.insert_utxos(&mut self.utxo_set); joins.push(join.pre_sender); new_zeroes.append(&mut join.zeroes); @@ -587,7 +637,13 @@ where joins.append(&mut iter.remainder()); pre_senders = joins; } - self.prepare_final_pre_senders::<SENDERS>(asset_id, new_zeroes, &mut pre_senders, posts)?; + self.prepare_final_pre_senders( + proving_context, + asset_id, + new_zeroes, + &mut pre_senders, + posts, + )?; Ok(into_array_unchecked( pre_senders .into_iter() @@ -607,15 +663,15 @@ where #[inline] fn sign_withdraw( &mut self, + proving_context: &MultiProvingContext<C>, asset: Asset, - receiver: impl Into<Option<ReceivingKey<C>>>, + receiver: Option<ReceivingKey<C>>, ) -> SignResult<C::HierarchicalKeyDerivationScheme, C, Self> { - const SENDERS: usize = PrivateTransferShape::SENDERS; - const RECEIVERS: usize = PrivateTransferShape::RECEIVERS; let selection = self.select(asset)?; let change = self.build_receiver(asset.id.with(selection.change))?; let mut posts = Vec::new(); - let senders = self.compute_batched_transactions::<SENDERS, RECEIVERS>( + let senders = self.compute_batched_transactions( + proving_context, asset.id, selection.pre_senders, &mut posts, @@ -623,9 +679,15 @@ where let final_post = match receiver.into() { Some(receiver) => { let receiver = self.prepare_receiver(asset, receiver); - self.build_post(PrivateTransfer::build(senders, [change, receiver]))? + self.private_transfer_post( + &proving_context.private_transfer, + PrivateTransfer::build(senders, [change, receiver]), + )? } - _ => self.build_post(Reclaim::build(senders, [change], asset))?, + _ => self.reclaim_post( + &proving_context.reclaim, + Reclaim::build(senders, [change], asset), + )?, }; posts.push(final_post); self.utxo_set.rollback(); @@ -634,20 +696,27 @@ where /// Signs the `transaction`, generating transfer posts. #[inline] - pub fn sign( + pub async fn sign( &mut self, transaction: Transaction<C>, ) -> SignResult<C::HierarchicalKeyDerivationScheme, C, Self> { - match transaction { + let reading_key = self.proving_context.aquire().await.ok().unwrap(); + let proving_context = self.proving_context.read(reading_key); + let result = match transaction { Transaction::Mint(asset) => { let receiver = self.build_receiver(asset)?; - Ok(SignResponse::new(vec![ - self.build_post(Mint::build(asset, receiver))? - ])) + Ok(SignResponse::new(vec![self.mint_post( + &proving_context.mint, + Mint::build(asset, receiver), + )?])) } - Transaction::PrivateTransfer(asset, receiver) => self.sign_withdraw(asset, receiver), - Transaction::Reclaim(asset) => self.sign_withdraw(asset, None), - } + Transaction::PrivateTransfer(asset, receiver) => { + self.sign_withdraw(proving_context, asset, Some(receiver)) + } + Transaction::Reclaim(asset) => self.sign_withdraw(proving_context, asset, None), + }; + self.proving_context.release().await; + result } /// Returns a [`ReceivingKey`] for `self` to receive assets with `index`. @@ -667,7 +736,8 @@ where { type SyncFuture = Ready<SyncResult<C::HierarchicalKeyDerivationScheme, C, Self>>; - type SignFuture = Ready<SignResult<C::HierarchicalKeyDerivationScheme, C, Self>>; + type SignFuture = + LocalBoxFuture<'static, SignResult<C::HierarchicalKeyDerivationScheme, C, Self>>; type ReceivingKeyFuture = Ready<ReceivingKeyResult<C::HierarchicalKeyDerivationScheme, C, Self>>; @@ -690,7 +760,7 @@ where #[inline] fn sign(&mut self, transaction: Transaction<C>) -> Self::SignFuture { - future::ready(self.sign(transaction)) + Box::pin(self.sign(transaction)) } #[inline] diff --git a/manta-codec/Cargo.toml b/manta-codec/Cargo.toml deleted file mode 100644 index 4fa336df4..000000000 --- a/manta-codec/Cargo.toml +++ /dev/null @@ -1,39 +0,0 @@ -[package] -name = "manta-codec" -edition = "2021" -version = "0.4.0" -authors = ["Manta Network <contact@manta.network>"] -readme = "README.md" -license-file = "LICENSE" -repository = "https://github.com/Manta-Network/manta-rs" -homepage = "https://github.com/Manta-Network" -documentation = "https://github.com/Manta-Network/manta-rs" -categories = [""] -keywords = [""] -description = "Serialization and Deserialization tools for Manta." -publish = false - -[package.metadata.docs.rs] -# To build locally: -# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --open -all-features = true -rustdoc-args = ["--cfg", "doc_cfg"] - -[badges] -is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } -is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } -maintenance = { status = "actively-developed" } - -[features] -# Scale Codec Derive Traits -derive = ["scale-codec/derive"] - -# Standard Library -std = [] - -[dependencies] -ark-serialize = { version = "0.3.0", default-features = false } -ark-std = { version = "0.3.0", default-features = false } -displaydoc = { version = "0.2.3", default-features = false } -manta-util = { path = "../manta-util" } -scale-codec = { package = "parity-scale-codec", version = "2.3.1", default-features = false } diff --git a/manta-codec/LICENSE b/manta-codec/LICENSE deleted file mode 120000 index ea5b60640..000000000 --- a/manta-codec/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE \ No newline at end of file diff --git a/manta-codec/README.md b/manta-codec/README.md deleted file mode 100644 index b96db7f8d..000000000 --- a/manta-codec/README.md +++ /dev/null @@ -1 +0,0 @@ -# manta-codec diff --git a/manta-codec/src/lib.rs b/manta-codec/src/lib.rs deleted file mode 100644 index dd48f3392..000000000 --- a/manta-codec/src/lib.rs +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Parity SCALE + Arkworks Codec System - -// FIXME: figure out how to "re-export" the `parity_scale_codec` crate so we don't have to explicitly -// depend on it downstream - -#![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(doc_cfg, feature(doc_cfg))] -#![forbid(rustdoc::broken_intra_doc_links)] -#![forbid(missing_docs)] - -use displaydoc::Display; -use manta_util::from_variant_impl; - -pub use ark_serialize::{Read, SerializationError as ArkCodecError, Write}; -pub use scale_codec::{ - Decode as ScaleDecode, Encode as ScaleEncode, Error as ScaleCodecError, Input, Output, -}; - -/// Codec Error Type -#[derive(Debug, Display)] -pub enum Error { - /// Arkworks Codec Error - #[displaydoc("Arkworks Codec Error: {0}")] - ArkCodecError(ArkCodecError), - - /// SCALE Codec Error - #[displaydoc("SCALE Codec Error: {0}")] - ScaleCodecError(ScaleCodecError), -} - -from_variant_impl!(Error, ArkCodecError, ArkCodecError); -from_variant_impl!(Error, ScaleCodecError, ScaleCodecError); - -impl From<ark_std::io::Error> for Error { - #[inline] - fn from(err: ark_std::io::Error) -> Self { - Self::ArkCodecError(ArkCodecError::IoError(err)) - } -} - -impl ark_std::error::Error for Error {} - -/// Decoding Result Alias -pub type DecodeResult<T> = Result<T, Error>; - -/// Encoding Result Alias -pub type EncodeResult = Result<(), Error>; - -/// Default Decode Implementation Marker Trait -pub trait DefaultDecode: ScaleDecode {} - -/// Decoding Trait -pub trait Decode: Sized { - /// Decodes `Self` from the reader. - fn decode<R>(reader: &mut R) -> DecodeResult<Self> - where - R: Input + Read; -} - -impl<D> Decode for D -where - D: DefaultDecode, -{ - #[inline] - fn decode<R>(reader: &mut R) -> DecodeResult<Self> - where - R: Input + Read, - { - scale_decode(reader) - } -} - -/// Default Encode Implementation Marker Trait -pub trait DefaultEncode: ScaleEncode {} - -/// Encoding Trait -pub trait Encode { - /// Encodes `self` to the writer. - fn encode<W>(&self, writer: &mut W) -> EncodeResult - where - W: Output + Write; -} - -impl<E> Encode for E -where - E: DefaultEncode, -{ - #[inline] - fn encode<W>(&self, writer: &mut W) -> EncodeResult - where - W: Output + Write, - { - scale_encode(self, writer) - } -} - -/// Codec Trait -pub trait Codec: Decode + Encode {} - -impl<S> Codec for S where S: Decode + Encode {} - -/// Decodes a value of type `T` from `reader` using the SCALE codec. -#[inline] -pub fn scale_decode<T, R>(reader: &mut R) -> DecodeResult<T> -where - T: ScaleDecode, - R: Input, -{ - Ok(T::decode(reader)?) -} - -/// Encodes a value `t` to `writer` using the SCALE codec. -#[inline] -pub fn scale_encode<T, W>(t: &T, writer: &mut W) -> EncodeResult -where - T: ScaleEncode, - W: Write, -{ - Ok(writer.write_all(t.encode().as_ref())?) -} diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index 2155cf02f..276d86f8c 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -421,6 +421,9 @@ pub trait ProofSystem { /// Constraint System type ConstraintSystem: ConstraintSystem; + /// Public Parameters Type + type PublicParameters; + /// Proving Context Type type ProvingContext; @@ -452,6 +455,7 @@ pub trait ProofSystem { /// Returns proving and verifying contexts for the constraints contained in `compiler`. fn generate_context<R>( compiler: Self::ConstraintSystem, + public_parameters: &Self::PublicParameters, rng: &mut R, ) -> Result<(Self::ProvingContext, Self::VerifyingContext), Self::Error> where @@ -475,7 +479,7 @@ pub trait ProofSystem { } /// Proof System Input -pub trait Input<T>: ProofSystem +pub trait ProofSystemInput<T>: ProofSystem where T: ?Sized, { diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 1dfca1594..ca42c6b28 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -42,8 +42,7 @@ use manta_crypto::{ accumulator, commitment::CommitmentScheme, constraint::{ - Add, Allocator, Constant, Equal, Input as ProofSystemInput, Public, Secret, ValueSource, - Variable, + Add, Allocator, Constant, Equal, ProofSystemInput, Public, Secret, ValueSource, Variable, }, ecc::DiffieHellman, encryption, diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs index 24b374766..2a330e006 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs @@ -72,6 +72,8 @@ where { type ConstraintSystem = R1CS<E::Fr>; + type PublicParameters = (); + type ProvingContext = ProvingKey<E>; type VerifyingContext = PreparedVerifyingKey<E>; @@ -97,11 +99,13 @@ where #[inline] fn generate_context<R>( cs: Self::ConstraintSystem, + public_parameters: &Self::PublicParameters, rng: &mut R, ) -> Result<(Self::ProvingContext, Self::VerifyingContext), Self::Error> where R: CryptoRng + RngCore + ?Sized, { + let _ = public_parameters; let (proving_key, verifying_key) = ArkGroth16::circuit_specific_setup( ConstraintSynthesizerWrapper(cs), &mut SizedRng(rng), diff --git a/manta-pay/src/ledger.rs b/manta-pay/src/ledger.rs index ebeed53ab..ac0476e79 100644 --- a/manta-pay/src/ledger.rs +++ b/manta-pay/src/ledger.rs @@ -23,7 +23,8 @@ use alloc::vec::Vec; use manta_accounting::{ asset::{AssetId, AssetValue}, transfer::{ - self, AccountBalance, InvalidSinkAccounts, InvalidSourceAccounts, Proof, ReceiverLedger, + canonical::{MultiVerifyingContext, TransferShape}, + AccountBalance, InvalidSinkAccounts, InvalidSourceAccounts, Proof, ReceiverLedger, ReceiverPostingKey, SenderLedger, SenderPostingKey, SinkPostingKey, SourcePostingKey, TransferLedger, TransferLedgerSuperPostingKey, TransferPostingKey, UtxoSetOutput, }, @@ -78,7 +79,7 @@ pub struct Ledger { accounts: HashMap<u128, HashMap<AssetId, AssetValue>>, /// Verifying Contexts - verifying_context: transfer::canonical::VerifyingContext<Config>, + verifying_context: MultiVerifyingContext<Config>, } impl SenderLedger<Config> for Ledger { @@ -241,13 +242,13 @@ impl TransferLedger<Config> for Ledger { sinks: &[SinkPostingKey<Config, Self>], proof: Proof<Config>, ) -> Option<(Self::ValidProof, Self::Event)> { - let verifying_context = self.verifying_context.select( + let verifying_context = self.verifying_context.select(TransferShape::select( asset_id.is_some(), sources.len(), senders.len(), receivers.len(), sinks.len(), - )?; + )?); ProofSystem::verify( &TransferPostingKey::generate_proof_input(asset_id, sources, senders, receivers, sinks), &proof, diff --git a/manta-pay/src/test/transfer.rs b/manta-pay/src/test/transfer.rs index b6f6d4707..cecc6417b 100644 --- a/manta-pay/src/test/transfer.rs +++ b/manta-pay/src/test/transfer.rs @@ -32,7 +32,7 @@ fn sample_mint_context() { let mut rng = thread_rng(); let cs = Mint::unknown_constraints(FullParameters::new(&rng.gen(), &rng.gen())); println!("Mint: {:?}", cs.measure()); - config::ProofSystem::generate_context(cs, &mut rng).unwrap(); + config::ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); } /// Tests the generation of proving/verifying contexts for [`PrivateTransfer`]. @@ -41,7 +41,7 @@ fn sample_private_transfer_context() { let mut rng = thread_rng(); let cs = PrivateTransfer::unknown_constraints(FullParameters::new(&rng.gen(), &rng.gen())); println!("PrivateTransfer: {:?}", cs.measure()); - config::ProofSystem::generate_context(cs, &mut rng).unwrap(); + config::ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); } /// Tests the generation of proving/verifying contexts for [`Reclaim`]. @@ -50,14 +50,15 @@ fn sample_reclaim_context() { let mut rng = thread_rng(); let cs = Reclaim::unknown_constraints(FullParameters::new(&rng.gen(), &rng.gen())); println!("Reclaim: {:?}", cs.measure()); - config::ProofSystem::generate_context(cs, &mut rng).unwrap(); + config::ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); } /// Tests the generation of a [`Mint`]. #[test] fn mint() { let mut rng = thread_rng(); - let result = Mint::sample_and_check_proof(&rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng); + let result = + Mint::sample_and_check_proof(&(), &rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng); println!("Mint: {:?}", result); assert!(matches!(result, Ok(true))); } @@ -66,8 +67,12 @@ fn mint() { #[test] fn private_transfer() { let mut rng = thread_rng(); - let result = - PrivateTransfer::sample_and_check_proof(&rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng); + let result = PrivateTransfer::sample_and_check_proof( + &(), + &rng.gen(), + &mut UtxoSet::new(rng.gen()), + &mut rng, + ); println!("PrivateTransfer: {:?}", result); assert!(matches!(result, Ok(true))); } @@ -77,7 +82,7 @@ fn private_transfer() { fn reclaim() { let mut rng = thread_rng(); let result = - Reclaim::sample_and_check_proof(&rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng); + Reclaim::sample_and_check_proof(&(), &rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng); println!("Reclaim: {:?}", result); assert!(matches!(result, Ok(true))); } diff --git a/manta-util/src/cache.rs b/manta-util/src/cache.rs new file mode 100644 index 000000000..f02ab3b65 --- /dev/null +++ b/manta-util/src/cache.rs @@ -0,0 +1,62 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Caching Utilities + +use crate::future::LocalBoxFuture; +use alloc::boxed::Box; +use core::convert::Infallible; + +/// Cached Resource +pub trait CachedResource<T> { + /// Reading Key Type + type ReadingKey; + + /// Aquisition Error Type + type Error; + + /// Tries to aquire the resource with `self`, returning a reading key if successful, storing + /// the aquired resource in the cache. + fn aquire(&mut self) -> LocalBoxFuture<Result<Self::ReadingKey, Self::Error>>; + + /// Reads the resource, spending the `reading_key`. The reference can be held on to until + /// [`release`](Self::release) is called or the reference falls out of scope. + fn read(&self, reading_key: Self::ReadingKey) -> &T; + + /// Releases the resource with `self`, clearing the cache. + fn release(&mut self) -> LocalBoxFuture; +} + +impl<T> CachedResource<T> for T { + type ReadingKey = (); + type Error = Infallible; + + #[inline] + fn aquire(&mut self) -> LocalBoxFuture<Result<Self::ReadingKey, Self::Error>> { + Box::pin(async { Ok(()) }) + } + + #[inline] + fn read(&self, reading_key: Self::ReadingKey) -> &T { + let _ = reading_key; + self + } + + #[inline] + fn release(&mut self) -> LocalBoxFuture { + Box::pin(async {}) + } +} diff --git a/manta-util/src/future.rs b/manta-util/src/future.rs new file mode 100644 index 000000000..dbb3d27c0 --- /dev/null +++ b/manta-util/src/future.rs @@ -0,0 +1,26 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Futures Utilities + +use alloc::boxed::Box; +use core::{future::Future, pin::Pin}; + +/// Generic Future +pub type BoxFuture<'f, T = ()> = Pin<Box<dyn Future<Output = T> + 'f + Send>>; + +/// Generic Local Future +pub type LocalBoxFuture<'f, T = ()> = Pin<Box<dyn Future<Output = T> + 'f>>; diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index 2cd0b77b0..d758064f9 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -28,6 +28,8 @@ mod concat; mod sealed; mod serde; +pub mod cache; +pub mod future; pub mod iter; pub mod num; pub mod pointer; diff --git a/src/lib.rs b/src/lib.rs index 37193b8a3..d773f2558 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,9 +27,6 @@ #[doc(inline)] pub use manta_accounting as accounting; -#[doc(inline)] -pub use manta_codec as codec; - #[doc(inline)] pub use manta_crypto as crypto; From 95390bd3de57b6d49d82a6d7bb999394697d69c0 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 10 Jan 2022 12:21:45 -0500 Subject: [PATCH 176/275] feat: simplify signer::Connection and add provingkey cache --- manta-accounting/src/wallet/signer.rs | 575 +++++++++++++++----------- manta-accounting/src/wallet/state.rs | 74 ++-- manta-pay/src/wallet.rs | 10 +- manta-util/src/cache.rs | 14 + 4 files changed, 382 insertions(+), 291 deletions(-) diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index af82670d0..ac9681c3e 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -22,6 +22,8 @@ // about what went wrong during signing. The kind of error returned from a signing could // reveal information about the internal state (privacy leak, not a secrecy leak). // TODO: Setup multi-account wallets using `crate::key::AccountTable`. +// TODO: Move `sync` to a stream-based algorithm instead of iterator-based. +// TODO: Save/Load `SignerState` to/from disk. use crate::{ asset::{Asset, AssetId, AssetMap, AssetValue}, @@ -38,11 +40,8 @@ use crate::{ Utxo, VoidNumber, }, }; -use alloc::{vec, vec::Vec}; -use core::{ - convert::Infallible, - future::{self, Future, Ready}, -}; +use alloc::{boxed::Box, vec, vec::Vec}; +use core::{convert::Infallible, fmt::Debug}; use manta_crypto::{ accumulator::{ Accumulator, ConstantCapacityAccumulator, ExactSizeAccumulator, OptimizedAccumulator, @@ -51,68 +50,61 @@ use manta_crypto::{ rand::{CryptoRng, Rand, RngCore}, }; use manta_util::{ - cache::CachedResource, fallible_array_map, future::LocalBoxFuture, into_array_unchecked, - iter::IteratorExt, Rollback, + cache::{CachedResource, CachedResourceError}, + fallible_array_map, + future::LocalBoxFuture, + into_array_unchecked, + iter::IteratorExt, + Rollback, }; /// Signer Connection -pub trait Connection<H, C> +pub trait Connection<C> where - H: HierarchicalKeyDerivationScheme<SecretKey = SecretKey<C>>, C: transfer::Configuration, { - /// Sync Future Type - /// - /// Future for the [`sync`](Self::sync) method. - type SyncFuture: Future<Output = SyncResult<H, C, Self>>; - - /// Sign Future Type - /// - /// Future for the [`sign`](Self::sign) method. - type SignFuture: Future<Output = SignResult<H, C, Self>>; - - /// Receiving Key Future Type - /// - /// Future for the [`receiving_key`](Self::receiving_key) method. - type ReceivingKeyFuture: Future<Output = ReceivingKeyResult<H, C, Self>>; + /// Key Index Type + type KeyIndex; /// Error Type type Error; /// Pushes updates from the ledger to the wallet, synchronizing it with the ledger state and /// returning an updated asset distribution. - fn sync<I, R>( - &mut self, + fn sync<'s, I, R>( + &'s mut self, insert_starting_index: usize, inserts: I, removes: R, - ) -> Self::SyncFuture + ) -> LocalBoxFuture<'s, SyncResult<C, Self>> where - I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, - R: IntoIterator<Item = VoidNumber<C>>; + I: 's + IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, + R: 's + IntoIterator<Item = VoidNumber<C>>; /// Signs a `transaction` and returns the ledger transfer posts if successful. - fn sign(&mut self, transaction: Transaction<C>) -> Self::SignFuture; + fn sign(&mut self, transaction: Transaction<C>) -> LocalBoxFuture<SignResult<C, Self>>; /// Returns a [`ReceivingKey`] for `self` to receive assets with `index`. - fn receiving_key(&mut self, index: key::Index<H>) -> Self::ReceivingKeyFuture; + fn receiving_key( + &mut self, + index: Self::KeyIndex, + ) -> LocalBoxFuture<ReceivingKeyResult<C, Self>>; } /// Synchronization Result /// /// See the [`sync`](Connection::sync) method on [`Connection`] for more. -pub type SyncResult<H, C, S> = Result<SyncResponse, Error<H, C, <S as Connection<H, C>>::Error>>; +pub type SyncResult<C, S> = Result<SyncResponse, <S as Connection<C>>::Error>; /// Signing Result /// /// See the [`sign`](Connection::sign) method on [`Connection`] for more. -pub type SignResult<H, C, S> = Result<SignResponse<C>, Error<H, C, <S as Connection<H, C>>::Error>>; +pub type SignResult<C, S> = Result<SignResponse<C>, <S as Connection<C>>::Error>; /// Receving Key Result /// /// See the [`receiving_key`](Connection::receiving_key) method on [`Connection`] for more. -pub type ReceivingKeyResult<H, C, S> = - Result<ReceivingKey<C>, Error<H, C, <S as Connection<H, C>>::Error>>; +pub type ReceivingKeyResult<C, S> = Result<ReceivingKey<C>, <S as Connection<C>>::Error>; /// Signer Synchronization Response /// @@ -154,45 +146,6 @@ where } } -/// Signer Error -pub enum Error<H, C, CE = Infallible> -where - H: HierarchicalKeyDerivationScheme<SecretKey = SecretKey<C>>, - C: transfer::Configuration, -{ - /// Hierarchical Key Derivation Scheme Error - HierarchicalKeyDerivationSchemeError(key::Error<H>), - - /// Missing [`Utxo`] Membership Proof - MissingUtxoMembershipProof, - - /// Insufficient Balance - InsufficientBalance(Asset), - - /// Proof System Error - ProofSystemError(ProofSystemError<C>), - - /// Inconsistent Synchronization State - InconsistentSynchronization, - - /// Signer Connection Error - ConnectionError(CE), -} - -impl<H, C, CE> From<key::Error<H>> for Error<H, C, CE> -where - H: HierarchicalKeyDerivationScheme<SecretKey = SecretKey<C>>, - C: transfer::Configuration, -{ - #[inline] - fn from(err: key::Error<H>) -> Self { - Self::HierarchicalKeyDerivationSchemeError(err) - } -} - -/// Signer Error over a [`Configuration`] -type SignerError<C> = Error<<C as Configuration>::HierarchicalKeyDerivationScheme, C>; - /// Signer Configuration pub trait Configuration: transfer::Configuration { /// Hierarchical Key Derivation Scheme @@ -224,6 +177,9 @@ pub type Index<C> = key::Index<<C as Configuration>::HierarchicalKeyDerivationSc type HierarchicalKeyDerivationSchemeIndex<C> = <<C as Configuration>::HierarchicalKeyDerivationScheme as HierarchicalKeyDerivationScheme>::Index; +/// Account Table Type +pub type AccountTable<C> = key::AccountTable<<C as Configuration>::HierarchicalKeyDerivationScheme>; + /// Spend Index Type pub type SpendIndex<C> = HierarchicalKeyDerivationSchemeIndex<C>; @@ -233,22 +189,87 @@ pub type ViewIndex<C> = HierarchicalKeyDerivationSchemeIndex<C>; /// Asset Map Key Type pub type AssetMapKey<C> = (SpendIndex<C>, PublicKey<C>); -/// Account Table Type -pub type AccountTable<C> = key::AccountTable<<C as Configuration>::HierarchicalKeyDerivationScheme>; +/// Proving Context Cache Error Type +pub type ProvingContextCacheError<C> = + CachedResourceError<MultiProvingContext<C>, <C as Configuration>::ProvingContextCache>; -/// Signer -pub struct Signer<C> +/// Signer Error +#[derive(derivative::Derivative)] +#[derivative(Debug(bound = r#" + key::Error<C::HierarchicalKeyDerivationScheme>: Debug, + ProvingContextCacheError<C>: Debug, + ProofSystemError<C>: Debug, + CE: Debug + "#))] +pub enum Error<C, CE = Infallible> where C: Configuration, { - /// Account Table - account_table: AccountTable<C>, + /// Hierarchical Key Derivation Scheme Error + HierarchicalKeyDerivationSchemeError(key::Error<C::HierarchicalKeyDerivationScheme>), - /// Proving Context - proving_context: C::ProvingContextCache, + /// Proving Context Cache Error + ProvingContextCacheError(ProvingContextCacheError<C>), + /// Missing [`Utxo`] Membership Proof + MissingUtxoMembershipProof, + + /// Insufficient Balance + InsufficientBalance(Asset), + + /// Proof System Error + ProofSystemError(ProofSystemError<C>), + + /// Inconsistent Synchronization State + InconsistentSynchronization, + + /// Signer Connection Error + ConnectionError(CE), +} + +impl<C, CE> From<key::Error<C::HierarchicalKeyDerivationScheme>> for Error<C, CE> +where + C: Configuration, +{ + #[inline] + fn from(err: key::Error<C::HierarchicalKeyDerivationScheme>) -> Self { + Self::HierarchicalKeyDerivationSchemeError(err) + } +} + +/// Signer Parameters +pub struct SignerParameters<C> +where + C: Configuration, +{ /// Parameters - parameters: Parameters<C>, + pub parameters: Parameters<C>, + + /// Proving Context + pub proving_context: C::ProvingContextCache, +} + +impl<C> SignerParameters<C> +where + C: Configuration, +{ + /// Returns the parameters by reading from the proving context cache. + #[inline] + pub async fn get( + &mut self, + ) -> Result<(&Parameters<C>, &MultiProvingContext<C>), ProvingContextCacheError<C>> { + let reading_key = self.proving_context.aquire().await?; + Ok((&self.parameters, self.proving_context.read(reading_key))) + } +} + +/// Signer State +struct SignerState<C> +where + C: Configuration, +{ + /// Account Table + account_table: AccountTable<C>, /// UTXO Set utxo_set: C::UtxoSet, @@ -260,56 +281,10 @@ where rng: C::Rng, } -impl<C> Signer<C> +impl<C> SignerState<C> where C: Configuration, { - /// Builds a new [`Signer`]. - #[inline] - fn new_inner( - account_table: AccountTable<C>, - proving_context: C::ProvingContextCache, - parameters: Parameters<C>, - utxo_set: C::UtxoSet, - assets: C::AssetMap, - rng: C::Rng, - ) -> Self { - Self { - account_table, - proving_context, - parameters, - utxo_set, - assets, - rng, - } - } - - /// Builds a new [`Signer`] from a fresh `account_table`. - /// - /// # Warning - /// - /// This method assumes that `account_table` has never been used before, and does not attempt - /// to perform wallet recovery on this table. - // - // FIXME: Check that this warning even makes sense. - #[inline] - pub fn new( - account_table: AccountTable<C>, - proving_context: C::ProvingContextCache, - parameters: Parameters<C>, - utxo_set: C::UtxoSet, - rng: C::Rng, - ) -> Self { - Self::new_inner( - account_table, - proving_context, - parameters, - utxo_set, - Default::default(), - rng, - ) - } - /// Returns the hierarchical key indices for the current account. #[inline] fn account(&self) -> AccountKeys<C::HierarchicalKeyDerivationScheme> { @@ -322,11 +297,12 @@ where #[inline] fn insert_next_item( &mut self, + parameters: &Parameters<C>, utxo: Utxo<C>, encrypted_note: EncryptedNote<C>, void_numbers: &mut Vec<VoidNumber<C>>, assets: &mut Vec<Asset>, - ) -> Result<(), SignerError<C>> { + ) -> Result<(), Error<C>> { let mut finder = DecryptedMessage::find(encrypted_note); if let Some(ViewKeySelection { index, @@ -338,11 +314,11 @@ where }, }) = self .account() - .find_index(|k| finder.decrypt(&self.parameters.key_agreement, k)) + .find_index(|k| finder.decrypt(&parameters.key_agreement, k)) .map_err(key::Error::KeyDerivationError)? { if let Some(void_number) = C::check_full_asset( - &self.parameters, + parameters, &keypair.spend, &ephemeral_public_key, &asset, @@ -372,62 +348,40 @@ where #[inline] fn sync_with<I>( &mut self, + parameters: &Parameters<C>, inserts: I, mut void_numbers: Vec<VoidNumber<C>>, - ) -> SyncResult<C::HierarchicalKeyDerivationScheme, C, Self> + ) -> SyncResult<C, Signer<C>> where I: Iterator<Item = (Utxo<C>, EncryptedNote<C>)>, { // TODO: Do this loop in parallel. let mut assets = Vec::new(); for (utxo, encrypted_note) in inserts { - self.insert_next_item(utxo, encrypted_note, &mut void_numbers, &mut assets)?; + self.insert_next_item( + parameters, + utxo, + encrypted_note, + &mut void_numbers, + &mut assets, + )?; } // FIXME: Do we need to check the void numbers which survived the above loop? self.utxo_set.commit(); Ok(SyncResponse::new(assets)) } - /// Updates the internal ledger state, returning the new asset distribution. - #[inline] - pub fn sync<I, R>( - &mut self, - insert_starting_index: usize, - inserts: I, - removes: R, - ) -> SyncResult<C::HierarchicalKeyDerivationScheme, C, Self> - where - I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, - R: IntoIterator<Item = VoidNumber<C>>, - { - // FIXME: Do a capacity check on the current UTXO set? - // - // if self.utxo_set.capacity() < starting_index { - // panic!("something is very wrong here") - // } - // - // TODO: Use a smarter object than `Vec` for `removes.into_iter().collect()` like a - // `HashSet` or some other set-like container with fast membership and remove ops. - // - match self.utxo_set.len().checked_sub(insert_starting_index) { - Some(diff) => self.sync_with( - inserts.into_iter().skip(diff), - removes.into_iter().collect(), - ), - _ => Err(Error::InconsistentSynchronization), - } - } - /// Builds the pre-sender associated to `spend_index`, `ephemeral_key`, and `asset`. #[inline] fn build_pre_sender( &self, + parameters: &Parameters<C>, key: AssetMapKey<C>, asset: Asset, - ) -> Result<PreSender<C>, SignerError<C>> { + ) -> Result<PreSender<C>, Error<C>> { let (spend_index, ephemeral_key) = key; Ok(PreSender::new( - &self.parameters, + parameters, self.account().spend_key(spend_index)?, ephemeral_key, asset, @@ -436,29 +390,36 @@ where /// Builds the pre-receiver associated to `spend_index`, `view_index`, and `asset`. #[inline] - fn build_receiver(&mut self, asset: Asset) -> Result<Receiver<C>, SignerError<C>> { + fn build_receiver( + &mut self, + parameters: &Parameters<C>, + asset: Asset, + ) -> Result<Receiver<C>, Error<C>> { let keypair = self .account() .default_keypair() .map_err(key::Error::KeyDerivationError)?; Ok(SpendingKey::new(keypair.spend, keypair.view).receiver( - &self.parameters, + parameters, self.rng.gen(), asset, )) } - /// Mints an asset with zero value for the given `asset_id`, returning the appropriate - /// transfer and pre-sender. + /// Builds a new internal [`Mint`] for zero assets. #[inline] - fn mint_zero(&mut self, asset_id: AssetId) -> Result<(Mint<C>, PreSender<C>), SignerError<C>> { + fn mint_zero( + &mut self, + parameters: &Parameters<C>, + asset_id: AssetId, + ) -> Result<(Mint<C>, PreSender<C>), Error<C>> { let asset = Asset::zero(asset_id); let keypair = self .account() .default_keypair() .map_err(key::Error::KeyDerivationError)?; let (receiver, pre_sender) = SpendingKey::new(keypair.spend, keypair.view).internal_pair( - &self.parameters, + parameters, self.rng.gen(), asset, ); @@ -467,13 +428,17 @@ where /// Selects the pre-senders which collectively own at least `asset`, returning any change. #[inline] - fn select(&mut self, asset: Asset) -> Result<Selection<C>, SignerError<C>> { + fn select( + &mut self, + parameters: &Parameters<C>, + asset: Asset, + ) -> Result<Selection<C>, Error<C>> { let selection = self.assets.select(asset); if selection.is_empty() { return Err(Error::InsufficientBalance(asset)); } Selection::new(selection, move |k, v| { - self.build_pre_sender(k, asset.id.with(v)) + self.build_pre_sender(parameters, k, asset.id.with(v)) }) } @@ -489,21 +454,23 @@ where proving_context: &ProvingContext<C>, transfer: Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>, rng: &mut C::Rng, - ) -> Result<TransferPost<C>, SignerError<C>> { + ) -> Result<TransferPost<C>, Error<C>> { transfer .into_post(parameters, proving_context, rng) .map_err(Error::ProofSystemError) } + /// Mints an asset with zero value for the given `asset_id`, returning the appropriate /// Builds a [`TransferPost`] for `mint`. #[inline] fn mint_post( &mut self, + parameters: &Parameters<C>, proving_context: &ProvingContext<C>, mint: Mint<C>, - ) -> Result<TransferPost<C>, SignerError<C>> { + ) -> Result<TransferPost<C>, Error<C>> { Self::build_post( - FullParameters::new(&self.parameters, self.utxo_set.model()), + FullParameters::new(parameters, self.utxo_set.model()), proving_context, mint, &mut self.rng, @@ -514,11 +481,12 @@ where #[inline] fn private_transfer_post( &mut self, + parameters: &Parameters<C>, proving_context: &ProvingContext<C>, private_transfer: PrivateTransfer<C>, - ) -> Result<TransferPost<C>, SignerError<C>> { + ) -> Result<TransferPost<C>, Error<C>> { Self::build_post( - FullParameters::new(&self.parameters, self.utxo_set.model()), + FullParameters::new(parameters, self.utxo_set.model()), proving_context, private_transfer, &mut self.rng, @@ -529,11 +497,12 @@ where #[inline] fn reclaim_post( &mut self, + parameters: &Parameters<C>, proving_context: &ProvingContext<C>, reclaim: Reclaim<C>, - ) -> Result<TransferPost<C>, SignerError<C>> { + ) -> Result<TransferPost<C>, Error<C>> { Self::build_post( - FullParameters::new(&self.parameters, self.utxo_set.model()), + FullParameters::new(parameters, self.utxo_set.model()), proving_context, reclaim, &mut self.rng, @@ -545,15 +514,16 @@ where #[inline] fn next_join( &mut self, + parameters: &Parameters<C>, asset_id: AssetId, total: AssetValue, - ) -> Result<([Receiver<C>; PrivateTransferShape::RECEIVERS], Join<C>), SignerError<C>> { + ) -> Result<([Receiver<C>; PrivateTransferShape::RECEIVERS], Join<C>), Error<C>> { let keypair = self .account() .default_keypair() .map_err(key::Error::KeyDerivationError)?; Ok(Join::new( - &self.parameters, + parameters, asset_id.with(total), &SpendingKey::new(keypair.spend, keypair.view), &mut self.rng, @@ -564,12 +534,13 @@ where #[inline] fn prepare_final_pre_senders( &mut self, + parameters: &Parameters<C>, proving_context: &MultiProvingContext<C>, asset_id: AssetId, mut new_zeroes: Vec<PreSender<C>>, pre_senders: &mut Vec<PreSender<C>>, posts: &mut Vec<TransferPost<C>>, - ) -> Result<(), SignerError<C>> { + ) -> Result<(), Error<C>> { let mut needed_zeroes = PrivateTransferShape::SENDERS - pre_senders.len(); if needed_zeroes == 0 { return Ok(()); @@ -577,7 +548,7 @@ where let zeroes = self.assets.zeroes(needed_zeroes, asset_id); needed_zeroes -= zeroes.len(); for zero in zeroes { - pre_senders.push(self.build_pre_sender(zero, Asset::zero(asset_id))?); + pre_senders.push(self.build_pre_sender(parameters, zero, Asset::zero(asset_id))?); } if needed_zeroes == 0 { return Ok(()); @@ -593,8 +564,8 @@ where return Ok(()); } for _ in 0..needed_mints { - let (mint, pre_sender) = self.mint_zero(asset_id)?; - posts.push(self.mint_post(&proving_context.mint, mint)?); + let (mint, pre_sender) = self.mint_zero(parameters, asset_id)?; + posts.push(self.mint_post(parameters, &proving_context.mint, mint)?); pre_senders.push(pre_sender); } Ok(()) @@ -604,11 +575,12 @@ where #[inline] fn compute_batched_transactions( &mut self, + parameters: &Parameters<C>, proving_context: &MultiProvingContext<C>, asset_id: AssetId, mut pre_senders: Vec<PreSender<C>>, posts: &mut Vec<TransferPost<C>>, - ) -> Result<[Sender<C>; PrivateTransferShape::SENDERS], SignerError<C>> { + ) -> Result<[Sender<C>; PrivateTransferShape::SENDERS], Error<C>> { assert!( !pre_senders.is_empty(), "The set of initial senders cannot be empty." @@ -624,9 +596,13 @@ where s.try_upgrade(&self.utxo_set) .ok_or(Error::MissingUtxoMembershipProof) })?; - let (receivers, mut join) = - self.next_join(asset_id, senders.iter().map(Sender::asset_value).sum())?; + let (receivers, mut join) = self.next_join( + parameters, + asset_id, + senders.iter().map(Sender::asset_value).sum(), + )?; posts.push(self.private_transfer_post( + parameters, &proving_context.private_transfer, PrivateTransfer::build(senders, receivers), )?); @@ -638,6 +614,7 @@ where pre_senders = joins; } self.prepare_final_pre_senders( + parameters, proving_context, asset_id, new_zeroes, @@ -655,116 +632,230 @@ where /// Prepares a given [`ReceivingKey`] for receiving `asset`. #[inline] - pub fn prepare_receiver(&mut self, asset: Asset, receiver: ReceivingKey<C>) -> Receiver<C> { - receiver.into_receiver(&self.parameters, self.rng.gen(), asset) + fn prepare_receiver( + &mut self, + parameters: &Parameters<C>, + asset: Asset, + receiver: ReceivingKey<C>, + ) -> Receiver<C> { + receiver.into_receiver(parameters, self.rng.gen(), asset) + } +} + +/// Signer +pub struct Signer<C> +where + C: Configuration, +{ + /// Signer Parameters + parameters: SignerParameters<C>, + + /// Signer State + state: SignerState<C>, +} + +impl<C> Signer<C> +where + C: Configuration, +{ + /// Builds a new [`Signer`]. + #[inline] + fn new_inner( + account_table: AccountTable<C>, + proving_context: C::ProvingContextCache, + parameters: Parameters<C>, + utxo_set: C::UtxoSet, + assets: C::AssetMap, + rng: C::Rng, + ) -> Self { + Self { + parameters: SignerParameters { + parameters, + proving_context, + }, + state: SignerState { + account_table, + utxo_set, + assets, + rng, + }, + } + } + + /// Builds a new [`Signer`] from a fresh `account_table`. + /// + /// # Warning + /// + /// This method assumes that `account_table` has never been used before, and does not attempt + /// to perform wallet recovery on this table. + // + // FIXME: Check that this warning even makes sense. + #[inline] + pub fn new( + account_table: AccountTable<C>, + proving_context: C::ProvingContextCache, + parameters: Parameters<C>, + utxo_set: C::UtxoSet, + rng: C::Rng, + ) -> Self { + Self::new_inner( + account_table, + proving_context, + parameters, + utxo_set, + Default::default(), + rng, + ) + } + + /// Updates the internal ledger state, returning the new asset distribution. + #[inline] + pub fn sync<I, R>( + &mut self, + insert_starting_index: usize, + inserts: I, + removes: R, + ) -> SyncResult<C, Self> + where + I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, + R: IntoIterator<Item = VoidNumber<C>>, + { + // FIXME: Do a capacity check on the current UTXO set? + // + // if self.utxo_set.capacity() < starting_index { + // panic!("something is very wrong here") + // } + // + // TODO: Use a smarter object than `Vec` for `removes.into_iter().collect()` like a + // `HashSet` or some other set-like container with fast membership and remove ops. + // + match self.state.utxo_set.len().checked_sub(insert_starting_index) { + Some(diff) => self.state.sync_with( + &self.parameters.parameters, + inserts.into_iter().skip(diff), + removes.into_iter().collect(), + ), + _ => Err(Error::InconsistentSynchronization), + } } /// Signs a withdraw transaction for `asset` sent to `receiver`. #[inline] - fn sign_withdraw( + async fn sign_withdraw( &mut self, - proving_context: &MultiProvingContext<C>, asset: Asset, receiver: Option<ReceivingKey<C>>, - ) -> SignResult<C::HierarchicalKeyDerivationScheme, C, Self> { - let selection = self.select(asset)?; - let change = self.build_receiver(asset.id.with(selection.change))?; + ) -> SignResult<C, Self> { + let selection = self.state.select(&self.parameters.parameters, asset)?; + let change = self + .state + .build_receiver(&self.parameters.parameters, asset.id.with(selection.change))?; + let (parameters, proving_context) = self + .parameters + .get() + .await + .map_err(Error::ProvingContextCacheError)?; let mut posts = Vec::new(); - let senders = self.compute_batched_transactions( + let senders = self.state.compute_batched_transactions( + parameters, proving_context, asset.id, selection.pre_senders, &mut posts, )?; - let final_post = match receiver.into() { + let final_post = match receiver { Some(receiver) => { - let receiver = self.prepare_receiver(asset, receiver); - self.private_transfer_post( + let receiver = self.state.prepare_receiver(parameters, asset, receiver); + self.state.private_transfer_post( + parameters, &proving_context.private_transfer, PrivateTransfer::build(senders, [change, receiver]), )? } - _ => self.reclaim_post( + _ => self.state.reclaim_post( + parameters, &proving_context.reclaim, Reclaim::build(senders, [change], asset), )?, }; posts.push(final_post); - self.utxo_set.rollback(); + self.state.utxo_set.rollback(); Ok(SignResponse::new(posts)) } - /// Signs the `transaction`, generating transfer posts. + /// Signs the `transaction`, generating transfer posts without releasing resources. #[inline] - pub async fn sign( - &mut self, - transaction: Transaction<C>, - ) -> SignResult<C::HierarchicalKeyDerivationScheme, C, Self> { - let reading_key = self.proving_context.aquire().await.ok().unwrap(); - let proving_context = self.proving_context.read(reading_key); - let result = match transaction { + async fn sign_internal(&mut self, transaction: Transaction<C>) -> SignResult<C, Self> { + match transaction { Transaction::Mint(asset) => { - let receiver = self.build_receiver(asset)?; - Ok(SignResponse::new(vec![self.mint_post( + let receiver = self + .state + .build_receiver(&self.parameters.parameters, asset)?; + let (parameters, proving_context) = self + .parameters + .get() + .await + .map_err(Error::ProvingContextCacheError)?; + Ok(SignResponse::new(vec![self.state.mint_post( + parameters, &proving_context.mint, Mint::build(asset, receiver), )?])) } Transaction::PrivateTransfer(asset, receiver) => { - self.sign_withdraw(proving_context, asset, Some(receiver)) + self.sign_withdraw(asset, Some(receiver)).await } - Transaction::Reclaim(asset) => self.sign_withdraw(proving_context, asset, None), - }; - self.proving_context.release().await; + Transaction::Reclaim(asset) => self.sign_withdraw(asset, None).await, + } + } + + /// Signs the `transaction`, generating transfer posts. + #[inline] + pub async fn sign(&mut self, transaction: Transaction<C>) -> SignResult<C, Self> { + // TODO: Should we do a time-based release mechanism to amortize the cost of reading/writing + // to disk? + let result = self.sign_internal(transaction).await; + self.parameters.proving_context.release().await; result } /// Returns a [`ReceivingKey`] for `self` to receive assets with `index`. #[inline] - pub fn receiving_key( - &mut self, - index: Index<C>, - ) -> ReceivingKeyResult<C::HierarchicalKeyDerivationScheme, C, Self> { - let keypair = self.account().keypair(index)?; - Ok(SpendingKey::new(keypair.spend, keypair.view).derive(&self.parameters.key_agreement)) + pub fn receiving_key(&self, index: Index<C>) -> ReceivingKeyResult<C, Self> { + let keypair = self.state.account().keypair(index)?; + Ok(SpendingKey::new(keypair.spend, keypair.view) + .derive(&self.parameters.parameters.key_agreement)) } } -impl<C> Connection<C::HierarchicalKeyDerivationScheme, C> for Signer<C> +impl<C> Connection<C> for Signer<C> where C: Configuration, { - type SyncFuture = Ready<SyncResult<C::HierarchicalKeyDerivationScheme, C, Self>>; - - type SignFuture = - LocalBoxFuture<'static, SignResult<C::HierarchicalKeyDerivationScheme, C, Self>>; - - type ReceivingKeyFuture = - Ready<ReceivingKeyResult<C::HierarchicalKeyDerivationScheme, C, Self>>; - - type Error = Infallible; + type KeyIndex = Index<C>; + type Error = Error<C>; #[inline] - fn sync<I, R>( - &mut self, + fn sync<'s, I, R>( + &'s mut self, insert_starting_index: usize, inserts: I, removes: R, - ) -> Self::SyncFuture + ) -> LocalBoxFuture<'s, SyncResult<C, Self>> where - I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, - R: IntoIterator<Item = VoidNumber<C>>, + I: 's + IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, + R: 's + IntoIterator<Item = VoidNumber<C>>, { - future::ready(self.sync(insert_starting_index, inserts, removes)) + Box::pin(async move { self.sync(insert_starting_index, inserts, removes) }) } #[inline] - fn sign(&mut self, transaction: Transaction<C>) -> Self::SignFuture { + fn sign(&mut self, transaction: Transaction<C>) -> LocalBoxFuture<SignResult<C, Self>> { Box::pin(self.sign(transaction)) } #[inline] - fn receiving_key(&mut self, index: Index<C>) -> Self::ReceivingKeyFuture { - future::ready(self.receiving_key(index)) + fn receiving_key(&mut self, index: Index<C>) -> LocalBoxFuture<ReceivingKeyResult<C, Self>> { + Box::pin(async move { (&*self).receiving_key(index) }) } } diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index cb13d27f3..5dee9af52 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -18,10 +18,9 @@ use crate::{ asset::{Asset, AssetId, AssetValue}, - key::{HierarchicalKeyDerivationScheme, Index}, transfer::{ canonical::{Transaction, TransactionKind}, - Configuration, ReceivingKey, SecretKey, + Configuration, ReceivingKey, }, wallet::{ ledger::{self, Checkpoint, PullResponse, PushResponse}, @@ -32,7 +31,7 @@ use alloc::{ collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}, vec::Vec, }; -use core::marker::PhantomData; +use core::{fmt::Debug, marker::PhantomData}; #[cfg(feature = "std")] use std::{ @@ -159,22 +158,12 @@ where impl_balance_state_map_body! { HashMapEntry } } -/// Native Wallet -pub type NativeWallet<C, L, B = BTreeMapBalanceState> = Wallet< - <C as signer::Configuration>::HierarchicalKeyDerivationScheme, - C, - L, - signer::Signer<C>, - B, ->; - /// Wallet -pub struct Wallet<H, C, L, S, B = BTreeMapBalanceState> +pub struct Wallet<C, L, S = signer::Signer<C>, B = BTreeMapBalanceState> where - H: HierarchicalKeyDerivationScheme<SecretKey = SecretKey<C>>, C: Configuration, L: ledger::Connection<C>, - S: signer::Connection<H, C>, + S: signer::Connection<C>, B: BalanceState, { /// Ledger Connection @@ -190,24 +179,23 @@ where assets: B, /// Type Parameter Marker - __: PhantomData<(H, C)>, + __: PhantomData<C>, } -impl<H, C, L, S, B> Wallet<H, C, L, S, B> +impl<C, L, S, B> Wallet<C, L, S, B> where - H: HierarchicalKeyDerivationScheme<SecretKey = SecretKey<C>>, C: Configuration, L: ledger::Connection<C>, - S: signer::Connection<H, C>, + S: signer::Connection<C>, B: BalanceState, { /// Builds a new [`Wallet`]. #[inline] - pub fn new(signer: S, ledger: L, checkpoint: L::Checkpoint, assets: B) -> Self { + pub fn new(ledger: L, checkpoint: L::Checkpoint, signer: S, assets: B) -> Self { Self { - signer, ledger, checkpoint, + signer, assets, __: PhantomData, } @@ -215,8 +203,8 @@ where /// Starts a new [`Wallet`] from `signer` and `ledger` connections. #[inline] - pub fn empty(signer: S, ledger: L) -> Self { - Self::new(signer, ledger, Default::default(), Default::default()) + pub fn empty(ledger: L, signer: S) -> Self { + Self::new(ledger, Default::default(), signer, Default::default()) } /// Returns the current balance associated with this `id`. @@ -240,7 +228,7 @@ where /// Pulls data from the `ledger`, synchronizing the wallet and balance state. #[inline] - pub async fn sync(&mut self) -> Result<(), Error<H, C, L, S>> { + pub async fn sync(&mut self) -> Result<(), Error<C, L, S>> { // TODO: How to recover from an `InconsistentSynchronization` error? Need some sort of // recovery mode, like starting from the beginning of the state? let PullResponse { @@ -255,7 +243,8 @@ where self.assets.deposit_all( self.signer .sync(checkpoint.receiver_index(), receivers, senders) - .await? + .await + .map_err(Error::SignerError)? .assets, ); self.checkpoint = checkpoint; @@ -292,12 +281,16 @@ where /// This method returns an error in any other case. The internal state of the wallet is kept /// consistent between calls and recoverable errors are returned for the caller to handle. #[inline] - pub async fn post(&mut self, transaction: Transaction<C>) -> Result<bool, Error<H, C, L, S>> { + pub async fn post(&mut self, transaction: Transaction<C>) -> Result<bool, Error<C, L, S>> { self.sync().await?; let transaction_kind = self .check(&transaction) .map_err(Error::InsufficientBalance)?; - let SignResponse { posts } = self.signer.sign(transaction).await?; + let SignResponse { posts } = self + .signer + .sign(transaction) + .await + .map_err(Error::SignerError)?; match self.ledger.push(posts).await { Ok(PushResponse { success: true }) => { match transaction_kind { @@ -313,10 +306,7 @@ where /// Returns a [`ReceivingKey`] for `self` to receive assets with `index`. #[inline] - pub async fn receiving_key( - &mut self, - index: Index<H>, - ) -> Result<ReceivingKey<C>, signer::Error<H, C, S::Error>> { + pub async fn receiving_key(&mut self, index: S::KeyIndex) -> Result<ReceivingKey<C>, S::Error> { self.signer.receiving_key(index).await } } @@ -325,12 +315,13 @@ where /// /// This `enum` is the error state for [`Wallet`] methods. See [`sync`](Wallet::sync) and /// [`post`](Wallet::post) for more. -pub enum Error<H, C, L, S> +#[derive(derivative::Derivative)] +#[derivative(Debug(bound = "L::Error: Debug, S::Error: Debug"))] +pub enum Error<C, L, S> where - H: HierarchicalKeyDerivationScheme<SecretKey = SecretKey<C>>, C: Configuration, L: ledger::Connection<C>, - S: signer::Connection<H, C>, + S: signer::Connection<C>, { /// Insufficient Balance InsufficientBalance(Asset), @@ -339,18 +330,5 @@ where LedgerError(L::Error), /// Signer Error - SignerError(signer::Error<H, C, S::Error>), -} - -impl<H, C, L, S> From<signer::Error<H, C, S::Error>> for Error<H, C, L, S> -where - H: HierarchicalKeyDerivationScheme<SecretKey = SecretKey<C>>, - C: Configuration, - L: ledger::Connection<C>, - S: signer::Connection<H, C>, -{ - #[inline] - fn from(err: signer::Error<H, C, S::Error>) -> Self { - Self::SignerError(err) - } + SignerError(S::Error), } diff --git a/manta-pay/src/wallet.rs b/manta-pay/src/wallet.rs index 466d6eefd..f14530549 100644 --- a/manta-pay/src/wallet.rs +++ b/manta-pay/src/wallet.rs @@ -23,7 +23,11 @@ use crate::{ use manta_accounting::{ asset::HashAssetMap, key, - wallet::signer::{self, AssetMapKey}, + transfer::canonical::MultiProvingContext, + wallet::{ + self, + signer::{self, AssetMapKey}, + }, }; use manta_crypto::merkle_tree; @@ -42,8 +46,12 @@ impl signer::Configuration for Config { key::Map<TestnetKeySecret, HierarchicalKeyDerivationFunction>; type UtxoSet = UtxoSet; type AssetMap = HashAssetMap<AssetMapKey<Self>>; + type ProvingContextCache = MultiProvingContext<Self>; type Rng = rand_chacha::ChaCha20Rng; } /// Signer pub type Signer = signer::Signer<Config>; + +/// Wallet +pub type Wallet<L> = wallet::Wallet<Config, L, Signer>; diff --git a/manta-util/src/cache.rs b/manta-util/src/cache.rs index f02ab3b65..7f673e8c6 100644 --- a/manta-util/src/cache.rs +++ b/manta-util/src/cache.rs @@ -30,6 +30,11 @@ pub trait CachedResource<T> { /// Tries to aquire the resource with `self`, returning a reading key if successful, storing /// the aquired resource in the cache. + /// + /// # Contract + /// + /// This method should be idempotent unless calls to [`aquire`](Self::aquire) are interleaved + /// with calls to [`release`](Self::release). fn aquire(&mut self) -> LocalBoxFuture<Result<Self::ReadingKey, Self::Error>>; /// Reads the resource, spending the `reading_key`. The reference can be held on to until @@ -37,9 +42,18 @@ pub trait CachedResource<T> { fn read(&self, reading_key: Self::ReadingKey) -> &T; /// Releases the resource with `self`, clearing the cache. + /// + /// # Contract + /// + /// This method should be idempotent unless calls to [`release`](Self::release) are interleaved + /// with calls to [`aquire`](Self::aquire). This method can be a no-op if the resource was not + /// aquired. fn release(&mut self) -> LocalBoxFuture; } +/// Cached Resource Error Type +pub type CachedResourceError<T, R> = <R as CachedResource<T>>::Error; + impl<T> CachedResource<T> for T { type ReadingKey = (); type Error = Infallible; From 11fe290a87c22197521045fb834c7cd90712af57 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 11 Jan 2022 01:43:24 -0500 Subject: [PATCH 177/275] fix: update ledger/signer primitives, add serde impls --- manta-accounting/src/asset.rs | 1 + manta-accounting/src/transfer/batch.rs | 3 +- manta-accounting/src/wallet/ledger.rs | 30 ++---- manta-accounting/src/wallet/signer.rs | 28 ++---- manta-accounting/src/wallet/state.rs | 12 ++- manta-crypto/Cargo.toml | 1 + manta-crypto/src/merkle_tree/node.rs | 3 + manta-crypto/src/merkle_tree/path.rs | 47 +++++++++ manta-crypto/src/merkle_tree/single_path.rs | 59 +++++++++-- manta-pay/Cargo.toml | 1 + manta-pay/src/ledger.rs | 106 +++++++++++++++++++- 11 files changed, 232 insertions(+), 59 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index b81773ac5..9edbbe147 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -23,6 +23,7 @@ use alloc::{ collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}, + vec, vec::Vec, }; use core::{ diff --git a/manta-accounting/src/transfer/batch.rs b/manta-accounting/src/transfer/batch.rs index 26c8b53b9..cae7e5fbe 100644 --- a/manta-accounting/src/transfer/batch.rs +++ b/manta-accounting/src/transfer/batch.rs @@ -56,12 +56,11 @@ where { // TODO: Add optimization path for receiver re-sampling so that we ensure that all UTXOs // are maximally independent. - // let mut receivers = Vec::with_capacity(RECEIVERS); let mut zeroes = Vec::with_capacity(RECEIVERS - 1); let (receiver, pre_sender) = spending_key.internal_pair(parameters, rng.gen(), asset); receivers.push(receiver); - for _ in 0..RECEIVERS - 2 { + for _ in 1..RECEIVERS { let (receiver, pre_sender) = spending_key.internal_zero_pair(parameters, rng.gen(), asset.id); receivers.push(receiver); diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs index 505d6d27d..4b9fa83ec 100644 --- a/manta-accounting/src/wallet/ledger.rs +++ b/manta-accounting/src/wallet/ledger.rs @@ -18,16 +18,7 @@ use crate::transfer::{Configuration, EncryptedNote, TransferPost, Utxo, VoidNumber}; use alloc::vec::Vec; -use core::future::Future; - -/// Ledger Checkpoint -pub trait Checkpoint: Default + PartialOrd { - /// Returns the number of receivers that have participated in transactions on the ledger so far. - fn receiver_index(&self) -> usize; - - /// Returns the number of senders that have participated in transactions on the ledger so far. - fn sender_index(&self) -> usize; -} +use manta_util::future::LocalBoxFuture; /// Ledger Source Connection pub trait Connection<C> @@ -35,7 +26,7 @@ where C: Configuration, { /// Ledger State Checkpoint Type - type Checkpoint: Checkpoint; + type Checkpoint: Default + PartialOrd; /// Receiver Chunk Iterator Type type ReceiverChunk: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>; @@ -43,26 +34,19 @@ where /// Sender Chunk Iterator Type type SenderChunk: IntoIterator<Item = VoidNumber<C>>; - /// Pull Future Type - /// - /// Future for the [`pull`](Self::pull) method. - type PullFuture: Future<Output = PullResult<C, Self>>; - - /// Push Future Type - /// - /// Future for the [`push`](Self::push) method. - type PushFuture: Future<Output = PushResult<C, Self>>; - /// Error Type type Error; /// Pulls receiver data from the ledger starting from `checkpoint`, returning the current /// [`Checkpoint`](Self::Checkpoint). - fn pull(&self, checkpoint: &Self::Checkpoint) -> Self::PullFuture; + fn pull<'s>( + &'s mut self, + checkpoint: &'s Self::Checkpoint, + ) -> LocalBoxFuture<'s, PullResult<C, Self>>; /// Sends `posts` to the ledger, returning `true` or `false` depending on whether the entire /// batch succeeded or not. - fn push(&self, posts: Vec<TransferPost<C>>) -> Self::PushFuture; + fn push(&mut self, posts: Vec<TransferPost<C>>) -> LocalBoxFuture<PushResult<C, Self>>; } /// Ledger Source Pull Result diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index ac9681c3e..61bfe69d7 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -24,6 +24,7 @@ // TODO: Setup multi-account wallets using `crate::key::AccountTable`. // TODO: Move `sync` to a stream-based algorithm instead of iterator-based. // TODO: Save/Load `SignerState` to/from disk. +// TODO: Add self-destruct feature for clearing all secret and private data. use crate::{ asset::{Asset, AssetId, AssetMap, AssetValue}, @@ -73,7 +74,6 @@ where /// returning an updated asset distribution. fn sync<'s, I, R>( &'s mut self, - insert_starting_index: usize, inserts: I, removes: R, ) -> LocalBoxFuture<'s, SyncResult<C, Self>> @@ -220,9 +220,6 @@ where /// Proof System Error ProofSystemError(ProofSystemError<C>), - /// Inconsistent Synchronization State - InconsistentSynchronization, - /// Signer Connection Error ConnectionError(CE), } @@ -710,12 +707,7 @@ where /// Updates the internal ledger state, returning the new asset distribution. #[inline] - pub fn sync<I, R>( - &mut self, - insert_starting_index: usize, - inserts: I, - removes: R, - ) -> SyncResult<C, Self> + pub fn sync<I, R>(&mut self, inserts: I, removes: R) -> SyncResult<C, Self> where I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, R: IntoIterator<Item = VoidNumber<C>>, @@ -729,14 +721,11 @@ where // TODO: Use a smarter object than `Vec` for `removes.into_iter().collect()` like a // `HashSet` or some other set-like container with fast membership and remove ops. // - match self.state.utxo_set.len().checked_sub(insert_starting_index) { - Some(diff) => self.state.sync_with( - &self.parameters.parameters, - inserts.into_iter().skip(diff), - removes.into_iter().collect(), - ), - _ => Err(Error::InconsistentSynchronization), - } + self.state.sync_with( + &self.parameters.parameters, + inserts.into_iter(), + removes.into_iter().collect(), + ) } /// Signs a withdraw transaction for `asset` sent to `receiver`. @@ -838,7 +827,6 @@ where #[inline] fn sync<'s, I, R>( &'s mut self, - insert_starting_index: usize, inserts: I, removes: R, ) -> LocalBoxFuture<'s, SyncResult<C, Self>> @@ -846,7 +834,7 @@ where I: 's + IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, R: 's + IntoIterator<Item = VoidNumber<C>>, { - Box::pin(async move { self.sync(insert_starting_index, inserts, removes) }) + Box::pin(async move { self.sync(inserts, removes) }) } #[inline] diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 5dee9af52..6cd72fcb7 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -23,7 +23,7 @@ use crate::{ Configuration, ReceivingKey, }, wallet::{ - ledger::{self, Checkpoint, PullResponse, PushResponse}, + ledger::{self, PullResponse, PushResponse}, signer::{self, SignResponse}, }, }; @@ -229,8 +229,6 @@ where /// Pulls data from the `ledger`, synchronizing the wallet and balance state. #[inline] pub async fn sync(&mut self) -> Result<(), Error<C, L, S>> { - // TODO: How to recover from an `InconsistentSynchronization` error? Need some sort of - // recovery mode, like starting from the beginning of the state? let PullResponse { checkpoint, receivers, @@ -240,9 +238,12 @@ where .pull(&self.checkpoint) .await .map_err(Error::LedgerError)?; + if checkpoint < self.checkpoint { + return Err(Error::InconsistentCheckpoint); + } self.assets.deposit_all( self.signer - .sync(checkpoint.receiver_index(), receivers, senders) + .sync(receivers, senders) .await .map_err(Error::SignerError)? .assets, @@ -326,6 +327,9 @@ where /// Insufficient Balance InsufficientBalance(Asset), + /// Inconsistent Checkpoint Error + InconsistentCheckpoint, + /// Ledger Error LedgerError(L::Error), diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index 03da39470..31fc01f3b 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -38,6 +38,7 @@ test = [] derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } manta-util = { path = "../manta-util" } rand_core = { version = "0.6.3", default-features = false } +serde = { version = "1.0.133", optional = true, default-features = false, features = ["derive"] } [dev-dependencies] rand = "0.8.4" diff --git a/manta-crypto/src/merkle_tree/node.rs b/manta-crypto/src/merkle_tree/node.rs index 4cf6a5fa1..c8ac18deb 100644 --- a/manta-crypto/src/merkle_tree/node.rs +++ b/manta-crypto/src/merkle_tree/node.rs @@ -23,6 +23,7 @@ use core::{ }; /// Parity of a Subtree +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub enum Parity { /// Left Side of the Subtree @@ -146,6 +147,7 @@ impl Default for Parity { } /// Node Index +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct Node<Idx = usize>( /// Level-wise Index to a node in a Binary Tree @@ -365,6 +367,7 @@ where /// /// This `struct` is created by the [`parents`](Node::parents) method on [`Node`]. /// See its documentation for more. +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct NodeParents { /// Current Index diff --git a/manta-crypto/src/merkle_tree/path.rs b/manta-crypto/src/merkle_tree/path.rs index f2b916b9c..4890f83c3 100644 --- a/manta-crypto/src/merkle_tree/path.rs +++ b/manta-crypto/src/merkle_tree/path.rs @@ -34,12 +34,26 @@ use core::{ slice::SliceIndex, }; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + pub(super) mod prelude { #[doc(inline)] pub use super::{CurrentPath, Path}; } /// Merkle Tree Inner Path +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "Vec<InnerDigest<C>>: Deserialize<'de>", + serialize = "Vec<InnerDigest<C>>: Serialize", + ), + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "InnerDigest<C>: Clone"), @@ -224,6 +238,17 @@ where } /// Merkle Tree Current Inner Path +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "Vec<InnerDigest<C>>: Deserialize<'de>", + serialize = "Vec<InnerDigest<C>>: Serialize", + ), + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "InnerDigest<C>: Clone"), @@ -596,6 +621,17 @@ impl<C> ExactSizeIterator for CurrentInnerPathNodeIter<C> where C: Configuration impl<C> FusedIterator for CurrentInnerPathNodeIter<C> where C: Configuration + ?Sized {} /// Merkle Tree Path +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "LeafDigest<C>: Deserialize<'de>, InnerPath<C>: Deserialize<'de>", + serialize = "LeafDigest<C>: Serialize, InnerPath<C>: Serialize", + ), + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), @@ -734,6 +770,17 @@ where } /// Merkle Tree Current Path +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "LeafDigest<C>: Deserialize<'de>, CurrentInnerPath<C>: Deserialize<'de>", + serialize = "LeafDigest<C>: Serialize, CurrentInnerPath<C>: Serialize", + ), + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), diff --git a/manta-crypto/src/merkle_tree/single_path.rs b/manta-crypto/src/merkle_tree/single_path.rs index 4a7715025..9deaaf5b0 100644 --- a/manta-crypto/src/merkle_tree/single_path.rs +++ b/manta-crypto/src/merkle_tree/single_path.rs @@ -17,6 +17,8 @@ //! Single Path Merkle Tree Storage // TODO: Should we be storing the root? Can we have a version where we don't? +// TODO: How should we design the free functions here? We need them for now for ledger state, but +// it would be nice to have a more elegant solution that doesn't require duplicate interfaces. use crate::merkle_tree::{ capacity, Configuration, CurrentPath, InnerDigest, LeafDigest, MerkleTree, Parameters, Root, @@ -81,13 +83,7 @@ where /// Returns the state of the length of this tree. #[inline] pub fn length_state(&self) -> Length { - if self.leaf_digest.is_none() { - Length::Empty - } else if self.current_path.leaf_index().0 < capacity::<C>() - 1 { - Length::CanAccept - } else { - Length::Full - } + raw::length_state(&self.leaf_digest, &self.current_path) } /// Returns the current merkle tree root. @@ -180,3 +176,52 @@ where Some(true) } } + +/// Raw Merkle Tree +pub mod raw { + use super::*; + + /// Returns the state of the length of this tree. + #[inline] + pub fn length_state<C>( + leaf_digest: &Option<LeafDigest<C>>, + current_path: &CurrentPath<C>, + ) -> Length + where + C: Configuration + ?Sized, + { + if leaf_digest.is_none() { + Length::Empty + } else if current_path.leaf_index().0 < capacity::<C>() - 1 { + Length::CanAccept + } else { + Length::Full + } + } + + /// Inserts the `next` leaf digest into the tree updating the `leaf_digest` and the `current_path`. + #[inline] + pub fn insert<C>( + parameters: &Parameters<C>, + leaf_digest: &mut Option<LeafDigest<C>>, + current_path: &mut CurrentPath<C>, + next: LeafDigest<C>, + ) -> Option<Root<C>> + where + C: Configuration + ?Sized, + LeafDigest<C>: Default, + InnerDigest<C>: Default, + { + match length_state(leaf_digest, current_path) { + Length::Empty => { + let root = current_path.root(parameters, &next); + *leaf_digest = Some(next); + Some(root) + } + Length::CanAccept => { + Some(current_path.update(parameters, leaf_digest.as_mut().unwrap(), next)) + } + Length::Full => None, + } + } +} diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 0648a3e10..e806d2f93 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -66,6 +66,7 @@ bip32 = { version = "0.3.0", default-features = false, features = ["bip39", "sec blake2 = { version = "0.10.0", default-features = false } derivative = { version = "2.2.0", default-features = false } generic-array = { version = "0.14.4", default-features = false } +indexmap = { version = "1.8.0", default-features = false } manta-accounting = { path = "../manta-accounting" } manta-crypto = { path = "../manta-crypto" } manta-util = { path = "../manta-util" } diff --git a/manta-pay/src/ledger.rs b/manta-pay/src/ledger.rs index ac0476e79..0744f28eb 100644 --- a/manta-pay/src/ledger.rs +++ b/manta-pay/src/ledger.rs @@ -17,9 +17,11 @@ //! Ledger Implementation use crate::config::{ - Config, EncryptedNote, MerkleTreeConfiguration, ProofSystem, Utxo, VoidNumber, + Config, EncryptedNote, MerkleTreeConfiguration, ProofSystem, TransferPost, Utxo, VoidNumber, }; use alloc::vec::Vec; +use core::convert::Infallible; +use indexmap::IndexSet; use manta_accounting::{ asset::{AssetId, AssetValue}, transfer::{ @@ -28,8 +30,13 @@ use manta_accounting::{ ReceiverPostingKey, SenderLedger, SenderPostingKey, SinkPostingKey, SourcePostingKey, TransferLedger, TransferLedgerSuperPostingKey, TransferPostingKey, UtxoSetOutput, }, + wallet::ledger::{Connection, PullResponse, PullResult, PushResponse, PushResult}, }; -use manta_crypto::{constraint::ProofSystem as _, merkle_tree, merkle_tree::forest::Configuration}; +use manta_crypto::{ + constraint::ProofSystem as _, + merkle_tree::{self, forest::Configuration, Tree}, +}; +use manta_util::{future::LocalBoxFuture, into_array_unchecked}; use std::collections::{HashMap, HashSet}; /// UTXO Merkle Forest Type @@ -64,7 +71,7 @@ impl<L, R> AsRef<R> for WrapPair<L, R> { /// Ledger pub struct Ledger { /// Void Numbers - void_numbers: HashSet<VoidNumber>, + void_numbers: IndexSet<VoidNumber>, /// UTXOs utxos: HashSet<Utxo>, @@ -286,3 +293,96 @@ impl TransferLedger<Config> for Ledger { } } } + +/// Checkpoint +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Checkpoint { + /// Receiver Index + pub receiver_index: [usize; 256], + + /// Sender Index + pub sender_index: usize, +} + +impl Checkpoint { + /// + #[inline] + pub fn new(receiver_index: [usize; 256], sender_index: usize) -> Self { + Self { + receiver_index, + sender_index, + } + } +} + +impl Default for Checkpoint { + #[inline] + fn default() -> Self { + Self::new([0; 256], 0) + } +} + +impl Connection<Config> for Ledger { + type Checkpoint = Checkpoint; + type ReceiverChunk = Vec<(Utxo, EncryptedNote)>; + type SenderChunk = Vec<VoidNumber>; + type Error = Infallible; + + #[inline] + fn pull<'s>( + &'s mut self, + checkpoint: &'s Self::Checkpoint, + ) -> LocalBoxFuture<'s, PullResult<Config, Self>> { + Box::pin(async { + let mut receivers = Vec::new(); + for (mut index, shard) in checkpoint + .receiver_index + .iter() + .copied() + .zip(self.shards.values()) + { + while let Some(entry) = shard.get(&(index as u64)) { + receivers.push(*entry); + index += 1; + } + } + let senders = self + .void_numbers + .iter() + .skip(checkpoint.sender_index) + .copied() + .collect(); + Ok(PullResponse { + checkpoint: Checkpoint::new( + into_array_unchecked( + self.utxo_forest + .forest + .as_ref() + .iter() + .map(move |t| t.len()) + .collect::<Vec<_>>(), + ), + self.void_numbers.len(), + ), + receivers, + senders, + }) + }) + } + + #[inline] + fn push(&mut self, posts: Vec<TransferPost>) -> LocalBoxFuture<PushResult<Config, Self>> { + Box::pin(async { + for post in posts { + // FIXME: What should the public accounts be? + match post.validate(vec![], vec![], self) { + Ok(posting_key) => { + posting_key.post(&(), self); + } + _ => return Ok(PushResponse { success: false }), + } + } + Ok(PushResponse { success: true }) + }) + } +} From 00d1a7014e45b4b6b4ceebc4464c0232c5e0d17b Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 11 Jan 2022 13:36:51 -0500 Subject: [PATCH 178/275] wip: add on-disk caching of the proving keys --- manta-accounting/src/transfer/canonical.rs | 36 +++-- manta-accounting/src/transfer/mod.rs | 174 ++++++++++++-------- manta-accounting/src/transfer/test.rs | 2 +- manta-pay/Cargo.toml | 3 + manta-pay/src/config.rs | 6 + manta-pay/src/lib.rs | 8 +- manta-pay/src/{ => test}/ledger.rs | 176 +++++++++++++-------- manta-pay/src/test/mod.rs | 8 +- manta-pay/src/{ => test}/simulation/mod.rs | 0 manta-pay/src/wallet/cache.rs | 160 +++++++++++++++++++ manta-pay/src/{wallet.rs => wallet/mod.rs} | 5 +- 11 files changed, 428 insertions(+), 150 deletions(-) rename manta-pay/src/{ => test}/ledger.rs (69%) rename manta-pay/src/{ => test}/simulation/mod.rs (100%) create mode 100644 manta-pay/src/wallet/cache.rs rename manta-pay/src/{wallet.rs => wallet/mod.rs} (94%) diff --git a/manta-accounting/src/transfer/canonical.rs b/manta-accounting/src/transfer/canonical.rs index 3481e6177..9e53d10a5 100644 --- a/manta-accounting/src/transfer/canonical.rs +++ b/manta-accounting/src/transfer/canonical.rs @@ -19,8 +19,8 @@ use crate::{ asset::{self, Asset, AssetValue}, transfer::{ - Configuration, PreSender, ProvingContext, Receiver, ReceivingKey, Sender, Transfer, - VerifyingContext, + has_public_participants, Configuration, PreSender, ProvingContext, Receiver, ReceivingKey, + Sender, Transfer, TransferPost, VerifyingContext, }, }; use alloc::vec::Vec; @@ -183,7 +183,6 @@ pub enum TransferShape { impl TransferShape { /// Selects the [`TransferShape`] for the given shape if it matches a canonical shape. - #[allow(clippy::absurd_extreme_comparisons)] // NOTE: We want these comparisons as values.. #[inline] pub fn select( asset_id_is_some: bool, @@ -192,27 +191,29 @@ impl TransferShape { receivers: usize, sinks: usize, ) -> Option<Self> { - const MINT_VISIBLE_ID: bool = MintShape::SOURCES + MintShape::SINKS > 0; - const PRIVATE_TRANSFER_VISIBLE_ID: bool = - PrivateTransferShape::SOURCES + PrivateTransferShape::SINKS > 0; - const RECLAIM_VISIBLE_ID: bool = ReclaimShape::SOURCES + ReclaimShape::SINKS > 0; + const MINT_VISIBLE_ASSET_ID: bool = + has_public_participants(MintShape::SOURCES, MintShape::SINKS); + const PRIVATE_TRANSFER_VISIBLE_ASSET_ID: bool = + has_public_participants(PrivateTransferShape::SOURCES, PrivateTransferShape::SINKS); + const RECLAIM_VISIBLE_ASSET_ID: bool = + has_public_participants(ReclaimShape::SOURCES, ReclaimShape::SINKS); match (asset_id_is_some, sources, senders, receivers, sinks) { ( - MINT_VISIBLE_ID, + MINT_VISIBLE_ASSET_ID, MintShape::SOURCES, MintShape::SENDERS, MintShape::RECEIVERS, MintShape::SINKS, ) => Some(Self::Mint), ( - PRIVATE_TRANSFER_VISIBLE_ID, + PRIVATE_TRANSFER_VISIBLE_ASSET_ID, PrivateTransferShape::SOURCES, PrivateTransferShape::SENDERS, PrivateTransferShape::RECEIVERS, PrivateTransferShape::SINKS, ) => Some(Self::PrivateTransfer), ( - RECLAIM_VISIBLE_ID, + RECLAIM_VISIBLE_ASSET_ID, ReclaimShape::SOURCES, ReclaimShape::SENDERS, ReclaimShape::RECEIVERS, @@ -221,6 +222,21 @@ impl TransferShape { _ => None, } } + + /// Selects the [`TransferShape`] from `post`. + #[inline] + pub fn from_post<C>(post: &TransferPost<C>) -> Option<Self> + where + C: Configuration, + { + Self::select( + post.asset_id.is_some(), + post.sources.len(), + post.sender_posts.len(), + post.receiver_posts.len(), + post.sinks.len(), + ) + } } /// Canonical Transaction Type diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index e9865d860..fe727e56e 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -39,16 +39,13 @@ pub mod canonical; #[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] pub mod test; -/// Returns `true` if the transfer with this shape would have public participants. +#[doc(inline)] +pub use canonical::Shape; + +/// Returns `true` if the [`Transfer`] with this shape would have public participants. #[inline] -pub const fn has_public_participants( - sources: usize, - senders: usize, - receivers: usize, - sinks: usize, -) -> bool { - let _ = (senders, receivers); - sources > 0 || sinks > 0 +pub const fn has_public_participants(sources: usize, sinks: usize) -> bool { + (sources + sinks) > 0 } /// Transfer Configuration @@ -929,10 +926,10 @@ where C: Configuration, { /// UTXO Set Output - utxo_set_output: UtxoSetOutput<C>, + pub utxo_set_output: UtxoSetOutput<C>, /// Void Number - void_number: VoidNumber<C>, + pub void_number: VoidNumber<C>, } impl<C> SenderPost<C> @@ -1201,10 +1198,10 @@ where C: Configuration, { /// Unspent Transaction Output - utxo: Utxo<C>, + pub utxo: Utxo<C>, /// Encrypted Note - note: EncryptedNote<C>, + pub note: EncryptedNote<C>, } impl<C> ReceiverPost<C> @@ -1355,7 +1352,7 @@ where /// Checks that the given `asset_id` for [`Transfer`] building is visible exactly when required. #[inline] fn has_visible_asset_id_when_required(has_visible_asset_id: bool) { - if has_public_participants(SOURCES, SENDERS, RECEIVERS, SINKS) { + if has_public_participants(SOURCES, SINKS) { assert!( has_visible_asset_id, "Missing public asset id when required." @@ -1576,7 +1573,7 @@ where #[inline] fn new_unknown(compiler: &mut C::Compiler) -> Self { Self { - asset_id: has_public_participants(SOURCES, SENDERS, RECEIVERS, SINKS) + asset_id: has_public_participants(SOURCES, SINKS) .then(|| compiler.allocate_unknown::<Public, _>()), sources: (0..SOURCES) .into_iter() @@ -1644,20 +1641,21 @@ where /// Checks that the balances associated to the source accounts are sufficient to withdraw the /// amount given in `sources`. - fn check_source_accounts( + fn check_source_accounts<I>( &self, - asset_id: Option<AssetId>, - accounts: Vec<Self::AccountId>, - sources: Vec<AssetValue>, - ) -> Result<Vec<Self::ValidSourceAccount>, InvalidSourceAccounts<Self::AccountId>>; + asset_id: AssetId, + sources: I, + ) -> Result<Vec<Self::ValidSourceAccount>, InvalidSourceAccount<Self::AccountId>> + where + I: Iterator<Item = (Self::AccountId, AssetValue)>; /// Checks that the sink accounts exist. - fn check_sink_accounts( + fn check_sink_accounts<I>( &self, - asset_id: Option<AssetId>, - accounts: Vec<Self::AccountId>, - sinks: Vec<AssetValue>, - ) -> Result<Vec<Self::ValidSinkAccount>, InvalidSinkAccounts<Self::AccountId>>; + sinks: I, + ) -> Result<Vec<Self::ValidSinkAccount>, InvalidSinkAccount<Self::AccountId>> + where + I: Iterator<Item = (Self::AccountId, AssetValue)>; /// Checks that the transfer `proof` is valid. #[allow(clippy::too_many_arguments)] // FIXME: Write a better abstraction for this. @@ -1709,40 +1707,28 @@ pub enum AccountBalance { /// Invalid Source Accounts /// -/// This `enum` is the error state of the [`TransferLedger::check_source_accounts`] method. See its -/// documentation for more. +/// This `struct` is the error state of the [`TransferLedger::check_source_accounts`] method. See +/// its documentation for more. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum InvalidSourceAccounts<AccountId> { - /// Invalid Transfer Shape - InvalidShape, - - /// Account is Unknown or would be Overdraw - BadAccount { - /// Account Id - account_id: AccountId, +pub struct InvalidSourceAccount<AccountId> { + /// Account Id + pub account_id: AccountId, - /// Current Balance if Known - balance: AccountBalance, + /// Current Balance if Known + pub balance: AccountBalance, - /// Amount Attempting to Withdraw - withdraw: AssetValue, - }, + /// Amount Attempting to Withdraw + pub withdraw: AssetValue, } /// Invalid Sink Accounts /// -/// This `enum` is the error state of the [`TransferLedger::check_sink_accounts`] method. See its +/// This `struct` is the error state of the [`TransferLedger::check_sink_accounts`] method. See its /// documentation for more. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum InvalidSinkAccounts<AccountId> { - /// Invalid Transfer Shape - InvalidShape, - - /// Account is Unknown - BadAccount { - /// Account Id - account_id: AccountId, - }, +pub struct InvalidSinkAccount<AccountId> { + /// Account Id + pub account_id: AccountId, } /// Transfer Post Error @@ -1751,11 +1737,14 @@ pub enum InvalidSinkAccounts<AccountId> { /// for more. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum TransferPostError<AccountId> { + /// Invalid Transfer Post Shape + InvalidShape, + /// Invalid Source Accounts - InvalidSourceAccounts(InvalidSourceAccounts<AccountId>), + InvalidSourceAccount(InvalidSourceAccount<AccountId>), /// Invalid Sink Accounts - InvalidSinkAccounts(InvalidSinkAccounts<AccountId>), + InvalidSinkAccount(InvalidSinkAccount<AccountId>), /// Sender Post Error Sender(SenderPostError), @@ -1775,17 +1764,17 @@ pub enum TransferPostError<AccountId> { InvalidProof, } -impl<AccountId> From<InvalidSourceAccounts<AccountId>> for TransferPostError<AccountId> { +impl<AccountId> From<InvalidSourceAccount<AccountId>> for TransferPostError<AccountId> { #[inline] - fn from(err: InvalidSourceAccounts<AccountId>) -> Self { - Self::InvalidSourceAccounts(err) + fn from(err: InvalidSourceAccount<AccountId>) -> Self { + Self::InvalidSourceAccount(err) } } -impl<AccountId> From<InvalidSinkAccounts<AccountId>> for TransferPostError<AccountId> { +impl<AccountId> From<InvalidSinkAccount<AccountId>> for TransferPostError<AccountId> { #[inline] - fn from(err: InvalidSinkAccounts<AccountId>) -> Self { - Self::InvalidSinkAccounts(err) + fn from(err: InvalidSinkAccount<AccountId>) -> Self { + Self::InvalidSinkAccount(err) } } @@ -1809,22 +1798,22 @@ where C: Configuration, { /// Asset Id - asset_id: Option<AssetId>, + pub asset_id: Option<AssetId>, /// Sources - sources: Vec<AssetValue>, + pub sources: Vec<AssetValue>, /// Sender Posts - sender_posts: Vec<SenderPost<C>>, + pub sender_posts: Vec<SenderPost<C>>, /// Receiver Posts - receiver_posts: Vec<ReceiverPost<C>>, + pub receiver_posts: Vec<ReceiverPost<C>>, /// Sinks - sinks: Vec<AssetValue>, + pub sinks: Vec<AssetValue>, /// Validity Proof - validity_proof: Proof<C>, + pub validity_proof: Proof<C>, } impl<C> TransferPost<C> @@ -1853,6 +1842,51 @@ where input } + /// Checks that the public participant data is well-formed and runs `ledger` validation on + /// source and sink accounts. + #[allow(clippy::type_complexity)] // FIXME: Use a better abstraction for this. + #[inline] + fn check_public_participants<L>( + asset_id: Option<AssetId>, + source_accounts: Vec<L::AccountId>, + source_values: Vec<AssetValue>, + sink_accounts: Vec<L::AccountId>, + sink_values: Vec<AssetValue>, + ledger: &L, + ) -> Result< + (Vec<L::ValidSourceAccount>, Vec<L::ValidSinkAccount>), + TransferPostError<L::AccountId>, + > + where + L: TransferLedger<C>, + { + let sources = source_values.len(); + let sinks = sink_values.len(); + if has_public_participants(sources, sinks) != asset_id.is_some() { + return Err(TransferPostError::InvalidShape); + } + if source_accounts.len() != sources { + return Err(TransferPostError::InvalidShape); + } + if sink_accounts.len() != sinks { + return Err(TransferPostError::InvalidShape); + } + let sources = if sources > 0 { + ledger.check_source_accounts( + asset_id.unwrap(), + source_accounts.into_iter().zip(source_values), + )? + } else { + Vec::new() + }; + let sinks = if sinks > 0 { + ledger.check_sink_accounts(sink_accounts.into_iter().zip(sink_values))? + } else { + Vec::new() + }; + Ok((sources, sinks)) + } + /// Validates `self` on the transfer `ledger`. #[inline] pub fn validate<L>( @@ -1864,10 +1898,14 @@ where where L: TransferLedger<C>, { - let source_posting_keys = - ledger.check_source_accounts(self.asset_id, source_accounts, self.sources)?; - let sink_posting_keys = - ledger.check_sink_accounts(self.asset_id, sink_accounts, self.sinks)?; + let (source_posting_keys, sink_posting_keys) = Self::check_public_participants( + self.asset_id, + source_accounts, + self.sources, + sink_accounts, + self.sinks, + ledger, + )?; for (i, p) in self.sender_posts.iter().enumerate() { if self .sender_posts diff --git a/manta-accounting/src/transfer/test.rs b/manta-accounting/src/transfer/test.rs index d7e723fa1..e95958194 100644 --- a/manta-accounting/src/transfer/test.rs +++ b/manta-accounting/src/transfer/test.rs @@ -245,7 +245,7 @@ where rng, ); Self::new( - has_public_participants(SOURCES, SENDERS, RECEIVERS, SINKS).then(|| asset.id), + has_public_participants(SOURCES, SINKS).then(|| asset.id), into_array_unchecked(input), into_array_unchecked(senders), into_array_unchecked(receivers), diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index e806d2f93..53cf02ad9 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -38,6 +38,7 @@ arkworks = [ "ark-ff", "ark-r1cs-std", "ark-relations", + "ark-serialize", ] # Enable Groth16 ZKP System @@ -62,6 +63,8 @@ ark-ff = { version = "0.3.0", optional = true, default-features = false } ark-groth16 = { version = "0.3.0", optional = true, default-features = false } ark-r1cs-std = { version = "0.3.1", optional = true, default-features = false } ark-relations = { version = "0.3.0", optional = true, default-features = false } +ark-serialize = { version = "0.3.0", optional = true, default-features = false } +async-std = { version = "1.10.0" } bip32 = { version = "0.3.0", default-features = false, features = ["bip39", "secp256k1"] } blake2 = { version = "0.10.0", default-features = false } derivative = { version = "2.2.0", default-features = false } diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index ca42c6b28..a89df5768 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -579,6 +579,12 @@ pub type ProvingContext = transfer::ProvingContext<Config>; /// Verifying Context Type pub type VerifyingContext = transfer::VerifyingContext<Config>; +/// Multi-Proving Context Type +pub type MultiProvingContext = transfer::canonical::MultiProvingContext<Config>; + +/// Multi-Verifying Context Type +pub type MultiVerifyingContext = transfer::canonical::MultiVerifyingContext<Config>; + /// Hierarchical Key Derivation Function pub struct HierarchicalKeyDerivationFunction; diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index ebac2a540..07d978086 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -23,12 +23,8 @@ extern crate alloc; -#[cfg(test)] -mod test; - pub mod crypto; pub mod key; -pub mod simulation; #[cfg(all(feature = "arkworks", feature = "groth16"))] #[cfg_attr(doc_cfg, doc(cfg(all(feature = "arkworks", feature = "groth16"))))] @@ -39,11 +35,11 @@ pub mod config; doc_cfg, doc(cfg(all(feature = "std", feature = "arkworks", feature = "groth16"))) )] -pub mod ledger; +pub mod wallet; #[cfg(all(feature = "std", feature = "arkworks", feature = "groth16"))] #[cfg_attr( doc_cfg, doc(cfg(all(feature = "std", feature = "arkworks", feature = "groth16"))) )] -pub mod wallet; +pub mod test; diff --git a/manta-pay/src/ledger.rs b/manta-pay/src/test/ledger.rs similarity index 69% rename from manta-pay/src/ledger.rs rename to manta-pay/src/test/ledger.rs index 0744f28eb..0891cd5d9 100644 --- a/manta-pay/src/ledger.rs +++ b/manta-pay/src/test/ledger.rs @@ -14,21 +14,23 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Ledger Implementation +//! Test Ledger Implementation use crate::config::{ - Config, EncryptedNote, MerkleTreeConfiguration, ProofSystem, TransferPost, Utxo, VoidNumber, + Config, EncryptedNote, MerkleTreeConfiguration, MultiVerifyingContext, ProofSystem, + TransferPost, Utxo, VoidNumber, }; use alloc::vec::Vec; +use async_std::sync::RwLock; use core::convert::Infallible; use indexmap::IndexSet; use manta_accounting::{ asset::{AssetId, AssetValue}, transfer::{ - canonical::{MultiVerifyingContext, TransferShape}, - AccountBalance, InvalidSinkAccounts, InvalidSourceAccounts, Proof, ReceiverLedger, - ReceiverPostingKey, SenderLedger, SenderPostingKey, SinkPostingKey, SourcePostingKey, - TransferLedger, TransferLedgerSuperPostingKey, TransferPostingKey, UtxoSetOutput, + canonical::TransferShape, AccountBalance, InvalidSinkAccount, InvalidSourceAccount, Proof, + ReceiverLedger, ReceiverPostingKey, SenderLedger, SenderPostingKey, SinkPostingKey, + SourcePostingKey, TransferLedger, TransferLedgerSuperPostingKey, TransferPostingKey, + UtxoSetOutput, }, wallet::ledger::{Connection, PullResponse, PullResult, PushResponse, PushResult}, }; @@ -68,6 +70,10 @@ impl<L, R> AsRef<R> for WrapPair<L, R> { } } +/// Account Id +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct AccountId(pub u64); + /// Ledger pub struct Ledger { /// Void Numbers @@ -83,10 +89,34 @@ pub struct Ledger { utxo_forest: UtxoMerkleForest, /// Account Table - accounts: HashMap<u128, HashMap<AssetId, AssetValue>>, + accounts: HashMap<AccountId, HashMap<AssetId, AssetValue>>, /// Verifying Contexts - verifying_context: MultiVerifyingContext<Config>, + verifying_context: MultiVerifyingContext, +} + +impl Ledger { + /// Builds an empty [`Ledger`]. + #[inline] + pub fn new( + utxo_forest_parameters: merkle_tree::Parameters<MerkleTreeConfiguration>, + verifying_context: MultiVerifyingContext, + ) -> Self { + Self { + void_numbers: Default::default(), + utxos: Default::default(), + shards: Default::default(), + utxo_forest: UtxoMerkleForest::new(utxo_forest_parameters), + accounts: Default::default(), + verifying_context, + } + } + + /// Sets the public balance of `account` in assets with `id` to `value`. + #[inline] + pub fn set_public_balance(&mut self, account: AccountId, id: AssetId, value: AssetValue) { + self.accounts.entry(account).or_default().insert(id, value); + } } impl SenderLedger<Config> for Ledger { @@ -155,11 +185,12 @@ impl ReceiverLedger<Config> for Ledger { .unwrap(); let len = shard.len(); shard.insert(len as u64, (utxo.0, note)); + self.utxos.insert(utxo.0); } } impl TransferLedger<Config> for Ledger { - type AccountId = u128; + type AccountId = AccountId; type Event = (); type ValidSourceAccount = WrapPair<Self::AccountId, AssetValue>; type ValidSinkAccount = WrapPair<Self::AccountId, AssetValue>; @@ -167,76 +198,65 @@ impl TransferLedger<Config> for Ledger { type SuperPostingKey = (); #[inline] - fn check_source_accounts( + fn check_source_accounts<I>( &self, - asset_id: Option<AssetId>, - accounts: Vec<Self::AccountId>, - sources: Vec<AssetValue>, - ) -> Result<Vec<Self::ValidSourceAccount>, InvalidSourceAccounts<Self::AccountId>> { - if let Some(asset_id) = asset_id { - let mut valid_source_accounts = Vec::new(); - for (account_id, withdraw) in accounts.into_iter().zip(sources) { + asset_id: AssetId, + sources: I, + ) -> Result<Vec<Self::ValidSourceAccount>, InvalidSourceAccount<Self::AccountId>> + where + I: Iterator<Item = (Self::AccountId, AssetValue)>, + { + sources + .map(|(account_id, withdraw)| { match self.accounts.get(&account_id) { Some(map) => match map.get(&asset_id) { Some(balance) => { if balance >= &withdraw { - valid_source_accounts.push(WrapPair(account_id, withdraw)); + Ok(WrapPair(account_id, withdraw)) } else { - return Err(InvalidSourceAccounts::BadAccount { + Err(InvalidSourceAccount { account_id, balance: AccountBalance::Known(*balance), withdraw, - }); + }) } } _ => { // FIXME: What about zero values in `sources`? - return Err(InvalidSourceAccounts::BadAccount { + Err(InvalidSourceAccount { account_id, balance: AccountBalance::Known(AssetValue(0)), withdraw, - }); + }) } }, - _ => { - return Err(InvalidSourceAccounts::BadAccount { - account_id, - balance: AccountBalance::UnknownAccount, - withdraw, - }); - } + _ => Err(InvalidSourceAccount { + account_id, + balance: AccountBalance::UnknownAccount, + withdraw, + }), } - } - Ok(valid_source_accounts) - } else if accounts.is_empty() && sources.is_empty() { - Ok(Vec::new()) - } else { - Err(InvalidSourceAccounts::InvalidShape) - } + }) + .collect() } #[inline] - fn check_sink_accounts( + fn check_sink_accounts<I>( &self, - asset_id: Option<AssetId>, - accounts: Vec<Self::AccountId>, - sinks: Vec<AssetValue>, - ) -> Result<Vec<Self::ValidSinkAccount>, InvalidSinkAccounts<Self::AccountId>> { - if asset_id.is_some() { - let mut valid_sink_accounts = Vec::new(); - for (account_id, deposit) in accounts.into_iter().zip(sinks) { + sinks: I, + ) -> Result<Vec<Self::ValidSinkAccount>, InvalidSinkAccount<Self::AccountId>> + where + I: Iterator<Item = (Self::AccountId, AssetValue)>, + { + sinks + .map(move |(account_id, deposit)| { if self.accounts.contains_key(&account_id) { - valid_sink_accounts.push(WrapPair(account_id, deposit)); + Ok(WrapPair(account_id, deposit)) } else { - return Err(InvalidSinkAccounts::BadAccount { account_id }); + Err(InvalidSinkAccount { account_id }) } - } - Ok(valid_sink_accounts) - } else if accounts.is_empty() && sinks.is_empty() { - Ok(Vec::new()) - } else { - Err(InvalidSinkAccounts::InvalidShape) - } + }) + .collect() } #[inline] @@ -305,7 +325,7 @@ pub struct Checkpoint { } impl Checkpoint { - /// + /// Builds a new [`Checkpoint`] from `receiver_index` and `sender_index`. #[inline] pub fn new(receiver_index: [usize; 256], sender_index: usize) -> Self { Self { @@ -322,7 +342,16 @@ impl Default for Checkpoint { } } -impl Connection<Config> for Ledger { +/// Ledger Connection +pub struct LedgerConnection { + /// Ledger Account + pub account: AccountId, + + /// Ledger Access + pub ledger: RwLock<Ledger>, +} + +impl Connection<Config> for LedgerConnection { type Checkpoint = Checkpoint; type ReceiverChunk = Vec<(Utxo, EncryptedNote)>; type SenderChunk = Vec<VoidNumber>; @@ -334,19 +363,20 @@ impl Connection<Config> for Ledger { checkpoint: &'s Self::Checkpoint, ) -> LocalBoxFuture<'s, PullResult<Config, Self>> { Box::pin(async { + let ledger = self.ledger.read().await; let mut receivers = Vec::new(); for (mut index, shard) in checkpoint .receiver_index .iter() .copied() - .zip(self.shards.values()) + .zip(ledger.shards.values()) { while let Some(entry) = shard.get(&(index as u64)) { receivers.push(*entry); index += 1; } } - let senders = self + let senders = ledger .void_numbers .iter() .skip(checkpoint.sender_index) @@ -355,14 +385,15 @@ impl Connection<Config> for Ledger { Ok(PullResponse { checkpoint: Checkpoint::new( into_array_unchecked( - self.utxo_forest + ledger + .utxo_forest .forest .as_ref() .iter() .map(move |t| t.len()) .collect::<Vec<_>>(), ), - self.void_numbers.len(), + ledger.void_numbers.len(), ), receivers, senders, @@ -373,11 +404,17 @@ impl Connection<Config> for Ledger { #[inline] fn push(&mut self, posts: Vec<TransferPost>) -> LocalBoxFuture<PushResult<Config, Self>> { Box::pin(async { + let mut ledger = self.ledger.write().await; for post in posts { - // FIXME: What should the public accounts be? - match post.validate(vec![], vec![], self) { + let (sources, sinks) = match TransferShape::from_post(&post) { + Some(TransferShape::Mint) => (vec![self.account], vec![]), + Some(TransferShape::PrivateTransfer) => (vec![], vec![]), + Some(TransferShape::Reclaim) => (vec![], vec![self.account]), + _ => return Ok(PushResponse { success: false }), + }; + match post.validate(sources, sinks, &*ledger) { Ok(posting_key) => { - posting_key.post(&(), self); + posting_key.post(&(), &mut *ledger); } _ => return Ok(PushResponse { success: false }), } @@ -386,3 +423,18 @@ impl Connection<Config> for Ledger { }) } } + +#[cfg(test)] +mod test { + use super::*; + use rand::thread_rng; + + #[test] + fn test() { + /* + let mut rng = thread_rng(); + let parameters = rng.gen(); + let utxo_set_parameters = rng.gen(); + */ + } +} diff --git a/manta-pay/src/test/mod.rs b/manta-pay/src/test/mod.rs index 6f2847103..dd797c6fa 100644 --- a/manta-pay/src/test/mod.rs +++ b/manta-pay/src/test/mod.rs @@ -16,5 +16,11 @@ //! Manta Pay Testing -// TODO: pub mod merkle_tree; +pub mod ledger; +pub mod simulation; + +// TODO: #[cfg(test)] +// pub mod merkle_tree; + +#[cfg(test)] pub mod transfer; diff --git a/manta-pay/src/simulation/mod.rs b/manta-pay/src/test/simulation/mod.rs similarity index 100% rename from manta-pay/src/simulation/mod.rs rename to manta-pay/src/test/simulation/mod.rs diff --git a/manta-pay/src/wallet/cache.rs b/manta-pay/src/wallet/cache.rs new file mode 100644 index 000000000..aa7a77a80 --- /dev/null +++ b/manta-pay/src/wallet/cache.rs @@ -0,0 +1,160 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Proving Context Caching + +use crate::config::{MultiProvingContext, ProvingContext}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; +use async_std::{ + fs, io, + path::{Path, PathBuf}, +}; +use core::marker::PhantomData; +use manta_util::{cache::CachedResource, future::LocalBoxFuture}; + +/// Caching Error +pub enum Error { + /// Serialization Error + Serialization(SerializationError), + + /// I/O Error + Io(io::Error), +} + +impl From<SerializationError> for Error { + #[inline] + fn from(err: SerializationError) -> Self { + Self::Serialization(err) + } +} + +impl From<io::Error> for Error { + #[inline] + fn from(err: io::Error) -> Self { + Self::Io(err) + } +} + +/// Cache Reading Key +pub struct ReadingKey(PhantomData<()>); + +impl ReadingKey { + #[inline] + fn new() -> Self { + Self(PhantomData) + } +} + +/// On-Disk Multi-Proving Context +pub struct OnDiskMultiProvingContext { + /// Source Directory + directory: PathBuf, + + /// Current Cached Context + context: Option<MultiProvingContext>, +} + +impl OnDiskMultiProvingContext { + /// Builds a new [`OnDiskMultiProvingContext`] setting the source directory to `directory`. + /// + /// To save the cache data to disk, use [`save`](Self::save). + #[inline] + pub fn new<P>(directory: P) -> Self + where + P: AsRef<Path>, + { + Self { + directory: directory.as_ref().to_owned(), + context: None, + } + } + + /// Returns the directory where `self` stores the [`MultiProvingContext`]. + #[inline] + pub fn directory(&self) -> &Path { + &self.directory + } + + /// Reads a single [`ProvingContext`] from `path`. + #[inline] + async fn read_context<P>(path: P) -> Result<ProvingContext, Error> + where + P: AsRef<Path>, + { + Ok(ProvingContext::deserialize_unchecked( + &mut fs::read(path).await?.as_slice(), + )?) + } + + /// Writes `context` to `path`. + #[inline] + async fn write_context<P>(path: P, context: &ProvingContext) -> Result<(), Error> + where + P: AsRef<Path>, + { + let mut buffer = Vec::new(); + context.serialize(&mut buffer.as_mut_slice())?; + Ok(fs::write(path, buffer).await?) + } + + /// Saves the `context` to the on-disk directory. This method _does not_ write `context` into + /// the cache. + #[inline] + pub async fn save(&self, context: &MultiProvingContext) -> Result<(), Error> { + let mint_path = self.directory.join("mint.bin"); + let private_transfer_path = self.directory.join("private-transfer.bin"); + let reclaim_path = self.directory.join("reclaim.bin"); + Self::write_context(mint_path, &context.mint).await?; + Self::write_context(private_transfer_path, &context.private_transfer).await?; + Self::write_context(reclaim_path, &context.reclaim).await?; + Ok(()) + } +} + +impl CachedResource<MultiProvingContext> for OnDiskMultiProvingContext { + type ReadingKey = ReadingKey; + type Error = Error; + + #[inline] + fn aquire(&mut self) -> LocalBoxFuture<Result<Self::ReadingKey, Self::Error>> { + Box::pin(async { + let mint_path = self.directory.join("mint.bin"); + let private_transfer_path = self.directory.join("private-transfer.bin"); + let reclaim_path = self.directory.join("reclaim.bin"); + self.context = Some(MultiProvingContext { + mint: Self::read_context(mint_path).await?, + private_transfer: Self::read_context(private_transfer_path).await?, + reclaim: Self::read_context(reclaim_path).await?, + }); + Ok(ReadingKey::new()) + }) + } + + #[inline] + fn read(&self, reading_key: Self::ReadingKey) -> &MultiProvingContext { + // SAFETY: Since `reading_key` is only given out when we know that `context` is `Some`, + // we can safely `unwrap` here. + let _ = reading_key; + self.context.as_ref().unwrap() + } + + #[inline] + fn release(&mut self) -> LocalBoxFuture { + Box::pin(async { + self.context.take(); + }) + } +} diff --git a/manta-pay/src/wallet.rs b/manta-pay/src/wallet/mod.rs similarity index 94% rename from manta-pay/src/wallet.rs rename to manta-pay/src/wallet/mod.rs index f14530549..45132daa7 100644 --- a/manta-pay/src/wallet.rs +++ b/manta-pay/src/wallet/mod.rs @@ -23,7 +23,6 @@ use crate::{ use manta_accounting::{ asset::HashAssetMap, key, - transfer::canonical::MultiProvingContext, wallet::{ self, signer::{self, AssetMapKey}, @@ -31,6 +30,8 @@ use manta_accounting::{ }; use manta_crypto::merkle_tree; +pub mod cache; + /// Signer UTXO Set pub type UtxoSet = merkle_tree::forest::TreeArrayMerkleForest< MerkleTreeConfiguration, @@ -46,7 +47,7 @@ impl signer::Configuration for Config { key::Map<TestnetKeySecret, HierarchicalKeyDerivationFunction>; type UtxoSet = UtxoSet; type AssetMap = HashAssetMap<AssetMapKey<Self>>; - type ProvingContextCache = MultiProvingContext<Self>; + type ProvingContextCache = cache::OnDiskMultiProvingContext; type Rng = rand_chacha::ChaCha20Rng; } From 8258efa1bc9ec746ff329f33be9e79bbf13c1168 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 11 Jan 2022 14:42:49 -0500 Subject: [PATCH 179/275] wip: setup signer/ledger tests --- Cargo.toml | 1 - manta-accounting/Cargo.toml | 1 - manta-accounting/src/key.rs | 25 +++++++ manta-accounting/src/transfer/canonical.rs | 34 ++++++++- manta-accounting/src/transfer/mod.rs | 24 +++++++ manta-pay/Cargo.toml | 3 + manta-pay/src/config.rs | 4 ++ manta-pay/src/key.rs | 2 +- manta-pay/src/test/ledger.rs | 84 ++++++++++++++++++++-- manta-pay/src/test/transfer.rs | 14 ++-- manta-pay/src/wallet/cache.rs | 10 ++- manta-util/src/cache.rs | 7 +- 12 files changed, 188 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 66819f149..13c735d91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,5 +48,4 @@ manta-accounting = { path = "manta-accounting" } manta-crypto = { path = "manta-crypto" } manta-pay = { path = "manta-pay" } manta-util = { path = "manta-util" } -parity-scale-codec = { version = "2.2.0", default-features = false } diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index a62a193e3..337bb097c 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -51,4 +51,3 @@ serde = { version = "1.0.133", optional = true, default-features = false, featur [dev-dependencies] rand = "0.8.4" -tempfile = "3.2.0" diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index 574167fa4..7de5ddbe8 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -68,6 +68,16 @@ pub trait HierarchicalKeyDerivationScheme { self.derive_view(account, spend, view)?, )) } + + /// Maps `self` along a key derivation function. + #[inline] + fn map<K>(self) -> Map<Self, K> + where + Self: Sized, + K: KeyDerivationFunction<Key = Self::SecretKey>, + { + Map::new(self) + } } impl<H> HierarchicalKeyDerivationScheme for &H @@ -117,6 +127,21 @@ where __: PhantomData<K>, } +impl<H, K> Map<H, K> +where + H: HierarchicalKeyDerivationScheme, + K: KeyDerivationFunction<Key = H::SecretKey>, +{ + /// Builds a new [`Map`] from `base`. + #[inline] + pub fn new(base: H) -> Self { + Self { + base, + __: PhantomData, + } + } +} + impl<H, K> HierarchicalKeyDerivationScheme for Map<H, K> where H: HierarchicalKeyDerivationScheme, diff --git a/manta-accounting/src/transfer/canonical.rs b/manta-accounting/src/transfer/canonical.rs index 9e53d10a5..f2337c524 100644 --- a/manta-accounting/src/transfer/canonical.rs +++ b/manta-accounting/src/transfer/canonical.rs @@ -19,12 +19,14 @@ use crate::{ asset::{self, Asset, AssetValue}, transfer::{ - has_public_participants, Configuration, PreSender, ProvingContext, Receiver, ReceivingKey, - Sender, Transfer, TransferPost, VerifyingContext, + has_public_participants, Configuration, FullParameters, PreSender, ProofSystemError, + ProofSystemPublicParameters, ProvingContext, Receiver, ReceivingKey, Sender, Transfer, + TransferPost, VerifyingContext, }, }; use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; +use manta_crypto::rand::{CryptoRng, RngCore}; use manta_util::{create_seal, seal}; create_seal! {} @@ -415,3 +417,31 @@ where } } } + +/// Generates proving and verifying multi-contexts for the canonical transfer shapes. +#[inline] +pub fn generate_context<C, R>( + public_parameters: &ProofSystemPublicParameters<C>, + parameters: FullParameters<C>, + rng: &mut R, +) -> Result<(MultiProvingContext<C>, MultiVerifyingContext<C>), ProofSystemError<C>> +where + C: Configuration, + R: CryptoRng + RngCore + ?Sized, +{ + let mint = Mint::generate_context(public_parameters, parameters, rng)?; + let private_transfer = PrivateTransfer::generate_context(public_parameters, parameters, rng)?; + let reclaim = Reclaim::generate_context(public_parameters, parameters, rng)?; + Ok(( + MultiProvingContext { + mint: mint.0, + private_transfer: private_transfer.0, + reclaim: reclaim.0, + }, + MultiVerifyingContext { + mint: mint.1, + private_transfer: private_transfer.1, + reclaim: reclaim.1, + }, + )) +} diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index fe727e56e..36d4c0182 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -355,6 +355,30 @@ pub type ProofInput<C> = <<C as Configuration>::ProofSystem as ProofSystem>::Inp pub type Proof<C> = <ProofSystemType<C> as ProofSystem>::Proof; /// Transfer Parameters +#[derive(derivative::Derivative)] +#[derivative( + Clone( + bound = "C::KeyAgreementScheme: Clone, C::UtxoCommitmentScheme: Clone, C::VoidNumberHashFunction: Clone" + ), + Copy( + bound = "C::KeyAgreementScheme: Copy, C::UtxoCommitmentScheme: Copy, C::VoidNumberHashFunction: Copy" + ), + Debug( + bound = "C::KeyAgreementScheme: Debug, C::UtxoCommitmentScheme: Debug, C::VoidNumberHashFunction: Debug" + ), + Default( + bound = "C::KeyAgreementScheme: Default, C::UtxoCommitmentScheme: Default, C::VoidNumberHashFunction: Default" + ), + Eq( + bound = "C::KeyAgreementScheme: Eq, C::UtxoCommitmentScheme: Eq, C::VoidNumberHashFunction: Eq" + ), + Hash( + bound = "C::KeyAgreementScheme: Hash, C::UtxoCommitmentScheme: Hash, C::VoidNumberHashFunction: Hash" + ), + PartialEq( + bound = "C::KeyAgreementScheme: PartialEq, C::UtxoCommitmentScheme: PartialEq, C::VoidNumberHashFunction: PartialEq" + ) +)] pub struct Parameters<C> where C: Configuration + ?Sized, diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 53cf02ad9..998c9fd0f 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -80,8 +80,11 @@ x25519-dalek = { git = "https://github.com/Manta-Network/x25519-dalek", default- zk-garage-plonk = { package = "plonk", git = "https://github.com/zk-garage/plonk", optional = true, default-features = false } [dev-dependencies] +async-std = { version = "1.10.0", features = ["attributes", "unstable"] } criterion = "0.3.5" manta-accounting = { path = "../manta-accounting", features = ["test"] } manta-crypto = { path = "../manta-crypto", features = ["test"] } manta-pay = { path = ".", features = ["test"] } rand = "0.8.4" +tempfile = "3.2.0" + diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index a89df5768..f0a532d98 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -128,6 +128,7 @@ pub type KeyAgreementSchemeVar = DiffieHellman<GroupVar, Compiler>; pub type Utxo = Fp<ConstraintField>; /// UTXO Commitment Scheme +#[derive(Clone)] pub struct UtxoCommitmentScheme(pub Poseidon4); impl CommitmentScheme for UtxoCommitmentScheme { @@ -203,6 +204,7 @@ impl Constant<Compiler> for UtxoCommitmentSchemeVar { pub type VoidNumber = Fp<ConstraintField>; /// Void Number Hash Function +#[derive(Clone)] pub struct VoidNumberHashFunction(pub Poseidon2); impl BinaryHashFunction for VoidNumberHashFunction { @@ -353,6 +355,7 @@ pub type LeafHash = merkle_tree::IdentityLeafHash<Utxo>; pub type LeafHashVar = merkle_tree::IdentityLeafHash<UtxoVar, Compiler>; /// Inner Hash Configuration +#[derive(Clone)] pub struct InnerHash; impl merkle_tree::InnerHash for InnerHash { @@ -411,6 +414,7 @@ impl merkle_tree::InnerHash<Compiler> for InnerHashVar { } /// Merkle Tree Configuration +#[derive(Clone)] pub struct MerkleTreeConfiguration; impl merkle_tree::HashConfiguration for MerkleTreeConfiguration { diff --git a/manta-pay/src/key.rs b/manta-pay/src/key.rs index dad8ae310..d100fd6b0 100644 --- a/manta-pay/src/key.rs +++ b/manta-pay/src/key.rs @@ -32,7 +32,7 @@ use manta_accounting::key::{ }; use manta_util::{create_seal, seal}; -pub use bip32::{Error, Mnemonic}; +pub use bip32::{Error, Language, Mnemonic}; create_seal! {} diff --git a/manta-pay/src/test/ledger.rs b/manta-pay/src/test/ledger.rs index 0891cd5d9..b2116842d 100644 --- a/manta-pay/src/test/ledger.rs +++ b/manta-pay/src/test/ledger.rs @@ -20,7 +20,7 @@ use crate::config::{ Config, EncryptedNote, MerkleTreeConfiguration, MultiVerifyingContext, ProofSystem, TransferPost, Utxo, VoidNumber, }; -use alloc::vec::Vec; +use alloc::{rc::Rc, vec::Vec}; use async_std::sync::RwLock; use core::convert::Infallible; use indexmap::IndexSet; @@ -75,6 +75,7 @@ impl<L, R> AsRef<R> for WrapPair<L, R> { pub struct AccountId(pub u64); /// Ledger +#[derive(Debug)] pub struct Ledger { /// Void Numbers void_numbers: IndexSet<VoidNumber>, @@ -345,10 +346,18 @@ impl Default for Checkpoint { /// Ledger Connection pub struct LedgerConnection { /// Ledger Account - pub account: AccountId, + account: AccountId, /// Ledger Access - pub ledger: RwLock<Ledger>, + ledger: Rc<RwLock<Ledger>>, +} + +impl LedgerConnection { + /// Builds a new [`LedgerConnection`] for `account` and `ledger`. + #[inline] + pub fn new(account: AccountId, ledger: Rc<RwLock<Ledger>>) -> Self { + Self { account, ledger } + } } impl Connection<Config> for LedgerConnection { @@ -424,17 +433,78 @@ impl Connection<Config> for LedgerConnection { } } +/// Testing Suite #[cfg(test)] mod test { use super::*; + use crate::{ + config::FullParameters, + key::{Language, Mnemonic, TestnetKeySecret}, + wallet::{self, cache::OnDiskMultiProvingContext, Signer, Wallet}, + }; + use async_std::task; + use manta_accounting::{ + key::{AccountTable, HierarchicalKeyDerivationScheme}, + transfer, + }; + use manta_crypto::rand::{Rand, SeedableRng}; use rand::thread_rng; + use rand_chacha::ChaCha20Rng; - #[test] - fn test() { - /* + /// + #[async_std::test] + async fn test() { let mut rng = thread_rng(); let parameters = rng.gen(); let utxo_set_parameters = rng.gen(); - */ + + let (proving_context, verifying_context) = transfer::canonical::generate_context( + &(), + FullParameters::new(&parameters, &utxo_set_parameters), + &mut rng, + ) + .expect("Failed to generate contexts."); + + let directory = task::spawn_blocking(tempfile::tempdir) + .await + .expect("Unable to generate temporary test directory."); + + let cache = OnDiskMultiProvingContext::new(directory.path()); + cache + .save(proving_context) + .await + .expect("Unable to save proving context to disk."); + + let mut ledger = Ledger::new(utxo_set_parameters.clone(), verifying_context); + ledger.set_public_balance(AccountId(0), AssetId(0), AssetValue(10000)); + ledger.set_public_balance(AccountId(1), AssetId(0), AssetValue(1000)); + ledger.set_public_balance(AccountId(2), AssetId(0), AssetValue(100)); + + println!("{:#?}", ledger); + + let ledger = Rc::new(RwLock::new(ledger)); + + let mut wallet = Wallet::empty( + LedgerConnection::new(AccountId(0), ledger.clone()), + Signer::new( + AccountTable::new( + TestnetKeySecret::new( + Mnemonic::random(&mut rng, Language::English), + "password", + ) + .map(), + ), + cache.clone(), + parameters.clone(), + wallet::UtxoSet::new(utxo_set_parameters.clone()), + ChaCha20Rng::from_rng(&mut rng).expect("Failed to sample PRNG for signer."), + ), + ); + + let _ = wallet.sync().await; + + task::spawn_blocking(move || directory.close()) + .await + .expect("Unable to delete temporary test directory."); } } diff --git a/manta-pay/src/test/transfer.rs b/manta-pay/src/test/transfer.rs index cecc6417b..fb7754a82 100644 --- a/manta-pay/src/test/transfer.rs +++ b/manta-pay/src/test/transfer.rs @@ -16,15 +16,17 @@ //! Manta Pay Transfer Testing -use crate::config::{self, FullParameters, Mint, PrivateTransfer, Reclaim}; +use crate::config::{ + FullParameters, MerkleTreeConfiguration, Mint, PrivateTransfer, ProofSystem, Reclaim, +}; use manta_crypto::{ - constraint::{measure::Measure, ProofSystem}, + constraint::{measure::Measure, ProofSystem as _}, merkle_tree, rand::Rand, }; use rand::thread_rng; -type UtxoSet = merkle_tree::full::FullMerkleTree<config::MerkleTreeConfiguration>; +type UtxoSet = merkle_tree::full::FullMerkleTree<MerkleTreeConfiguration>; /// Tests the generation of proving/verifying contexts for [`Mint`]. #[test] @@ -32,7 +34,7 @@ fn sample_mint_context() { let mut rng = thread_rng(); let cs = Mint::unknown_constraints(FullParameters::new(&rng.gen(), &rng.gen())); println!("Mint: {:?}", cs.measure()); - config::ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); + ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); } /// Tests the generation of proving/verifying contexts for [`PrivateTransfer`]. @@ -41,7 +43,7 @@ fn sample_private_transfer_context() { let mut rng = thread_rng(); let cs = PrivateTransfer::unknown_constraints(FullParameters::new(&rng.gen(), &rng.gen())); println!("PrivateTransfer: {:?}", cs.measure()); - config::ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); + ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); } /// Tests the generation of proving/verifying contexts for [`Reclaim`]. @@ -50,7 +52,7 @@ fn sample_reclaim_context() { let mut rng = thread_rng(); let cs = Reclaim::unknown_constraints(FullParameters::new(&rng.gen(), &rng.gen())); println!("Reclaim: {:?}", cs.measure()); - config::ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); + ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); } /// Tests the generation of a [`Mint`]. diff --git a/manta-pay/src/wallet/cache.rs b/manta-pay/src/wallet/cache.rs index aa7a77a80..780e90d5f 100644 --- a/manta-pay/src/wallet/cache.rs +++ b/manta-pay/src/wallet/cache.rs @@ -26,6 +26,7 @@ use core::marker::PhantomData; use manta_util::{cache::CachedResource, future::LocalBoxFuture}; /// Caching Error +#[derive(Debug)] pub enum Error { /// Serialization Error Serialization(SerializationError), @@ -113,7 +114,7 @@ impl OnDiskMultiProvingContext { /// Saves the `context` to the on-disk directory. This method _does not_ write `context` into /// the cache. #[inline] - pub async fn save(&self, context: &MultiProvingContext) -> Result<(), Error> { + pub async fn save(&self, context: MultiProvingContext) -> Result<(), Error> { let mint_path = self.directory.join("mint.bin"); let private_transfer_path = self.directory.join("private-transfer.bin"); let reclaim_path = self.directory.join("reclaim.bin"); @@ -158,3 +159,10 @@ impl CachedResource<MultiProvingContext> for OnDiskMultiProvingContext { }) } } + +impl Clone for OnDiskMultiProvingContext { + #[inline] + fn clone(&self) -> Self { + Self::new(&self.directory) + } +} diff --git a/manta-util/src/cache.rs b/manta-util/src/cache.rs index 7f673e8c6..ef37f77bc 100644 --- a/manta-util/src/cache.rs +++ b/manta-util/src/cache.rs @@ -18,7 +18,7 @@ use crate::future::LocalBoxFuture; use alloc::boxed::Box; -use core::convert::Infallible; +use core::{convert::Infallible, ops::Deref}; /// Cached Resource pub trait CachedResource<T> { @@ -54,7 +54,10 @@ pub trait CachedResource<T> { /// Cached Resource Error Type pub type CachedResourceError<T, R> = <R as CachedResource<T>>::Error; -impl<T> CachedResource<T> for T { +impl<T, D> CachedResource<T> for D +where + D: Deref<Target = T>, +{ type ReadingKey = (); type Error = Infallible; From 61f8099dabb50db974a82d815a969b3901acb4c7 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 12 Jan 2022 02:03:56 -0500 Subject: [PATCH 180/275] wip: working on signer/ledger tests --- manta-accounting/src/key.rs | 9 +++ manta-accounting/src/transfer/mod.rs | 26 +++++++ manta-accounting/src/wallet/ledger.rs | 14 ++++ manta-accounting/src/wallet/signer.rs | 52 ++++++++------ manta-accounting/src/wallet/state.rs | 65 +++++++++++------- manta-crypto/src/encryption.rs | 57 ++++++++++++++++ manta-crypto/src/key.rs | 22 ++++++ manta-crypto/src/merkle_tree/forest.rs | 58 +++++++++++++--- manta-pay/src/config.rs | 9 ++- manta-pay/src/crypto/ecc.rs | 6 +- manta-pay/src/crypto/encryption.rs | 24 ++++++- manta-pay/src/test/ledger.rs | 93 +++++++++++++++++++++----- manta-pay/src/wallet/cache.rs | 51 ++++++++------ manta-util/src/array.rs | 18 ++++- 14 files changed, 407 insertions(+), 97 deletions(-) diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index 7de5ddbe8..dee7b8f53 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -203,6 +203,15 @@ where } /// Error Type +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "H::Error: Clone"), + Copy(bound = "H::Error: Copy"), + Debug(bound = "H::Error: Debug"), + Eq(bound = "H::Error: Eq"), + Hash(bound = "H::Error: Hash"), + PartialEq(bound = "H::Error: PartialEq") +)] pub enum Error<H> where H: HierarchicalKeyDerivationScheme + ?Sized, diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index 36d4c0182..bbac0b235 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -945,6 +945,15 @@ pub enum SenderPostError { } /// Sender Post +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "UtxoSetOutput<C>: Clone, VoidNumber<C>: Clone"), + Copy(bound = "UtxoSetOutput<C>: Copy, VoidNumber<C>: Copy"), + Debug(bound = "UtxoSetOutput<C>: Debug, VoidNumber<C>: Debug"), + Eq(bound = "UtxoSetOutput<C>: Eq, VoidNumber<C>: Eq"), + Hash(bound = "UtxoSetOutput<C>: Hash, VoidNumber<C>: Hash"), + PartialEq(bound = "UtxoSetOutput<C>: PartialEq, VoidNumber<C>: PartialEq") +)] pub struct SenderPost<C> where C: Configuration, @@ -1217,6 +1226,15 @@ pub enum ReceiverPostError { } /// Receiver Post +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "Utxo<C>: Clone, EncryptedNote<C>: Clone"), + Copy(bound = "Utxo<C>: Copy, EncryptedNote<C>: Copy"), + Debug(bound = "Utxo<C>: Debug, EncryptedNote<C>: Debug"), + Eq(bound = "Utxo<C>: Eq, EncryptedNote<C>: Eq"), + Hash(bound = "Utxo<C>: Hash, EncryptedNote<C>: Hash"), + PartialEq(bound = "Utxo<C>: PartialEq, EncryptedNote<C>: PartialEq") +)] pub struct ReceiverPost<C> where C: Configuration, @@ -1817,6 +1835,14 @@ impl<AccountId> From<ReceiverPostError> for TransferPostError<AccountId> { } /// Transfer Post +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "SenderPost<C>: Clone, ReceiverPost<C>: Clone, Proof<C>: Clone"), + Debug(bound = "SenderPost<C>: Debug, ReceiverPost<C>: Debug, Proof<C>: Debug"), + Eq(bound = "SenderPost<C>: Eq, ReceiverPost<C>: Eq, Proof<C>: Eq"), + Hash(bound = "SenderPost<C>: Hash, ReceiverPost<C>: Hash, Proof<C>: Hash"), + PartialEq(bound = "SenderPost<C>: PartialEq, ReceiverPost<C>: PartialEq, Proof<C>: PartialEq") +)] pub struct TransferPost<C> where C: Configuration, diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs index 4b9fa83ec..94ed125f2 100644 --- a/manta-accounting/src/wallet/ledger.rs +++ b/manta-accounting/src/wallet/ledger.rs @@ -18,6 +18,7 @@ use crate::transfer::{Configuration, EncryptedNote, TransferPost, Utxo, VoidNumber}; use alloc::vec::Vec; +use core::{fmt::Debug, hash::Hash}; use manta_util::future::LocalBoxFuture; /// Ledger Source Connection @@ -63,6 +64,18 @@ pub type PushResult<C, L> = Result<PushResponse, <L as Connection<C>>::Error>; /// /// This `struct` is created by the [`pull`](Connection::pull) method on [`Connection`]. /// See its documentation for more. +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "L::Checkpoint: Clone, L::ReceiverChunk: Clone, L::SenderChunk: Clone"), + Copy(bound = "L::Checkpoint: Copy, L::ReceiverChunk: Copy, L::SenderChunk: Copy"), + Debug(bound = "L::Checkpoint: Debug, L::ReceiverChunk: Debug, L::SenderChunk: Debug"), + Default(bound = "L::Checkpoint: Default, L::ReceiverChunk: Default, L::SenderChunk: Default"), + Eq(bound = "L::Checkpoint: Eq, L::ReceiverChunk: Eq, L::SenderChunk: Eq"), + Hash(bound = "L::Checkpoint: Hash, L::ReceiverChunk: Hash, L::SenderChunk: Hash"), + PartialEq( + bound = "L::Checkpoint: PartialEq, L::ReceiverChunk: PartialEq, L::SenderChunk: PartialEq" + ) +)] pub struct PullResponse<C, L> where C: Configuration, @@ -82,6 +95,7 @@ where /// /// This `struct` is created by the [`push`](Connection::push) method on [`Connection`]. /// See its documentation for more. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct PushResponse { /// Transaction Success Flag pub success: bool, diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 61bfe69d7..29bde9909 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -25,6 +25,7 @@ // TODO: Move `sync` to a stream-based algorithm instead of iterator-based. // TODO: Save/Load `SignerState` to/from disk. // TODO: Add self-destruct feature for clearing all secret and private data. +// TODO: Compress the `SyncResponse` data before sending (improves privacy and bandwidth). use crate::{ asset::{Asset, AssetId, AssetMap, AssetValue}, @@ -110,16 +111,20 @@ pub type ReceivingKeyResult<C, S> = Result<ReceivingKey<C>, <S as Connection<C>> /// /// This `struct` is created by the [`sync`](Connection::sync) method on [`Connection`]. /// See its documentation for more. +#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] pub struct SyncResponse { - /// Updates to the Asset Distribution - pub assets: Vec<Asset>, + /// Assets Deposited + pub deposit: Vec<Asset>, + + /// Assets Withdrawn + pub withdraw: Vec<Asset>, } impl SyncResponse { - /// Builds a new [`SyncResponse`] from `assets`. + /// Builds a new [`SyncResponse`] from `deposit` and `withdraw`. #[inline] - pub fn new(assets: Vec<Asset>) -> Self { - Self { assets } + pub fn new(deposit: Vec<Asset>, withdraw: Vec<Asset>) -> Self { + Self { deposit, withdraw } } } @@ -261,7 +266,7 @@ where } /// Signer State -struct SignerState<C> +pub struct SignerState<C> where C: Configuration, { @@ -272,7 +277,7 @@ where utxo_set: C::UtxoSet, /// Asset Distribution - assets: C::AssetMap, + pub assets: C::AssetMap, /// Random Number Generator rng: C::Rng, @@ -298,7 +303,8 @@ where utxo: Utxo<C>, encrypted_note: EncryptedNote<C>, void_numbers: &mut Vec<VoidNumber<C>>, - assets: &mut Vec<Asset>, + deposit: &mut Vec<Asset>, + withdraw: &mut Vec<Asset>, ) -> Result<(), Error<C>> { let mut finder = DecryptedMessage::find(encrypted_note); if let Some(ViewKeySelection { @@ -324,15 +330,18 @@ where if let Some(void_number_index) = void_numbers.iter().position(move |v| v == &void_number) { + println!("REMOVE: {:?}", asset); void_numbers.remove(void_number_index); self.utxo_set.remove_proof(&utxo); self.assets .remove((index.spend, ephemeral_public_key), asset); + withdraw.push(asset); } else { - assets.push(asset); + println!("INSERT: {:?}", asset); + self.utxo_set.insert(&utxo); self.assets .insert((index.spend, ephemeral_public_key), asset); - self.utxo_set.insert(&utxo); + deposit.push(asset); return Ok(()); } } @@ -353,22 +362,24 @@ where I: Iterator<Item = (Utxo<C>, EncryptedNote<C>)>, { // TODO: Do this loop in parallel. - let mut assets = Vec::new(); + let mut deposit = Vec::new(); + let mut withdraw = Vec::new(); for (utxo, encrypted_note) in inserts { self.insert_next_item( parameters, utxo, encrypted_note, &mut void_numbers, - &mut assets, + &mut deposit, + &mut withdraw, )?; } // FIXME: Do we need to check the void numbers which survived the above loop? self.utxo_set.commit(); - Ok(SyncResponse::new(assets)) + Ok(SyncResponse::new(deposit, withdraw)) } - /// Builds the pre-sender associated to `spend_index`, `ephemeral_key`, and `asset`. + /// Builds the pre-sender associated to `key` and `asset`. #[inline] fn build_pre_sender( &self, @@ -385,7 +396,7 @@ where )) } - /// Builds the pre-receiver associated to `spend_index`, `view_index`, and `asset`. + /// Builds the receiver for `asset`. #[inline] fn build_receiver( &mut self, @@ -545,7 +556,9 @@ where let zeroes = self.assets.zeroes(needed_zeroes, asset_id); needed_zeroes -= zeroes.len(); for zero in zeroes { - pre_senders.push(self.build_pre_sender(parameters, zero, Asset::zero(asset_id))?); + let pre_sender = self.build_pre_sender(parameters, zero, Asset::zero(asset_id))?; + pre_sender.insert_utxo(&mut self.utxo_set); + pre_senders.push(pre_sender); } if needed_zeroes == 0 { return Ok(()); @@ -563,6 +576,7 @@ where for _ in 0..needed_mints { let (mint, pre_sender) = self.mint_zero(parameters, asset_id)?; posts.push(self.mint_post(parameters, &proving_context.mint, mint)?); + pre_sender.insert_utxo(&mut self.utxo_set); pre_senders.push(pre_sender); } Ok(()) @@ -648,7 +662,7 @@ where parameters: SignerParameters<C>, /// Signer State - state: SignerState<C>, + pub state: SignerState<C>, } impl<C> Signer<C> @@ -810,7 +824,7 @@ where /// Returns a [`ReceivingKey`] for `self` to receive assets with `index`. #[inline] - pub fn receiving_key(&self, index: Index<C>) -> ReceivingKeyResult<C, Self> { + fn compute_receiving_key(&self, index: Index<C>) -> ReceivingKeyResult<C, Self> { let keypair = self.state.account().keypair(index)?; Ok(SpendingKey::new(keypair.spend, keypair.view) .derive(&self.parameters.parameters.key_agreement)) @@ -844,6 +858,6 @@ where #[inline] fn receiving_key(&mut self, index: Index<C>) -> LocalBoxFuture<ReceivingKeyResult<C, Self>> { - Box::pin(async move { (&*self).receiving_key(index) }) + Box::pin(async move { self.compute_receiving_key(index) }) } } diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 6cd72fcb7..0b9608a3e 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -24,7 +24,7 @@ use crate::{ }, wallet::{ ledger::{self, PullResponse, PushResponse}, - signer::{self, SignResponse}, + signer::{self, SignResponse, SyncResponse}, }, }; use alloc::{ @@ -70,6 +70,23 @@ pub trait BalanceState: Default { /// This method does not check if withdrawing `asset` from the balance state would cause an /// overdraw, but if it were to overdraw, this method must panic. fn withdraw_unchecked(&mut self, asset: Asset); + + /// Withdraws every asset in `assets` from the balance state without checking if it would + /// overdraw. + /// + /// # Panics + /// + /// This method does not check if withdrawing `asset` from the balance state would cause an + /// overdraw, but if it were to overdraw, this method must panic. + #[inline] + fn withdraw_all_unchecked<I>(&mut self, assets: I) + where + I: IntoIterator<Item = Asset>, + { + assets + .into_iter() + .for_each(move |a| self.withdraw_unchecked(a)) + } } /// Performs an unchecked withdraw on `balance`, panicking on overflow. @@ -173,7 +190,7 @@ where checkpoint: L::Checkpoint, /// Signer Connection - signer: S, + pub signer: S, /// Balance State assets: B, @@ -207,16 +224,22 @@ where Self::new(ledger, Default::default(), signer, Default::default()) } + /// Returns true if `self` contains at least `asset.value` of the asset of kind `asset.id`. + #[inline] + pub fn contains(&self, asset: Asset) -> bool { + self.assets.contains(asset) + } + /// Returns the current balance associated with this `id`. #[inline] pub fn balance(&self, id: AssetId) -> AssetValue { self.assets.balance(id) } - /// Returns true if `self` contains at least `asset.value` of the asset of kind `asset.id`. + /// Returns the entire balance state associated to `self`. #[inline] - pub fn contains(&self, asset: Asset) -> bool { - self.assets.contains(asset) + pub fn assets(&self) -> &B { + &self.assets } /// Returns the [`Checkpoint`](ledger::Connection::Checkpoint) representing the current state @@ -241,13 +264,13 @@ where if checkpoint < self.checkpoint { return Err(Error::InconsistentCheckpoint); } - self.assets.deposit_all( - self.signer - .sync(receivers, senders) - .await - .map_err(Error::SignerError)? - .assets, - ); + let SyncResponse { deposit, withdraw } = self + .signer + .sync(receivers, senders) + .await + .map_err(Error::SignerError)?; + self.assets.deposit_all(deposit); + self.assets.withdraw_all_unchecked(withdraw); self.checkpoint = checkpoint; Ok(()) } @@ -267,7 +290,7 @@ where /// Posts a transaction to the ledger, returning `true` if the `transaction` was successfully /// saved onto the ledger. This method automatically synchronizes with the ledger before - /// posting. To amortize the cost of future calls to [`post`](Self::post), the + /// posting, _but not after_. To amortize the cost of future calls to [`post`](Self::post), the /// [`sync`](Self::sync) method can be used to synchronize with the ledger. /// /// # Failure Conditions @@ -284,25 +307,15 @@ where #[inline] pub async fn post(&mut self, transaction: Transaction<C>) -> Result<bool, Error<C, L, S>> { self.sync().await?; - let transaction_kind = self - .check(&transaction) + self.check(&transaction) .map_err(Error::InsufficientBalance)?; let SignResponse { posts } = self .signer .sign(transaction) .await .map_err(Error::SignerError)?; - match self.ledger.push(posts).await { - Ok(PushResponse { success: true }) => { - match transaction_kind { - TransactionKind::Deposit(asset) => self.assets.deposit(asset), - TransactionKind::Withdraw(asset) => self.assets.withdraw_unchecked(asset), - } - Ok(true) - } - Ok(PushResponse { success: false }) => Ok(false), - Err(err) => Err(Error::LedgerError(err)), - } + let PushResponse { success } = self.ledger.push(posts).await.map_err(Error::LedgerError)?; + Ok(success) } /// Returns a [`ReceivingKey`] for `self` to receive assets with `index`. diff --git a/manta-crypto/src/encryption.rs b/manta-crypto/src/encryption.rs index 33e37f979..a85cdd52f 100644 --- a/manta-crypto/src/encryption.rs +++ b/manta-crypto/src/encryption.rs @@ -323,3 +323,60 @@ where None } } + +/// Testing Framework +#[cfg(feature = "test")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] +pub mod test { + use super::*; + + /// Tests if symmetric encryption of `plaintext` using `key` returns the same plaintext on + /// decryption. + #[inline] + pub fn symmetric_encryption<S>(key: S::Key, plaintext: S::Plaintext) + where + S: SymmetricKeyEncryptionScheme, + S::Key: Clone, + S::Plaintext: Clone + Debug + PartialEq, + { + assert_eq!( + S::decrypt(key.clone(), &S::encrypt(key, plaintext.clone())) + .expect("Decryption of encrypted message should have succeeded."), + plaintext, + "Plaintext should have matched decrypted-encrypted plaintext." + ) + } + + /// Tests if hybrid encryption of `plaintext` using `public_key` returns the same plaintext on + /// decryption. + #[inline] + pub fn hybrid_encryption<H>( + parameters: &H::KeyAgreementScheme, + secret_key: &SecretKey<H>, + ephemeral_secret_key: &SecretKey<H>, + plaintext: H::Plaintext, + ) where + H: HybridPublicKeyEncryptionScheme, + H::Plaintext: Clone + Debug + PartialEq, + H::Ciphertext: Debug, + PublicKey<H>: Debug + PartialEq, + { + let decrypted_message = EncryptedMessage::<H>::new( + parameters, + &parameters.derive(secret_key, &mut ()), + ephemeral_secret_key, + plaintext.clone(), + ) + .decrypt(parameters, secret_key) + .expect("Decryption of encrypted message should have succeeded."); + assert_eq!( + decrypted_message.plaintext, plaintext, + "Plaintext should have matched decrypted-encrypted plaintext." + ); + assert_eq!( + decrypted_message.ephemeral_public_key, + parameters.derive(ephemeral_secret_key, &mut ()), + "Decrypted message should have included the correct ephemeral public key.", + ); + } +} diff --git a/manta-crypto/src/key.rs b/manta-crypto/src/key.rs index 386b1143f..861f22f50 100644 --- a/manta-crypto/src/key.rs +++ b/manta-crypto/src/key.rs @@ -159,3 +159,25 @@ pub trait KeyAgreementScheme<COM = ()> { self.agree(&secret_key, &public_key, compiler) } } + +/// Testing Framework +#[cfg(feature = "test")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] +pub mod test { + use super::*; + use core::fmt::Debug; + + /// Tests if the `agreement` property is satisfied for `K`. + #[inline] + pub fn key_agreement<K>(parameters: &K, lhs: &K::SecretKey, rhs: &K::SecretKey) + where + K: KeyAgreementScheme, + K::SharedSecret: Debug + PartialEq, + { + assert_eq!( + parameters.agree(lhs, &parameters.derive(rhs, &mut ()), &mut ()), + parameters.agree(rhs, &parameters.derive(lhs, &mut ()), &mut ()), + "Key agreement schemes should satisfy the agreement property." + ) + } +} diff --git a/manta-crypto/src/merkle_tree/forest.rs b/manta-crypto/src/merkle_tree/forest.rs index 0841b29c2..f2f1a6d51 100644 --- a/manta-crypto/src/merkle_tree/forest.rs +++ b/manta-crypto/src/merkle_tree/forest.rs @@ -19,6 +19,8 @@ // FIXME: Replace `N` parameter on `FixedIndex` with an associated value in the trait when // `generic_const_exprs` is stabilized and get rid of `ConstantWidthForest`. // FIXME: Reduce code duplication between this and `tree.rs` code. +// TODO: What should we do if a tree fills before others? Need some way of chosing a second option +// for a leaf. use crate::{ accumulator::{ @@ -32,9 +34,9 @@ use crate::{ InnerDigest, LeafDigest, WithProofs, }, }; -use alloc::vec::Vec; +use alloc::{boxed::Box, vec::Vec}; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; -use manta_util::{into_array_unchecked, pointer::PointerFamily, Rollback}; +use manta_util::{into_boxed_array_unchecked, pointer::PointerFamily, Rollback}; /// Merkle Forest Configuration pub trait Configuration: tree::Configuration { @@ -220,6 +222,14 @@ where pub fn is_empty(&self) -> bool { self.forest.is_empty() } + + /// Inserts `leaf` at the next available leaf node of the tree corresponding with `leaf`, + /// returning `false` if the leaf could not be inserted because its tree has exhausted its + /// capacity. + #[inline] + pub fn push(&mut self, leaf: &Leaf<C>) -> bool { + self.forest.get_tree_mut(leaf).push(&self.parameters, leaf) + } } impl<C, F> AsMut<F> for MerkleForest<C, F> @@ -352,7 +362,6 @@ pub type TreeArrayMerkleForest<C, T, const N: usize> = MerkleForest<C, TreeArray #[derive(derivative::Derivative)] #[derivative( Clone(bound = "T: Clone"), - Copy(bound = "T: Copy"), Debug(bound = "T: Debug"), Eq(bound = "T: Eq"), Hash(bound = "T: Hash"), @@ -365,7 +374,10 @@ where T: Tree<C>, { /// Array of Trees - array: [T; N], + /// + /// Typically, even for reasonable `N`, the size of this array is too big to fit on the stack, + /// so we wrap it in a box to store on the heap. + array: Box<[T; N]>, /// Type Parameter Marker __: PhantomData<C>, @@ -383,6 +395,18 @@ where } } +impl<C, T, const N: usize> AsMut<[T; N]> for TreeArray<C, T, N> +where + C: Configuration + ?Sized, + C::Index: FixedIndex<N>, + T: Tree<C>, +{ + #[inline] + fn as_mut(&mut self) -> &mut [T; N] { + &mut self.array + } +} + impl<C, T, const N: usize> Default for TreeArray<C, T, N> where C: Configuration + ?Sized, @@ -391,11 +415,12 @@ where { #[inline] fn default() -> Self { - Self::from(into_array_unchecked( + Self::from(into_boxed_array_unchecked( (0..N) .into_iter() .map(move |_| Default::default()) - .collect::<Vec<_>>(), + .collect::<Vec<_>>() + .into_boxed_slice(), )) } } @@ -410,11 +435,12 @@ where #[inline] fn new(parameters: &Parameters<C>) -> Self { - Self::from(into_array_unchecked( + Self::from(into_boxed_array_unchecked( (0..N) .into_iter() .map(move |_| T::new(parameters)) - .collect::<Vec<_>>(), + .collect::<Vec<_>>() + .into_boxed_slice(), )) } @@ -456,6 +482,18 @@ where { #[inline] fn from(array: [T; N]) -> Self { + Self::from(Box::new(array)) + } +} + +impl<C, T, const N: usize> From<Box<[T; N]>> for TreeArray<C, T, N> +where + C: Configuration + ?Sized, + C::Index: FixedIndex<N>, + T: Tree<C>, +{ + #[inline] + fn from(array: Box<[T; N]>) -> Self { Self { array, __: PhantomData, @@ -476,14 +514,14 @@ where { #[inline] fn rollback(&mut self) { - for tree in &mut self.forest.array { + for tree in self.forest.as_mut() { tree.reset_fork(&self.parameters); } } #[inline] fn commit(&mut self) { - for tree in &mut self.forest.array { + for tree in self.forest.as_mut() { tree.merge_fork(&self.parameters); } } diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index f0a532d98..22575ccc6 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -27,6 +27,7 @@ use crate::{ key::TestnetKeySecret, }; use ark_ff::{PrimeField, ToConstraintField}; +use ark_serialize::CanonicalSerialize; use blake2::{ digest::{Update, VariableOutput}, Blake2sVar, @@ -614,9 +615,11 @@ impl merkle_tree::forest::Configuration for MerkleTreeConfiguration { #[inline] fn tree_index(leaf: &merkle_tree::Leaf<Self>) -> Self::Index { let mut hasher = Blake2sVar::new(1).unwrap(); - hasher.update( - &ark_ff::to_bytes!(leaf.0).expect("Converting to bytes is not allowed to fail."), - ); + let mut buffer = Vec::new(); + leaf.0 + .serialize_unchecked(&mut buffer) + .expect("Serializing is not allowed to fail."); + hasher.update(&buffer); let mut result = [0]; hasher .finalize_variable(&mut result) diff --git a/manta-pay/src/crypto/ecc.rs b/manta-pay/src/crypto/ecc.rs index 4a5df5c2e..0c987d062 100644 --- a/manta-pay/src/crypto/ecc.rs +++ b/manta-pay/src/crypto/ecc.rs @@ -103,7 +103,11 @@ pub mod arkworks { { #[inline] fn as_bytes(&self) -> Vec<u8> { - ark_ff::to_bytes!(&self.0).expect("Byte conversion does not fail.") + let mut buffer = Vec::new(); + self.0 + .serialize_unchecked(&mut buffer) + .expect("Serialization does not fail."); + buffer } } diff --git a/manta-pay/src/crypto/encryption.rs b/manta-pay/src/crypto/encryption.rs index 3e78e793a..2bcd90d53 100644 --- a/manta-pay/src/crypto/encryption.rs +++ b/manta-pay/src/crypto/encryption.rs @@ -49,9 +49,7 @@ pub mod aes { impl<const P: usize, const C: usize> SymmetricKeyEncryptionScheme for AesGcm<P, C> { type Key = [u8; 32]; - type Plaintext = [u8; P]; - type Ciphertext = [u8; C]; #[inline] @@ -73,4 +71,26 @@ pub mod aes { .map(into_array_unchecked) } } + + /// Test Suite + #[cfg(test)] + mod test { + use super::*; + use manta_accounting::asset::Asset; + use manta_crypto::{encryption, rand::RngCore}; + use rand::thread_rng; + + /// Tests if symmetric encryption of [`Asset`] decrypts properly. + #[test] + fn asset_encryption() { + let mut rng = thread_rng(); + let mut key = [0; 32]; + rng.fill_bytes(&mut key); + let mut plaintext = [0; Asset::SIZE]; + rng.fill_bytes(&mut plaintext); + encryption::test::symmetric_encryption::< + AesGcm<{ Asset::SIZE }, { ciphertext_size(Asset::SIZE) }>, + >(key, plaintext); + } + } } diff --git a/manta-pay/src/test/ledger.rs b/manta-pay/src/test/ledger.rs index b2116842d..168e772e5 100644 --- a/manta-pay/src/test/ledger.rs +++ b/manta-pay/src/test/ledger.rs @@ -106,7 +106,7 @@ impl Ledger { Self { void_numbers: Default::default(), utxos: Default::default(), - shards: Default::default(), + shards: (0..=255).map(move |i| (i, Default::default())).collect(), utxo_forest: UtxoMerkleForest::new(utxo_forest_parameters), accounts: Default::default(), verifying_context, @@ -183,10 +183,11 @@ impl ReceiverLedger<Config> for Ledger { let shard = self .shards .get_mut(&MerkleTreeConfiguration::tree_index(&utxo.0)) - .unwrap(); + .expect("All shards exist when the ledger is constructed."); let len = shard.len(); shard.insert(len as u64, (utxo.0, note)); self.utxos.insert(utxo.0); + self.utxo_forest.push(&utxo.0); } } @@ -374,12 +375,8 @@ impl Connection<Config> for LedgerConnection { Box::pin(async { let ledger = self.ledger.read().await; let mut receivers = Vec::new(); - for (mut index, shard) in checkpoint - .receiver_index - .iter() - .copied() - .zip(ledger.shards.values()) - { + for (i, mut index) in checkpoint.receiver_index.iter().copied().enumerate() { + let shard = &ledger.shards[&(i as u8)]; while let Some(entry) = shard.get(&(index as u64)) { receivers.push(*entry); index += 1; @@ -444,8 +441,9 @@ mod test { }; use async_std::task; use manta_accounting::{ + asset::Asset, key::{AccountTable, HierarchicalKeyDerivationScheme}, - transfer, + transfer::{self, canonical::Transaction}, }; use manta_crypto::rand::{Rand, SeedableRng}; use rand::thread_rng; @@ -454,6 +452,11 @@ mod test { /// #[async_std::test] async fn test() { + let directory = task::spawn_blocking(tempfile::tempdir) + .await + .expect("Unable to generate temporary test directory."); + async_std::println!("Temporary Directory: {:?}", directory).await; + let mut rng = thread_rng(); let parameters = rng.gen(); let utxo_set_parameters = rng.gen(); @@ -465,10 +468,6 @@ mod test { ) .expect("Failed to generate contexts."); - let directory = task::spawn_blocking(tempfile::tempdir) - .await - .expect("Unable to generate temporary test directory."); - let cache = OnDiskMultiProvingContext::new(directory.path()); cache .save(proving_context) @@ -480,11 +479,13 @@ mod test { ledger.set_public_balance(AccountId(1), AssetId(0), AssetValue(1000)); ledger.set_public_balance(AccountId(2), AssetId(0), AssetValue(100)); - println!("{:#?}", ledger); + async_std::println!("Ledger: {:?}", ledger.accounts).await; let ledger = Rc::new(RwLock::new(ledger)); - let mut wallet = Wallet::empty( + async_std::println!("Building Wallets").await; + + let mut alice = Wallet::empty( LedgerConnection::new(AccountId(0), ledger.clone()), Signer::new( AccountTable::new( @@ -501,7 +502,67 @@ mod test { ), ); - let _ = wallet.sync().await; + let mut bob = Wallet::empty( + LedgerConnection::new(AccountId(1), ledger.clone()), + Signer::new( + AccountTable::new( + TestnetKeySecret::new( + Mnemonic::random(&mut rng, Language::English), + "password", + ) + .map(), + ), + cache.clone(), + parameters.clone(), + wallet::UtxoSet::new(utxo_set_parameters.clone()), + ChaCha20Rng::from_rng(&mut rng).expect("Failed to sample PRNG for signer."), + ), + ); + + let bob_public_key = bob + .receiving_key(Default::default()) + .await + .expect("Unable to get Bob's public key."); + + async_std::println!("Alice [wallet]: {:?}", alice.assets()).await; + async_std::println!("Alice [signer]: {:?}", alice.signer.state.assets).await; + async_std::println!( + "{:?}", + alice + .post(Transaction::Mint(Asset::new(AssetId(0), AssetValue(100)))) + .await + .expect("Unable to mint.") + ) + .await; + + alice.sync().await.expect(""); + + async_std::println!("Alice [wallet]: {:?}", alice.assets()).await; + async_std::println!("Alice [signer]: {:?}", alice.signer.state.assets).await; + async_std::println!( + "{:?}", + alice + .post(Transaction::PrivateTransfer( + Asset::new(AssetId(0), AssetValue(10)), + bob_public_key + )) + .await + .expect("Unable to private transfer.") + ) + .await; + + alice + .sync() + .await + .expect("Unable to synchronise with the ledger."); + bob.sync() + .await + .expect("Unable to synchronise with the ledger."); + + async_std::println!("Alice [wallet]: {:?}", alice.assets()).await; + async_std::println!("Alice [signer]: {:?}", alice.signer.state.assets).await; + async_std::println!("Bob [wallet]: {:?}", bob.assets()).await; + async_std::println!("Bob [signer]: {:?}", bob.signer.state.assets).await; task::spawn_blocking(move || directory.close()) .await diff --git a/manta-pay/src/wallet/cache.rs b/manta-pay/src/wallet/cache.rs index 780e90d5f..8a45b58fd 100644 --- a/manta-pay/src/wallet/cache.rs +++ b/manta-pay/src/wallet/cache.rs @@ -19,11 +19,13 @@ use crate::config::{MultiProvingContext, ProvingContext}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; use async_std::{ - fs, io, + io, path::{Path, PathBuf}, + task, }; use core::marker::PhantomData; use manta_util::{cache::CachedResource, future::LocalBoxFuture}; +use std::fs::{File, OpenOptions}; /// Caching Error #[derive(Debug)] @@ -93,34 +95,45 @@ impl OnDiskMultiProvingContext { #[inline] async fn read_context<P>(path: P) -> Result<ProvingContext, Error> where - P: AsRef<Path>, + P: 'static + AsRef<Path> + Send, { - Ok(ProvingContext::deserialize_unchecked( - &mut fs::read(path).await?.as_slice(), - )?) + Ok(task::spawn_blocking(move || { + File::open(path.as_ref()) + .map_err(Error::Io) + .and_then(move |f| { + ProvingContext::deserialize_unchecked(f).map_err(Error::Serialization) + }) + }) + .await?) } /// Writes `context` to `path`. #[inline] - async fn write_context<P>(path: P, context: &ProvingContext) -> Result<(), Error> + async fn write_context<P>(path: P, context: ProvingContext) -> Result<(), Error> where - P: AsRef<Path>, + P: 'static + AsRef<Path> + Send, { - let mut buffer = Vec::new(); - context.serialize(&mut buffer.as_mut_slice())?; - Ok(fs::write(path, buffer).await?) + Ok(task::spawn_blocking(move || { + OpenOptions::new() + .write(true) + .create(true) + .open(path.as_ref()) + .map_err(Error::Io) + .and_then(move |f| context.serialize_unchecked(f).map_err(Error::Serialization)) + }) + .await?) } /// Saves the `context` to the on-disk directory. This method _does not_ write `context` into /// the cache. #[inline] pub async fn save(&self, context: MultiProvingContext) -> Result<(), Error> { - let mint_path = self.directory.join("mint.bin"); - let private_transfer_path = self.directory.join("private-transfer.bin"); - let reclaim_path = self.directory.join("reclaim.bin"); - Self::write_context(mint_path, &context.mint).await?; - Self::write_context(private_transfer_path, &context.private_transfer).await?; - Self::write_context(reclaim_path, &context.reclaim).await?; + let mint_path = self.directory.join("mint.pk"); + let private_transfer_path = self.directory.join("private-transfer.pk"); + let reclaim_path = self.directory.join("reclaim.pk"); + Self::write_context(mint_path, context.mint).await?; + Self::write_context(private_transfer_path, context.private_transfer).await?; + Self::write_context(reclaim_path, context.reclaim).await?; Ok(()) } } @@ -132,9 +145,9 @@ impl CachedResource<MultiProvingContext> for OnDiskMultiProvingContext { #[inline] fn aquire(&mut self) -> LocalBoxFuture<Result<Self::ReadingKey, Self::Error>> { Box::pin(async { - let mint_path = self.directory.join("mint.bin"); - let private_transfer_path = self.directory.join("private-transfer.bin"); - let reclaim_path = self.directory.join("reclaim.bin"); + let mint_path = self.directory.join("mint.pk"); + let private_transfer_path = self.directory.join("private-transfer.pk"); + let reclaim_path = self.directory.join("reclaim.pk"); self.context = Some(MultiProvingContext { mint: Self::read_context(mint_path).await?, private_transfer: Self::read_context(private_transfer_path).await?, diff --git a/manta-util/src/array.rs b/manta-util/src/array.rs index 3b14e69e0..cecc1ac79 100644 --- a/manta-util/src/array.rs +++ b/manta-util/src/array.rs @@ -16,7 +16,7 @@ //! Array Utilities -use alloc::vec::Vec; +use alloc::{boxed::Box, vec::Vec}; use core::convert::TryInto; /// Performs the [`TryInto`] conversion into an array without checking if the conversion succeeded. @@ -34,6 +34,22 @@ where } } +/// Performs the [`TryInto`] conversion into a boxed array without checking if the conversion +/// succeeded. +#[inline] +pub fn into_boxed_array_unchecked<T, V, const N: usize>(v: V) -> Box<[T; N]> +where + V: TryInto<Box<[T; N]>>, +{ + match v.try_into() { + Ok(array) => array, + _ => unreachable!( + "Input did not have the correct length to match the output slice of length {:?}.", + N, + ), + } +} + /// Maps `f` over the `array`. #[inline] pub fn array_map<T, U, F, const N: usize>(array: [T; N], f: F) -> [U; N] From a994c0f73b78c2728ce2daf6b3794a774073790c Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 12 Jan 2022 02:51:04 -0500 Subject: [PATCH 181/275] fix: make sure that all void numbers are checked in signer sync --- manta-accounting/src/asset.rs | 13 ++++++++ manta-accounting/src/wallet/signer.rs | 48 +++++++++++++++++++++++---- manta-accounting/src/wallet/state.rs | 2 +- manta-pay/src/test/ledger.rs | 5 +-- 4 files changed, 57 insertions(+), 11 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 9edbbe147..05b5bd366 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -631,6 +631,11 @@ pub trait AssetMap: Default { iter.into_iter() .for_each(move |(key, asset)| self.remove(key, asset)); } + + /// Removes elements from `self` if they return `true` from `f`. + fn remove_if<F>(&mut self, f: F) + where + F: FnMut(&Self::Key, &[Asset]) -> bool; } /// Implements [`AssetMap`] for map types. @@ -703,6 +708,14 @@ macro_rules! impl_asset_map_for_maps_body { } } } + + #[inline] + fn remove_if<F>(&mut self, mut f: F) + where + F: FnMut(&Self::Key, &[Asset]) -> bool, + { + self.retain(move |key, assets| !f(key, &assets)); + } }; } diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 29bde9909..9709ee270 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -266,7 +266,7 @@ where } /// Signer State -pub struct SignerState<C> +struct SignerState<C> where C: Configuration, { @@ -277,7 +277,7 @@ where utxo_set: C::UtxoSet, /// Asset Distribution - pub assets: C::AssetMap, + assets: C::AssetMap, /// Random Number Generator rng: C::Rng, @@ -330,14 +330,12 @@ where if let Some(void_number_index) = void_numbers.iter().position(move |v| v == &void_number) { - println!("REMOVE: {:?}", asset); void_numbers.remove(void_number_index); self.utxo_set.remove_proof(&utxo); self.assets .remove((index.spend, ephemeral_public_key), asset); withdraw.push(asset); } else { - println!("INSERT: {:?}", asset); self.utxo_set.insert(&utxo); self.assets .insert((index.spend, ephemeral_public_key), asset); @@ -374,7 +372,45 @@ where &mut withdraw, )?; } - // FIXME: Do we need to check the void numbers which survived the above loop? + + for void_number in void_numbers { + // FIXME: Use default account method like everywhere else. + self.assets + .remove_if(|(index, ephemeral_public_key), assets| { + assets.iter().any(|asset| { + match self + .account_table + .get(Default::default()) + .unwrap() + .spend_key(*index) + { + Ok(secret_key) => { + let utxo = C::utxo( + &parameters.key_agreement, + &parameters.utxo_commitment, + &secret_key, + ephemeral_public_key, + asset, + ); + let known_void_number = C::void_number( + &parameters.void_number_hash, + &utxo, + &secret_key, + ); + if void_number == known_void_number { + self.utxo_set.remove_proof(&utxo); + withdraw.push(*asset); + true + } else { + false + } + } + _ => false, + } + }) + }); + } + self.utxo_set.commit(); Ok(SyncResponse::new(deposit, withdraw)) } @@ -662,7 +698,7 @@ where parameters: SignerParameters<C>, /// Signer State - pub state: SignerState<C>, + state: SignerState<C>, } impl<C> Signer<C> diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 0b9608a3e..85b0345b0 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -190,7 +190,7 @@ where checkpoint: L::Checkpoint, /// Signer Connection - pub signer: S, + signer: S, /// Balance State assets: B, diff --git a/manta-pay/src/test/ledger.rs b/manta-pay/src/test/ledger.rs index 168e772e5..c73d0cbf3 100644 --- a/manta-pay/src/test/ledger.rs +++ b/manta-pay/src/test/ledger.rs @@ -418,6 +418,7 @@ impl Connection<Config> for LedgerConnection { Some(TransferShape::Reclaim) => (vec![], vec![self.account]), _ => return Ok(PushResponse { success: false }), }; + async_std::println!("PUSH [receivers]: {:?}", post.receiver_posts).await; match post.validate(sources, sinks, &*ledger) { Ok(posting_key) => { posting_key.post(&(), &mut *ledger); @@ -525,7 +526,6 @@ mod test { .expect("Unable to get Bob's public key."); async_std::println!("Alice [wallet]: {:?}", alice.assets()).await; - async_std::println!("Alice [signer]: {:?}", alice.signer.state.assets).await; async_std::println!( "{:?}", alice @@ -538,7 +538,6 @@ mod test { alice.sync().await.expect(""); async_std::println!("Alice [wallet]: {:?}", alice.assets()).await; - async_std::println!("Alice [signer]: {:?}", alice.signer.state.assets).await; async_std::println!( "{:?}", alice @@ -560,9 +559,7 @@ mod test { .expect("Unable to synchronise with the ledger."); async_std::println!("Alice [wallet]: {:?}", alice.assets()).await; - async_std::println!("Alice [signer]: {:?}", alice.signer.state.assets).await; async_std::println!("Bob [wallet]: {:?}", bob.assets()).await; - async_std::println!("Bob [signer]: {:?}", bob.signer.state.assets).await; task::spawn_blocking(move || directory.close()) .await From 04a51dfacc7e9e248ca4d6133e3f9132fa8d5309 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 12 Jan 2022 12:05:29 -0500 Subject: [PATCH 182/275] feat: add back some synchronization checks between wallet/signer --- manta-accounting/src/key.rs | 6 ++ manta-accounting/src/wallet/ledger.rs | 11 ++- manta-accounting/src/wallet/signer.rs | 98 +++++++++++++++------------ manta-accounting/src/wallet/state.rs | 14 +++- manta-pay/src/test/ledger.rs | 26 ++++++- 5 files changed, 105 insertions(+), 50 deletions(-) diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index dee7b8f53..6094a8e28 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -599,6 +599,12 @@ where )) } + /// Returns the account keys for the default account. + #[inline] + pub fn get_default(&self) -> AccountKeys<H> { + self.get(Default::default()).unwrap() + } + /// Adds a new account to the map, returning the new account parameter. #[inline] pub fn create_account(&mut self) -> H::Account { diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs index 94ed125f2..fd9b75502 100644 --- a/manta-accounting/src/wallet/ledger.rs +++ b/manta-accounting/src/wallet/ledger.rs @@ -21,13 +21,22 @@ use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; use manta_util::future::LocalBoxFuture; +/// Ledger Checkpoint +pub trait Checkpoint: Default + PartialOrd { + /// Returns the index into the receiver set for the ledger. + /// + /// This index is used to ensure that wallets are synchronized even during connection failures + /// or other errors during synchronization. + fn receiver_index(&self) -> usize; +} + /// Ledger Source Connection pub trait Connection<C> where C: Configuration, { /// Ledger State Checkpoint Type - type Checkpoint: Default + PartialOrd; + type Checkpoint: Checkpoint; /// Receiver Chunk Iterator Type type ReceiverChunk: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>; diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 9709ee270..ccd392a85 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -16,7 +16,7 @@ //! Wallet Signer -// TODO: Add wallet recovery i.e. remove the assumption that a new signer represents a completely +// FIXME: Add wallet recovery i.e. remove the assumption that a new signer represents a completely // new derived secret key generator. // TODO: Should have a mode on the signer where we return a generic error which reveals no detail // about what went wrong during signing. The kind of error returned from a signing could @@ -26,10 +26,11 @@ // TODO: Save/Load `SignerState` to/from disk. // TODO: Add self-destruct feature for clearing all secret and private data. // TODO: Compress the `SyncResponse` data before sending (improves privacy and bandwidth). +// TODO: Should we split the errors into two groups, one for `sync` and one for `sign`? use crate::{ asset::{Asset, AssetId, AssetMap, AssetValue}, - key::{self, AccountKeys, HierarchicalKeyDerivationScheme, ViewKeySelection}, + key::{self, HierarchicalKeyDerivationScheme, ViewKeySelection}, transfer::{ self, batch::Join, @@ -75,6 +76,7 @@ where /// returning an updated asset distribution. fn sync<'s, I, R>( &'s mut self, + starting_index: usize, inserts: I, removes: R, ) -> LocalBoxFuture<'s, SyncResult<C, Self>> @@ -201,11 +203,11 @@ pub type ProvingContextCacheError<C> = /// Signer Error #[derive(derivative::Derivative)] #[derivative(Debug(bound = r#" - key::Error<C::HierarchicalKeyDerivationScheme>: Debug, - ProvingContextCacheError<C>: Debug, - ProofSystemError<C>: Debug, - CE: Debug - "#))] + key::Error<C::HierarchicalKeyDerivationScheme>: Debug, + ProvingContextCacheError<C>: Debug, + ProofSystemError<C>: Debug, + CE: Debug +"#))] pub enum Error<C, CE = Infallible> where C: Configuration, @@ -222,6 +224,9 @@ where /// Insufficient Balance InsufficientBalance(Asset), + /// Inconsistent Synchronization + InconsistentSynchronization, + /// Proof System Error ProofSystemError(ProofSystemError<C>), @@ -271,7 +276,13 @@ where C: Configuration, { /// Account Table - account_table: AccountTable<C>, + /// + /// # Note + /// + /// For now, we only use the default account, and the rest of the storage data is related to + /// this account. Eventually, we want to have a global `utxo_set` for all accounts and a local + /// `assets` map for each account. + accounts: AccountTable<C>, /// UTXO Set utxo_set: C::UtxoSet, @@ -287,13 +298,6 @@ impl<C> SignerState<C> where C: Configuration, { - /// Returns the hierarchical key indices for the current account. - #[inline] - fn account(&self) -> AccountKeys<C::HierarchicalKeyDerivationScheme> { - // FIXME: Implement multiple accounts. - self.account_table.get(Default::default()).unwrap() - } - /// Inserts the new `utxo`-`encrypted_note` pair if a known key can decrypt the note and /// validate the utxo. #[inline] @@ -316,7 +320,8 @@ where ephemeral_public_key, }, }) = self - .account() + .accounts + .get_default() .find_index(|k| finder.decrypt(&parameters.key_agreement, k)) .map_err(key::Error::KeyDerivationError)? { @@ -377,13 +382,8 @@ where // FIXME: Use default account method like everywhere else. self.assets .remove_if(|(index, ephemeral_public_key), assets| { - assets.iter().any(|asset| { - match self - .account_table - .get(Default::default()) - .unwrap() - .spend_key(*index) - { + assets.iter().any( + |asset| match self.accounts.get_default().spend_key(*index) { Ok(secret_key) => { let utxo = C::utxo( &parameters.key_agreement, @@ -406,8 +406,8 @@ where } } _ => false, - } - }) + }, + ) }); } @@ -426,7 +426,7 @@ where let (spend_index, ephemeral_key) = key; Ok(PreSender::new( parameters, - self.account().spend_key(spend_index)?, + self.accounts.get_default().spend_key(spend_index)?, ephemeral_key, asset, )) @@ -440,7 +440,8 @@ where asset: Asset, ) -> Result<Receiver<C>, Error<C>> { let keypair = self - .account() + .accounts + .get_default() .default_keypair() .map_err(key::Error::KeyDerivationError)?; Ok(SpendingKey::new(keypair.spend, keypair.view).receiver( @@ -459,7 +460,8 @@ where ) -> Result<(Mint<C>, PreSender<C>), Error<C>> { let asset = Asset::zero(asset_id); let keypair = self - .account() + .accounts + .get_default() .default_keypair() .map_err(key::Error::KeyDerivationError)?; let (receiver, pre_sender) = SpendingKey::new(keypair.spend, keypair.view).internal_pair( @@ -563,7 +565,8 @@ where total: AssetValue, ) -> Result<([Receiver<C>; PrivateTransferShape::RECEIVERS], Join<C>), Error<C>> { let keypair = self - .account() + .accounts + .get_default() .default_keypair() .map_err(key::Error::KeyDerivationError)?; Ok(Join::new( @@ -708,7 +711,7 @@ where /// Builds a new [`Signer`]. #[inline] fn new_inner( - account_table: AccountTable<C>, + accounts: AccountTable<C>, proving_context: C::ProvingContextCache, parameters: Parameters<C>, utxo_set: C::UtxoSet, @@ -721,7 +724,7 @@ where proving_context, }, state: SignerState { - account_table, + accounts, utxo_set, assets, rng, @@ -729,24 +732,24 @@ where } } - /// Builds a new [`Signer`] from a fresh `account_table`. + /// Builds a new [`Signer`] from a fresh set of `accounts`. /// /// # Warning /// - /// This method assumes that `account_table` has never been used before, and does not attempt + /// This method assumes that `accounts` has never been used before, and does not attempt /// to perform wallet recovery on this table. // // FIXME: Check that this warning even makes sense. #[inline] pub fn new( - account_table: AccountTable<C>, + accounts: AccountTable<C>, proving_context: C::ProvingContextCache, parameters: Parameters<C>, utxo_set: C::UtxoSet, rng: C::Rng, ) -> Self { Self::new_inner( - account_table, + accounts, proving_context, parameters, utxo_set, @@ -757,7 +760,12 @@ where /// Updates the internal ledger state, returning the new asset distribution. #[inline] - pub fn sync<I, R>(&mut self, inserts: I, removes: R) -> SyncResult<C, Self> + pub fn sync<I, R>( + &mut self, + starting_index: usize, + inserts: I, + removes: R, + ) -> SyncResult<C, Self> where I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, R: IntoIterator<Item = VoidNumber<C>>, @@ -771,11 +779,14 @@ where // TODO: Use a smarter object than `Vec` for `removes.into_iter().collect()` like a // `HashSet` or some other set-like container with fast membership and remove ops. // - self.state.sync_with( - &self.parameters.parameters, - inserts.into_iter(), - removes.into_iter().collect(), - ) + match self.state.utxo_set.len().checked_sub(starting_index) { + Some(diff) => self.state.sync_with( + &self.parameters.parameters, + inserts.into_iter().skip(diff), + removes.into_iter().collect(), + ), + _ => Err(Error::InconsistentSynchronization), + } } /// Signs a withdraw transaction for `asset` sent to `receiver`. @@ -861,7 +872,7 @@ where /// Returns a [`ReceivingKey`] for `self` to receive assets with `index`. #[inline] fn compute_receiving_key(&self, index: Index<C>) -> ReceivingKeyResult<C, Self> { - let keypair = self.state.account().keypair(index)?; + let keypair = self.state.accounts.get_default().keypair(index)?; Ok(SpendingKey::new(keypair.spend, keypair.view) .derive(&self.parameters.parameters.key_agreement)) } @@ -877,6 +888,7 @@ where #[inline] fn sync<'s, I, R>( &'s mut self, + starting_index: usize, inserts: I, removes: R, ) -> LocalBoxFuture<'s, SyncResult<C, Self>> @@ -884,7 +896,7 @@ where I: 's + IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, R: 's + IntoIterator<Item = VoidNumber<C>>, { - Box::pin(async move { self.sync(inserts, removes) }) + Box::pin(async move { self.sync(starting_index, inserts, removes) }) } #[inline] diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 85b0345b0..7bde42943 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -23,7 +23,7 @@ use crate::{ Configuration, ReceivingKey, }, wallet::{ - ledger::{self, PullResponse, PushResponse}, + ledger::{self, Checkpoint, PullResponse, PushResponse}, signer::{self, SignResponse, SyncResponse}, }, }; @@ -236,7 +236,7 @@ where self.assets.balance(id) } - /// Returns the entire balance state associated to `self`. + /// Returns a shared reference to the balance state associated to `self`. #[inline] pub fn assets(&self) -> &B { &self.assets @@ -252,6 +252,14 @@ where /// Pulls data from the `ledger`, synchronizing the wallet and balance state. #[inline] pub async fn sync(&mut self) -> Result<(), Error<C, L, S>> { + // FIXME: What should be done when we receive an `InconsistentSynchronization` error from + // the signer? + // - One option is to do some sort of (exponential) backoff algorithm to find the + // point at which the signer and the wallet are able to synchronize again. The + // correct algorithm may be simply to exchange some checkpoints between the + // signer and the wallet until they can agree on a minimal one. + // - In the worst case we would have to recover the entire wallet. + // let PullResponse { checkpoint, receivers, @@ -266,7 +274,7 @@ where } let SyncResponse { deposit, withdraw } = self .signer - .sync(receivers, senders) + .sync(self.checkpoint.receiver_index(), receivers, senders) .await .map_err(Error::SignerError)?; self.assets.deposit_all(deposit); diff --git a/manta-pay/src/test/ledger.rs b/manta-pay/src/test/ledger.rs index c73d0cbf3..2fb01c3c7 100644 --- a/manta-pay/src/test/ledger.rs +++ b/manta-pay/src/test/ledger.rs @@ -16,6 +16,8 @@ //! Test Ledger Implementation +// FIXME: How to model existential deposits and fee payments? + use crate::config::{ Config, EncryptedNote, MerkleTreeConfiguration, MultiVerifyingContext, ProofSystem, TransferPost, Utxo, VoidNumber, @@ -32,7 +34,7 @@ use manta_accounting::{ SourcePostingKey, TransferLedger, TransferLedgerSuperPostingKey, TransferPostingKey, UtxoSetOutput, }, - wallet::ledger::{Connection, PullResponse, PullResult, PushResponse, PushResult}, + wallet::ledger::{self, PullResponse, PullResult, PushResponse, PushResult}, }; use manta_crypto::{ constraint::ProofSystem as _, @@ -344,6 +346,13 @@ impl Default for Checkpoint { } } +impl ledger::Checkpoint for Checkpoint { + #[inline] + fn receiver_index(&self) -> usize { + self.receiver_index.iter().sum() + } +} + /// Ledger Connection pub struct LedgerConnection { /// Ledger Account @@ -361,7 +370,7 @@ impl LedgerConnection { } } -impl Connection<Config> for LedgerConnection { +impl ledger::Connection<Config> for LedgerConnection { type Checkpoint = Checkpoint; type ReceiverChunk = Vec<(Utxo, EncryptedNote)>; type SenderChunk = Vec<VoidNumber>; @@ -418,7 +427,6 @@ impl Connection<Config> for LedgerConnection { Some(TransferShape::Reclaim) => (vec![], vec![self.account]), _ => return Ok(PushResponse { success: false }), }; - async_std::println!("PUSH [receivers]: {:?}", post.receiver_posts).await; match post.validate(sources, sinks, &*ledger) { Ok(posting_key) => { posting_key.post(&(), &mut *ledger); @@ -550,6 +558,18 @@ mod test { ) .await; + alice.sync().await.expect(""); + + async_std::println!("Alice [wallet]: {:?}", alice.assets()).await; + async_std::println!( + "{:?}", + alice + .post(Transaction::Reclaim(Asset::new(AssetId(0), AssetValue(7)))) + .await + .expect("Unable to private transfer.") + ) + .await; + alice .sync() .await From 4a81599b4390f1bfb4d9d91ff85663242af6e9b2 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 12 Jan 2022 12:19:59 -0500 Subject: [PATCH 183/275] fix: use affine as basic coordinates for groups --- manta-pay/src/config.rs | 9 ++++----- manta-pay/src/crypto/ecc.rs | 9 +++++---- manta-pay/src/test/transfer.rs | 34 +++++++++++++++++++--------------- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 22575ccc6..079b83609 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -144,11 +144,10 @@ impl CommitmentScheme for UtxoCommitmentScheme { input: &Self::Input, _: &mut (), ) -> Self::Output { - // NOTE: The group is in projective form, so we need to convert it first. - let trapdoor = trapdoor.0.into_affine(); + // NOTE: The group is already in affine form, so we can extract `x` and `y`. self.0.hash([ - &Fp(trapdoor.x), - &Fp(trapdoor.y), + &Fp(trapdoor.0.x), + &Fp(trapdoor.0.y), &Fp(input.id.0.into()), &Fp(input.value.0.into()), ]) @@ -502,7 +501,7 @@ impl ProofSystemInput<Group> for ProofSystem { #[inline] fn extend(input: &mut Self::Input, next: &Group) { // FIXME: Make sure we can type check the coordinate system here. - input.append(&mut next.0.into_affine().to_field_elements().unwrap()); + input.append(&mut next.0.to_field_elements().unwrap()); } } diff --git a/manta-pay/src/crypto/ecc.rs b/manta-pay/src/crypto/ecc.rs index 0c987d062..a66a04a30 100644 --- a/manta-pay/src/crypto/ecc.rs +++ b/manta-pay/src/crypto/ecc.rs @@ -25,6 +25,7 @@ pub mod arkworks { use ark_ff::{BigInteger, Field, FpParameters, PrimeField}; use ark_r1cs_std::ToBitsGadget; use ark_relations::ns; + use ark_serialize::CanonicalSerialize; use core::marker::PhantomData; use manta_crypto::{ constraint::{Allocator, Constant, Equal, Public, Secret, ValueSource, Variable}, @@ -33,7 +34,7 @@ pub mod arkworks { rand::{CryptoRng, RngCore, Sample, Standard}, }; - pub use ark_ec::ProjectiveCurve; + pub use ark_ec::{AffineCurve, ProjectiveCurve}; pub use ark_r1cs_std::groups::CurveVar; /// Constraint Field Type @@ -93,7 +94,7 @@ pub mod arkworks { /// Elliptic Curve Group Element #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct Group<C>(pub(crate) C) + pub struct Group<C>(pub(crate) C::Affine) where C: ProjectiveCurve; @@ -124,7 +125,7 @@ pub mod arkworks { #[inline] fn scalar_mul(&self, scalar: &Self::Scalar, _: &mut ()) -> Self { - Self(self.0.mul(scalar.0.into_repr())) + Self(self.0.mul(scalar.0.into_repr()).into()) } } @@ -138,7 +139,7 @@ pub mod arkworks { R: CryptoRng + RngCore + ?Sized, { let _ = distribution; - Self(C::rand(rng)) + Self(C::rand(rng).into()) } } diff --git a/manta-pay/src/test/transfer.rs b/manta-pay/src/test/transfer.rs index fb7754a82..99cb0474c 100644 --- a/manta-pay/src/test/transfer.rs +++ b/manta-pay/src/test/transfer.rs @@ -59,32 +59,36 @@ fn sample_reclaim_context() { #[test] fn mint() { let mut rng = thread_rng(); - let result = - Mint::sample_and_check_proof(&(), &rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng); - println!("Mint: {:?}", result); - assert!(matches!(result, Ok(true))); + assert!( + Mint::sample_and_check_proof(&(), &rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng) + .expect("Random Mint should have successfully produced a proof."), + "The Mint proof should have been valid." + ); } /// Tests the generation of a [`PrivateTransfer`]. #[test] fn private_transfer() { let mut rng = thread_rng(); - let result = PrivateTransfer::sample_and_check_proof( - &(), - &rng.gen(), - &mut UtxoSet::new(rng.gen()), - &mut rng, + assert!( + PrivateTransfer::sample_and_check_proof( + &(), + &rng.gen(), + &mut UtxoSet::new(rng.gen()), + &mut rng + ) + .expect("Random PrivateTransfer should have successfully produced a proof."), + "The PrivateTransfer proof should have been valid." ); - println!("PrivateTransfer: {:?}", result); - assert!(matches!(result, Ok(true))); } /// Tests the generation of a [`Reclaim`]. #[test] fn reclaim() { let mut rng = thread_rng(); - let result = - Reclaim::sample_and_check_proof(&(), &rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng); - println!("Reclaim: {:?}", result); - assert!(matches!(result, Ok(true))); + assert!( + Reclaim::sample_and_check_proof(&(), &rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng) + .expect("Random Reclaim should have successfully produced a proof."), + "The Reclaim proof should have been valid." + ); } From 48e9af8c6a745567f7ca1de43a248ee2950f03db Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 12 Jan 2022 12:38:51 -0500 Subject: [PATCH 184/275] fix: use correct removal implementation for signer sync --- manta-accounting/src/asset.rs | 12 ++-- manta-accounting/src/wallet/signer.rs | 79 ++++++++++++++++----------- 2 files changed, 52 insertions(+), 39 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 05b5bd366..4cd4ec89a 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -632,10 +632,10 @@ pub trait AssetMap: Default { .for_each(move |(key, asset)| self.remove(key, asset)); } - /// Removes elements from `self` if they return `true` from `f`. - fn remove_if<F>(&mut self, f: F) + /// Retains the elements from `self` that return `true` after applying `f`. + fn retain<F>(&mut self, f: F) where - F: FnMut(&Self::Key, &[Asset]) -> bool; + F: FnMut(&Self::Key, &mut Vec<Asset>) -> bool; } /// Implements [`AssetMap`] for map types. @@ -710,11 +710,11 @@ macro_rules! impl_asset_map_for_maps_body { } #[inline] - fn remove_if<F>(&mut self, mut f: F) + fn retain<F>(&mut self, mut f: F) where - F: FnMut(&Self::Key, &[Asset]) -> bool, + F: FnMut(&Self::Key, &mut Vec<Asset>) -> bool, { - self.retain(move |key, assets| !f(key, &assets)); + self.retain(move |key, assets| f(key, assets)); } }; } diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index ccd392a85..4b7d48e91 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -353,6 +353,35 @@ where Ok(()) } + /// Checks if `asset` matches with `void_number`, removing it from the `utxo_set` and inserting + /// it into the `withdraw` set if this is the case. + #[inline] + fn is_asset_unspent( + parameters: &Parameters<C>, + secret_key: &SecretKey<C>, + ephemeral_public_key: &PublicKey<C>, + asset: Asset, + void_number: &VoidNumber<C>, + utxo_set: &mut C::UtxoSet, + withdraw: &mut Vec<Asset>, + ) -> bool { + let utxo = C::utxo( + &parameters.key_agreement, + &parameters.utxo_commitment, + secret_key, + ephemeral_public_key, + &asset, + ); + let known_void_number = C::void_number(&parameters.void_number_hash, &utxo, secret_key); + if *void_number == known_void_number { + utxo_set.remove_proof(&utxo); + withdraw.push(asset); + false + } else { + true + } + } + /// Updates the internal ledger state, returning the new asset distribution. #[inline] fn sync_with<I>( @@ -364,7 +393,6 @@ where where I: Iterator<Item = (Utxo<C>, EncryptedNote<C>)>, { - // TODO: Do this loop in parallel. let mut deposit = Vec::new(); let mut withdraw = Vec::new(); for (utxo, encrypted_note) in inserts { @@ -377,40 +405,25 @@ where &mut withdraw, )?; } - for void_number in void_numbers { - // FIXME: Use default account method like everywhere else. - self.assets - .remove_if(|(index, ephemeral_public_key), assets| { - assets.iter().any( - |asset| match self.accounts.get_default().spend_key(*index) { - Ok(secret_key) => { - let utxo = C::utxo( - &parameters.key_agreement, - &parameters.utxo_commitment, - &secret_key, - ephemeral_public_key, - asset, - ); - let known_void_number = C::void_number( - &parameters.void_number_hash, - &utxo, - &secret_key, - ); - if void_number == known_void_number { - self.utxo_set.remove_proof(&utxo); - withdraw.push(*asset); - true - } else { - false - } - } - _ => false, - }, - ) - }); + self.assets.retain(|(index, ephemeral_public_key), assets| { + assets.retain( + |asset| match self.accounts.get_default().spend_key(*index) { + Ok(secret_key) => Self::is_asset_unspent( + parameters, + &secret_key, + ephemeral_public_key, + *asset, + &void_number, + &mut self.utxo_set, + &mut withdraw, + ), + _ => true, + }, + ); + !assets.is_empty() + }); } - self.utxo_set.commit(); Ok(SyncResponse::new(deposit, withdraw)) } From d42e4a09f3c2254a04db4ebaacaf529a15ccc6a8 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 13 Jan 2022 00:59:26 -0500 Subject: [PATCH 185/275] fix: improve robustness of signer and make ledger test user-friendly --- manta-accounting/src/asset.rs | 26 +++- manta-accounting/src/fs.rs | 118 +++++++++------ manta-accounting/src/key.rs | 19 ++- manta-accounting/src/wallet/signer.rs | 75 ++++++---- manta-accounting/src/wallet/state.rs | 36 ++++- manta-crypto/src/rand.rs | 9 ++ manta-pay/Cargo.toml | 5 + manta-pay/src/key.rs | 31 +++- manta-pay/src/test/ledger.rs | 198 +++++++++++++++----------- manta-pay/src/test/mod.rs | 3 + manta-pay/src/test/simulation/mod.rs | 190 ++++++++---------------- 11 files changed, 412 insertions(+), 298 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 4cd4ec89a..986ff01c3 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -67,13 +67,18 @@ impl AssetId { /// The size of this type in bytes. pub const SIZE: usize = (Self::BITS / 8) as usize; - /// Constructs a new [`Asset`] with `self` as the [`AssetId`] and `value` as the - /// [`AssetValue`]. + /// Constructs a new [`Asset`] with `self` as the [`AssetId`] and `value` as the [`AssetValue`]. #[inline] pub const fn with(self, value: AssetValue) -> Asset { Asset::new(self, value) } + /// Constructs a new [`Asset`] with `self` as the [`AssetId`] and `value` as the [`AssetValue`]. + #[inline] + pub const fn value(self, value: AssetValueType) -> Asset { + self.with(AssetValue(value)) + } + /// Converts a byte array into `self`. #[inline] pub const fn from_bytes(bytes: [u8; Self::SIZE]) -> Self { @@ -581,6 +586,9 @@ pub trait AssetMap: Default { /// Keys are used to access the underlying asset values. type Key; + /// Returns the sum of all the assets in `self`. + fn assets(&self) -> Vec<Asset>; + /// Selects asset keys which total up to at least `asset` in value. fn select(&self, asset: Asset) -> Selection<Self>; @@ -643,6 +651,20 @@ macro_rules! impl_asset_map_for_maps_body { ($k:tt, $entry:tt) => { type Key = $k; + #[inline] + fn assets(&self) -> Vec<Asset> { + let mut result = Vec::<Asset>::new(); + for (_, assets) in self { + for asset in assets { + match result.binary_search_by_key(&asset.id, move |a| a.id) { + Ok(index) => result[index] += asset.value, + Err(index) => result.insert(index, *asset), + } + } + } + result + } + #[inline] fn select(&self, asset: Asset) -> Selection<Self> { // TODO: Use a smarter coin-selection algorithm (max-heap?). diff --git a/manta-accounting/src/fs.rs b/manta-accounting/src/fs.rs index 5fb671188..f25b52ffe 100644 --- a/manta-accounting/src/fs.rs +++ b/manta-accounting/src/fs.rs @@ -19,30 +19,38 @@ // FIXME: Add asynchronous streaming interfaces. use alloc::vec::Vec; -use core::future::Future; -use manta_util::{Deserialize, Serialize}; +use core::{fmt::Debug, hash::Hash}; +use manta_util::{future::LocalBoxFuture, Deserialize, Serialize}; /// Filesystem Encrypted Saving pub trait SaveEncrypted { /// Path Type - type Path; + type Path: ?Sized; /// Saving Key Type - type SavingKey; + type SavingKey: ?Sized; /// Saving Error type Error; - /// Saving Future - type Future: Future<Output = Result<(), Self::Error>>; - /// Saves the `payload` to `path` using the `saving_key` to encrypt it. - fn save_bytes(path: Self::Path, saving_key: Self::SavingKey, payload: Vec<u8>) -> Self::Future; + fn save_bytes<'s, P>( + path: P, + saving_key: &'s Self::SavingKey, + payload: Vec<u8>, + ) -> LocalBoxFuture<'s, Result<(), Self::Error>> + where + P: 's + AsRef<Self::Path>; /// Saves the `payload` to `path` after serializing using the `saving_key` to encrypt it. #[inline] - fn save<S>(path: Self::Path, saving_key: Self::SavingKey, payload: &S) -> Self::Future + fn save<'s, P, S>( + path: P, + saving_key: &'s Self::SavingKey, + payload: &'s S, + ) -> LocalBoxFuture<'s, Result<(), Self::Error>> where + P: 's + AsRef<Self::Path>, S: Serialize, { Self::save_bytes(path, saving_key, payload.to_vec()) @@ -52,41 +60,56 @@ pub trait SaveEncrypted { /// Filesystem Decrypted Loading pub trait LoadDecrypted { /// Path Type - type Path; + type Path: ?Sized; /// Loading Key Type - type LoadingKey; + type LoadingKey: ?Sized; /// Loading Error Type type Error; - /// Loading Future - type Future: Future<Output = Result<Vec<u8>, Self::Error>>; - /// Loads a vector of bytes from `path` using `loading_key` to decrypt them. - fn load_bytes(path: Self::Path, loading_key: Self::LoadingKey) -> Self::Future; -} + fn load_bytes<'s, P>( + path: P, + loading_key: &'s Self::LoadingKey, + ) -> LocalBoxFuture<'s, Result<Vec<u8>, Self::Error>> + where + P: 's + AsRef<Self::Path>; -/// Loads a vector of bytes from `path` using `loading_key` to decrypt them, then deserializing -/// the bytes to a concrete value of type `D`. -#[inline] -pub async fn load<L, D>(path: L::Path, loading_key: L::LoadingKey) -> Result<D, LoadError<D, L>> -where - L: LoadDecrypted, - D: Deserialize, -{ - match L::load_bytes(path, loading_key).await { - Ok(bytes) => D::from_vec(bytes).map_err(LoadError::Deserialize), - Err(err) => Err(LoadError::Loading(err)), + /// Loads a vector of bytes from `path` using `loading_key` to decrypt them, then deserializing + /// the bytes to a concrete value of type `D`. + #[inline] + fn load<'s, P, D>( + path: P, + loading_key: &'s Self::LoadingKey, + ) -> LocalBoxFuture<'s, Result<D, LoadError<D, Self>>> + where + P: 's + AsRef<Self::Path>, + D: Deserialize, + { + Box::pin(async { + match Self::load_bytes(path, loading_key).await { + Ok(bytes) => D::from_vec(bytes).map_err(LoadError::Deserialize), + Err(err) => Err(LoadError::Loading(err)), + } + }) } } /// Loading Error -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "D::Error: Clone, L::Error: Clone"), + Copy(bound = "D::Error: Copy, L::Error: Copy"), + Debug(bound = "D::Error: Debug, L::Error: Debug"), + Eq(bound = "D::Error: Eq, L::Error: Eq"), + Hash(bound = "D::Error: Hash, L::Error: Hash"), + PartialEq(bound = "D::Error: PartialEq, L::Error: PartialEq") +)] pub enum LoadError<D, L> where D: Deserialize, - L: LoadDecrypted, + L: LoadDecrypted + ?Sized, { /// Deserialization Error Deserialize(D::Error), @@ -103,7 +126,7 @@ pub mod cocoon { use async_std::{ fs::OpenOptions, io::{Error as IoError, ReadExt, WriteExt}, - path::PathBuf, + path::Path, }; use cocoon_crate::{Cocoon, Error as CocoonError}; use core::fmt; @@ -142,21 +165,22 @@ pub mod cocoon { pub struct Save; impl SaveEncrypted for Save { - type Path = PathBuf; - type SavingKey = Vec<u8>; + type Path = Path; + type SavingKey = [u8]; type Error = Error; - type Future = LocalBoxFuture<'static, Result<(), Self::Error>>; #[inline] - fn save_bytes( - path: Self::Path, - saving_key: Self::SavingKey, + fn save_bytes<'s, P>( + path: P, + saving_key: &'s Self::SavingKey, payload: Vec<u8>, - ) -> Self::Future { + ) -> LocalBoxFuture<'s, Result<(), Self::Error>> + where + P: 's + AsRef<Self::Path>, + { Box::pin(async { - let saving_key = Zeroizing::new(saving_key); let mut buffer = Zeroizing::new(Vec::new()); - Cocoon::new(&saving_key).dump(payload, &mut buffer.as_mut_slice())?; + Cocoon::new(saving_key).dump(payload, &mut buffer.as_mut_slice())?; OpenOptions::new() .create(true) .truncate(true) @@ -175,19 +199,23 @@ pub mod cocoon { pub struct Load; impl LoadDecrypted for Load { - type Path = PathBuf; - type LoadingKey = Vec<u8>; + type Path = Path; + type LoadingKey = [u8]; type Error = Error; - type Future = LocalBoxFuture<'static, Result<Vec<u8>, Self::Error>>; #[inline] - fn load_bytes(path: Self::Path, loading_key: Self::LoadingKey) -> Self::Future { + fn load_bytes<'s, P>( + path: P, + loading_key: &'s Self::LoadingKey, + ) -> LocalBoxFuture<'s, Result<Vec<u8>, Self::Error>> + where + P: 's + AsRef<Self::Path>, + { Box::pin(async move { - let loading_key = Zeroizing::new(loading_key); let mut buffer = Zeroizing::new(Vec::new()); let mut file = OpenOptions::new().read(true).open(path).await?; file.read_to_end(&mut buffer).await?; - Ok(Cocoon::parse_only(&loading_key).parse(&mut buffer.as_slice())?) + Ok(Cocoon::parse_only(loading_key).parse(&mut buffer.as_slice())?) }) } } diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index 6094a8e28..cc82bb80d 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -18,7 +18,10 @@ use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; -use manta_crypto::key::KeyDerivationFunction; +use manta_crypto::{ + key::KeyDerivationFunction, + rand::{CryptoRng, RngCore, Sample}, +}; /// Hierarchical Key Derivation Parameter pub trait HierarchicalKeyDerivationParameter: Copy + Default + PartialOrd { @@ -179,6 +182,20 @@ where } } +impl<H, K, D> Sample<D> for Map<H, K> +where + H: HierarchicalKeyDerivationScheme + Sample<D>, + K: KeyDerivationFunction<Key = H::SecretKey>, +{ + #[inline] + fn sample<R>(distribution: D, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + Self::new(H::sample(distribution, rng)) + } +} + /// Hierarchical Key Derivation Secret Key Pair pub struct SecretKeyPair<H> where diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 4b7d48e91..e36a6797a 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -111,23 +111,31 @@ pub type ReceivingKeyResult<C, S> = Result<ReceivingKey<C>, <S as Connection<C>> /// Signer Synchronization Response /// -/// This `struct` is created by the [`sync`](Connection::sync) method on [`Connection`]. +/// This `enum` is created by the [`sync`](Connection::sync) method on [`Connection`]. /// See its documentation for more. -#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] -pub struct SyncResponse { - /// Assets Deposited - pub deposit: Vec<Asset>, - - /// Assets Withdrawn - pub withdraw: Vec<Asset>, -} +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub enum SyncResponse { + /// Partial Update + /// + /// This is the typical response from the [`Signer`]. In rare cases, we may need to perform a + /// [`Full`](Self::Full) update. + Partial { + /// Assets Deposited in the Last Update + deposit: Vec<Asset>, + + /// Assets Withdrawn in the Last Update + withdraw: Vec<Asset>, + }, -impl SyncResponse { - /// Builds a new [`SyncResponse`] from `deposit` and `withdraw`. - #[inline] - pub fn new(deposit: Vec<Asset>, withdraw: Vec<Asset>) -> Self { - Self { deposit, withdraw } - } + /// Full Update + /// + /// Whenever the [`Signer`] gets ahead of the synchronization point, it would have updated its + /// internal balance state further along than any connection following its updates. In this + /// case, to entire balance state needs to be sent to catch up. + Full { + /// Full Balance State + assets: Vec<Asset>, + }, } /// Signer Signing Response @@ -222,10 +230,16 @@ where MissingUtxoMembershipProof, /// Insufficient Balance - InsufficientBalance(Asset), + InsufficientBalance { + /// Attempting to withdraw the given asset + withdraw: Asset, + }, /// Inconsistent Synchronization - InconsistentSynchronization, + InconsistentSynchronization { + /// Desired starting index to fix synchronization + starting_index: usize, + }, /// Proof System Error ProofSystemError(ProofSystemError<C>), @@ -389,10 +403,13 @@ where parameters: &Parameters<C>, inserts: I, mut void_numbers: Vec<VoidNumber<C>>, + is_partial: bool, ) -> SyncResult<C, Signer<C>> where I: Iterator<Item = (Utxo<C>, EncryptedNote<C>)>, { + // FIXME: Use a more efficient synchronization algorithm for partial vs full. + // let mut deposit = Vec::new(); let mut withdraw = Vec::new(); for (utxo, encrypted_note) in inserts { @@ -425,7 +442,13 @@ where }); } self.utxo_set.commit(); - Ok(SyncResponse::new(deposit, withdraw)) + if is_partial { + Ok(SyncResponse::Partial { deposit, withdraw }) + } else { + Ok(SyncResponse::Full { + assets: self.assets.assets(), + }) + } } /// Builds the pre-sender associated to `key` and `asset`. @@ -494,7 +517,7 @@ where ) -> Result<Selection<C>, Error<C>> { let selection = self.assets.select(asset); if selection.is_empty() { - return Err(Error::InsufficientBalance(asset)); + return Err(Error::InsufficientBalance { withdraw: asset }); } Selection::new(selection, move |k, v| { self.build_pre_sender(parameters, k, asset.id.with(v)) @@ -792,13 +815,17 @@ where // TODO: Use a smarter object than `Vec` for `removes.into_iter().collect()` like a // `HashSet` or some other set-like container with fast membership and remove ops. // - match self.state.utxo_set.len().checked_sub(starting_index) { + let utxo_set_len = self.state.utxo_set.len(); + match utxo_set_len.checked_sub(starting_index) { Some(diff) => self.state.sync_with( &self.parameters.parameters, inserts.into_iter().skip(diff), removes.into_iter().collect(), + diff == 0, ), - _ => Err(Error::InconsistentSynchronization), + _ => Err(Error::InconsistentSynchronization { + starting_index: utxo_set_len, + }), } } @@ -842,7 +869,6 @@ where )?, }; posts.push(final_post); - self.state.utxo_set.rollback(); Ok(SignResponse::new(posts)) } @@ -878,13 +904,14 @@ where // TODO: Should we do a time-based release mechanism to amortize the cost of reading/writing // to disk? let result = self.sign_internal(transaction).await; + self.state.utxo_set.rollback(); self.parameters.proving_context.release().await; result } /// Returns a [`ReceivingKey`] for `self` to receive assets with `index`. #[inline] - fn compute_receiving_key(&self, index: Index<C>) -> ReceivingKeyResult<C, Self> { + pub fn receiving_key(&self, index: Index<C>) -> ReceivingKeyResult<C, Self> { let keypair = self.state.accounts.get_default().keypair(index)?; Ok(SpendingKey::new(keypair.spend, keypair.view) .derive(&self.parameters.parameters.key_agreement)) @@ -919,6 +946,6 @@ where #[inline] fn receiving_key(&mut self, index: Index<C>) -> LocalBoxFuture<ReceivingKeyResult<C, Self>> { - Box::pin(async move { self.compute_receiving_key(index) }) + Box::pin(async move { (&*self).receiving_key(index) }) } } diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 7bde42943..ba759c85e 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -87,6 +87,9 @@ pub trait BalanceState: Default { .into_iter() .for_each(move |a| self.withdraw_unchecked(a)) } + + /// Clears the entire balance state. + fn clear(&mut self); } /// Performs an unchecked withdraw on `balance`, panicking on overflow. @@ -111,11 +114,15 @@ impl BalanceState for VecBalanceState { #[inline] fn deposit(&mut self, asset: Asset) { - self.push(asset); + match self.binary_search_by_key(&asset.id, move |a| a.id) { + Ok(index) => self[index] += asset.value, + Err(index) => self.insert(index, asset), + } } #[inline] fn withdraw_unchecked(&mut self, asset: Asset) { + // TODO: Use binary search for withdraw. if !asset.is_zero() { withdraw_unchecked( self.iter_mut().find_map(move |a| a.value_of_mut(asset.id)), @@ -123,6 +130,11 @@ impl BalanceState for VecBalanceState { ); } } + + #[inline] + fn clear(&mut self) { + self.clear(); + } } /// Adds implementation of [`BalanceState`] for a map type with the given `$entry` type. @@ -151,6 +163,11 @@ macro_rules! impl_balance_state_map_body { withdraw_unchecked(self.get_mut(&asset.id), asset.value); } } + + #[inline] + fn clear(&mut self) { + self.clear(); + } }; } @@ -272,13 +289,22 @@ where if checkpoint < self.checkpoint { return Err(Error::InconsistentCheckpoint); } - let SyncResponse { deposit, withdraw } = self + match self .signer .sync(self.checkpoint.receiver_index(), receivers, senders) .await - .map_err(Error::SignerError)?; - self.assets.deposit_all(deposit); - self.assets.withdraw_all_unchecked(withdraw); + .map_err(Error::SignerError)? + { + SyncResponse::Partial { deposit, withdraw } => { + self.assets.deposit_all(deposit); + self.assets.withdraw_all_unchecked(withdraw); + } + SyncResponse::Full { assets } => { + // TODO: Perform these two as one call, something like `self.assets.reset(assets)`. + self.assets.clear(); + self.assets.deposit_all(assets); + } + } self.checkpoint = checkpoint; Ok(()) } diff --git a/manta-crypto/src/rand.rs b/manta-crypto/src/rand.rs index 6c67feea6..fb0300de2 100644 --- a/manta-crypto/src/rand.rs +++ b/manta-crypto/src/rand.rs @@ -389,6 +389,15 @@ pub trait Rand: CryptoRng + RngCore { { T::try_gen(self) } + + /// Seeds another random number generator `R` using entropy from `self`. + #[inline] + fn seed_rng<R>(&mut self) -> Result<R, Error> + where + R: SeedableRng, + { + R::from_rng(self) + } } impl<R> Rand for R where R: CryptoRng + RngCore + ?Sized {} diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 998c9fd0f..177192209 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -50,6 +50,9 @@ plonk = ["zk-garage-plonk"] # Testing Frameworks test = ["manta-accounting/test", "manta-crypto/test"] +# Simulation Framework +simulation = ["rand", "statrs"] + # Standard Library std = ["manta-accounting/std"] @@ -73,9 +76,11 @@ indexmap = { version = "1.8.0", default-features = false } manta-accounting = { path = "../manta-accounting" } manta-crypto = { path = "../manta-crypto" } manta-util = { path = "../manta-util" } +rand = { version = "0.8.4", optional = true } rand_chacha = { version = "0.3.1", default-features = false } scale-codec = { package = "parity-scale-codec", version = "2.3.1", optional = true, default-features = false, features = ["derive"] } serde = { version = "1.0.133", optional = true, default-features = false, features = ["derive"] } +statrs = { version = "0.15.0", optional = true, default-features = false } x25519-dalek = { git = "https://github.com/Manta-Network/x25519-dalek", default-features = false, features = ["u64_backend"] } zk-garage-plonk = { package = "plonk", git = "https://github.com/zk-garage/plonk", optional = true, default-features = false } diff --git a/manta-pay/src/key.rs b/manta-pay/src/key.rs index d100fd6b0..2ddda3081 100644 --- a/manta-pay/src/key.rs +++ b/manta-pay/src/key.rs @@ -30,6 +30,7 @@ use core::{marker::PhantomData, num::ParseIntError, str::FromStr}; use manta_accounting::key::{ self, HierarchicalKeyDerivationParameter, HierarchicalKeyDerivationScheme, }; +use manta_crypto::rand::{CryptoRng, RngCore, Sample, Standard}; use manta_util::{create_seal, seal}; pub use bip32::{Error, Language, Mnemonic}; @@ -185,15 +186,37 @@ impl<C> KeySecret<C> where C: CoinType, { - /// Converts a `mnemonic` phrase into a [`KeySecret`], locking it with `password`. + /// Builds a [`KeySecret`] from a raw `seed`. #[inline] - #[must_use] - pub fn new(mnemonic: Mnemonic, password: &str) -> Self { + fn from_seed(seed: Seed) -> Self { Self { - seed: mnemonic.to_seed(password), + seed, __: PhantomData, } } + + /// Converts a `mnemonic` phrase into a [`KeySecret`], locking it with `password`. + #[inline] + #[must_use] + pub fn new(mnemonic: Mnemonic, password: &str) -> Self { + Self::from_seed(mnemonic.to_seed(password)) + } +} + +impl<C> Sample for KeySecret<C> +where + C: CoinType, +{ + #[inline] + fn sample<R>(distribution: Standard, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + let _ = distribution; + let mut seed = [0; Seed::SIZE]; + rng.fill_bytes(&mut seed); + Self::from_seed(Seed::new(seed)) + } } /// Computes the [`BIP-0044`] path string for the given coin settings. diff --git a/manta-pay/src/test/ledger.rs b/manta-pay/src/test/ledger.rs index 2fb01c3c7..2b0749753 100644 --- a/manta-pay/src/test/ledger.rs +++ b/manta-pay/src/test/ledger.rs @@ -353,19 +353,22 @@ impl ledger::Checkpoint for Checkpoint { } } +/// Shared Ledger +pub type SharedLedger = Rc<RwLock<Ledger>>; + /// Ledger Connection pub struct LedgerConnection { /// Ledger Account account: AccountId, - /// Ledger Access - ledger: Rc<RwLock<Ledger>>, + /// Ledger Accessor + ledger: SharedLedger, } impl LedgerConnection { /// Builds a new [`LedgerConnection`] for `account` and `ledger`. #[inline] - pub fn new(account: AccountId, ledger: Rc<RwLock<Ledger>>) -> Self { + pub fn new(account: AccountId, ledger: SharedLedger) -> Self { Self { account, ledger } } } @@ -445,26 +448,48 @@ mod test { use super::*; use crate::{ config::FullParameters, - key::{Language, Mnemonic, TestnetKeySecret}, wallet::{self, cache::OnDiskMultiProvingContext, Signer, Wallet}, }; - use async_std::task; + use async_std::{println, task}; use manta_accounting::{ - asset::Asset, - key::{AccountTable, HierarchicalKeyDerivationScheme}, + key::AccountTable, transfer::{self, canonical::Transaction}, }; - use manta_crypto::rand::{Rand, SeedableRng}; + use manta_crypto::rand::{CryptoRng, Rand, RngCore}; use rand::thread_rng; - use rand_chacha::ChaCha20Rng; - /// + /// Samples an empty wallet for `account` on `ledger`. + #[inline] + fn sample_wallet<R>( + account: AccountId, + ledger: &SharedLedger, + cache: &OnDiskMultiProvingContext, + parameters: &transfer::Parameters<Config>, + utxo_set_parameters: &merkle_tree::Parameters<MerkleTreeConfiguration>, + rng: &mut R, + ) -> Wallet<LedgerConnection> + where + R: CryptoRng + RngCore + ?Sized, + { + Wallet::empty( + LedgerConnection::new(account, ledger.clone()), + Signer::new( + AccountTable::new(rng.gen()), + cache.clone(), + parameters.clone(), + wallet::UtxoSet::new(utxo_set_parameters.clone()), + rng.seed_rng().expect("Failed to sample PRNG for signer."), + ), + ) + } + + /// Runs a simple simulation to test that the signer-wallet-ledger connection works. #[async_std::test] - async fn test() { + async fn test_simulation() { let directory = task::spawn_blocking(tempfile::tempdir) .await .expect("Unable to generate temporary test directory."); - async_std::println!("Temporary Directory: {:?}", directory).await; + println!("Temporary Directory: {:?}", directory).await; let mut rng = thread_rng(); let parameters = rng.gen(); @@ -488,98 +513,101 @@ mod test { ledger.set_public_balance(AccountId(1), AssetId(0), AssetValue(1000)); ledger.set_public_balance(AccountId(2), AssetId(0), AssetValue(100)); - async_std::println!("Ledger: {:?}", ledger.accounts).await; + println!("Ledger: {:?}", ledger.accounts).await; let ledger = Rc::new(RwLock::new(ledger)); - async_std::println!("Building Wallets").await; + println!("Building Wallets").await; - let mut alice = Wallet::empty( - LedgerConnection::new(AccountId(0), ledger.clone()), - Signer::new( - AccountTable::new( - TestnetKeySecret::new( - Mnemonic::random(&mut rng, Language::English), - "password", - ) - .map(), - ), - cache.clone(), - parameters.clone(), - wallet::UtxoSet::new(utxo_set_parameters.clone()), - ChaCha20Rng::from_rng(&mut rng).expect("Failed to sample PRNG for signer."), - ), + let mut alice = sample_wallet( + AccountId(0), + &ledger, + &cache, + &parameters, + &utxo_set_parameters, + &mut rng, ); - let mut bob = Wallet::empty( - LedgerConnection::new(AccountId(1), ledger.clone()), - Signer::new( - AccountTable::new( - TestnetKeySecret::new( - Mnemonic::random(&mut rng, Language::English), - "password", - ) - .map(), - ), - cache.clone(), - parameters.clone(), - wallet::UtxoSet::new(utxo_set_parameters.clone()), - ChaCha20Rng::from_rng(&mut rng).expect("Failed to sample PRNG for signer."), - ), + let mut bob = sample_wallet( + AccountId(1), + &ledger, + &cache, + &parameters, + &utxo_set_parameters, + &mut rng, ); - let bob_public_key = bob + let mut charlie = sample_wallet( + AccountId(2), + &ledger, + &cache, + &parameters, + &utxo_set_parameters, + &mut rng, + ); + + let alice_key = alice + .receiving_key(Default::default()) + .await + .expect("Unable to get Alices's public key."); + + let bob_key = bob .receiving_key(Default::default()) .await .expect("Unable to get Bob's public key."); - async_std::println!("Alice [wallet]: {:?}", alice.assets()).await; - async_std::println!( - "{:?}", - alice - .post(Transaction::Mint(Asset::new(AssetId(0), AssetValue(100)))) - .await - .expect("Unable to mint.") - ) - .await; - - alice.sync().await.expect(""); - - async_std::println!("Alice [wallet]: {:?}", alice.assets()).await; - async_std::println!( - "{:?}", - alice - .post(Transaction::PrivateTransfer( - Asset::new(AssetId(0), AssetValue(10)), - bob_public_key - )) - .await - .expect("Unable to private transfer.") - ) - .await; + let charlie_key = charlie + .receiving_key(Default::default()) + .await + .expect("Unable to get Charlie's public key."); - alice.sync().await.expect(""); + println!("Alice [wallet]: {:?}", alice.assets()).await; + println!("Bob [wallet]: {:?}", bob.assets()).await; + println!("Charlie [wallet]: {:?}", charlie.assets()).await; - async_std::println!("Alice [wallet]: {:?}", alice.assets()).await; - async_std::println!( - "{:?}", - alice - .post(Transaction::Reclaim(Asset::new(AssetId(0), AssetValue(7)))) - .await - .expect("Unable to private transfer.") - ) - .await; + alice + .post(Transaction::Mint(AssetId(0).value(1000))) + .await + .expect("Unable to build mint."); + alice.sync().await.expect("Unable to sync."); + println!("Alice [wallet]: {:?}", alice.assets()).await; alice - .sync() + .post(Transaction::PrivateTransfer(AssetId(0).value(89), bob_key)) .await - .expect("Unable to synchronise with the ledger."); - bob.sync() + .expect("Unable to build private transfer."); + alice.sync().await.expect("Unable to sync."); + println!("Alice [wallet]: {:?}", alice.assets()).await; + + bob.post(Transaction::PrivateTransfer( + AssetId(0).value(6), + charlie_key, + )) + .await + .expect("Unable to build private transfer."); + bob.sync().await.expect("Unable to sync."); + println!("Bob [wallet]: {:?}", bob.assets()).await; + + charlie + .post(Transaction::PrivateTransfer(AssetId(0).value(1), alice_key)) .await - .expect("Unable to synchronise with the ledger."); + .expect("Unable to build private transfer."); + charlie.sync().await.expect("Unable to sync."); + println!("Charlie [wallet]: {:?}", charlie.assets()).await; - async_std::println!("Alice [wallet]: {:?}", alice.assets()).await; - async_std::println!("Bob [wallet]: {:?}", bob.assets()).await; + alice + .post(Transaction::Reclaim(AssetId(0).value(71))) + .await + .expect("Unable to build private transfer."); + alice.sync().await.expect("Unable to sync."); + println!("Alice [wallet]: {:?}", alice.assets()).await; + + alice.sync().await.expect("Unable to sync."); + bob.sync().await.expect("Unable to sync."); + charlie.sync().await.expect("Unable to sync."); + println!("Alice [wallet]: {:?}", alice.assets()).await; + println!("Bob [wallet]: {:?}", bob.assets()).await; + println!("Charlie [wallet]: {:?}", charlie.assets()).await; task::spawn_blocking(move || directory.close()) .await diff --git a/manta-pay/src/test/mod.rs b/manta-pay/src/test/mod.rs index dd797c6fa..453b5896f 100644 --- a/manta-pay/src/test/mod.rs +++ b/manta-pay/src/test/mod.rs @@ -17,6 +17,9 @@ //! Manta Pay Testing pub mod ledger; + +#[cfg(feature = "simulation")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "simulation")))] pub mod simulation; // TODO: #[cfg(test)] diff --git a/manta-pay/src/test/simulation/mod.rs b/manta-pay/src/test/simulation/mod.rs index ee13d5f7e..cb9b447af 100644 --- a/manta-pay/src/test/simulation/mod.rs +++ b/manta-pay/src/test/simulation/mod.rs @@ -16,26 +16,19 @@ //! Manta Pay Simulation -/* -use clap::{App, Arg}; -use core::cmp::min; -use core::ops::Range; +// TODO: Implement asynchronous/dynamic simulation and have this static simulation as a degenerate +// form of this simulation when "asynchronousity" is turned down to zero. + +use core::{cmp::min, ops::Range}; use indexmap::IndexMap; -use rand::{distributions::Distribution, seq::SliceRandom, thread_rng, Rng, RngCore}; -use serde::{Deserialize, Serialize}; +use manta_accounting::asset::{Asset, AssetId, AssetValue}; +use manta_crypto::rand::RngCore; +use rand::{distributions::Distribution, seq::SliceRandom, Rng}; use statrs::{ distribution::{Categorical, Discrete, Poisson}, StatsError, }; use std::collections::HashMap; -use std::fs; - -/// Flushes the STDOUT buffer. -#[inline] -fn flush_stdout() { - use std::io::Write; - let _ = std::io::stdout().flush(); -} /// Choose `count`-many elements from `vec` randomly and drop the remaining ones. #[inline] @@ -47,34 +40,8 @@ where vec.drain(0..drop_count); } -/// Asset Id -pub type AssetId = u32; - -/// Asset Value -pub type AssetValue = u128; - -/// Asset -#[derive(Clone, Copy, Debug, Default, Deserialize, Hash, Eq, PartialEq, Serialize)] -#[serde(deny_unknown_fields)] -pub struct Asset { - /// Asset Id - pub id: AssetId, - - /// Asset Value - pub value: AssetValue, -} - -impl Asset { - /// Builds a new [`Asset`] from the given `id` and `value`. - #[inline] - pub fn new(id: AssetId, value: AssetValue) -> Self { - Self { id, value } - } -} - /// Balance State -#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] -#[serde(deny_unknown_fields, transparent)] +#[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct BalanceState { /// Asset Map map: HashMap<AssetId, AssetValue>, @@ -121,8 +88,7 @@ impl BalanceState { } /// Action Types -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[serde(deny_unknown_fields, tag = "type")] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum Action { /// No Action None, @@ -144,7 +110,7 @@ pub enum Action { } /// Action Distribution Probability Mass Function -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct ActionDistributionPMF<T = f64> { /// No Action Weight pub none: T, @@ -194,12 +160,7 @@ impl From<ActionDistribution> for ActionDistributionPMF { } /// Action Distribution -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] -#[serde( - deny_unknown_fields, - into = "ActionDistributionPMF", - try_from = "ActionDistributionPMF" -)] +#[derive(Clone, Debug, PartialEq)] pub struct ActionDistribution { /// Distribution over Actions distribution: Categorical, @@ -249,8 +210,7 @@ impl Distribution<Action> for ActionDistribution { } /// User Account -#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] -#[serde(deny_unknown_fields)] +#[derive(Clone, Debug, Default, PartialEq)] pub struct Account { /// Public Balances pub public: BalanceState, @@ -293,35 +253,65 @@ impl Account { } /// Simulation Update -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] -#[serde(deny_unknown_fields, tag = "type")] +#[derive(Clone, Debug, PartialEq)] pub enum Update { /// Create Account - CreateAccount { account: Account }, + CreateAccount { + /// Account to Create + account: Account, + }, /// Deposit Public Balance - PublicDeposit { account_index: usize, asset: Asset }, + PublicDeposit { + /// Index of Target Account + account_index: usize, + + /// Asset to Deposit + asset: Asset, + }, /// Withdraw Public Balance - PublicWithdraw { account_index: usize, asset: Asset }, + PublicWithdraw { + /// Index of Target Account + account_index: usize, + + /// Asset to Withdraw + asset: Asset, + }, /// Mint Asset - Mint { source_index: usize, asset: Asset }, + Mint { + /// Source Index + source_index: usize, + + /// Asset to Mint + asset: Asset, + }, /// Private Transfer Asset PrivateTransfer { + /// Sender Index sender_index: usize, + + /// Receiver Index receiver_index: usize, + + /// Asset to Private Transfer asset: Asset, }, /// Reclaim Asset - Reclaim { sender_index: usize, asset: Asset }, + Reclaim { + /// Reclaim Index + sender_index: usize, + + /// Asset to Reclaim + asset: Asset, + }, } /// Simulation Configuration -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] -#[serde(deny_unknown_fields)] +#[derive(Clone, Debug, PartialEq)] pub struct Config { /// Number of starting accounts pub starting_account_count: u64, @@ -408,7 +398,7 @@ impl Config { where R: RngCore + ?Sized, { - rng.gen_range(0..=self.allowed_asset_sampling[&id]) + AssetValue(rng.gen_range(0..=self.allowed_asset_sampling[&id].0)) } /// Samples an allowed withdraw from `balances`. @@ -424,17 +414,18 @@ impl Config { if balance != 0 { return Asset::new( **id, - rng.gen_range(0..=min(balance, self.allowed_asset_sampling[*id])), + AssetValue( + rng.gen_range(0..=min(balance.0, self.allowed_asset_sampling[*id].0)), + ), ); } } - Asset::new(*ids[ids.len() - 1], 0) + Asset::zero(*ids[ids.len() - 1]) } } /// Simulator -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] -#[serde(deny_unknown_fields)] +#[derive(Clone, Debug, PartialEq)] pub struct Simulator { /// Configuration config: Config, @@ -641,8 +632,7 @@ impl Simulator { } /// Simulation Final State -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] -#[serde(deny_unknown_fields)] +#[derive(Clone, Debug, PartialEq)] pub struct Simulation { /// Configuration pub config: Config, @@ -656,67 +646,3 @@ pub struct Simulation { /// Updates pub updates: Vec<Update>, } - -/// Runs a [`Simulator`] in a CLI. -pub fn main() { - let matches = App::new("Manta Simulation") - .arg( - Arg::with_name("steps") - .help("The number of steps to run the simulation.") - .required(true), - ) - .arg( - Arg::with_name("config") - .short("c") - .long("config") - .value_name("FILE") - .help("Sets a custom config file. By default, `default-config.json` is used.") - .takes_value(true), - ) - .arg( - Arg::with_name("output") - .short("o") - .long("output") - .value_name("FILE") - .help("Sets a custom output file") - .takes_value(true), - ) - .get_matches(); - - let config_path = matches.value_of("config").unwrap_or("default-config.json"); - let config = match fs::read_to_string(&config_path) { - Ok(config) => match serde_json::from_str(&config) { - Ok(config) => config, - err => panic!("ERROR: {:?}", err), - }, - _ => panic!("ERROR: Invalid configuration path: {:?}", config_path), - }; - - let steps = matches - .value_of("steps") - .unwrap() - .parse::<usize>() - .expect("ERROR: Invalid number of simulation steps."); - - let mut rng = thread_rng(); - - print!("INFO: Running simulation ... "); - flush_stdout(); - let simulation = Simulator::new(config, &mut rng).run(steps, &mut rng); - println!("DONE."); - - if let Some(output_path) = matches.value_of("output") { - print!("INFO: Writing simulation to file ... "); - flush_stdout(); - match serde_json::to_writer( - fs::File::create(output_path).expect("ERROR: Unable to create output file."), - &simulation, - ) { - Ok(()) => println!("DONE. Output written to `{}`.", output_path), - err => panic!("ERROR: {:?}", err), - } - } else if let Err(err) = serde_json::to_writer_pretty(std::io::stdout(), &simulation) { - panic!("ERROR: {:?}", err); - } -} -*/ From 3a05ea281182863e645082d7496f7d0e7b10f580 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 13 Jan 2022 12:45:39 -0500 Subject: [PATCH 186/275] feat: add AssetList sorted asset vector --- manta-accounting/src/asset.rs | 230 ++++++++++++++++++++++++-- manta-accounting/src/wallet/signer.rs | 2 +- manta-accounting/src/wallet/state.rs | 48 ++---- 3 files changed, 229 insertions(+), 51 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 986ff01c3..2c15674f4 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -16,21 +16,17 @@ //! Assets -// TODO: Add macro to build `AssetId` and `AssetValue`. -// TODO: Implement all `rand` sampling traits. -// TODO: Implement `Concat` for `AssetId` and `AssetValue`. -// TODO: Add implementations for `AssetMap` using sorted vectors and/or max-heaps - use alloc::{ collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}, vec, vec::Vec, }; use core::{ + borrow::Borrow, fmt::Debug, hash::Hash, iter::{self, FusedIterator, Sum}, - ops::{Add, AddAssign, Mul, Sub, SubAssign}, + ops::{Add, AddAssign, Deref, Mul, Sub, SubAssign}, slice, }; use derive_more::{ @@ -183,6 +179,12 @@ impl AssetValue { Asset::new(id, self) } + /// Constructs a new [`Asset`] with `self` as the [`AssetValue`] and `id` as the [`AssetId`]. + #[inline] + pub const fn id(self, id: AssetIdType) -> Asset { + self.with(AssetId(id)) + } + /// Converts a byte array into `self`. #[inline] pub const fn from_bytes(bytes: [u8; Self::SIZE]) -> Self { @@ -573,6 +575,205 @@ where } } +/// Asset List +/// +/// Stores assets sorted by [`AssetId`] as a flat key-value vector. This type can be relied on to +/// maintain sorted order for iterating. +#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] +pub struct AssetList { + /// Sorted Asset Vector + /// + /// The elements of the vector are sorted by [`AssetId`]. To insert/remove we perform a binary + /// search on the [`AssetId`] and update the [`AssetValue`] at that location. + map: Vec<Asset>, +} + +impl AssetList { + /// Builds a new empty [`AssetList`]. + #[inline] + pub const fn new() -> Self { + Self { map: Vec::new() } + } + + /// Returns the number of entries stored in `self`. + #[inline] + pub fn len(&self) -> usize { + self.map.len() + } + + /// Returns `true` if the number of entries in `self` is zero. + #[inline] + pub fn is_empty(&self) -> bool { + self.map.is_empty() + } + + /// Returns the number of [`AssetId`] that can be inserted into `self` before needing to + /// reallocate. + #[inline] + pub fn capacity(&self) -> usize { + self.map.capacity() + } + + /// Finds the insertion point for an [`Asset`] with the given `id`, returning `Ok` if there is + /// an [`Asset`] at that index, or `Err` otherwise. + #[inline] + fn find(&self, id: AssetId) -> Result<usize, usize> { + self.map.binary_search_by_key(&id, move |a| a.id) + } + + /// Returns the total value for assets with the given `id`. + #[inline] + pub fn value(&self, id: AssetId) -> AssetValue { + self.find(id) + .map(move |i| self.map[i].value) + .unwrap_or_default() + } + + /// Returns `true` if `self` contains at least `asset.value` of the asset of kind `asset.id`. + #[inline] + pub fn contains(&self, asset: Asset) -> bool { + self.value(asset.id) >= asset.value + } + + /// Returns an iterator over the assets in `self`. + #[inline] + pub fn iter(&self) -> slice::Iter<Asset> { + self.map.iter() + } + + /// Inserts `asset` into `self` increasing the [`AssetValue`] at `asset.id`. + #[inline] + pub fn deposit(&mut self, asset: Asset) { + if asset.is_zero() { + return; + } + match self.find(asset.id) { + Ok(index) => self.map[index] += asset.value, + Err(index) => self.map.insert(index, asset), + } + } + + /// Sets the value at the `index` to `value` or removes the entry at `index` if `value == 0`. + #[inline] + fn set_or_remove(&mut self, index: usize, value: AssetValue) { + if value == 0 { + self.map.remove(index); + } else { + self.map[index].value = value; + } + } + + /// Tries to remove `asset` from `self` decreasing the [`AssetValue`] at `asset.id`, returning + /// `false` if this would overflow. To skip the overflow check, use + /// [`withdraw_unchecked`](Self::withdraw_unchecked) instead. + #[inline] + pub fn withdraw(&mut self, asset: Asset) -> bool { + if asset.is_zero() { + return true; + } + if let Ok(index) = self.find(asset.id) { + if let Some(value) = self.map[index].value.checked_sub(asset.value) { + self.set_or_remove(index, value); + return true; + } + } + false + } + + /// Removes `asset` from `self` decreasing the [`AssetValue`] at `asset.id`. + /// + /// # Panics + /// + /// The method panics if removing `asset` would decrease the value of any entry to below zero. + /// To catch this condition, use [`withdraw`](Self::withdraw) instead. + #[inline] + pub fn withdraw_unchecked(&mut self, asset: Asset) { + if asset.is_zero() { + return; + } + match self.find(asset.id) { + Ok(index) => self.set_or_remove(index, self.map[index].value - asset.value), + _ => panic!("Trying to subtract from an Asset with zero value."), + } + } + + /// Removes all entries in `self` which return `false` after applying `f`. + #[inline] + pub fn retain<F>(&mut self, mut f: F) + where + F: FnMut(Asset) -> bool, + { + self.map.retain(move |asset| f(*asset)) + } + + /// Removes all assets from `self`. + #[inline] + pub fn clear(&mut self) { + self.map.clear() + } + + /// Removes all assets with the given `id`. + #[inline] + pub fn remove(&mut self, id: AssetId) -> Option<AssetValue> { + match self.find(id) { + Ok(index) => Some(self.map.remove(index).value), + _ => None, + } + } +} + +impl AsRef<[Asset]> for AssetList { + #[inline] + fn as_ref(&self) -> &[Asset] { + self.map.as_ref() + } +} + +impl Borrow<[Asset]> for AssetList { + #[inline] + fn borrow(&self) -> &[Asset] { + self.map.borrow() + } +} + +impl Deref for AssetList { + type Target = [Asset]; + + #[inline] + fn deref(&self) -> &[Asset] { + self.map.deref() + } +} + +impl From<AssetList> for Vec<Asset> { + #[inline] + fn from(list: AssetList) -> Self { + list.map + } +} + +impl FromIterator<Asset> for AssetList { + #[inline] + fn from_iter<I>(iter: I) -> Self + where + I: IntoIterator<Item = Asset>, + { + let mut list = Self::new(); + iter.into_iter().for_each(|a| list.deposit(a)); + list + } +} + +impl IntoIterator for AssetList { + type Item = Asset; + type IntoIter = vec::IntoIter<Asset>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.map.into_iter() + } +} + /// Asset Map /// /// This trait represents an asset distribution over some [`Key`](Self::Key) type. @@ -587,7 +788,7 @@ pub trait AssetMap: Default { type Key; /// Returns the sum of all the assets in `self`. - fn assets(&self) -> Vec<Asset>; + fn assets(&self) -> AssetList; /// Selects asset keys which total up to at least `asset` in value. fn select(&self, asset: Asset) -> Selection<Self>; @@ -652,17 +853,10 @@ macro_rules! impl_asset_map_for_maps_body { type Key = $k; #[inline] - fn assets(&self) -> Vec<Asset> { - let mut result = Vec::<Asset>::new(); - for (_, assets) in self { - for asset in assets { - match result.binary_search_by_key(&asset.id, move |a| a.id) { - Ok(index) => result[index] += asset.value, - Err(index) => result.insert(index, *asset), - } - } - } - result + fn assets(&self) -> AssetList { + self.iter() + .flat_map(move |(_, assets)| assets.iter().copied()) + .collect() } #[inline] diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index e36a6797a..069abc4b6 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -446,7 +446,7 @@ where Ok(SyncResponse::Partial { deposit, withdraw }) } else { Ok(SyncResponse::Full { - assets: self.assets.assets(), + assets: self.assets.assets().into(), }) } } diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index ba759c85e..b23b2811f 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -17,7 +17,7 @@ //! Full Wallet Implementation use crate::{ - asset::{Asset, AssetId, AssetValue}, + asset::{Asset, AssetId, AssetList, AssetValue}, transfer::{ canonical::{Transaction, TransactionKind}, Configuration, ReceivingKey, @@ -27,10 +27,7 @@ use crate::{ signer::{self, SignResponse, SyncResponse}, }, }; -use alloc::{ - collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}, - vec::Vec, -}; +use alloc::collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}; use core::{fmt::Debug, marker::PhantomData}; #[cfg(feature = "std")] @@ -101,34 +98,20 @@ fn withdraw_unchecked(balance: Option<&mut AssetValue>, withdraw: AssetValue) { .expect("Overdrawn balance state."); } -/// Vector [`BalanceState`] Implementation -pub type VecBalanceState = Vec<Asset>; - -impl BalanceState for VecBalanceState { +impl BalanceState for AssetList { #[inline] fn balance(&self, id: AssetId) -> AssetValue { - self.iter() - .find_map(move |a| a.value_of(id)) - .unwrap_or_default() + self.value(id) } #[inline] fn deposit(&mut self, asset: Asset) { - match self.binary_search_by_key(&asset.id, move |a| a.id) { - Ok(index) => self[index] += asset.value, - Err(index) => self.insert(index, asset), - } + self.deposit(asset) } #[inline] fn withdraw_unchecked(&mut self, asset: Asset) { - // TODO: Use binary search for withdraw. - if !asset.is_zero() { - withdraw_unchecked( - self.iter_mut().find_map(move |a| a.value_of_mut(asset.id)), - asset.value, - ); - } + self.withdraw_unchecked(asset) } #[inline] @@ -147,13 +130,14 @@ macro_rules! impl_balance_state_map_body { #[inline] fn deposit(&mut self, asset: Asset) { + if asset.is_zero() { + return; + } match self.entry(asset.id) { $entry::Vacant(entry) => { entry.insert(asset.value); } - $entry::Occupied(entry) => { - *entry.into_mut() += asset.value; - } + $entry::Occupied(entry) => *entry.into_mut() += asset.value, } } @@ -241,18 +225,18 @@ where Self::new(ledger, Default::default(), signer, Default::default()) } - /// Returns true if `self` contains at least `asset.value` of the asset of kind `asset.id`. - #[inline] - pub fn contains(&self, asset: Asset) -> bool { - self.assets.contains(asset) - } - /// Returns the current balance associated with this `id`. #[inline] pub fn balance(&self, id: AssetId) -> AssetValue { self.assets.balance(id) } + /// Returns true if `self` contains at least `asset.value` of the asset of kind `asset.id`. + #[inline] + pub fn contains(&self, asset: Asset) -> bool { + self.assets.contains(asset) + } + /// Returns a shared reference to the balance state associated to `self`. #[inline] pub fn assets(&self) -> &B { From c592cc095719105e26582f1569da3efdb01f0667 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 14 Jan 2022 16:25:43 -0500 Subject: [PATCH 187/275] fix: move to correct key schedule --- manta-accounting/Cargo.toml | 7 +- manta-accounting/src/asset.rs | 8 +- manta-accounting/src/fs.rs | 1 - manta-accounting/src/key.rs | 467 ++++++++++++++++++-------- manta-accounting/src/wallet/signer.rs | 65 ++-- manta-accounting/src/wallet/state.rs | 24 +- manta-accounting/src/wallet/test.rs | 174 +++++++++- manta-pay/Cargo.toml | 1 - manta-pay/src/config.rs | 8 +- manta-pay/src/key.rs | 120 +------ manta-pay/src/test/ledger.rs | 12 +- manta-pay/src/test/simulation/mod.rs | 108 +++--- 12 files changed, 619 insertions(+), 376 deletions(-) diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 337bb097c..0dcca6e94 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -29,24 +29,23 @@ maintenance = { status = "actively-developed" } cocoon-fs = [ "async-std", "cocoon/std", - "futures", ] # Standard Library std = ["cocoon/std", "async-std"] # Testing Frameworks -test = [] +test = ["futures", "indexmap"] [dependencies] async-std = { version = "1.10.0", optional = true } cocoon = { version = "0.3.0", optional = true, default-features = false } derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } derive_more = { version = "0.99.16", default-features = false, features = ["add", "add_assign", "display", "from", "mul", "mul_assign", "sum"] } -futures = { version = "0.3.19", optional = true } +futures = { version = "0.3.19", optional = true, default-features = false, features = ["alloc"] } +indexmap = { version = "1.8.0", optional = true, default-features = false } manta-crypto = { path = "../manta-crypto" } manta-util = { path = "../manta-util", features = ["zeroize"] } -rayon = { version = "1.5.1", optional = true, default-features = false } serde = { version = "1.0.133", optional = true, default-features = false, features = ["derive"] } [dev-dependencies] diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 2c15674f4..835045a2a 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -712,13 +712,11 @@ impl AssetList { self.map.clear() } - /// Removes all assets with the given `id`. + /// Removes all assets with the given `id`, returning their total value. This method returns + /// `None` in the case that `id` is not stored in `self`. #[inline] pub fn remove(&mut self, id: AssetId) -> Option<AssetValue> { - match self.find(id) { - Ok(index) => Some(self.map.remove(index).value), - _ => None, - } + self.find(id).ok().map(move |i| self.map.remove(i).value) } } diff --git a/manta-accounting/src/fs.rs b/manta-accounting/src/fs.rs index f25b52ffe..4b402096b 100644 --- a/manta-accounting/src/fs.rs +++ b/manta-accounting/src/fs.rs @@ -130,7 +130,6 @@ pub mod cocoon { }; use cocoon_crate::{Cocoon, Error as CocoonError}; use core::fmt; - use futures::future::LocalBoxFuture; use manta_util::{from_variant_impl, zeroize::Zeroizing}; /// Cocoon Loading/Saving Error diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index cc82bb80d..cf5e620da 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -17,54 +17,127 @@ //! Hierarchical Key Derivation Schemes use alloc::vec::Vec; -use core::{fmt::Debug, hash::Hash, marker::PhantomData}; +use core::{ + fmt::{self, Debug}, + hash::Hash, + marker::PhantomData, +}; use manta_crypto::{ key::KeyDerivationFunction, rand::{CryptoRng, RngCore, Sample}, }; +/// Hierarchical Key Derivation Parameter Type +pub type IndexType = u32; + /// Hierarchical Key Derivation Parameter -pub trait HierarchicalKeyDerivationParameter: Copy + Default + PartialOrd { - /// Increments the key parameter by one unit. - fn increment(&mut self); +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct HierarchicalKeyDerivationParameter<M> { + /// Index + index: IndexType, + + /// Type Parameter Marker + __: PhantomData<M>, } -/// Hierarchical Key Derivation Scheme -pub trait HierarchicalKeyDerivationScheme { - /// Account Type - type Account: HierarchicalKeyDerivationParameter + From<usize> + Into<usize>; +impl<M> HierarchicalKeyDerivationParameter<M> { + /// Builds a new [`HierarchicalKeyDerivationParameter`] from `index`. + #[inline] + fn new(index: IndexType) -> Self { + Self { + index, + __: PhantomData, + } + } + + /// Resets the index of `self` to zero. + #[inline] + fn reset(&mut self) { + self.index = 0; + } + + /// Increments the index of `self` by one unit. + #[inline] + fn increment(&mut self) { + self.index += 1; + } - /// Index Type - type Index: HierarchicalKeyDerivationParameter; + /// Returns the index of `self`. + #[inline] + pub fn index(&self) -> IndexType { + self.index + } +} +/// Implements the [`HierarchicalKeyDerivationParameter`] subtype for `$kind` and `$index`. +macro_rules! impl_index_kind { + ($doc:expr, $fmt:expr, $kind:ident, $index:ident) => { + #[doc = $doc] + #[doc = "Kind"] + #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct $kind; + + #[doc = $doc] + pub type $index = HierarchicalKeyDerivationParameter<$kind>; + + impl Debug for $index { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple($fmt).field(&self.index).finish() + } + } + }; +} + +impl_index_kind!("Account Index", "AccountIndex", AccountKind, AccountIndex); +impl_index_kind!("Spend Index", "SpendIndex", SpendKind, SpendIndex); +impl_index_kind!("View Index", "ViewIndex", ViewKind, ViewIndex); + +/// Hierarchical Key Derivation Scheme +pub trait HierarchicalKeyDerivationScheme { /// Secret Key Type type SecretKey; /// Key Derivation Error Type type Error; + /// Derives a secret key for `account` with `spend` and optional `view`. + fn derive( + &self, + account: AccountIndex, + spend: SpendIndex, + view: Option<ViewIndex>, + ) -> Result<Self::SecretKey, Self::Error>; + /// Derives a spend secret key for `account` using the `spend` index. + #[inline] fn derive_spend( &self, - account: Self::Account, - spend: Self::Index, - ) -> Result<Self::SecretKey, Self::Error>; + account: AccountIndex, + spend: SpendIndex, + ) -> Result<Self::SecretKey, Self::Error> { + self.derive(account, spend, None) + } /// Derives a view secret key for `account` using the `spend` and `view` indices. + #[inline] fn derive_view( &self, - account: Self::Account, - spend: Self::Index, - view: Self::Index, - ) -> Result<Self::SecretKey, Self::Error>; + account: AccountIndex, + spend: SpendIndex, + view: ViewIndex, + ) -> Result<Self::SecretKey, Self::Error> { + self.derive(account, spend, Some(view)) + } /// Derives a spend-view pair of secret keys for `account` using the `spend` and `view` indices. #[inline] - fn derive( + fn derive_pair( &self, - account: Self::Account, - spend: Self::Index, - view: Self::Index, + account: AccountIndex, + spend: SpendIndex, + view: ViewIndex, ) -> Result<SecretKeyPair<Self>, Self::Error> { Ok(SecretKeyPair::new( self.derive_spend(account, spend)?, @@ -87,19 +160,24 @@ impl<H> HierarchicalKeyDerivationScheme for &H where H: HierarchicalKeyDerivationScheme + ?Sized, { - type Account = H::Account; - - type Index = H::Index; - type SecretKey = H::SecretKey; - type Error = H::Error; + #[inline] + fn derive( + &self, + account: AccountIndex, + spend: SpendIndex, + view: Option<ViewIndex>, + ) -> Result<Self::SecretKey, Self::Error> { + (*self).derive(account, spend, view) + } + #[inline] fn derive_spend( &self, - account: Self::Account, - spend: Self::Index, + account: AccountIndex, + spend: SpendIndex, ) -> Result<Self::SecretKey, Self::Error> { (*self).derive_spend(account, spend) } @@ -107,9 +185,9 @@ where #[inline] fn derive_view( &self, - account: Self::Account, - spend: Self::Index, - view: Self::Index, + account: AccountIndex, + spend: SpendIndex, + view: ViewIndex, ) -> Result<Self::SecretKey, Self::Error> { (*self).derive_view(account, spend, view) } @@ -150,19 +228,26 @@ where H: HierarchicalKeyDerivationScheme, K: KeyDerivationFunction<Key = H::SecretKey>, { - type Account = H::Account; - - type Index = H::Index; - type SecretKey = K::Output; - type Error = H::Error; + #[inline] + fn derive( + &self, + account: AccountIndex, + spend: SpendIndex, + view: Option<ViewIndex>, + ) -> Result<Self::SecretKey, Self::Error> { + self.base + .derive(account, spend, view) + .map(move |k| K::derive(&k)) + } + #[inline] fn derive_spend( &self, - account: Self::Account, - spend: Self::Index, + account: AccountIndex, + spend: SpendIndex, ) -> Result<Self::SecretKey, Self::Error> { self.base .derive_spend(account, spend) @@ -172,9 +257,9 @@ where #[inline] fn derive_view( &self, - account: Self::Account, - spend: Self::Index, - view: Self::Index, + account: AccountIndex, + spend: SpendIndex, + view: ViewIndex, ) -> Result<Self::SecretKey, Self::Error> { self.base .derive_view(account, spend, view) @@ -248,61 +333,93 @@ where } /// Key Index Type -#[derive(derivative::Derivative)] -#[derivative( - Clone, - Copy, - Debug(bound = "H::Index: Debug"), - Default, - Eq, - Hash(bound = "H::Index: Hash"), - PartialEq -)] -pub struct Index<H> -where - H: HierarchicalKeyDerivationScheme + ?Sized, -{ +#[derive(Clone, Copy, Default, Eq, Hash, PartialEq)] +pub struct Index { /// Spend Part of the Key Index - pub spend: H::Index, + pub spend: SpendIndex, /// View Part of the Key Index - pub view: H::Index, + pub view: ViewIndex, } -impl<H> Index<H> -where - H: HierarchicalKeyDerivationScheme + ?Sized, -{ +impl Index { /// Builds a new [`Index`] using `spend` and `view`. #[inline] - pub fn new(spend: H::Index, view: H::Index) -> Self { + pub fn new(spend: SpendIndex, view: ViewIndex) -> Self { Self { spend, view } } } -/// View Key Selection -pub struct ViewKeySelection<H, T> -where - H: HierarchicalKeyDerivationScheme + ?Sized, -{ - /// Selection Index - pub index: Index<H>, +impl Debug for Index { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Index") + .field("spend", &self.spend.index) + .field("view", &self.view.index) + .finish() + } +} - /// Selection Key Pair - pub keypair: SecretKeyPair<H>, +/// Maximum Index Bounds +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct MaxIndex { + /// Maximum View Indices + /// + /// The maximum view indices are sorted in order of each spend index. Therefore, the maximum + /// spend key is one less than the length of this vector. + view: Vec<ViewIndex>, +} - /// Selection Item - pub item: T, +impl MaxIndex { + /// Returns the maximum spend index. + #[inline] + pub fn spend(&self) -> SpendIndex { + SpendIndex::new((self.view.len() - 1) as u32) + } + + /// Returns the set of maximum view indices for each spend key in the range given by + /// [`spend`](Self::spend). + /// + /// # Note + /// + /// This array is always inhabited since there is always at least one spend key we can build and + /// so its corresponding view key must have a maximal index. + #[inline] + pub fn view(&self) -> &[ViewIndex] { + &self.view + } + + /// Increments the maximum spend index. + #[inline] + fn increment_spend(&mut self) -> &Self { + self.view.push(Default::default()); + &*self + } + + /// Increments the maximum view index for the given `spend` index. + #[inline] + fn increment_view(&mut self, spend: SpendIndex) -> Option<&Self> { + self.view.get_mut(spend.index() as usize)?.increment(); + Some(&*self) + } +} + +impl Default for MaxIndex { + #[inline] + fn default() -> Self { + Self { + view: vec![Default::default()], + } + } } /// Account Keys #[derive(derivative::Derivative)] #[derivative( - Clone(bound = ""), Copy(bound = ""), - Debug(bound = "H: Debug, H::Account: Debug, H::Index: Debug"), + Debug(bound = "H: Debug"), Eq(bound = "H: Eq"), - Hash(bound = "H: Hash, H::Account: Hash, H::Index: Hash"), + Hash(bound = "H: Hash"), PartialEq(bound = "H: PartialEq") )] pub struct AccountKeys<'h, H> @@ -312,11 +429,11 @@ where /// Hierarchical Key Derivation Scheme keys: &'h H, - /// Account Parameter - account: H::Account, + /// Account Index + account: AccountIndex, - /// Maximum Key Index - max_index: Index<H>, + /// Maximum Indices + max_index: &'h MaxIndex, } impl<'h, H> AccountKeys<'h, H> @@ -325,7 +442,7 @@ where { /// Builds a new [`AccountKeys`] from `keys`, `account`, and `max_index`. #[inline] - fn new(keys: &'h H, account: H::Account, max_index: Index<H>) -> Self { + fn new(keys: &'h H, account: AccountIndex, max_index: &'h MaxIndex) -> Self { Self { keys, account, @@ -335,30 +452,30 @@ where /// Performs the bounds check on `spend` and then runs `f`. #[inline] - fn with_spend_bounds_check<T, F>(&self, spend: H::Index, f: F) -> Result<T, Error<H>> + fn with_spend_bounds_check<T, F>(&self, spend: SpendIndex, f: F) -> Result<T, Error<H>> where - F: FnOnce(&Self, H::Index) -> Result<T, H::Error>, + F: FnOnce(&Self, SpendIndex) -> Result<T, H::Error>, { - if spend <= self.max_index.spend { + if spend <= self.max_index.spend() { f(self, spend).map_err(Error::KeyDerivationError) } else { Err(Error::ExceedingCurrentMaximumSpendIndex) } } - /// Performs the bounds check on `spend` and `view` and then runs `f`. + /// Performs the bounds check on `view` and then runs `f`. #[inline] fn with_view_bounds_check<T, F>( &self, - spend: H::Index, - view: H::Index, + spend: SpendIndex, + view: ViewIndex, f: F, ) -> Result<T, Error<H>> where - F: FnOnce(&Self, H::Index, H::Index) -> Result<T, H::Error>, + F: FnOnce(&Self, SpendIndex, ViewIndex) -> Result<T, H::Error>, { - if spend <= self.max_index.spend { - if view <= self.max_index.view { + if spend <= self.max_index.spend() { + if view <= self.max_index.view[spend.index() as usize] { f(self, spend, view).map_err(Error::KeyDerivationError) } else { Err(Error::ExceedingCurrentMaximumViewIndex) @@ -370,7 +487,7 @@ where /// Derives the spend key for this account at `spend` without performing bounds checks. #[inline] - fn derive_spend(&self, spend: H::Index) -> Result<H::SecretKey, H::Error> { + fn derive_spend(&self, spend: SpendIndex) -> Result<H::SecretKey, H::Error> { self.keys.derive_spend(self.account, spend) } @@ -383,54 +500,60 @@ where /// Returns the spend key for this account at the `spend` index, if it does not exceed the /// maximum index. #[inline] - pub fn spend_key(&self, spend: H::Index) -> Result<H::SecretKey, Error<H>> { + pub fn spend_key(&self, spend: SpendIndex) -> Result<H::SecretKey, Error<H>> { self.with_spend_bounds_check(spend, Self::derive_spend) } - /// Derives the view key for this account at `spend` and `view` without performing bounds - /// checks. + /// Derives the view key for this account at the `spend` and `view` indices without performing + /// bounds checks. #[inline] - fn derive_view(&self, spend: H::Index, view: H::Index) -> Result<H::SecretKey, H::Error> { + fn derive_view(&self, spend: SpendIndex, view: ViewIndex) -> Result<H::SecretKey, H::Error> { self.keys.derive_view(self.account, spend, view) } /// Returns the default view key for this account. #[inline] pub fn default_view_key(&self) -> Result<H::SecretKey, H::Error> { - let default_index = Default::default(); - self.derive_view(default_index, default_index) + self.derive_view(Default::default(), Default::default()) } /// Returns the view key for this account at `index`, if it does not exceed the maximum index. #[inline] - pub fn view_key(&self, index: Index<H>) -> Result<H::SecretKey, Error<H>> { + pub fn view_key(&self, index: Index) -> Result<H::SecretKey, Error<H>> { self.view_key_with(index.spend, index.view) } - /// Returns the view key for this account at the `spend` and `view` indices, if those indices - /// do not exceed the maximum indices. + /// Returns the view key for this account at the `spend` and `view` indices, if it does not + /// exceed the maximum index. #[inline] - pub fn view_key_with(&self, spend: H::Index, view: H::Index) -> Result<H::SecretKey, Error<H>> { + pub fn view_key_with( + &self, + spend: SpendIndex, + view: ViewIndex, + ) -> Result<H::SecretKey, Error<H>> { self.with_view_bounds_check(spend, view, Self::derive_view) } /// Derives the secret key pair for this account at `spend` and `view` without performing bounds /// checks. #[inline] - fn derive(&self, spend: H::Index, view: H::Index) -> Result<SecretKeyPair<H>, H::Error> { - self.keys.derive(self.account, spend, view) + fn derive_pair( + &self, + spend: SpendIndex, + view: ViewIndex, + ) -> Result<SecretKeyPair<H>, H::Error> { + self.keys.derive_pair(self.account, spend, view) } /// Returns the default secret key pair for this account. #[inline] pub fn default_keypair(&self) -> Result<SecretKeyPair<H>, H::Error> { - let default_index = Default::default(); - self.derive(default_index, default_index) + self.derive_pair(Default::default(), Default::default()) } /// Returns the key pair for this account at `index`, if it does not exceed the maximum index. #[inline] - pub fn keypair(&self, index: Index<H>) -> Result<SecretKeyPair<H>, Error<H>> { + pub fn keypair(&self, index: Index) -> Result<SecretKeyPair<H>, Error<H>> { self.keypair_with(index.spend, index.view) } @@ -439,10 +562,10 @@ where #[inline] pub fn keypair_with( &self, - spend: H::Index, - view: H::Index, + spend: SpendIndex, + view: ViewIndex, ) -> Result<SecretKeyPair<H>, Error<H>> { - self.with_view_bounds_check(spend, view, Self::derive) + self.with_view_bounds_check(spend, view, Self::derive_pair) } /// Applies `f` to the view keys generated by `self` returning the first non-`None` result with @@ -475,40 +598,61 @@ where index.view.increment(); } index.spend.increment(); + index.view.reset(); } } } -/// Account Map Trait -pub trait AccountMap<H> +// NOTE: We need this because `derivative::Derivative` doesn't derive this trait properly. +impl<'h, H> Clone for AccountKeys<'h, H> +where + H: HierarchicalKeyDerivationScheme + ?Sized, +{ + #[inline] + fn clone(&self) -> Self { + Self::new(self.keys, self.account, self.max_index) + } +} + +/// View Key Selection +pub struct ViewKeySelection<H, T> where H: HierarchicalKeyDerivationScheme + ?Sized, { + /// Selection Index + pub index: Index, + + /// Selection Key Pair + pub keypair: SecretKeyPair<H>, + + /// Selection Item + pub item: T, +} + +/// Account Map Trait +pub trait AccountMap { /// Builds a new [`AccountMap`] with a starting account with default max indices. fn new() -> Self; /// Returns the maximum spend and view indices for `account`, if it exists. - fn max_index(&self, account: H::Account) -> Option<Index<H>>; + fn max_index(&self, account: AccountIndex) -> Option<&MaxIndex>; /// Adds a new account to the map, returning the new account parameter. - fn create_account(&mut self) -> H::Account; + fn create_account(&mut self) -> AccountIndex; /// Increments the maximum spend index for `account`, if it exists, returning the current /// maximum indices. - fn increment_spend(&mut self, account: H::Account) -> Option<Index<H>>; + fn increment_spend(&mut self, account: AccountIndex) -> Option<&MaxIndex>; /// Increments the maximum view index for `account`, if it exists, returning the current /// maximum indices. - fn increment_view(&mut self, account: H::Account) -> Option<Index<H>>; + fn increment_view(&mut self, account: AccountIndex, spend: SpendIndex) -> Option<&MaxIndex>; } /// [`Vec`] Account Map Type -pub type VecAccountMap<H> = Vec<Index<H>>; +pub type VecAccountMap = Vec<MaxIndex>; -impl<H> AccountMap<H> for VecAccountMap<H> -where - H: HierarchicalKeyDerivationScheme + ?Sized, -{ +impl AccountMap for VecAccountMap { #[inline] fn new() -> Self { let mut this = Self::new(); @@ -517,30 +661,31 @@ where } #[inline] - fn max_index(&self, account: H::Account) -> Option<Index<H>> { - self.get(account.into()).copied() + fn max_index(&self, account: AccountIndex) -> Option<&MaxIndex> { + self.get(account.index() as usize) } #[inline] - fn create_account(&mut self) -> H::Account { + fn create_account(&mut self) -> AccountIndex { + let index = AccountIndex::new( + self.len() + .try_into() + .expect("AccountIndex is not allowed to exceed IndexType::MAX."), + ); self.push(Default::default()); - (self.len() - 1).into() + index } #[inline] - fn increment_spend(&mut self, account: H::Account) -> Option<Index<H>> { - self.get_mut(account.into()).map(move |index| { - index.spend.increment(); - *index - }) + fn increment_spend(&mut self, account: AccountIndex) -> Option<&MaxIndex> { + self.get_mut(account.index() as usize) + .map(MaxIndex::increment_spend) } #[inline] - fn increment_view(&mut self, account: H::Account) -> Option<Index<H>> { - self.get_mut(account.into()).map(move |index| { - index.view.increment(); - *index - }) + fn increment_view(&mut self, account: AccountIndex, spend: SpendIndex) -> Option<&MaxIndex> { + self.get_mut(account.index() as usize) + .and_then(move |index| index.increment_view(spend)) } } @@ -554,10 +699,10 @@ where Hash(bound = "H: Hash, M: Hash"), PartialEq(bound = "H: PartialEq, M: PartialEq") )] -pub struct AccountTable<H, M = VecAccountMap<H>> +pub struct AccountTable<H, M = VecAccountMap> where H: HierarchicalKeyDerivationScheme, - M: AccountMap<H>, + M: AccountMap, { /// Hierarchical Key Derivation Scheme keys: H, @@ -569,7 +714,7 @@ where impl<H, M> AccountTable<H, M> where H: HierarchicalKeyDerivationScheme, - M: AccountMap<H>, + M: AccountMap, { /// Builds a new [`AccountTable`] using `keys` and the default account map. #[inline] @@ -588,8 +733,8 @@ where #[inline] pub fn keypair( &self, - account: H::Account, - index: Index<H>, + account: AccountIndex, + index: Index, ) -> Option<Result<SecretKeyPair<H>, Error<H>>> { self.keypair_with(account, index.spend, index.view) } @@ -599,16 +744,16 @@ where #[inline] pub fn keypair_with( &self, - account: H::Account, - spend: H::Index, - view: H::Index, + account: AccountIndex, + spend: SpendIndex, + view: ViewIndex, ) -> Option<Result<SecretKeyPair<H>, Error<H>>> { self.get(account).map(move |k| k.keypair_with(spend, view)) } /// Returns the account keys for `account` if it exists. #[inline] - pub fn get(&self, account: H::Account) -> Option<AccountKeys<H>> { + pub fn get(&self, account: AccountIndex) -> Option<AccountKeys<H>> { Some(AccountKeys::new( &self.keys, account, @@ -622,31 +767,57 @@ where self.get(Default::default()).unwrap() } + /// Returns the maximum spend and view indices for `account`, if it exists. + #[inline] + pub fn max_index(&self, account: AccountIndex) -> Option<&MaxIndex> { + self.accounts.max_index(account) + } + /// Adds a new account to the map, returning the new account parameter. #[inline] - pub fn create_account(&mut self) -> H::Account { + pub fn create_account(&mut self) -> AccountIndex { self.accounts.create_account() } /// Increments the maximum spend index for `account`, if it exists, returning the current /// maximum indices. #[inline] - pub fn increment_spend(&mut self, account: H::Account) -> Option<Index<H>> { + pub fn increment_spend(&mut self, account: AccountIndex) -> Option<&MaxIndex> { self.accounts.increment_spend(account) } /// Increments the maximum view index for `account`, if it exists, returning the current /// maximum indices. #[inline] - pub fn increment_view(&mut self, account: H::Account) -> Option<Index<H>> { - self.accounts.increment_view(account) + pub fn increment_view( + &mut self, + account: AccountIndex, + spend: SpendIndex, + ) -> Option<&MaxIndex> { + self.accounts.increment_view(account, spend) + } + + /// Increments the spend index and returns the [`Index`] pair for the new spend index and its + /// first view key. + #[inline] + pub fn next_index(&mut self, account: AccountIndex) -> Option<Index> { + let max_index = self.increment_spend(account)?; + Some(Index::new(max_index.spend(), Default::default())) + } + + /// Increments the spend index and returns the [`SecretKeyPair`] for the new spend index and + /// its first view key. + #[inline] + pub fn next(&mut self, account: AccountIndex) -> Option<Result<SecretKeyPair<H>, H::Error>> { + let index = self.next_index(account)?; + Some(self.keys.derive_pair(account, index.spend, index.view)) } } impl<H, M> Default for AccountTable<H, M> where H: Default + HierarchicalKeyDerivationScheme, - M: AccountMap<H>, + M: AccountMap, { #[inline] fn default() -> Self { diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 069abc4b6..8cbf85625 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -30,7 +30,7 @@ use crate::{ asset::{Asset, AssetId, AssetMap, AssetValue}, - key::{self, HierarchicalKeyDerivationScheme, ViewKeySelection}, + key::{self, HierarchicalKeyDerivationScheme, SpendIndex, ViewKeySelection}, transfer::{ self, batch::Join, @@ -66,9 +66,6 @@ pub trait Connection<C> where C: transfer::Configuration, { - /// Key Index Type - type KeyIndex; - /// Error Type type Error; @@ -87,11 +84,8 @@ where /// Signs a `transaction` and returns the ledger transfer posts if successful. fn sign(&mut self, transaction: Transaction<C>) -> LocalBoxFuture<SignResult<C, Self>>; - /// Returns a [`ReceivingKey`] for `self` to receive assets with `index`. - fn receiving_key( - &mut self, - index: Self::KeyIndex, - ) -> LocalBoxFuture<ReceivingKeyResult<C, Self>>; + /// Returns a new [`ReceivingKey`] for `self` to receive assets. + fn receiving_key(&mut self) -> LocalBoxFuture<ReceivingKeyResult<C, Self>>; } /// Synchronization Result @@ -185,24 +179,11 @@ pub trait Configuration: transfer::Configuration { type Rng: CryptoRng + RngCore; } -/// Index Type -pub type Index<C> = key::Index<<C as Configuration>::HierarchicalKeyDerivationScheme>; - -/// Hierarchical Key Derivation Scheme Index -type HierarchicalKeyDerivationSchemeIndex<C> = - <<C as Configuration>::HierarchicalKeyDerivationScheme as HierarchicalKeyDerivationScheme>::Index; - /// Account Table Type pub type AccountTable<C> = key::AccountTable<<C as Configuration>::HierarchicalKeyDerivationScheme>; -/// Spend Index Type -pub type SpendIndex<C> = HierarchicalKeyDerivationSchemeIndex<C>; - -/// View Index Type -pub type ViewIndex<C> = HierarchicalKeyDerivationSchemeIndex<C>; - /// Asset Map Key Type -pub type AssetMapKey<C> = (SpendIndex<C>, PublicKey<C>); +pub type AssetMapKey<C> = (SpendIndex, PublicKey<C>); /// Proving Context Cache Error Type pub type ProvingContextCacheError<C> = @@ -230,10 +211,7 @@ where MissingUtxoMembershipProof, /// Insufficient Balance - InsufficientBalance { - /// Attempting to withdraw the given asset - withdraw: Asset, - }, + InsufficientBalance(Asset), /// Inconsistent Synchronization InconsistentSynchronization { @@ -353,12 +331,16 @@ where self.utxo_set.remove_proof(&utxo); self.assets .remove((index.spend, ephemeral_public_key), asset); - withdraw.push(asset); + if !asset.is_zero() { + withdraw.push(asset); + } } else { self.utxo_set.insert(&utxo); self.assets .insert((index.spend, ephemeral_public_key), asset); - deposit.push(asset); + if !asset.is_zero() { + deposit.push(asset); + } return Ok(()); } } @@ -389,7 +371,9 @@ where let known_void_number = C::void_number(&parameters.void_number_hash, &utxo, secret_key); if *void_number == known_void_number { utxo_set.remove_proof(&utxo); - withdraw.push(asset); + if !asset.is_zero() { + withdraw.push(asset); + } false } else { true @@ -409,7 +393,6 @@ where I: Iterator<Item = (Utxo<C>, EncryptedNote<C>)>, { // FIXME: Use a more efficient synchronization algorithm for partial vs full. - // let mut deposit = Vec::new(); let mut withdraw = Vec::new(); for (utxo, encrypted_note) in inserts { @@ -517,7 +500,7 @@ where ) -> Result<Selection<C>, Error<C>> { let selection = self.assets.select(asset); if selection.is_empty() { - return Err(Error::InsufficientBalance { withdraw: asset }); + return Err(Error::InsufficientBalance(asset)); } Selection::new(selection, move |k, v| { self.build_pre_sender(parameters, k, asset.id.with(v)) @@ -902,17 +885,22 @@ where #[inline] pub async fn sign(&mut self, transaction: Transaction<C>) -> SignResult<C, Self> { // TODO: Should we do a time-based release mechanism to amortize the cost of reading/writing - // to disk? + // to the proving context cache? let result = self.sign_internal(transaction).await; self.state.utxo_set.rollback(); self.parameters.proving_context.release().await; result } - /// Returns a [`ReceivingKey`] for `self` to receive assets with `index`. + /// Returns a new [`ReceivingKey`] for `self` to receive assets. #[inline] - pub fn receiving_key(&self, index: Index<C>) -> ReceivingKeyResult<C, Self> { - let keypair = self.state.accounts.get_default().keypair(index)?; + pub fn receiving_key(&mut self) -> ReceivingKeyResult<C, Self> { + let keypair = self + .state + .accounts + .next(Default::default()) + .unwrap() + .map_err(key::Error::KeyDerivationError)?; Ok(SpendingKey::new(keypair.spend, keypair.view) .derive(&self.parameters.parameters.key_agreement)) } @@ -922,7 +910,6 @@ impl<C> Connection<C> for Signer<C> where C: Configuration, { - type KeyIndex = Index<C>; type Error = Error<C>; #[inline] @@ -945,7 +932,7 @@ where } #[inline] - fn receiving_key(&mut self, index: Index<C>) -> LocalBoxFuture<ReceivingKeyResult<C, Self>> { - Box::pin(async move { (&*self).receiving_key(index) }) + fn receiving_key(&mut self) -> LocalBoxFuture<ReceivingKeyResult<C, Self>> { + Box::pin(async move { self.receiving_key() }) } } diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index b23b2811f..09dab572c 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -89,15 +89,6 @@ pub trait BalanceState: Default { fn clear(&mut self); } -/// Performs an unchecked withdraw on `balance`, panicking on overflow. -#[inline] -fn withdraw_unchecked(balance: Option<&mut AssetValue>, withdraw: AssetValue) { - let balance = balance.expect("Trying to withdraw from a zero balance."); - *balance = balance - .checked_sub(withdraw) - .expect("Overdrawn balance state."); -} - impl BalanceState for AssetList { #[inline] fn balance(&self, id: AssetId) -> AssetValue { @@ -120,6 +111,15 @@ impl BalanceState for AssetList { } } +/// Performs an unchecked withdraw on `balance`, panicking on overflow. +#[inline] +fn withdraw_unchecked(balance: Option<&mut AssetValue>, withdraw: AssetValue) { + let balance = balance.expect("Trying to withdraw from a zero balance."); + *balance = balance + .checked_sub(withdraw) + .expect("Overdrawn balance state."); +} + /// Adds implementation of [`BalanceState`] for a map type with the given `$entry` type. macro_rules! impl_balance_state_map_body { ($entry:tt) => { @@ -336,10 +336,10 @@ where Ok(success) } - /// Returns a [`ReceivingKey`] for `self` to receive assets with `index`. + /// Returns a new [`ReceivingKey`] for `self` to receive assets. #[inline] - pub async fn receiving_key(&mut self, index: S::KeyIndex) -> Result<ReceivingKey<C>, S::Error> { - self.signer.receiving_key(index).await + pub async fn receiving_key(&mut self) -> Result<ReceivingKey<C>, S::Error> { + self.signer.receiving_key().await } } diff --git a/manta-accounting/src/wallet/test.rs b/manta-accounting/src/wallet/test.rs index e032a146d..b5872b56a 100644 --- a/manta-accounting/src/wallet/test.rs +++ b/manta-accounting/src/wallet/test.rs @@ -14,6 +14,176 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Testing Framework +//! Testing and Simulation Framework -// TODO: Add tests for the asynchronous wallet protocol. +use crate::{ + asset::Asset, + transfer::{canonical::Transaction, Configuration, PublicKey, ReceivingKey}, + wallet::{self, ledger, signer, Wallet}, +}; +use alloc::rc::Rc; +use async_std::sync::RwLock; +use core::{hash::Hash, marker::PhantomData}; +use manta_crypto::rand::{CryptoRng, RngCore}; +use manta_util::future::LocalBoxFuture; +use std::collections::HashSet; + +/// Actor Simulation +pub mod sim { + use alloc::vec::Vec; + use core::{fmt::Debug, hash::Hash}; + use futures::stream::{self, select_all::SelectAll, Stream}; + use manta_crypto::rand::{CryptoRng, RngCore}; + use manta_util::future::LocalBoxFuture; + + /// Abstract Simulation + pub trait Simulation { + /// Actor Type + type Actor; + + /// Event Type + type Event; + + /// Runs the given `actor` returning a future event. + /// + /// This method should return `None` when the actor is done being simulated for this round + /// of the simulation. + fn step<'s, R>( + &'s self, + actor: &'s mut Self::Actor, + rng: &'s mut R, + ) -> LocalBoxFuture<'s, Option<Self::Event>> + where + R: CryptoRng + RngCore + ?Sized; + } + + /// Simulator + #[derive(derivative::Derivative)] + #[derivative( + Clone(bound = "S: Clone, S::Actor: Clone"), + Debug(bound = "S: Debug, S::Actor: Debug"), + Default(bound = "S: Default"), + Eq(bound = "S: Eq, S::Actor: Eq"), + Hash(bound = "S: Hash, S::Actor: Hash"), + PartialEq(bound = "S: PartialEq, S::Actor: PartialEq") + )] + pub struct Simulator<S> + where + S: Simulation, + { + /// Simulation + pub simulation: S, + + /// Actors + pub actors: Vec<S::Actor>, + } + + impl<S> Simulator<S> + where + S: Simulation, + { + /// Builds a new [`Simulator`] from `simulation` and `actors`. + #[inline] + pub fn new(simulation: S, actors: Vec<S::Actor>) -> Self { + Self { simulation, actors } + } + + /// Builds a stream of future events for a particular `actor`. + #[inline] + fn build_actor_stream<'s, R>( + simulator: &'s S, + actor: &'s mut S::Actor, + mut rng: R, + ) -> impl 's + Stream<Item = S::Event> + where + R: 's + CryptoRng + RngCore, + { + stream::poll_fn(move |ctx| simulator.step(actor, &mut rng).as_mut().poll(ctx)) + } + + /// Runs the simulator using `rng`, returning a stream of future events. + #[inline] + pub async fn run<'s, R, F>(&'s mut self, mut rng: F) -> impl 's + Stream<Item = S::Event> + where + F: FnMut() -> R, + R: 's + CryptoRng + RngCore, + { + let mut streams = SelectAll::new(); + for actor in &mut self.actors { + streams.push(Self::build_actor_stream(&self.simulation, actor, rng())); + } + streams + } + } +} + +/// Simulation Action Space +pub enum Action<C> +where + C: Configuration, +{ + /// Public Deposit Action + PublicDeposit(Asset), + + /// Public Withdraw Action + PublicWithdraw(Asset), + + /// Post Transaction + Post(Transaction<C>), + + /// Generate Public Key + GeneratePublicKey, +} + +/// Public Key Database +pub type PublicKeyDatabase<C> = HashSet<ReceivingKey<C>>; + +/// Shared Public Key Database +pub type SharedPublicKeyDatabase<C> = Rc<RwLock<PublicKeyDatabase<C>>>; + +/// +pub struct Simulation<C, L, S> +where + C: Configuration, + L: ledger::Connection<C>, + S: signer::Connection<C>, +{ + /// + public_keys: SharedPublicKeyDatabase<C>, + + /// + __: PhantomData<(L, S)>, +} + +impl<C, L, S> sim::Simulation for Simulation<C, L, S> +where + C: Configuration, + L: ledger::Connection<C>, + S: signer::Connection<C>, + PublicKey<C>: Eq + Hash, +{ + type Actor = Wallet<C, L, S>; + type Event = Result<(), wallet::Error<C, L, S>>; + + #[inline] + fn step<'s, R>( + &'s self, + actor: &'s mut Self::Actor, + rng: &'s mut R, + ) -> LocalBoxFuture<'s, Option<Self::Event>> + where + R: CryptoRng + RngCore + ?Sized, + { + let _ = rng; + Box::pin(async move { + // TODO: + Some(match actor.receiving_key().await { + Ok(key) => { + self.public_keys.write().await.insert(key); + Ok(()) + } + Err(err) => Err(wallet::Error::SignerError(err)), + }) + }) + } +} diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 177192209..6474406d1 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -81,7 +81,6 @@ rand_chacha = { version = "0.3.1", default-features = false } scale-codec = { package = "parity-scale-codec", version = "2.3.1", optional = true, default-features = false, features = ["derive"] } serde = { version = "1.0.133", optional = true, default-features = false, features = ["derive"] } statrs = { version = "0.15.0", optional = true, default-features = false } -x25519-dalek = { git = "https://github.com/Manta-Network/x25519-dalek", default-features = false, features = ["u64_backend"] } zk-garage-plonk = { package = "plonk", git = "https://github.com/zk-garage/plonk", optional = true, default-features = false } [dev-dependencies] diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 079b83609..c51c74076 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -95,8 +95,8 @@ pub type Poseidon2Var = poseidon::Hash<PoseidonSpec<2>, 2, Compiler>; impl poseidon::arkworks::Specification for PoseidonSpec<2> { type Field = ConstraintField; - const FULL_ROUNDS: usize = 10; - const PARTIAL_ROUNDS: usize = 10; + const FULL_ROUNDS: usize = 8; + const PARTIAL_ROUNDS: usize = 57; const SBOX_EXPONENT: u64 = 5; } @@ -108,8 +108,8 @@ pub type Poseidon4Var = poseidon::Hash<PoseidonSpec<4>, 4, Compiler>; impl poseidon::arkworks::Specification for PoseidonSpec<4> { type Field = ConstraintField; - const FULL_ROUNDS: usize = 10; - const PARTIAL_ROUNDS: usize = 10; + const FULL_ROUNDS: usize = 8; + const PARTIAL_ROUNDS: usize = 60; const SBOX_EXPONENT: u64 = 5; } diff --git a/manta-pay/src/key.rs b/manta-pay/src/key.rs index 2ddda3081..2c24e92a9 100644 --- a/manta-pay/src/key.rs +++ b/manta-pay/src/key.rs @@ -26,9 +26,9 @@ use alloc::{format, string::String}; use bip32::{Seed, XPrv}; -use core::{marker::PhantomData, num::ParseIntError, str::FromStr}; +use core::marker::PhantomData; use manta_accounting::key::{ - self, HierarchicalKeyDerivationParameter, HierarchicalKeyDerivationScheme, + self, AccountIndex, HierarchicalKeyDerivationScheme, SpendIndex, ViewIndex, }; use manta_crypto::rand::{CryptoRng, RngCore, Sample, Standard}; use manta_util::{create_seal, seal}; @@ -89,78 +89,6 @@ pub const CALAMARI_COIN_TYPE_ID: CoinTypeId = 612; impl_coin_type!(Calamari, CALAMARI_COIN_TYPE_ID); -/// Parse Parameter Error -pub struct ParseParameterError(ParseIntError); - -/// Implements some [`From`] traits for `$name`. -macro_rules! impl_from_for_parameter { - ($name:ty, $($from:ty),+$(,)?) => { - $( - impl From<$from> for $name { - #[inline] - fn from(t: $from) -> Self { - Self(t.into()) - } - } - )+ - } -} - -/// Implements the [`HierarchicalKeyDerivationParameter`] trait for `$name`. -macro_rules! impl_parameter { - ($name:ty) => { - impl HierarchicalKeyDerivationParameter for $name { - #[inline] - fn increment(&mut self) { - self.0 += 1; - } - } - - impl_from_for_parameter!($name, bool, u8, u16, u32); - - impl FromStr for $name { - type Err = ParseParameterError; - - #[inline] - fn from_str(s: &str) -> Result<Self, Self::Err> { - Ok(Self(s.parse().map_err(ParseParameterError)?)) - } - } - }; -} - -/// Account Parameter Type -type AccountParameterType = u64; - -/// Account Parameter -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct AccountParameter(AccountParameterType); - -impl_parameter!(AccountParameter); - -impl From<usize> for AccountParameter { - #[inline] - fn from(index: usize) -> Self { - Self(index as u64) - } -} - -impl From<AccountParameter> for usize { - #[inline] - fn from(parameter: AccountParameter) -> Self { - parameter.0 as usize - } -} - -/// Index Parameter Type -type IndexParameterType = u128; - -/// Index Parameter -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct IndexParameter(IndexParameterType); - -impl_parameter!(IndexParameter); - /// Testnet [`KeySecret`] Type pub type TestnetKeySecret = KeySecret<Testnet>; @@ -224,22 +152,18 @@ where /// [`BIP-0044`]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki #[inline] #[must_use] -pub fn path_string<C>( - account: AccountParameterType, - spend: IndexParameterType, - view: IndexParameterType, -) -> String +pub fn path_string<C>(account: AccountIndex, spend: SpendIndex, view: Option<ViewIndex>) -> String where C: CoinType, { const BIP_44_PURPOSE_ID: u8 = 44; format!( - "m/{}'/{}'/{}'/{}/{}", + "m/{}'/{}'/{}'/{}'/{}'", BIP_44_PURPOSE_ID, C::COIN_TYPE_ID, - account, - spend, - view, + account.index(), + spend.index(), + view.map(move |v| v.index() + 1).unwrap_or_default(), ) } @@ -248,36 +172,16 @@ where C: CoinType, { type SecretKey = XPrv; - - type Account = AccountParameter; - - type Index = IndexParameter; - type Error = Error; #[inline] - fn derive_spend( - &self, - account: Self::Account, - spend: Self::Index, - ) -> Result<Self::SecretKey, Self::Error> { - XPrv::derive_from_path( - &self.seed, - &path_string::<C>(account.0, spend.0, 0).parse()?, - ) - } - - #[inline] - fn derive_view( + fn derive( &self, - account: Self::Account, - spend: Self::Index, - view: Self::Index, + account: AccountIndex, + spend: SpendIndex, + view: Option<ViewIndex>, ) -> Result<Self::SecretKey, Self::Error> { - XPrv::derive_from_path( - &self.seed, - &path_string::<C>(account.0, spend.0, view.0 + 1).parse()?, - ) + XPrv::derive_from_path(&self.seed, &path_string::<C>(account, spend, view).parse()?) } } diff --git a/manta-pay/src/test/ledger.rs b/manta-pay/src/test/ledger.rs index 2b0749753..65c0de5bb 100644 --- a/manta-pay/src/test/ledger.rs +++ b/manta-pay/src/test/ledger.rs @@ -547,17 +547,17 @@ mod test { ); let alice_key = alice - .receiving_key(Default::default()) + .receiving_key() .await .expect("Unable to get Alices's public key."); let bob_key = bob - .receiving_key(Default::default()) + .receiving_key() .await .expect("Unable to get Bob's public key."); let charlie_key = charlie - .receiving_key(Default::default()) + .receiving_key() .await .expect("Unable to get Charlie's public key."); @@ -565,6 +565,7 @@ mod test { println!("Bob [wallet]: {:?}", bob.assets()).await; println!("Charlie [wallet]: {:?}", charlie.assets()).await; + println!("ALICE MINT 1000").await; alice .post(Transaction::Mint(AssetId(0).value(1000))) .await @@ -572,6 +573,7 @@ mod test { alice.sync().await.expect("Unable to sync."); println!("Alice [wallet]: {:?}", alice.assets()).await; + println!("ALICE PRIVATE TRANSFER 89").await; alice .post(Transaction::PrivateTransfer(AssetId(0).value(89), bob_key)) .await @@ -579,6 +581,7 @@ mod test { alice.sync().await.expect("Unable to sync."); println!("Alice [wallet]: {:?}", alice.assets()).await; + println!("BOB PRIVATE TRANSFER 6").await; bob.post(Transaction::PrivateTransfer( AssetId(0).value(6), charlie_key, @@ -588,6 +591,7 @@ mod test { bob.sync().await.expect("Unable to sync."); println!("Bob [wallet]: {:?}", bob.assets()).await; + println!("CHARLIE PRIVATE TRANSFER 1").await; charlie .post(Transaction::PrivateTransfer(AssetId(0).value(1), alice_key)) .await @@ -595,6 +599,7 @@ mod test { charlie.sync().await.expect("Unable to sync."); println!("Charlie [wallet]: {:?}", charlie.assets()).await; + println!("ALICE RECLAIM 71").await; alice .post(Transaction::Reclaim(AssetId(0).value(71))) .await @@ -605,6 +610,7 @@ mod test { alice.sync().await.expect("Unable to sync."); bob.sync().await.expect("Unable to sync."); charlie.sync().await.expect("Unable to sync."); + println!("Alice [wallet]: {:?}", alice.assets()).await; println!("Bob [wallet]: {:?}", bob.assets()).await; println!("Charlie [wallet]: {:?}", charlie.assets()).await; diff --git a/manta-pay/src/test/simulation/mod.rs b/manta-pay/src/test/simulation/mod.rs index cb9b447af..369ce155a 100644 --- a/manta-pay/src/test/simulation/mod.rs +++ b/manta-pay/src/test/simulation/mod.rs @@ -22,7 +22,7 @@ use core::{cmp::min, ops::Range}; use indexmap::IndexMap; use manta_accounting::asset::{Asset, AssetId, AssetValue}; -use manta_crypto::rand::RngCore; +use manta_crypto::rand::{CryptoRng, RngCore, Sample}; use rand::{distributions::Distribution, seq::SliceRandom, Rng}; use statrs::{ distribution::{Categorical, Discrete, Poisson}, @@ -40,53 +40,6 @@ where vec.drain(0..drop_count); } -/// Balance State -#[derive(Clone, Debug, Default, Eq, PartialEq)] -pub struct BalanceState { - /// Asset Map - map: HashMap<AssetId, AssetValue>, -} - -impl BalanceState { - /// Returns the asset balance associated to the assets with the given `id`. - #[inline] - pub fn balance(&self, id: AssetId) -> AssetValue { - self.map.get(&id).copied().unwrap_or_default() - } - - /// Returns `true` if `self` contains at least `value` amount of the asset with the given `id`. - #[inline] - pub fn contains(&self, id: AssetId, value: AssetValue) -> bool { - self.balance(id) >= value - } - - /// Deposit `asset` into `self`. - #[inline] - pub fn deposit(&mut self, asset: Asset) { - *self.map.entry(asset.id).or_default() += asset.value; - } - - /// Withdraw `asset` from `self`, returning `false` if it would overdraw the balance. - #[inline] - pub fn withdraw(&mut self, asset: Asset) -> bool { - if asset.value == 0 { - true - } else { - self.map - .get_mut(&asset.id) - .map(move |balance| { - if let Some(result) = balance.checked_sub(asset.value) { - *balance = result; - true - } else { - false - } - }) - .unwrap_or(false) - } - } -} - /// Action Types #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum Action { @@ -148,7 +101,7 @@ impl Default for ActionDistributionPMF { impl From<ActionDistribution> for ActionDistributionPMF { #[inline] fn from(actions: ActionDistribution) -> Self { - ActionDistributionPMF { + Self { none: actions.distribution.pmf(0), public_deposit: actions.distribution.pmf(1), public_withdraw: actions.distribution.pmf(2), @@ -209,6 +162,63 @@ impl Distribution<Action> for ActionDistribution { } } +impl Sample<ActionDistribution> for Action { + #[inline] + fn sample<R>(distribution: ActionDistribution, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + distribution.sample(rng) + } +} + +/// Balance State +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct BalanceState { + /// Asset Map + map: HashMap<AssetId, AssetValue>, +} + +impl BalanceState { + /// Returns the asset balance associated to the assets with the given `id`. + #[inline] + pub fn balance(&self, id: AssetId) -> AssetValue { + self.map.get(&id).copied().unwrap_or_default() + } + + /// Returns `true` if `self` contains at least `value` amount of the asset with the given `id`. + #[inline] + pub fn contains(&self, id: AssetId, value: AssetValue) -> bool { + self.balance(id) >= value + } + + /// Deposit `asset` into `self`. + #[inline] + pub fn deposit(&mut self, asset: Asset) { + *self.map.entry(asset.id).or_default() += asset.value; + } + + /// Withdraw `asset` from `self`, returning `false` if it would overdraw the balance. + #[inline] + pub fn withdraw(&mut self, asset: Asset) -> bool { + if asset.value == 0 { + true + } else { + self.map + .get_mut(&asset.id) + .map(move |balance| { + if let Some(result) = balance.checked_sub(asset.value) { + *balance = result; + true + } else { + false + } + }) + .unwrap_or(false) + } + } +} + /// User Account #[derive(Clone, Debug, Default, PartialEq)] pub struct Account { From 066f88a041fc9b4f48bb317eac37c327706250d4 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 17 Jan 2022 18:29:39 -0500 Subject: [PATCH 188/275] fix: correct some issues uncovered during simulation --- manta-accounting/Cargo.toml | 4 +- manta-accounting/src/asset.rs | 30 +- manta-accounting/src/wallet/ledger.rs | 3 + manta-accounting/src/wallet/signer.rs | 36 +-- manta-accounting/src/wallet/state.rs | 22 +- manta-accounting/src/wallet/test.rs | 189 ----------- manta-accounting/src/wallet/test/mod.rs | 405 ++++++++++++++++++++++++ manta-accounting/src/wallet/test/sim.rs | 255 +++++++++++++++ manta-crypto/src/merkle_tree/forest.rs | 79 ++++- manta-crypto/src/merkle_tree/fork.rs | 11 +- manta-crypto/src/merkle_tree/test.rs | 92 +++++- manta-crypto/src/merkle_tree/tree.rs | 5 +- manta-pay/Cargo.toml | 2 +- manta-pay/src/config.rs | 22 ++ manta-pay/src/test/ledger.rs | 176 +++++----- manta-pay/src/wallet/mod.rs | 2 +- 16 files changed, 997 insertions(+), 336 deletions(-) delete mode 100644 manta-accounting/src/wallet/test.rs create mode 100644 manta-accounting/src/wallet/test/mod.rs create mode 100644 manta-accounting/src/wallet/test/sim.rs diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 0dcca6e94..8f92a4ed2 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -35,7 +35,7 @@ cocoon-fs = [ std = ["cocoon/std", "async-std"] # Testing Frameworks -test = ["futures", "indexmap"] +test = ["futures", "indexmap", "rand/alloc", "statrs"] [dependencies] async-std = { version = "1.10.0", optional = true } @@ -47,6 +47,8 @@ indexmap = { version = "1.8.0", optional = true, default-features = false } manta-crypto = { path = "../manta-crypto" } manta-util = { path = "../manta-util", features = ["zeroize"] } serde = { version = "1.0.133", optional = true, default-features = false, features = ["derive"] } +statrs = { version = "0.15.0", optional = true, default-features = false } +rand = { version = "0.8.4", optional = true, default-features = false } [dev-dependencies] rand = "0.8.4" diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 835045a2a..ef39af1c7 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -772,6 +772,16 @@ impl IntoIterator for AssetList { } } +impl<'a> IntoIterator for &'a AssetList { + type Item = &'a Asset; + type IntoIter = slice::Iter<'a, Asset>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + /// Asset Map /// /// This trait represents an asset distribution over some [`Key`](Self::Key) type. @@ -827,16 +837,18 @@ pub trait AssetMap: Default { .for_each(move |key| self.insert(key, Asset::zero(id))); } - /// Removes the `key` from the map. - fn remove(&mut self, key: Self::Key, asset: Asset); + /// Tries to remove the `key` from the map, returning `true` if the `key` was stored in the + /// map and removed. + fn remove(&mut self, key: Self::Key, asset: Asset) -> bool; /// Removes all the keys in `iter` from the map. fn remove_all<I>(&mut self, iter: I) where I: IntoIterator<Item = (Self::Key, Asset)>, { - iter.into_iter() - .for_each(move |(key, asset)| self.remove(key, asset)); + for (key, asset) in iter { + self.remove(key, asset); + } } /// Retains the elements from `self` that return `true` after applying `f`. @@ -911,16 +923,18 @@ macro_rules! impl_asset_map_for_maps_body { } #[inline] - fn remove(&mut self, key: Self::Key, asset: Asset) { + fn remove(&mut self, key: Self::Key, asset: Asset) -> bool { if let $entry::Occupied(mut entry) = self.entry(key) { let assets = entry.get_mut(); if let Ok(index) = assets.binary_search(&asset) { assets.remove(index); - } - if assets.is_empty() { - entry.remove(); + if assets.is_empty() { + entry.remove(); + } + return true; } } + false } #[inline] diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs index fd9b75502..4792c28b3 100644 --- a/manta-accounting/src/wallet/ledger.rs +++ b/manta-accounting/src/wallet/ledger.rs @@ -16,6 +16,9 @@ //! Ledger Connection +// TODO: Report a more meaningful error on `push` failure. In some way, it must match the +// `TransferPostError` variants. + use crate::transfer::{Configuration, EncryptedNote, TransferPost, Utxo, VoidNumber}; use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 8cbf85625..71b007854 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -300,7 +300,6 @@ where encrypted_note: EncryptedNote<C>, void_numbers: &mut Vec<VoidNumber<C>>, deposit: &mut Vec<Asset>, - withdraw: &mut Vec<Asset>, ) -> Result<(), Error<C>> { let mut finder = DecryptedMessage::find(encrypted_note); if let Some(ViewKeySelection { @@ -324,16 +323,8 @@ where &asset, &utxo, ) { - if let Some(void_number_index) = - void_numbers.iter().position(move |v| v == &void_number) - { - void_numbers.remove(void_number_index); - self.utxo_set.remove_proof(&utxo); - self.assets - .remove((index.spend, ephemeral_public_key), asset); - if !asset.is_zero() { - withdraw.push(asset); - } + if let Some(index) = void_numbers.iter().position(move |v| v == &void_number) { + void_numbers.remove(index); } else { self.utxo_set.insert(&utxo); self.assets @@ -392,7 +383,6 @@ where where I: Iterator<Item = (Utxo<C>, EncryptedNote<C>)>, { - // FIXME: Use a more efficient synchronization algorithm for partial vs full. let mut deposit = Vec::new(); let mut withdraw = Vec::new(); for (utxo, encrypted_note) in inserts { @@ -402,7 +392,6 @@ where encrypted_note, &mut void_numbers, &mut deposit, - &mut withdraw, )?; } for void_number in void_numbers { @@ -424,7 +413,6 @@ where !assets.is_empty() }); } - self.utxo_set.commit(); if is_partial { Ok(SyncResponse::Partial { deposit, withdraw }) } else { @@ -615,7 +603,6 @@ where needed_zeroes -= zeroes.len(); for zero in zeroes { let pre_sender = self.build_pre_sender(parameters, zero, Asset::zero(asset_id))?; - pre_sender.insert_utxo(&mut self.utxo_set); pre_senders.push(pre_sender); } if needed_zeroes == 0 { @@ -757,8 +744,6 @@ where /// /// This method assumes that `accounts` has never been used before, and does not attempt /// to perform wallet recovery on this table. - // - // FIXME: Check that this warning even makes sense. #[inline] pub fn new( accounts: AccountTable<C>, @@ -800,12 +785,16 @@ where // let utxo_set_len = self.state.utxo_set.len(); match utxo_set_len.checked_sub(starting_index) { - Some(diff) => self.state.sync_with( - &self.parameters.parameters, - inserts.into_iter().skip(diff), - removes.into_iter().collect(), - diff == 0, - ), + Some(diff) => { + let result = self.state.sync_with( + &self.parameters.parameters, + inserts.into_iter().skip(diff), + removes.into_iter().collect(), + diff == 0, + ); + self.state.utxo_set.commit(); + result + } _ => Err(Error::InconsistentSynchronization { starting_index: utxo_set_len, }), @@ -819,6 +808,7 @@ where asset: Asset, receiver: Option<ReceivingKey<C>>, ) -> SignResult<C, Self> { + // FIXME: Handle the withdraw of `asset.value == 0` case. let selection = self.state.select(&self.parameters.parameters, asset)?; let change = self .state diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 09dab572c..48b8c1109 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -114,10 +114,17 @@ impl BalanceState for AssetList { /// Performs an unchecked withdraw on `balance`, panicking on overflow. #[inline] fn withdraw_unchecked(balance: Option<&mut AssetValue>, withdraw: AssetValue) { - let balance = balance.expect("Trying to withdraw from a zero balance."); - *balance = balance - .checked_sub(withdraw) - .expect("Overdrawn balance state."); + let balance = match balance { + Some(balance) => balance, + _ => panic!("Trying to withdraw `{:?}` from a zero balance.", withdraw), + }; + *balance = match balance.checked_sub(withdraw) { + Some(balance) => balance, + _ => panic!( + "Overdrawn balance state. Tried to subtract `{:?}` from `{:?}`.", + withdraw, balance + ), + } } /// Adds implementation of [`BalanceState`] for a map type with the given `$entry` type. @@ -243,6 +250,12 @@ where &self.assets } + /// Returns a shared reference to the ledger connection associated to `self`. + #[inline] + pub fn ledger(&self) -> &L { + &self.ledger + } + /// Returns the [`Checkpoint`](ledger::Connection::Checkpoint) representing the current state /// of this wallet. #[inline] @@ -284,7 +297,6 @@ where self.assets.withdraw_all_unchecked(withdraw); } SyncResponse::Full { assets } => { - // TODO: Perform these two as one call, something like `self.assets.reset(assets)`. self.assets.clear(); self.assets.deposit_all(assets); } diff --git a/manta-accounting/src/wallet/test.rs b/manta-accounting/src/wallet/test.rs deleted file mode 100644 index b5872b56a..000000000 --- a/manta-accounting/src/wallet/test.rs +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Testing and Simulation Framework - -use crate::{ - asset::Asset, - transfer::{canonical::Transaction, Configuration, PublicKey, ReceivingKey}, - wallet::{self, ledger, signer, Wallet}, -}; -use alloc::rc::Rc; -use async_std::sync::RwLock; -use core::{hash::Hash, marker::PhantomData}; -use manta_crypto::rand::{CryptoRng, RngCore}; -use manta_util::future::LocalBoxFuture; -use std::collections::HashSet; - -/// Actor Simulation -pub mod sim { - use alloc::vec::Vec; - use core::{fmt::Debug, hash::Hash}; - use futures::stream::{self, select_all::SelectAll, Stream}; - use manta_crypto::rand::{CryptoRng, RngCore}; - use manta_util::future::LocalBoxFuture; - - /// Abstract Simulation - pub trait Simulation { - /// Actor Type - type Actor; - - /// Event Type - type Event; - - /// Runs the given `actor` returning a future event. - /// - /// This method should return `None` when the actor is done being simulated for this round - /// of the simulation. - fn step<'s, R>( - &'s self, - actor: &'s mut Self::Actor, - rng: &'s mut R, - ) -> LocalBoxFuture<'s, Option<Self::Event>> - where - R: CryptoRng + RngCore + ?Sized; - } - - /// Simulator - #[derive(derivative::Derivative)] - #[derivative( - Clone(bound = "S: Clone, S::Actor: Clone"), - Debug(bound = "S: Debug, S::Actor: Debug"), - Default(bound = "S: Default"), - Eq(bound = "S: Eq, S::Actor: Eq"), - Hash(bound = "S: Hash, S::Actor: Hash"), - PartialEq(bound = "S: PartialEq, S::Actor: PartialEq") - )] - pub struct Simulator<S> - where - S: Simulation, - { - /// Simulation - pub simulation: S, - - /// Actors - pub actors: Vec<S::Actor>, - } - - impl<S> Simulator<S> - where - S: Simulation, - { - /// Builds a new [`Simulator`] from `simulation` and `actors`. - #[inline] - pub fn new(simulation: S, actors: Vec<S::Actor>) -> Self { - Self { simulation, actors } - } - - /// Builds a stream of future events for a particular `actor`. - #[inline] - fn build_actor_stream<'s, R>( - simulator: &'s S, - actor: &'s mut S::Actor, - mut rng: R, - ) -> impl 's + Stream<Item = S::Event> - where - R: 's + CryptoRng + RngCore, - { - stream::poll_fn(move |ctx| simulator.step(actor, &mut rng).as_mut().poll(ctx)) - } - - /// Runs the simulator using `rng`, returning a stream of future events. - #[inline] - pub async fn run<'s, R, F>(&'s mut self, mut rng: F) -> impl 's + Stream<Item = S::Event> - where - F: FnMut() -> R, - R: 's + CryptoRng + RngCore, - { - let mut streams = SelectAll::new(); - for actor in &mut self.actors { - streams.push(Self::build_actor_stream(&self.simulation, actor, rng())); - } - streams - } - } -} - -/// Simulation Action Space -pub enum Action<C> -where - C: Configuration, -{ - /// Public Deposit Action - PublicDeposit(Asset), - - /// Public Withdraw Action - PublicWithdraw(Asset), - - /// Post Transaction - Post(Transaction<C>), - - /// Generate Public Key - GeneratePublicKey, -} - -/// Public Key Database -pub type PublicKeyDatabase<C> = HashSet<ReceivingKey<C>>; - -/// Shared Public Key Database -pub type SharedPublicKeyDatabase<C> = Rc<RwLock<PublicKeyDatabase<C>>>; - -/// -pub struct Simulation<C, L, S> -where - C: Configuration, - L: ledger::Connection<C>, - S: signer::Connection<C>, -{ - /// - public_keys: SharedPublicKeyDatabase<C>, - - /// - __: PhantomData<(L, S)>, -} - -impl<C, L, S> sim::Simulation for Simulation<C, L, S> -where - C: Configuration, - L: ledger::Connection<C>, - S: signer::Connection<C>, - PublicKey<C>: Eq + Hash, -{ - type Actor = Wallet<C, L, S>; - type Event = Result<(), wallet::Error<C, L, S>>; - - #[inline] - fn step<'s, R>( - &'s self, - actor: &'s mut Self::Actor, - rng: &'s mut R, - ) -> LocalBoxFuture<'s, Option<Self::Event>> - where - R: CryptoRng + RngCore + ?Sized, - { - let _ = rng; - Box::pin(async move { - // TODO: - Some(match actor.receiving_key().await { - Ok(key) => { - self.public_keys.write().await.insert(key); - Ok(()) - } - Err(err) => Err(wallet::Error::SignerError(err)), - }) - }) - } -} diff --git a/manta-accounting/src/wallet/test/mod.rs b/manta-accounting/src/wallet/test/mod.rs new file mode 100644 index 000000000..aa84869f0 --- /dev/null +++ b/manta-accounting/src/wallet/test/mod.rs @@ -0,0 +1,405 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Testing and Simulation Framework + +use crate::{ + asset::{Asset, AssetList}, + transfer::{canonical::Transaction, Configuration, PublicKey, ReceivingKey}, + wallet::{self, ledger, signer, Wallet}, +}; +use alloc::rc::Rc; +use async_std::sync::RwLock; +use core::{fmt::Debug, hash::Hash, marker::PhantomData}; +use indexmap::IndexSet; +use manta_crypto::rand::{CryptoRng, RngCore, Sample}; +use manta_util::future::LocalBoxFuture; +use rand::{distributions::Distribution, Rng}; +use statrs::{ + distribution::{Categorical, Discrete}, + StatsError, +}; + +pub mod sim; + +/// Simulation Action Space +pub enum Action<C> +where + C: Configuration, +{ + /// No Action + Skip, + + /// Post Transaction + Post(Transaction<C>), + + /// Generate Public Key + GeneratePublicKey, +} + +/// Action Types +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum ActionType { + /// No Action + Skip, + + /// Mint Action + Mint, + + /// Private Transfer Action + PrivateTransfer, + + /// Reclaim Action + Reclaim, + + /// Generate Public Key Action + GeneratePublicKey, +} + +/// Action Distribution Probability Mass Function +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct ActionDistributionPMF<T = f64> { + /// No Action Weight + pub skip: T, + + /// Mint Action Weight + pub mint: T, + + /// Private Transfer Action Weight + pub private_transfer: T, + + /// Reclaim Action Weight + pub reclaim: T, + + /// Generate Public Key + pub generate_public_key: T, +} + +impl Default for ActionDistributionPMF { + #[inline] + fn default() -> Self { + Self { + skip: 4.0, + mint: 1.0, + private_transfer: 1.0, + reclaim: 1.0, + generate_public_key: 0.5, + } + } +} + +impl From<ActionDistribution> for ActionDistributionPMF { + #[inline] + fn from(actions: ActionDistribution) -> Self { + Self { + skip: actions.distribution.pmf(0), + mint: actions.distribution.pmf(1), + private_transfer: actions.distribution.pmf(2), + reclaim: actions.distribution.pmf(3), + generate_public_key: actions.distribution.pmf(4), + } + } +} + +/// Action Distribution +#[derive(Clone, Debug, PartialEq)] +pub struct ActionDistribution { + /// Distribution over Actions + distribution: Categorical, +} + +impl Default for ActionDistribution { + #[inline] + fn default() -> Self { + Self::try_from(ActionDistributionPMF::default()) + .expect("The default distribution is a valid categorical distribution.") + } +} + +impl TryFrom<ActionDistributionPMF> for ActionDistribution { + type Error = StatsError; + + #[inline] + fn try_from(pmf: ActionDistributionPMF) -> Result<Self, StatsError> { + Ok(Self { + distribution: Categorical::new(&[ + pmf.skip, + pmf.mint, + pmf.private_transfer, + pmf.reclaim, + pmf.generate_public_key, + ])?, + }) + } +} + +impl Distribution<ActionType> for ActionDistribution { + #[inline] + fn sample<R>(&self, rng: &mut R) -> ActionType + where + R: RngCore + ?Sized, + { + match self.distribution.sample(rng) as usize { + 0 => ActionType::Skip, + 1 => ActionType::Mint, + 2 => ActionType::PrivateTransfer, + 3 => ActionType::Reclaim, + 4 => ActionType::GeneratePublicKey, + _ => unreachable!(), + } + } +} + +impl Sample<ActionDistribution> for ActionType { + #[inline] + fn sample<R>(distribution: ActionDistribution, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + distribution.sample(rng) + } +} + +/// Public Balance Oracle +pub trait PublicBalanceOracle { + /// Returns the public balances of `self`. + fn public_balances(&self) -> LocalBoxFuture<Option<AssetList>>; +} + +/// Actor +pub struct Actor<C, L, S> +where + C: Configuration, + L: ledger::Connection<C>, + S: signer::Connection<C>, +{ + /// Wallet + pub wallet: Wallet<C, L, S>, + + /// Action Distribution + pub distribution: ActionDistribution, + + /// Actor Lifetime + pub lifetime: usize, +} + +impl<C, L, S> Actor<C, L, S> +where + C: Configuration, + L: ledger::Connection<C>, + S: signer::Connection<C>, +{ + /// Builds a new [`Actor`] with `wallet`, `distribution`, and `lifetime`. + #[inline] + pub fn new(wallet: Wallet<C, L, S>, distribution: ActionDistribution, lifetime: usize) -> Self { + Self { + wallet, + distribution, + lifetime, + } + } + + /// Reduces the lifetime of `self` returning `None` if the lifetime is zero. + #[inline] + fn reduce_lifetime(&mut self) -> Option<()> { + self.lifetime = self.lifetime.checked_sub(1)?; + Some(()) + } + + /// Samples a deposit from `self` using `rng` returning `None` if no deposit is possible. + #[inline] + async fn sample_deposit<R>(&mut self, rng: &mut R) -> Option<Asset> + where + L: PublicBalanceOracle, + R: CryptoRng + RngCore + ?Sized, + { + let _ = self.wallet.sync().await; + let assets = self.wallet.ledger().public_balances().await?; + let len = assets.len(); + if len == 0 { + return None; + } + let asset = assets.iter().nth(rng.gen_range(0..len)).unwrap(); + Some(asset.id.value(rng.gen_range(0..asset.value.0))) + } + + /// Samples a withdraw from `self` using `rng` returning `None` if no withdrawal is possible. + /// + /// # Note + /// + /// This method samples from a uniform distribution over the asset IDs and asset values present + /// in the balance state of `self`. + #[inline] + async fn sample_withdraw<R>(&mut self, rng: &mut R) -> Option<Asset> + where + R: CryptoRng + RngCore + ?Sized, + { + let _ = self.wallet.sync().await; + let assets = self.wallet.assets(); + let len = assets.len(); + if len == 0 { + return None; + } + let (asset_id, asset_value) = assets.iter().nth(rng.gen_range(0..len)).unwrap(); + Some(asset_id.value(rng.gen_range(0..asset_value.0))) + } +} + +/// Simulation Event +#[derive(derivative::Derivative)] +#[derivative(Debug(bound = "wallet::Error<C, L, S>: Debug"))] +pub struct Event<C, L, S> +where + C: Configuration, + L: ledger::Connection<C>, + S: signer::Connection<C>, +{ + /// Action Type + pub action: ActionType, + + /// Action Result + pub result: Result<bool, wallet::Error<C, L, S>>, +} + +/// Public Key Database +pub type PublicKeyDatabase<C> = IndexSet<ReceivingKey<C>>; + +/// Shared Public Key Database +pub type SharedPublicKeyDatabase<C> = Rc<RwLock<PublicKeyDatabase<C>>>; + +/// Simulation +pub struct Simulation<C, L, S> +where + C: Configuration, + L: ledger::Connection<C>, + S: signer::Connection<C>, + PublicKey<C>: Eq + Hash, +{ + /// Public Key Database + public_keys: SharedPublicKeyDatabase<C>, + + /// Type Parameter Marker + __: PhantomData<(L, S)>, +} + +impl<C, L, S> Simulation<C, L, S> +where + C: Configuration, + L: ledger::Connection<C>, + S: signer::Connection<C>, + PublicKey<C>: Eq + Hash, +{ + /// Builds a new [`Simulation`] with a starting set of public `keys`. + #[inline] + pub fn new<const N: usize>(keys: [ReceivingKey<C>; N]) -> Self { + Self { + public_keys: Rc::new(RwLock::new(keys.into_iter().collect())), + __: PhantomData, + } + } +} + +impl<C, L, S> sim::ActionSimulation for Simulation<C, L, S> +where + C: Configuration, + L: ledger::Connection<C> + PublicBalanceOracle, + S: signer::Connection<C>, + PublicKey<C>: Eq + Hash, +{ + type Actor = Actor<C, L, S>; + type Action = Action<C>; + type Event = Event<C, L, S>; + + #[inline] + fn sample<'s, R>( + &'s self, + actor: &'s mut Self::Actor, + rng: &'s mut R, + ) -> LocalBoxFuture<'s, Option<Self::Action>> + where + R: CryptoRng + RngCore + ?Sized, + { + Box::pin(async { + actor.reduce_lifetime()?; + let action = actor.distribution.sample(rng); + Some(match action { + ActionType::Skip => Action::Skip, + ActionType::Mint => match actor.sample_deposit(rng).await { + Some(asset) => Action::Post(Transaction::Mint(asset)), + _ => Action::Skip, + }, + ActionType::PrivateTransfer => match actor.sample_withdraw(rng).await { + Some(asset) => { + let public_keys = self.public_keys.read().await; + Action::Post(Transaction::PrivateTransfer( + asset, + public_keys[rng.gen_range(0..public_keys.len())].clone(), + )) + } + _ => match actor.sample_deposit(rng).await { + Some(asset) => Action::Post(Transaction::Mint(asset)), + _ => Action::Skip, + }, + }, + ActionType::Reclaim => match actor.sample_withdraw(rng).await { + Some(asset) => Action::Post(Transaction::Reclaim(asset)), + _ => match actor.sample_deposit(rng).await { + Some(asset) => Action::Post(Transaction::Mint(asset)), + _ => Action::Skip, + }, + }, + ActionType::GeneratePublicKey => Action::GeneratePublicKey, + }) + }) + } + + #[inline] + fn act<'s>( + &'s self, + actor: &'s mut Self::Actor, + action: Self::Action, + ) -> LocalBoxFuture<'s, Self::Event> { + Box::pin(async move { + match action { + Action::Skip => Event { + action: ActionType::Skip, + result: Ok(true), + }, + Action::Post(transaction) => { + let action = match &transaction { + Transaction::Mint(_) => ActionType::Mint, + Transaction::PrivateTransfer(_, _) => ActionType::PrivateTransfer, + Transaction::Reclaim(_) => ActionType::Reclaim, + }; + Event { + action, + result: actor.wallet.post(transaction).await, + } + } + Action::GeneratePublicKey => Event { + action: ActionType::GeneratePublicKey, + result: match actor.wallet.receiving_key().await { + Ok(key) => { + self.public_keys.write().await.insert(key); + Ok(true) + } + Err(err) => Err(wallet::Error::SignerError(err)), + }, + }, + } + }) + } +} diff --git a/manta-accounting/src/wallet/test/sim.rs b/manta-accounting/src/wallet/test/sim.rs new file mode 100644 index 000000000..1d44991eb --- /dev/null +++ b/manta-accounting/src/wallet/test/sim.rs @@ -0,0 +1,255 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Actor Simulation Framework + +use alloc::vec::Vec; +use core::{fmt::Debug, hash::Hash}; +use futures::stream::{self, select_all::SelectAll, Stream, StreamExt}; +use manta_crypto::rand::{CryptoRng, RngCore}; +use manta_util::future::LocalBoxFuture; + +/// Abstract Simulation +pub trait Simulation { + /// Actor Type + type Actor; + + /// Event Type + type Event; + + /// Runs the given `actor` returning a future event. + /// + /// This method should return `None` when the actor is done being simulated for this round of + /// the simulation. + fn step<'s, R>( + &'s self, + actor: &'s mut Self::Actor, + rng: &'s mut R, + ) -> LocalBoxFuture<'s, Option<Self::Event>> + where + R: CryptoRng + RngCore + ?Sized; +} + +impl<S> Simulation for &S +where + S: Simulation, +{ + type Actor = S::Actor; + type Event = S::Event; + + #[inline] + fn step<'s, R>( + &'s self, + actor: &'s mut Self::Actor, + rng: &'s mut R, + ) -> LocalBoxFuture<'s, Option<Self::Event>> + where + R: CryptoRng + RngCore + ?Sized, + { + (*self).step(actor, rng) + } +} + +/// Simulation Event +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "S::Event: Clone"), + Copy(bound = "S::Event: Copy"), + Debug(bound = "S::Event: Debug"), + Default(bound = "S::Event: Default"), + Eq(bound = "S::Event: Eq"), + Hash(bound = "S::Event: Hash"), + PartialEq(bound = "S::Event: PartialEq") +)] +pub struct Event<S> +where + S: Simulation, +{ + /// Actor Index + pub actor: usize, + + /// Step Index of the Actor + pub step: usize, + + /// Step Event + pub event: S::Event, +} + +impl<S> Event<S> +where + S: Simulation, +{ + /// Builds a new [`Event`] from `actor`, `step`, and `event`. + #[inline] + pub fn new(actor: usize, step: usize, event: S::Event) -> Self { + Self { actor, step, event } + } +} + +/// Simulator +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "S: Clone, S::Actor: Clone"), + Debug(bound = "S: Debug, S::Actor: Debug"), + Default(bound = "S: Default"), + Eq(bound = "S: Eq, S::Actor: Eq"), + Hash(bound = "S: Hash, S::Actor: Hash"), + PartialEq(bound = "S: PartialEq, S::Actor: PartialEq") +)] +pub struct Simulator<S> +where + S: Simulation, +{ + /// Simulation + pub simulation: S, + + /// Actors + pub actors: Vec<S::Actor>, +} + +impl<S> Simulator<S> +where + S: Simulation, +{ + /// Builds a new [`Simulator`] from `simulation` and `actors`. + #[inline] + pub fn new(simulation: S, actors: Vec<S::Actor>) -> Self { + Self { simulation, actors } + } + + /// Builds a stream of future events for a particular `actor`. + #[inline] + fn build_actor_stream<'s, R>( + simulation: &'s S, + index: usize, + actor: &'s mut S::Actor, + rng: R, + ) -> impl 's + Stream<Item = Event<S>> + where + R: 's + CryptoRng + RngCore, + { + // TODO: Look for a more efficient way to allocate the memory here instead of `Box::pin`. + stream::unfold((actor, rng), move |mut state| { + Box::pin(async move { + simulation + .step(state.0, &mut state.1) + .await + .map(move |event| (event, state)) + }) + }) + .enumerate() + .map(move |(step, event)| Event::new(index, step, event)) + } + + /// Runs the simulator using `rng`, returning a stream of future events. + #[inline] + pub fn run<'s, R, G>(&'s mut self, mut rng: G) -> impl 's + Stream<Item = Event<S>> + where + R: 's + CryptoRng + RngCore, + G: 's + FnMut() -> R, + { + let mut streams = SelectAll::new(); + for (i, actor) in self.actors.iter_mut().enumerate() { + streams.push(Self::build_actor_stream(&self.simulation, i, actor, rng())); + } + streams + } +} + +impl<S> AsRef<S> for Simulator<S> +where + S: Simulation, +{ + #[inline] + fn as_ref(&self) -> &S { + &self.simulation + } +} + +/// Action Simulation +pub trait ActionSimulation { + /// Actor Type + type Actor; + + /// Action Type + type Action; + + /// Event Type + type Event; + + /// Samples an action for the `actor` to take using `rng`. + /// + /// This method should return `None` when the actor is done being simulated for this round of + /// the simulation. + fn sample<'s, R>( + &'s self, + actor: &'s mut Self::Actor, + rng: &'s mut R, + ) -> LocalBoxFuture<'s, Option<Self::Action>> + where + R: CryptoRng + RngCore + ?Sized; + + /// Executes the given `action` on `actor` returning a future event. + fn act<'s>( + &'s self, + actor: &'s mut Self::Actor, + action: Self::Action, + ) -> LocalBoxFuture<'s, Self::Event>; +} + +/// Action Simulation Wrapper +/// +/// This `struct` wraps an implementation of [`ActionSimulation`] and implements [`Simulation`] for +/// use in some [`Simulator`]. +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct ActionSim<S>(pub S) +where + S: ActionSimulation; + +impl<S> AsRef<S> for ActionSim<S> +where + S: ActionSimulation, +{ + #[inline] + fn as_ref(&self) -> &S { + &self.0 + } +} + +impl<S> Simulation for ActionSim<S> +where + S: ActionSimulation, +{ + type Actor = S::Actor; + type Event = S::Event; + + #[inline] + fn step<'s, R>( + &'s self, + actor: &'s mut Self::Actor, + rng: &'s mut R, + ) -> LocalBoxFuture<'s, Option<Self::Event>> + where + R: CryptoRng + RngCore + ?Sized, + { + Box::pin(async { + match self.0.sample(actor, rng).await { + Some(action) => Some(self.0.act(actor, action).await), + _ => None, + } + }) + } +} diff --git a/manta-crypto/src/merkle_tree/forest.rs b/manta-crypto/src/merkle_tree/forest.rs index f2f1a6d51..98c66cd02 100644 --- a/manta-crypto/src/merkle_tree/forest.rs +++ b/manta-crypto/src/merkle_tree/forest.rs @@ -61,10 +61,29 @@ pub trait Configuration: tree::Configuration { /// [`Index`](Configuration::Index) for a merkle forest configuration, the /// [`tree_index`](Configuration::tree_index) method must return values from a distribution over /// exactly `N` values. -pub trait FixedIndex<const N: usize>: Into<usize> {} +pub trait FixedIndex<const N: usize>: Into<usize> { + /// Returns a representative index of type `Self` if `index` is within `0..N`. + /// + /// # Panics + /// + /// This method can return any value or panic whenever `index` is out of the correct range but + /// cannot run into undefined behavior. + fn from_index(index: usize) -> Self; +} -impl FixedIndex<256> for u8 {} -impl FixedIndex<65536> for u16 {} +impl FixedIndex<256> for u8 { + #[inline] + fn from_index(index: usize) -> Self { + index as Self + } +} + +impl FixedIndex<65536> for u16 { + #[inline] + fn from_index(index: usize) -> Self { + index as Self + } +} /// Merkle Forest Structure pub trait Forest<C> @@ -355,6 +374,60 @@ where } } +/// [`SingleTree`] Merkle Forest Index +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct SingleTreeIndex; + +impl FixedIndex<1> for SingleTreeIndex { + #[inline] + fn from_index(index: usize) -> Self { + let _ = index; + Self + } +} + +impl From<SingleTreeIndex> for usize { + #[inline] + fn from(index: SingleTreeIndex) -> Self { + let _ = index; + 0 + } +} + +/// Single Tree Merkle Forest +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct SingleTree<C, COM = ()>(PhantomData<(C, COM)>) +where + C: tree::Configuration<COM>; + +impl<C, COM> tree::HashConfiguration<COM> for SingleTree<C, COM> +where + C: tree::Configuration<COM>, +{ + type LeafHash = C::LeafHash; + type InnerHash = C::InnerHash; +} + +impl<C, COM> tree::Configuration<COM> for SingleTree<C, COM> +where + C: tree::Configuration<COM>, +{ + const HEIGHT: usize = C::HEIGHT; +} + +impl<C> Configuration for SingleTree<C> +where + C: tree::Configuration, +{ + type Index = SingleTreeIndex; + + #[inline] + fn tree_index(leaf: &Leaf<Self>) -> Self::Index { + let _ = leaf; + Default::default() + } +} + /// Tree Array Merkle Forest Alias pub type TreeArrayMerkleForest<C, T, const N: usize> = MerkleForest<C, TreeArray<C, T, N>>; diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs index 6380fbca5..0516ee656 100644 --- a/manta-crypto/src/merkle_tree/fork.rs +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -494,9 +494,12 @@ where #[inline] pub fn position(&self, leaf_digest: &LeafDigest<C>) -> Option<usize> where + T: WithProofs<C>, LeafDigest<C>: PartialEq, { - self.branch.position(leaf_digest) + self.branch + .position(leaf_digest) + .or_else(move || P::upgrade(&self.base).and_then(move |b| b.position(leaf_digest))) } /// Returns the current (right-most) leaf of the tree. @@ -524,7 +527,6 @@ where InnerDigest<C>: Clone, { // FIXME: Move this algorithm to `crate::merkle_tree::path`. - let length = self.len(); if index > 0 && index >= length { return Err(PathError::IndexTooLarge { length }); @@ -534,17 +536,14 @@ where Some(base) => { let base_index = Node(index); let base_path = base.path(parameters, base_index.0)?; - let fork_index = self.branch.starting_leaf_node(); let mut fork_path = self.branch.path_unchecked(fork_index.0); - if !Node::are_siblings(&base_index, &fork_index) { let matching_index = base_index .parents() .zip(fork_index.parents()) .position(|(b, f)| Node::are_siblings(&b, &f)) .unwrap(); - fork_path.inner_path.path[matching_index] = InnerPath::fold( parameters, fork_index, @@ -556,11 +555,9 @@ where ), &fork_path.inner_path.path[..matching_index], ); - fork_path.inner_path.path[..matching_index] .clone_from_slice(&base_path.inner_path.path[..matching_index]); } - fork_path.inner_path.leaf_index = base_path.inner_path.leaf_index; fork_path.sibling_digest = base_path.sibling_digest; Ok(fork_path) diff --git a/manta-crypto/src/merkle_tree/test.rs b/manta-crypto/src/merkle_tree/test.rs index 2d806bd1f..d01dc5d04 100644 --- a/manta-crypto/src/merkle_tree/test.rs +++ b/manta-crypto/src/merkle_tree/test.rs @@ -19,11 +19,13 @@ use crate::{ merkle_tree::{ Configuration, HashConfiguration, IdentityLeafHash, InnerDigest, InnerHash, - InnerHashParameters, Leaf, LeafHashParameters, MerkleTree, Parameters, Tree, WithProofs, + InnerHashParameters, Leaf, LeafHashParameters, MerkleTree, Parameters, Path, Tree, + WithProofs, }, rand::{CryptoRng, RngCore, Sample}, }; -use core::{fmt::Debug, hash::Hash, marker::PhantomData, ops::BitXor}; +use alloc::string::String; +use core::{fmt::Debug, hash::Hash, marker::PhantomData}; /// Hash Parameter Sampling pub trait HashParameterSampling: HashConfiguration { @@ -130,13 +132,17 @@ pub fn assert_valid_path<C, T>(tree: &MerkleTree<C, T>, index: usize, leaf: &Lea where C: Configuration + ?Sized, T: Tree<C> + WithProofs<C>, - InnerDigest<C>: PartialEq, + InnerDigest<C>: Debug + PartialEq, + Path<C>: Debug, { + let path = tree.path(index).expect("Only valid queries are accepted."); + let root = tree.root(); assert!( - tree.path(index) - .expect("Only valid queries are accepted.") - .verify(tree.parameters(), tree.root(), leaf), - "Path returned from tree was not valid." + path.verify(tree.parameters(), root, leaf), + "Path returned from tree was not valid: {:?}. Expected {:?} but got {:?}.", + path, + root, + path.root(&tree.parameters, &tree.parameters.digest(leaf)), ); } @@ -147,7 +153,8 @@ pub fn assert_valid_paths<C, T>(tree: &mut MerkleTree<C, T>, leaves: &[Leaf<C>]) where C: Configuration + ?Sized, T: Tree<C> + WithProofs<C>, - InnerDigest<C>: PartialEq, + InnerDigest<C>: Debug + PartialEq, + Path<C>: Debug, Leaf<C>: Sized, { let starting_index = tree.len(); @@ -159,6 +166,33 @@ where } } +/// Test Inner Hash +/// +/// # Warning +/// +/// This is only meant for testing purposes, and should not be used in any production or +/// cryptographically secure environments. +pub trait TestHash { + /// Joins `lhs` and `rhs` into a third hash value. + fn join(lhs: &Self, rhs: &Self) -> Self; +} + +impl TestHash for u64 { + #[inline] + fn join(lhs: &Self, rhs: &Self) -> Self { + *lhs ^ *rhs + } +} + +impl TestHash for String { + #[inline] + fn join(lhs: &Self, rhs: &Self) -> Self { + let mut lhs = lhs.clone(); + lhs.push_str(rhs); + lhs + } +} + /// Test Merkle Tree Configuration /// /// # Warning @@ -168,11 +202,11 @@ where #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Test<T = u64, const HEIGHT: usize = 20>(PhantomData<T>) where - T: Clone + BitXor<Output = T> + Default + PartialEq; + T: Clone + Default + PartialEq + TestHash; impl<T, const HEIGHT: usize> InnerHash for Test<T, HEIGHT> where - T: Clone + BitXor<Output = T> + Default + PartialEq, + T: Clone + Default + PartialEq + TestHash, { type LeafDigest = T; type Parameters = (); @@ -186,7 +220,7 @@ where _: &mut (), ) -> Self::Output { let _ = parameters; - lhs.clone() ^ rhs.clone() + TestHash::join(lhs, rhs) } #[inline] @@ -197,13 +231,13 @@ where _: &mut (), ) -> Self::Output { let _ = parameters; - lhs.clone() ^ rhs.clone() + TestHash::join(lhs, rhs) } } impl<T, const HEIGHT: usize> HashConfiguration for Test<T, HEIGHT> where - T: Clone + BitXor<Output = T> + Default + PartialEq, + T: Clone + Default + PartialEq + TestHash, { type LeafHash = IdentityLeafHash<T>; type InnerHash = Test<T, HEIGHT>; @@ -211,7 +245,37 @@ where impl<T, const HEIGHT: usize> Configuration for Test<T, HEIGHT> where - T: Clone + BitXor<Output = T> + Default + PartialEq, + T: Clone + Default + PartialEq + TestHash, { const HEIGHT: usize = HEIGHT; } + +impl<T, const HEIGHT: usize> HashParameterSampling for Test<T, HEIGHT> +where + T: Clone + Default + PartialEq + TestHash, +{ + type LeafHashParameterDistribution = (); + type InnerHashParameterDistribution = (); + + #[inline] + fn sample_leaf_hash_parameters<R>( + distribution: Self::LeafHashParameterDistribution, + rng: &mut R, + ) -> LeafHashParameters<Self> + where + R: CryptoRng + RngCore + ?Sized, + { + let _ = (distribution, rng); + } + + #[inline] + fn sample_inner_hash_parameters<R>( + distribution: Self::InnerHashParameterDistribution, + rng: &mut R, + ) -> InnerHashParameters<Self> + where + R: CryptoRng + RngCore + ?Sized, + { + let _ = (distribution, rng); + } +} diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index df2289c70..573375f25 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -362,7 +362,10 @@ where if matches!(leaf_digests.size_hint().1, Some(max) if max <= capacity::<C>() - self.len()) { loop { match self.maybe_push_digest(parameters, || leaf_digests.next()) { - Some(result) => assert!(result), + Some(result) => assert!( + result, + "Unable to push digest even though the tree should have enough capacity to do so." + ), _ => return Ok(()), } } diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 6474406d1..7857210f3 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -86,9 +86,9 @@ zk-garage-plonk = { package = "plonk", git = "https://github.com/zk-garage/plonk [dev-dependencies] async-std = { version = "1.10.0", features = ["attributes", "unstable"] } criterion = "0.3.5" +futures = "0.3.19" manta-accounting = { path = "../manta-accounting", features = ["test"] } manta-crypto = { path = "../manta-crypto", features = ["test"] } manta-pay = { path = ".", features = ["test"] } rand = "0.8.4" tempfile = "3.2.0" - diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index c51c74076..7f7b30b9a 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -608,6 +608,11 @@ impl KeyDerivationFunction for HierarchicalKeyDerivationFunction { } } +impl MerkleTreeConfiguration { + /// Width of the Merkle Forest + pub const FOREST_WIDTH: usize = 256; +} + impl merkle_tree::forest::Configuration for MerkleTreeConfiguration { type Index = u8; @@ -626,3 +631,20 @@ impl merkle_tree::forest::Configuration for MerkleTreeConfiguration { result[0] } } + +/* NOTE: Configuration for testing single-tree forest. +impl MerkleTreeConfiguration { + /// Width of the Merkle Forest + pub const FOREST_WIDTH: usize = 1; +} + +impl merkle_tree::forest::Configuration for MerkleTreeConfiguration { + type Index = merkle_tree::forest::SingleTreeIndex; + + #[inline] + fn tree_index(leaf: &merkle_tree::Leaf<Self>) -> Self::Index { + let _ = leaf; + Default::default() + } +} +*/ diff --git a/manta-pay/src/test/ledger.rs b/manta-pay/src/test/ledger.rs index 65c0de5bb..4a7951796 100644 --- a/manta-pay/src/test/ledger.rs +++ b/manta-pay/src/test/ledger.rs @@ -17,6 +17,7 @@ //! Test Ledger Implementation // FIXME: How to model existential deposits and fee payments? +// FIXME: Add in some concurrency (and measure how much we need it). use crate::config::{ Config, EncryptedNote, MerkleTreeConfiguration, MultiVerifyingContext, ProofSystem, @@ -38,16 +39,23 @@ use manta_accounting::{ }; use manta_crypto::{ constraint::ProofSystem as _, - merkle_tree::{self, forest::Configuration, Tree}, + merkle_tree::{ + self, + forest::{Configuration, FixedIndex}, + Tree, + }, }; use manta_util::{future::LocalBoxFuture, into_array_unchecked}; use std::collections::{HashMap, HashSet}; +/// Merkle Forest Index +pub type MerkleForestIndex = <MerkleTreeConfiguration as Configuration>::Index; + /// UTXO Merkle Forest Type pub type UtxoMerkleForest = merkle_tree::forest::TreeArrayMerkleForest< MerkleTreeConfiguration, merkle_tree::single_path::SinglePath<MerkleTreeConfiguration>, - 256, + { MerkleTreeConfiguration::FOREST_WIDTH }, >; /// Wrap Type @@ -86,7 +94,7 @@ pub struct Ledger { utxos: HashSet<Utxo>, /// Shards - shards: HashMap<u8, HashMap<u64, (Utxo, EncryptedNote)>>, + shards: HashMap<MerkleForestIndex, IndexSet<(Utxo, EncryptedNote)>>, /// UTXO Forest utxo_forest: UtxoMerkleForest, @@ -108,7 +116,9 @@ impl Ledger { Self { void_numbers: Default::default(), utxos: Default::default(), - shards: (0..=255).map(move |i| (i, Default::default())).collect(), + shards: (0..MerkleTreeConfiguration::FOREST_WIDTH) + .map(move |i| (MerkleForestIndex::from_index(i), Default::default())) + .collect(), utxo_forest: UtxoMerkleForest::new(utxo_forest_parameters), accounts: Default::default(), verifying_context, @@ -186,8 +196,8 @@ impl ReceiverLedger<Config> for Ledger { .shards .get_mut(&MerkleTreeConfiguration::tree_index(&utxo.0)) .expect("All shards exist when the ledger is constructed."); - let len = shard.len(); - shard.insert(len as u64, (utxo.0, note)); + // let len = shard.len(); + shard.insert((utxo.0, note)); self.utxos.insert(utxo.0); self.utxo_forest.push(&utxo.0); } @@ -322,7 +332,7 @@ impl TransferLedger<Config> for Ledger { #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Checkpoint { /// Receiver Index - pub receiver_index: [usize; 256], + pub receiver_index: [usize; MerkleTreeConfiguration::FOREST_WIDTH], /// Sender Index pub sender_index: usize, @@ -331,7 +341,10 @@ pub struct Checkpoint { impl Checkpoint { /// Builds a new [`Checkpoint`] from `receiver_index` and `sender_index`. #[inline] - pub fn new(receiver_index: [usize; 256], sender_index: usize) -> Self { + pub fn new( + receiver_index: [usize; MerkleTreeConfiguration::FOREST_WIDTH], + sender_index: usize, + ) -> Self { Self { receiver_index, sender_index, @@ -342,7 +355,7 @@ impl Checkpoint { impl Default for Checkpoint { #[inline] fn default() -> Self { - Self::new([0; 256], 0) + Self::new([0; MerkleTreeConfiguration::FOREST_WIDTH], 0) } } @@ -388,8 +401,8 @@ impl ledger::Connection<Config> for LedgerConnection { let ledger = self.ledger.read().await; let mut receivers = Vec::new(); for (i, mut index) in checkpoint.receiver_index.iter().copied().enumerate() { - let shard = &ledger.shards[&(i as u8)]; - while let Some(entry) = shard.get(&(index as u64)) { + let shard = &ledger.shards[&MerkleForestIndex::from_index(i)]; + while let Some(entry) = shard.get_index(index) { receivers.push(*entry); index += 1; } @@ -451,13 +464,37 @@ mod test { wallet::{self, cache::OnDiskMultiProvingContext, Signer, Wallet}, }; use async_std::{println, task}; + use futures::stream::StreamExt; use manta_accounting::{ + asset::{Asset, AssetList}, key::AccountTable, - transfer::{self, canonical::Transaction}, + transfer, + wallet::test::{ + sim::{ActionSim, Simulator}, + ActionType, Actor, PublicBalanceOracle, Simulation, + }, }; use manta_crypto::rand::{CryptoRng, Rand, RngCore}; use rand::thread_rng; + impl PublicBalanceOracle for LedgerConnection { + #[inline] + fn public_balances(&self) -> LocalBoxFuture<Option<AssetList>> { + Box::pin(async { + Some( + self.ledger + .read() + .await + .accounts + .get(&self.account)? + .iter() + .map(|(id, value)| Asset::new(*id, *value)) + .collect(), + ) + }) + } + } + /// Samples an empty wallet for `account` on `ledger`. #[inline] fn sample_wallet<R>( @@ -489,7 +526,7 @@ mod test { let directory = task::spawn_blocking(tempfile::tempdir) .await .expect("Unable to generate temporary test directory."); - println!("Temporary Directory: {:?}", directory).await; + println!("[INFO] Temporary Directory: {:?}", directory).await; let mut rng = thread_rng(); let parameters = rng.gen(); @@ -509,15 +546,20 @@ mod test { .expect("Unable to save proving context to disk."); let mut ledger = Ledger::new(utxo_set_parameters.clone(), verifying_context); - ledger.set_public_balance(AccountId(0), AssetId(0), AssetValue(10000)); - ledger.set_public_balance(AccountId(1), AssetId(0), AssetValue(1000)); - ledger.set_public_balance(AccountId(2), AssetId(0), AssetValue(100)); - println!("Ledger: {:?}", ledger.accounts).await; + ledger.set_public_balance(AccountId(0), AssetId(0), AssetValue(1000000)); + ledger.set_public_balance(AccountId(0), AssetId(1), AssetValue(1000000)); + ledger.set_public_balance(AccountId(0), AssetId(2), AssetValue(1000000)); + ledger.set_public_balance(AccountId(1), AssetId(0), AssetValue(1000000)); + ledger.set_public_balance(AccountId(1), AssetId(1), AssetValue(1000000)); + ledger.set_public_balance(AccountId(1), AssetId(2), AssetValue(1000000)); + ledger.set_public_balance(AccountId(2), AssetId(0), AssetValue(1000000)); + ledger.set_public_balance(AccountId(2), AssetId(1), AssetValue(1000000)); + ledger.set_public_balance(AccountId(2), AssetId(2), AssetValue(1000000)); let ledger = Rc::new(RwLock::new(ledger)); - println!("Building Wallets").await; + println!("[INFO] Building Wallets").await; let mut alice = sample_wallet( AccountId(0), @@ -546,74 +588,42 @@ mod test { &mut rng, ); - let alice_key = alice - .receiving_key() - .await - .expect("Unable to get Alices's public key."); - - let bob_key = bob - .receiving_key() - .await - .expect("Unable to get Bob's public key."); - - let charlie_key = charlie - .receiving_key() - .await - .expect("Unable to get Charlie's public key."); - - println!("Alice [wallet]: {:?}", alice.assets()).await; - println!("Bob [wallet]: {:?}", bob.assets()).await; - println!("Charlie [wallet]: {:?}", charlie.assets()).await; - - println!("ALICE MINT 1000").await; - alice - .post(Transaction::Mint(AssetId(0).value(1000))) - .await - .expect("Unable to build mint."); - alice.sync().await.expect("Unable to sync."); - println!("Alice [wallet]: {:?}", alice.assets()).await; - - println!("ALICE PRIVATE TRANSFER 89").await; - alice - .post(Transaction::PrivateTransfer(AssetId(0).value(89), bob_key)) - .await - .expect("Unable to build private transfer."); - alice.sync().await.expect("Unable to sync."); - println!("Alice [wallet]: {:?}", alice.assets()).await; - - println!("BOB PRIVATE TRANSFER 6").await; - bob.post(Transaction::PrivateTransfer( - AssetId(0).value(6), - charlie_key, - )) - .await - .expect("Unable to build private transfer."); - bob.sync().await.expect("Unable to sync."); - println!("Bob [wallet]: {:?}", bob.assets()).await; - - println!("CHARLIE PRIVATE TRANSFER 1").await; - charlie - .post(Transaction::PrivateTransfer(AssetId(0).value(1), alice_key)) - .await - .expect("Unable to build private transfer."); - charlie.sync().await.expect("Unable to sync."); - println!("Charlie [wallet]: {:?}", charlie.assets()).await; + let mut dave = sample_wallet( + AccountId(3), + &ledger, + &cache, + &parameters, + &utxo_set_parameters, + &mut rng, + ); - println!("ALICE RECLAIM 71").await; - alice - .post(Transaction::Reclaim(AssetId(0).value(71))) - .await - .expect("Unable to build private transfer."); - alice.sync().await.expect("Unable to sync."); - println!("Alice [wallet]: {:?}", alice.assets()).await; + let alice_key = alice.receiving_key().await.unwrap(); + let bob_key = bob.receiving_key().await.unwrap(); + let charlie_key = charlie.receiving_key().await.unwrap(); + let dave_key = dave.receiving_key().await.unwrap(); + + let mut simulator = Simulator::new( + ActionSim(Simulation::new([alice_key, bob_key, charlie_key, dave_key])), + vec![ + Actor::new(alice, Default::default(), 200), + Actor::new(bob, Default::default(), 200), + Actor::new(charlie, Default::default(), 200), + Actor::new(dave, Default::default(), 200), + ], + ); - alice.sync().await.expect("Unable to sync."); - bob.sync().await.expect("Unable to sync."); - charlie.sync().await.expect("Unable to sync."); + println!("[INFO] Running Simulation\n").await; - println!("Alice [wallet]: {:?}", alice.assets()).await; - println!("Bob [wallet]: {:?}", bob.assets()).await; - println!("Charlie [wallet]: {:?}", charlie.assets()).await; + let mut events = simulator.run(thread_rng); + while let Some(event) = events.next().await { + match event.event.action { + ActionType::Skip | ActionType::GeneratePublicKey => {} + _ => println!("{:?}", event).await, + } + if event.event.result.is_err() { + break; + } + } task::spawn_blocking(move || directory.close()) .await diff --git a/manta-pay/src/wallet/mod.rs b/manta-pay/src/wallet/mod.rs index 45132daa7..70f7e6f92 100644 --- a/manta-pay/src/wallet/mod.rs +++ b/manta-pay/src/wallet/mod.rs @@ -39,7 +39,7 @@ pub type UtxoSet = merkle_tree::forest::TreeArrayMerkleForest< MerkleTreeConfiguration, merkle_tree::full::Full<MerkleTreeConfiguration>, >, - 256, + { MerkleTreeConfiguration::FOREST_WIDTH }, >; impl signer::Configuration for Config { From ac33048d8beb36c8de51019851c0c72ba19b5831 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 17 Jan 2022 21:59:41 -0500 Subject: [PATCH 189/275] feat: generalize simulation test --- manta-accounting/src/wallet/test/mod.rs | 19 +++-- manta-pay/src/test/ledger.rs | 93 +++++++++---------------- 2 files changed, 45 insertions(+), 67 deletions(-) diff --git a/manta-accounting/src/wallet/test/mod.rs b/manta-accounting/src/wallet/test/mod.rs index aa84869f0..44516aab9 100644 --- a/manta-accounting/src/wallet/test/mod.rs +++ b/manta-accounting/src/wallet/test/mod.rs @@ -16,6 +16,8 @@ //! Testing and Simulation Framework +// TODO: Perform delays instead of just `Skip` which doesn't really spread actors out in time. + use crate::{ asset::{Asset, AssetList}, transfer::{canonical::Transaction, Configuration, PublicKey, ReceivingKey}, @@ -92,7 +94,7 @@ impl Default for ActionDistributionPMF { #[inline] fn default() -> Self { Self { - skip: 4.0, + skip: 0.0, mint: 1.0, private_transfer: 1.0, reclaim: 1.0, @@ -281,6 +283,8 @@ pub type PublicKeyDatabase<C> = IndexSet<ReceivingKey<C>>; pub type SharedPublicKeyDatabase<C> = Rc<RwLock<PublicKeyDatabase<C>>>; /// Simulation +#[derive(derivative::Derivative)] +#[derivative(Default(bound = ""))] pub struct Simulation<C, L, S> where C: Configuration, @@ -344,10 +348,15 @@ where ActionType::PrivateTransfer => match actor.sample_withdraw(rng).await { Some(asset) => { let public_keys = self.public_keys.read().await; - Action::Post(Transaction::PrivateTransfer( - asset, - public_keys[rng.gen_range(0..public_keys.len())].clone(), - )) + let len = public_keys.len(); + if len == 0 { + Action::GeneratePublicKey + } else { + Action::Post(Transaction::PrivateTransfer( + asset, + public_keys[rng.gen_range(0..len)].clone(), + )) + } } _ => match actor.sample_deposit(rng).await { Some(asset) => Action::Post(Transaction::Mint(asset)), diff --git a/manta-pay/src/test/ledger.rs b/manta-pay/src/test/ledger.rs index 4a7951796..ceb1727d5 100644 --- a/manta-pay/src/test/ledger.rs +++ b/manta-pay/src/test/ledger.rs @@ -196,7 +196,6 @@ impl ReceiverLedger<Config> for Ledger { .shards .get_mut(&MerkleTreeConfiguration::tree_index(&utxo.0)) .expect("All shards exist when the ledger is constructed."); - // let len = shard.len(); shard.insert((utxo.0, note)); self.utxos.insert(utxo.0); self.utxo_forest.push(&utxo.0); @@ -447,7 +446,10 @@ impl ledger::Connection<Config> for LedgerConnection { Ok(posting_key) => { posting_key.post(&(), &mut *ledger); } - _ => return Ok(PushResponse { success: false }), + Err(err) => { + async_std::println!("\n[INFO] Ledger Validation Error: {:?}\n", err).await; + return Ok(PushResponse { success: false }); + } } } Ok(PushResponse { success: true }) @@ -545,72 +547,38 @@ mod test { .await .expect("Unable to save proving context to disk."); + const ACTOR_COUNT: usize = 10; + let mut ledger = Ledger::new(utxo_set_parameters.clone(), verifying_context); - ledger.set_public_balance(AccountId(0), AssetId(0), AssetValue(1000000)); - ledger.set_public_balance(AccountId(0), AssetId(1), AssetValue(1000000)); - ledger.set_public_balance(AccountId(0), AssetId(2), AssetValue(1000000)); - ledger.set_public_balance(AccountId(1), AssetId(0), AssetValue(1000000)); - ledger.set_public_balance(AccountId(1), AssetId(1), AssetValue(1000000)); - ledger.set_public_balance(AccountId(1), AssetId(2), AssetValue(1000000)); - ledger.set_public_balance(AccountId(2), AssetId(0), AssetValue(1000000)); - ledger.set_public_balance(AccountId(2), AssetId(1), AssetValue(1000000)); - ledger.set_public_balance(AccountId(2), AssetId(2), AssetValue(1000000)); + for i in 0..ACTOR_COUNT { + ledger.set_public_balance(AccountId(i as u64), AssetId(0), AssetValue(1000000)); + ledger.set_public_balance(AccountId(i as u64), AssetId(1), AssetValue(1000000)); + ledger.set_public_balance(AccountId(i as u64), AssetId(2), AssetValue(1000000)); + } let ledger = Rc::new(RwLock::new(ledger)); println!("[INFO] Building Wallets").await; - let mut alice = sample_wallet( - AccountId(0), - &ledger, - &cache, - &parameters, - &utxo_set_parameters, - &mut rng, - ); - - let mut bob = sample_wallet( - AccountId(1), - &ledger, - &cache, - &parameters, - &utxo_set_parameters, - &mut rng, - ); - - let mut charlie = sample_wallet( - AccountId(2), - &ledger, - &cache, - &parameters, - &utxo_set_parameters, - &mut rng, - ); - - let mut dave = sample_wallet( - AccountId(3), - &ledger, - &cache, - &parameters, - &utxo_set_parameters, - &mut rng, - ); - - let alice_key = alice.receiving_key().await.unwrap(); - let bob_key = bob.receiving_key().await.unwrap(); - let charlie_key = charlie.receiving_key().await.unwrap(); - let dave_key = dave.receiving_key().await.unwrap(); - - let mut simulator = Simulator::new( - ActionSim(Simulation::new([alice_key, bob_key, charlie_key, dave_key])), - vec![ - Actor::new(alice, Default::default(), 200), - Actor::new(bob, Default::default(), 200), - Actor::new(charlie, Default::default(), 200), - Actor::new(dave, Default::default(), 200), - ], - ); + let actors = (0..ACTOR_COUNT) + .map(|i| { + Actor::new( + sample_wallet( + AccountId(i as u64), + &ledger, + &cache, + &parameters, + &utxo_set_parameters, + &mut rng, + ), + Default::default(), + rand::Rng::gen_range(&mut rng, 50..300), + ) + }) + .collect::<Vec<_>>(); + + let mut simulator = Simulator::new(ActionSim(Simulation::default()), actors); println!("[INFO] Running Simulation\n").await; @@ -620,7 +588,8 @@ mod test { ActionType::Skip | ActionType::GeneratePublicKey => {} _ => println!("{:?}", event).await, } - if event.event.result.is_err() { + if let Err(err) = event.event.result { + println!("\n[ERROR] Simulation Error: {:?}\n", err).await; break; } } From 9424b42c4dcf8d145867c0458814ee498dc35348 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 18 Jan 2022 01:12:27 -0500 Subject: [PATCH 190/275] fix: improve signer edge-cases and speed up sync algorithm --- manta-accounting/src/transfer/canonical.rs | 19 ++++---- manta-accounting/src/wallet/signer.rs | 52 ++++++++++------------ manta-accounting/src/wallet/test/mod.rs | 20 ++++++--- 3 files changed, 49 insertions(+), 42 deletions(-) diff --git a/manta-accounting/src/transfer/canonical.rs b/manta-accounting/src/transfer/canonical.rs index f2337c524..fca5b6ea5 100644 --- a/manta-accounting/src/transfer/canonical.rs +++ b/manta-accounting/src/transfer/canonical.rs @@ -66,14 +66,8 @@ macro_rules! impl_shape { /// Builds a new alias using the given shape type. macro_rules! alias_type { ($type:tt, $t:ident, $shape:tt) => { - $type< - $t, - { $shape::SOURCES }, - { $shape::SENDERS }, - { $shape::RECEIVERS }, - { $shape::SINKS }, - > - } + $type<$t, { $shape::SOURCES }, { $shape::SENDERS }, { $shape::RECEIVERS }, { $shape::SINKS }> + }; } /// Builds a new [`Transfer`] alias using the given shape type. @@ -242,6 +236,15 @@ impl TransferShape { } /// Canonical Transaction Type +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "ReceivingKey<C>: Clone"), + Copy(bound = "ReceivingKey<C>: Copy"), + Debug(bound = "ReceivingKey<C>: Debug"), + Eq(bound = "ReceivingKey<C>: Eq"), + Hash(bound = "ReceivingKey<C>: Hash"), + PartialEq(bound = "ReceivingKey<C>: PartialEq") +)] pub enum Transaction<C> where C: Configuration, diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 71b007854..769586299 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -125,7 +125,7 @@ pub enum SyncResponse { /// /// Whenever the [`Signer`] gets ahead of the synchronization point, it would have updated its /// internal balance state further along than any connection following its updates. In this - /// case, to entire balance state needs to be sent to catch up. + /// case, the entire balance state needs to be sent to catch up. Full { /// Full Balance State assets: Vec<Asset>, @@ -348,7 +348,7 @@ where secret_key: &SecretKey<C>, ephemeral_public_key: &PublicKey<C>, asset: Asset, - void_number: &VoidNumber<C>, + void_numbers: &mut Vec<VoidNumber<C>>, utxo_set: &mut C::UtxoSet, withdraw: &mut Vec<Asset>, ) -> bool { @@ -359,8 +359,9 @@ where ephemeral_public_key, &asset, ); - let known_void_number = C::void_number(&parameters.void_number_hash, &utxo, secret_key); - if *void_number == known_void_number { + let void_number = C::void_number(&parameters.void_number_hash, &utxo, secret_key); + if let Some(index) = void_numbers.iter().position(move |v| v == &void_number) { + void_numbers.remove(index); utxo_set.remove_proof(&utxo); if !asset.is_zero() { withdraw.push(asset); @@ -394,25 +395,23 @@ where &mut deposit, )?; } - for void_number in void_numbers { - self.assets.retain(|(index, ephemeral_public_key), assets| { - assets.retain( - |asset| match self.accounts.get_default().spend_key(*index) { - Ok(secret_key) => Self::is_asset_unspent( - parameters, - &secret_key, - ephemeral_public_key, - *asset, - &void_number, - &mut self.utxo_set, - &mut withdraw, - ), - _ => true, - }, - ); - !assets.is_empty() - }); - } + self.assets.retain(|(index, ephemeral_public_key), assets| { + assets.retain( + |asset| match self.accounts.get_default().spend_key(*index) { + Ok(secret_key) => Self::is_asset_unspent( + parameters, + &secret_key, + ephemeral_public_key, + *asset, + &mut void_numbers, + &mut self.utxo_set, + &mut withdraw, + ), + _ => true, + }, + ); + !assets.is_empty() + }); if is_partial { Ok(SyncResponse::Partial { deposit, withdraw }) } else { @@ -487,7 +486,7 @@ where asset: Asset, ) -> Result<Selection<C>, Error<C>> { let selection = self.assets.select(asset); - if selection.is_empty() { + if !asset.is_zero() && selection.is_empty() { return Err(Error::InsufficientBalance(asset)); } Selection::new(selection, move |k, v| { @@ -637,10 +636,6 @@ where mut pre_senders: Vec<PreSender<C>>, posts: &mut Vec<TransferPost<C>>, ) -> Result<[Sender<C>; PrivateTransferShape::SENDERS], Error<C>> { - assert!( - !pre_senders.is_empty(), - "The set of initial senders cannot be empty." - ); let mut new_zeroes = Vec::new(); while pre_senders.len() > PrivateTransferShape::SENDERS { let mut joins = Vec::new(); @@ -808,7 +803,6 @@ where asset: Asset, receiver: Option<ReceivingKey<C>>, ) -> SignResult<C, Self> { - // FIXME: Handle the withdraw of `asset.value == 0` case. let selection = self.state.select(&self.parameters.parameters, asset)?; let change = self .state diff --git a/manta-accounting/src/wallet/test/mod.rs b/manta-accounting/src/wallet/test/mod.rs index 44516aab9..925696778 100644 --- a/manta-accounting/src/wallet/test/mod.rs +++ b/manta-accounting/src/wallet/test/mod.rs @@ -96,8 +96,8 @@ impl Default for ActionDistributionPMF { Self { skip: 0.0, mint: 1.0, - private_transfer: 1.0, - reclaim: 1.0, + private_transfer: 2.0, + reclaim: 0.5, generate_public_key: 0.5, } } @@ -393,9 +393,19 @@ where Transaction::PrivateTransfer(_, _) => ActionType::PrivateTransfer, Transaction::Reclaim(_) => ActionType::Reclaim, }; - Event { - action, - result: actor.wallet.post(transaction).await, + let mut retries = 4; // TODO: Make this parameter tunable based on concurrency. + loop { + let result = actor.wallet.post(transaction.clone()).await; + if let Ok(false) = result { + if retries == 0 { + break Event { action, result }; + } else { + retries -= 1; + continue; + } + } else { + break Event { action, result }; + } } } Action::GeneratePublicKey => Event { From 34164f5076fb9ce97492cdf266ecb67d42a50361 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 18 Jan 2022 12:52:31 -0500 Subject: [PATCH 191/275] feat: improve interface consistency and docs --- manta-accounting/Cargo.toml | 2 +- manta-accounting/src/fs.rs | 2 +- manta-accounting/src/key.rs | 2 +- manta-accounting/src/transfer/batch.rs | 4 +- manta-accounting/src/transfer/canonical.rs | 10 +- manta-accounting/src/transfer/mod.rs | 29 ++--- manta-accounting/src/transfer/test.rs | 18 +-- manta-accounting/src/wallet/ledger.rs | 1 + manta-accounting/src/wallet/signer.rs | 21 ++-- manta-accounting/src/wallet/test/mod.rs | 4 +- manta-accounting/src/wallet/test/sim.rs | 7 +- manta-crypto/src/accumulator.rs | 1 - manta-crypto/src/ecc.rs | 8 +- manta-crypto/src/encryption.rs | 12 +- manta-crypto/src/key.rs | 93 +++++++++++---- manta-crypto/src/merkle_tree/forest.rs | 3 +- manta-crypto/src/merkle_tree/fork.rs | 2 +- manta-crypto/src/merkle_tree/inner_tree.rs | 7 -- manta-crypto/src/merkle_tree/mod.rs | 2 +- manta-crypto/src/merkle_tree/path.rs | 8 +- manta-crypto/src/merkle_tree/single_path.rs | 2 +- manta-crypto/src/merkle_tree/tree.rs | 1 - manta-crypto/src/rand.rs | 2 - manta-pay/Cargo.toml | 2 +- manta-pay/src/config.rs | 7 -- .../constraint/arkworks/constraint_system.rs | 13 +- .../constraint/arkworks/proof_system.rs | 7 -- manta-pay/src/crypto/ecc.rs | 4 +- manta-pay/src/crypto/hash/poseidon.rs | 42 +++---- manta-pay/src/crypto/key.rs | 4 +- manta-pay/src/key.rs | 111 ++++++++++-------- manta-pay/src/test/ledger.rs | 5 +- manta-pay/src/test/merkle_tree.rs | 47 -------- manta-pay/src/test/mod.rs | 12 +- manta-util/src/array.rs | 6 +- manta-util/src/iter/chunk_by.rs | 6 + manta-util/src/iter/mod.rs | 2 +- manta-util/src/lib.rs | 1 - manta-util/src/num.rs | 53 --------- manta-util/src/pointer.rs | 1 - manta-util/src/sealed.rs | 2 +- 41 files changed, 233 insertions(+), 333 deletions(-) delete mode 100644 manta-pay/src/test/merkle_tree.rs delete mode 100644 manta-util/src/num.rs diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 8f92a4ed2..7749e569e 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -35,7 +35,7 @@ cocoon-fs = [ std = ["cocoon/std", "async-std"] # Testing Frameworks -test = ["futures", "indexmap", "rand/alloc", "statrs"] +test = ["async-std", "futures", "indexmap", "rand/alloc", "statrs"] [dependencies] async-std = { version = "1.10.0", optional = true } diff --git a/manta-accounting/src/fs.rs b/manta-accounting/src/fs.rs index 4b402096b..a6c9f9c79 100644 --- a/manta-accounting/src/fs.rs +++ b/manta-accounting/src/fs.rs @@ -18,7 +18,7 @@ // FIXME: Add asynchronous streaming interfaces. -use alloc::vec::Vec; +use alloc::{boxed::Box, vec::Vec}; use core::{fmt::Debug, hash::Hash}; use manta_util::{future::LocalBoxFuture, Deserialize, Serialize}; diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index cf5e620da..45710f3f4 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -16,7 +16,7 @@ //! Hierarchical Key Derivation Schemes -use alloc::vec::Vec; +use alloc::{vec, vec::Vec}; use core::{ fmt::{self, Debug}, hash::Hash, diff --git a/manta-accounting/src/transfer/batch.rs b/manta-accounting/src/transfer/batch.rs index cae7e5fbe..1020b6b1f 100644 --- a/manta-accounting/src/transfer/batch.rs +++ b/manta-accounting/src/transfer/batch.rs @@ -16,6 +16,8 @@ //! Batched Transfers +// TODO: Move more of the batching algorithm here to improve library interfaces. + use crate::{ asset::Asset, transfer::{Configuration, Parameters, PreSender, Receiver, SpendingKey, Utxo}, @@ -54,8 +56,6 @@ where where R: CryptoRng + RngCore + ?Sized, { - // TODO: Add optimization path for receiver re-sampling so that we ensure that all UTXOs - // are maximally independent. let mut receivers = Vec::with_capacity(RECEIVERS); let mut zeroes = Vec::with_capacity(RECEIVERS - 1); let (receiver, pre_sender) = spending_key.internal_pair(parameters, rng.gen(), asset); diff --git a/manta-accounting/src/transfer/canonical.rs b/manta-accounting/src/transfer/canonical.rs index fca5b6ea5..dc4e60d2f 100644 --- a/manta-accounting/src/transfer/canonical.rs +++ b/manta-accounting/src/transfer/canonical.rs @@ -97,7 +97,7 @@ where /// Builds a [`Mint`] from `asset` and `receiver`. #[inline] pub fn build(asset: Asset, receiver: Receiver<C>) -> Self { - Self::new(Some(asset.id), [asset.value], [], [receiver], []) + Self::new_unchecked(Some(asset.id), [asset.value], [], [receiver], []) } } @@ -124,7 +124,7 @@ where senders: [Sender<C>; PrivateTransferShape::SENDERS], receivers: [Receiver<C>; PrivateTransferShape::RECEIVERS], ) -> Self { - Self::new(None, [], senders, receivers, []) + Self::new_unchecked(None, [], senders, receivers, []) } } @@ -141,10 +141,10 @@ pub struct ReclaimShape; impl_shape!( ReclaimShape, - 0, + PrivateTransferShape::SOURCES, PrivateTransferShape::SENDERS, PrivateTransferShape::RECEIVERS - 1, - 1 + PrivateTransferShape::SINKS + 1 ); /// [`Reclaim`] Transfer @@ -161,7 +161,7 @@ where receivers: [Receiver<C>; ReclaimShape::RECEIVERS], reclaim: Asset, ) -> Self { - Self::new(reclaim.id, [], senders, receivers, [reclaim.value]) + Self::new_unchecked(Some(reclaim.id), [], senders, receivers, [reclaim.value]) } } diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index bbac0b235..cddcb72f9 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -176,7 +176,7 @@ pub trait Configuration { secret_key: &SecretKeyVar<Self>, compiler: &mut Self::Compiler, ) -> PublicKeyVar<Self> { - parameters.derive(secret_key, compiler) + parameters.derive_in(secret_key, compiler) } /// Generates the commitment trapdoor associated to `secret_key` and `public_key`. @@ -186,7 +186,7 @@ pub trait Configuration { secret_key: &SecretKey<Self>, public_key: &PublicKey<Self>, ) -> Trapdoor<Self> { - key_agreement.agree(secret_key, public_key, &mut ()) + key_agreement.agree(secret_key, public_key) } /// Generates the commitment trapdoor associated to `secret_key` and `public_key`. @@ -197,7 +197,7 @@ pub trait Configuration { public_key: &PublicKeyVar<Self>, compiler: &mut Self::Compiler, ) -> TrapdoorVar<Self> { - key_agreement.agree(secret_key, public_key, compiler) + key_agreement.agree_in(secret_key, public_key, compiler) } /// Generates the trapdoor associated to `secret_key` and `public_key` and then uses it to @@ -508,8 +508,8 @@ where #[inline] pub fn derive(&self, parameters: &C::KeyAgreementScheme) -> ReceivingKey<C> { ReceivingKey { - spend: parameters.derive(&self.spend, &mut ()), - view: parameters.derive(&self.view, &mut ()), + spend: parameters.derive(&self.spend), + view: parameters.derive(&self.view), } } @@ -534,7 +534,6 @@ where ephemeral_key: PublicKey<C>, asset: Asset, ) -> PreSender<C> { - // TODO: See if this clone is really needed. PreSender::new(parameters, self.spend.clone(), ephemeral_key, asset) } @@ -921,7 +920,7 @@ where /// # Safety /// /// This method can only be called once we check that `void_number` is not already stored on - /// the ledger. See [`is_unspent`](Self::is_unspent). + /// the ledger. See [`is_unspent`](Self::is_unspent) for more. fn spend( &mut self, utxo_set_output: Self::ValidUtxoSetOutput, @@ -1207,7 +1206,7 @@ where /// # Safety /// /// This method can only be called once we check that `utxo` is not already stored on the - /// ledger. See [`is_not_registered`](Self::is_not_registered). + /// ledger. See [`is_not_registered`](Self::is_not_registered) for more. fn register( &mut self, utxo: Self::ValidUtxo, @@ -1481,10 +1480,10 @@ where )?, asset_id: self.asset_id, sources: self.sources.into(), - sender_posts: IntoIterator::into_iter(self.senders) - .map(Sender::into_post) - .collect(), - receiver_posts: IntoIterator::into_iter(self.receivers) + sender_posts: self.senders.into_iter().map(Sender::into_post).collect(), + receiver_posts: self + .receivers + .into_iter() .map(Receiver::into_post) .collect(), sinks: self.sinks.into(), @@ -1531,7 +1530,6 @@ where compiler: &mut C::Compiler, ) { let mut secret_asset_ids = Vec::with_capacity(SENDERS + RECEIVERS); - let input_sum = Self::value_sum( self.senders .into_iter() @@ -1544,7 +1542,6 @@ where .collect::<Vec<_>>(), compiler, ); - let output_sum = Self::value_sum( self.receivers .into_iter() @@ -1557,9 +1554,7 @@ where .collect::<Vec<_>>(), compiler, ); - compiler.assert_eq(&input_sum, &output_sum); - match self.asset_id { Some(asset_id) => compiler.assert_all_eq_to_base(&asset_id, secret_asset_ids.iter()), _ => compiler.assert_all_eq(secret_asset_ids.iter()), @@ -1572,6 +1567,7 @@ where where I: IntoIterator<Item = C::AssetValueVar>, { + // TODO: Add a `Sum` trait for `compiler` and just do a sum here. iter.into_iter() .reduce(move |l, r| Add::add(l, r, compiler)) .unwrap() @@ -1700,7 +1696,6 @@ where I: Iterator<Item = (Self::AccountId, AssetValue)>; /// Checks that the transfer `proof` is valid. - #[allow(clippy::too_many_arguments)] // FIXME: Write a better abstraction for this. fn is_valid( &self, asset_id: Option<AssetId>, diff --git a/manta-accounting/src/transfer/test.rs b/manta-accounting/src/transfer/test.rs index e95958194..b0087a423 100644 --- a/manta-accounting/src/transfer/test.rs +++ b/manta-accounting/src/transfer/test.rs @@ -180,11 +180,7 @@ where let sender = PreSender::new( parameters, rng.gen(), - C::KeyAgreementScheme::derive_owned( - &parameters.key_agreement, - rng.gen(), - &mut (), - ), + parameters.key_agreement.derive_owned(rng.gen()), asset_id.with(*v), ); sender.insert_utxo(utxo_set); @@ -197,16 +193,8 @@ where Receiver::new( parameters, rng.gen(), - C::KeyAgreementScheme::derive_owned( - &parameters.key_agreement, - rng.gen(), - &mut (), - ), - C::KeyAgreementScheme::derive_owned( - &parameters.key_agreement, - rng.gen(), - &mut (), - ), + parameters.key_agreement.derive_owned(rng.gen()), + parameters.key_agreement.derive_owned(rng.gen()), asset_id.with(*v), ) }) diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs index 4792c28b3..977480031 100644 --- a/manta-accounting/src/wallet/ledger.rs +++ b/manta-accounting/src/wallet/ledger.rs @@ -18,6 +18,7 @@ // TODO: Report a more meaningful error on `push` failure. In some way, it must match the // `TransferPostError` variants. +// TODO: Move to a streaming protocol for `Connection::pull`. use crate::transfer::{Configuration, EncryptedNote, TransferPost, Utxo, VoidNumber}; use alloc::vec::Vec; diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 769586299..e7f7159c7 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -21,6 +21,11 @@ // TODO: Should have a mode on the signer where we return a generic error which reveals no detail // about what went wrong during signing. The kind of error returned from a signing could // reveal information about the internal state (privacy leak, not a secrecy leak). +// TODO: Review which subset of the errors are actually recoverable or not. For example, the +// non-existence of a membership proof may be a non-recoverable error since it only relies +// on invariants that must be maintained by the `Signer` and not by external sources; i.e. +// even if the `Signer` is fed malicious UTXOs, it should not error when looking for +// membership proofs and so if this error condition is met, the `Signer` has a bug in it. // TODO: Setup multi-account wallets using `crate::key::AccountTable`. // TODO: Move `sync` to a stream-based algorithm instead of iterator-based. // TODO: Save/Load `SignerState` to/from disk. @@ -252,7 +257,7 @@ impl<C> SignerParameters<C> where C: Configuration, { - /// Returns the parameters by reading from the proving context cache. + /// Returns the public parameters by reading from the proving context cache. #[inline] pub async fn get( &mut self, @@ -412,6 +417,9 @@ where ); !assets.is_empty() }); + + // TODO: Whenever we are doing a full update, don't even build the `deposit` and `withdraw` + // vectors, since we won't be needing them. if is_partial { Ok(SyncResponse::Partial { deposit, withdraw }) } else { @@ -769,15 +777,12 @@ where I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, R: IntoIterator<Item = VoidNumber<C>>, { - // FIXME: Do a capacity check on the current UTXO set? + // TODO: Do a capacity check on the current UTXO set? // // if self.utxo_set.capacity() < starting_index { - // panic!("something is very wrong here") + // panic!("full capacity") // } // - // TODO: Use a smarter object than `Vec` for `removes.into_iter().collect()` like a - // `HashSet` or some other set-like container with fast membership and remove ops. - // let utxo_set_len = self.state.utxo_set.len(); match utxo_set_len.checked_sub(starting_index) { Some(diff) => { @@ -868,8 +873,8 @@ where /// Signs the `transaction`, generating transfer posts. #[inline] pub async fn sign(&mut self, transaction: Transaction<C>) -> SignResult<C, Self> { - // TODO: Should we do a time-based release mechanism to amortize the cost of reading/writing - // to the proving context cache? + // TODO: Should we do a time-based release mechanism to amortize the cost of reading + // from the proving context cache? let result = self.sign_internal(transaction).await; self.state.utxo_set.rollback(); self.parameters.proving_context.release().await; diff --git a/manta-accounting/src/wallet/test/mod.rs b/manta-accounting/src/wallet/test/mod.rs index 925696778..c46da0481 100644 --- a/manta-accounting/src/wallet/test/mod.rs +++ b/manta-accounting/src/wallet/test/mod.rs @@ -23,7 +23,7 @@ use crate::{ transfer::{canonical::Transaction, Configuration, PublicKey, ReceivingKey}, wallet::{self, ledger, signer, Wallet}, }; -use alloc::rc::Rc; +use alloc::{boxed::Box, rc::Rc}; use async_std::sync::RwLock; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use indexmap::IndexSet; @@ -393,7 +393,7 @@ where Transaction::PrivateTransfer(_, _) => ActionType::PrivateTransfer, Transaction::Reclaim(_) => ActionType::Reclaim, }; - let mut retries = 4; // TODO: Make this parameter tunable based on concurrency. + let mut retries = 5; // TODO: Make this parameter tunable based on concurrency. loop { let result = actor.wallet.post(transaction.clone()).await; if let Ok(false) = result { diff --git a/manta-accounting/src/wallet/test/sim.rs b/manta-accounting/src/wallet/test/sim.rs index 1d44991eb..2eb20b557 100644 --- a/manta-accounting/src/wallet/test/sim.rs +++ b/manta-accounting/src/wallet/test/sim.rs @@ -16,7 +16,7 @@ //! Actor Simulation Framework -use alloc::vec::Vec; +use alloc::{boxed::Box, vec::Vec}; use core::{fmt::Debug, hash::Hash}; use futures::stream::{self, select_all::SelectAll, Stream, StreamExt}; use manta_crypto::rand::{CryptoRng, RngCore}; @@ -141,7 +141,6 @@ where where R: 's + CryptoRng + RngCore, { - // TODO: Look for a more efficient way to allocate the memory here instead of `Box::pin`. stream::unfold((actor, rng), move |mut state| { Box::pin(async move { simulation @@ -156,10 +155,10 @@ where /// Runs the simulator using `rng`, returning a stream of future events. #[inline] - pub fn run<'s, R, G>(&'s mut self, mut rng: G) -> impl 's + Stream<Item = Event<S>> + pub fn run<'s, R, F>(&'s mut self, mut rng: F) -> impl 's + Stream<Item = Event<S>> where R: 's + CryptoRng + RngCore, - G: 's + FnMut() -> R, + F: FnMut() -> R, { let mut streams = SelectAll::new(); for (i, actor) in self.actors.iter_mut().enumerate() { diff --git a/manta-crypto/src/accumulator.rs b/manta-crypto/src/accumulator.rs index 92249c9ba..c5a30c56a 100644 --- a/manta-crypto/src/accumulator.rs +++ b/manta-crypto/src/accumulator.rs @@ -110,7 +110,6 @@ where A: Accumulator + ?Sized, { type Item = A::Item; - type Model = A::Model; #[inline] diff --git a/manta-crypto/src/ecc.rs b/manta-crypto/src/ecc.rs index c4720ff58..c983ff0b7 100644 --- a/manta-crypto/src/ecc.rs +++ b/manta-crypto/src/ecc.rs @@ -109,18 +109,16 @@ where G: Group<COM>, { type SecretKey = G::Scalar; - type PublicKey = G; - type SharedSecret = G; #[inline] - fn derive(&self, secret_key: &Self::SecretKey, compiler: &mut COM) -> Self::PublicKey { - self.agree(secret_key, &self.generator, compiler) + fn derive_in(&self, secret_key: &Self::SecretKey, compiler: &mut COM) -> Self::PublicKey { + self.agree_in(secret_key, &self.generator, compiler) } #[inline] - fn agree( + fn agree_in( &self, secret_key: &Self::SecretKey, public_key: &Self::PublicKey, diff --git a/manta-crypto/src/encryption.rs b/manta-crypto/src/encryption.rs index a85cdd52f..aaa7b2a7b 100644 --- a/manta-crypto/src/encryption.rs +++ b/manta-crypto/src/encryption.rs @@ -67,9 +67,7 @@ pub mod symmetric { P: Into<S::Plaintext> + TryFrom<S::Plaintext>, { type Key = S::Key; - type Plaintext = P; - type Ciphertext = S::Ciphertext; #[inline] @@ -108,7 +106,7 @@ pub trait HybridPublicKeyEncryptionScheme: SymmetricKeyEncryptionScheme { secret_key: &SecretKey<Self>, public_key: &PublicKey<Self>, ) -> Self::Key { - Self::KeyDerivationFunction::derive(&parameters.agree(secret_key, public_key, &mut ())) + Self::KeyDerivationFunction::derive(&parameters.agree(secret_key, public_key)) } } @@ -145,9 +143,7 @@ where F: KeyDerivationFunction<Key = K::SharedSecret, Output = S::Key>, { type Key = S::Key; - type Plaintext = S::Plaintext; - type Ciphertext = S::Ciphertext; #[inline] @@ -210,7 +206,7 @@ where H::agree_derive(parameters, ephemeral_secret_key, public_key), plaintext, ), - ephemeral_public_key: parameters.derive(ephemeral_secret_key, &mut ()), + ephemeral_public_key: parameters.derive(ephemeral_secret_key), } } @@ -363,7 +359,7 @@ pub mod test { { let decrypted_message = EncryptedMessage::<H>::new( parameters, - &parameters.derive(secret_key, &mut ()), + &parameters.derive(secret_key), ephemeral_secret_key, plaintext.clone(), ) @@ -375,7 +371,7 @@ pub mod test { ); assert_eq!( decrypted_message.ephemeral_public_key, - parameters.derive(ephemeral_secret_key, &mut ()), + parameters.derive(ephemeral_secret_key), "Decrypted message should have included the correct ephemeral public key.", ); } diff --git a/manta-crypto/src/key.rs b/manta-crypto/src/key.rs index 861f22f50..12c8f3bda 100644 --- a/manta-crypto/src/key.rs +++ b/manta-crypto/src/key.rs @@ -16,6 +16,7 @@ //! Cryptographic Key Primitives +use crate::constraint::Native; use core::marker::PhantomData; /// Key Derivation Function @@ -49,7 +50,6 @@ pub mod kdf { F: KeyDerivationFunction<Key = [u8]>, { type Key = T; - type Output = F::Output; #[inline] @@ -78,7 +78,6 @@ pub mod kdf { F: KeyDerivationFunction<Key = [u8]>, { type Key = T; - type Output = F::Output; #[inline] @@ -113,27 +112,50 @@ pub trait KeyAgreementScheme<COM = ()> { /// Shared Secret Type type SharedSecret; - /// Derives a public key corresponding to `secret_key`. This public key should be sent to the - /// other party involved in the shared computation. - fn derive(&self, secret_key: &Self::SecretKey, compiler: &mut COM) -> Self::PublicKey; + /// Derives a public key corresponding to `secret_key` in the given `compiler`. This public key + /// should be sent to the other party involved in the shared computation. + fn derive_in(&self, secret_key: &Self::SecretKey, compiler: &mut COM) -> Self::PublicKey; /// Derives a public key corresponding to `secret_key`. This public key should be sent to the /// other party involved in the shared computation. + #[inline] + fn derive(&self, secret_key: &Self::SecretKey) -> Self::PublicKey + where + COM: Native, + { + self.derive_in(secret_key, &mut COM::compiler()) + } + + /// Derives a public key corresponding to `secret_key` in the given `compiler`. This public key + /// should be sent to the other party involved in the shared computation. /// /// # Implementation Note /// - /// This method is an optimization path for [`derive`] when the `secret_key` value is owned, - /// and by default, [`derive`] is used as its implementation. This method must return the same - /// value as [`derive`] on the same input. + /// This method is an optimization path for [`derive_in`] when the `secret_key` value is owned, + /// and by default, [`derive_in`] is used as its implementation. This method must return the same + /// value as [`derive_in`] on the same input. /// - /// [`derive`]: Self::derive + /// [`derive_in`]: Self::derive_in #[inline] - fn derive_owned(&self, secret_key: Self::SecretKey, compiler: &mut COM) -> Self::PublicKey { - self.derive(&secret_key, compiler) + fn derive_owned_in(&self, secret_key: Self::SecretKey, compiler: &mut COM) -> Self::PublicKey { + self.derive_in(&secret_key, compiler) } - /// Computes the shared secret given the known `secret_key` and the given `public_key`. - fn agree( + /// Derives a public key corresponding to `secret_key`. This public key should be sent to the + /// other party involved in the shared computation. + /// + /// See [`derive_owned_in`](Self::derive_owned_in) for more. + #[inline] + fn derive_owned(&self, secret_key: Self::SecretKey) -> Self::PublicKey + where + COM: Native, + { + self.derive(&secret_key) + } + + /// Computes the shared secret given the known `secret_key` and the given `public_key` in the + /// given `compiler`. + fn agree_in( &self, secret_key: &Self::SecretKey, public_key: &Self::PublicKey, @@ -141,22 +163,51 @@ pub trait KeyAgreementScheme<COM = ()> { ) -> Self::SharedSecret; /// Computes the shared secret given the known `secret_key` and the given `public_key`. + #[inline] + fn agree( + &self, + secret_key: &Self::SecretKey, + public_key: &Self::PublicKey, + ) -> Self::SharedSecret + where + COM: Native, + { + self.agree_in(secret_key, public_key, &mut COM::compiler()) + } + + /// Computes the shared secret given the known `secret_key` and the given `public_key` in the + /// given `compiler`. /// /// # Implementation Note /// - /// This method is an optimization path for [`agree`] when the `secret_key` value and - /// `public_key` value are owned, and by default, [`agree`] is used as its implementation. This - /// method must return the same value as [`agree`] on the same input. + /// This method is an optimization path for [`agree_in`] when the `secret_key` value and + /// `public_key` value are owned, and by default, [`agree_in`] is used as its implementation. This + /// method must return the same value as [`agree_in`] on the same input. /// - /// [`agree`]: Self::agree + /// [`agree_in`]: Self::agree_in #[inline] - fn agree_owned( + fn agree_owned_in( &self, secret_key: Self::SecretKey, public_key: Self::PublicKey, compiler: &mut COM, ) -> Self::SharedSecret { - self.agree(&secret_key, &public_key, compiler) + self.agree_in(&secret_key, &public_key, compiler) + } + + /// Computes the shared secret given the known `secret_key` and the given `public_key`. + /// + /// See [`agree_owned_in`](Self::agree_owned_in) for more. + #[inline] + fn agree_owned( + &self, + secret_key: Self::SecretKey, + public_key: Self::PublicKey, + ) -> Self::SharedSecret + where + COM: Native, + { + self.agree(&secret_key, &public_key) } } @@ -175,8 +226,8 @@ pub mod test { K::SharedSecret: Debug + PartialEq, { assert_eq!( - parameters.agree(lhs, &parameters.derive(rhs, &mut ()), &mut ()), - parameters.agree(rhs, &parameters.derive(lhs, &mut ()), &mut ()), + parameters.agree(lhs, &parameters.derive(rhs)), + parameters.agree(rhs, &parameters.derive(lhs)), "Key agreement schemes should satisfy the agreement property." ) } diff --git a/manta-crypto/src/merkle_tree/forest.rs b/manta-crypto/src/merkle_tree/forest.rs index 98c66cd02..8b9fe7a17 100644 --- a/manta-crypto/src/merkle_tree/forest.rs +++ b/manta-crypto/src/merkle_tree/forest.rs @@ -281,7 +281,6 @@ where InnerDigest<C>: Clone + PartialEq, { type Item = Leaf<C>; - type Model = Parameters<C>; #[inline] @@ -390,7 +389,7 @@ impl From<SingleTreeIndex> for usize { #[inline] fn from(index: SingleTreeIndex) -> Self { let _ = index; - 0 + Default::default() } } diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs index 0516ee656..dd95d38a4 100644 --- a/manta-crypto/src/merkle_tree/fork.rs +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -526,7 +526,7 @@ where LeafDigest<C>: Clone + Default, InnerDigest<C>: Clone, { - // FIXME: Move this algorithm to `crate::merkle_tree::path`. + // TODO: Move this algorithm to `crate::merkle_tree::path`. let length = self.len(); if index > 0 && index >= length { return Err(PathError::IndexTooLarge { length }); diff --git a/manta-crypto/src/merkle_tree/inner_tree.rs b/manta-crypto/src/merkle_tree/inner_tree.rs index 5a7a35ef4..cb2c20d0c 100644 --- a/manta-crypto/src/merkle_tree/inner_tree.rs +++ b/manta-crypto/src/merkle_tree/inner_tree.rs @@ -668,17 +668,12 @@ where M: Default, S: Default, { - // TODO: Remove duplicated method calls. - let mut inner_tree = InnerTree::<C, M, S>::default(); - let leaf_index = path.leaf_index.as_left(); let node_iter = path.into_nodes(); - if node_iter.len() == 0 { return inner_tree.into(); } - let root = node_iter.fold(base, |acc, (node, digest)| { let index = node.map_index(); match digest { @@ -693,9 +688,7 @@ where ), } }); - inner_tree.set_root(root); - Self::new(inner_tree, leaf_index) } diff --git a/manta-crypto/src/merkle_tree/mod.rs b/manta-crypto/src/merkle_tree/mod.rs index 5cb2f8c42..32f52df35 100644 --- a/manta-crypto/src/merkle_tree/mod.rs +++ b/manta-crypto/src/merkle_tree/mod.rs @@ -16,11 +16,11 @@ //! Merkle Trees and Forests +// FIXME: Get rid of as many `pub(super)` declarations as we can. // TODO: Should `Leaf` move into `Tree`/`Configuration` since we might want the tree to have // special kinds of leaf input (metadata along with just the digest)? // TODO: Maybe we should require `INNER_HEIGHT` instead of `HEIGHT` so that we don't have to rely // on the user to check that `HEIGHT >= 2`. -// FIXME: Get rid of as many `pub(super)` declarations as we can. // TODO: Extend to arbitrary arity. mod node; diff --git a/manta-crypto/src/merkle_tree/path.rs b/manta-crypto/src/merkle_tree/path.rs index 4890f83c3..d20d9d4af 100644 --- a/manta-crypto/src/merkle_tree/path.rs +++ b/manta-crypto/src/merkle_tree/path.rs @@ -267,9 +267,8 @@ where /// Inner Digest Path /// - /// Inner digests are stored from leaf to root, not including the root. - /// - /// For [`CurrentInnerPath`], only non-default inner digests are stored in the `path`. + /// Inner digests are stored from leaf to root, not including the root. For + /// [`CurrentInnerPath`], only non-default inner digests are stored in the `path`. pub path: Vec<InnerDigest<C>>, } @@ -451,11 +450,9 @@ where accumulator = parameters.join(&accumulator, &default_inner_digest); depth += 1; } - mem::drop(self.path.drain(0..i)); self.path.insert(0, last_accumulator); accumulator = parameters.join(&self.path[0], &accumulator); - Self::fold( parameters, depth + 1, @@ -507,7 +504,6 @@ where InnerDigest<C>: Default, { type Item = InnerDigest<C>; - type IntoIter = CurrentInnerPathIntoIter<C>; #[inline] diff --git a/manta-crypto/src/merkle_tree/single_path.rs b/manta-crypto/src/merkle_tree/single_path.rs index 9deaaf5b0..a971998b0 100644 --- a/manta-crypto/src/merkle_tree/single_path.rs +++ b/manta-crypto/src/merkle_tree/single_path.rs @@ -177,7 +177,7 @@ where } } -/// Raw Merkle Tree +/// Raw Merkle Tree Interfaces pub mod raw { use super::*; diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index 573375f25..df017c62a 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -967,7 +967,6 @@ where InnerDigest<C>: Clone + PartialEq, { type Item = Leaf<C>; - type Model = Parameters<C>; #[inline] diff --git a/manta-crypto/src/rand.rs b/manta-crypto/src/rand.rs index fb0300de2..1d989e10a 100644 --- a/manta-crypto/src/rand.rs +++ b/manta-crypto/src/rand.rs @@ -65,7 +65,6 @@ where R: block::BlockRngCore + ?Sized, { type Item = R::Item; - type Results = R::Results; #[inline] @@ -138,7 +137,6 @@ where R: block::BlockRngCore, { type Item = R::Item; - type Results = R::Results; #[inline] diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 7857210f3..fd4b6aad2 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -67,7 +67,7 @@ ark-groth16 = { version = "0.3.0", optional = true, default-features = false } ark-r1cs-std = { version = "0.3.1", optional = true, default-features = false } ark-relations = { version = "0.3.0", optional = true, default-features = false } ark-serialize = { version = "0.3.0", optional = true, default-features = false } -async-std = { version = "1.10.0" } +async-std = { version = "1.10.0", features = ["unstable"] } bip32 = { version = "0.3.0", default-features = false, features = ["bip39", "secp256k1"] } blake2 = { version = "0.10.0", default-features = false } derivative = { version = "2.2.0", default-features = false } diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 7f7b30b9a..95e777281 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -414,7 +414,6 @@ impl merkle_tree::InnerHash<Compiler> for InnerHashVar { } /// Merkle Tree Configuration -#[derive(Clone)] pub struct MerkleTreeConfiguration; impl merkle_tree::HashConfiguration for MerkleTreeConfiguration { @@ -530,29 +529,23 @@ impl transfer::Configuration for Config { type PublicKeyVar = <Self::KeyAgreementSchemeVar as key::KeyAgreementScheme<Self::Compiler>>::PublicKey; type KeyAgreementSchemeVar = KeyAgreementSchemeVar; - type Utxo = <Self::UtxoCommitmentScheme as CommitmentScheme>::Output; type UtxoCommitmentScheme = UtxoCommitmentScheme; type UtxoVar = <Self::UtxoCommitmentSchemeVar as CommitmentScheme<Self::Compiler>>::Output; type UtxoCommitmentSchemeVar = UtxoCommitmentSchemeVar; - type VoidNumber = <Self::VoidNumberHashFunction as BinaryHashFunction>::Output; type VoidNumberHashFunction = VoidNumberHashFunction; type VoidNumberVar = <Self::VoidNumberHashFunctionVar as BinaryHashFunction<Self::Compiler>>::Output; type VoidNumberHashFunctionVar = VoidNumberHashFunctionVar; - type UtxoSetModel = merkle_tree::Parameters<MerkleTreeConfiguration>; type UtxoSetWitnessVar = <Self::UtxoSetModelVar as accumulator::Model<Self::Compiler>>::Witness; type UtxoSetOutputVar = <Self::UtxoSetModelVar as accumulator::Model<Self::Compiler>>::Output; type UtxoSetModelVar = merkle_tree::Parameters<MerkleTreeConfigurationVar, Compiler>; - type AssetIdVar = AssetIdVar; type AssetValueVar = AssetValueVar; - type Compiler = Compiler; type ProofSystem = ProofSystem; - type NoteEncryptionScheme = NoteEncryptionScheme; } diff --git a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs index 8e060843e..fa2b01321 100644 --- a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs @@ -58,18 +58,17 @@ pub type SynthesisResult<T = ()> = Result<T, SynthesisError>; /// # Warning /// /// This does not work for all variable assignments! For some assignemnts, the variable inherits -/// some structure from its input even though the input itself will not form part of the proving -/// key and verifying key that we produce after compiling the constraint system. For those cases, -/// some mocking is required and this function can not be used directly. +/// some structure from its input, like its length or number of bits, which are only known at +/// run-time. For those cases, some mocking is required and this function can not be used directly. #[inline] pub fn empty<T>() -> SynthesisResult<T> { Err(SynthesisError::AssignmentMissing) } -/// Returns a filled variable assignment. +/// Returns a filled variable assignment with the given `value`. #[inline] -pub fn full<T>(t: T) -> impl FnOnce() -> SynthesisResult<T> { - move || Ok(t) +pub fn full<T>(value: T) -> impl FnOnce() -> SynthesisResult<T> { + move || Ok(value) } /// Arkworks Rank-1 Constraint System @@ -114,7 +113,7 @@ where #[inline] fn assert(&mut self, b: Self::Bool) { b.enforce_equal(&Boolean::TRUE) - .expect("This should never fail."); + .expect("Enforcing equality is not allowed to fail."); } } diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs index 2a330e006..253df7766 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs @@ -71,19 +71,12 @@ where E: ark_ec::PairingEngine, { type ConstraintSystem = R1CS<E::Fr>; - type PublicParameters = (); - type ProvingContext = ProvingKey<E>; - type VerifyingContext = PreparedVerifyingKey<E>; - type Input = Vec<E::Fr>; - type Proof = Proof<E>; - type Verification = bool; - type Error = SynthesisError; #[inline] diff --git a/manta-pay/src/crypto/ecc.rs b/manta-pay/src/crypto/ecc.rs index a66a04a30..e0531fffd 100644 --- a/manta-pay/src/crypto/ecc.rs +++ b/manta-pay/src/crypto/ecc.rs @@ -107,7 +107,7 @@ pub mod arkworks { let mut buffer = Vec::new(); self.0 .serialize_unchecked(&mut buffer) - .expect("Serialization does not fail."); + .expect("Serialization is not allowed to fail."); buffer } } @@ -274,7 +274,7 @@ pub mod arkworks { let _ = compiler; lhs.0 .is_eq(&rhs.0) - .expect("Equality check is not allowed to fail.") + .expect("Equality checking is not allowed to fail.") } } diff --git a/manta-pay/src/crypto/hash/poseidon.rs b/manta-pay/src/crypto/hash/poseidon.rs index eaac6651e..e9ed8b2aa 100644 --- a/manta-pay/src/crypto/hash/poseidon.rs +++ b/manta-pay/src/crypto/hash/poseidon.rs @@ -98,6 +98,15 @@ where /// Width of the State Buffer pub const WIDTH: usize = ARITY + 1; + /// Total Number of Rounds + pub const ROUNDS: usize = 2 * S::FULL_ROUNDS + S::PARTIAL_ROUNDS; + + /// Number of Entries in the MDS Matrix + pub const MDS_MATRIX_SIZE: usize = Self::WIDTH * Self::WIDTH; + + /// Total Number of Additive Rounds Keys + pub const ADDITIVE_ROUND_KEYS_COUNT: usize = Self::ROUNDS * Self::WIDTH; + /// Builds a new [`Hash`](self::Hash) from `additive_round_keys` and `mds_matrix`. /// /// # Panics @@ -108,12 +117,12 @@ where pub fn new(additive_round_keys: Vec<S::Field>, mds_matrix: Vec<S::Field>) -> Self { assert_eq!( additive_round_keys.len(), - Self::additive_round_keys_len(), + Self::ADDITIVE_ROUND_KEYS_COUNT, "Additive Rounds Keys are not the correct size." ); assert_eq!( mds_matrix.len(), - Self::mds_matrix_size(), + Self::MDS_MATRIX_SIZE, "MDS Matrix is not the correct size." ); Self::new_unchecked(additive_round_keys, mds_matrix) @@ -129,26 +138,6 @@ where } } - /// Returns the total number of rounds for this Poseidon hash function specification. - #[inline] - pub fn rounds() -> usize { - rounds::<S, COM>() - } - - /// Returns the number of additive round keys. - #[inline] - pub fn additive_round_keys_len() -> usize { - Self::rounds() * Self::WIDTH - } - - /// Returns the size of the MDS matrix. - /// - /// This is the square of the [`WIDTH`](Self::WIDTH). - #[inline] - pub fn mds_matrix_size() -> usize { - Self::WIDTH.pow(2) - } - /// Returns the additive keys for the given `round`. #[inline] fn additive_keys(&self, round: usize) -> &[S::Field] { @@ -220,7 +209,6 @@ where S: Specification<COM>, { type Input = S::Field; - type Output = S::Field; #[inline] @@ -241,7 +229,7 @@ where } } -#[cfg(test)] // NOTE: This is only safe in a test. +#[cfg(test)] // NOTE: This is only safe to use in a test. impl<D, S, const ARITY: usize, COM> Sample<D> for Hash<S, ARITY, COM> where D: Clone, @@ -261,10 +249,10 @@ where { Self { additive_round_keys: rng - .sample_iter(repeat(distribution.clone()).take(Self::additive_round_keys_len())) + .sample_iter(repeat(distribution.clone()).take(Self::ADDITIVE_ROUND_KEYS_COUNT)) .collect(), mds_matrix: rng - .sample_iter(repeat(distribution).take(Self::mds_matrix_size())) + .sample_iter(repeat(distribution).take(Self::MDS_MATRIX_SIZE)) .collect(), } } @@ -315,7 +303,6 @@ pub mod arkworks { type Field = Fp<S::Field>; const FULL_ROUNDS: usize = S::FULL_ROUNDS; - const PARTIAL_ROUNDS: usize = S::PARTIAL_ROUNDS; #[inline] @@ -351,7 +338,6 @@ pub mod arkworks { type Field = FpVar<S::Field>; const FULL_ROUNDS: usize = S::FULL_ROUNDS; - const PARTIAL_ROUNDS: usize = S::PARTIAL_ROUNDS; #[inline] diff --git a/manta-pay/src/crypto/key.rs b/manta-pay/src/crypto/key.rs index bc4b817db..063d340a5 100644 --- a/manta-pay/src/crypto/key.rs +++ b/manta-pay/src/crypto/key.rs @@ -21,13 +21,11 @@ use manta_crypto::key::KeyDerivationFunction; use manta_util::into_array_unchecked; /// Blake2s KDF -#[derive(derivative::Derivative)] -#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Blake2sKdf; impl KeyDerivationFunction for Blake2sKdf { type Key = [u8]; - type Output = [u8; 32]; #[inline] diff --git a/manta-pay/src/key.rs b/manta-pay/src/key.rs index 2c24e92a9..99ec78c2d 100644 --- a/manta-pay/src/key.rs +++ b/manta-pay/src/key.rs @@ -55,48 +55,75 @@ pub trait CoinType: sealed::Sealed { const COIN_TYPE_ID: CoinTypeId; } -/// Implements the [`CoinType`] trait for `$name` with coin type id given by `$id`. +/// Implements the [`CoinType`] trait for `$coin` with coin type id given by `$id`. macro_rules! impl_coin_type { - ($name:ty, $id:ident) => { - seal!($name); - impl CoinType for $name { - const COIN_TYPE_ID: CoinTypeId = $id; + ( + $coin:ident, + $doc:expr, + $name:expr, + $id:expr, + $coin_type_id:ident, + $key_secret:ident, + $account_table:ident + ) => { + #[doc = $doc] + #[doc = "Network"] + #[doc = $name] + #[doc = "Coin Type"] + pub struct $coin; + + #[doc = stringify!($coin)] + #[doc = "Coin Type Id"] + pub const $coin_type_id: CoinTypeId = $id; + + #[doc = stringify!($coin)] + #[doc = "[`KeySecret`] Type"] + pub type $key_secret = KeySecret<$coin>; + + #[doc = stringify!($coin)] + #[doc = "[`AccountTable`] Type"] + pub type $account_table = AccountTable<$coin>; + + seal!($coin); + + impl CoinType for $coin { + const COIN_TYPE_ID: CoinTypeId = $coin_type_id; } }; } -/// Test Network `testnet` Coin Type -pub struct Testnet; +impl_coin_type!( + Testnet, + "Test", + "`testnet`", + 1, + TESTNET_COIN_TYPE_ID, + TestnetKeySecret, + TestnetAccountTable +); + +impl_coin_type!( + Manta, + "Main", + "`manta`", + 611, + MANTA_COIN_TYPE_ID, + MantaKeySecret, + MantaAccountTable +); + +impl_coin_type!( + Calamari, + "Canary", + "`calamari`", + 612, + CALAMARI_COIN_TYPE_ID, + CalamariKeySecret, + CalamariAccountTable +); -/// Test Network `testnet` Coin Type Id -pub const TESTNET_COIN_TYPE_ID: CoinTypeId = 1; - -impl_coin_type!(Testnet, TESTNET_COIN_TYPE_ID); - -/// Main Network `manta` Coin Type -pub struct Manta; - -/// Main Network `manta` Coin Type Id -pub const MANTA_COIN_TYPE_ID: CoinTypeId = 611; - -impl_coin_type!(Manta, MANTA_COIN_TYPE_ID); - -/// Canary Network `calamari` Coin Type -pub struct Calamari; - -/// Canary Network `calamari` Coin Type Id -pub const CALAMARI_COIN_TYPE_ID: CoinTypeId = 612; - -impl_coin_type!(Calamari, CALAMARI_COIN_TYPE_ID); - -/// Testnet [`KeySecret`] Type -pub type TestnetKeySecret = KeySecret<Testnet>; - -/// Manta [`KeySecret`] Type -pub type MantaKeySecret = KeySecret<Manta>; - -/// Calamari [`KeySecret`] Type -pub type CalamariKeySecret = KeySecret<Calamari>; +/// Account Table Type +pub type AccountTable<C> = key::AccountTable<KeySecret<C>>; /// Key Secret pub struct KeySecret<C> @@ -184,15 +211,3 @@ where XPrv::derive_from_path(&self.seed, &path_string::<C>(account, spend, view).parse()?) } } - -/// Account Table Type -pub type AccountTable<C> = key::AccountTable<KeySecret<C>>; - -/// Testnet [`AccountTable`] Type -pub type TestnetAccountTable = AccountTable<Testnet>; - -/// Manta [`AccountTable`] Type -pub type MantaAccountTable = AccountTable<Manta>; - -/// Calamari [`AccountTable`] Type -pub type CalamariAccountTable = AccountTable<Calamari>; diff --git a/manta-pay/src/test/ledger.rs b/manta-pay/src/test/ledger.rs index ceb1727d5..f3009f3a3 100644 --- a/manta-pay/src/test/ledger.rs +++ b/manta-pay/src/test/ledger.rs @@ -446,10 +446,7 @@ impl ledger::Connection<Config> for LedgerConnection { Ok(posting_key) => { posting_key.post(&(), &mut *ledger); } - Err(err) => { - async_std::println!("\n[INFO] Ledger Validation Error: {:?}\n", err).await; - return Ok(PushResponse { success: false }); - } + _ => return Ok(PushResponse { success: false }), } } Ok(PushResponse { success: true }) diff --git a/manta-pay/src/test/merkle_tree.rs b/manta-pay/src/test/merkle_tree.rs deleted file mode 100644 index a5a2530b2..000000000 --- a/manta-pay/src/test/merkle_tree.rs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Manta Pay Merkle Tree Testing - -use crate::config::MerkleTreeConfiguration; -use core::iter::repeat; -use manta_crypto::{ - accumulator, - accumulator::Accumulator, - merkle_tree::{self, forest, fork, test, MerkleTree}, - rand::{Rand, Standard}, -}; -use rand::thread_rng; - -/* -/// Base Tree -pub type Base = merkle_tree::full::Full<MerkleTreeConfiguration>; - -/// Forked Base Tree -pub type ForkedBase = fork::ForkedTree<MerkleTreeConfiguration, Base>; - -/// Tree Wrapper for Base -pub type Tree = MerkleTree<MerkleTreeConfiguration, Base>; - -/// Tree Wrapper for Forked Base -pub type ForkedTree = MerkleTree<MerkleTreeConfiguration, ForkedBase>; - -/// Forest Wrapper for Base -pub type Forest = forest::TreeArrayMerkleForest<MerkleTreeConfiguration, Base, 256>; - -/// Forest Wrapper for Forked Base -pub type ForkedForest = forest::TreeArrayMerkleForest<MerkleTreeConfiguration, ForkedBase, 256>; -*/ diff --git a/manta-pay/src/test/mod.rs b/manta-pay/src/test/mod.rs index 453b5896f..c8bf2fcb3 100644 --- a/manta-pay/src/test/mod.rs +++ b/manta-pay/src/test/mod.rs @@ -18,12 +18,12 @@ pub mod ledger; -#[cfg(feature = "simulation")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "simulation")))] -pub mod simulation; - -// TODO: #[cfg(test)] -// pub mod merkle_tree; +// TODO: This is the old simulation. We need to integrate its features into the new asynchronous +// simulation. +// +// #[cfg(feature = "simulation")] +// #[cfg_attr(doc_cfg, doc(cfg(feature = "simulation")))] +// pub mod simulation; #[cfg(test)] pub mod transfer; diff --git a/manta-util/src/array.rs b/manta-util/src/array.rs index cecc1ac79..485a8453c 100644 --- a/manta-util/src/array.rs +++ b/manta-util/src/array.rs @@ -56,7 +56,7 @@ pub fn array_map<T, U, F, const N: usize>(array: [T; N], f: F) -> [U; N] where F: FnMut(T) -> U, { - into_array_unchecked(IntoIterator::into_iter(array).map(f).collect::<Vec<_>>()) + into_array_unchecked(array.into_iter().map(f).collect::<Vec<_>>()) } /// Maps `f` over the `array` by reference. @@ -76,9 +76,7 @@ where F: FnMut(T) -> Result<U, E>, { Ok(into_array_unchecked( - IntoIterator::into_iter(array) - .map(f) - .collect::<Result<Vec<_>, _>>()?, + array.into_iter().map(f).collect::<Result<Vec<_>, _>>()?, )) } diff --git a/manta-util/src/iter/chunk_by.rs b/manta-util/src/iter/chunk_by.rs index b0fc8babc..a6ee9ccd6 100644 --- a/manta-util/src/iter/chunk_by.rs +++ b/manta-util/src/iter/chunk_by.rs @@ -90,6 +90,12 @@ where } Some(into_array_unchecked(vec)) } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + let (min, max) = self.iter.size_hint(); + (min / N, max.map(|m| m / N)) + } } impl<I, const N: usize> FusedIterator for ChunkBy<I, N> where I: Iterator {} diff --git a/manta-util/src/iter/mod.rs b/manta-util/src/iter/mod.rs index f420b74f7..a9797d966 100644 --- a/manta-util/src/iter/mod.rs +++ b/manta-util/src/iter/mod.rs @@ -41,7 +41,7 @@ pub trait IteratorExt: Iterator { /// Folds every element into an accumulator by applying an operation, returning the final result. /// /// This function differs from [`Iterator::fold`] because its initial state is borrowed instead - /// of moved. This means that we have to return `Option<B>` in case the iterator is empty. + /// of owned. This means that we have to return `Option<B>` in case the iterator is empty. #[inline] fn fold_ref<B, F>(mut self, init: &B, mut f: F) -> Option<B> where diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index d758064f9..5a5c330de 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -31,7 +31,6 @@ mod serde; pub mod cache; pub mod future; pub mod iter; -pub mod num; pub mod pointer; pub use array::*; diff --git a/manta-util/src/num.rs b/manta-util/src/num.rs deleted file mode 100644 index 22bb88bdb..000000000 --- a/manta-util/src/num.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Numeric Utilities - -use core::num::Wrapping; - -/// Parity Trait -pub trait HasParity { - /// Returns `true` if `self` represents an even integer. - fn is_even(&self) -> bool; - - /// Returns `true` if `self` does not represent an even integer. - #[inline] - fn is_odd(&self) -> bool { - !self.is_even() - } -} - -macro_rules! impl_has_parity { - ($($type:tt),+) => { - $( - impl HasParity for $type { - #[inline] - fn is_even(&self) -> bool { - self % 2 == 0 - } - } - - impl HasParity for Wrapping<$type> { - #[inline] - fn is_even(&self) -> bool { - self.0 % 2 == 0 - } - } - )* - }; -} - -impl_has_parity!(i8, i16, i32, i64, i128, isize, u8, u16, u64, u128, usize); diff --git a/manta-util/src/pointer.rs b/manta-util/src/pointer.rs index e7b3d62a2..4c6ec131d 100644 --- a/manta-util/src/pointer.rs +++ b/manta-util/src/pointer.rs @@ -63,7 +63,6 @@ macro_rules! impl_pointer_family { seal!($type); impl<T> PointerFamily<T> for $type { type Strong = $strong<T>; - type Weak = $weak<T>; #[inline] diff --git a/manta-util/src/sealed.rs b/manta-util/src/sealed.rs index 6b9cf7a90..6eede2b2e 100644 --- a/manta-util/src/sealed.rs +++ b/manta-util/src/sealed.rs @@ -28,7 +28,7 @@ macro_rules! create_seal { }; } -/// Adds an `sealed::Sealed` implementation to `$type`. +/// Adds a `sealed::Sealed` implementation to `$type`. #[macro_export] macro_rules! seal { ($($type:tt),+ $(,)?) => { From e8a303d98ab743f132f8bf8b3e9d601488c14868 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 18 Jan 2022 13:08:05 -0500 Subject: [PATCH 192/275] fix: make codebase rust-stable compatible --- manta-crypto/src/hash.rs | 2 +- manta-crypto/src/merkle_tree/test.rs | 4 ++-- manta-crypto/src/merkle_tree/tree.rs | 6 +++--- manta-pay/src/config.rs | 8 ++++---- manta-pay/src/crypto/hash/poseidon.rs | 20 ++++++++++---------- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/manta-crypto/src/hash.rs b/manta-crypto/src/hash.rs index e7223d0ff..b425c5078 100644 --- a/manta-crypto/src/hash.rs +++ b/manta-crypto/src/hash.rs @@ -19,7 +19,7 @@ use crate::constraint::Native; /// Hash Function -pub trait HashFunction<const ARITY: usize = 1, COM = ()> { +pub trait HashFunction<COM, const ARITY: usize> { /// Input Type type Input: ?Sized; diff --git a/manta-crypto/src/merkle_tree/test.rs b/manta-crypto/src/merkle_tree/test.rs index d01dc5d04..bb302defa 100644 --- a/manta-crypto/src/merkle_tree/test.rs +++ b/manta-crypto/src/merkle_tree/test.rs @@ -173,7 +173,7 @@ where /// This is only meant for testing purposes, and should not be used in any production or /// cryptographically secure environments. pub trait TestHash { - /// Joins `lhs` and `rhs` into a third hash value. + /// Joins `lhs` and `rhs` into an output hash value. fn join(lhs: &Self, rhs: &Self) -> Self; } @@ -200,7 +200,7 @@ impl TestHash for String { /// This is only meant for testing purposes, and should not be used in production or /// cryptographically secure environments. #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Test<T = u64, const HEIGHT: usize = 20>(PhantomData<T>) +pub struct Test<T, const HEIGHT: usize>(PhantomData<T>) where T: Clone + Default + PartialEq + TestHash; diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index df017c62a..bf88e3221 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -179,11 +179,11 @@ pub trait Configuration<COM = ()>: HashConfiguration<COM> { /// Since this `struct` is meant to be used as a type parameter, any values of this type have no /// meaning, just like values of type [`HashConfiguration`] or [`Configuration`]. #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Config<C, const HEIGHT: usize, COM = ()>(PhantomData<(COM, C)>) +pub struct Config<C, COM, const HEIGHT: usize>(PhantomData<(COM, C)>) where C: HashConfiguration<COM> + ?Sized; -impl<C, const HEIGHT: usize, COM> HashConfiguration<COM> for Config<C, HEIGHT, COM> +impl<C, COM, const HEIGHT: usize> HashConfiguration<COM> for Config<C, COM, HEIGHT> where C: HashConfiguration<COM> + ?Sized, { @@ -191,7 +191,7 @@ where type InnerHash = C::InnerHash; } -impl<C, const HEIGHT: usize, COM> Configuration<COM> for Config<C, HEIGHT, COM> +impl<C, COM, const HEIGHT: usize> Configuration<COM> for Config<C, COM, HEIGHT> where C: HashConfiguration<COM> + ?Sized, { diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 95e777281..e8b60bf17 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -88,10 +88,10 @@ pub type ProofSystem = Groth16<PairingCurve>; pub struct PoseidonSpec<const ARITY: usize>; /// Poseidon-2 Hash Parameters -pub type Poseidon2 = poseidon::Hash<PoseidonSpec<2>, 2>; +pub type Poseidon2 = poseidon::Hash<PoseidonSpec<2>, (), 2>; /// Poseidon-2 Hash Parameters Variable -pub type Poseidon2Var = poseidon::Hash<PoseidonSpec<2>, 2, Compiler>; +pub type Poseidon2Var = poseidon::Hash<PoseidonSpec<2>, Compiler, 2>; impl poseidon::arkworks::Specification for PoseidonSpec<2> { type Field = ConstraintField; @@ -101,10 +101,10 @@ impl poseidon::arkworks::Specification for PoseidonSpec<2> { } /// Poseidon-4 Hash Parameters -pub type Poseidon4 = poseidon::Hash<PoseidonSpec<4>, 4>; +pub type Poseidon4 = poseidon::Hash<PoseidonSpec<4>, (), 4>; /// Poseidon-4 Hash Parameters Variable -pub type Poseidon4Var = poseidon::Hash<PoseidonSpec<4>, 4, Compiler>; +pub type Poseidon4Var = poseidon::Hash<PoseidonSpec<4>, Compiler, 4>; impl poseidon::arkworks::Specification for PoseidonSpec<4> { type Field = ConstraintField; diff --git a/manta-pay/src/crypto/hash/poseidon.rs b/manta-pay/src/crypto/hash/poseidon.rs index e9ed8b2aa..523bc5fa2 100644 --- a/manta-pay/src/crypto/hash/poseidon.rs +++ b/manta-pay/src/crypto/hash/poseidon.rs @@ -80,7 +80,7 @@ where Hash(bound = "S::Field: core::hash::Hash"), PartialEq(bound = "S::Field: PartialEq") )] -pub struct Hash<S, const ARITY: usize = 1, COM = ()> +pub struct Hash<S, COM, const ARITY: usize> where S: Specification<COM>, { @@ -91,7 +91,7 @@ where mds_matrix: Vec<S::Field>, } -impl<S, const ARITY: usize, COM> Hash<S, ARITY, COM> +impl<S, COM, const ARITY: usize> Hash<S, COM, ARITY> where S: Specification<COM>, { @@ -204,7 +204,7 @@ where } } -impl<S, const ARITY: usize, COM> HashFunction<ARITY, COM> for Hash<S, ARITY, COM> +impl<S, COM, const ARITY: usize> HashFunction<COM, ARITY> for Hash<S, COM, ARITY> where S: Specification<COM>, { @@ -230,7 +230,7 @@ where } #[cfg(test)] // NOTE: This is only safe to use in a test. -impl<D, S, const ARITY: usize, COM> Sample<D> for Hash<S, ARITY, COM> +impl<D, S, COM, const ARITY: usize> Sample<D> for Hash<S, COM, ARITY> where D: Clone, S: Specification<COM>, @@ -259,12 +259,12 @@ where } /// Poseidon Hash Input Type -pub type Input<S, const ARITY: usize, COM = ()> = - <Hash<S, ARITY, COM> as HashFunction<ARITY, COM>>::Input; +pub type Input<S, COM, const ARITY: usize> = + <Hash<S, COM, ARITY> as HashFunction<COM, ARITY>>::Input; /// Poseidon Commitment Output Type -pub type Output<S, const ARITY: usize, COM = ()> = - <Hash<S, ARITY, COM> as HashFunction<ARITY, COM>>::Output; +pub type Output<S, COM, const ARITY: usize> = + <Hash<S, COM, ARITY> as HashFunction<COM, ARITY>>::Output; /// Arkworks Backend #[cfg(feature = "arkworks")] @@ -373,11 +373,11 @@ pub mod arkworks { } } - impl<S, const ARITY: usize> Constant<Compiler<S>> for super::Hash<S, ARITY, Compiler<S>> + impl<S, const ARITY: usize> Constant<Compiler<S>> for super::Hash<S, Compiler<S>, ARITY> where S: Specification, { - type Type = super::Hash<S, ARITY>; + type Type = super::Hash<S, (), ARITY>; #[inline] fn new_constant(this: &Self::Type, compiler: &mut Compiler<S>) -> Self { From ba92a62d459558b6ef6c1e793dffc664474b2fee Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 18 Jan 2022 15:53:15 -0500 Subject: [PATCH 193/275] feat: add scale encode/decode to arkworks primitive types --- manta-pay/Cargo.toml | 4 +- manta-pay/src/config.rs | 21 +- .../constraint/arkworks/constraint_system.rs | 64 ++++++ .../constraint/arkworks/proof_system.rs | 197 +++++++++++------- manta-pay/src/crypto/ecc.rs | 40 +++- 5 files changed, 247 insertions(+), 79 deletions(-) diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index fd4b6aad2..9ac5389a3 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -39,6 +39,7 @@ arkworks = [ "ark-r1cs-std", "ark-relations", "ark-serialize", + "ark-std", ] # Enable Groth16 ZKP System @@ -67,6 +68,7 @@ ark-groth16 = { version = "0.3.0", optional = true, default-features = false } ark-r1cs-std = { version = "0.3.1", optional = true, default-features = false } ark-relations = { version = "0.3.0", optional = true, default-features = false } ark-serialize = { version = "0.3.0", optional = true, default-features = false } +ark-std = { version = "0.3.0", optional = true, default-features = false } async-std = { version = "1.10.0", features = ["unstable"] } bip32 = { version = "0.3.0", default-features = false, features = ["bip39", "secp256k1"] } blake2 = { version = "0.10.0", default-features = false } @@ -78,7 +80,7 @@ manta-crypto = { path = "../manta-crypto" } manta-util = { path = "../manta-util" } rand = { version = "0.8.4", optional = true } rand_chacha = { version = "0.3.1", default-features = false } -scale-codec = { package = "parity-scale-codec", version = "2.3.1", optional = true, default-features = false, features = ["derive"] } +scale-codec = { package = "parity-scale-codec", version = "2.3.1", default-features = false, features = ["derive"] } serde = { version = "1.0.133", optional = true, default-features = false, features = ["derive"] } statrs = { version = "0.15.0", optional = true, default-features = false } zk-garage-plonk = { package = "plonk", git = "https://github.com/zk-garage/plonk", optional = true, default-features = false } diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index e8b60bf17..5d1aea213 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -18,7 +18,7 @@ use crate::{ crypto::{ - constraint::arkworks::{Boolean, Fp, FpVar, Groth16, R1CS}, + constraint::arkworks::{groth16, Boolean, Fp, FpVar, R1CS}, ecc::{self, arkworks::ProjectiveCurve}, encryption::aes::{self, AesGcm}, hash::poseidon, @@ -26,6 +26,7 @@ use crate::{ }, key::TestnetKeySecret, }; +use alloc::vec::Vec; use ark_ff::{PrimeField, ToConstraintField}; use ark_serialize::CanonicalSerialize; use blake2::{ @@ -81,8 +82,11 @@ pub type ConstraintFieldVar = FpVar<ConstraintField>; /// Constraint Compiler pub type Compiler = R1CS<ConstraintField>; +/// Proof System Proof +pub type Proof = groth16::Proof<PairingCurve>; + /// Proof System -pub type ProofSystem = Groth16<PairingCurve>; +pub type ProofSystem = groth16::Groth16<PairingCurve>; /// Poseidon Specification pub struct PoseidonSpec<const ARITY: usize>; @@ -413,6 +417,9 @@ impl merkle_tree::InnerHash<Compiler> for InnerHashVar { } } +/// UTXO Set Output +pub type UtxoSetOutput = merkle_tree::Root<MerkleTreeConfiguration>; + /// Merkle Tree Configuration pub struct MerkleTreeConfiguration; @@ -517,6 +524,10 @@ pub type NoteEncryptionScheme = encryption::Hybrid< >, >; +/// Asset Ciphertext +pub type Ciphertext = + <NoteEncryptionScheme as encryption::SymmetricKeyEncryptionScheme>::Ciphertext; + /// Base Configuration pub struct Config; @@ -567,6 +578,12 @@ pub type PrivateTransfer = transfer::canonical::PrivateTransfer<Config>; /// Reclaim Transfer Type pub type Reclaim = transfer::canonical::Reclaim<Config>; +/// Sender Post Type +pub type SenderPost = transfer::SenderPost<Config>; + +/// Receiver Post Type +pub type ReceiverPost = transfer::ReceiverPost<Config>; + /// Transfer Post Type pub type TransferPost = transfer::TransferPost<Config>; diff --git a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs index fa2b01321..8d360d041 100644 --- a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs @@ -16,6 +16,7 @@ //! Arkworks Constraint System Implementation +use alloc::vec::Vec; use ark_ff::PrimeField; use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget, select::CondSelectGadget}; use ark_relations::{ns, r1cs as ark_r1cs}; @@ -26,6 +27,7 @@ use manta_crypto::{ }, rand::{CryptoRng, RngCore, Sample, Standard}, }; +use scale_codec::{Decode, Encode, EncodeLike}; pub use ark_r1cs::SynthesisError; pub use ark_r1cs_std::{bits::boolean::Boolean, fields::fp::FpVar}; @@ -36,6 +38,40 @@ pub struct Fp<F>(pub F) where F: PrimeField; +impl<F> Decode for Fp<F> +where + F: PrimeField, +{ + #[inline] + fn decode<I>(input: &mut I) -> Result<Self, scale_codec::Error> + where + I: scale_codec::Input, + { + Ok(Self( + F::deserialize(codec::ScaleCodecReader(input)).map_err(|_| "Deserialization Error")?, + )) + } +} + +impl<F> Encode for Fp<F> +where + F: PrimeField, +{ + #[inline] + fn using_encoded<R, Encoder>(&self, f: Encoder) -> R + where + Encoder: FnOnce(&[u8]) -> R, + { + let mut buffer = Vec::new(); + self.0 + .serialize(&mut buffer) + .expect("Encoding is not allowed to fail."); + f(&buffer) + } +} + +impl<F> EncodeLike for Fp<F> where F: PrimeField {} + impl<F> Sample for Fp<F> where F: PrimeField, @@ -290,3 +326,31 @@ where lhs + rhs } } + +/// Codec Utilities +pub mod codec { + use ark_std::io::{Error, ErrorKind, Read}; + use scale_codec::Input; + + /// Scale-Codec Input as Reader Wrapper + #[derive(Debug, Eq, Hash, PartialEq)] + pub struct ScaleCodecReader<'i, I>(pub &'i mut I) + where + I: Input; + + impl<I> Read for ScaleCodecReader<'_, I> + where + I: Input, + { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> { + let len = buf.len(); + self.read_exact(buf).map(|_| len) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Error> { + Input::read(self.0, buf).map_err(|_| ErrorKind::Other.into()) + } + } +} diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs index 253df7766..7b1661295 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs @@ -19,21 +19,11 @@ use crate::crypto::constraint::arkworks::{constraint_system::SynthesisResult, R1CS}; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef}; -#[cfg(feature = "groth16")] -use { - crate::crypto::constraint::arkworks::constraint_system::SynthesisError, - alloc::vec::Vec, - ark_crypto_primitives::SNARK, - ark_groth16::{Groth16 as ArkGroth16, PreparedVerifyingKey, Proof, ProvingKey}, - core::marker::PhantomData, - manta_crypto::{ - constraint::ProofSystem, - rand::{CryptoRng, RngCore, SizedRng}, - }, -}; - /// Constraint Synthesizer Wrapper -struct ConstraintSynthesizerWrapper<F>(R1CS<F>) +/// +/// This wraps an [`R1CS`] constraint system and allows it to be used as a [`ConstraintSynthesizer`] +/// for building proofs using arkworks proof systems. +pub struct ConstraintSynthesizerWrapper<F>(pub R1CS<F>) where F: ark_ff::PrimeField; @@ -56,78 +46,137 @@ where } } -/// Arkworks Groth16 Proof System +/// Groth16 Proving System #[cfg(feature = "groth16")] #[cfg_attr(doc_cfg, doc(cfg(feature = "groth16")))] -#[derive(derivative::Derivative)] -#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Groth16<E>(PhantomData<E>) -where - E: ark_ec::PairingEngine; - -#[cfg(feature = "groth16")] -impl<E> ProofSystem for Groth16<E> -where - E: ark_ec::PairingEngine, -{ - type ConstraintSystem = R1CS<E::Fr>; - type PublicParameters = (); - type ProvingContext = ProvingKey<E>; - type VerifyingContext = PreparedVerifyingKey<E>; - type Input = Vec<E::Fr>; - type Proof = Proof<E>; - type Verification = bool; - type Error = SynthesisError; +pub mod groth16 { + use super::*; + use crate::crypto::constraint::arkworks::{self, constraint_system::SynthesisError}; + use alloc::vec::Vec; + use ark_crypto_primitives::SNARK; + use ark_ec::PairingEngine; + use ark_groth16::{Groth16 as ArkGroth16, PreparedVerifyingKey, ProvingKey}; + use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; + use core::marker::PhantomData; + use manta_crypto::{ + constraint::ProofSystem, + rand::{CryptoRng, RngCore, SizedRng}, + }; + use scale_codec::{Decode, Encode, EncodeLike}; - #[inline] - fn for_unknown() -> Self::ConstraintSystem { - Self::ConstraintSystem::for_unknown() - } + /// Groth16 Proof + #[derive(derivative::Derivative)] + #[derivative(Clone, Debug, Default, Eq, PartialEq)] + pub struct Proof<E>(ark_groth16::Proof<E>) + where + E: PairingEngine; - #[inline] - fn for_known() -> Self::ConstraintSystem { - Self::ConstraintSystem::for_known() + impl<E> Decode for Proof<E> + where + E: PairingEngine, + { + #[inline] + fn decode<I>(input: &mut I) -> Result<Self, scale_codec::Error> + where + I: scale_codec::Input, + { + Ok(Self( + ark_groth16::Proof::deserialize(arkworks::codec::ScaleCodecReader(input)) + .map_err(|_| "Deserialization Error")?, + )) + } } - #[inline] - fn generate_context<R>( - cs: Self::ConstraintSystem, - public_parameters: &Self::PublicParameters, - rng: &mut R, - ) -> Result<(Self::ProvingContext, Self::VerifyingContext), Self::Error> + impl<E> Encode for Proof<E> where - R: CryptoRng + RngCore + ?Sized, + E: PairingEngine, { - let _ = public_parameters; - let (proving_key, verifying_key) = ArkGroth16::circuit_specific_setup( - ConstraintSynthesizerWrapper(cs), - &mut SizedRng(rng), - )?; - Ok((proving_key, ArkGroth16::process_vk(&verifying_key)?)) + #[inline] + fn using_encoded<R, Encoder>(&self, f: Encoder) -> R + where + Encoder: FnOnce(&[u8]) -> R, + { + let mut buffer = Vec::new(); + self.0 + .serialize(&mut buffer) + .expect("Encoding is not allowed to fail."); + f(&buffer) + } } - #[inline] - fn prove<R>( - cs: Self::ConstraintSystem, - context: &Self::ProvingContext, - rng: &mut R, - ) -> Result<Self::Proof, Self::Error> + impl<E> EncodeLike for Proof<E> where E: PairingEngine {} + + /// Arkworks Groth16 Proof System + #[derive(derivative::Derivative)] + #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct Groth16<E>(PhantomData<E>) + where + E: PairingEngine; + + impl<E> ProofSystem for Groth16<E> where - R: CryptoRng + RngCore + ?Sized, + E: PairingEngine, { - ArkGroth16::prove( - context, - ConstraintSynthesizerWrapper(cs), - &mut SizedRng(rng), - ) - } + type ConstraintSystem = R1CS<E::Fr>; + type PublicParameters = (); + type ProvingContext = ProvingKey<E>; + type VerifyingContext = PreparedVerifyingKey<E>; + type Input = Vec<E::Fr>; + type Proof = Proof<E>; + type Verification = bool; + type Error = SynthesisError; - #[inline] - fn verify( - input: &Self::Input, - proof: &Self::Proof, - context: &Self::VerifyingContext, - ) -> Result<Self::Verification, Self::Error> { - ArkGroth16::verify_with_processed_vk(context, input, proof) + #[inline] + fn for_unknown() -> Self::ConstraintSystem { + Self::ConstraintSystem::for_unknown() + } + + #[inline] + fn for_known() -> Self::ConstraintSystem { + Self::ConstraintSystem::for_known() + } + + #[inline] + fn generate_context<R>( + cs: Self::ConstraintSystem, + public_parameters: &Self::PublicParameters, + rng: &mut R, + ) -> Result<(Self::ProvingContext, Self::VerifyingContext), Self::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + let _ = public_parameters; + let (proving_key, verifying_key) = ArkGroth16::circuit_specific_setup( + ConstraintSynthesizerWrapper(cs), + &mut SizedRng(rng), + )?; + Ok((proving_key, ArkGroth16::process_vk(&verifying_key)?)) + } + + #[inline] + fn prove<R>( + cs: Self::ConstraintSystem, + context: &Self::ProvingContext, + rng: &mut R, + ) -> Result<Self::Proof, Self::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + ArkGroth16::prove( + context, + ConstraintSynthesizerWrapper(cs), + &mut SizedRng(rng), + ) + .map(Proof) + } + + #[inline] + fn verify( + input: &Self::Input, + proof: &Self::Proof, + context: &Self::VerifyingContext, + ) -> Result<Self::Verification, Self::Error> { + ArkGroth16::verify_with_processed_vk(context, input, &proof.0) + } } } diff --git a/manta-pay/src/crypto/ecc.rs b/manta-pay/src/crypto/ecc.rs index e0531fffd..c2a3c1fa2 100644 --- a/manta-pay/src/crypto/ecc.rs +++ b/manta-pay/src/crypto/ecc.rs @@ -20,12 +20,12 @@ #[cfg(feature = "arkworks")] #[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] pub mod arkworks { - use crate::crypto::constraint::arkworks::{empty, full, Boolean, Fp, FpVar, R1CS}; + use crate::crypto::constraint::arkworks::{self, empty, full, Boolean, Fp, FpVar, R1CS}; use alloc::vec::Vec; use ark_ff::{BigInteger, Field, FpParameters, PrimeField}; use ark_r1cs_std::ToBitsGadget; use ark_relations::ns; - use ark_serialize::CanonicalSerialize; + use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use core::marker::PhantomData; use manta_crypto::{ constraint::{Allocator, Constant, Equal, Public, Secret, ValueSource, Variable}, @@ -33,6 +33,7 @@ pub mod arkworks { key::kdf, rand::{CryptoRng, RngCore, Sample, Standard}, }; + use scale_codec::{Decode, Encode, EncodeLike}; pub use ark_ec::{AffineCurve, ProjectiveCurve}; pub use ark_r1cs_std::groups::CurveVar; @@ -98,6 +99,41 @@ pub mod arkworks { where C: ProjectiveCurve; + impl<C> Decode for Group<C> + where + C: ProjectiveCurve, + { + #[inline] + fn decode<I>(input: &mut I) -> Result<Self, scale_codec::Error> + where + I: scale_codec::Input, + { + Ok(Self( + C::Affine::deserialize(arkworks::codec::ScaleCodecReader(input)) + .map_err(|_| "Deserialization Error")?, + )) + } + } + + impl<C> Encode for Group<C> + where + C: ProjectiveCurve, + { + #[inline] + fn using_encoded<R, Encoder>(&self, f: Encoder) -> R + where + Encoder: FnOnce(&[u8]) -> R, + { + let mut buffer = Vec::new(); + self.0 + .serialize(&mut buffer) + .expect("Encoding is not allowed to fail."); + f(&buffer) + } + } + + impl<C> EncodeLike for Group<C> where C: ProjectiveCurve {} + impl<C> kdf::AsBytes for Group<C> where C: ProjectiveCurve, From c976c68499e924badb7083b1608c590b09a7f60a Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 19 Jan 2022 04:11:57 -0500 Subject: [PATCH 194/275] wip: start adding parameter generation for sdk --- manta-accounting/Cargo.toml | 10 +- manta-accounting/src/fs.rs | 8 +- manta-accounting/src/transfer/mod.rs | 78 +++- manta-accounting/src/wallet/signer.rs | 4 +- manta-pay/Cargo.toml | 4 + manta-pay/src/bin/generate_parameters.rs | 102 +++++ manta-pay/src/config.rs | 13 +- .../constraint/arkworks/proof_system.rs | 44 +- manta-pay/src/crypto/hash/poseidon.rs | 4 +- manta-pay/src/wallet/cache.rs | 17 +- manta-util/Cargo.toml | 6 +- manta-util/src/lib.rs | 10 +- manta-util/src/serde.rs | 393 ++++++++++++++++-- 13 files changed, 609 insertions(+), 84 deletions(-) create mode 100644 manta-pay/src/bin/generate_parameters.rs diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 7749e569e..0c8d67afe 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -26,13 +26,10 @@ maintenance = { status = "actively-developed" } [features] # Cocoon Filesystem Adapters -cocoon-fs = [ - "async-std", - "cocoon/std", -] +cocoon-fs = ["async-std", "cocoon/std", "zeroize"] # Standard Library -std = ["cocoon/std", "async-std"] +std = ["async-std", "cocoon/std"] # Testing Frameworks test = ["async-std", "futures", "indexmap", "rand/alloc", "statrs"] @@ -45,10 +42,11 @@ derive_more = { version = "0.99.16", default-features = false, features = ["add" futures = { version = "0.3.19", optional = true, default-features = false, features = ["alloc"] } indexmap = { version = "1.8.0", optional = true, default-features = false } manta-crypto = { path = "../manta-crypto" } -manta-util = { path = "../manta-util", features = ["zeroize"] } +manta-util = { path = "../manta-util" } serde = { version = "1.0.133", optional = true, default-features = false, features = ["derive"] } statrs = { version = "0.15.0", optional = true, default-features = false } rand = { version = "0.8.4", optional = true, default-features = false } +zeroize = { version = "1.4.3", optional = true, default-features = false, features = ["alloc"] } [dev-dependencies] rand = "0.8.4" diff --git a/manta-accounting/src/fs.rs b/manta-accounting/src/fs.rs index a6c9f9c79..e8ba92cea 100644 --- a/manta-accounting/src/fs.rs +++ b/manta-accounting/src/fs.rs @@ -20,7 +20,10 @@ use alloc::{boxed::Box, vec::Vec}; use core::{fmt::Debug, hash::Hash}; -use manta_util::{future::LocalBoxFuture, Deserialize, Serialize}; +use manta_util::{ + future::LocalBoxFuture, + serde::{Deserialize, Serialize}, +}; /// Filesystem Encrypted Saving pub trait SaveEncrypted { @@ -130,7 +133,8 @@ pub mod cocoon { }; use cocoon_crate::{Cocoon, Error as CocoonError}; use core::fmt; - use manta_util::{from_variant_impl, zeroize::Zeroizing}; + use manta_util::from_variant_impl; + use zeroize::Zeroizing; /// Cocoon Loading/Saving Error #[derive(Debug)] diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index cddcb72f9..6ab221577 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -354,30 +354,66 @@ pub type ProofInput<C> = <<C as Configuration>::ProofSystem as ProofSystem>::Inp /// Transfer Validity Proof Type pub type Proof<C> = <ProofSystemType<C> as ProofSystem>::Proof; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + /// Transfer Parameters +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = r" + C::KeyAgreementScheme: Deserialize<'de>, + C::UtxoCommitmentScheme: Deserialize<'de>, + C::VoidNumberHashFunction: Deserialize<'de>, + ", + serialize = r" + C::KeyAgreementScheme: Serialize, + C::UtxoCommitmentScheme: Serialize, + C::VoidNumberHashFunction: Serialize, + ", + ), + deny_unknown_fields, + ) +)] #[derive(derivative::Derivative)] #[derivative( - Clone( - bound = "C::KeyAgreementScheme: Clone, C::UtxoCommitmentScheme: Clone, C::VoidNumberHashFunction: Clone" - ), - Copy( - bound = "C::KeyAgreementScheme: Copy, C::UtxoCommitmentScheme: Copy, C::VoidNumberHashFunction: Copy" - ), - Debug( - bound = "C::KeyAgreementScheme: Debug, C::UtxoCommitmentScheme: Debug, C::VoidNumberHashFunction: Debug" - ), - Default( - bound = "C::KeyAgreementScheme: Default, C::UtxoCommitmentScheme: Default, C::VoidNumberHashFunction: Default" - ), - Eq( - bound = "C::KeyAgreementScheme: Eq, C::UtxoCommitmentScheme: Eq, C::VoidNumberHashFunction: Eq" - ), - Hash( - bound = "C::KeyAgreementScheme: Hash, C::UtxoCommitmentScheme: Hash, C::VoidNumberHashFunction: Hash" - ), - PartialEq( - bound = "C::KeyAgreementScheme: PartialEq, C::UtxoCommitmentScheme: PartialEq, C::VoidNumberHashFunction: PartialEq" - ) + Clone(bound = r" + C::KeyAgreementScheme: Clone, + C::UtxoCommitmentScheme: Clone, + C::VoidNumberHashFunction: Clone + "), + Copy(bound = r" + C::KeyAgreementScheme: Copy, + C::UtxoCommitmentScheme: Copy, + C::VoidNumberHashFunction: Copy + "), + Debug(bound = r" + C::KeyAgreementScheme: Debug, + C::UtxoCommitmentScheme: Debug, + C::VoidNumberHashFunction: Debug + "), + Default(bound = r" + C::KeyAgreementScheme: Default, + C::UtxoCommitmentScheme: Default, + C::VoidNumberHashFunction: Default + "), + Eq(bound = r" + C::KeyAgreementScheme: Eq, + C::UtxoCommitmentScheme: Eq, + C::VoidNumberHashFunction: Eq + "), + Hash(bound = r" + C::KeyAgreementScheme: Hash, + C::UtxoCommitmentScheme: Hash, + C::VoidNumberHashFunction: Hash + "), + PartialEq(bound = r" + C::KeyAgreementScheme: PartialEq, + C::UtxoCommitmentScheme: PartialEq, + C::VoidNumberHashFunction: PartialEq + ") )] pub struct Parameters<C> where diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index e7f7159c7..88dff218e 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -196,12 +196,12 @@ pub type ProvingContextCacheError<C> = /// Signer Error #[derive(derivative::Derivative)] -#[derivative(Debug(bound = r#" +#[derivative(Debug(bound = r" key::Error<C::HierarchicalKeyDerivationScheme>: Debug, ProvingContextCacheError<C>: Debug, ProofSystemError<C>: Debug, CE: Debug -"#))] +"))] pub enum Error<C, CE = Infallible> where C: Configuration, diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 9ac5389a3..b4fe66cd7 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -28,6 +28,10 @@ maintenance = { status = "actively-developed" } name = "main" harness = false +[[bin]] +name = "generate_parameters" +required-features = ["arkworks", "test"] + [features] # Enable Arkworks Backend arkworks = [ diff --git a/manta-pay/src/bin/generate_parameters.rs b/manta-pay/src/bin/generate_parameters.rs new file mode 100644 index 000000000..66097783e --- /dev/null +++ b/manta-pay/src/bin/generate_parameters.rs @@ -0,0 +1,102 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Generate Parameters + +use ark_ff::bytes::ToBytes; +use ark_serialize::{CanonicalSerialize, Write}; +use manta_accounting::transfer::Parameters; +use manta_crypto::{ + constraint::{measure::Measure, ProofSystem as _}, + rand::{Rand, SeedableRng}, +}; +use manta_pay::config::{FullParameters, Mint, PrivateTransfer, ProofSystem, Reclaim}; +use rand_chacha::ChaCha20Rng; +use std::io; + +/// +#[inline] +pub fn main() -> io::Result<()> { + let mut rng = ChaCha20Rng::from_seed([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, + ]); + + let parameters = rng.gen(); + let utxo_set_parameters = rng.gen(); + let full_parameters = FullParameters::new(&parameters, &utxo_set_parameters); + + /* + let mut bytes = Vec::new(); + + let Parameters { + key_agreement, + utxo_commitment, + void_number_hash, + } = &parameters; + + key_agreement.generator().0.serialize(&mut bytes).unwrap(); + utxo_commitment.0.serialize(&mut bytes).unwrap(); + void_number_hash.0.serialize(&mut bytes).unwrap(); + + println!("Parameters: {:?}", bytes.as_slice()); + + let mut bytes = Vec::new(); + utxo_set_parameters.serialize(&mut bytes).unwrap(); + println!("UTXO Set Parameters: {:?}", bytes.as_slice()); + */ + + let cs = Mint::unknown_constraints(full_parameters); + // println!("Mint: {:#?}", cs.measure()); + let mut buffer = Vec::new(); + let (_, verifying_context) = ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); + verifying_context + .0 + .vk + .serialize_unchecked(&mut buffer) + .unwrap(); + verifying_context + .0 + .alpha_g1_beta_g2 + .serialize_unchecked(&mut buffer) + .unwrap(); + verifying_context + .0 + .gamma_g2_neg_pc + .write(&mut buffer) + .unwrap(); + verifying_context + .0 + .delta_g2_neg_pc + .write(&mut buffer) + .unwrap(); + + std::fs::OpenOptions::new() + .create(true) + .write(true) + .open("mint")? + .write_all(&buffer)?; + + // let cs = PrivateTransfer::unknown_constraints(full_parameters); + // println!("PrivateTransfer: {:#?}", cs.measure()); + // let _ = ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); + + // let cs = Reclaim::unknown_constraints(full_parameters); + // println!("Reclaim: {:#?}", cs.measure()); + // let _ = ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); + + Ok(()) +} diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 5d1aea213..0aacc39d8 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -54,7 +54,7 @@ use manta_crypto::{ merkle_tree, }; -#[cfg(test)] +#[cfg(any(feature = "test", test))] use manta_crypto::rand::{CryptoRng, Rand, RngCore, Sample, Standard}; #[doc(inline)] @@ -133,7 +133,7 @@ pub type KeyAgreementSchemeVar = DiffieHellman<GroupVar, Compiler>; pub type Utxo = Fp<ConstraintField>; /// UTXO Commitment Scheme -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct UtxoCommitmentScheme(pub Poseidon4); impl CommitmentScheme for UtxoCommitmentScheme { @@ -158,7 +158,7 @@ impl CommitmentScheme for UtxoCommitmentScheme { } } -#[cfg(test)] // NOTE: This is only safe in a test. +#[cfg(any(feature = "test", test))] // NOTE: This is only safe in a test. impl Sample for UtxoCommitmentScheme { #[inline] fn sample<R>(distribution: Standard, rng: &mut R) -> Self @@ -208,7 +208,7 @@ impl Constant<Compiler> for UtxoCommitmentSchemeVar { pub type VoidNumber = Fp<ConstraintField>; /// Void Number Hash Function -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct VoidNumberHashFunction(pub Poseidon2); impl BinaryHashFunction for VoidNumberHashFunction { @@ -227,7 +227,7 @@ impl BinaryHashFunction for VoidNumberHashFunction { } } -#[cfg(test)] // NOTE: This is only safe in a test. +#[cfg(any(feature = "test", test))] // NOTE: This is only safe in a test. impl Sample for VoidNumberHashFunction { #[inline] fn sample<R>(distribution: Standard, rng: &mut R) -> Self @@ -359,7 +359,6 @@ pub type LeafHash = merkle_tree::IdentityLeafHash<Utxo>; pub type LeafHashVar = merkle_tree::IdentityLeafHash<UtxoVar, Compiler>; /// Inner Hash Configuration -#[derive(Clone)] pub struct InnerHash; impl merkle_tree::InnerHash for InnerHash { @@ -432,7 +431,7 @@ impl merkle_tree::Configuration for MerkleTreeConfiguration { const HEIGHT: usize = 20; } -#[cfg(test)] +#[cfg(any(feature = "test", test))] impl merkle_tree::test::HashParameterSampling for MerkleTreeConfiguration { type LeafHashParameterDistribution = Standard; type InnerHashParameterDistribution = Standard; diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs index 7b1661295..ec0fc95fb 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs @@ -55,7 +55,7 @@ pub mod groth16 { use alloc::vec::Vec; use ark_crypto_primitives::SNARK; use ark_ec::PairingEngine; - use ark_groth16::{Groth16 as ArkGroth16, PreparedVerifyingKey, ProvingKey}; + use ark_groth16::{Groth16 as ArkGroth16, PreparedVerifyingKey}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use core::marker::PhantomData; use manta_crypto::{ @@ -64,10 +64,12 @@ pub mod groth16 { }; use scale_codec::{Decode, Encode, EncodeLike}; + pub use ark_groth16::ProvingKey; + /// Groth16 Proof #[derive(derivative::Derivative)] #[derivative(Clone, Debug, Default, Eq, PartialEq)] - pub struct Proof<E>(ark_groth16::Proof<E>) + pub struct Proof<E>(pub ark_groth16::Proof<E>) where E: PairingEngine; @@ -106,6 +108,31 @@ pub mod groth16 { impl<E> EncodeLike for Proof<E> where E: PairingEngine {} + /// Proving Context + #[derive(derivative::Derivative)] + #[derivative(Clone, Debug, Eq, PartialEq)] + pub struct ProvingContext<E>(pub ProvingKey<E>) + where + E: PairingEngine; + + impl<E> ProvingContext<E> + where + E: PairingEngine, + { + /// + #[inline] + pub fn new(proving_key: ProvingKey<E>) -> Self { + Self(proving_key) + } + } + + /// Verifying Context + #[derive(derivative::Derivative)] + #[derivative(Clone, Debug, Default)] + pub struct VerifyingContext<E>(pub PreparedVerifyingKey<E>) + where + E: PairingEngine; + /// Arkworks Groth16 Proof System #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -119,8 +146,8 @@ pub mod groth16 { { type ConstraintSystem = R1CS<E::Fr>; type PublicParameters = (); - type ProvingContext = ProvingKey<E>; - type VerifyingContext = PreparedVerifyingKey<E>; + type ProvingContext = ProvingContext<E>; + type VerifyingContext = VerifyingContext<E>; type Input = Vec<E::Fr>; type Proof = Proof<E>; type Verification = bool; @@ -150,7 +177,10 @@ pub mod groth16 { ConstraintSynthesizerWrapper(cs), &mut SizedRng(rng), )?; - Ok((proving_key, ArkGroth16::process_vk(&verifying_key)?)) + Ok(( + ProvingContext(proving_key), + VerifyingContext(ArkGroth16::process_vk(&verifying_key)?), + )) } #[inline] @@ -163,7 +193,7 @@ pub mod groth16 { R: CryptoRng + RngCore + ?Sized, { ArkGroth16::prove( - context, + &context.0, ConstraintSynthesizerWrapper(cs), &mut SizedRng(rng), ) @@ -176,7 +206,7 @@ pub mod groth16 { proof: &Self::Proof, context: &Self::VerifyingContext, ) -> Result<Self::Verification, Self::Error> { - ArkGroth16::verify_with_processed_vk(context, input, &proof.0) + ArkGroth16::verify_with_processed_vk(&context.0, input, &proof.0) } } } diff --git a/manta-pay/src/crypto/hash/poseidon.rs b/manta-pay/src/crypto/hash/poseidon.rs index 523bc5fa2..65239f55d 100644 --- a/manta-pay/src/crypto/hash/poseidon.rs +++ b/manta-pay/src/crypto/hash/poseidon.rs @@ -23,7 +23,7 @@ use alloc::vec::Vec; use core::{fmt::Debug, iter, mem}; use manta_crypto::hash::HashFunction; -#[cfg(test)] +#[cfg(any(feature = "test", test))] use { core::iter::repeat, manta_crypto::rand::{CryptoRng, Rand, RngCore, Sample}, @@ -229,7 +229,7 @@ where } } -#[cfg(test)] // NOTE: This is only safe to use in a test. +#[cfg(any(feature = "test", test))] // NOTE: This is only safe to use in a test. impl<D, S, COM, const ARITY: usize> Sample<D> for Hash<S, COM, ARITY> where D: Clone, diff --git a/manta-pay/src/wallet/cache.rs b/manta-pay/src/wallet/cache.rs index 8a45b58fd..89e3470eb 100644 --- a/manta-pay/src/wallet/cache.rs +++ b/manta-pay/src/wallet/cache.rs @@ -16,7 +16,10 @@ //! Proving Context Caching -use crate::config::{MultiProvingContext, ProvingContext}; +use crate::{ + config::{MultiProvingContext, ProvingContext}, + crypto::constraint::arkworks::groth16::ProvingKey, +}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; use async_std::{ io, @@ -101,7 +104,10 @@ impl OnDiskMultiProvingContext { File::open(path.as_ref()) .map_err(Error::Io) .and_then(move |f| { - ProvingContext::deserialize_unchecked(f).map_err(Error::Serialization) + // FIXME: Move to new serde platform so we don't need to specify the key here. + ProvingKey::deserialize_unchecked(f) + .map(ProvingContext::new) + .map_err(Error::Serialization) }) }) .await?) @@ -119,7 +125,12 @@ impl OnDiskMultiProvingContext { .create(true) .open(path.as_ref()) .map_err(Error::Io) - .and_then(move |f| context.serialize_unchecked(f).map_err(Error::Serialization)) + .and_then(move |f| { + context + .0 + .serialize_unchecked(f) + .map_err(Error::Serialization) + }) }) .await?) } diff --git a/manta-util/Cargo.toml b/manta-util/Cargo.toml index 5a55c9555..fae55a516 100644 --- a/manta-util/Cargo.toml +++ b/manta-util/Cargo.toml @@ -24,5 +24,9 @@ is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } +[features] +# Standard Library +std = [] + [dependencies] -zeroize = { version = "1.4.3", optional = true, default-features = false, features = ["alloc"] } +derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index 5a5c330de..2093189eb 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -16,7 +16,7 @@ //! Utilities -#![no_std] +#![cfg_attr(not(any(feature = "std", test)), no_std)] #![cfg_attr(doc_cfg, feature(doc_cfg))] #![forbid(rustdoc::broken_intra_doc_links)] #![forbid(missing_docs)] @@ -26,21 +26,15 @@ extern crate alloc; mod array; mod concat; mod sealed; -mod serde; pub mod cache; pub mod future; pub mod iter; pub mod pointer; +pub mod serde; pub use array::*; pub use concat::*; -pub use serde::*; - -#[doc(inline)] -#[cfg(feature = "zeroize")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "zeroize")))] -pub use zeroize; /// Implements [`From`]`<$from>` for an enum `$to`, choosing the `$kind` variant. // TODO: add `where` clauses diff --git a/manta-util/src/serde.rs b/manta-util/src/serde.rs index 62a9de145..1807e0aae 100644 --- a/manta-util/src/serde.rs +++ b/manta-util/src/serde.rs @@ -14,81 +14,424 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Serialization Utilities +//! Serialization and Deserialization Utilities use alloc::vec::Vec; +use core::{convert::Infallible, fmt::Debug, hash::Hash}; + +/// Reader +pub trait Read { + /// Error Type + type Error; + + /// Reads bytes from `self`, pushing them to `output` until exhausting the buffer inside of + /// `output`. + fn read<T>(&mut self, output: &mut T) -> Result<(), Self::Error> + where + T: AsMut<[u8]> + ?Sized; + + /// Reads all bytes from `self`, pushing them to `output`. + fn read_all(&mut self, output: &mut Vec<u8>) -> Result<(), Self::Error>; +} + +impl<R> Read for &mut R +where + R: Read, +{ + type Error = R::Error; + + #[inline] + fn read<T>(&mut self, output: &mut T) -> Result<(), Self::Error> + where + T: AsMut<[u8]> + ?Sized, + { + (*self).read(output) + } + + #[inline] + fn read_all(&mut self, output: &mut Vec<u8>) -> Result<(), Self::Error> { + (*self).read_all(output) + } +} + +impl Read for &[u8] { + type Error = usize; + + #[inline] + fn read<T>(&mut self, output: &mut T) -> Result<(), Self::Error> + where + T: AsMut<[u8]> + ?Sized, + { + let output = output.as_mut(); + let output_len = output.len(); + let len = self.len(); + if output_len > len { + return Err(output_len - len); + } + output.copy_from_slice(&self[..output_len]); + *self = &self[output_len..]; + Ok(()) + } + + #[inline] + fn read_all(&mut self, output: &mut Vec<u8>) -> Result<(), Self::Error> { + output + .write(self) + .map_err(|_| unreachable!("Infallible cannot be constructed.")) + } +} + +impl<const N: usize> Read for [u8; N] { + type Error = usize; + + #[inline] + fn read<T>(&mut self, output: &mut T) -> Result<(), Self::Error> + where + T: AsMut<[u8]> + ?Sized, + { + let output = output.as_mut(); + let output_len = output.len(); + let len = self.len(); + if output_len > len { + return Err(output_len - len); + } + output.copy_from_slice(&self[..output_len]); + Ok(()) + } + + #[inline] + fn read_all(&mut self, output: &mut Vec<u8>) -> Result<(), Self::Error> { + output + .write(&mut self.as_ref()) + .map_err(|_| unreachable!("Infallible cannot be constructed.")) + } +} + +impl Read for Vec<u8> { + type Error = usize; + + #[inline] + fn read<T>(&mut self, output: &mut T) -> Result<(), Self::Error> + where + T: AsMut<[u8]> + ?Sized, + { + let mut slice = self.as_slice(); + slice.read(output)?; + let len = slice.len(); + self.drain(..(self.len() - len)); + Ok(()) + } + + #[inline] + fn read_all(&mut self, output: &mut Vec<u8>) -> Result<(), Self::Error> { + output + .write_drain(self) + .map_err(|_| unreachable!("Infallible cannot be constructed.")) + } +} + +/// I/O Reader +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct IoReader<R>( + /// Reader + pub R, +) +where + R: std::io::Read; + +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +impl<R> Read for IoReader<R> +where + R: std::io::Read, +{ + type Error = std::io::Error; + + #[inline] + fn read<T>(&mut self, output: &mut T) -> Result<(), Self::Error> + where + T: AsMut<[u8]> + ?Sized, + { + self.0.read_exact(output.as_mut()) + } + + #[inline] + fn read_all(&mut self, output: &mut Vec<u8>) -> Result<(), Self::Error> { + self.0.read_to_end(output).map(|_| ()) + } +} + +/// Writer +pub trait Write { + /// Error Type + type Error; + + /// Writes bytes into `self`, pulling them from `input` until exhausting the buffer inside of + /// `self`. + fn write(&mut self, input: &mut &[u8]) -> Result<(), Self::Error>; + + /// Writes bytes into `self` from the bytes of `input`, returning the bytes which were not + /// written. + #[inline] + fn write_ref<'t, T>(&mut self, input: &'t T) -> Result<&'t [u8], Self::Error> + where + T: AsRef<[u8]> + ?Sized, + { + let mut slice = input.as_ref(); + self.write(&mut slice)?; + Ok(slice) + } + + /// Writes bytes into `self` from an `input` vector of bytes. + /// + /// # Implementation Note + /// + /// This method is here to provide an optimization path against the [`Vec`] implemention of + /// [`Write`]. The default implementation should be efficient enough for other cases. + #[inline] + fn write_drain(&mut self, input: &mut Vec<u8>) -> Result<(), Self::Error> { + let slice_len = self.write_ref(input)?.len(); + input.drain(..(input.len() - slice_len)); + Ok(()) + } +} + +impl<W> Write for &mut W +where + W: Write, +{ + type Error = W::Error; + + #[inline] + fn write(&mut self, input: &mut &[u8]) -> Result<(), Self::Error> { + (*self).write(input) + } + + #[inline] + fn write_ref<'t, T>(&mut self, input: &'t T) -> Result<&'t [u8], Self::Error> + where + T: AsRef<[u8]> + ?Sized, + { + (*self).write_ref(input) + } + + #[inline] + fn write_drain(&mut self, input: &mut Vec<u8>) -> Result<(), Self::Error> { + (*self).write_drain(input) + } +} + +impl Write for [u8] { + type Error = usize; + + #[inline] + fn write(&mut self, input: &mut &[u8]) -> Result<(), Self::Error> { + input.read(self) + } +} + +impl<const N: usize> Write for [u8; N] { + type Error = usize; + + #[inline] + fn write(&mut self, input: &mut &[u8]) -> Result<(), Self::Error> { + input.read(self) + } +} + +impl Write for Vec<u8> { + type Error = Infallible; + + #[inline] + fn write(&mut self, input: &mut &[u8]) -> Result<(), Self::Error> { + self.reserve(input.len()); + self.extend_from_slice(*input); + Ok(()) + } + + #[inline] + fn write_drain(&mut self, input: &mut Vec<u8>) -> Result<(), Self::Error> { + self.append(input); + Ok(()) + } +} + +/// I/O Writer +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct IoWriter<W>( + /// Writer + pub W, +) +where + W: std::io::Write; + +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +impl<W> Write for IoWriter<W> +where + W: std::io::Write, +{ + type Error = std::io::Error; + + #[inline] + fn write(&mut self, input: &mut &[u8]) -> Result<(), Self::Error> { + self.0.write_all(input)?; + *input = &input[..0]; + Ok(()) + } +} /// Serialization -pub trait Serialize { +pub trait Serialize<C = ()> { /// Appends representation of `self` in bytes to `buffer`. - fn serialize(&self, buffer: &mut Vec<u8>); + fn serialize<W>(&self, writer: W) -> Result<(), W::Error> + where + W: Write; /// Converts `self` into a vector of bytes. #[inline] fn to_vec(&self) -> Vec<u8> { let mut buffer = Vec::new(); - self.serialize(&mut buffer); + self.serialize(&mut buffer) + .expect("Writing to a `Vec<u8>` cannot fail."); buffer } } impl Serialize for u8 { #[inline] - fn serialize(&self, buffer: &mut Vec<u8>) { - buffer.push(*self); + fn serialize<W>(&self, mut writer: W) -> Result<(), W::Error> + where + W: Write, + { + writer.write_ref(&[*self])?; + Ok(()) } } -impl<T> Serialize for [T] +impl<T, C> Serialize<C> for [T] where - T: Serialize, + T: Serialize<C>, { #[inline] - fn serialize(&self, buffer: &mut Vec<u8>) { + fn serialize<W>(&self, mut writer: W) -> Result<(), W::Error> + where + W: Write, + { for item in self { - item.serialize(buffer); + item.serialize(&mut writer)?; } + Ok(()) } } -impl<T, const N: usize> Serialize for [T; N] +impl<T, C, const N: usize> Serialize<C> for [T; N] where - T: Serialize, + T: Serialize<C>, { #[inline] - fn serialize(&self, buffer: &mut Vec<u8>) { + fn serialize<W>(&self, mut writer: W) -> Result<(), W::Error> + where + W: Write, + { for item in self { - item.serialize(buffer); + item.serialize(&mut writer)?; } + Ok(()) } } /// Exact Size Serialization -pub trait SerializeExactSize<const N: usize>: Serialize { - /// Converts `self` into a exactly known byte array. - fn to_array(&self) -> [u8; N]; +pub trait SerializeExactSize<C, const N: usize>: Serialize<C> { + /// Converts `self` into an exactly known byte array. + #[inline] + fn to_array(&self) -> [u8; N] { + let mut buffer = [0; N]; + self.serialize(&mut buffer) + .expect("The implementation of this trait means that this cannot fail."); + buffer + } +} + +/// Deserialization Error +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "R::Error: Clone, D::Error: Clone"), + Copy(bound = "R::Error: Copy, D::Error: Copy"), + Debug(bound = "R::Error: Debug, D::Error: Debug"), + Eq(bound = "R::Error: Eq, D::Error: Eq"), + Hash(bound = "R::Error: Hash, D::Error: Hash"), + PartialEq(bound = "R::Error: PartialEq, D::Error: PartialEq") +)] +pub enum Error<R, D, C = ()> +where + R: Read + ?Sized, + D: Deserialize<C> + ?Sized, +{ + /// Reading Error + /// + /// See [`Read`] for more. + Read(R::Error), + + /// Deserialization Error + /// + /// See [`Deserialize`] for more. + Deserialize(D::Error), +} + +impl<R, D, C> Error<R, D, C> +where + R: Read + ?Sized, + D: Deserialize<C> + ?Sized, +{ + /// Converts `self` into an option over [`R::Error`](Read::Error). + #[inline] + pub fn read(self) -> Option<R::Error> { + match self { + Self::Read(err) => Some(err), + _ => None, + } + } + + /// Converts `self` into an option over [`D::Error`](Deserialize::Error). + #[inline] + pub fn deserialize(self) -> Option<D::Error> { + match self { + Self::Deserialize(err) => Some(err), + _ => None, + } + } } /// Deserialization -pub trait Deserialize: Sized { +pub trait Deserialize<C = ()>: Sized { /// Error Type type Error; /// Parses the input `buffer` into a concrete value of type `Self` if possible. - fn deserialize(buffer: &mut Vec<u8>) -> Result<Self, Self::Error>; + fn deserialize<R>(reader: R) -> Result<Self, Error<R, Self, C>> + where + R: Read; /// Converts a byte vector into a concrete value of type `Self` if possible. - #[cfg(feature = "zeroize")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "zeroize")))] #[inline] fn from_vec(buffer: Vec<u8>) -> Result<Self, Self::Error> { - let mut buffer = zeroize::Zeroizing::new(buffer); - Self::deserialize(&mut buffer) + Self::deserialize(buffer) + .map_err(move |err| err.deserialize().expect("Reading from `[u8]` cannot fail.")) } } /// Exact Size Deserialization -pub trait DeserializeExactSize<const N: usize>: Deserialize { +pub trait DeserializeExactSize<C, const N: usize>: Deserialize<C> { /// Converts a fixed-length byte array into a concrete value of type `Self`. - fn from_array(buffer: [u8; N]) -> Self; + #[inline] + fn from_array(buffer: [u8; N]) -> Self { + Self::deserialize(buffer) + .ok() + .expect("The implementation of this trait means that this cannot fail.") + } } From 6b5e9b8ab581b16ed92e7d3464d3bbf6e6cc4e07 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 19 Jan 2022 19:05:24 -0500 Subject: [PATCH 195/275] fix: add shim for large verifiying key serde --- manta-accounting/Cargo.toml | 4 +- manta-accounting/src/asset.rs | 72 +- manta-accounting/src/fs.rs | 38 +- manta-accounting/src/transfer/test.rs | 42 +- manta-accounting/src/wallet/signer.rs | 2 +- manta-crypto/Cargo.toml | 4 +- manta-crypto/src/merkle_tree/forest.rs | 2 +- manta-crypto/src/merkle_tree/tree.rs | 2 +- manta-pay/Cargo.toml | 2 +- manta-pay/src/bin/generate_parameters.rs | 51 +- .../constraint/arkworks/constraint_system.rs | 81 +- .../constraint/arkworks/proof_system.rs | 348 ++++++++- manta-util/Cargo.toml | 5 +- manta-util/src/array.rs | 14 +- manta-util/src/codec.rs | 733 ++++++++++++++++++ manta-util/src/concat.rs | 217 ------ manta-util/src/convert.rs | 31 + manta-util/src/iter/mod.rs | 4 + manta-util/src/lib.rs | 41 +- manta-util/src/persistance.rs | 40 + manta-util/src/pointer.rs | 17 +- manta-util/src/serde.rs | 437 ----------- 22 files changed, 1354 insertions(+), 833 deletions(-) create mode 100644 manta-util/src/codec.rs delete mode 100644 manta-util/src/concat.rs create mode 100644 manta-util/src/convert.rs create mode 100644 manta-util/src/persistance.rs delete mode 100644 manta-util/src/serde.rs diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 0c8d67afe..6cf248f80 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -29,7 +29,7 @@ maintenance = { status = "actively-developed" } cocoon-fs = ["async-std", "cocoon/std", "zeroize"] # Standard Library -std = ["async-std", "cocoon/std"] +std = ["async-std", "cocoon/std", "manta-util/std"] # Testing Frameworks test = ["async-std", "futures", "indexmap", "rand/alloc", "statrs"] @@ -42,7 +42,7 @@ derive_more = { version = "0.99.16", default-features = false, features = ["add" futures = { version = "0.3.19", optional = true, default-features = false, features = ["alloc"] } indexmap = { version = "1.8.0", optional = true, default-features = false } manta-crypto = { path = "../manta-crypto" } -manta-util = { path = "../manta-util" } +manta-util = { path = "../manta-util", features = ["alloc"] } serde = { version = "1.0.133", optional = true, default-features = false, features = ["derive"] } statrs = { version = "0.15.0", optional = true, default-features = false } rand = { version = "0.8.4", optional = true, default-features = false } diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index ef39af1c7..37b2c9b00 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -36,7 +36,7 @@ use manta_crypto::{ constraint::{Allocator, Secret, ValueSource, Variable}, rand::{CryptoRng, Rand, RngCore, Sample, Standard}, }; -use manta_util::{into_array_unchecked, Concat, ConcatAccumulator}; +use manta_util::into_array_unchecked; #[cfg(feature = "std")] use std::{ @@ -88,23 +88,6 @@ impl AssetId { } } -impl Concat for AssetId { - type Item = u8; - - #[inline] - fn concat<A>(&self, accumulator: &mut A) - where - A: ConcatAccumulator<Self::Item> + ?Sized, - { - accumulator.extend(&self.into_bytes()); - } - - #[inline] - fn size_hint(&self) -> Option<usize> { - Some(Self::SIZE) - } -} - impl From<AssetId> for [u8; AssetId::SIZE] { #[inline] fn from(entry: AssetId) -> Self { @@ -222,23 +205,6 @@ impl AssetValue { } } -impl Concat for AssetValue { - type Item = u8; - - #[inline] - fn concat<A>(&self, accumulator: &mut A) - where - A: ConcatAccumulator<Self::Item> + ?Sized, - { - accumulator.extend(&self.into_bytes()); - } - - #[inline] - fn size_hint(&self) -> Option<usize> { - Some(Self::SIZE) - } -} - impl From<AssetValue> for [u8; AssetValue::SIZE] { #[inline] fn from(entry: AssetValue) -> Self { @@ -396,17 +362,19 @@ impl Asset { /// Converts a byte array into `self`. #[inline] pub fn from_bytes(bytes: [u8; Self::SIZE]) -> Self { - let split = (AssetId::BITS / 8) as usize; Self::new( - AssetId::from_bytes(into_array_unchecked(&bytes[..split])), - AssetValue::from_bytes(into_array_unchecked(&bytes[split..])), + AssetId::from_bytes(into_array_unchecked(&bytes[..AssetId::SIZE])), + AssetValue::from_bytes(into_array_unchecked(&bytes[AssetId::SIZE..])), ) } /// Converts `self` into a byte array. #[inline] pub fn into_bytes(self) -> [u8; Self::SIZE] { - into_array_unchecked(self.accumulated::<Vec<_>>()) + let mut buffer = [0; Self::SIZE]; + buffer[..AssetId::SIZE].copy_from_slice(&self.id.into_bytes()); + buffer[AssetId::SIZE..].copy_from_slice(&self.value.into_bytes()); + buffer } /// Returns [`self.value`](Self::value) if the given `id` matches [`self.id`](Self::id). @@ -476,32 +444,6 @@ where } } -impl<I, V> Concat for Asset<I, V> -where - I: Concat, - V: Concat<Item = I::Item>, -{ - type Item = I::Item; - - #[inline] - fn concat<A>(&self, accumulator: &mut A) - where - A: ConcatAccumulator<Self::Item> + ?Sized, - { - self.id.concat(accumulator); - self.value.concat(accumulator); - } - - #[inline] - fn size_hint(&self) -> Option<usize> { - if let (Some(id_size), Some(value_size)) = (self.id.size_hint(), self.value.size_hint()) { - Some(id_size + value_size) - } else { - None - } - } -} - impl From<[u8; Self::SIZE]> for Asset { #[inline] fn from(array: [u8; Self::SIZE]) -> Self { diff --git a/manta-accounting/src/fs.rs b/manta-accounting/src/fs.rs index e8ba92cea..a86d660f8 100644 --- a/manta-accounting/src/fs.rs +++ b/manta-accounting/src/fs.rs @@ -21,8 +21,8 @@ use alloc::{boxed::Box, vec::Vec}; use core::{fmt::Debug, hash::Hash}; use manta_util::{ + codec::{Decode, Encode}, future::LocalBoxFuture, - serde::{Deserialize, Serialize}, }; /// Filesystem Encrypted Saving @@ -47,14 +47,14 @@ pub trait SaveEncrypted { /// Saves the `payload` to `path` after serializing using the `saving_key` to encrypt it. #[inline] - fn save<'s, P, S>( + fn save<'s, P, E, C>( path: P, saving_key: &'s Self::SavingKey, - payload: &'s S, + payload: &'s E, ) -> LocalBoxFuture<'s, Result<(), Self::Error>> where P: 's + AsRef<Self::Path>, - S: Serialize, + E: Encode<C>, { Self::save_bytes(path, saving_key, payload.to_vec()) } @@ -82,17 +82,17 @@ pub trait LoadDecrypted { /// Loads a vector of bytes from `path` using `loading_key` to decrypt them, then deserializing /// the bytes to a concrete value of type `D`. #[inline] - fn load<'s, P, D>( + fn load<'s, P, D, C>( path: P, loading_key: &'s Self::LoadingKey, - ) -> LocalBoxFuture<'s, Result<D, LoadError<D, Self>>> + ) -> LocalBoxFuture<'s, Result<D, LoadError<Self, D, C>>> where P: 's + AsRef<Self::Path>, - D: Deserialize, + D: Decode<C>, { Box::pin(async { match Self::load_bytes(path, loading_key).await { - Ok(bytes) => D::from_vec(bytes).map_err(LoadError::Deserialize), + Ok(bytes) => D::from_vec(bytes).map_err(LoadError::Decode), Err(err) => Err(LoadError::Loading(err)), } }) @@ -102,23 +102,23 @@ pub trait LoadDecrypted { /// Loading Error #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "D::Error: Clone, L::Error: Clone"), - Copy(bound = "D::Error: Copy, L::Error: Copy"), - Debug(bound = "D::Error: Debug, L::Error: Debug"), - Eq(bound = "D::Error: Eq, L::Error: Eq"), - Hash(bound = "D::Error: Hash, L::Error: Hash"), - PartialEq(bound = "D::Error: PartialEq, L::Error: PartialEq") + Clone(bound = "L::Error: Clone, D::Error: Clone"), + Copy(bound = "L::Error: Copy, D::Error: Copy"), + Debug(bound = "L::Error: Debug, D::Error: Debug"), + Eq(bound = "L::Error: Eq, D::Error: Eq"), + Hash(bound = "L::Error: Hash, D::Error: Hash"), + PartialEq(bound = "L::Error: PartialEq, D::Error: PartialEq") )] -pub enum LoadError<D, L> +pub enum LoadError<L, D, C = ()> where - D: Deserialize, L: LoadDecrypted + ?Sized, + D: Decode<C> + ?Sized, { - /// Deserialization Error - Deserialize(D::Error), - /// Payload Loading Error Loading(L::Error), + + /// Decoding Error + Decode(D::Error), } /// Cocoon Adapters diff --git a/manta-accounting/src/transfer/test.rs b/manta-accounting/src/transfer/test.rs index b0087a423..cdd5c4122 100644 --- a/manta-accounting/src/transfer/test.rs +++ b/manta-accounting/src/transfer/test.rs @@ -20,7 +20,8 @@ use crate::{ asset::{Asset, AssetId, AssetValue, AssetValueType}, transfer::{ has_public_participants, Configuration, FullParameters, Parameters, PreSender, - ProofSystemError, ProofSystemPublicParameters, Receiver, Sender, Transfer, Utxo, + ProofSystemError, ProofSystemPublicParameters, ProvingContext, Receiver, Sender, Transfer, + Utxo, VerifyingContext, }, }; use alloc::vec::Vec; @@ -135,6 +136,34 @@ where utxo_set: &mut A, rng: &mut R, ) -> Result<bool, ProofSystemError<C>> + where + A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + R: CryptoRng + RngCore + ?Sized, + { + let (proving_context, verifying_context) = Self::generate_context( + public_parameters, + FullParameters::new(parameters, utxo_set.model()), + rng, + )?; + Self::sample_and_check_proof_with_context( + &proving_context, + &verifying_context, + parameters, + utxo_set, + rng, + ) + } + + /// Samples a new [`Transfer`] and builds a correctness proof for it, checking if it was + /// validated using the given `proving_context` and `verifying_context`. + #[inline] + pub fn sample_and_check_proof_with_context<A, R>( + proving_context: &ProvingContext<C>, + verifying_context: &VerifyingContext<C>, + parameters: &Parameters<C>, + utxo_set: &mut A, + rng: &mut R, + ) -> Result<bool, ProofSystemError<C>> where A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, R: CryptoRng + RngCore + ?Sized, @@ -146,14 +175,15 @@ where }, rng, ); - let full_parameters = FullParameters::new(parameters, utxo_set.model()); - let (proving_context, verifying_context) = - Self::generate_context(public_parameters, full_parameters, rng)?; - let post = transfer.into_post(full_parameters, &proving_context, rng)?; + let post = transfer.into_post( + FullParameters::new(parameters, utxo_set.model()), + proving_context, + rng, + )?; C::ProofSystem::verify( &post.generate_proof_input(), &post.validity_proof, - &verifying_context, + verifying_context, ) } } diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 88dff218e..fb7b748a0 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -63,7 +63,7 @@ use manta_util::{ future::LocalBoxFuture, into_array_unchecked, iter::IteratorExt, - Rollback, + persistance::Rollback, }; /// Signer Connection diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index 31fc01f3b..3487f8189 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -29,14 +29,14 @@ maintenance = { status = "actively-developed" } getrandom = ["rand_core/getrandom"] # Standard Library -std = [] +std = ["manta-util/std"] # Testing Frameworks test = [] [dependencies] derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } -manta-util = { path = "../manta-util" } +manta-util = { path = "../manta-util", features = ["alloc"] } rand_core = { version = "0.6.3", default-features = false } serde = { version = "1.0.133", optional = true, default-features = false, features = ["derive"] } diff --git a/manta-crypto/src/merkle_tree/forest.rs b/manta-crypto/src/merkle_tree/forest.rs index 8b9fe7a17..5b7de44e1 100644 --- a/manta-crypto/src/merkle_tree/forest.rs +++ b/manta-crypto/src/merkle_tree/forest.rs @@ -36,7 +36,7 @@ use crate::{ }; use alloc::{boxed::Box, vec::Vec}; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; -use manta_util::{into_boxed_array_unchecked, pointer::PointerFamily, Rollback}; +use manta_util::{into_boxed_array_unchecked, persistance::Rollback, pointer::PointerFamily}; /// Merkle Forest Configuration pub trait Configuration: tree::Configuration { diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index bf88e3221..c638c2a86 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -37,7 +37,7 @@ use crate::{ }, }; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; -use manta_util::{pointer::PointerFamily, Rollback}; +use manta_util::{persistance::Rollback, pointer::PointerFamily}; /// Merkle Tree Leaf Hash pub trait LeafHash<COM = ()> { diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index b4fe66cd7..9f9b3cf65 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -59,7 +59,7 @@ test = ["manta-accounting/test", "manta-crypto/test"] simulation = ["rand", "statrs"] # Standard Library -std = ["manta-accounting/std"] +std = ["manta-accounting/std", "manta-util/std"] [dependencies] aes-gcm = { version = "0.9.4", default-features = false, features = ["aes", "alloc"] } diff --git a/manta-pay/src/bin/generate_parameters.rs b/manta-pay/src/bin/generate_parameters.rs index 66097783e..99e90940c 100644 --- a/manta-pay/src/bin/generate_parameters.rs +++ b/manta-pay/src/bin/generate_parameters.rs @@ -16,16 +16,19 @@ //! Generate Parameters -use ark_ff::bytes::ToBytes; -use ark_serialize::{CanonicalSerialize, Write}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use manta_accounting::transfer::Parameters; use manta_crypto::{ + accumulator::Accumulator, constraint::{measure::Measure, ProofSystem as _}, rand::{Rand, SeedableRng}, }; -use manta_pay::config::{FullParameters, Mint, PrivateTransfer, ProofSystem, Reclaim}; +use manta_pay::{ + config::{FullParameters, Mint, PrivateTransfer, ProofSystem, Reclaim, VerifyingContext}, + wallet::UtxoSet, +}; use rand_chacha::ChaCha20Rng; -use std::io; +use std::{fs::OpenOptions, io}; /// #[inline] @@ -36,8 +39,7 @@ pub fn main() -> io::Result<()> { ]); let parameters = rng.gen(); - let utxo_set_parameters = rng.gen(); - let full_parameters = FullParameters::new(&parameters, &utxo_set_parameters); + let mut utxo_set = UtxoSet::new(rng.gen()); /* let mut bytes = Vec::new(); @@ -59,36 +61,19 @@ pub fn main() -> io::Result<()> { println!("UTXO Set Parameters: {:?}", bytes.as_slice()); */ - let cs = Mint::unknown_constraints(full_parameters); + let cs = Mint::unknown_constraints(FullParameters::new(&parameters, utxo_set.model())); // println!("Mint: {:#?}", cs.measure()); - let mut buffer = Vec::new(); - let (_, verifying_context) = ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); - verifying_context - .0 - .vk - .serialize_unchecked(&mut buffer) - .unwrap(); - verifying_context - .0 - .alpha_g1_beta_g2 - .serialize_unchecked(&mut buffer) - .unwrap(); - verifying_context - .0 - .gamma_g2_neg_pc - .write(&mut buffer) - .unwrap(); - verifying_context - .0 - .delta_g2_neg_pc - .write(&mut buffer) - .unwrap(); - - std::fs::OpenOptions::new() + + let mut mint_file = OpenOptions::new() + .read(true) .create(true) .write(true) - .open("mint")? - .write_all(&buffer)?; + .open("mint")?; + + let (proving_context, verifying_context) = + ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); + + verifying_context.serialize(&mut mint_file).unwrap(); // let cs = PrivateTransfer::unknown_constraints(full_parameters); // println!("PrivateTransfer: {:#?}", cs.measure()); diff --git a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs index 8d360d041..2b33c9e0e 100644 --- a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs @@ -329,16 +329,22 @@ where /// Codec Utilities pub mod codec { - use ark_std::io::{Error, ErrorKind, Read}; + use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; + use ark_std::io::{self, Error, ErrorKind}; + use manta_util::codec::{Read, ReadExactError, Write}; use scale_codec::Input; + /// Arkworks Encoding Marker + #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct Ark; + /// Scale-Codec Input as Reader Wrapper #[derive(Debug, Eq, Hash, PartialEq)] pub struct ScaleCodecReader<'i, I>(pub &'i mut I) where I: Input; - impl<I> Read for ScaleCodecReader<'_, I> + impl<I> io::Read for ScaleCodecReader<'_, I> where I: Input, { @@ -353,4 +359,75 @@ pub mod codec { Input::read(self.0, buf).map_err(|_| ErrorKind::Other.into()) } } + + /// Serialization Hook + pub trait HasSerialization<'s>: 's { + /// Serialize Type + type Serialize: CanonicalSerialize + From<&'s Self>; + } + + /// Deserialization Hook + pub trait HasDeserialization: Sized { + /// Deserialize Type + type Deserialize: CanonicalDeserialize + Into<Self>; + } + + /// Arkworks Reader + #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct ArkReader<R>(pub R) + where + R: Read; + + impl<R> io::Read for ArkReader<R> + where + R: Read<Error = Error>, + { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> { + self.0.read(buf) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Error> { + self.0.read_exact(buf).map_err(|err| match err { + ReadExactError::UnexpectedEnd(_) => todo!(), + ReadExactError::Read(err) => err, + }) + } + } + + /// Arkworks Writer + #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct ArkWriter<W>(pub W) + where + W: Write; + + impl<W> io::Write for ArkWriter<W> + where + W: Write<Error = Error>, + { + #[inline] + fn write(&mut self, mut buf: &[u8]) -> Result<usize, Error> { + self.0.write(&mut buf) + } + + #[inline] + fn flush(&mut self) -> Result<(), Error> { + // NOTE: We can't necessarily do better than this for now, unfortunately. + Ok(()) + } + + #[inline] + fn write_all(&mut self, mut buf: &[u8]) -> Result<(), Error> { + self.0.write(&mut buf)?; + if buf.is_empty() { + Ok(()) + } else { + Err(Error::new( + ErrorKind::WriteZero, + "failed to write whole buffer", + )) + } + } + } } diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs index ec0fc95fb..175b40b48 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs @@ -16,7 +16,7 @@ //! Arkworks Proof System Implementations -use crate::crypto::constraint::arkworks::{constraint_system::SynthesisResult, R1CS}; +use crate::crypto::constraint::arkworks::{SynthesisResult, R1CS}; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef}; /// Constraint Synthesizer Wrapper @@ -46,23 +46,190 @@ where } } +/// Pairing Engine Utilities +pub mod pairing { + /// BLS-12 Utilities + pub mod bls12 { + use crate::crypto::constraint::arkworks::codec::{HasDeserialization, HasSerialization}; + use ark_ec::models::bls12::{g2, Bls12Parameters}; + use ark_ff::Fp2; + use ark_serialize::{ + CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write, + }; + + /// Line Evaluation Coefficients + pub type EllCoeff<F> = (F, F, F); + + /// G2 Prepared Point + #[derive(derivative::Derivative, CanonicalSerialize, CanonicalDeserialize)] + #[derivative(Clone, Default, Debug, Eq, PartialEq)] + pub struct G2Prepared<P> + where + P: Bls12Parameters, + { + /// Coefficients + pub ell_coeffs: Vec<EllCoeff<Fp2<P::Fp2Params>>>, + + /// Infinity Flag + pub infinity: bool, + } + + impl<P> From<g2::G2Prepared<P>> for G2Prepared<P> + where + P: Bls12Parameters, + { + #[inline] + fn from(point: g2::G2Prepared<P>) -> Self { + Self { + ell_coeffs: point.ell_coeffs, + infinity: point.infinity, + } + } + } + + impl<P> From<G2Prepared<P>> for g2::G2Prepared<P> + where + P: Bls12Parameters, + { + #[inline] + fn from(point: G2Prepared<P>) -> Self { + Self { + ell_coeffs: point.ell_coeffs, + infinity: point.infinity, + } + } + } + + /// G2 Prepared Point Reference + #[derive(derivative::Derivative)] + #[derivative(Debug, Eq, PartialEq)] + pub struct G2PreparedRef<'p, P>(pub &'p g2::G2Prepared<P>) + where + P: Bls12Parameters; + + impl<'p, P> CanonicalSerialize for G2PreparedRef<'p, P> + where + P: Bls12Parameters, + { + #[inline] + fn serialize<W>(&self, mut writer: W) -> Result<(), SerializationError> + where + W: Write, + { + let g2::G2Prepared { + ell_coeffs, + infinity, + } = &self.0; + ell_coeffs.serialize(&mut writer)?; + infinity.serialize(&mut writer)?; + Ok(()) + } + + #[inline] + fn serialized_size(&self) -> usize { + let g2::G2Prepared { + ell_coeffs, + infinity, + } = &self.0; + ell_coeffs.serialized_size() + infinity.serialized_size() + } + + #[inline] + fn serialize_uncompressed<W>(&self, mut writer: W) -> Result<(), SerializationError> + where + W: Write, + { + let g2::G2Prepared { + ell_coeffs, + infinity, + } = &self.0; + ell_coeffs.serialize_uncompressed(&mut writer)?; + infinity.serialize_uncompressed(&mut writer)?; + Ok(()) + } + + #[inline] + fn serialize_unchecked<W>(&self, mut writer: W) -> Result<(), SerializationError> + where + W: Write, + { + let g2::G2Prepared { + ell_coeffs, + infinity, + } = &self.0; + ell_coeffs.serialize_unchecked(&mut writer)?; + infinity.serialize_unchecked(&mut writer)?; + Ok(()) + } + + #[inline] + fn uncompressed_size(&self) -> usize { + let g2::G2Prepared { + ell_coeffs, + infinity, + } = &self.0; + ell_coeffs.uncompressed_size() + infinity.uncompressed_size() + } + } + + impl<'p, P> From<&'p g2::G2Prepared<P>> for G2PreparedRef<'p, P> + where + P: Bls12Parameters, + { + #[inline] + fn from(point: &'p g2::G2Prepared<P>) -> Self { + Self(point) + } + } + + impl<'p, P> From<G2PreparedRef<'p, P>> for &'p g2::G2Prepared<P> + where + P: Bls12Parameters, + { + #[inline] + fn from(point: G2PreparedRef<'p, P>) -> Self { + point.0 + } + } + + impl<'p, P> HasSerialization<'p> for g2::G2Prepared<P> + where + P: Bls12Parameters, + { + type Serialize = G2PreparedRef<'p, P>; + } + + impl<P> HasDeserialization for g2::G2Prepared<P> + where + P: Bls12Parameters, + { + type Deserialize = G2Prepared<P>; + } + } +} + /// Groth16 Proving System #[cfg(feature = "groth16")] #[cfg_attr(doc_cfg, doc(cfg(feature = "groth16")))] pub mod groth16 { use super::*; - use crate::crypto::constraint::arkworks::{self, constraint_system::SynthesisError}; + use crate::crypto::constraint::arkworks::{ + self, + codec::{HasDeserialization, HasSerialization}, + SynthesisError, + }; use alloc::vec::Vec; use ark_crypto_primitives::SNARK; use ark_ec::PairingEngine; use ark_groth16::{Groth16 as ArkGroth16, PreparedVerifyingKey}; - use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; + use ark_serialize::{ + CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write, + }; use core::marker::PhantomData; use manta_crypto::{ constraint::ProofSystem, rand::{CryptoRng, RngCore, SizedRng}, }; - use scale_codec::{Decode, Encode, EncodeLike}; pub use ark_groth16::ProvingKey; @@ -73,7 +240,7 @@ pub mod groth16 { where E: PairingEngine; - impl<E> Decode for Proof<E> + impl<E> scale_codec::Decode for Proof<E> where E: PairingEngine, { @@ -89,7 +256,7 @@ pub mod groth16 { } } - impl<E> Encode for Proof<E> + impl<E> scale_codec::Encode for Proof<E> where E: PairingEngine, { @@ -106,7 +273,7 @@ pub mod groth16 { } } - impl<E> EncodeLike for Proof<E> where E: PairingEngine {} + impl<E> scale_codec::EncodeLike for Proof<E> where E: PairingEngine {} /// Proving Context #[derive(derivative::Derivative)] @@ -119,7 +286,7 @@ pub mod groth16 { where E: PairingEngine, { - /// + /// Builds a new [`ProvingContext`] from `proving_key`. #[inline] pub fn new(proving_key: ProvingKey<E>) -> Self { Self(proving_key) @@ -133,6 +300,171 @@ pub mod groth16 { where E: PairingEngine; + impl<E> CanonicalSerialize for VerifyingContext<E> + where + E: PairingEngine, + for<'s> E::G2Prepared: HasSerialization<'s>, + { + #[inline] + fn serialize<W>(&self, mut writer: W) -> Result<(), SerializationError> + where + W: Write, + { + let PreparedVerifyingKey { + vk, + alpha_g1_beta_g2, + gamma_g2_neg_pc, + delta_g2_neg_pc, + } = &self.0; + vk.serialize(&mut writer)?; + alpha_g1_beta_g2.serialize(&mut writer)?; + <E::G2Prepared as HasSerialization>::Serialize::from(gamma_g2_neg_pc) + .serialize(&mut writer)?; + <E::G2Prepared as HasSerialization>::Serialize::from(delta_g2_neg_pc) + .serialize(&mut writer)?; + Ok(()) + } + + #[inline] + fn serialized_size(&self) -> usize { + let PreparedVerifyingKey { + vk, + alpha_g1_beta_g2, + gamma_g2_neg_pc, + delta_g2_neg_pc, + } = &self.0; + vk.serialized_size() + + alpha_g1_beta_g2.serialized_size() + + <E::G2Prepared as HasSerialization>::Serialize::from(gamma_g2_neg_pc) + .serialized_size() + + <E::G2Prepared as HasSerialization>::Serialize::from(delta_g2_neg_pc) + .serialized_size() + } + + #[inline] + fn serialize_uncompressed<W>(&self, mut writer: W) -> Result<(), SerializationError> + where + W: Write, + { + let PreparedVerifyingKey { + vk, + alpha_g1_beta_g2, + gamma_g2_neg_pc, + delta_g2_neg_pc, + } = &self.0; + vk.serialize_uncompressed(&mut writer)?; + alpha_g1_beta_g2.serialize_uncompressed(&mut writer)?; + <E::G2Prepared as HasSerialization>::Serialize::from(gamma_g2_neg_pc) + .serialize_uncompressed(&mut writer)?; + <E::G2Prepared as HasSerialization>::Serialize::from(delta_g2_neg_pc) + .serialize_uncompressed(&mut writer)?; + Ok(()) + } + + #[inline] + fn serialize_unchecked<W>(&self, mut writer: W) -> Result<(), SerializationError> + where + W: Write, + { + let PreparedVerifyingKey { + vk, + alpha_g1_beta_g2, + gamma_g2_neg_pc, + delta_g2_neg_pc, + } = &self.0; + vk.serialize_unchecked(&mut writer)?; + alpha_g1_beta_g2.serialize_unchecked(&mut writer)?; + <E::G2Prepared as HasSerialization>::Serialize::from(gamma_g2_neg_pc) + .serialize_unchecked(&mut writer)?; + <E::G2Prepared as HasSerialization>::Serialize::from(delta_g2_neg_pc) + .serialize_unchecked(&mut writer)?; + Ok(()) + } + + #[inline] + fn uncompressed_size(&self) -> usize { + let PreparedVerifyingKey { + vk, + alpha_g1_beta_g2, + gamma_g2_neg_pc, + delta_g2_neg_pc, + } = &self.0; + vk.uncompressed_size() + + alpha_g1_beta_g2.uncompressed_size() + + <E::G2Prepared as HasSerialization>::Serialize::from(gamma_g2_neg_pc) + .uncompressed_size() + + <E::G2Prepared as HasSerialization>::Serialize::from(delta_g2_neg_pc) + .uncompressed_size() + } + } + + impl<E> CanonicalDeserialize for VerifyingContext<E> + where + E: PairingEngine, + E::G2Prepared: HasDeserialization, + { + #[inline] + fn deserialize<R>(mut reader: R) -> Result<Self, SerializationError> + where + R: Read, + { + Ok(Self(PreparedVerifyingKey { + vk: CanonicalDeserialize::deserialize(&mut reader)?, + alpha_g1_beta_g2: CanonicalDeserialize::deserialize(&mut reader)?, + gamma_g2_neg_pc: <E::G2Prepared as HasDeserialization>::Deserialize::deserialize( + &mut reader, + )? + .into(), + delta_g2_neg_pc: <E::G2Prepared as HasDeserialization>::Deserialize::deserialize( + &mut reader, + )? + .into(), + })) + } + + #[inline] + fn deserialize_uncompressed<R>(mut reader: R) -> Result<Self, SerializationError> + where + R: Read, + { + Ok(Self(PreparedVerifyingKey { + vk: CanonicalDeserialize::deserialize_uncompressed(&mut reader)?, + alpha_g1_beta_g2: CanonicalDeserialize::deserialize_uncompressed(&mut reader)?, + gamma_g2_neg_pc: + <E::G2Prepared as HasDeserialization>::Deserialize::deserialize_uncompressed( + &mut reader, + )? + .into(), + delta_g2_neg_pc: + <E::G2Prepared as HasDeserialization>::Deserialize::deserialize_uncompressed( + &mut reader, + )? + .into(), + })) + } + + #[inline] + fn deserialize_unchecked<R>(mut reader: R) -> Result<Self, SerializationError> + where + R: Read, + { + Ok(Self(PreparedVerifyingKey { + vk: CanonicalDeserialize::deserialize_unchecked(&mut reader)?, + alpha_g1_beta_g2: CanonicalDeserialize::deserialize_unchecked(&mut reader)?, + gamma_g2_neg_pc: + <E::G2Prepared as HasDeserialization>::Deserialize::deserialize_unchecked( + &mut reader, + )? + .into(), + delta_g2_neg_pc: + <E::G2Prepared as HasDeserialization>::Deserialize::deserialize_unchecked( + &mut reader, + )? + .into(), + })) + } + } + /// Arkworks Groth16 Proof System #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] diff --git a/manta-util/Cargo.toml b/manta-util/Cargo.toml index fae55a516..fb2f8f874 100644 --- a/manta-util/Cargo.toml +++ b/manta-util/Cargo.toml @@ -25,8 +25,11 @@ is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } [features] +# Allocation +alloc = [] + # Standard Library -std = [] +std = ["alloc"] [dependencies] derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } diff --git a/manta-util/src/array.rs b/manta-util/src/array.rs index 485a8453c..376392a72 100644 --- a/manta-util/src/array.rs +++ b/manta-util/src/array.rs @@ -16,9 +16,11 @@ //! Array Utilities -use alloc::{boxed::Box, vec::Vec}; use core::convert::TryInto; +#[cfg(feature = "alloc")] +use alloc::{boxed::Box, vec::Vec}; + /// Performs the [`TryInto`] conversion into an array without checking if the conversion succeeded. #[inline] pub fn into_array_unchecked<T, V, const N: usize>(v: V) -> [T; N] @@ -36,6 +38,8 @@ where /// Performs the [`TryInto`] conversion into a boxed array without checking if the conversion /// succeeded. +#[cfg(feature = "alloc")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] #[inline] pub fn into_boxed_array_unchecked<T, V, const N: usize>(v: V) -> Box<[T; N]> where @@ -51,6 +55,8 @@ where } /// Maps `f` over the `array`. +#[cfg(feature = "alloc")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] #[inline] pub fn array_map<T, U, F, const N: usize>(array: [T; N], f: F) -> [U; N] where @@ -60,6 +66,8 @@ where } /// Maps `f` over the `array` by reference. +#[cfg(feature = "alloc")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] #[inline] pub fn array_map_ref<T, U, F, const N: usize>(array: &[T; N], f: F) -> [U; N] where @@ -70,6 +78,8 @@ where /// Maps `f` over the `array` returning the target array if all of the mappings succeeded, or /// returning the first error that occurs. +#[cfg(feature = "alloc")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] #[inline] pub fn fallible_array_map<T, U, E, F, const N: usize>(array: [T; N], f: F) -> Result<[U; N], E> where @@ -82,6 +92,8 @@ where /// Maps `f` over the `array` by reference returning the target array if all of the mappings /// succeeded, or returning the first error that occurs. +#[cfg(feature = "alloc")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] #[inline] pub fn fallible_array_map_ref<T, U, E, F, const N: usize>(array: &[T; N], f: F) -> Result<[U; N], E> where diff --git a/manta-util/src/codec.rs b/manta-util/src/codec.rs new file mode 100644 index 000000000..f370fd229 --- /dev/null +++ b/manta-util/src/codec.rs @@ -0,0 +1,733 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Encoding and Decoding Utilities + +// TODO: Add `ReadFrom` and `WriteInto` traits for conversion between different serde/codec impls +// which are specialized so that you can automatically convert between a type and itself. + +use core::{convert::Infallible, fmt::Debug, hash::Hash}; + +#[cfg(feature = "alloc")] +use alloc::vec::Vec; + +/// Reader +pub trait Read { + /// Error Type + type Error; + + /// Reads bytes from `self`, pushing them to `output`. The reader need not fill the `output` + /// buffer, but this method must return the number of bytes read into the `output`. + fn read<T>(&mut self, output: &mut T) -> Result<usize, Self::Error> + where + T: AsMut<[u8]> + ?Sized; + + /// Reads bytes from `self`, pushing them to `output` until exhausting the buffer inside of + /// `output`. + fn read_exact<T>(&mut self, output: &mut T) -> Result<(), ReadExactError<Self>> + where + T: AsMut<[u8]> + ?Sized; + + /// Creates a “by mutable reference” adaptor for this instance of [`Read`]. + #[inline] + fn by_ref(&mut self) -> &mut Self { + self + } +} + +/// Reader Extension Trait +pub trait ReadExt: Read { + /// Reads all bytes from `self`, pushing them to `output`, returning the number of bytes read if + /// successful. + #[cfg(feature = "alloc")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] + fn read_all(&mut self, output: &mut Vec<u8>) -> Result<usize, Self::Error>; +} + +impl<R> Read for &mut R +where + R: Read, +{ + type Error = R::Error; + + #[inline] + fn read<T>(&mut self, output: &mut T) -> Result<usize, Self::Error> + where + T: AsMut<[u8]> + ?Sized, + { + (*self).read(output) + } + + #[inline] + fn read_exact<T>(&mut self, output: &mut T) -> Result<(), ReadExactError<Self>> + where + T: AsMut<[u8]> + ?Sized, + { + (*self).read_exact(output).map_err(ReadExactError::map_same) + } +} + +impl<R> ReadExt for &mut R +where + R: ReadExt, +{ + #[cfg(feature = "alloc")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] + #[inline] + fn read_all(&mut self, output: &mut Vec<u8>) -> Result<usize, Self::Error> { + (*self).read_all(output) + } +} + +impl Read for &[u8] { + type Error = Infallible; + + #[inline] + fn read<T>(&mut self, output: &mut T) -> Result<usize, Self::Error> + where + T: AsMut<[u8]> + ?Sized, + { + let output = output.as_mut(); + let output_len = output.len(); + output.copy_from_slice(&self[..output_len]); + *self = &self[output_len..]; + Ok(output_len) + } + + #[inline] + fn read_exact<T>(&mut self, output: &mut T) -> Result<(), ReadExactError<Self>> + where + T: AsMut<[u8]> + ?Sized, + { + let output = output.as_mut(); + let output_len = output.len(); + let len = self.len(); + if output_len > len { + return Err(ReadExactError::UnexpectedEnd(output_len - len)); + } + output.copy_from_slice(&self[..output_len]); + *self = &self[output_len..]; + Ok(()) + } +} + +impl ReadExt for &[u8] { + #[cfg(feature = "alloc")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] + #[inline] + fn read_all(&mut self, output: &mut Vec<u8>) -> Result<usize, Self::Error> { + output.write(self) + } +} + +impl<const N: usize> Read for [u8; N] { + type Error = Infallible; + + #[inline] + fn read<T>(&mut self, output: &mut T) -> Result<usize, Self::Error> + where + T: AsMut<[u8]> + ?Sized, + { + let output = output.as_mut(); + let output_len = output.len(); + output.copy_from_slice(&self[..output_len]); + Ok(output_len) + } + + #[inline] + fn read_exact<T>(&mut self, output: &mut T) -> Result<(), ReadExactError<Self>> + where + T: AsMut<[u8]> + ?Sized, + { + let output = output.as_mut(); + let output_len = output.len(); + let len = self.len(); + if output_len > len { + return Err(ReadExactError::UnexpectedEnd(output_len - len)); + } + output.copy_from_slice(&self[..output_len]); + Ok(()) + } +} + +impl<const N: usize> ReadExt for [u8; N] { + #[cfg(feature = "alloc")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] + #[inline] + fn read_all(&mut self, output: &mut Vec<u8>) -> Result<usize, Self::Error> { + output.write(&mut self.as_ref()) + } +} + +#[cfg(feature = "alloc")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] +impl Read for Vec<u8> { + type Error = Infallible; + + #[inline] + fn read<T>(&mut self, output: &mut T) -> Result<usize, Self::Error> + where + T: AsMut<[u8]> + ?Sized, + { + let mut slice = self.as_slice(); + let output_len = slice.read(output)?; + let len = slice.len(); + self.drain(..(self.len() - len)); + Ok(output_len) + } + + #[inline] + fn read_exact<T>(&mut self, output: &mut T) -> Result<(), ReadExactError<Self>> + where + T: AsMut<[u8]> + ?Sized, + { + let mut slice = self.as_slice(); + slice.read_exact(output).map_err(ReadExactError::map_same)?; + let len = slice.len(); + self.drain(..(self.len() - len)); + Ok(()) + } +} + +#[cfg(feature = "alloc")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] +impl ReadExt for Vec<u8> { + #[inline] + fn read_all(&mut self, output: &mut Vec<u8>) -> Result<usize, Self::Error> { + output.write_drain(self) + } +} + +/// I/O Reader +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct IoReader<R>( + /// Reader + pub R, +) +where + R: std::io::Read; + +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +impl<R> Read for IoReader<R> +where + R: std::io::Read, +{ + type Error = std::io::Error; + + #[inline] + fn read<T>(&mut self, output: &mut T) -> Result<usize, Self::Error> + where + T: AsMut<[u8]> + ?Sized, + { + self.0.read(output.as_mut()) + } + + #[inline] + fn read_exact<T>(&mut self, output: &mut T) -> Result<(), ReadExactError<Self>> + where + T: AsMut<[u8]> + ?Sized, + { + // NOTE: We can't use `ReadExactError::UnexpectedEnd` here since the `std::io::Read` trait + // doesn't expose any information about how many bytes remain in the output buffer. + self.0 + .read_exact(output.as_mut()) + .map_err(ReadExactError::Read) + } +} + +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +impl<R> ReadExt for IoReader<R> +where + R: std::io::Read, +{ + #[inline] + fn read_all(&mut self, output: &mut Vec<u8>) -> Result<usize, Self::Error> { + self.0.read_to_end(output) + } +} + +/// Read-Exact Error +/// +/// This `enum` is the error state for the [`read_exact`](Read::read_exact) method of [`Read`]. +/// See its documentation for more. +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "R::Error: Clone"), + Copy(bound = "R::Error: Copy"), + Debug(bound = "R::Error: Debug"), + Eq(bound = "R::Error: Eq"), + Hash(bound = "R::Error: Hash"), + PartialEq(bound = "R::Error: PartialEq") +)] +pub enum ReadExactError<R> +where + R: Read + ?Sized, +{ + /// Unexpected End of Reader + /// + /// The reader finished producing bytes before the output buffer was filled. The amount + /// of bytes remaining in the output buffer is returned to the caller here. + UnexpectedEnd(usize), + + /// Reading Error + Read(R::Error), +} + +impl<R> ReadExactError<R> +where + R: Read + ?Sized, +{ + /// Maps `self` along `f` for the [`Self::Read`] variant into another [`ReadExactError`]. + #[inline] + pub fn map<S, F>(self, f: F) -> ReadExactError<S> + where + S: Read + ?Sized, + F: FnOnce(R::Error) -> S::Error, + { + match self { + Self::UnexpectedEnd(remaining) => ReadExactError::UnexpectedEnd(remaining), + Self::Read(err) => ReadExactError::Read(f(err)), + } + } + + /// Maps `self` along `f` for the [`Self::Read`] variant into another [`ReadExactError`] + /// using the same error value. + #[inline] + pub fn map_same<S>(self) -> ReadExactError<S> + where + S: Read<Error = R::Error> + ?Sized, + { + self.map(core::convert::identity) + } +} + +/// Writer +pub trait Write { + /// Error Type + type Error; + + /// Writes bytes into `self`, pulling them from `input` until exhausting the buffer inside of + /// `self`. This method then resets the `input` slice to the remaining input data and returns + /// the number of bytes written. + /// + /// To preserve the input slice and return the remaining data, use the + /// [`write_ref`](Self::write_ref) method instead. + fn write(&mut self, input: &mut &[u8]) -> Result<usize, Self::Error>; + + /// Writes bytes into `self` from the bytes of `input`, returning the bytes which were not + /// written. + #[inline] + fn write_ref<'t, T>(&mut self, input: &'t T) -> Result<&'t [u8], Self::Error> + where + T: AsRef<[u8]> + ?Sized, + { + let mut slice = input.as_ref(); + self.write(&mut slice)?; + Ok(slice) + } + + /// Writes bytes into `self` from an `input` vector of bytes. + /// + /// # Implementation Note + /// + /// This method is here to provide an optimization path against the [`Vec`] implemention of + /// [`Write`]. The default implementation should be efficient enough for other cases. + #[cfg(feature = "alloc")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] + #[inline] + fn write_drain(&mut self, input: &mut Vec<u8>) -> Result<usize, Self::Error> { + let len = input.len(); + let bytes_written = len - self.write_ref(input)?.len(); + input.drain(..bytes_written); + Ok(bytes_written) + } + + /// Creates a “by mutable reference” adaptor for this instance of [`Write`]. + #[inline] + fn by_ref(&mut self) -> &mut Self { + self + } +} + +impl<W> Write for &mut W +where + W: Write, +{ + type Error = W::Error; + + #[inline] + fn write(&mut self, input: &mut &[u8]) -> Result<usize, Self::Error> { + (*self).write(input) + } + + #[inline] + fn write_ref<'t, T>(&mut self, input: &'t T) -> Result<&'t [u8], Self::Error> + where + T: AsRef<[u8]> + ?Sized, + { + (*self).write_ref(input) + } + + #[cfg(feature = "alloc")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] + #[inline] + fn write_drain(&mut self, input: &mut Vec<u8>) -> Result<usize, Self::Error> { + (*self).write_drain(input) + } +} + +impl Write for [u8] { + type Error = Infallible; + + #[inline] + fn write(&mut self, input: &mut &[u8]) -> Result<usize, Self::Error> { + input.read(self) + } +} + +impl<const N: usize> Write for [u8; N] { + type Error = Infallible; + + #[inline] + fn write(&mut self, input: &mut &[u8]) -> Result<usize, Self::Error> { + input.read(self) + } +} + +#[cfg(feature = "alloc")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] +impl Write for Vec<u8> { + type Error = Infallible; + + #[inline] + fn write(&mut self, input: &mut &[u8]) -> Result<usize, Self::Error> { + let len = input.len(); + self.reserve(len); + self.extend_from_slice(*input); + Ok(len) + } + + #[inline] + fn write_drain(&mut self, input: &mut Vec<u8>) -> Result<usize, Self::Error> { + let len = input.len(); + self.append(input); + Ok(len) + } +} + +/// I/O Writer +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct IoWriter<W>( + /// Writer + pub W, +) +where + W: std::io::Write; + +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +impl<W> Write for IoWriter<W> +where + W: std::io::Write, +{ + type Error = std::io::Error; + + #[inline] + fn write(&mut self, input: &mut &[u8]) -> Result<usize, Self::Error> { + let len = input.len(); + self.0.write_all(input)?; + *input = &input[..0]; + Ok(len) + } +} + +/// Pipelined Reader/Writer +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Pipeline<T>(pub T); + +impl<R> Pipeline<R> +where + R: Read, +{ + /// Reads bytes from `self`, pushing them to `output`. The reader need not fill the `output` + /// buffer. + #[inline] + pub fn read<T>(mut self, output: &mut T) -> Result<Self, R::Error> + where + T: AsMut<[u8]> + ?Sized, + { + self.0.read(output)?; + Ok(self) + } + + /// Reads bytes from `self`, pushing them to `output` until exhausting the buffer inside of + /// `output`. + #[inline] + pub fn read_exact<T>(mut self, output: &mut T) -> Result<Self, ReadExactError<R>> + where + T: AsMut<[u8]> + ?Sized, + { + self.0.read_exact(output)?; + Ok(self) + } +} + +impl<R> Pipeline<R> +where + R: ReadExt, +{ + /// Reads all bytes from `self`, pushing them to `output`. + #[cfg(feature = "alloc")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] + #[inline] + pub fn read_all(mut self, output: &mut Vec<u8>) -> Result<Self, R::Error> { + self.0.read_all(output)?; + Ok(self) + } +} + +impl<W> Pipeline<W> +where + W: Write, +{ + /// Writes bytes into `self`, pulling them from `input` until exhausting the buffer inside of + /// `self`. This method then resets the `input` slice to the remaining input data. + /// + /// To preserve the input slice, use the [`write_ref`](Self::write_ref) method instead. + #[inline] + pub fn write(mut self, input: &mut &[u8]) -> Result<Self, W::Error> { + self.0.write(input)?; + Ok(self) + } + + /// Writes bytes into `self` from the bytes of `input`. + #[inline] + pub fn write_ref<T>(mut self, input: &T) -> Result<Self, W::Error> + where + T: AsRef<[u8]> + ?Sized, + { + self.0.write_ref(input)?; + Ok(self) + } + + /// Writes bytes into `self` from an `input` vector of bytes. + /// + /// # Implementation Note + /// + /// This method is here to provide an optimization path against the [`Vec`] implemention of + /// [`Write`]. The default implementation should be efficient enough for other cases. + #[cfg(feature = "alloc")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] + #[inline] + pub fn write_drain(mut self, input: &mut Vec<u8>) -> Result<Self, W::Error> { + self.0.write_drain(input)?; + Ok(self) + } +} + +/// Encoding +pub trait Encode<C = ()> { + /// Appends representation of `self` in bytes to `buffer`. + fn encode<W>(&self, writer: W) -> Result<(), W::Error> + where + W: Write; + + /// Converts `self` into a vector of bytes. + #[cfg(feature = "alloc")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] + #[inline] + fn to_vec(&self) -> Vec<u8> { + let mut buffer = Vec::new(); + self.encode(&mut buffer) + .expect("Writing to a `Vec<u8>` cannot fail."); + buffer + } +} + +impl Encode for u8 { + #[inline] + fn encode<W>(&self, mut writer: W) -> Result<(), W::Error> + where + W: Write, + { + writer.write_ref(&[*self])?; + Ok(()) + } +} + +impl<T, C> Encode<C> for [T] +where + T: Encode<C>, +{ + #[inline] + fn encode<W>(&self, mut writer: W) -> Result<(), W::Error> + where + W: Write, + { + for item in self { + item.encode(&mut writer)?; + } + Ok(()) + } +} + +impl<T, C, const N: usize> Encode<C> for [T; N] +where + T: Encode<C>, +{ + #[inline] + fn encode<W>(&self, mut writer: W) -> Result<(), W::Error> + where + W: Write, + { + for item in self { + item.encode(&mut writer)?; + } + Ok(()) + } +} + +/// Exact Size Encoding +pub trait EncodeExactSize<C, const N: usize>: Encode<C> { + /// Converts `self` into an exactly known byte array. + #[inline] + fn to_array(&self) -> [u8; N] { + let mut buffer = [0; N]; + self.encode(&mut buffer) + .expect("The implementation of this trait means that this cannot fail."); + buffer + } +} + +/// Decoding +pub trait Decode<C = ()>: Sized { + /// Error Type + type Error; + + /// Parses the input `buffer` into a concrete value of type `Self` if possible. + fn decode<R>(reader: R) -> Result<Self, DecodeError<R, Self, C>> + where + R: Read; + + /// Converts a byte vector into a concrete value of type `Self` if possible. + #[cfg(feature = "alloc")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] + #[inline] + fn from_vec(buffer: Vec<u8>) -> Result<Self, Self::Error> { + Self::decode(buffer) + .map_err(move |err| err.decode().expect("Reading from `[u8]` cannot fail.")) + } +} + +/// Exact Size Decoding +pub trait DecodeExactSize<C, const N: usize>: Decode<C> { + /// Converts a fixed-length byte array into a concrete value of type `Self`. + #[inline] + fn from_array(buffer: [u8; N]) -> Self { + Self::decode(buffer) + .ok() + .expect("The implementation of this trait means that this cannot fail.") + } +} + +/// Decoding Error +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "R::Error: Clone, D::Error: Clone"), + Copy(bound = "R::Error: Copy, D::Error: Copy"), + Debug(bound = "R::Error: Debug, D::Error: Debug"), + Eq(bound = "R::Error: Eq, D::Error: Eq"), + Hash(bound = "R::Error: Hash, D::Error: Hash"), + PartialEq(bound = "R::Error: PartialEq, D::Error: PartialEq") +)] +pub enum DecodeError<R, D, C = ()> +where + R: Read + ?Sized, + D: Decode<C> + ?Sized, +{ + /// Reading Error + /// + /// See [`Read`] for more. + Read(R::Error), + + /// Decoding Error + /// + /// See [`Decode`] for more. + Decode(D::Error), +} + +impl<R, D, C> DecodeError<R, D, C> +where + R: Read + ?Sized, + D: Decode<C> + ?Sized, +{ + /// Converts `self` into an option over [`R::Error`](Read::Error). + #[inline] + pub fn read(self) -> Option<R::Error> { + match self { + Self::Read(err) => Some(err), + _ => None, + } + } + + /// Converts `self` into an option over [`D::Error`](Decode::Error). + #[inline] + pub fn decode(self) -> Option<D::Error> { + match self { + Self::Decode(err) => Some(err), + _ => None, + } + } + + /// Maps `self` into a [`DecodeError`] with the given `Reader`, `Decoder`, and `Codec` + /// implementations using `r` and `d` to convert errors. + #[inline] + pub fn map<Reader, Decoder, Codec, FR, FD>( + self, + r: FR, + d: FD, + ) -> DecodeError<Reader, Decoder, Codec> + where + Reader: Read + ?Sized, + Decoder: Decode<Codec> + ?Sized, + FR: FnOnce(R::Error) -> Reader::Error, + FD: FnOnce(D::Error) -> Decoder::Error, + { + match self { + Self::Read(err) => DecodeError::Read(r(err)), + Self::Decode(err) => DecodeError::Decode(d(err)), + } + } + + /// Maps `self` into a [`DecodeError`] with the given `Reader`, `Decoder`, and `Codec` + /// implementations using [`Into::into`] to convert errors. + #[inline] + pub fn into<Reader, Decoder, Codec>(self) -> DecodeError<Reader, Decoder, Codec> + where + Reader: Read + ?Sized, + Decoder: Decode<Codec> + ?Sized, + R::Error: Into<Reader::Error>, + D::Error: Into<Decoder::Error>, + { + self.map(Into::into, Into::into) + } +} diff --git a/manta-util/src/concat.rs b/manta-util/src/concat.rs deleted file mode 100644 index c69f9c733..000000000 --- a/manta-util/src/concat.rs +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Byte Concatenation Utilities - -use alloc::vec::Vec; - -/// Concatenation Accumulator Trait -pub trait ConcatAccumulator<T> { - /// Extends the current accumulator by a `buffer` of elements. - fn extend(&mut self, buffer: &[T]); - - /// Reserves space in the accumulator for `additional` more elements. - #[inline] - fn reserve(&mut self, additional: usize) { - let _ = additional; - } - - /// Drops extra capacity in the accumulator. - #[inline] - fn shrink_to_fit(&mut self) {} - - /// Captures the accumulator and drops extra capacity before returning an owned copy. - #[inline] - #[must_use] - fn finish(mut self) -> Self - where - Self: Sized, - { - self.shrink_to_fit(); - self - } - - /// Creates a "by mutable reference" adaptor for this instance of [`ConcatAccumulator`]. - #[inline] - fn by_ref(&mut self) -> &mut Self - where - Self: Sized, - { - self - } -} - -impl<T, A> ConcatAccumulator<T> for &mut A -where - A: ConcatAccumulator<T> + ?Sized, -{ - #[inline] - fn extend(&mut self, buffer: &[T]) { - (**self).extend(buffer); - } - - #[inline] - fn reserve(&mut self, additional: usize) { - (**self).reserve(additional); - } - - #[inline] - fn shrink_to_fit(&mut self) { - (**self).shrink_to_fit(); - } -} - -impl<T> ConcatAccumulator<T> for Vec<T> -where - T: Clone, -{ - #[inline] - fn extend(&mut self, buffer: &[T]) { - self.extend_from_slice(buffer); - } - - #[inline] - fn reserve(&mut self, additional: usize) { - self.reserve(additional); - } - - #[inline] - fn shrink_to_fit(&mut self) { - self.shrink_to_fit(); - } -} - -/// Concatenation Trait -pub trait Concat { - /// Item Type - type Item; - - /// Concatenates `self` on the end of the accumulator. - /// - /// # Note - /// - /// Implementations should not ask to reserve additional space for elements in this method. - /// Instead, reimplement [`reserve_concat`](Self::reserve_concat) if the default implementation - /// is not efficient. - fn concat<A>(&self, accumulator: &mut A) - where - A: ConcatAccumulator<Self::Item> + ?Sized; - - /// Returns a hint to the possible number of bytes that will be accumulated when concatenating - /// `self`. - #[inline] - fn size_hint(&self) -> Option<usize> { - None - } - - /// Concatenates `self` on the end of the accumulator after trying to reserve space for it. - #[inline] - fn reserve_concat<A>(&self, accumulator: &mut A) - where - A: ConcatAccumulator<Self::Item> + ?Sized, - { - if let Some(capacity) = self.size_hint() { - accumulator.reserve(capacity); - } - self.concat(accumulator); - } - - /// Constructs a default accumulator and accumulates over `self`, reserving the appropriate - /// capacity. - #[inline] - fn accumulated<A>(&self) -> A - where - A: Default + ConcatAccumulator<Self::Item>, - { - let mut accumulator = A::default(); - self.reserve_concat(&mut accumulator); - accumulator.finish() - } -} - -impl<T> Concat for [T] { - type Item = T; - - #[inline] - fn concat<A>(&self, accumulator: &mut A) - where - A: ConcatAccumulator<T> + ?Sized, - { - accumulator.extend(self); - } - - #[inline] - fn size_hint(&self) -> Option<usize> { - Some(self.len()) - } -} - -impl<T, const N: usize> Concat for [T; N] { - type Item = T; - - #[inline] - fn concat<A>(&self, accumulator: &mut A) - where - A: ConcatAccumulator<T> + ?Sized, - { - accumulator.extend(self); - } - - #[inline] - fn size_hint(&self) -> Option<usize> { - Some(self.len()) - } -} - -impl<T> Concat for Vec<T> { - type Item = T; - - #[inline] - fn concat<A>(&self, accumulator: &mut A) - where - A: ConcatAccumulator<T> + ?Sized, - { - accumulator.extend(self); - } - - #[inline] - fn size_hint(&self) -> Option<usize> { - Some(self.len()) - } -} - -/// Concatenates `$item`s together by building a [`ConcatAccumulator`] and running -/// [`Concat::concat`] over each `$item`. -#[macro_export] -macro_rules! concatenate { - ($($item:expr),*) => { - { - extern crate alloc; - let mut accumulator = ::alloc::vec::Vec::new(); - $($crate::Concat::reserve_concat($item, &mut accumulator);)* - $crate::ConcatAccumulator::finish(accumulator) - } - } -} - -/// Returns byte vector representation of `$item` if it implements [`Concat<Item = u8>`](Concat). -#[macro_export] -macro_rules! as_bytes { - ($item:expr) => {{ - let bytes: Vec<u8> = $crate::concatenate!($item); - bytes - }}; -} diff --git a/manta-util/src/convert.rs b/manta-util/src/convert.rs new file mode 100644 index 000000000..4e1b58509 --- /dev/null +++ b/manta-util/src/convert.rs @@ -0,0 +1,31 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Conversion Utilities + +use core::convert::Infallible; + +/// The Never Type +/// +/// This `type` will eventually be replaced by `!`, the primitive never type. See the ongoing +/// discussion for the [never_type #35121](https://github.com/rust-lang/rust/issues/35121) feature. +pub type Never = Infallible; + +/// Promotes a [`Never`] value to another type. +#[inline] +pub fn never<T>(_: Never) -> T { + unreachable!("This type never has any values, so this promotion is safe.") +} diff --git a/manta-util/src/iter/mod.rs b/manta-util/src/iter/mod.rs index a9797d966..5a9aa7b8f 100644 --- a/manta-util/src/iter/mod.rs +++ b/manta-util/src/iter/mod.rs @@ -16,8 +16,10 @@ //! Iteration Utilities +#[cfg(feature = "alloc")] mod chunk_by; +#[cfg(feature = "alloc")] pub use chunk_by::*; /// Iterator Extensions @@ -30,6 +32,8 @@ pub trait IteratorExt: Iterator { /// chunk size must be known at compile time. /// /// [`ChunksExact`]: core::slice::ChunksExact + #[cfg(feature = "alloc")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] #[inline] fn chunk_by<const N: usize>(self) -> ChunkBy<Self, N> where diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index 2093189eb..df67bf4c4 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -21,20 +21,28 @@ #![forbid(rustdoc::broken_intra_doc_links)] #![forbid(missing_docs)] +#[cfg(feature = "alloc")] extern crate alloc; mod array; -mod concat; mod sealed; -pub mod cache; -pub mod future; +pub mod codec; +pub mod convert; pub mod iter; +pub mod persistance; pub mod pointer; -pub mod serde; + +#[cfg(feature = "alloc")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] +pub mod cache; + +#[cfg(feature = "alloc")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] +pub mod future; pub use array::*; -pub use concat::*; +pub use sealed::*; /// Implements [`From`]`<$from>` for an enum `$to`, choosing the `$kind` variant. // TODO: add `where` clauses @@ -49,26 +57,3 @@ macro_rules! from_variant_impl { } }; } - -/// Rollback Trait -/// -/// This trait should be implemented by strucutres which have a canonical working state which can be -/// discarded easily. -pub trait Rollback { - /// Rolls back `self` to the previous state. - /// - /// # Implementation Note - /// - /// Rolling back to the previous state must be idempotent, i.e. two consecutive calls to - /// [`rollback`](Self::rollback) should have the same effect as one call. - fn rollback(&mut self); - - /// Commits `self` to the current state, preventing a future call to - /// [`rollback`](Self::rollback) from clearing the state. - /// - /// # Implementation Note - /// - /// Commiting to the current state must be idempotent, i.e. two consecutive calls to - /// [`commit`](Self::commit) should have the same effect as one call. - fn commit(&mut self); -} diff --git a/manta-util/src/persistance.rs b/manta-util/src/persistance.rs new file mode 100644 index 000000000..c65390b77 --- /dev/null +++ b/manta-util/src/persistance.rs @@ -0,0 +1,40 @@ +// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. + +//! Persistance and Backups Utilities + +/// Rollback Trait +/// +/// This trait should be implemented by strucutres which have a canonical working state which can be +/// discarded easily. +pub trait Rollback { + /// Rolls back `self` to the previous state. + /// + /// # Implementation Note + /// + /// Rolling back to the previous state must be idempotent, i.e. two consecutive calls to + /// [`rollback`](Self::rollback) should have the same effect as one call. + fn rollback(&mut self); + + /// Commits `self` to the current state, preventing a future call to + /// [`rollback`](Self::rollback) from clearing the state. + /// + /// # Implementation Note + /// + /// Commiting to the current state must be idempotent, i.e. two consecutive calls to + /// [`commit`](Self::commit) should have the same effect as one call. + fn commit(&mut self); +} diff --git a/manta-util/src/pointer.rs b/manta-util/src/pointer.rs index 4c6ec131d..4b62e2a5b 100644 --- a/manta-util/src/pointer.rs +++ b/manta-util/src/pointer.rs @@ -16,18 +16,18 @@ //! Pointer Utilities -// TODO: Think about whether we want to keep the `PointerFamily` API sealed or not. - -use crate::{create_seal, seal}; -use alloc::{rc::Weak as WeakRc, sync::Weak as WeakArc}; use core::{borrow::Borrow, ops::Deref}; -create_seal! {} +#[cfg(feature = "alloc")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] +use alloc::{rc::Weak as WeakRc, sync::Weak as WeakArc}; +#[cfg(feature = "alloc")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] pub use alloc::{rc::Rc, sync::Arc}; /// Pointer Family -pub trait PointerFamily<T>: sealed::Sealed { +pub trait PointerFamily<T> { /// Strong Pointer type Strong: AsRef<T> + Borrow<T> + Deref<Target = T>; @@ -37,7 +37,7 @@ pub trait PointerFamily<T>: sealed::Sealed { /// Returns a new strong pointer holding `base`. fn new(base: T) -> Self::Strong; - /// Claims ownership of the underlying merkle tree from `strong`. + /// Claims ownership of the underlying owned value from `strong`. /// /// # Panics /// @@ -60,7 +60,8 @@ pub trait PointerFamily<T>: sealed::Sealed { /// Implements [`PointerFamily`] for `$type` with `$strong` and `$weak` pointers. macro_rules! impl_pointer_family { ($type:tt, $strong:ident, $weak:ident) => { - seal!($type); + #[cfg(feature = "alloc")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] impl<T> PointerFamily<T> for $type { type Strong = $strong<T>; type Weak = $weak<T>; diff --git a/manta-util/src/serde.rs b/manta-util/src/serde.rs deleted file mode 100644 index 1807e0aae..000000000 --- a/manta-util/src/serde.rs +++ /dev/null @@ -1,437 +0,0 @@ -// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>. - -//! Serialization and Deserialization Utilities - -use alloc::vec::Vec; -use core::{convert::Infallible, fmt::Debug, hash::Hash}; - -/// Reader -pub trait Read { - /// Error Type - type Error; - - /// Reads bytes from `self`, pushing them to `output` until exhausting the buffer inside of - /// `output`. - fn read<T>(&mut self, output: &mut T) -> Result<(), Self::Error> - where - T: AsMut<[u8]> + ?Sized; - - /// Reads all bytes from `self`, pushing them to `output`. - fn read_all(&mut self, output: &mut Vec<u8>) -> Result<(), Self::Error>; -} - -impl<R> Read for &mut R -where - R: Read, -{ - type Error = R::Error; - - #[inline] - fn read<T>(&mut self, output: &mut T) -> Result<(), Self::Error> - where - T: AsMut<[u8]> + ?Sized, - { - (*self).read(output) - } - - #[inline] - fn read_all(&mut self, output: &mut Vec<u8>) -> Result<(), Self::Error> { - (*self).read_all(output) - } -} - -impl Read for &[u8] { - type Error = usize; - - #[inline] - fn read<T>(&mut self, output: &mut T) -> Result<(), Self::Error> - where - T: AsMut<[u8]> + ?Sized, - { - let output = output.as_mut(); - let output_len = output.len(); - let len = self.len(); - if output_len > len { - return Err(output_len - len); - } - output.copy_from_slice(&self[..output_len]); - *self = &self[output_len..]; - Ok(()) - } - - #[inline] - fn read_all(&mut self, output: &mut Vec<u8>) -> Result<(), Self::Error> { - output - .write(self) - .map_err(|_| unreachable!("Infallible cannot be constructed.")) - } -} - -impl<const N: usize> Read for [u8; N] { - type Error = usize; - - #[inline] - fn read<T>(&mut self, output: &mut T) -> Result<(), Self::Error> - where - T: AsMut<[u8]> + ?Sized, - { - let output = output.as_mut(); - let output_len = output.len(); - let len = self.len(); - if output_len > len { - return Err(output_len - len); - } - output.copy_from_slice(&self[..output_len]); - Ok(()) - } - - #[inline] - fn read_all(&mut self, output: &mut Vec<u8>) -> Result<(), Self::Error> { - output - .write(&mut self.as_ref()) - .map_err(|_| unreachable!("Infallible cannot be constructed.")) - } -} - -impl Read for Vec<u8> { - type Error = usize; - - #[inline] - fn read<T>(&mut self, output: &mut T) -> Result<(), Self::Error> - where - T: AsMut<[u8]> + ?Sized, - { - let mut slice = self.as_slice(); - slice.read(output)?; - let len = slice.len(); - self.drain(..(self.len() - len)); - Ok(()) - } - - #[inline] - fn read_all(&mut self, output: &mut Vec<u8>) -> Result<(), Self::Error> { - output - .write_drain(self) - .map_err(|_| unreachable!("Infallible cannot be constructed.")) - } -} - -/// I/O Reader -#[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct IoReader<R>( - /// Reader - pub R, -) -where - R: std::io::Read; - -#[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] -impl<R> Read for IoReader<R> -where - R: std::io::Read, -{ - type Error = std::io::Error; - - #[inline] - fn read<T>(&mut self, output: &mut T) -> Result<(), Self::Error> - where - T: AsMut<[u8]> + ?Sized, - { - self.0.read_exact(output.as_mut()) - } - - #[inline] - fn read_all(&mut self, output: &mut Vec<u8>) -> Result<(), Self::Error> { - self.0.read_to_end(output).map(|_| ()) - } -} - -/// Writer -pub trait Write { - /// Error Type - type Error; - - /// Writes bytes into `self`, pulling them from `input` until exhausting the buffer inside of - /// `self`. - fn write(&mut self, input: &mut &[u8]) -> Result<(), Self::Error>; - - /// Writes bytes into `self` from the bytes of `input`, returning the bytes which were not - /// written. - #[inline] - fn write_ref<'t, T>(&mut self, input: &'t T) -> Result<&'t [u8], Self::Error> - where - T: AsRef<[u8]> + ?Sized, - { - let mut slice = input.as_ref(); - self.write(&mut slice)?; - Ok(slice) - } - - /// Writes bytes into `self` from an `input` vector of bytes. - /// - /// # Implementation Note - /// - /// This method is here to provide an optimization path against the [`Vec`] implemention of - /// [`Write`]. The default implementation should be efficient enough for other cases. - #[inline] - fn write_drain(&mut self, input: &mut Vec<u8>) -> Result<(), Self::Error> { - let slice_len = self.write_ref(input)?.len(); - input.drain(..(input.len() - slice_len)); - Ok(()) - } -} - -impl<W> Write for &mut W -where - W: Write, -{ - type Error = W::Error; - - #[inline] - fn write(&mut self, input: &mut &[u8]) -> Result<(), Self::Error> { - (*self).write(input) - } - - #[inline] - fn write_ref<'t, T>(&mut self, input: &'t T) -> Result<&'t [u8], Self::Error> - where - T: AsRef<[u8]> + ?Sized, - { - (*self).write_ref(input) - } - - #[inline] - fn write_drain(&mut self, input: &mut Vec<u8>) -> Result<(), Self::Error> { - (*self).write_drain(input) - } -} - -impl Write for [u8] { - type Error = usize; - - #[inline] - fn write(&mut self, input: &mut &[u8]) -> Result<(), Self::Error> { - input.read(self) - } -} - -impl<const N: usize> Write for [u8; N] { - type Error = usize; - - #[inline] - fn write(&mut self, input: &mut &[u8]) -> Result<(), Self::Error> { - input.read(self) - } -} - -impl Write for Vec<u8> { - type Error = Infallible; - - #[inline] - fn write(&mut self, input: &mut &[u8]) -> Result<(), Self::Error> { - self.reserve(input.len()); - self.extend_from_slice(*input); - Ok(()) - } - - #[inline] - fn write_drain(&mut self, input: &mut Vec<u8>) -> Result<(), Self::Error> { - self.append(input); - Ok(()) - } -} - -/// I/O Writer -#[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct IoWriter<W>( - /// Writer - pub W, -) -where - W: std::io::Write; - -#[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] -impl<W> Write for IoWriter<W> -where - W: std::io::Write, -{ - type Error = std::io::Error; - - #[inline] - fn write(&mut self, input: &mut &[u8]) -> Result<(), Self::Error> { - self.0.write_all(input)?; - *input = &input[..0]; - Ok(()) - } -} - -/// Serialization -pub trait Serialize<C = ()> { - /// Appends representation of `self` in bytes to `buffer`. - fn serialize<W>(&self, writer: W) -> Result<(), W::Error> - where - W: Write; - - /// Converts `self` into a vector of bytes. - #[inline] - fn to_vec(&self) -> Vec<u8> { - let mut buffer = Vec::new(); - self.serialize(&mut buffer) - .expect("Writing to a `Vec<u8>` cannot fail."); - buffer - } -} - -impl Serialize for u8 { - #[inline] - fn serialize<W>(&self, mut writer: W) -> Result<(), W::Error> - where - W: Write, - { - writer.write_ref(&[*self])?; - Ok(()) - } -} - -impl<T, C> Serialize<C> for [T] -where - T: Serialize<C>, -{ - #[inline] - fn serialize<W>(&self, mut writer: W) -> Result<(), W::Error> - where - W: Write, - { - for item in self { - item.serialize(&mut writer)?; - } - Ok(()) - } -} - -impl<T, C, const N: usize> Serialize<C> for [T; N] -where - T: Serialize<C>, -{ - #[inline] - fn serialize<W>(&self, mut writer: W) -> Result<(), W::Error> - where - W: Write, - { - for item in self { - item.serialize(&mut writer)?; - } - Ok(()) - } -} - -/// Exact Size Serialization -pub trait SerializeExactSize<C, const N: usize>: Serialize<C> { - /// Converts `self` into an exactly known byte array. - #[inline] - fn to_array(&self) -> [u8; N] { - let mut buffer = [0; N]; - self.serialize(&mut buffer) - .expect("The implementation of this trait means that this cannot fail."); - buffer - } -} - -/// Deserialization Error -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "R::Error: Clone, D::Error: Clone"), - Copy(bound = "R::Error: Copy, D::Error: Copy"), - Debug(bound = "R::Error: Debug, D::Error: Debug"), - Eq(bound = "R::Error: Eq, D::Error: Eq"), - Hash(bound = "R::Error: Hash, D::Error: Hash"), - PartialEq(bound = "R::Error: PartialEq, D::Error: PartialEq") -)] -pub enum Error<R, D, C = ()> -where - R: Read + ?Sized, - D: Deserialize<C> + ?Sized, -{ - /// Reading Error - /// - /// See [`Read`] for more. - Read(R::Error), - - /// Deserialization Error - /// - /// See [`Deserialize`] for more. - Deserialize(D::Error), -} - -impl<R, D, C> Error<R, D, C> -where - R: Read + ?Sized, - D: Deserialize<C> + ?Sized, -{ - /// Converts `self` into an option over [`R::Error`](Read::Error). - #[inline] - pub fn read(self) -> Option<R::Error> { - match self { - Self::Read(err) => Some(err), - _ => None, - } - } - - /// Converts `self` into an option over [`D::Error`](Deserialize::Error). - #[inline] - pub fn deserialize(self) -> Option<D::Error> { - match self { - Self::Deserialize(err) => Some(err), - _ => None, - } - } -} - -/// Deserialization -pub trait Deserialize<C = ()>: Sized { - /// Error Type - type Error; - - /// Parses the input `buffer` into a concrete value of type `Self` if possible. - fn deserialize<R>(reader: R) -> Result<Self, Error<R, Self, C>> - where - R: Read; - - /// Converts a byte vector into a concrete value of type `Self` if possible. - #[inline] - fn from_vec(buffer: Vec<u8>) -> Result<Self, Self::Error> { - Self::deserialize(buffer) - .map_err(move |err| err.deserialize().expect("Reading from `[u8]` cannot fail.")) - } -} - -/// Exact Size Deserialization -pub trait DeserializeExactSize<C, const N: usize>: Deserialize<C> { - /// Converts a fixed-length byte array into a concrete value of type `Self`. - #[inline] - fn from_array(buffer: [u8; N]) -> Self { - Self::deserialize(buffer) - .ok() - .expect("The implementation of this trait means that this cannot fail.") - } -} From 21d4c85979bc9e2b3c7326c1643598381acec8af Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 20 Jan 2022 00:10:42 -0500 Subject: [PATCH 196/275] wip: start building parameter generation script --- manta-pay/README.md | 9 ++ manta-pay/src/bin/generate_parameters.rs | 137 ++++++++++++------ .../constraint/arkworks/proof_system.rs | 2 +- manta-pay/src/wallet/cache.rs | 19 +-- 4 files changed, 107 insertions(+), 60 deletions(-) diff --git a/manta-pay/README.md b/manta-pay/README.md index 6d90a370a..d392fae63 100644 --- a/manta-pay/README.md +++ b/manta-pay/README.md @@ -1 +1,10 @@ # manta-pay + +## Parameters + +To generate the public parameters use the following command: + +```sh +cargo run --release --all-features generate_parameters +``` + diff --git a/manta-pay/src/bin/generate_parameters.rs b/manta-pay/src/bin/generate_parameters.rs index 99e90940c..2d5fc1586 100644 --- a/manta-pay/src/bin/generate_parameters.rs +++ b/manta-pay/src/bin/generate_parameters.rs @@ -16,72 +16,123 @@ //! Generate Parameters -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; -use manta_accounting::transfer::Parameters; +// TODO: Specify target directory in `main`. +// TODO: Deduplicate the per-circuit proving context and verifying context serialization code. + +use ark_serialize::CanonicalSerialize; use manta_crypto::{ - accumulator::Accumulator, - constraint::{measure::Measure, ProofSystem as _}, + constraint::ProofSystem as _, rand::{Rand, SeedableRng}, }; -use manta_pay::{ - config::{FullParameters, Mint, PrivateTransfer, ProofSystem, Reclaim, VerifyingContext}, - wallet::UtxoSet, -}; +use manta_pay::config::{FullParameters, Mint, PrivateTransfer, ProofSystem, Reclaim}; use rand_chacha::ChaCha20Rng; use std::{fs::OpenOptions, io}; +/// Parameter Generation Seed +/// +/// This is a nothing-up-my-sleve parameter generation number. Its just the numbers from `0` to `31` +/// as `u8` bytes. +/// +/// # Warning /// +/// Right now, this seed is also used to generate to the proving and verifying keys for the ZKP +/// circuits. This is not safe, and a real system must use a Multi-Party-Computation to arrive at +/// the ZKP parameters. +pub const SEED: [u8; 32] = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, +]; + +/// Generates the parameters using the [`SEED`] and saves them to the filesystem. #[inline] pub fn main() -> io::Result<()> { - let mut rng = ChaCha20Rng::from_seed([ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, - ]); + let mut rng = ChaCha20Rng::from_seed(SEED); let parameters = rng.gen(); - let mut utxo_set = UtxoSet::new(rng.gen()); - - /* - let mut bytes = Vec::new(); + let utxo_set_parameters = rng.gen(); + /* TODO: let Parameters { key_agreement, utxo_commitment, void_number_hash, } = &parameters; - key_agreement.generator().0.serialize(&mut bytes).unwrap(); - utxo_commitment.0.serialize(&mut bytes).unwrap(); - void_number_hash.0.serialize(&mut bytes).unwrap(); - - println!("Parameters: {:?}", bytes.as_slice()); - - let mut bytes = Vec::new(); - utxo_set_parameters.serialize(&mut bytes).unwrap(); - println!("UTXO Set Parameters: {:?}", bytes.as_slice()); + key_agreement.generator().serialize(&mut bytes).unwrap(); + utxo_commitment.serialize(&mut bytes).unwrap(); + void_number_hash.serialize(&mut bytes).unwrap(); + + utxo_set_parameters + .serialize( + &mut OpenOptions::new() + .create(true) + .write(true) + .open("utxo-set-parameters.dat")?, + ) + .unwrap(); */ - let cs = Mint::unknown_constraints(FullParameters::new(&parameters, utxo_set.model())); - // println!("Mint: {:#?}", cs.measure()); - - let mut mint_file = OpenOptions::new() - .read(true) - .create(true) - .write(true) - .open("mint")?; + let full_parameters = FullParameters::new(&parameters, &utxo_set_parameters); + let cs = Mint::unknown_constraints(full_parameters); let (proving_context, verifying_context) = ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); - - verifying_context.serialize(&mut mint_file).unwrap(); - - // let cs = PrivateTransfer::unknown_constraints(full_parameters); - // println!("PrivateTransfer: {:#?}", cs.measure()); - // let _ = ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); - - // let cs = Reclaim::unknown_constraints(full_parameters); - // println!("Reclaim: {:#?}", cs.measure()); - // let _ = ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); + proving_context + .serialize( + &mut OpenOptions::new() + .create(true) + .write(true) + .open("mint.pk")?, + ) + .unwrap(); + verifying_context + .serialize( + &mut OpenOptions::new() + .create(true) + .write(true) + .open("mint.vk")?, + ) + .unwrap(); + + let cs = PrivateTransfer::unknown_constraints(full_parameters); + let (proving_context, verifying_context) = + ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); + proving_context + .serialize( + &mut OpenOptions::new() + .create(true) + .write(true) + .open("private-transfer.pk")?, + ) + .unwrap(); + verifying_context + .serialize( + &mut OpenOptions::new() + .create(true) + .write(true) + .open("private-transfer.vk")?, + ) + .unwrap(); + + let cs = Reclaim::unknown_constraints(full_parameters); + let (proving_context, verifying_context) = + ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); + proving_context + .serialize( + &mut OpenOptions::new() + .create(true) + .write(true) + .open("reclaim.pk")?, + ) + .unwrap(); + verifying_context + .serialize( + &mut OpenOptions::new() + .create(true) + .write(true) + .open("reclaim.vk")?, + ) + .unwrap(); Ok(()) } diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs index 175b40b48..3a404d5ae 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs @@ -276,7 +276,7 @@ pub mod groth16 { impl<E> scale_codec::EncodeLike for Proof<E> where E: PairingEngine {} /// Proving Context - #[derive(derivative::Derivative)] + #[derive(derivative::Derivative, CanonicalSerialize, CanonicalDeserialize)] #[derivative(Clone, Debug, Eq, PartialEq)] pub struct ProvingContext<E>(pub ProvingKey<E>) where diff --git a/manta-pay/src/wallet/cache.rs b/manta-pay/src/wallet/cache.rs index 89e3470eb..1cf56dede 100644 --- a/manta-pay/src/wallet/cache.rs +++ b/manta-pay/src/wallet/cache.rs @@ -16,10 +16,7 @@ //! Proving Context Caching -use crate::{ - config::{MultiProvingContext, ProvingContext}, - crypto::constraint::arkworks::groth16::ProvingKey, -}; +use crate::config::{MultiProvingContext, ProvingContext}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; use async_std::{ io, @@ -103,12 +100,7 @@ impl OnDiskMultiProvingContext { Ok(task::spawn_blocking(move || { File::open(path.as_ref()) .map_err(Error::Io) - .and_then(move |f| { - // FIXME: Move to new serde platform so we don't need to specify the key here. - ProvingKey::deserialize_unchecked(f) - .map(ProvingContext::new) - .map_err(Error::Serialization) - }) + .and_then(move |f| ProvingContext::deserialize(f).map_err(Error::Serialization)) }) .await?) } @@ -125,12 +117,7 @@ impl OnDiskMultiProvingContext { .create(true) .open(path.as_ref()) .map_err(Error::Io) - .and_then(move |f| { - context - .0 - .serialize_unchecked(f) - .map_err(Error::Serialization) - }) + .and_then(move |f| context.serialize(f).map_err(Error::Serialization)) }) .await?) } From 339c02ee45ce68f2e3cde7457f605ab19cc3e717 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 21 Jan 2022 14:44:31 -0500 Subject: [PATCH 197/275] fix: add ArkReader/Writer for Encode/Decode impls --- manta-pay/src/bin/generate_parameters.rs | 26 ++-- manta-pay/src/config.rs | 2 +- .../constraint/arkworks/constraint_system.rs | 117 +++++++++++++++--- .../constraint/arkworks/proof_system.rs | 43 ++++++- manta-pay/src/wallet/cache.rs | 6 +- 5 files changed, 161 insertions(+), 33 deletions(-) diff --git a/manta-pay/src/bin/generate_parameters.rs b/manta-pay/src/bin/generate_parameters.rs index 2d5fc1586..b923e0f60 100644 --- a/manta-pay/src/bin/generate_parameters.rs +++ b/manta-pay/src/bin/generate_parameters.rs @@ -18,6 +18,7 @@ // TODO: Specify target directory in `main`. // TODO: Deduplicate the per-circuit proving context and verifying context serialization code. +// TODO: Print some statistics about the parameters and circuits and into a stats file as well. use ark_serialize::CanonicalSerialize; use manta_crypto::{ @@ -25,6 +26,7 @@ use manta_crypto::{ rand::{Rand, SeedableRng}, }; use manta_pay::config::{FullParameters, Mint, PrivateTransfer, ProofSystem, Reclaim}; +use manta_util::codec::{Encode, IoWriter}; use rand_chacha::ChaCha20Rng; use std::{fs::OpenOptions, io}; @@ -79,19 +81,19 @@ pub fn main() -> io::Result<()> { ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); proving_context .serialize( - &mut OpenOptions::new() + OpenOptions::new() .create(true) .write(true) .open("mint.pk")?, ) .unwrap(); verifying_context - .serialize( - &mut OpenOptions::new() + .encode(IoWriter( + OpenOptions::new() .create(true) .write(true) .open("mint.vk")?, - ) + )) .unwrap(); let cs = PrivateTransfer::unknown_constraints(full_parameters); @@ -99,19 +101,19 @@ pub fn main() -> io::Result<()> { ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); proving_context .serialize( - &mut OpenOptions::new() + OpenOptions::new() .create(true) .write(true) .open("private-transfer.pk")?, ) .unwrap(); verifying_context - .serialize( - &mut OpenOptions::new() + .encode(IoWriter( + OpenOptions::new() .create(true) .write(true) .open("private-transfer.vk")?, - ) + )) .unwrap(); let cs = Reclaim::unknown_constraints(full_parameters); @@ -119,19 +121,19 @@ pub fn main() -> io::Result<()> { ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); proving_context .serialize( - &mut OpenOptions::new() + OpenOptions::new() .create(true) .write(true) .open("reclaim.pk")?, ) .unwrap(); verifying_context - .serialize( - &mut OpenOptions::new() + .encode(IoWriter( + OpenOptions::new() .create(true) .write(true) .open("reclaim.vk")?, - ) + )) .unwrap(); Ok(()) diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 0aacc39d8..953893b36 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -612,7 +612,7 @@ impl KeyDerivationFunction for HierarchicalKeyDerivationFunction { .private_key() .to_bytes() .try_into() - .expect("The private key has 32 bytes."); + .expect("The secret key has 32 bytes."); Fp(<Bls12_381_Edwards as ProjectiveCurve>::ScalarField::from_le_bytes_mod_order(&bytes)) } } diff --git a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs index 2b33c9e0e..dc2dd23a4 100644 --- a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs @@ -329,11 +329,12 @@ where /// Codec Utilities pub mod codec { - use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::io::{self, Error, ErrorKind}; use manta_util::codec::{Read, ReadExactError, Write}; use scale_codec::Input; + pub use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; + /// Arkworks Encoding Marker #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Ark; @@ -373,42 +374,124 @@ pub mod codec { } /// Arkworks Reader - #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct ArkReader<R>(pub R) + pub struct ArkReader<R> where - R: Read; + R: Read, + { + /// Reader State + state: Result<R, R::Error>, + } + + impl<R> ArkReader<R> + where + R: Read, + { + /// Builds a new [`ArkReader`] from `reader`. + #[inline] + pub fn new(reader: R) -> Self { + Self { state: Ok(reader) } + } + + /// Updates the internal reader state by performing the `f` computation. + #[inline] + fn update<T, F>(&mut self, f: F) -> Option<T> + where + F: FnOnce(&mut R) -> Result<T, R::Error>, + { + if let Ok(reader) = self.state.as_mut() { + match f(reader) { + Ok(value) => return Some(value), + Err(err) => self.state = Err(err), + } + } + None + } + + /// Returns the reader state back or an error if it occured during any [`Read`](io::Read) + /// methods. + #[inline] + pub fn finish(self) -> Result<R, R::Error> { + self.state + } + } impl<R> io::Read for ArkReader<R> where - R: Read<Error = Error>, + R: Read, { #[inline] fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> { - self.0.read(buf) + self.update(|reader| reader.read(buf)) + .ok_or_else(|| Error::new(ErrorKind::Other, "Reading Error")) } #[inline] fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Error> { - self.0.read_exact(buf).map_err(|err| match err { - ReadExactError::UnexpectedEnd(_) => todo!(), - ReadExactError::Read(err) => err, - }) + match self.update(|reader| match reader.read_exact(buf) { + Ok(value) => Ok(Ok(value)), + Err(ReadExactError::Read(err)) => Err(err), + Err(ReadExactError::UnexpectedEnd(err)) => Ok(Err(err)), + }) { + Some(Ok(_)) => Ok(()), + Some(Err(_)) => Err(Error::new( + ErrorKind::UnexpectedEof, + "Unexpected end of buffer.", + )), + _ => Err(Error::new(ErrorKind::Other, "Reading Error")), + } } } /// Arkworks Writer - #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct ArkWriter<W>(pub W) + pub struct ArkWriter<W> where - W: Write; + W: Write, + { + /// Writer State + state: Result<W, W::Error>, + } + + impl<W> ArkWriter<W> + where + W: Write, + { + /// Builds a new [`ArkWriter`] from `writer`. + #[inline] + pub fn new(writer: W) -> Self { + Self { state: Ok(writer) } + } + + /// Updates the internal writer state by performing the `f` computation. + #[inline] + fn update<T, F>(&mut self, f: F) -> Option<T> + where + F: FnOnce(&mut W) -> Result<T, W::Error>, + { + if let Ok(writer) = self.state.as_mut() { + match f(writer) { + Ok(value) => return Some(value), + Err(err) => self.state = Err(err), + } + } + None + } + + /// Returns the writer state back or an error if it occured during any [`Write`](io::Write) + /// methods. + #[inline] + pub fn finish(self) -> Result<W, W::Error> { + self.state + } + } impl<W> io::Write for ArkWriter<W> where - W: Write<Error = Error>, + W: Write, { #[inline] fn write(&mut self, mut buf: &[u8]) -> Result<usize, Error> { - self.0.write(&mut buf) + self.update(|writer| writer.write(&mut buf)) + .ok_or_else(|| Error::new(ErrorKind::Other, "Writing Error")) } #[inline] @@ -418,8 +501,8 @@ pub mod codec { } #[inline] - fn write_all(&mut self, mut buf: &[u8]) -> Result<(), Error> { - self.0.write(&mut buf)?; + fn write_all(&mut self, buf: &[u8]) -> Result<(), Error> { + let _ = self.write(buf)?; if buf.is_empty() { Ok(()) } else { diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs index 3a404d5ae..ac4618bc5 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs @@ -51,6 +51,7 @@ pub mod pairing { /// BLS-12 Utilities pub mod bls12 { use crate::crypto::constraint::arkworks::codec::{HasDeserialization, HasSerialization}; + use alloc::vec::Vec; use ark_ec::models::bls12::{g2, Bls12Parameters}; use ark_ff::Fp2; use ark_serialize::{ @@ -215,7 +216,7 @@ pub mod groth16 { use super::*; use crate::crypto::constraint::arkworks::{ self, - codec::{HasDeserialization, HasSerialization}, + codec::{ArkReader, ArkWriter, HasDeserialization, HasSerialization}, SynthesisError, }; use alloc::vec::Vec; @@ -230,6 +231,7 @@ pub mod groth16 { constraint::ProofSystem, rand::{CryptoRng, RngCore, SizedRng}, }; + use manta_util::codec::{self, DecodeError}; pub use ark_groth16::ProvingKey; @@ -465,6 +467,45 @@ pub mod groth16 { } } + impl<E> codec::Encode for VerifyingContext<E> + where + E: PairingEngine, + for<'s> E::G2Prepared: HasSerialization<'s>, + { + #[inline] + fn encode<W>(&self, writer: W) -> Result<(), W::Error> + where + W: codec::Write, + { + let mut writer = ArkWriter::new(writer); + let _ = self.serialize(&mut writer); + writer.finish().map(move |_| ()) + } + } + + impl<E> codec::Decode for VerifyingContext<E> + where + E: PairingEngine, + E::G2Prepared: HasDeserialization, + { + type Error = SerializationError; + + #[inline] + fn decode<R>(reader: R) -> Result<Self, DecodeError<R, Self>> + where + R: codec::Read, + { + let mut reader = ArkReader::new(reader); + match CanonicalDeserialize::deserialize(&mut reader) { + Ok(value) => reader + .finish() + .map(move |_| value) + .map_err(DecodeError::Read), + Err(err) => Err(DecodeError::Decode(err)), + } + } + } + /// Arkworks Groth16 Proof System #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] diff --git a/manta-pay/src/wallet/cache.rs b/manta-pay/src/wallet/cache.rs index 1cf56dede..8a45b58fd 100644 --- a/manta-pay/src/wallet/cache.rs +++ b/manta-pay/src/wallet/cache.rs @@ -100,7 +100,9 @@ impl OnDiskMultiProvingContext { Ok(task::spawn_blocking(move || { File::open(path.as_ref()) .map_err(Error::Io) - .and_then(move |f| ProvingContext::deserialize(f).map_err(Error::Serialization)) + .and_then(move |f| { + ProvingContext::deserialize_unchecked(f).map_err(Error::Serialization) + }) }) .await?) } @@ -117,7 +119,7 @@ impl OnDiskMultiProvingContext { .create(true) .open(path.as_ref()) .map_err(Error::Io) - .and_then(move |f| context.serialize(f).map_err(Error::Serialization)) + .and_then(move |f| context.serialize_unchecked(f).map_err(Error::Serialization)) }) .await?) } From 43a46cf64a20fe62705b66e128bae1219e6e5489 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 23 Jan 2022 13:23:24 -0500 Subject: [PATCH 198/275] fix: add encode/decode impls and remove dead code --- manta-accounting/Cargo.toml | 3 +- manta-accounting/src/asset.rs | 109 ++---------------- manta-accounting/src/transfer/mod.rs | 22 ---- manta-crypto/Cargo.toml | 1 - manta-crypto/src/ecc.rs | 29 +++++ manta-crypto/src/merkle_tree/node.rs | 3 - manta-crypto/src/merkle_tree/path.rs | 47 -------- manta-crypto/src/merkle_tree/tree.rs | 63 +++++++++- manta-pay/Cargo.toml | 1 - manta-pay/src/bin/generate_parameters.rs | 98 ++++++++++++---- manta-pay/src/config.rs | 47 +++++++- .../constraint/arkworks/constraint_system.rs | 60 +++++++--- .../constraint/arkworks/proof_system.rs | 71 +++++++++--- manta-pay/src/crypto/ecc.rs | 54 +++++++-- manta-pay/src/crypto/hash/poseidon.rs | 46 +++++++- manta-util/src/codec.rs | 100 +++++++--------- 16 files changed, 453 insertions(+), 301 deletions(-) diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 6cf248f80..c043a82b8 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -41,9 +41,8 @@ derivative = { version = "2.2.0", default-features = false, features = ["use_cor derive_more = { version = "0.99.16", default-features = false, features = ["add", "add_assign", "display", "from", "mul", "mul_assign", "sum"] } futures = { version = "0.3.19", optional = true, default-features = false, features = ["alloc"] } indexmap = { version = "1.8.0", optional = true, default-features = false } -manta-crypto = { path = "../manta-crypto" } +manta-crypto = { path = "../manta-crypto", default-features = false } manta-util = { path = "../manta-util", features = ["alloc"] } -serde = { version = "1.0.133", optional = true, default-features = false, features = ["derive"] } statrs = { version = "0.15.0", optional = true, default-features = false } rand = { version = "0.8.4", optional = true, default-features = false } zeroize = { version = "1.4.3", optional = true, default-features = false, features = ["alloc"] } diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 37b2c9b00..e3398a6ca 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -26,11 +26,11 @@ use core::{ fmt::Debug, hash::Hash, iter::{self, FusedIterator, Sum}, - ops::{Add, AddAssign, Deref, Mul, Sub, SubAssign}, + ops::{Add, AddAssign, Deref, Sub, SubAssign}, slice, }; use derive_more::{ - Add, AddAssign, Display, Div, DivAssign, From, Mul, MulAssign, Product, Sub, SubAssign, Sum, + Add, AddAssign, Display, Div, DivAssign, From, Mul, MulAssign, Sub, SubAssign, Sum, }; use manta_crypto::{ constraint::{Allocator, Secret, ValueSource, Variable}, @@ -48,7 +48,6 @@ use std::{ pub type AssetIdType = u32; /// Asset Id Type -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Clone, Copy, Debug, Default, Display, Eq, From, Hash, Ord, PartialEq, PartialOrd)] #[from(forward)] pub struct AssetId( @@ -119,7 +118,6 @@ where pub type AssetValueType = u128; /// Asset Value Type -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive( Add, AddAssign, @@ -138,7 +136,6 @@ pub type AssetValueType = u128; Ord, PartialEq, PartialOrd, - Product, Sub, SubAssign, Sum, @@ -197,12 +194,6 @@ impl AssetValue { _ => None, } } - - /// Returns an iterator over change amounts in `n` parts. - #[inline] - pub const fn make_change(self, n: usize) -> Option<Change> { - Change::new(self.0, n) - } } impl From<AssetValue> for [u8; AssetValue::SIZE] { @@ -212,15 +203,6 @@ impl From<AssetValue> for [u8; AssetValue::SIZE] { } } -impl Mul<AssetValue> for AssetValueType { - type Output = AssetValueType; - - #[inline] - fn mul(self, rhs: AssetValue) -> Self::Output { - self * rhs.0 - } -} - impl PartialEq<AssetValueType> for AssetValue { #[inline] fn eq(&self, rhs: &AssetValueType) -> bool { @@ -251,71 +233,7 @@ impl<'a> Sum<&'a AssetValue> for AssetValue { } } -/// Change Iterator -/// -/// An iterator over [`AssetValue`] change amounts. -/// -/// This `struct` is created by the [`make_change`](AssetValue::make_change) method on -/// [`AssetValue`]. See its documentation for more. -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] -pub struct Change { - /// Base Amount - base: AssetValueType, - - /// Remainder to be Divided - remainder: usize, - - /// Total Number of Units - units: usize, - - /// Current Index - index: usize, -} - -impl Change { - /// Builds a new [`Change`] iterator for `amount` into `n` pieces. - #[inline] - const fn new(amount: AssetValueType, n: usize) -> Option<Self> { - let n_div = n as AssetValueType; - match amount.checked_div(n_div) { - Some(base) => Some(Self { - base, - remainder: (amount % n_div) as usize, - units: n, - index: 0, - }), - _ => None, - } - } -} - -impl Iterator for Change { - type Item = AssetValue; - - #[inline] - fn next(&mut self) -> Option<Self::Item> { - if self.index >= self.units { - return None; - } - let amount = self.base + (self.index < self.remainder) as u128; - self.index += 1; - Some(AssetValue(amount)) - } - - #[inline] - fn size_hint(&self) -> (usize, Option<usize>) { - let len = self.units - self.index; - (len, Some(len)) - } -} - -impl ExactSizeIterator for Change {} - -impl FusedIterator for Change {} - /// Asset -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Clone, Copy, Debug, Default, Display, Eq, From, Hash, Ord, PartialEq, PartialOrd)] #[display(fmt = "{{id: {}, value: {}}}", id, value)] pub struct Asset<I = AssetId, V = AssetValue> { @@ -1013,6 +931,8 @@ where } } +impl<'s, M> FusedIterator for SelectionIter<'s, M> where M: AssetMap + ?Sized {} + /// [`SelectionKeys`] Map Function Type type SelectionKeysMapFnType<'s, M> = fn(&'s (<M as AssetMap>::Key, AssetValue)) -> &'s <M as AssetMap>::Key; @@ -1063,11 +983,13 @@ where } } +impl<'s, M> FusedIterator for SelectionKeys<'s, M> where M: AssetMap + ?Sized {} + /// Testing Suite #[cfg(test)] mod test { use super::*; - use rand::{thread_rng, Rng}; + use rand::thread_rng; /// Tests asset conversion into and from bytes. #[test] @@ -1084,24 +1006,11 @@ mod test { #[test] fn asset_arithmetic() { let mut rng = thread_rng(); - let mut asset = Asset::zero(AssetId::gen(&mut rng)); - let value = AssetValue::gen(&mut rng); + let mut asset = Asset::zero(rng.gen()); + let value = rng.gen(); let _ = asset + value; asset += value; let _ = asset - value; asset -= value; } - - /// Tests that the [`Change`] iterator makes the correct change. - #[test] - fn test_change_iterator() { - let mut rng = thread_rng(); - for _ in 0..0xFFF { - let amount = AssetValue(rng.gen_range(0..0xFFFF_FFFF)); - let n = rng.gen_range(1..0xFFFF); - let change = amount.make_change(n).unwrap().collect::<Vec<_>>(); - assert_eq!(n, change.len()); - assert_eq!(amount, change.into_iter().sum::<AssetValue>()); - } - } } diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index 6ab221577..6aef7c382 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -354,29 +354,7 @@ pub type ProofInput<C> = <<C as Configuration>::ProofSystem as ProofSystem>::Inp /// Transfer Validity Proof Type pub type Proof<C> = <ProofSystemType<C> as ProofSystem>::Proof; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - /// Transfer Parameters -#[cfg_attr( - feature = "serde", - derive(Deserialize, Serialize), - serde( - bound( - deserialize = r" - C::KeyAgreementScheme: Deserialize<'de>, - C::UtxoCommitmentScheme: Deserialize<'de>, - C::VoidNumberHashFunction: Deserialize<'de>, - ", - serialize = r" - C::KeyAgreementScheme: Serialize, - C::UtxoCommitmentScheme: Serialize, - C::VoidNumberHashFunction: Serialize, - ", - ), - deny_unknown_fields, - ) -)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = r" diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index 3487f8189..ebc8e3aef 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -38,7 +38,6 @@ test = [] derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } manta-util = { path = "../manta-util", features = ["alloc"] } rand_core = { version = "0.6.3", default-features = false } -serde = { version = "1.0.133", optional = true, default-features = false, features = ["derive"] } [dev-dependencies] rand = "0.8.4" diff --git a/manta-crypto/src/ecc.rs b/manta-crypto/src/ecc.rs index c983ff0b7..e82fd0d28 100644 --- a/manta-crypto/src/ecc.rs +++ b/manta-crypto/src/ecc.rs @@ -24,6 +24,7 @@ use crate::{ rand::{CryptoRng, RngCore, Sample}, }; use core::marker::PhantomData; +use manta_util::codec::{Decode, DecodeError, Encode, Read, Write}; /// Elliptic Curve Group pub trait Group<COM = ()>: Sized { @@ -104,6 +105,34 @@ where } } +impl<G, COM, CODEC> Decode<CODEC> for DiffieHellman<G, COM> +where + G: Group<COM> + Decode<CODEC>, +{ + type Error = G::Error; + + #[inline] + fn decode<R>(reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> + where + R: Read, + { + Ok(Self::new(G::decode(reader)?)) + } +} + +impl<G, COM, CODEC> Encode<CODEC> for DiffieHellman<G, COM> +where + G: Group<COM> + Encode<CODEC>, +{ + #[inline] + fn encode<W>(&self, writer: W) -> Result<(), W::Error> + where + W: Write, + { + self.generator.encode(writer) + } +} + impl<G, COM> KeyAgreementScheme<COM> for DiffieHellman<G, COM> where G: Group<COM>, diff --git a/manta-crypto/src/merkle_tree/node.rs b/manta-crypto/src/merkle_tree/node.rs index c8ac18deb..4cf6a5fa1 100644 --- a/manta-crypto/src/merkle_tree/node.rs +++ b/manta-crypto/src/merkle_tree/node.rs @@ -23,7 +23,6 @@ use core::{ }; /// Parity of a Subtree -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub enum Parity { /// Left Side of the Subtree @@ -147,7 +146,6 @@ impl Default for Parity { } /// Node Index -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct Node<Idx = usize>( /// Level-wise Index to a node in a Binary Tree @@ -367,7 +365,6 @@ where /// /// This `struct` is created by the [`parents`](Node::parents) method on [`Node`]. /// See its documentation for more. -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct NodeParents { /// Current Index diff --git a/manta-crypto/src/merkle_tree/path.rs b/manta-crypto/src/merkle_tree/path.rs index d20d9d4af..540439d23 100644 --- a/manta-crypto/src/merkle_tree/path.rs +++ b/manta-crypto/src/merkle_tree/path.rs @@ -34,26 +34,12 @@ use core::{ slice::SliceIndex, }; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - pub(super) mod prelude { #[doc(inline)] pub use super::{CurrentPath, Path}; } /// Merkle Tree Inner Path -#[cfg_attr( - feature = "serde", - derive(Deserialize, Serialize), - serde( - bound( - deserialize = "Vec<InnerDigest<C>>: Deserialize<'de>", - serialize = "Vec<InnerDigest<C>>: Serialize", - ), - deny_unknown_fields - ) -)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "InnerDigest<C>: Clone"), @@ -238,17 +224,6 @@ where } /// Merkle Tree Current Inner Path -#[cfg_attr( - feature = "serde", - derive(Deserialize, Serialize), - serde( - bound( - deserialize = "Vec<InnerDigest<C>>: Deserialize<'de>", - serialize = "Vec<InnerDigest<C>>: Serialize", - ), - deny_unknown_fields - ) -)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "InnerDigest<C>: Clone"), @@ -617,17 +592,6 @@ impl<C> ExactSizeIterator for CurrentInnerPathNodeIter<C> where C: Configuration impl<C> FusedIterator for CurrentInnerPathNodeIter<C> where C: Configuration + ?Sized {} /// Merkle Tree Path -#[cfg_attr( - feature = "serde", - derive(Deserialize, Serialize), - serde( - bound( - deserialize = "LeafDigest<C>: Deserialize<'de>, InnerPath<C>: Deserialize<'de>", - serialize = "LeafDigest<C>: Serialize, InnerPath<C>: Serialize", - ), - deny_unknown_fields - ) -)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), @@ -766,17 +730,6 @@ where } /// Merkle Tree Current Path -#[cfg_attr( - feature = "serde", - derive(Deserialize, Serialize), - serde( - bound( - deserialize = "LeafDigest<C>: Deserialize<'de>, CurrentInnerPath<C>: Deserialize<'de>", - serialize = "LeafDigest<C>: Serialize, CurrentInnerPath<C>: Serialize", - ), - deny_unknown_fields - ) -)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index c638c2a86..a20907338 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -37,7 +37,11 @@ use crate::{ }, }; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; -use manta_util::{persistance::Rollback, pointer::PointerFamily}; +use manta_util::{ + codec::{Decode, DecodeError, Encode, Read, Write}, + persistance::Rollback, + pointer::PointerFamily, +}; /// Merkle Tree Leaf Hash pub trait LeafHash<COM = ()> { @@ -635,6 +639,63 @@ where } } +/// Parameter Decode Error +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum ParameterDecodeError<L, I> { + /// Leaf Decoding Error + Leaf(L), + + /// Inner Decoding Error + Inner(I), +} + +impl<C, COM, CODEC> Decode<CODEC> for Parameters<C, COM> +where + C: HashConfiguration<COM> + ?Sized, + LeafHashParameters<C, COM>: Decode<CODEC>, + InnerHashParameters<C, COM>: Decode<CODEC>, +{ + #[allow(clippy::type_complexity)] // NOTE: This is an implementation type so it doesn't matter. + type Error = ParameterDecodeError< + <LeafHashParameters<C, COM> as Decode<CODEC>>::Error, + <InnerHashParameters<C, COM> as Decode<CODEC>>::Error, + >; + + #[inline] + fn decode<R>(mut reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> + where + R: Read, + { + Ok(Self::new( + LeafHashParameters::<C, COM>::decode(&mut reader).map_err(|err| match err { + DecodeError::Decode(err) => DecodeError::Decode(ParameterDecodeError::Leaf(err)), + DecodeError::Read(err) => DecodeError::Read(err), + })?, + InnerHashParameters::<C, COM>::decode(&mut reader).map_err(|err| match err { + DecodeError::Decode(err) => DecodeError::Decode(ParameterDecodeError::Inner(err)), + DecodeError::Read(err) => DecodeError::Read(err), + })?, + )) + } +} + +impl<C, COM, CODEC> Encode<CODEC> for Parameters<C, COM> +where + C: HashConfiguration<COM> + ?Sized, + LeafHashParameters<C, COM>: Encode<CODEC>, + InnerHashParameters<C, COM>: Encode<CODEC>, +{ + #[inline] + fn encode<W>(&self, mut writer: W) -> Result<(), W::Error> + where + W: Write, + { + self.leaf.encode(&mut writer)?; + self.inner.encode(&mut writer)?; + Ok(()) + } +} + /// Merkle Tree Root pub type Root<C, COM = ()> = InnerDigest<C, COM>; diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 9f9b3cf65..f2a9e0e0d 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -85,7 +85,6 @@ manta-util = { path = "../manta-util" } rand = { version = "0.8.4", optional = true } rand_chacha = { version = "0.3.1", default-features = false } scale-codec = { package = "parity-scale-codec", version = "2.3.1", default-features = false, features = ["derive"] } -serde = { version = "1.0.133", optional = true, default-features = false, features = ["derive"] } statrs = { version = "0.15.0", optional = true, default-features = false } zk-garage-plonk = { package = "plonk", git = "https://github.com/zk-garage/plonk", optional = true, default-features = false } diff --git a/manta-pay/src/bin/generate_parameters.rs b/manta-pay/src/bin/generate_parameters.rs index b923e0f60..76c0c1b65 100644 --- a/manta-pay/src/bin/generate_parameters.rs +++ b/manta-pay/src/bin/generate_parameters.rs @@ -20,15 +20,22 @@ // TODO: Deduplicate the per-circuit proving context and verifying context serialization code. // TODO: Print some statistics about the parameters and circuits and into a stats file as well. -use ark_serialize::CanonicalSerialize; +use manta_accounting::transfer; use manta_crypto::{ constraint::ProofSystem as _, rand::{Rand, SeedableRng}, }; -use manta_pay::config::{FullParameters, Mint, PrivateTransfer, ProofSystem, Reclaim}; +use manta_pay::config::{ + Config, FullParameters, Mint, Parameters, PrivateTransfer, ProofSystem, Reclaim, +}; use manta_util::codec::{Encode, IoWriter}; use rand_chacha::ChaCha20Rng; -use std::{fs::OpenOptions, io}; +use std::{ + env, + fs::{self, OpenOptions}, + io, + path::PathBuf, +}; /// Parameter Generation Seed /// @@ -48,51 +55,92 @@ pub const SEED: [u8; 32] = [ /// Generates the parameters using the [`SEED`] and saves them to the filesystem. #[inline] pub fn main() -> io::Result<()> { + let target_dir = env::args() + .nth(1) + .map(PathBuf::from) + .unwrap_or(env::current_dir()?); + assert!( + target_dir.is_dir() || !target_dir.exists(), + "Specify a directory to place the generated files: {:?}.", + target_dir, + ); + fs::create_dir_all(&target_dir)?; + let mut rng = ChaCha20Rng::from_seed(SEED); let parameters = rng.gen(); - let utxo_set_parameters = rng.gen(); + let utxo_set_parameters: <Config as transfer::Configuration>::UtxoSetModel = rng.gen(); - /* TODO: let Parameters { key_agreement, utxo_commitment, void_number_hash, } = &parameters; - key_agreement.generator().serialize(&mut bytes).unwrap(); - utxo_commitment.serialize(&mut bytes).unwrap(); - void_number_hash.serialize(&mut bytes).unwrap(); + let parameters_dir = target_dir.join("parameters"); + fs::create_dir_all(&parameters_dir)?; + + key_agreement + .encode(IoWriter( + OpenOptions::new() + .create(true) + .write(true) + .open(parameters_dir.join("key-agreement.dat"))?, + )) + .unwrap(); + + utxo_commitment + .encode(IoWriter( + OpenOptions::new() + .create(true) + .write(true) + .open(parameters_dir.join("utxo-commitment-scheme.dat"))?, + )) + .unwrap(); + + void_number_hash + .encode(IoWriter( + OpenOptions::new() + .create(true) + .write(true) + .open(parameters_dir.join("void-number-hash-function.dat"))?, + )) + .unwrap(); utxo_set_parameters - .serialize( - &mut OpenOptions::new() + .encode(IoWriter( + OpenOptions::new() .create(true) .write(true) - .open("utxo-set-parameters.dat")?, - ) + .open(parameters_dir.join("utxo-set-parameters.dat"))?, + )) .unwrap(); - */ let full_parameters = FullParameters::new(&parameters, &utxo_set_parameters); + let proving_context_dir = target_dir.join("proving"); + fs::create_dir_all(&proving_context_dir)?; + + let verifying_context_dir = target_dir.join("verifying"); + fs::create_dir_all(&verifying_context_dir)?; + let cs = Mint::unknown_constraints(full_parameters); let (proving_context, verifying_context) = ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); proving_context - .serialize( + .encode(IoWriter( OpenOptions::new() .create(true) .write(true) - .open("mint.pk")?, - ) + .open(proving_context_dir.join("mint.dat"))?, + )) .unwrap(); verifying_context .encode(IoWriter( OpenOptions::new() .create(true) .write(true) - .open("mint.vk")?, + .open(verifying_context_dir.join("mint.dat"))?, )) .unwrap(); @@ -100,19 +148,19 @@ pub fn main() -> io::Result<()> { let (proving_context, verifying_context) = ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); proving_context - .serialize( + .encode(IoWriter( OpenOptions::new() .create(true) .write(true) - .open("private-transfer.pk")?, - ) + .open(proving_context_dir.join("private-transfer.dat"))?, + )) .unwrap(); verifying_context .encode(IoWriter( OpenOptions::new() .create(true) .write(true) - .open("private-transfer.vk")?, + .open(verifying_context_dir.join("private-transfer.dat"))?, )) .unwrap(); @@ -120,19 +168,19 @@ pub fn main() -> io::Result<()> { let (proving_context, verifying_context) = ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); proving_context - .serialize( + .encode(IoWriter( OpenOptions::new() .create(true) .write(true) - .open("reclaim.pk")?, - ) + .open(proving_context_dir.join("reclaim.dat"))?, + )) .unwrap(); verifying_context .encode(IoWriter( OpenOptions::new() .create(true) .write(true) - .open("reclaim.vk")?, + .open(verifying_context_dir.join("reclaim.dat"))?, )) .unwrap(); diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 953893b36..2c4e35532 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -28,7 +28,7 @@ use crate::{ }; use alloc::vec::Vec; use ark_ff::{PrimeField, ToConstraintField}; -use ark_serialize::CanonicalSerialize; +use ark_serialize::{CanonicalSerialize, SerializationError}; use blake2::{ digest::{Update, VariableOutput}, Blake2sVar, @@ -53,6 +53,7 @@ use manta_crypto::{ key::KeyDerivationFunction, merkle_tree, }; +use manta_util::codec::{Decode, DecodeError, Encode, Read, Write}; #[cfg(any(feature = "test", test))] use manta_crypto::rand::{CryptoRng, Rand, RngCore, Sample, Standard}; @@ -158,6 +159,28 @@ impl CommitmentScheme for UtxoCommitmentScheme { } } +impl Decode for UtxoCommitmentScheme { + type Error = SerializationError; + + #[inline] + fn decode<R>(reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> + where + R: Read, + { + Ok(Self(Poseidon4::decode(reader)?)) + } +} + +impl Encode for UtxoCommitmentScheme { + #[inline] + fn encode<W>(&self, writer: W) -> Result<(), W::Error> + where + W: Write, + { + self.0.encode(writer) + } +} + #[cfg(any(feature = "test", test))] // NOTE: This is only safe in a test. impl Sample for UtxoCommitmentScheme { #[inline] @@ -227,6 +250,28 @@ impl BinaryHashFunction for VoidNumberHashFunction { } } +impl Decode for VoidNumberHashFunction { + type Error = SerializationError; + + #[inline] + fn decode<R>(reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> + where + R: Read, + { + Ok(Self(Poseidon2::decode(reader)?)) + } +} + +impl Encode for VoidNumberHashFunction { + #[inline] + fn encode<W>(&self, writer: W) -> Result<(), W::Error> + where + W: Write, + { + self.0.encode(writer) + } +} + #[cfg(any(feature = "test", test))] // NOTE: This is only safe in a test. impl Sample for VoidNumberHashFunction { #[inline] diff --git a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs index dc2dd23a4..214dc0154 100644 --- a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs @@ -27,7 +27,7 @@ use manta_crypto::{ }, rand::{CryptoRng, RngCore, Sample, Standard}, }; -use scale_codec::{Decode, Encode, EncodeLike}; +use manta_util::codec::{Decode, DecodeError, Encode, Read, Write}; pub use ark_r1cs::SynthesisError; pub use ark_r1cs_std::{bits::boolean::Boolean, fields::fp::FpVar}; @@ -39,6 +39,43 @@ where F: PrimeField; impl<F> Decode for Fp<F> +where + F: PrimeField, +{ + type Error = codec::SerializationError; + + #[inline] + fn decode<R>(reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> + where + R: Read, + { + let mut reader = codec::ArkReader::new(reader); + match codec::CanonicalDeserialize::deserialize(&mut reader) { + Ok(value) => reader + .finish() + .map(move |_| Self(value)) + .map_err(DecodeError::Read), + Err(err) => Err(DecodeError::Decode(err)), + } + } +} + +impl<F> Encode for Fp<F> +where + F: PrimeField, +{ + #[inline] + fn encode<W>(&self, writer: W) -> Result<(), W::Error> + where + W: Write, + { + let mut writer = codec::ArkWriter::new(writer); + let _ = self.0.serialize(&mut writer); + writer.finish().map(move |_| ()) + } +} + +impl<F> scale_codec::Decode for Fp<F> where F: PrimeField, { @@ -48,12 +85,13 @@ where I: scale_codec::Input, { Ok(Self( - F::deserialize(codec::ScaleCodecReader(input)).map_err(|_| "Deserialization Error")?, + codec::CanonicalDeserialize::deserialize(codec::ScaleCodecReader(input)) + .map_err(|_| "Deserialization Error")?, )) } } -impl<F> Encode for Fp<F> +impl<F> scale_codec::Encode for Fp<F> where F: PrimeField, { @@ -70,7 +108,7 @@ where } } -impl<F> EncodeLike for Fp<F> where F: PrimeField {} +impl<F> scale_codec::EncodeLike for Fp<F> where F: PrimeField {} impl<F> Sample for Fp<F> where @@ -501,16 +539,10 @@ pub mod codec { } #[inline] - fn write_all(&mut self, buf: &[u8]) -> Result<(), Error> { - let _ = self.write(buf)?; - if buf.is_empty() { - Ok(()) - } else { - Err(Error::new( - ErrorKind::WriteZero, - "failed to write whole buffer", - )) - } + fn write_all(&mut self, mut buf: &[u8]) -> Result<(), Error> { + self.update(|writer| writer.write(&mut buf)) + .map(|_| ()) + .ok_or_else(|| Error::new(ErrorKind::Other, "Writing Error")) } } } diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs index ac4618bc5..fb4a114e0 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs @@ -295,6 +295,43 @@ pub mod groth16 { } } + impl<E> codec::Decode for ProvingContext<E> + where + E: PairingEngine, + { + type Error = SerializationError; + + #[inline] + fn decode<R>(reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> + where + R: codec::Read, + { + let mut reader = ArkReader::new(reader); + match CanonicalDeserialize::deserialize(&mut reader) { + Ok(value) => reader + .finish() + .map(move |_| Self(value)) + .map_err(DecodeError::Read), + Err(err) => Err(DecodeError::Decode(err)), + } + } + } + + impl<E> codec::Encode for ProvingContext<E> + where + E: PairingEngine, + { + #[inline] + fn encode<W>(&self, writer: W) -> Result<(), W::Error> + where + W: codec::Write, + { + let mut writer = ArkWriter::new(writer); + let _ = self.0.serialize(&mut writer); + writer.finish().map(move |_| ()) + } + } + /// Verifying Context #[derive(derivative::Derivative)] #[derivative(Clone, Debug, Default)] @@ -467,22 +504,6 @@ pub mod groth16 { } } - impl<E> codec::Encode for VerifyingContext<E> - where - E: PairingEngine, - for<'s> E::G2Prepared: HasSerialization<'s>, - { - #[inline] - fn encode<W>(&self, writer: W) -> Result<(), W::Error> - where - W: codec::Write, - { - let mut writer = ArkWriter::new(writer); - let _ = self.serialize(&mut writer); - writer.finish().map(move |_| ()) - } - } - impl<E> codec::Decode for VerifyingContext<E> where E: PairingEngine, @@ -491,7 +512,7 @@ pub mod groth16 { type Error = SerializationError; #[inline] - fn decode<R>(reader: R) -> Result<Self, DecodeError<R, Self>> + fn decode<R>(reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> where R: codec::Read, { @@ -506,6 +527,22 @@ pub mod groth16 { } } + impl<E> codec::Encode for VerifyingContext<E> + where + E: PairingEngine, + for<'s> E::G2Prepared: HasSerialization<'s>, + { + #[inline] + fn encode<W>(&self, writer: W) -> Result<(), W::Error> + where + W: codec::Write, + { + let mut writer = ArkWriter::new(writer); + let _ = self.serialize(&mut writer); + writer.finish().map(move |_| ()) + } + } + /// Arkworks Groth16 Proof System #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] diff --git a/manta-pay/src/crypto/ecc.rs b/manta-pay/src/crypto/ecc.rs index c2a3c1fa2..e789a7765 100644 --- a/manta-pay/src/crypto/ecc.rs +++ b/manta-pay/src/crypto/ecc.rs @@ -20,12 +20,15 @@ #[cfg(feature = "arkworks")] #[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] pub mod arkworks { - use crate::crypto::constraint::arkworks::{self, empty, full, Boolean, Fp, FpVar, R1CS}; + use crate::crypto::constraint::arkworks::{ + codec::{ArkReader, ArkWriter, ScaleCodecReader}, + empty, full, Boolean, Fp, FpVar, R1CS, + }; use alloc::vec::Vec; use ark_ff::{BigInteger, Field, FpParameters, PrimeField}; use ark_r1cs_std::ToBitsGadget; use ark_relations::ns; - use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; + use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; use core::marker::PhantomData; use manta_crypto::{ constraint::{Allocator, Constant, Equal, Public, Secret, ValueSource, Variable}, @@ -33,7 +36,7 @@ pub mod arkworks { key::kdf, rand::{CryptoRng, RngCore, Sample, Standard}, }; - use scale_codec::{Decode, Encode, EncodeLike}; + use manta_util::codec; pub use ark_ec::{AffineCurve, ProjectiveCurve}; pub use ark_r1cs_std::groups::CurveVar; @@ -99,7 +102,44 @@ pub mod arkworks { where C: ProjectiveCurve; - impl<C> Decode for Group<C> + impl<C> codec::Decode for Group<C> + where + C: ProjectiveCurve, + { + type Error = SerializationError; + + #[inline] + fn decode<R>(reader: R) -> Result<Self, codec::DecodeError<R::Error, Self::Error>> + where + R: codec::Read, + { + let mut reader = ArkReader::new(reader); + match CanonicalDeserialize::deserialize(&mut reader) { + Ok(value) => reader + .finish() + .map(move |_| Self(value)) + .map_err(codec::DecodeError::Read), + Err(err) => Err(codec::DecodeError::Decode(err)), + } + } + } + + impl<C> codec::Encode for Group<C> + where + C: ProjectiveCurve, + { + #[inline] + fn encode<W>(&self, writer: W) -> Result<(), W::Error> + where + W: codec::Write, + { + let mut writer = ArkWriter::new(writer); + let _ = self.0.serialize(&mut writer); + writer.finish().map(|_| ()) + } + } + + impl<C> scale_codec::Decode for Group<C> where C: ProjectiveCurve, { @@ -109,13 +149,13 @@ pub mod arkworks { I: scale_codec::Input, { Ok(Self( - C::Affine::deserialize(arkworks::codec::ScaleCodecReader(input)) + CanonicalDeserialize::deserialize(ScaleCodecReader(input)) .map_err(|_| "Deserialization Error")?, )) } } - impl<C> Encode for Group<C> + impl<C> scale_codec::Encode for Group<C> where C: ProjectiveCurve, { @@ -132,7 +172,7 @@ pub mod arkworks { } } - impl<C> EncodeLike for Group<C> where C: ProjectiveCurve {} + impl<C> scale_codec::EncodeLike for Group<C> where C: ProjectiveCurve {} impl<C> kdf::AsBytes for Group<C> where diff --git a/manta-pay/src/crypto/hash/poseidon.rs b/manta-pay/src/crypto/hash/poseidon.rs index 65239f55d..05563550c 100644 --- a/manta-pay/src/crypto/hash/poseidon.rs +++ b/manta-pay/src/crypto/hash/poseidon.rs @@ -22,6 +22,7 @@ use alloc::vec::Vec; use core::{fmt::Debug, iter, mem}; use manta_crypto::hash::HashFunction; +use manta_util::codec::{Decode, DecodeError, Encode, Read, Write}; #[cfg(any(feature = "test", test))] use { @@ -59,7 +60,7 @@ pub trait Specification<COM = ()> { fn apply_sbox(point: &mut Self::Field, compiler: &mut COM); } -/// Internal State Vector +/// Poseidon State Vector type State<S, COM> = Vec<<S as Specification<COM>>::Field>; /// Returns the total number of rounds in a Poseidon permutation. @@ -258,6 +259,49 @@ where } } +impl<S, COM, CODEC, const ARITY: usize> Decode<CODEC> for Hash<S, COM, ARITY> +where + S: Specification<COM>, + S::Field: Decode<CODEC>, +{ + type Error = <S::Field as Decode<CODEC>>::Error; + + #[inline] + fn decode<R>(mut reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> + where + R: Read, + { + Ok(Self::new_unchecked( + (0..Self::ADDITIVE_ROUND_KEYS_COUNT) + .map(|_| S::Field::decode(&mut reader)) + .collect::<Result<Vec<_>, _>>()?, + (0..Self::MDS_MATRIX_SIZE) + .map(|_| S::Field::decode(&mut reader)) + .collect::<Result<Vec<_>, _>>()?, + )) + } +} + +impl<S, COM, CODEC, const ARITY: usize> Encode<CODEC> for Hash<S, COM, ARITY> +where + S: Specification<COM>, + S::Field: Encode<CODEC>, +{ + #[inline] + fn encode<W>(&self, mut writer: W) -> Result<(), W::Error> + where + W: Write, + { + for key in &self.additive_round_keys { + key.encode(&mut writer)?; + } + for entry in &self.mds_matrix { + entry.encode(&mut writer)?; + } + Ok(()) + } +} + /// Poseidon Hash Input Type pub type Input<S, COM, const ARITY: usize> = <Hash<S, COM, ARITY> as HashFunction<COM, ARITY>>::Input; diff --git a/manta-util/src/codec.rs b/manta-util/src/codec.rs index f370fd229..f826fe393 100644 --- a/manta-util/src/codec.rs +++ b/manta-util/src/codec.rs @@ -563,6 +563,17 @@ pub trait Encode<C = ()> { } } +impl<C> Encode<C> for () { + #[inline] + fn encode<W>(&self, writer: W) -> Result<(), W::Error> + where + W: Write, + { + let _ = writer; + Ok(()) + } +} + impl Encode for u8 { #[inline] fn encode<W>(&self, mut writer: W) -> Result<(), W::Error> @@ -624,7 +635,7 @@ pub trait Decode<C = ()>: Sized { type Error; /// Parses the input `buffer` into a concrete value of type `Self` if possible. - fn decode<R>(reader: R) -> Result<Self, DecodeError<R, Self, C>> + fn decode<R>(reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> where R: Read; @@ -638,6 +649,19 @@ pub trait Decode<C = ()>: Sized { } } +impl<C> Decode<C> for () { + type Error = Infallible; + + #[inline] + fn decode<R>(reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> + where + R: Read, + { + let _ = reader; + Ok(()) + } +} + /// Exact Size Decoding pub trait DecodeExactSize<C, const N: usize>: Decode<C> { /// Converts a fixed-length byte array into a concrete value of type `Self`. @@ -649,85 +673,43 @@ pub trait DecodeExactSize<C, const N: usize>: Decode<C> { } } +impl<C> DecodeExactSize<C, 0> for () { + #[inline] + fn from_array(buffer: [u8; 0]) -> Self { + let _ = buffer; + } +} + /// Decoding Error -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "R::Error: Clone, D::Error: Clone"), - Copy(bound = "R::Error: Copy, D::Error: Copy"), - Debug(bound = "R::Error: Debug, D::Error: Debug"), - Eq(bound = "R::Error: Eq, D::Error: Eq"), - Hash(bound = "R::Error: Hash, D::Error: Hash"), - PartialEq(bound = "R::Error: PartialEq, D::Error: PartialEq") -)] -pub enum DecodeError<R, D, C = ()> -where - R: Read + ?Sized, - D: Decode<C> + ?Sized, -{ +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum DecodeError<R, D> { /// Reading Error /// /// See [`Read`] for more. - Read(R::Error), + Read(R), /// Decoding Error /// /// See [`Decode`] for more. - Decode(D::Error), + Decode(D), } -impl<R, D, C> DecodeError<R, D, C> -where - R: Read + ?Sized, - D: Decode<C> + ?Sized, -{ - /// Converts `self` into an option over [`R::Error`](Read::Error). +impl<R, D> DecodeError<R, D> { + /// Converts `self` into an option over [`R`](Read::Error). #[inline] - pub fn read(self) -> Option<R::Error> { + pub fn read(self) -> Option<R> { match self { Self::Read(err) => Some(err), _ => None, } } - /// Converts `self` into an option over [`D::Error`](Decode::Error). + /// Converts `self` into an option over [`D`](Decode::Error). #[inline] - pub fn decode(self) -> Option<D::Error> { + pub fn decode(self) -> Option<D> { match self { Self::Decode(err) => Some(err), _ => None, } } - - /// Maps `self` into a [`DecodeError`] with the given `Reader`, `Decoder`, and `Codec` - /// implementations using `r` and `d` to convert errors. - #[inline] - pub fn map<Reader, Decoder, Codec, FR, FD>( - self, - r: FR, - d: FD, - ) -> DecodeError<Reader, Decoder, Codec> - where - Reader: Read + ?Sized, - Decoder: Decode<Codec> + ?Sized, - FR: FnOnce(R::Error) -> Reader::Error, - FD: FnOnce(D::Error) -> Decoder::Error, - { - match self { - Self::Read(err) => DecodeError::Read(r(err)), - Self::Decode(err) => DecodeError::Decode(d(err)), - } - } - - /// Maps `self` into a [`DecodeError`] with the given `Reader`, `Decoder`, and `Codec` - /// implementations using [`Into::into`] to convert errors. - #[inline] - pub fn into<Reader, Decoder, Codec>(self) -> DecodeError<Reader, Decoder, Codec> - where - Reader: Read + ?Sized, - Decoder: Decode<Codec> + ?Sized, - R::Error: Into<Reader::Error>, - D::Error: Into<Decoder::Error>, - { - self.map(Into::into, Into::into) - } } From 8ca50f5db5c7d6c98329e4580b8c52d8aa2e916c Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 23 Jan 2022 18:06:23 -0500 Subject: [PATCH 199/275] chore: bump license year --- FILE_TEMPLATE | 2 +- manta-accounting/src/asset.rs | 2 +- manta-accounting/src/fs.rs | 2 +- manta-accounting/src/key.rs | 2 +- manta-accounting/src/lib.rs | 2 +- manta-accounting/src/transfer/batch.rs | 2 +- manta-accounting/src/transfer/canonical.rs | 2 +- manta-accounting/src/transfer/mod.rs | 4 ++-- manta-accounting/src/transfer/test.rs | 2 +- manta-accounting/src/wallet/ledger.rs | 2 +- manta-accounting/src/wallet/mod.rs | 2 +- manta-accounting/src/wallet/signer.rs | 2 +- manta-accounting/src/wallet/state.rs | 2 +- manta-accounting/src/wallet/test/mod.rs | 2 +- manta-accounting/src/wallet/test/sim.rs | 2 +- manta-crypto/src/accumulator.rs | 2 +- manta-crypto/src/commitment.rs | 2 +- manta-crypto/src/constraint.rs | 4 ++-- manta-crypto/src/ecc.rs | 2 +- manta-crypto/src/encryption.rs | 2 +- manta-crypto/src/hash.rs | 2 +- manta-crypto/src/key.rs | 2 +- manta-crypto/src/lib.rs | 2 +- manta-crypto/src/merkle_tree/forest.rs | 2 +- manta-crypto/src/merkle_tree/fork.rs | 2 +- manta-crypto/src/merkle_tree/full.rs | 2 +- manta-crypto/src/merkle_tree/inner_tree.rs | 2 +- manta-crypto/src/merkle_tree/mod.rs | 2 +- manta-crypto/src/merkle_tree/node.rs | 2 +- manta-crypto/src/merkle_tree/partial.rs | 2 +- manta-crypto/src/merkle_tree/path.rs | 2 +- manta-crypto/src/merkle_tree/single_path.rs | 2 +- manta-crypto/src/merkle_tree/test.rs | 2 +- manta-crypto/src/merkle_tree/tree.rs | 2 +- manta-crypto/src/rand.rs | 2 +- manta-pay/README.md | 2 +- manta-pay/benches/main.rs | 2 +- manta-pay/src/bin/generate_parameters.rs | 2 +- manta-pay/src/config.rs | 2 +- .../src/crypto/constraint/arkworks/constraint_system.rs | 2 +- manta-pay/src/crypto/constraint/arkworks/mod.rs | 2 +- manta-pay/src/crypto/constraint/arkworks/proof_system.rs | 2 +- manta-pay/src/crypto/constraint/mod.rs | 2 +- manta-pay/src/crypto/constraint/plonk.rs | 2 +- manta-pay/src/crypto/ecc.rs | 2 +- manta-pay/src/crypto/encryption.rs | 2 +- manta-pay/src/crypto/hash/mod.rs | 2 +- manta-pay/src/crypto/hash/poseidon.rs | 2 +- manta-pay/src/crypto/key.rs | 2 +- manta-pay/src/crypto/mod.rs | 2 +- manta-pay/src/key.rs | 2 +- manta-pay/src/lib.rs | 2 +- manta-pay/src/test/ledger.rs | 2 +- manta-pay/src/test/mod.rs | 2 +- manta-pay/src/test/simulation/mod.rs | 2 +- manta-pay/src/test/transfer.rs | 2 +- manta-pay/src/wallet/cache.rs | 2 +- manta-pay/src/wallet/mod.rs | 2 +- manta-util/src/array.rs | 2 +- manta-util/src/cache.rs | 2 +- manta-util/src/codec.rs | 2 +- manta-util/src/convert.rs | 2 +- manta-util/src/future.rs | 2 +- manta-util/src/iter/chunk_by.rs | 2 +- manta-util/src/iter/mod.rs | 2 +- manta-util/src/lib.rs | 2 +- manta-util/src/persistance.rs | 2 +- manta-util/src/pointer.rs | 2 +- manta-util/src/sealed.rs | 2 +- src/lib.rs | 5 +---- 70 files changed, 72 insertions(+), 75 deletions(-) diff --git a/FILE_TEMPLATE b/FILE_TEMPLATE index c70cfc1b2..fbfdcd676 100644 --- a/FILE_TEMPLATE +++ b/FILE_TEMPLATE @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index e3398a6ca..127574d5b 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-accounting/src/fs.rs b/manta-accounting/src/fs.rs index a86d660f8..bf36685e8 100644 --- a/manta-accounting/src/fs.rs +++ b/manta-accounting/src/fs.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index 45710f3f4..82ed9cc48 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-accounting/src/lib.rs b/manta-accounting/src/lib.rs index 09d57ffb7..72ac5e1b7 100644 --- a/manta-accounting/src/lib.rs +++ b/manta-accounting/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-accounting/src/transfer/batch.rs b/manta-accounting/src/transfer/batch.rs index 1020b6b1f..d1fe77723 100644 --- a/manta-accounting/src/transfer/batch.rs +++ b/manta-accounting/src/transfer/batch.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-accounting/src/transfer/canonical.rs b/manta-accounting/src/transfer/canonical.rs index dc4e60d2f..20cfcc6a3 100644 --- a/manta-accounting/src/transfer/canonical.rs +++ b/manta-accounting/src/transfer/canonical.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index 6aef7c382..6276a9ed0 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify @@ -1215,7 +1215,7 @@ where /// Existence of such a UTXO could indicate a possible double-spend. fn is_not_registered(&self, utxo: Utxo<C>) -> Option<Self::ValidUtxo>; - /// Posts the `utxo` and `encrypted_asset` to the ledger, registering the asset. + /// Posts the `utxo` and `note` to the ledger, registering the asset. /// /// # Safety /// diff --git a/manta-accounting/src/transfer/test.rs b/manta-accounting/src/transfer/test.rs index cdd5c4122..3c82a2df3 100644 --- a/manta-accounting/src/transfer/test.rs +++ b/manta-accounting/src/transfer/test.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs index 977480031..cd42f94bd 100644 --- a/manta-accounting/src/wallet/ledger.rs +++ b/manta-accounting/src/wallet/ledger.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-accounting/src/wallet/mod.rs b/manta-accounting/src/wallet/mod.rs index e38a5adc6..6f8b6b66f 100644 --- a/manta-accounting/src/wallet/mod.rs +++ b/manta-accounting/src/wallet/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index fb7b748a0..d9f35ad43 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 48b8c1109..da5faba08 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-accounting/src/wallet/test/mod.rs b/manta-accounting/src/wallet/test/mod.rs index c46da0481..869ec4acd 100644 --- a/manta-accounting/src/wallet/test/mod.rs +++ b/manta-accounting/src/wallet/test/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-accounting/src/wallet/test/sim.rs b/manta-accounting/src/wallet/test/sim.rs index 2eb20b557..ea63f71d6 100644 --- a/manta-accounting/src/wallet/test/sim.rs +++ b/manta-accounting/src/wallet/test/sim.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-crypto/src/accumulator.rs b/manta-crypto/src/accumulator.rs index c5a30c56a..568ca63aa 100644 --- a/manta-crypto/src/accumulator.rs +++ b/manta-crypto/src/accumulator.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index c1b10e758..43d8a339e 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index 276d86f8c..56874dbdf 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ // generation and only known modes for proof generation, instead of relying on the `for_*` // methods to "do the right thing". // TODO: Find ways to enforce public input structure, since it's very easy to extend the input -// vector by the wrong amount. +// vector by the wrong amount or in the wrong order. use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use manta_util::{create_seal, seal}; diff --git a/manta-crypto/src/ecc.rs b/manta-crypto/src/ecc.rs index e82fd0d28..c5c105a7d 100644 --- a/manta-crypto/src/ecc.rs +++ b/manta-crypto/src/ecc.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-crypto/src/encryption.rs b/manta-crypto/src/encryption.rs index aaa7b2a7b..ece24c270 100644 --- a/manta-crypto/src/encryption.rs +++ b/manta-crypto/src/encryption.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-crypto/src/hash.rs b/manta-crypto/src/hash.rs index b425c5078..872299f0f 100644 --- a/manta-crypto/src/hash.rs +++ b/manta-crypto/src/hash.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-crypto/src/key.rs b/manta-crypto/src/key.rs index 12c8f3bda..02a14e3a3 100644 --- a/manta-crypto/src/key.rs +++ b/manta-crypto/src/key.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index d4b86713b..13c00ab2f 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-crypto/src/merkle_tree/forest.rs b/manta-crypto/src/merkle_tree/forest.rs index 5b7de44e1..2789a4a43 100644 --- a/manta-crypto/src/merkle_tree/forest.rs +++ b/manta-crypto/src/merkle_tree/forest.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs index dd95d38a4..93e6621f7 100644 --- a/manta-crypto/src/merkle_tree/fork.rs +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-crypto/src/merkle_tree/full.rs b/manta-crypto/src/merkle_tree/full.rs index 204ee217d..f6d15dd59 100644 --- a/manta-crypto/src/merkle_tree/full.rs +++ b/manta-crypto/src/merkle_tree/full.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-crypto/src/merkle_tree/inner_tree.rs b/manta-crypto/src/merkle_tree/inner_tree.rs index cb2c20d0c..0b5608d02 100644 --- a/manta-crypto/src/merkle_tree/inner_tree.rs +++ b/manta-crypto/src/merkle_tree/inner_tree.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-crypto/src/merkle_tree/mod.rs b/manta-crypto/src/merkle_tree/mod.rs index 32f52df35..7fadc165e 100644 --- a/manta-crypto/src/merkle_tree/mod.rs +++ b/manta-crypto/src/merkle_tree/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-crypto/src/merkle_tree/node.rs b/manta-crypto/src/merkle_tree/node.rs index 4cf6a5fa1..12ccb97b8 100644 --- a/manta-crypto/src/merkle_tree/node.rs +++ b/manta-crypto/src/merkle_tree/node.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-crypto/src/merkle_tree/partial.rs b/manta-crypto/src/merkle_tree/partial.rs index 948ff9669..b70142c4f 100644 --- a/manta-crypto/src/merkle_tree/partial.rs +++ b/manta-crypto/src/merkle_tree/partial.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-crypto/src/merkle_tree/path.rs b/manta-crypto/src/merkle_tree/path.rs index 540439d23..8357ec19c 100644 --- a/manta-crypto/src/merkle_tree/path.rs +++ b/manta-crypto/src/merkle_tree/path.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-crypto/src/merkle_tree/single_path.rs b/manta-crypto/src/merkle_tree/single_path.rs index a971998b0..8aee4cf0e 100644 --- a/manta-crypto/src/merkle_tree/single_path.rs +++ b/manta-crypto/src/merkle_tree/single_path.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-crypto/src/merkle_tree/test.rs b/manta-crypto/src/merkle_tree/test.rs index bb302defa..13296d9de 100644 --- a/manta-crypto/src/merkle_tree/test.rs +++ b/manta-crypto/src/merkle_tree/test.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index a20907338..1c3f90966 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-crypto/src/rand.rs b/manta-crypto/src/rand.rs index 1d989e10a..1c8b8062c 100644 --- a/manta-crypto/src/rand.rs +++ b/manta-crypto/src/rand.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-pay/README.md b/manta-pay/README.md index d392fae63..a465d5a2f 100644 --- a/manta-pay/README.md +++ b/manta-pay/README.md @@ -5,6 +5,6 @@ To generate the public parameters use the following command: ```sh -cargo run --release --all-features generate_parameters +cargo run --release --all-features --bin generate_parameters data ``` diff --git a/manta-pay/benches/main.rs b/manta-pay/benches/main.rs index baca3dd05..e27e450db 100644 --- a/manta-pay/benches/main.rs +++ b/manta-pay/benches/main.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-pay/src/bin/generate_parameters.rs b/manta-pay/src/bin/generate_parameters.rs index 76c0c1b65..25a8f0bbd 100644 --- a/manta-pay/src/bin/generate_parameters.rs +++ b/manta-pay/src/bin/generate_parameters.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 2c4e35532..0efe17153 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs index 214dc0154..a39204c13 100644 --- a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-pay/src/crypto/constraint/arkworks/mod.rs b/manta-pay/src/crypto/constraint/arkworks/mod.rs index 675e55562..763d6eb8f 100644 --- a/manta-pay/src/crypto/constraint/arkworks/mod.rs +++ b/manta-pay/src/crypto/constraint/arkworks/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs index fb4a114e0..738c7eb74 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-pay/src/crypto/constraint/mod.rs b/manta-pay/src/crypto/constraint/mod.rs index 521240148..656863cb9 100644 --- a/manta-pay/src/crypto/constraint/mod.rs +++ b/manta-pay/src/crypto/constraint/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-pay/src/crypto/constraint/plonk.rs b/manta-pay/src/crypto/constraint/plonk.rs index b72b8c82b..6920aada6 100644 --- a/manta-pay/src/crypto/constraint/plonk.rs +++ b/manta-pay/src/crypto/constraint/plonk.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-pay/src/crypto/ecc.rs b/manta-pay/src/crypto/ecc.rs index e789a7765..861260e00 100644 --- a/manta-pay/src/crypto/ecc.rs +++ b/manta-pay/src/crypto/ecc.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-pay/src/crypto/encryption.rs b/manta-pay/src/crypto/encryption.rs index 2bcd90d53..3ee6aa485 100644 --- a/manta-pay/src/crypto/encryption.rs +++ b/manta-pay/src/crypto/encryption.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-pay/src/crypto/hash/mod.rs b/manta-pay/src/crypto/hash/mod.rs index e35eed6a6..14963833c 100644 --- a/manta-pay/src/crypto/hash/mod.rs +++ b/manta-pay/src/crypto/hash/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-pay/src/crypto/hash/poseidon.rs b/manta-pay/src/crypto/hash/poseidon.rs index 05563550c..c1f1791cc 100644 --- a/manta-pay/src/crypto/hash/poseidon.rs +++ b/manta-pay/src/crypto/hash/poseidon.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-pay/src/crypto/key.rs b/manta-pay/src/crypto/key.rs index 063d340a5..8986c3dde 100644 --- a/manta-pay/src/crypto/key.rs +++ b/manta-pay/src/crypto/key.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-pay/src/crypto/mod.rs b/manta-pay/src/crypto/mod.rs index 35907a000..939d0122c 100644 --- a/manta-pay/src/crypto/mod.rs +++ b/manta-pay/src/crypto/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-pay/src/key.rs b/manta-pay/src/key.rs index 99ec78c2d..c80225214 100644 --- a/manta-pay/src/key.rs +++ b/manta-pay/src/key.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index 07d978086..0ea0348bb 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-pay/src/test/ledger.rs b/manta-pay/src/test/ledger.rs index f3009f3a3..0c6229d71 100644 --- a/manta-pay/src/test/ledger.rs +++ b/manta-pay/src/test/ledger.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-pay/src/test/mod.rs b/manta-pay/src/test/mod.rs index c8bf2fcb3..4de377922 100644 --- a/manta-pay/src/test/mod.rs +++ b/manta-pay/src/test/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-pay/src/test/simulation/mod.rs b/manta-pay/src/test/simulation/mod.rs index 369ce155a..39e98e486 100644 --- a/manta-pay/src/test/simulation/mod.rs +++ b/manta-pay/src/test/simulation/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-pay/src/test/transfer.rs b/manta-pay/src/test/transfer.rs index 99cb0474c..a4d53f4b8 100644 --- a/manta-pay/src/test/transfer.rs +++ b/manta-pay/src/test/transfer.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-pay/src/wallet/cache.rs b/manta-pay/src/wallet/cache.rs index 8a45b58fd..bef896930 100644 --- a/manta-pay/src/wallet/cache.rs +++ b/manta-pay/src/wallet/cache.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-pay/src/wallet/mod.rs b/manta-pay/src/wallet/mod.rs index 70f7e6f92..a329810ac 100644 --- a/manta-pay/src/wallet/mod.rs +++ b/manta-pay/src/wallet/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-util/src/array.rs b/manta-util/src/array.rs index 376392a72..96e540fb5 100644 --- a/manta-util/src/array.rs +++ b/manta-util/src/array.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-util/src/cache.rs b/manta-util/src/cache.rs index ef37f77bc..d871017a2 100644 --- a/manta-util/src/cache.rs +++ b/manta-util/src/cache.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-util/src/codec.rs b/manta-util/src/codec.rs index f826fe393..7448b7a1f 100644 --- a/manta-util/src/codec.rs +++ b/manta-util/src/codec.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-util/src/convert.rs b/manta-util/src/convert.rs index 4e1b58509..47d5d708e 100644 --- a/manta-util/src/convert.rs +++ b/manta-util/src/convert.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-util/src/future.rs b/manta-util/src/future.rs index dbb3d27c0..5b2a41729 100644 --- a/manta-util/src/future.rs +++ b/manta-util/src/future.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-util/src/iter/chunk_by.rs b/manta-util/src/iter/chunk_by.rs index a6ee9ccd6..bff3bb0ca 100644 --- a/manta-util/src/iter/chunk_by.rs +++ b/manta-util/src/iter/chunk_by.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-util/src/iter/mod.rs b/manta-util/src/iter/mod.rs index 5a9aa7b8f..4c288c0fd 100644 --- a/manta-util/src/iter/mod.rs +++ b/manta-util/src/iter/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index df67bf4c4..58c65df68 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-util/src/persistance.rs b/manta-util/src/persistance.rs index c65390b77..75c543d99 100644 --- a/manta-util/src/persistance.rs +++ b/manta-util/src/persistance.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-util/src/pointer.rs b/manta-util/src/pointer.rs index 4b62e2a5b..5870fcbbd 100644 --- a/manta-util/src/pointer.rs +++ b/manta-util/src/pointer.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/manta-util/src/sealed.rs b/manta-util/src/sealed.rs index 6eede2b2e..de9297215 100644 --- a/manta-util/src/sealed.rs +++ b/manta-util/src/sealed.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify diff --git a/src/lib.rs b/src/lib.rs index d773f2558..2aa16e4fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Manta Network. +// Copyright 2019-2022 Manta Network. // This file is part of manta-rs. // // manta-rs is free software: you can redistribute it and/or modify @@ -16,9 +16,6 @@ //! Manta Network Crates -// TODO: Reconsider how `manta-codec` is designed. Do we need it? Should it just be part of -// `manta-pay` or some `substrate`-specific interoperability crates? - #![cfg_attr(not(any(feature = "std", test)), no_std)] #![cfg_attr(doc_cfg, feature(doc_cfg))] #![forbid(rustdoc::broken_intra_doc_links)] From 9ddfb958847e9e2ec4aed8d027ac2d3dba093c86 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 23 Jan 2022 22:54:45 -0500 Subject: [PATCH 200/275] fix: add more aliases to manta_pay::config --- manta-pay/src/config.rs | 27 ++++++++++++++++++--------- manta-pay/src/test/ledger.rs | 16 ++++++++-------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 0efe17153..a30eb782e 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -461,6 +461,9 @@ impl merkle_tree::InnerHash<Compiler> for InnerHashVar { } } +/// UTXO Set Model +pub type UtxoSetModel = merkle_tree::Parameters<MerkleTreeConfiguration>; + /// UTXO Set Output pub type UtxoSetOutput = merkle_tree::Root<MerkleTreeConfiguration>; @@ -593,7 +596,7 @@ impl transfer::Configuration for Config { type VoidNumberVar = <Self::VoidNumberHashFunctionVar as BinaryHashFunction<Self::Compiler>>::Output; type VoidNumberHashFunctionVar = VoidNumberHashFunctionVar; - type UtxoSetModel = merkle_tree::Parameters<MerkleTreeConfiguration>; + type UtxoSetModel = UtxoSetModel; type UtxoSetWitnessVar = <Self::UtxoSetModelVar as accumulator::Model<Self::Compiler>>::Witness; type UtxoSetOutputVar = <Self::UtxoSetModelVar as accumulator::Model<Self::Compiler>>::Output; type UtxoSetModelVar = merkle_tree::Parameters<MerkleTreeConfigurationVar, Compiler>; @@ -613,24 +616,30 @@ pub type FullParameters<'p> = transfer::FullParameters<'p, Config>; /// Encrypted Note Type pub type EncryptedNote = transfer::EncryptedNote<Config>; -/// Mint Transfer Type -pub type Mint = transfer::canonical::Mint<Config>; - -/// Private Transfer Type -pub type PrivateTransfer = transfer::canonical::PrivateTransfer<Config>; - -/// Reclaim Transfer Type -pub type Reclaim = transfer::canonical::Reclaim<Config>; +/// Sender Type +pub type Sender = transfer::Sender<Config>; /// Sender Post Type pub type SenderPost = transfer::SenderPost<Config>; +/// Receiver Type +pub type Receiver = transfer::Receiver<Config>; + /// Receiver Post Type pub type ReceiverPost = transfer::ReceiverPost<Config>; /// Transfer Post Type pub type TransferPost = transfer::TransferPost<Config>; +/// Mint Transfer Type +pub type Mint = transfer::canonical::Mint<Config>; + +/// Private Transfer Type +pub type PrivateTransfer = transfer::canonical::PrivateTransfer<Config>; + +/// Reclaim Transfer Type +pub type Reclaim = transfer::canonical::Reclaim<Config>; + /// Proving Context Type pub type ProvingContext = transfer::ProvingContext<Config>; diff --git a/manta-pay/src/test/ledger.rs b/manta-pay/src/test/ledger.rs index 0c6229d71..8f7c2065c 100644 --- a/manta-pay/src/test/ledger.rs +++ b/manta-pay/src/test/ledger.rs @@ -21,7 +21,7 @@ use crate::config::{ Config, EncryptedNote, MerkleTreeConfiguration, MultiVerifyingContext, ProofSystem, - TransferPost, Utxo, VoidNumber, + TransferPost, Utxo, UtxoSetModel, VoidNumber, }; use alloc::{rc::Rc, vec::Vec}; use async_std::sync::RwLock; @@ -110,7 +110,7 @@ impl Ledger { /// Builds an empty [`Ledger`]. #[inline] pub fn new( - utxo_forest_parameters: merkle_tree::Parameters<MerkleTreeConfiguration>, + utxo_forest_parameters: UtxoSetModel, verifying_context: MultiVerifyingContext, ) -> Self { Self { @@ -501,7 +501,7 @@ mod test { ledger: &SharedLedger, cache: &OnDiskMultiProvingContext, parameters: &transfer::Parameters<Config>, - utxo_set_parameters: &merkle_tree::Parameters<MerkleTreeConfiguration>, + utxo_set_model: &UtxoSetModel, rng: &mut R, ) -> Wallet<LedgerConnection> where @@ -513,7 +513,7 @@ mod test { AccountTable::new(rng.gen()), cache.clone(), parameters.clone(), - wallet::UtxoSet::new(utxo_set_parameters.clone()), + wallet::UtxoSet::new(utxo_set_model.clone()), rng.seed_rng().expect("Failed to sample PRNG for signer."), ), ) @@ -529,11 +529,11 @@ mod test { let mut rng = thread_rng(); let parameters = rng.gen(); - let utxo_set_parameters = rng.gen(); + let utxo_set_model = rng.gen(); let (proving_context, verifying_context) = transfer::canonical::generate_context( &(), - FullParameters::new(&parameters, &utxo_set_parameters), + FullParameters::new(&parameters, &utxo_set_model), &mut rng, ) .expect("Failed to generate contexts."); @@ -546,7 +546,7 @@ mod test { const ACTOR_COUNT: usize = 10; - let mut ledger = Ledger::new(utxo_set_parameters.clone(), verifying_context); + let mut ledger = Ledger::new(utxo_set_model.clone(), verifying_context); for i in 0..ACTOR_COUNT { ledger.set_public_balance(AccountId(i as u64), AssetId(0), AssetValue(1000000)); @@ -566,7 +566,7 @@ mod test { &ledger, &cache, &parameters, - &utxo_set_parameters, + &utxo_set_model, &mut rng, ), Default::default(), From 68ba441bb2f3c1a87eb6ac507f9ada7c255a6c26 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 24 Jan 2022 13:03:30 -0500 Subject: [PATCH 201/275] fix: correct the decode/encode type for proving contexts --- manta-pay/src/crypto/constraint/arkworks/proof_system.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs index 738c7eb74..e30c6d197 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs @@ -307,7 +307,7 @@ pub mod groth16 { R: codec::Read, { let mut reader = ArkReader::new(reader); - match CanonicalDeserialize::deserialize(&mut reader) { + match CanonicalDeserialize::deserialize_unchecked(&mut reader) { Ok(value) => reader .finish() .map(move |_| Self(value)) @@ -327,7 +327,7 @@ pub mod groth16 { W: codec::Write, { let mut writer = ArkWriter::new(writer); - let _ = self.0.serialize(&mut writer); + let _ = self.0.serialize_unchecked(&mut writer); writer.finish().map(move |_| ()) } } From d756ce8d795957bcf2abf2507f46dc5a6831d913 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 24 Jan 2022 20:16:20 -0500 Subject: [PATCH 202/275] fix: add new sampling method --- manta-accounting/src/transfer/canonical.rs | 4 +-- manta-accounting/src/transfer/mod.rs | 32 ++++++++++++++++++++++ manta-accounting/src/transfer/test.rs | 2 +- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/manta-accounting/src/transfer/canonical.rs b/manta-accounting/src/transfer/canonical.rs index 20cfcc6a3..977e1f3db 100644 --- a/manta-accounting/src/transfer/canonical.rs +++ b/manta-accounting/src/transfer/canonical.rs @@ -159,9 +159,9 @@ where pub fn build( senders: [Sender<C>; ReclaimShape::SENDERS], receivers: [Receiver<C>; ReclaimShape::RECEIVERS], - reclaim: Asset, + asset: Asset, ) -> Self { - Self::new_unchecked(Some(reclaim.id), [], senders, receivers, [reclaim.value]) + Self::new_unchecked(Some(asset.id), [], senders, receivers, [asset.value]) } } diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index 6276a9ed0..0f866bc04 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -588,6 +588,24 @@ where } } +impl<C, D> Sample<D> for SpendingKey<C> +where + C: Configuration, + D: Clone, + SecretKey<C>: Sample<D>, +{ + #[inline] + fn sample<R>(distribution: D, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + Self::new( + Sample::sample(distribution.clone(), rng), + Sample::sample(distribution, rng), + ) + } +} + /// Receiving Key #[derive(derivative::Derivative)] #[derivative( @@ -717,6 +735,20 @@ where { Some(self.get_proof(utxo_set)?.upgrade(self)) } + + /// Inserts the [`Utxo`] corresponding to `self` into the `utxo_set` and upgrades to a full + /// [`Sender`] if the insertion succeeded. + #[inline] + pub fn insert_and_upgrade<S>(self, utxo_set: &mut S) -> Option<Sender<C>> + where + S: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + { + if self.insert_utxo(utxo_set) { + self.try_upgrade(utxo_set) + } else { + None + } + } } /// Sender Proof diff --git a/manta-accounting/src/transfer/test.rs b/manta-accounting/src/transfer/test.rs index 3c82a2df3..7579d55b2 100644 --- a/manta-accounting/src/transfer/test.rs +++ b/manta-accounting/src/transfer/test.rs @@ -44,7 +44,7 @@ where R: CryptoRng + RngCore + ?Sized, { if count == 0 { - return Default::default(); + return Vec::default(); } let mut result = Vec::with_capacity(count + 1); result.push(AssetValue(0)); From 23629059f4a81952075e40fcad1d64f5d052042c Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 24 Jan 2022 21:07:27 -0500 Subject: [PATCH 203/275] fix: move bip32 dependency out of the default feature-set --- manta-pay/Cargo.toml | 11 ++++++---- manta-pay/src/config.rs | 41 ++++++++----------------------------- manta-pay/src/lib.rs | 31 ++++++++++++++++++++++++---- manta-pay/src/wallet/mod.rs | 28 ++++++++++++++++++++++--- 4 files changed, 67 insertions(+), 44 deletions(-) diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index f2a9e0e0d..75abfd7b4 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -52,6 +52,9 @@ groth16 = ["arkworks", "ark-groth16"] # Enable PLONK ZKP System plonk = ["zk-garage-plonk"] +# Hierarchical-Deterministic Wallet Keys +hd-wallet = ["bip32"] + # Testing Frameworks test = ["manta-accounting/test", "manta-crypto/test"] @@ -74,14 +77,14 @@ ark-relations = { version = "0.3.0", optional = true, default-features = false } ark-serialize = { version = "0.3.0", optional = true, default-features = false } ark-std = { version = "0.3.0", optional = true, default-features = false } async-std = { version = "1.10.0", features = ["unstable"] } -bip32 = { version = "0.3.0", default-features = false, features = ["bip39", "secp256k1"] } +bip32 = { version = "0.3.0", default-features = false, features = ["bip39", "secp256k1"], optional = true } blake2 = { version = "0.10.0", default-features = false } derivative = { version = "2.2.0", default-features = false } generic-array = { version = "0.14.4", default-features = false } indexmap = { version = "1.8.0", default-features = false } -manta-accounting = { path = "../manta-accounting" } -manta-crypto = { path = "../manta-crypto" } -manta-util = { path = "../manta-util" } +manta-accounting = { path = "../manta-accounting", default-features = false } +manta-crypto = { path = "../manta-crypto", default-features = false } +manta-util = { path = "../manta-util", default-features = false } rand = { version = "0.8.4", optional = true } rand_chacha = { version = "0.3.1", default-features = false } scale-codec = { package = "parity-scale-codec", version = "2.3.1", default-features = false, features = ["derive"] } diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index a30eb782e..c8c09df18 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -16,18 +16,15 @@ //! Manta-Pay Configuration -use crate::{ - crypto::{ - constraint::arkworks::{groth16, Boolean, Fp, FpVar, R1CS}, - ecc::{self, arkworks::ProjectiveCurve}, - encryption::aes::{self, AesGcm}, - hash::poseidon, - key::Blake2sKdf, - }, - key::TestnetKeySecret, +use crate::crypto::{ + constraint::arkworks::{groth16, Boolean, Fp, FpVar, R1CS}, + ecc, + encryption::aes::{self, AesGcm}, + hash::poseidon, + key::Blake2sKdf, }; use alloc::vec::Vec; -use ark_ff::{PrimeField, ToConstraintField}; +use ark_ff::ToConstraintField; use ark_serialize::{CanonicalSerialize, SerializationError}; use blake2::{ digest::{Update, VariableOutput}, @@ -37,7 +34,6 @@ use bls12_381::Bls12_381; use bls12_381_ed::constraints::EdwardsVar as Bls12_381_EdwardsVar; use manta_accounting::{ asset::{Asset, AssetId, AssetValue}, - key::HierarchicalKeyDerivationScheme, transfer, }; use manta_crypto::{ @@ -49,9 +45,7 @@ use manta_crypto::{ ecc::DiffieHellman, encryption, hash::{BinaryHashFunction, HashFunction}, - key, - key::KeyDerivationFunction, - merkle_tree, + key, merkle_tree, }; use manta_util::codec::{Decode, DecodeError, Encode, Read, Write}; @@ -652,25 +646,6 @@ pub type MultiProvingContext = transfer::canonical::MultiProvingContext<Config>; /// Multi-Verifying Context Type pub type MultiVerifyingContext = transfer::canonical::MultiVerifyingContext<Config>; -/// Hierarchical Key Derivation Function -pub struct HierarchicalKeyDerivationFunction; - -impl KeyDerivationFunction for HierarchicalKeyDerivationFunction { - type Key = <TestnetKeySecret as HierarchicalKeyDerivationScheme>::SecretKey; - type Output = SecretKey; - - #[inline] - fn derive(secret_key: &Self::Key) -> Self::Output { - // FIXME: Check that this conversion is logical/safe. - let bytes: [u8; 32] = secret_key - .private_key() - .to_bytes() - .try_into() - .expect("The secret key has 32 bytes."); - Fp(<Bls12_381_Edwards as ProjectiveCurve>::ScalarField::from_le_bytes_mod_order(&bytes)) - } -} - impl MerkleTreeConfiguration { /// Width of the Merkle Forest pub const FOREST_WIDTH: usize = 256; diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index 0ea0348bb..14c44ee00 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -24,22 +24,45 @@ extern crate alloc; pub mod crypto; + +#[cfg(feature = "hd-wallet")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "hd-wallet")))] pub mod key; #[cfg(all(feature = "arkworks", feature = "groth16"))] #[cfg_attr(doc_cfg, doc(cfg(all(feature = "arkworks", feature = "groth16"))))] pub mod config; -#[cfg(all(feature = "std", feature = "arkworks", feature = "groth16"))] +#[cfg(all( + feature = "std", + feature = "arkworks", + feature = "groth16", + feature = "hd-wallet" +))] #[cfg_attr( doc_cfg, - doc(cfg(all(feature = "std", feature = "arkworks", feature = "groth16"))) + doc(cfg(all( + feature = "std", + feature = "arkworks", + feature = "groth16", + feature = "hd-wallet" + ))) )] pub mod wallet; -#[cfg(all(feature = "std", feature = "arkworks", feature = "groth16"))] +#[cfg(all( + feature = "std", + feature = "arkworks", + feature = "groth16", + feature = "hd-wallet" +))] #[cfg_attr( doc_cfg, - doc(cfg(all(feature = "std", feature = "arkworks", feature = "groth16"))) + doc(cfg(all( + feature = "std", + feature = "arkworks", + feature = "groth16", + feature = "hd-wallet" + ))) )] pub mod test; diff --git a/manta-pay/src/wallet/mod.rs b/manta-pay/src/wallet/mod.rs index a329810ac..ed71af7c7 100644 --- a/manta-pay/src/wallet/mod.rs +++ b/manta-pay/src/wallet/mod.rs @@ -17,21 +17,43 @@ //! Manta Pay Base Wallet Implementation use crate::{ - config::{Config, HierarchicalKeyDerivationFunction, MerkleTreeConfiguration}, + config::{Bls12_381_Edwards, Config, MerkleTreeConfiguration, SecretKey}, + crypto::constraint::arkworks::Fp, key::TestnetKeySecret, }; +use ark_ec::ProjectiveCurve; +use ark_ff::PrimeField; use manta_accounting::{ asset::HashAssetMap, - key, + key::{self, HierarchicalKeyDerivationScheme}, wallet::{ self, signer::{self, AssetMapKey}, }, }; -use manta_crypto::merkle_tree; +use manta_crypto::{key::KeyDerivationFunction, merkle_tree}; pub mod cache; +/// Hierarchical Key Derivation Function +pub struct HierarchicalKeyDerivationFunction; + +impl KeyDerivationFunction for HierarchicalKeyDerivationFunction { + type Key = <TestnetKeySecret as HierarchicalKeyDerivationScheme>::SecretKey; + type Output = SecretKey; + + #[inline] + fn derive(secret_key: &Self::Key) -> Self::Output { + // FIXME: Check that this conversion is logical/safe. + let bytes: [u8; 32] = secret_key + .private_key() + .to_bytes() + .try_into() + .expect("The secret key has 32 bytes."); + Fp(<Bls12_381_Edwards as ProjectiveCurve>::ScalarField::from_le_bytes_mod_order(&bytes)) + } +} + /// Signer UTXO Set pub type UtxoSet = merkle_tree::forest::TreeArrayMerkleForest< MerkleTreeConfiguration, From 6c38d4d51bc5f5207741e182a87fc2e6381d71cc Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 25 Jan 2022 10:54:29 -0500 Subject: [PATCH 204/275] fix: sharpen async-std dependency --- manta-pay/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 75abfd7b4..e693b4e9a 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -76,7 +76,7 @@ ark-r1cs-std = { version = "0.3.1", optional = true, default-features = false } ark-relations = { version = "0.3.0", optional = true, default-features = false } ark-serialize = { version = "0.3.0", optional = true, default-features = false } ark-std = { version = "0.3.0", optional = true, default-features = false } -async-std = { version = "1.10.0", features = ["unstable"] } +async-std = { version = "1.10.0", default-features = false } bip32 = { version = "0.3.0", default-features = false, features = ["bip39", "secp256k1"], optional = true } blake2 = { version = "0.10.0", default-features = false } derivative = { version = "2.2.0", default-features = false } From d8e35053199f28652422f4c8d62b06fc5491d157 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 26 Jan 2022 16:18:59 -0500 Subject: [PATCH 205/275] fix: remove some inconsistencies in codec and improve feature selection --- manta-accounting/Cargo.toml | 2 +- manta-accounting/src/transfer/canonical.rs | 2 ++ manta-crypto/Cargo.toml | 2 +- manta-pay/Cargo.toml | 10 ++++---- manta-pay/src/lib.rs | 28 ++++++---------------- manta-pay/src/test/ledger.rs | 7 ++++-- manta-pay/src/test/mod.rs | 1 - manta-pay/src/wallet/cache.rs | 27 ++++++++++----------- manta-pay/src/wallet/mod.rs | 4 +++- manta-util/Cargo.toml | 2 +- manta-util/src/lib.rs | 2 +- 11 files changed, 38 insertions(+), 49 deletions(-) diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index c043a82b8..d9d7ab7ec 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "manta-accounting" -edition = "2021" version = "0.4.0" +edition = "2021" authors = ["Manta Network <contact@manta.network>"] readme = "README.md" license-file = "LICENSE" diff --git a/manta-accounting/src/transfer/canonical.rs b/manta-accounting/src/transfer/canonical.rs index 977e1f3db..1c46a6120 100644 --- a/manta-accounting/src/transfer/canonical.rs +++ b/manta-accounting/src/transfer/canonical.rs @@ -16,6 +16,8 @@ //! Canonical Transfer Types +// TODO: Add typing for `ProvingContext` and `VerifyingContext` against the canonical shapes. + use crate::{ asset::{self, Asset, AssetValue}, transfer::{ diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index ebc8e3aef..0eaf34627 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "manta-crypto" -edition = "2021" version = "0.4.0" +edition = "2021" authors = ["Manta Network <contact@manta.network>"] readme = "README.md" license-file = "LICENSE" diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index e693b4e9a..1d361321c 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "manta-pay" -edition = "2021" version = "0.4.0" +edition = "2021" authors = ["Manta Network <contact@manta.network>"] readme = "README.md" license-file = "LICENSE" @@ -53,10 +53,10 @@ groth16 = ["arkworks", "ark-groth16"] plonk = ["zk-garage-plonk"] # Hierarchical-Deterministic Wallet Keys -hd-wallet = ["bip32"] +hd-wallet = ["bip32", "async-std/unstable"] # Testing Frameworks -test = ["manta-accounting/test", "manta-crypto/test"] +test = ["manta-accounting/test", "manta-crypto/test", "indexmap", "async-std/unstable"] # Simulation Framework simulation = ["rand", "statrs"] @@ -81,7 +81,7 @@ bip32 = { version = "0.3.0", default-features = false, features = ["bip39", "sec blake2 = { version = "0.10.0", default-features = false } derivative = { version = "2.2.0", default-features = false } generic-array = { version = "0.14.4", default-features = false } -indexmap = { version = "1.8.0", default-features = false } +indexmap = { version = "1.8.0", default-features = false, optional = true } manta-accounting = { path = "../manta-accounting", default-features = false } manta-crypto = { path = "../manta-crypto", default-features = false } manta-util = { path = "../manta-util", default-features = false } @@ -97,6 +97,6 @@ criterion = "0.3.5" futures = "0.3.19" manta-accounting = { path = "../manta-accounting", features = ["test"] } manta-crypto = { path = "../manta-crypto", features = ["test"] } -manta-pay = { path = ".", features = ["test"] } +manta-pay = { path = ".", features = ["arkworks", "groth16", "hd-wallet", "std", "test"] } rand = "0.8.4" tempfile = "3.2.0" diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index 14c44ee00..827f02e82 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -23,6 +23,9 @@ extern crate alloc; +#[cfg(test)] +mod test; + pub mod crypto; #[cfg(feature = "hd-wallet")] @@ -34,35 +37,18 @@ pub mod key; pub mod config; #[cfg(all( - feature = "std", feature = "arkworks", feature = "groth16", - feature = "hd-wallet" -))] -#[cfg_attr( - doc_cfg, - doc(cfg(all( - feature = "std", - feature = "arkworks", - feature = "groth16", - feature = "hd-wallet" - ))) -)] -pub mod wallet; - -#[cfg(all( + feature = "hd-wallet", feature = "std", - feature = "arkworks", - feature = "groth16", - feature = "hd-wallet" ))] #[cfg_attr( doc_cfg, doc(cfg(all( - feature = "std", feature = "arkworks", feature = "groth16", - feature = "hd-wallet" + feature = "hd-wallet", + feature = "std", ))) )] -pub mod test; +pub mod wallet; diff --git a/manta-pay/src/test/ledger.rs b/manta-pay/src/test/ledger.rs index 8f7c2065c..e0c67c734 100644 --- a/manta-pay/src/test/ledger.rs +++ b/manta-pay/src/test/ledger.rs @@ -446,7 +446,10 @@ impl ledger::Connection<Config> for LedgerConnection { Ok(posting_key) => { posting_key.post(&(), &mut *ledger); } - _ => return Ok(PushResponse { success: false }), + Err(err) => { + async_std::println!("ERROR: {:?}", err).await; + return Ok(PushResponse { success: false }); + } } } Ok(PushResponse { success: true }) @@ -544,7 +547,7 @@ mod test { .await .expect("Unable to save proving context to disk."); - const ACTOR_COUNT: usize = 10; + const ACTOR_COUNT: usize = 3; let mut ledger = Ledger::new(utxo_set_model.clone(), verifying_context); diff --git a/manta-pay/src/test/mod.rs b/manta-pay/src/test/mod.rs index 4de377922..6a5da2d47 100644 --- a/manta-pay/src/test/mod.rs +++ b/manta-pay/src/test/mod.rs @@ -25,5 +25,4 @@ pub mod ledger; // #[cfg_attr(doc_cfg, doc(cfg(feature = "simulation")))] // pub mod simulation; -#[cfg(test)] pub mod transfer; diff --git a/manta-pay/src/wallet/cache.rs b/manta-pay/src/wallet/cache.rs index bef896930..69f296a79 100644 --- a/manta-pay/src/wallet/cache.rs +++ b/manta-pay/src/wallet/cache.rs @@ -17,33 +17,32 @@ //! Proving Context Caching use crate::config::{MultiProvingContext, ProvingContext}; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; use async_std::{ io, path::{Path, PathBuf}, task, }; use core::marker::PhantomData; -use manta_util::{cache::CachedResource, future::LocalBoxFuture}; +use manta_util::{ + cache::CachedResource, + codec::{Decode, Encode, IoReader, IoWriter}, + future::LocalBoxFuture, +}; use std::fs::{File, OpenOptions}; /// Caching Error #[derive(Debug)] pub enum Error { - /// Serialization Error - Serialization(SerializationError), + /// Encoding Error + Encode, + + /// Decoding Error + Decode, /// I/O Error Io(io::Error), } -impl From<SerializationError> for Error { - #[inline] - fn from(err: SerializationError) -> Self { - Self::Serialization(err) - } -} - impl From<io::Error> for Error { #[inline] fn from(err: io::Error) -> Self { @@ -100,9 +99,7 @@ impl OnDiskMultiProvingContext { Ok(task::spawn_blocking(move || { File::open(path.as_ref()) .map_err(Error::Io) - .and_then(move |f| { - ProvingContext::deserialize_unchecked(f).map_err(Error::Serialization) - }) + .and_then(move |f| ProvingContext::decode(IoReader(f)).map_err(|_| Error::Decode)) }) .await?) } @@ -119,7 +116,7 @@ impl OnDiskMultiProvingContext { .create(true) .open(path.as_ref()) .map_err(Error::Io) - .and_then(move |f| context.serialize_unchecked(f).map_err(Error::Serialization)) + .and_then(move |f| context.encode(IoWriter(f)).map_err(|_| Error::Encode)) }) .await?) } diff --git a/manta-pay/src/wallet/mod.rs b/manta-pay/src/wallet/mod.rs index ed71af7c7..9180881fe 100644 --- a/manta-pay/src/wallet/mod.rs +++ b/manta-pay/src/wallet/mod.rs @@ -14,7 +14,9 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Manta Pay Base Wallet Implementation +//! Manta Pay Wallet Implementation + +// TODO: Build websockets wallet. use crate::{ config::{Bls12_381_Edwards, Config, MerkleTreeConfiguration, SecretKey}, diff --git a/manta-util/Cargo.toml b/manta-util/Cargo.toml index fb2f8f874..a9429d8c1 100644 --- a/manta-util/Cargo.toml +++ b/manta-util/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "manta-util" -edition = "2021" version = "0.4.0" +edition = "2021" authors = ["Manta Network <contact@manta.network>"] readme = "README.md" license-file = "LICENSE" diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index 58c65df68..246e9686d 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -16,7 +16,7 @@ //! Utilities -#![cfg_attr(not(any(feature = "std", test)), no_std)] +#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(doc_cfg, feature(doc_cfg))] #![forbid(rustdoc::broken_intra_doc_links)] #![forbid(missing_docs)] From 76384f9585b23d1fd09ec4fdee1858454dd5ded8 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 26 Jan 2022 18:23:10 -0500 Subject: [PATCH 206/275] wip: start removing asynchronous protocols --- manta-accounting/Cargo.toml | 15 ++- manta-accounting/src/fs.rs | 108 +++++++--------- manta-accounting/src/wallet/ledger.rs | 8 +- manta-accounting/src/wallet/signer.rs | 62 ++++------ manta-accounting/src/wallet/state.rs | 20 ++- manta-accounting/src/wallet/test/mod.rs | 157 +++++++++++------------- manta-accounting/src/wallet/test/sim.rs | 66 +++------- manta-util/src/cache.rs | 14 +-- manta-util/src/future.rs | 26 ---- manta-util/src/lib.rs | 9 +- 10 files changed, 179 insertions(+), 306 deletions(-) delete mode 100644 manta-util/src/future.rs diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index d9d7ab7ec..39dae76cd 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -26,25 +26,28 @@ maintenance = { status = "actively-developed" } [features] # Cocoon Filesystem Adapters -cocoon-fs = ["async-std", "cocoon/std", "zeroize"] +cocoon-fs = ["std", "zeroize"] # Standard Library -std = ["async-std", "cocoon/std", "manta-util/std"] +std = [ + "cocoon/std", + "manta-crypto/std", + "manta-util/std", +] # Testing Frameworks -test = ["async-std", "futures", "indexmap", "rand/alloc", "statrs"] +test = ["indexmap", "parking_lot", "rand/alloc", "statrs"] [dependencies] -async-std = { version = "1.10.0", optional = true } cocoon = { version = "0.3.0", optional = true, default-features = false } derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } derive_more = { version = "0.99.16", default-features = false, features = ["add", "add_assign", "display", "from", "mul", "mul_assign", "sum"] } -futures = { version = "0.3.19", optional = true, default-features = false, features = ["alloc"] } indexmap = { version = "1.8.0", optional = true, default-features = false } manta-crypto = { path = "../manta-crypto", default-features = false } manta-util = { path = "../manta-util", features = ["alloc"] } -statrs = { version = "0.15.0", optional = true, default-features = false } +parking_lot = { version = "0.11.2", optional = true, default-features = false } rand = { version = "0.8.4", optional = true, default-features = false } +statrs = { version = "0.15.0", optional = true, default-features = false } zeroize = { version = "1.4.3", optional = true, default-features = false, features = ["alloc"] } [dev-dependencies] diff --git a/manta-accounting/src/fs.rs b/manta-accounting/src/fs.rs index bf36685e8..f44b1ce1d 100644 --- a/manta-accounting/src/fs.rs +++ b/manta-accounting/src/fs.rs @@ -16,14 +16,11 @@ //! Encrypted Filesystem Primitives -// FIXME: Add asynchronous streaming interfaces. +// FIXME: Add streaming interfaces. -use alloc::{boxed::Box, vec::Vec}; +use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; -use manta_util::{ - codec::{Decode, Encode}, - future::LocalBoxFuture, -}; +use manta_util::codec::{Decode, Encode}; /// Filesystem Encrypted Saving pub trait SaveEncrypted { @@ -37,23 +34,19 @@ pub trait SaveEncrypted { type Error; /// Saves the `payload` to `path` using the `saving_key` to encrypt it. - fn save_bytes<'s, P>( + fn save_bytes<P>( path: P, - saving_key: &'s Self::SavingKey, + saving_key: &Self::SavingKey, payload: Vec<u8>, - ) -> LocalBoxFuture<'s, Result<(), Self::Error>> + ) -> Result<(), Self::Error> where - P: 's + AsRef<Self::Path>; + P: AsRef<Self::Path>; /// Saves the `payload` to `path` after serializing using the `saving_key` to encrypt it. #[inline] - fn save<'s, P, E, C>( - path: P, - saving_key: &'s Self::SavingKey, - payload: &'s E, - ) -> LocalBoxFuture<'s, Result<(), Self::Error>> + fn save<P, E, C>(path: P, saving_key: &Self::SavingKey, payload: &E) -> Result<(), Self::Error> where - P: 's + AsRef<Self::Path>, + P: AsRef<Self::Path>, E: Encode<C>, { Self::save_bytes(path, saving_key, payload.to_vec()) @@ -72,30 +65,22 @@ pub trait LoadDecrypted { type Error; /// Loads a vector of bytes from `path` using `loading_key` to decrypt them. - fn load_bytes<'s, P>( - path: P, - loading_key: &'s Self::LoadingKey, - ) -> LocalBoxFuture<'s, Result<Vec<u8>, Self::Error>> + fn load_bytes<P>(path: P, loading_key: &Self::LoadingKey) -> Result<Vec<u8>, Self::Error> where - P: 's + AsRef<Self::Path>; + P: AsRef<Self::Path>; /// Loads a vector of bytes from `path` using `loading_key` to decrypt them, then deserializing /// the bytes to a concrete value of type `D`. #[inline] - fn load<'s, P, D, C>( - path: P, - loading_key: &'s Self::LoadingKey, - ) -> LocalBoxFuture<'s, Result<D, LoadError<Self, D, C>>> + fn load<P, D, C>(path: P, loading_key: &Self::LoadingKey) -> Result<D, LoadError<Self, D, C>> where - P: 's + AsRef<Self::Path>, + P: AsRef<Self::Path>, D: Decode<C>, { - Box::pin(async { - match Self::load_bytes(path, loading_key).await { - Ok(bytes) => D::from_vec(bytes).map_err(LoadError::Decode), - Err(err) => Err(LoadError::Loading(err)), - } - }) + match Self::load_bytes(path, loading_key) { + Ok(bytes) => D::from_vec(bytes).map_err(LoadError::Decode), + Err(err) => Err(LoadError::Loading(err)), + } } } @@ -126,14 +111,14 @@ where #[cfg_attr(doc_cfg, doc(cfg(feature = "cocoon-fs")))] pub mod cocoon { use super::*; - use async_std::{ - fs::OpenOptions, - io::{Error as IoError, ReadExt, WriteExt}, - path::Path, - }; use cocoon_crate::{Cocoon, Error as CocoonError}; use core::fmt; use manta_util::from_variant_impl; + use std::{ + fs::OpenOptions, + io::{Error as IoError, Read, Write}, + path::Path, + }; use zeroize::Zeroizing; /// Cocoon Loading/Saving Error @@ -159,8 +144,6 @@ pub mod cocoon { } } - #[cfg(feature = "std")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] impl std::error::Error for Error {} /// Cocoon [`SaveEncrypted`] Adapter @@ -173,27 +156,23 @@ pub mod cocoon { type Error = Error; #[inline] - fn save_bytes<'s, P>( + fn save_bytes<P>( path: P, - saving_key: &'s Self::SavingKey, + saving_key: &Self::SavingKey, payload: Vec<u8>, - ) -> LocalBoxFuture<'s, Result<(), Self::Error>> + ) -> Result<(), Self::Error> where - P: 's + AsRef<Self::Path>, + P: AsRef<Self::Path>, { - Box::pin(async { - let mut buffer = Zeroizing::new(Vec::new()); - Cocoon::new(saving_key).dump(payload, &mut buffer.as_mut_slice())?; - OpenOptions::new() - .create(true) - .truncate(true) - .write(true) - .open(path) - .await? - .write_all(&buffer) - .await?; - Ok(()) - }) + let mut buffer = Zeroizing::new(Vec::new()); + Cocoon::new(saving_key).dump(payload, &mut buffer.as_mut_slice())?; + OpenOptions::new() + .create(true) + .truncate(true) + .write(true) + .open(path)? + .write_all(&buffer)?; + Ok(()) } } @@ -207,19 +186,14 @@ pub mod cocoon { type Error = Error; #[inline] - fn load_bytes<'s, P>( - path: P, - loading_key: &'s Self::LoadingKey, - ) -> LocalBoxFuture<'s, Result<Vec<u8>, Self::Error>> + fn load_bytes<P>(path: P, loading_key: &Self::LoadingKey) -> Result<Vec<u8>, Self::Error> where - P: 's + AsRef<Self::Path>, + P: AsRef<Self::Path>, { - Box::pin(async move { - let mut buffer = Zeroizing::new(Vec::new()); - let mut file = OpenOptions::new().read(true).open(path).await?; - file.read_to_end(&mut buffer).await?; - Ok(Cocoon::parse_only(loading_key).parse(&mut buffer.as_slice())?) - }) + let mut buffer = Zeroizing::new(Vec::new()); + let mut file = OpenOptions::new().read(true).open(path)?; + file.read_to_end(&mut buffer)?; + Ok(Cocoon::parse_only(loading_key).parse(&mut buffer.as_slice())?) } } } diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs index cd42f94bd..a32e01444 100644 --- a/manta-accounting/src/wallet/ledger.rs +++ b/manta-accounting/src/wallet/ledger.rs @@ -23,7 +23,6 @@ use crate::transfer::{Configuration, EncryptedNote, TransferPost, Utxo, VoidNumber}; use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; -use manta_util::future::LocalBoxFuture; /// Ledger Checkpoint pub trait Checkpoint: Default + PartialOrd { @@ -53,14 +52,11 @@ where /// Pulls receiver data from the ledger starting from `checkpoint`, returning the current /// [`Checkpoint`](Self::Checkpoint). - fn pull<'s>( - &'s mut self, - checkpoint: &'s Self::Checkpoint, - ) -> LocalBoxFuture<'s, PullResult<C, Self>>; + fn pull(&mut self, checkpoint: &Self::Checkpoint) -> PullResult<C, Self>; /// Sends `posts` to the ledger, returning `true` or `false` depending on whether the entire /// batch succeeded or not. - fn push(&mut self, posts: Vec<TransferPost<C>>) -> LocalBoxFuture<PushResult<C, Self>>; + fn push(&mut self, posts: Vec<TransferPost<C>>) -> PushResult<C, Self>; } /// Ledger Source Pull Result diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index d9f35ad43..1864aaa2c 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -48,7 +48,7 @@ use crate::{ Utxo, VoidNumber, }, }; -use alloc::{boxed::Box, vec, vec::Vec}; +use alloc::{vec, vec::Vec}; use core::{convert::Infallible, fmt::Debug}; use manta_crypto::{ accumulator::{ @@ -59,9 +59,7 @@ use manta_crypto::{ }; use manta_util::{ cache::{CachedResource, CachedResourceError}, - fallible_array_map, - future::LocalBoxFuture, - into_array_unchecked, + fallible_array_map, into_array_unchecked, iter::IteratorExt, persistance::Rollback, }; @@ -76,21 +74,16 @@ where /// Pushes updates from the ledger to the wallet, synchronizing it with the ledger state and /// returning an updated asset distribution. - fn sync<'s, I, R>( - &'s mut self, - starting_index: usize, - inserts: I, - removes: R, - ) -> LocalBoxFuture<'s, SyncResult<C, Self>> + fn sync<I, R>(&mut self, starting_index: usize, inserts: I, removes: R) -> SyncResult<C, Self> where - I: 's + IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, - R: 's + IntoIterator<Item = VoidNumber<C>>; + I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, + R: IntoIterator<Item = VoidNumber<C>>; /// Signs a `transaction` and returns the ledger transfer posts if successful. - fn sign(&mut self, transaction: Transaction<C>) -> LocalBoxFuture<SignResult<C, Self>>; + fn sign(&mut self, transaction: Transaction<C>) -> SignResult<C, Self>; /// Returns a new [`ReceivingKey`] for `self` to receive assets. - fn receiving_key(&mut self) -> LocalBoxFuture<ReceivingKeyResult<C, Self>>; + fn receiving_key(&mut self) -> ReceivingKeyResult<C, Self>; } /// Synchronization Result @@ -259,10 +252,10 @@ where { /// Returns the public parameters by reading from the proving context cache. #[inline] - pub async fn get( + pub fn get( &mut self, ) -> Result<(&Parameters<C>, &MultiProvingContext<C>), ProvingContextCacheError<C>> { - let reading_key = self.proving_context.aquire().await?; + let reading_key = self.proving_context.aquire()?; Ok((&self.parameters, self.proving_context.read(reading_key))) } } @@ -803,7 +796,7 @@ where /// Signs a withdraw transaction for `asset` sent to `receiver`. #[inline] - async fn sign_withdraw( + fn sign_withdraw( &mut self, asset: Asset, receiver: Option<ReceivingKey<C>>, @@ -815,7 +808,6 @@ where let (parameters, proving_context) = self .parameters .get() - .await .map_err(Error::ProvingContextCacheError)?; let mut posts = Vec::new(); let senders = self.state.compute_batched_transactions( @@ -846,7 +838,7 @@ where /// Signs the `transaction`, generating transfer posts without releasing resources. #[inline] - async fn sign_internal(&mut self, transaction: Transaction<C>) -> SignResult<C, Self> { + fn sign_internal(&mut self, transaction: Transaction<C>) -> SignResult<C, Self> { match transaction { Transaction::Mint(asset) => { let receiver = self @@ -855,7 +847,6 @@ where let (parameters, proving_context) = self .parameters .get() - .await .map_err(Error::ProvingContextCacheError)?; Ok(SignResponse::new(vec![self.state.mint_post( parameters, @@ -864,20 +855,20 @@ where )?])) } Transaction::PrivateTransfer(asset, receiver) => { - self.sign_withdraw(asset, Some(receiver)).await + self.sign_withdraw(asset, Some(receiver)) } - Transaction::Reclaim(asset) => self.sign_withdraw(asset, None).await, + Transaction::Reclaim(asset) => self.sign_withdraw(asset, None), } } /// Signs the `transaction`, generating transfer posts. #[inline] - pub async fn sign(&mut self, transaction: Transaction<C>) -> SignResult<C, Self> { + pub fn sign(&mut self, transaction: Transaction<C>) -> SignResult<C, Self> { // TODO: Should we do a time-based release mechanism to amortize the cost of reading // from the proving context cache? - let result = self.sign_internal(transaction).await; + let result = self.sign_internal(transaction); self.state.utxo_set.rollback(); - self.parameters.proving_context.release().await; + self.parameters.proving_context.release(); result } @@ -902,26 +893,21 @@ where type Error = Error<C>; #[inline] - fn sync<'s, I, R>( - &'s mut self, - starting_index: usize, - inserts: I, - removes: R, - ) -> LocalBoxFuture<'s, SyncResult<C, Self>> + fn sync<I, R>(&mut self, starting_index: usize, inserts: I, removes: R) -> SyncResult<C, Self> where - I: 's + IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, - R: 's + IntoIterator<Item = VoidNumber<C>>, + I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, + R: IntoIterator<Item = VoidNumber<C>>, { - Box::pin(async move { self.sync(starting_index, inserts, removes) }) + self.sync(starting_index, inserts, removes) } #[inline] - fn sign(&mut self, transaction: Transaction<C>) -> LocalBoxFuture<SignResult<C, Self>> { - Box::pin(self.sign(transaction)) + fn sign(&mut self, transaction: Transaction<C>) -> SignResult<C, Self> { + self.sign(transaction) } #[inline] - fn receiving_key(&mut self) -> LocalBoxFuture<ReceivingKeyResult<C, Self>> { - Box::pin(async move { self.receiving_key() }) + fn receiving_key(&mut self) -> ReceivingKeyResult<C, Self> { + self.receiving_key() } } diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index da5faba08..9ce82f5d2 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -265,7 +265,7 @@ where /// Pulls data from the `ledger`, synchronizing the wallet and balance state. #[inline] - pub async fn sync(&mut self) -> Result<(), Error<C, L, S>> { + pub fn sync(&mut self) -> Result<(), Error<C, L, S>> { // FIXME: What should be done when we receive an `InconsistentSynchronization` error from // the signer? // - One option is to do some sort of (exponential) backoff algorithm to find the @@ -281,7 +281,6 @@ where } = self .ledger .pull(&self.checkpoint) - .await .map_err(Error::LedgerError)?; if checkpoint < self.checkpoint { return Err(Error::InconsistentCheckpoint); @@ -289,7 +288,6 @@ where match self .signer .sync(self.checkpoint.receiver_index(), receivers, senders) - .await .map_err(Error::SignerError)? { SyncResponse::Partial { deposit, withdraw } => { @@ -335,23 +333,19 @@ where /// This method returns an error in any other case. The internal state of the wallet is kept /// consistent between calls and recoverable errors are returned for the caller to handle. #[inline] - pub async fn post(&mut self, transaction: Transaction<C>) -> Result<bool, Error<C, L, S>> { - self.sync().await?; + pub fn post(&mut self, transaction: Transaction<C>) -> Result<bool, Error<C, L, S>> { + self.sync()?; self.check(&transaction) .map_err(Error::InsufficientBalance)?; - let SignResponse { posts } = self - .signer - .sign(transaction) - .await - .map_err(Error::SignerError)?; - let PushResponse { success } = self.ledger.push(posts).await.map_err(Error::LedgerError)?; + let SignResponse { posts } = self.signer.sign(transaction).map_err(Error::SignerError)?; + let PushResponse { success } = self.ledger.push(posts).map_err(Error::LedgerError)?; Ok(success) } /// Returns a new [`ReceivingKey`] for `self` to receive assets. #[inline] - pub async fn receiving_key(&mut self) -> Result<ReceivingKey<C>, S::Error> { - self.signer.receiving_key().await + pub fn receiving_key(&mut self) -> Result<ReceivingKey<C>, S::Error> { + self.signer.receiving_key() } } diff --git a/manta-accounting/src/wallet/test/mod.rs b/manta-accounting/src/wallet/test/mod.rs index 869ec4acd..3fef67ad0 100644 --- a/manta-accounting/src/wallet/test/mod.rs +++ b/manta-accounting/src/wallet/test/mod.rs @@ -23,12 +23,11 @@ use crate::{ transfer::{canonical::Transaction, Configuration, PublicKey, ReceivingKey}, wallet::{self, ledger, signer, Wallet}, }; -use alloc::{boxed::Box, rc::Rc}; -use async_std::sync::RwLock; +use alloc::rc::Rc; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use indexmap::IndexSet; use manta_crypto::rand::{CryptoRng, RngCore, Sample}; -use manta_util::future::LocalBoxFuture; +use parking_lot::RwLock; use rand::{distributions::Distribution, Rng}; use statrs::{ distribution::{Categorical, Discrete}, @@ -178,7 +177,7 @@ impl Sample<ActionDistribution> for ActionType { /// Public Balance Oracle pub trait PublicBalanceOracle { /// Returns the public balances of `self`. - fn public_balances(&self) -> LocalBoxFuture<Option<AssetList>>; + fn public_balances(&self) -> Option<AssetList>; } /// Actor @@ -223,13 +222,13 @@ where /// Samples a deposit from `self` using `rng` returning `None` if no deposit is possible. #[inline] - async fn sample_deposit<R>(&mut self, rng: &mut R) -> Option<Asset> + fn sample_deposit<R>(&mut self, rng: &mut R) -> Option<Asset> where L: PublicBalanceOracle, R: CryptoRng + RngCore + ?Sized, { - let _ = self.wallet.sync().await; - let assets = self.wallet.ledger().public_balances().await?; + let _ = self.wallet.sync(); + let assets = self.wallet.ledger().public_balances()?; let len = assets.len(); if len == 0 { return None; @@ -245,11 +244,11 @@ where /// This method samples from a uniform distribution over the asset IDs and asset values present /// in the balance state of `self`. #[inline] - async fn sample_withdraw<R>(&mut self, rng: &mut R) -> Option<Asset> + fn sample_withdraw<R>(&mut self, rng: &mut R) -> Option<Asset> where R: CryptoRng + RngCore + ?Sized, { - let _ = self.wallet.sync().await; + let _ = self.wallet.sync(); let assets = self.wallet.assets(); let len = assets.len(); if len == 0 { @@ -328,97 +327,85 @@ where type Event = Event<C, L, S>; #[inline] - fn sample<'s, R>( - &'s self, - actor: &'s mut Self::Actor, - rng: &'s mut R, - ) -> LocalBoxFuture<'s, Option<Self::Action>> + fn sample<R>(&self, actor: &mut Self::Actor, rng: &mut R) -> Option<Self::Action> where R: CryptoRng + RngCore + ?Sized, { - Box::pin(async { - actor.reduce_lifetime()?; - let action = actor.distribution.sample(rng); - Some(match action { - ActionType::Skip => Action::Skip, - ActionType::Mint => match actor.sample_deposit(rng).await { + actor.reduce_lifetime()?; + let action = actor.distribution.sample(rng); + Some(match action { + ActionType::Skip => Action::Skip, + ActionType::Mint => match actor.sample_deposit(rng) { + Some(asset) => Action::Post(Transaction::Mint(asset)), + _ => Action::Skip, + }, + ActionType::PrivateTransfer => match actor.sample_withdraw(rng) { + Some(asset) => { + let public_keys = self.public_keys.read(); + let len = public_keys.len(); + if len == 0 { + Action::GeneratePublicKey + } else { + Action::Post(Transaction::PrivateTransfer( + asset, + public_keys[rng.gen_range(0..len)].clone(), + )) + } + } + _ => match actor.sample_deposit(rng) { Some(asset) => Action::Post(Transaction::Mint(asset)), _ => Action::Skip, }, - ActionType::PrivateTransfer => match actor.sample_withdraw(rng).await { - Some(asset) => { - let public_keys = self.public_keys.read().await; - let len = public_keys.len(); - if len == 0 { - Action::GeneratePublicKey - } else { - Action::Post(Transaction::PrivateTransfer( - asset, - public_keys[rng.gen_range(0..len)].clone(), - )) - } - } - _ => match actor.sample_deposit(rng).await { - Some(asset) => Action::Post(Transaction::Mint(asset)), - _ => Action::Skip, - }, - }, - ActionType::Reclaim => match actor.sample_withdraw(rng).await { - Some(asset) => Action::Post(Transaction::Reclaim(asset)), - _ => match actor.sample_deposit(rng).await { - Some(asset) => Action::Post(Transaction::Mint(asset)), - _ => Action::Skip, - }, + }, + ActionType::Reclaim => match actor.sample_withdraw(rng) { + Some(asset) => Action::Post(Transaction::Reclaim(asset)), + _ => match actor.sample_deposit(rng) { + Some(asset) => Action::Post(Transaction::Mint(asset)), + _ => Action::Skip, }, - ActionType::GeneratePublicKey => Action::GeneratePublicKey, - }) + }, + ActionType::GeneratePublicKey => Action::GeneratePublicKey, }) } #[inline] - fn act<'s>( - &'s self, - actor: &'s mut Self::Actor, - action: Self::Action, - ) -> LocalBoxFuture<'s, Self::Event> { - Box::pin(async move { - match action { - Action::Skip => Event { - action: ActionType::Skip, - result: Ok(true), - }, - Action::Post(transaction) => { - let action = match &transaction { - Transaction::Mint(_) => ActionType::Mint, - Transaction::PrivateTransfer(_, _) => ActionType::PrivateTransfer, - Transaction::Reclaim(_) => ActionType::Reclaim, - }; - let mut retries = 5; // TODO: Make this parameter tunable based on concurrency. - loop { - let result = actor.wallet.post(transaction.clone()).await; - if let Ok(false) = result { - if retries == 0 { - break Event { action, result }; - } else { - retries -= 1; - continue; - } - } else { + fn act(&self, actor: &mut Self::Actor, action: Self::Action) -> Self::Event { + match action { + Action::Skip => Event { + action: ActionType::Skip, + result: Ok(true), + }, + Action::Post(transaction) => { + let action = match &transaction { + Transaction::Mint(_) => ActionType::Mint, + Transaction::PrivateTransfer(_, _) => ActionType::PrivateTransfer, + Transaction::Reclaim(_) => ActionType::Reclaim, + }; + let mut retries = 5; // TODO: Make this parameter tunable based on concurrency. + loop { + let result = actor.wallet.post(transaction.clone()); + if let Ok(false) = result { + if retries == 0 { break Event { action, result }; + } else { + retries -= 1; + continue; } + } else { + break Event { action, result }; } } - Action::GeneratePublicKey => Event { - action: ActionType::GeneratePublicKey, - result: match actor.wallet.receiving_key().await { - Ok(key) => { - self.public_keys.write().await.insert(key); - Ok(true) - } - Err(err) => Err(wallet::Error::SignerError(err)), - }, - }, } - }) + Action::GeneratePublicKey => Event { + action: ActionType::GeneratePublicKey, + result: match actor.wallet.receiving_key() { + Ok(key) => { + self.public_keys.write().insert(key); + Ok(true) + } + Err(err) => Err(wallet::Error::SignerError(err)), + }, + }, + } } } diff --git a/manta-accounting/src/wallet/test/sim.rs b/manta-accounting/src/wallet/test/sim.rs index ea63f71d6..b093cd299 100644 --- a/manta-accounting/src/wallet/test/sim.rs +++ b/manta-accounting/src/wallet/test/sim.rs @@ -16,11 +16,9 @@ //! Actor Simulation Framework -use alloc::{boxed::Box, vec::Vec}; -use core::{fmt::Debug, hash::Hash}; -use futures::stream::{self, select_all::SelectAll, Stream, StreamExt}; +use alloc::vec::Vec; +use core::{fmt::Debug, hash::Hash, iter}; use manta_crypto::rand::{CryptoRng, RngCore}; -use manta_util::future::LocalBoxFuture; /// Abstract Simulation pub trait Simulation { @@ -34,11 +32,7 @@ pub trait Simulation { /// /// This method should return `None` when the actor is done being simulated for this round of /// the simulation. - fn step<'s, R>( - &'s self, - actor: &'s mut Self::Actor, - rng: &'s mut R, - ) -> LocalBoxFuture<'s, Option<Self::Event>> + fn step<R>(&self, actor: &mut Self::Actor, rng: &mut R) -> Option<Self::Event> where R: CryptoRng + RngCore + ?Sized; } @@ -51,11 +45,7 @@ where type Event = S::Event; #[inline] - fn step<'s, R>( - &'s self, - actor: &'s mut Self::Actor, - rng: &'s mut R, - ) -> LocalBoxFuture<'s, Option<Self::Event>> + fn step<R>(&self, actor: &mut Self::Actor, rng: &mut R) -> Option<Self::Event> where R: CryptoRng + RngCore + ?Sized, { @@ -136,26 +126,20 @@ where simulation: &'s S, index: usize, actor: &'s mut S::Actor, - rng: R, - ) -> impl 's + Stream<Item = Event<S>> + mut rng: R, + ) -> impl 's + Iterator<Item = Event<S>> where R: 's + CryptoRng + RngCore, { - stream::unfold((actor, rng), move |mut state| { - Box::pin(async move { - simulation - .step(state.0, &mut state.1) - .await - .map(move |event| (event, state)) - }) - }) - .enumerate() - .map(move |(step, event)| Event::new(index, step, event)) + iter::from_fn(move || simulation.step(actor, &mut rng)) + .enumerate() + .map(move |(step, event)| Event::new(index, step, event)) } + /* TODO: /// Runs the simulator using `rng`, returning a stream of future events. #[inline] - pub fn run<'s, R, F>(&'s mut self, mut rng: F) -> impl 's + Stream<Item = Event<S>> + pub fn run<'s, R, F>(&'s mut self, mut rng: F) -> impl 's + Iterator<Item = Event<S>> where R: 's + CryptoRng + RngCore, F: FnMut() -> R, @@ -166,6 +150,7 @@ where } streams } + */ } impl<S> AsRef<S> for Simulator<S> @@ -193,20 +178,12 @@ pub trait ActionSimulation { /// /// This method should return `None` when the actor is done being simulated for this round of /// the simulation. - fn sample<'s, R>( - &'s self, - actor: &'s mut Self::Actor, - rng: &'s mut R, - ) -> LocalBoxFuture<'s, Option<Self::Action>> + fn sample<R>(&self, actor: &mut Self::Actor, rng: &mut R) -> Option<Self::Action> where R: CryptoRng + RngCore + ?Sized; /// Executes the given `action` on `actor` returning a future event. - fn act<'s>( - &'s self, - actor: &'s mut Self::Actor, - action: Self::Action, - ) -> LocalBoxFuture<'s, Self::Event>; + fn act(&self, actor: &mut Self::Actor, action: Self::Action) -> Self::Event; } /// Action Simulation Wrapper @@ -236,19 +213,12 @@ where type Event = S::Event; #[inline] - fn step<'s, R>( - &'s self, - actor: &'s mut Self::Actor, - rng: &'s mut R, - ) -> LocalBoxFuture<'s, Option<Self::Event>> + fn step<R>(&self, actor: &mut Self::Actor, rng: &mut R) -> Option<Self::Event> where R: CryptoRng + RngCore + ?Sized, { - Box::pin(async { - match self.0.sample(actor, rng).await { - Some(action) => Some(self.0.act(actor, action).await), - _ => None, - } - }) + self.0 + .sample(actor, rng) + .map(move |action| self.0.act(actor, action)) } } diff --git a/manta-util/src/cache.rs b/manta-util/src/cache.rs index d871017a2..093979b2e 100644 --- a/manta-util/src/cache.rs +++ b/manta-util/src/cache.rs @@ -16,8 +16,6 @@ //! Caching Utilities -use crate::future::LocalBoxFuture; -use alloc::boxed::Box; use core::{convert::Infallible, ops::Deref}; /// Cached Resource @@ -35,7 +33,7 @@ pub trait CachedResource<T> { /// /// This method should be idempotent unless calls to [`aquire`](Self::aquire) are interleaved /// with calls to [`release`](Self::release). - fn aquire(&mut self) -> LocalBoxFuture<Result<Self::ReadingKey, Self::Error>>; + fn aquire(&mut self) -> Result<Self::ReadingKey, Self::Error>; /// Reads the resource, spending the `reading_key`. The reference can be held on to until /// [`release`](Self::release) is called or the reference falls out of scope. @@ -48,7 +46,7 @@ pub trait CachedResource<T> { /// This method should be idempotent unless calls to [`release`](Self::release) are interleaved /// with calls to [`aquire`](Self::aquire). This method can be a no-op if the resource was not /// aquired. - fn release(&mut self) -> LocalBoxFuture; + fn release(&mut self); } /// Cached Resource Error Type @@ -62,8 +60,8 @@ where type Error = Infallible; #[inline] - fn aquire(&mut self) -> LocalBoxFuture<Result<Self::ReadingKey, Self::Error>> { - Box::pin(async { Ok(()) }) + fn aquire(&mut self) -> Result<Self::ReadingKey, Self::Error> { + Ok(()) } #[inline] @@ -73,7 +71,5 @@ where } #[inline] - fn release(&mut self) -> LocalBoxFuture { - Box::pin(async {}) - } + fn release(&mut self) {} } diff --git a/manta-util/src/future.rs b/manta-util/src/future.rs deleted file mode 100644 index 5b2a41729..000000000 --- a/manta-util/src/future.rs +++ /dev/null @@ -1,26 +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 <http://www.gnu.org/licenses/>. - -//! Futures Utilities - -use alloc::boxed::Box; -use core::{future::Future, pin::Pin}; - -/// Generic Future -pub type BoxFuture<'f, T = ()> = Pin<Box<dyn Future<Output = T> + 'f + Send>>; - -/// Generic Local Future -pub type LocalBoxFuture<'f, T = ()> = Pin<Box<dyn Future<Output = T> + 'f>>; diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index 246e9686d..0c0ba0cbe 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -27,20 +27,13 @@ extern crate alloc; mod array; mod sealed; +pub mod cache; pub mod codec; pub mod convert; pub mod iter; pub mod persistance; pub mod pointer; -#[cfg(feature = "alloc")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] -pub mod cache; - -#[cfg(feature = "alloc")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] -pub mod future; - pub use array::*; pub use sealed::*; From 2878cfccb58cb9588a635ebda901d8324ee09562 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 26 Jan 2022 23:31:54 -0500 Subject: [PATCH 207/275] feat: remove async in favor of synchronous --- manta-accounting/Cargo.toml | 5 + manta-accounting/src/wallet/test/mod.rs | 6 +- manta-accounting/src/wallet/test/sim.rs | 198 +++++++++++++++++++++--- manta-pay/Cargo.toml | 16 +- manta-pay/src/test/ledger.rs | 182 ++++++++++------------ manta-pay/src/wallet/cache.rs | 83 ++++------ manta-pay/src/wallet/mod.rs | 2 + 7 files changed, 313 insertions(+), 179 deletions(-) diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 39dae76cd..a026ddde9 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -28,6 +28,9 @@ maintenance = { status = "actively-developed" } # Cocoon Filesystem Adapters cocoon-fs = ["std", "zeroize"] +# Parallel Execution +parallel = ["crossbeam/std", "rayon"] + # Standard Library std = [ "cocoon/std", @@ -40,6 +43,7 @@ test = ["indexmap", "parking_lot", "rand/alloc", "statrs"] [dependencies] cocoon = { version = "0.3.0", optional = true, default-features = false } +crossbeam = { version = "0.8.1", optional = true, default-features = false } derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } derive_more = { version = "0.99.16", default-features = false, features = ["add", "add_assign", "display", "from", "mul", "mul_assign", "sum"] } indexmap = { version = "1.8.0", optional = true, default-features = false } @@ -47,6 +51,7 @@ manta-crypto = { path = "../manta-crypto", default-features = false } manta-util = { path = "../manta-util", features = ["alloc"] } parking_lot = { version = "0.11.2", optional = true, default-features = false } rand = { version = "0.8.4", optional = true, default-features = false } +rayon = { version = "1.5.1", optional = true, default-features = false } statrs = { version = "0.15.0", optional = true, default-features = false } zeroize = { version = "1.4.3", optional = true, default-features = false, features = ["alloc"] } diff --git a/manta-accounting/src/wallet/test/mod.rs b/manta-accounting/src/wallet/test/mod.rs index 3fef67ad0..e870c0e8d 100644 --- a/manta-accounting/src/wallet/test/mod.rs +++ b/manta-accounting/src/wallet/test/mod.rs @@ -23,7 +23,7 @@ use crate::{ transfer::{canonical::Transaction, Configuration, PublicKey, ReceivingKey}, wallet::{self, ledger, signer, Wallet}, }; -use alloc::rc::Rc; +use alloc::sync::Arc; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use indexmap::IndexSet; use manta_crypto::rand::{CryptoRng, RngCore, Sample}; @@ -279,7 +279,7 @@ where pub type PublicKeyDatabase<C> = IndexSet<ReceivingKey<C>>; /// Shared Public Key Database -pub type SharedPublicKeyDatabase<C> = Rc<RwLock<PublicKeyDatabase<C>>>; +pub type SharedPublicKeyDatabase<C> = Arc<RwLock<PublicKeyDatabase<C>>>; /// Simulation #[derive(derivative::Derivative)] @@ -309,7 +309,7 @@ where #[inline] pub fn new<const N: usize>(keys: [ReceivingKey<C>; N]) -> Self { Self { - public_keys: Rc::new(RwLock::new(keys.into_iter().collect())), + public_keys: Arc::new(RwLock::new(keys.into_iter().collect())), __: PhantomData, } } diff --git a/manta-accounting/src/wallet/test/sim.rs b/manta-accounting/src/wallet/test/sim.rs index b093cd299..a518f0e85 100644 --- a/manta-accounting/src/wallet/test/sim.rs +++ b/manta-accounting/src/wallet/test/sim.rs @@ -17,9 +17,15 @@ //! Actor Simulation Framework use alloc::vec::Vec; -use core::{fmt::Debug, hash::Hash, iter}; +use core::{fmt::Debug, hash::Hash}; use manta_crypto::rand::{CryptoRng, RngCore}; +#[cfg(feature = "parallel")] +use { + crossbeam::channel::{self, Receiver, Select}, + rayon::Scope, +}; + /// Abstract Simulation pub trait Simulation { /// Actor Type @@ -28,7 +34,7 @@ pub trait Simulation { /// Event Type type Event; - /// Runs the given `actor` returning a future event. + /// Runs the given `actor` returning an event. /// /// This method should return `None` when the actor is done being simulated for this round of /// the simulation. @@ -120,37 +126,181 @@ where Self { simulation, actors } } - /// Builds a stream of future events for a particular `actor`. + /// Runs the simulator using `rng`, returning an iterator over events. + #[cfg(feature = "parallel")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "parallel")))] #[inline] - fn build_actor_stream<'s, R>( - simulation: &'s S, - index: usize, - actor: &'s mut S::Actor, - mut rng: R, - ) -> impl 's + Iterator<Item = Event<S>> + pub fn run<'s, R, F>(&'s mut self, mut rng: F, scope: &Scope<'s>) -> RunIter<S> where - R: 's + CryptoRng + RngCore, + S: Sync, + S::Actor: Send, + S::Event: Send, + R: 's + CryptoRng + RngCore + Send, + F: FnMut() -> R, { - iter::from_fn(move || simulation.step(actor, &mut rng)) - .enumerate() - .map(move |(step, event)| Event::new(index, step, event)) + RunIter::new( + self.actors + .iter_mut() + .enumerate() + .map(|(i, actor)| ActorIter::new(&self.simulation, i, actor, rng())) + .collect(), + scope, + ) + } +} + +/// Actor Iterator +pub struct ActorIter<'s, S, R> +where + S: Simulation, + R: 's + CryptoRng + RngCore, +{ + /// Base Simulation + simulation: &'s S, + + /// Simulation Step Index + step_index: usize, + + /// Actor Index + actor_index: usize, + + /// Actor + /// + /// If the actor is done for this round of simulation, then this field is `None`. + actor: Option<&'s mut S::Actor>, + + /// Actor's Random Number Generator + rng: R, +} + +impl<'s, S, R> ActorIter<'s, S, R> +where + S: Simulation, + R: 's + CryptoRng + RngCore, +{ + /// Builds a new [`ActorIter`] from `simulation`, `actor_index, `actor`, and `rng`. + #[inline] + pub fn new(simulation: &'s S, actor_index: usize, actor: &'s mut S::Actor, rng: R) -> Self { + Self { + simulation, + step_index: 0, + actor_index, + actor: Some(actor), + rng, + } + } +} + +impl<'s, S, R> Iterator for ActorIter<'s, S, R> +where + S: Simulation, + R: 's + CryptoRng + RngCore, +{ + type Item = Event<S>; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + if let Some(actor) = self.actor.as_mut() { + if let Some(event) = self.simulation.step(actor, &mut self.rng) { + let event = Event::new(self.actor_index, self.step_index, event); + self.step_index += 1; + return Some(event); + } else { + self.actor = None; + } + } + None } +} - /* TODO: - /// Runs the simulator using `rng`, returning a stream of future events. +/// Simulation Run Iterator +#[cfg(feature = "parallel")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "parallel")))] +pub struct RunIter<S> +where + S: Simulation, +{ + /// Receivers + receivers: Vec<Receiver<Event<S>>>, +} + +#[cfg(feature = "parallel")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "parallel")))] +impl<S> RunIter<S> +where + S: Simulation, +{ + /// Builds a new [`RunIter`] for `iterators`, pulling from them in parallel using `scope`. #[inline] - pub fn run<'s, R, F>(&'s mut self, mut rng: F) -> impl 's + Iterator<Item = Event<S>> + fn new<'s, R>(iterators: Vec<ActorIter<'s, S, R>>, scope: &Scope<'s>) -> Self where - R: 's + CryptoRng + RngCore, - F: FnMut() -> R, + S: Sync, + S::Actor: Send, + S::Event: Send, + R: 's + CryptoRng + RngCore + Send, { - let mut streams = SelectAll::new(); - for (i, actor) in self.actors.iter_mut().enumerate() { - streams.push(Self::build_actor_stream(&self.simulation, i, actor, rng())); + let len = iterators.len(); + let mut senders = Vec::with_capacity(len); + let mut receivers = Vec::with_capacity(len); + for _ in 0..len { + let (sender, receiver) = channel::unbounded(); + senders.push(sender); + receivers.push(receiver); + } + let (task_sender, task_receiver) = channel::unbounded(); + let mut tasks = iterators.into_iter().zip(senders).collect::<Vec<_>>(); + scope.spawn(move |scope| loop { + for (mut iter, sender) in tasks.drain(..) { + let sender = sender.clone(); + let task_sender = task_sender.clone(); + scope.spawn(move |_| { + if let Some(next) = iter.next() { + let _ = sender.send(next); + let _ = task_sender.send((iter, sender)); + } + }); + } + if let Ok(task) = task_receiver.recv() { + tasks.push(task); + } else { + break; + } + }); + Self { receivers } + } +} + +#[cfg(feature = "parallel")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "parallel")))] +impl<S> Iterator for RunIter<S> +where + S: Simulation, +{ + type Item = Event<S>; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + let mut drop_indices = Vec::<usize>::with_capacity(self.receivers.len()); + let mut select = Select::new(); + for receiver in &self.receivers { + select.recv(receiver); + } + loop { + let index = select.ready(); + match self.receivers[index].try_recv() { + Ok(event) => { + drop_indices.sort_unstable_by(move |l, r| r.cmp(l)); + drop_indices.dedup(); + for index in drop_indices { + self.receivers.remove(index); + } + return Some(event); + } + Err(e) if e.is_disconnected() => drop_indices.push(index), + _ => {} + } } - streams } - */ } impl<S> AsRef<S> for Simulator<S> @@ -182,7 +332,7 @@ pub trait ActionSimulation { where R: CryptoRng + RngCore + ?Sized; - /// Executes the given `action` on `actor` returning a future event. + /// Executes the given `action` on `actor` returning an event. fn act(&self, actor: &mut Self::Actor, action: Self::Action) -> Self::Event; } diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 1d361321c..7bf6f0b91 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -53,10 +53,17 @@ groth16 = ["arkworks", "ark-groth16"] plonk = ["zk-garage-plonk"] # Hierarchical-Deterministic Wallet Keys -hd-wallet = ["bip32", "async-std/unstable"] +hd-wallet = ["bip32"] # Testing Frameworks -test = ["manta-accounting/test", "manta-crypto/test", "indexmap", "async-std/unstable"] +test = [ + "indexmap", + "manta-accounting/parallel", + "manta-accounting/test", + "manta-crypto/test", + "parking_lot", + "rayon", +] # Simulation Framework simulation = ["rand", "statrs"] @@ -76,7 +83,6 @@ ark-r1cs-std = { version = "0.3.1", optional = true, default-features = false } ark-relations = { version = "0.3.0", optional = true, default-features = false } ark-serialize = { version = "0.3.0", optional = true, default-features = false } ark-std = { version = "0.3.0", optional = true, default-features = false } -async-std = { version = "1.10.0", default-features = false } bip32 = { version = "0.3.0", default-features = false, features = ["bip39", "secp256k1"], optional = true } blake2 = { version = "0.10.0", default-features = false } derivative = { version = "2.2.0", default-features = false } @@ -85,16 +91,16 @@ indexmap = { version = "1.8.0", default-features = false, optional = true } manta-accounting = { path = "../manta-accounting", default-features = false } manta-crypto = { path = "../manta-crypto", default-features = false } manta-util = { path = "../manta-util", default-features = false } +parking_lot = { version = "0.11.2", optional = true, default-features = false } rand = { version = "0.8.4", optional = true } rand_chacha = { version = "0.3.1", default-features = false } +rayon = { version = "1.5.1", optional = true, default-features = false } scale-codec = { package = "parity-scale-codec", version = "2.3.1", default-features = false, features = ["derive"] } statrs = { version = "0.15.0", optional = true, default-features = false } zk-garage-plonk = { package = "plonk", git = "https://github.com/zk-garage/plonk", optional = true, default-features = false } [dev-dependencies] -async-std = { version = "1.10.0", features = ["attributes", "unstable"] } criterion = "0.3.5" -futures = "0.3.19" manta-accounting = { path = "../manta-accounting", features = ["test"] } manta-crypto = { path = "../manta-crypto", features = ["test"] } manta-pay = { path = ".", features = ["arkworks", "groth16", "hd-wallet", "std", "test"] } diff --git a/manta-pay/src/test/ledger.rs b/manta-pay/src/test/ledger.rs index e0c67c734..be35fcf2c 100644 --- a/manta-pay/src/test/ledger.rs +++ b/manta-pay/src/test/ledger.rs @@ -23,8 +23,7 @@ use crate::config::{ Config, EncryptedNote, MerkleTreeConfiguration, MultiVerifyingContext, ProofSystem, TransferPost, Utxo, UtxoSetModel, VoidNumber, }; -use alloc::{rc::Rc, vec::Vec}; -use async_std::sync::RwLock; +use alloc::{sync::Arc, vec::Vec}; use core::convert::Infallible; use indexmap::IndexSet; use manta_accounting::{ @@ -45,7 +44,8 @@ use manta_crypto::{ Tree, }, }; -use manta_util::{future::LocalBoxFuture, into_array_unchecked}; +use manta_util::into_array_unchecked; +use parking_lot::RwLock; use std::collections::{HashMap, HashSet}; /// Merkle Forest Index @@ -366,7 +366,7 @@ impl ledger::Checkpoint for Checkpoint { } /// Shared Ledger -pub type SharedLedger = Rc<RwLock<Ledger>>; +pub type SharedLedger = Arc<RwLock<Ledger>>; /// Ledger Connection pub struct LedgerConnection { @@ -392,68 +392,61 @@ impl ledger::Connection<Config> for LedgerConnection { type Error = Infallible; #[inline] - fn pull<'s>( - &'s mut self, - checkpoint: &'s Self::Checkpoint, - ) -> LocalBoxFuture<'s, PullResult<Config, Self>> { - Box::pin(async { - let ledger = self.ledger.read().await; - let mut receivers = Vec::new(); - for (i, mut index) in checkpoint.receiver_index.iter().copied().enumerate() { - let shard = &ledger.shards[&MerkleForestIndex::from_index(i)]; - while let Some(entry) = shard.get_index(index) { - receivers.push(*entry); - index += 1; - } + fn pull(&mut self, checkpoint: &Self::Checkpoint) -> PullResult<Config, Self> { + let ledger = self.ledger.read(); + let mut receivers = Vec::new(); + for (i, mut index) in checkpoint.receiver_index.iter().copied().enumerate() { + let shard = &ledger.shards[&MerkleForestIndex::from_index(i)]; + while let Some(entry) = shard.get_index(index) { + receivers.push(*entry); + index += 1; } - let senders = ledger - .void_numbers - .iter() - .skip(checkpoint.sender_index) - .copied() - .collect(); - Ok(PullResponse { - checkpoint: Checkpoint::new( - into_array_unchecked( - ledger - .utxo_forest - .forest - .as_ref() - .iter() - .map(move |t| t.len()) - .collect::<Vec<_>>(), - ), - ledger.void_numbers.len(), + } + let senders = ledger + .void_numbers + .iter() + .skip(checkpoint.sender_index) + .copied() + .collect(); + Ok(PullResponse { + checkpoint: Checkpoint::new( + into_array_unchecked( + ledger + .utxo_forest + .forest + .as_ref() + .iter() + .map(move |t| t.len()) + .collect::<Vec<_>>(), ), - receivers, - senders, - }) + ledger.void_numbers.len(), + ), + receivers, + senders, }) } #[inline] - fn push(&mut self, posts: Vec<TransferPost>) -> LocalBoxFuture<PushResult<Config, Self>> { - Box::pin(async { - let mut ledger = self.ledger.write().await; - for post in posts { - let (sources, sinks) = match TransferShape::from_post(&post) { - Some(TransferShape::Mint) => (vec![self.account], vec![]), - Some(TransferShape::PrivateTransfer) => (vec![], vec![]), - Some(TransferShape::Reclaim) => (vec![], vec![self.account]), - _ => return Ok(PushResponse { success: false }), - }; - match post.validate(sources, sinks, &*ledger) { - Ok(posting_key) => { - posting_key.post(&(), &mut *ledger); - } - Err(err) => { - async_std::println!("ERROR: {:?}", err).await; - return Ok(PushResponse { success: false }); - } + fn push(&mut self, posts: Vec<TransferPost>) -> PushResult<Config, Self> { + let mut ledger = self.ledger.write(); + for post in posts { + let (sources, sinks) = match TransferShape::from_post(&post) { + Some(TransferShape::Mint) => (vec![self.account], vec![]), + Some(TransferShape::PrivateTransfer) => (vec![], vec![]), + Some(TransferShape::Reclaim) => (vec![], vec![self.account]), + _ => return Ok(PushResponse { success: false }), + }; + match post.validate(sources, sinks, &*ledger) { + Ok(posting_key) => { + posting_key.post(&(), &mut *ledger); + } + Err(err) => { + println!("ERROR: {:?}", err); + return Ok(PushResponse { success: false }); } } - Ok(PushResponse { success: true }) - }) + } + Ok(PushResponse { success: true }) } } @@ -465,8 +458,6 @@ mod test { config::FullParameters, wallet::{self, cache::OnDiskMultiProvingContext, Signer, Wallet}, }; - use async_std::{println, task}; - use futures::stream::StreamExt; use manta_accounting::{ asset::{Asset, AssetList}, key::AccountTable, @@ -476,24 +467,21 @@ mod test { ActionType, Actor, PublicBalanceOracle, Simulation, }, }; - use manta_crypto::rand::{CryptoRng, Rand, RngCore}; - use rand::thread_rng; + use manta_crypto::rand::{CryptoRng, Rand, RngCore, SeedableRng}; + use rand::{rngs::StdRng, thread_rng}; impl PublicBalanceOracle for LedgerConnection { #[inline] - fn public_balances(&self) -> LocalBoxFuture<Option<AssetList>> { - Box::pin(async { - Some( - self.ledger - .read() - .await - .accounts - .get(&self.account)? - .iter() - .map(|(id, value)| Asset::new(*id, *value)) - .collect(), - ) - }) + fn public_balances(&self) -> Option<AssetList> { + Some( + self.ledger + .read() + .accounts + .get(&self.account)? + .iter() + .map(|(id, value)| Asset::new(*id, *value)) + .collect(), + ) } } @@ -523,12 +511,10 @@ mod test { } /// Runs a simple simulation to test that the signer-wallet-ledger connection works. - #[async_std::test] - async fn test_simulation() { - let directory = task::spawn_blocking(tempfile::tempdir) - .await - .expect("Unable to generate temporary test directory."); - println!("[INFO] Temporary Directory: {:?}", directory).await; + #[test] + fn test_simulation() { + let directory = tempfile::tempdir().expect("Unable to generate temporary test directory."); + println!("[INFO] Temporary Directory: {:?}", directory); let mut rng = thread_rng(); let parameters = rng.gen(); @@ -544,10 +530,9 @@ mod test { let cache = OnDiskMultiProvingContext::new(directory.path()); cache .save(proving_context) - .await .expect("Unable to save proving context to disk."); - const ACTOR_COUNT: usize = 3; + const ACTOR_COUNT: usize = 10; let mut ledger = Ledger::new(utxo_set_model.clone(), verifying_context); @@ -557,9 +542,9 @@ mod test { ledger.set_public_balance(AccountId(i as u64), AssetId(2), AssetValue(1000000)); } - let ledger = Rc::new(RwLock::new(ledger)); + let ledger = Arc::new(RwLock::new(ledger)); - println!("[INFO] Building Wallets").await; + println!("[INFO] Building {:?} Wallets", ACTOR_COUNT); let actors = (0..ACTOR_COUNT) .map(|i| { @@ -580,22 +565,23 @@ mod test { let mut simulator = Simulator::new(ActionSim(Simulation::default()), actors); - println!("[INFO] Running Simulation\n").await; + println!("[INFO] Starting Simulation\n"); - let mut events = simulator.run(thread_rng); - while let Some(event) = events.next().await { - match event.event.action { - ActionType::Skip | ActionType::GeneratePublicKey => {} - _ => println!("{:?}", event).await, - } - if let Err(err) = event.event.result { - println!("\n[ERROR] Simulation Error: {:?}\n", err).await; - break; + rayon::in_place_scope(|scope| { + for event in simulator.run(move || StdRng::from_rng(&mut rng).unwrap(), scope) { + match event.event.action { + ActionType::Skip | ActionType::GeneratePublicKey => {} + _ => println!("{:?}", event), + } + if let Err(err) = event.event.result { + println!("\n[ERROR] Simulation Error: {:?}\n", err); + break; + } } - } + }); - task::spawn_blocking(move || directory.close()) - .await + directory + .close() .expect("Unable to delete temporary test directory."); } } diff --git a/manta-pay/src/wallet/cache.rs b/manta-pay/src/wallet/cache.rs index 69f296a79..f0aad17cc 100644 --- a/manta-pay/src/wallet/cache.rs +++ b/manta-pay/src/wallet/cache.rs @@ -17,18 +17,16 @@ //! Proving Context Caching use crate::config::{MultiProvingContext, ProvingContext}; -use async_std::{ - io, - path::{Path, PathBuf}, - task, -}; use core::marker::PhantomData; use manta_util::{ cache::CachedResource, codec::{Decode, Encode, IoReader, IoWriter}, - future::LocalBoxFuture, }; -use std::fs::{File, OpenOptions}; +use std::{ + fs::{File, OpenOptions}, + io, + path::{Path, PathBuf}, +}; /// Caching Error #[derive(Debug)] @@ -92,45 +90,39 @@ impl OnDiskMultiProvingContext { /// Reads a single [`ProvingContext`] from `path`. #[inline] - async fn read_context<P>(path: P) -> Result<ProvingContext, Error> + fn read_context<P>(path: P) -> Result<ProvingContext, Error> where - P: 'static + AsRef<Path> + Send, + P: AsRef<Path>, { - Ok(task::spawn_blocking(move || { - File::open(path.as_ref()) - .map_err(Error::Io) - .and_then(move |f| ProvingContext::decode(IoReader(f)).map_err(|_| Error::Decode)) - }) - .await?) + File::open(path.as_ref()) + .map_err(Error::Io) + .and_then(move |f| ProvingContext::decode(IoReader(f)).map_err(|_| Error::Decode)) } /// Writes `context` to `path`. #[inline] - async fn write_context<P>(path: P, context: ProvingContext) -> Result<(), Error> + fn write_context<P>(path: P, context: ProvingContext) -> Result<(), Error> where - P: 'static + AsRef<Path> + Send, + P: AsRef<Path>, { - Ok(task::spawn_blocking(move || { - OpenOptions::new() - .write(true) - .create(true) - .open(path.as_ref()) - .map_err(Error::Io) - .and_then(move |f| context.encode(IoWriter(f)).map_err(|_| Error::Encode)) - }) - .await?) + OpenOptions::new() + .write(true) + .create(true) + .open(path.as_ref()) + .map_err(Error::Io) + .and_then(move |f| context.encode(IoWriter(f)).map_err(|_| Error::Encode)) } /// Saves the `context` to the on-disk directory. This method _does not_ write `context` into /// the cache. #[inline] - pub async fn save(&self, context: MultiProvingContext) -> Result<(), Error> { - let mint_path = self.directory.join("mint.pk"); - let private_transfer_path = self.directory.join("private-transfer.pk"); - let reclaim_path = self.directory.join("reclaim.pk"); - Self::write_context(mint_path, context.mint).await?; - Self::write_context(private_transfer_path, context.private_transfer).await?; - Self::write_context(reclaim_path, context.reclaim).await?; + pub fn save(&self, context: MultiProvingContext) -> Result<(), Error> { + Self::write_context(self.directory.join("mint.pk"), context.mint)?; + Self::write_context( + self.directory.join("private-transfer.pk"), + context.private_transfer, + )?; + Self::write_context(self.directory.join("reclaim.pk"), context.reclaim)?; Ok(()) } } @@ -140,18 +132,13 @@ impl CachedResource<MultiProvingContext> for OnDiskMultiProvingContext { type Error = Error; #[inline] - fn aquire(&mut self) -> LocalBoxFuture<Result<Self::ReadingKey, Self::Error>> { - Box::pin(async { - let mint_path = self.directory.join("mint.pk"); - let private_transfer_path = self.directory.join("private-transfer.pk"); - let reclaim_path = self.directory.join("reclaim.pk"); - self.context = Some(MultiProvingContext { - mint: Self::read_context(mint_path).await?, - private_transfer: Self::read_context(private_transfer_path).await?, - reclaim: Self::read_context(reclaim_path).await?, - }); - Ok(ReadingKey::new()) - }) + fn aquire(&mut self) -> Result<Self::ReadingKey, Self::Error> { + self.context = Some(MultiProvingContext { + mint: Self::read_context(self.directory.join("mint.pk"))?, + private_transfer: Self::read_context(self.directory.join("private-transfer.pk"))?, + reclaim: Self::read_context(self.directory.join("reclaim.pk"))?, + }); + Ok(ReadingKey::new()) } #[inline] @@ -163,10 +150,8 @@ impl CachedResource<MultiProvingContext> for OnDiskMultiProvingContext { } #[inline] - fn release(&mut self) -> LocalBoxFuture { - Box::pin(async { - self.context.take(); - }) + fn release(&mut self) { + self.context.take(); } } diff --git a/manta-pay/src/wallet/mod.rs b/manta-pay/src/wallet/mod.rs index 9180881fe..15764800d 100644 --- a/manta-pay/src/wallet/mod.rs +++ b/manta-pay/src/wallet/mod.rs @@ -34,6 +34,7 @@ use manta_accounting::{ }, }; use manta_crypto::{key::KeyDerivationFunction, merkle_tree}; +use manta_util::pointer::ThreadSafe; pub mod cache; @@ -62,6 +63,7 @@ pub type UtxoSet = merkle_tree::forest::TreeArrayMerkleForest< merkle_tree::fork::ForkedTree< MerkleTreeConfiguration, merkle_tree::full::Full<MerkleTreeConfiguration>, + ThreadSafe, >, { MerkleTreeConfiguration::FOREST_WIDTH }, >; From 096b11e1551eb0f9854d2d512dcee59c2bb325c0 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 27 Jan 2022 00:23:04 -0500 Subject: [PATCH 208/275] wip: start building websocket signer client/server --- manta-pay/Cargo.toml | 6 +- manta-pay/src/lib.rs | 8 +-- manta-pay/src/test/ledger.rs | 15 +++-- manta-pay/src/wallet/mod.rs | 24 ++++--- manta-pay/src/wallet/signer.rs | 113 +++++++++++++++++++++++++++++++++ 5 files changed, 144 insertions(+), 22 deletions(-) create mode 100644 manta-pay/src/wallet/signer.rs diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 7bf6f0b91..562e347e5 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -52,9 +52,6 @@ groth16 = ["arkworks", "ark-groth16"] # Enable PLONK ZKP System plonk = ["zk-garage-plonk"] -# Hierarchical-Deterministic Wallet Keys -hd-wallet = ["bip32"] - # Testing Frameworks test = [ "indexmap", @@ -97,12 +94,13 @@ rand_chacha = { version = "0.3.1", default-features = false } rayon = { version = "1.5.1", optional = true, default-features = false } scale-codec = { package = "parity-scale-codec", version = "2.3.1", default-features = false, features = ["derive"] } statrs = { version = "0.15.0", optional = true, default-features = false } +tungstenite = { version = "0.16.0", optional = true, default-features = false, features = ["native-tls"] } zk-garage-plonk = { package = "plonk", git = "https://github.com/zk-garage/plonk", optional = true, default-features = false } [dev-dependencies] criterion = "0.3.5" manta-accounting = { path = "../manta-accounting", features = ["test"] } manta-crypto = { path = "../manta-crypto", features = ["test"] } -manta-pay = { path = ".", features = ["arkworks", "groth16", "hd-wallet", "std", "test"] } +manta-pay = { path = ".", features = ["arkworks", "bip32", "groth16", "std", "test"] } rand = "0.8.4" tempfile = "3.2.0" diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index 827f02e82..f665553a5 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -28,8 +28,8 @@ mod test; pub mod crypto; -#[cfg(feature = "hd-wallet")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "hd-wallet")))] +#[cfg(feature = "bip32")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "bip32")))] pub mod key; #[cfg(all(feature = "arkworks", feature = "groth16"))] @@ -39,7 +39,7 @@ pub mod config; #[cfg(all( feature = "arkworks", feature = "groth16", - feature = "hd-wallet", + feature = "bip32", feature = "std", ))] #[cfg_attr( @@ -47,7 +47,7 @@ pub mod config; doc(cfg(all( feature = "arkworks", feature = "groth16", - feature = "hd-wallet", + feature = "bip32", feature = "std", ))) )] diff --git a/manta-pay/src/test/ledger.rs b/manta-pay/src/test/ledger.rs index be35fcf2c..5097fee06 100644 --- a/manta-pay/src/test/ledger.rs +++ b/manta-pay/src/test/ledger.rs @@ -456,15 +456,18 @@ mod test { use super::*; use crate::{ config::FullParameters, - wallet::{self, cache::OnDiskMultiProvingContext, Signer, Wallet}, + wallet::{self, cache::OnDiskMultiProvingContext, SignerBase}, }; use manta_accounting::{ asset::{Asset, AssetList}, key::AccountTable, transfer, - wallet::test::{ - sim::{ActionSim, Simulator}, - ActionType, Actor, PublicBalanceOracle, Simulation, + wallet::{ + test::{ + sim::{ActionSim, Simulator}, + ActionType, Actor, PublicBalanceOracle, Simulation, + }, + Wallet, }, }; use manta_crypto::rand::{CryptoRng, Rand, RngCore, SeedableRng}; @@ -494,13 +497,13 @@ mod test { parameters: &transfer::Parameters<Config>, utxo_set_model: &UtxoSetModel, rng: &mut R, - ) -> Wallet<LedgerConnection> + ) -> Wallet<Config, LedgerConnection, SignerBase> where R: CryptoRng + RngCore + ?Sized, { Wallet::empty( LedgerConnection::new(account, ledger.clone()), - Signer::new( + SignerBase::new( AccountTable::new(rng.gen()), cache.clone(), parameters.clone(), diff --git a/manta-pay/src/wallet/mod.rs b/manta-pay/src/wallet/mod.rs index 15764800d..1fdc5688d 100644 --- a/manta-pay/src/wallet/mod.rs +++ b/manta-pay/src/wallet/mod.rs @@ -28,16 +28,17 @@ use ark_ff::PrimeField; use manta_accounting::{ asset::HashAssetMap, key::{self, HierarchicalKeyDerivationScheme}, - wallet::{ - self, - signer::{self, AssetMapKey}, - }, + wallet::{self, signer::AssetMapKey}, }; use manta_crypto::{key::KeyDerivationFunction, merkle_tree}; use manta_util::pointer::ThreadSafe; pub mod cache; +#[cfg(feature = "tungstenite")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "tungstenite")))] +pub mod signer; + /// Hierarchical Key Derivation Function pub struct HierarchicalKeyDerivationFunction; @@ -68,7 +69,7 @@ pub type UtxoSet = merkle_tree::forest::TreeArrayMerkleForest< { MerkleTreeConfiguration::FOREST_WIDTH }, >; -impl signer::Configuration for Config { +impl manta_accounting::wallet::signer::Configuration for Config { type HierarchicalKeyDerivationScheme = key::Map<TestnetKeySecret, HierarchicalKeyDerivationFunction>; type UtxoSet = UtxoSet; @@ -77,8 +78,15 @@ impl signer::Configuration for Config { type Rng = rand_chacha::ChaCha20Rng; } -/// Signer -pub type Signer = signer::Signer<Config>; +/// Signer Base Type +pub type SignerBase = manta_accounting::wallet::signer::Signer<Config>; + +/// Signer Server +#[cfg(feature = "tungstenite")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "tungstenite")))] +pub type Signer = signer::Server; /// Wallet -pub type Wallet<L> = wallet::Wallet<Config, L, Signer>; +#[cfg(feature = "tungstenite")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "tungstenite")))] +pub type Wallet<L> = wallet::Wallet<Config, L, signer::Client>; diff --git a/manta-pay/src/wallet/signer.rs b/manta-pay/src/wallet/signer.rs new file mode 100644 index 000000000..ef7a69f18 --- /dev/null +++ b/manta-pay/src/wallet/signer.rs @@ -0,0 +1,113 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Signer Client and Server + +// TODO: Make this generic over the `Config`. + +use crate::{config::Config, wallet::SignerBase}; +use manta_accounting::{ + transfer::{canonical::Transaction, EncryptedNote, Utxo, VoidNumber}, + wallet::{ + signer, + signer::{ReceivingKeyResult, SignResult, SyncResult}, + }, +}; +use std::net::TcpStream; +use tungstenite::{ + client::IntoClientRequest, + handshake::server::{NoCallback, ServerHandshake}, + stream::MaybeTlsStream, +}; + +/// Stream Type +pub type Stream = MaybeTlsStream<TcpStream>; + +/// Web Socket Connection Type +pub type WebSocket = tungstenite::WebSocket<Stream>; + +/// Handshake Error Type +pub type HandshakeError = + tungstenite::handshake::HandshakeError<ServerHandshake<Stream, NoCallback>>; + +/// Client +pub struct Client { + /// Underlying Client Connection + connection: WebSocket, +} + +impl Client { + /// Builds a new [`Client`] from Web Socket `url`. + #[inline] + pub fn new<U>(url: U) -> Result<Self, tungstenite::error::Error> + where + U: IntoClientRequest, + { + Ok(Self { + connection: tungstenite::connect(url)?.0, + }) + } +} + +impl signer::Connection<Config> for Client { + type Error = (); + + #[inline] + fn sync<I, R>( + &mut self, + starting_index: usize, + inserts: I, + removes: R, + ) -> SyncResult<Config, Self> + where + I: IntoIterator<Item = (Utxo<Config>, EncryptedNote<Config>)>, + R: IntoIterator<Item = VoidNumber<Config>>, + { + todo!() + } + + #[inline] + fn sign(&mut self, transaction: Transaction<Config>) -> SignResult<Config, Self> { + todo!() + } + + #[inline] + fn receiving_key(&mut self) -> ReceivingKeyResult<Config, Self> { + todo!() + } +} + +/// Signer Server +pub struct Server { + /// Signer Base + base: SignerBase, + + /// Underlying Server Connection + connection: WebSocket, +} + +impl Server { + /// Builds a new [`Server`] from a [`Stream`]. + #[inline] + pub fn new(stream: Stream) -> Result<Self, HandshakeError> { + /* TODO: + Ok(Self { + connection: tungstenite::accept(stream)?, + }) + */ + todo!() + } +} From d250e917b5a81a562623997501f7ddaead75f682 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 27 Jan 2022 11:46:48 -0500 Subject: [PATCH 209/275] feat: add optimization paths for posting multiple posting keys --- manta-accounting/src/transfer/mod.rs | 111 ++++++++++++++++++++++++--- 1 file changed, 102 insertions(+), 9 deletions(-) diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index 0f866bc04..f448232b8 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -18,7 +18,7 @@ use crate::asset::{Asset, AssetId, AssetValue}; use alloc::vec::Vec; -use core::{fmt::Debug, hash::Hash, marker::PhantomData}; +use core::{fmt::Debug, hash::Hash, iter, marker::PhantomData}; use manta_crypto::{ accumulator::{Accumulator, MembershipProof, Model}, commitment::CommitmentScheme, @@ -967,12 +967,49 @@ where /// /// This method can only be called once we check that `void_number` is not already stored on /// the ledger. See [`is_unspent`](Self::is_unspent) for more. + /// + /// # Implementation Note + /// + /// This method, by defualt, calls the [`spend_all`] method on an iterator of length one + /// containing `(utxo, note)`. Either [`spend`] or [`spend_all`] can be implemented + /// depending on which is more efficient. + /// + /// [`spend`]: Self::spend + /// [`spend_all`]: Self::spend_all + #[inline] fn spend( &mut self, utxo_set_output: Self::ValidUtxoSetOutput, void_number: Self::ValidVoidNumber, super_key: &Self::SuperPostingKey, - ); + ) { + self.spend_all(iter::once((utxo_set_output, void_number)), super_key) + } + + /// Posts all of the [`VoidNumber`] to the ledger, spending the assets. + /// + /// # Safety + /// + /// This method can only be called once we check that all the [`VoidNumber`] are not already + /// stored on the ledger. See [`is_unspent`](Self::is_unspent) for more. + /// + /// # Implementation Note + /// + /// This method is an optimization path for multiple calls to [`spend`] and by default just + /// iterates over `iter` calling [`spend`] on each item returned. Either [`spend`] or + /// [`spend_all`] can be implemented depending on which is more efficient. + /// + /// [`spend`]: Self::spend + /// [`spend_all`]: Self::Spend_all + #[inline] + fn spend_all<I>(&mut self, iter: I, super_key: &Self::SuperPostingKey) + where + I: IntoIterator<Item = (Self::ValidUtxoSetOutput, Self::ValidVoidNumber)>, + { + for (utxo_set_output, void_number) in iter { + self.spend(utxo_set_output, void_number, super_key) + } + } } /// Sender Post Error @@ -1070,6 +1107,19 @@ where pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) { ledger.spend(self.utxo_set_output, self.void_number, super_key); } + + /// Posts all of the [`SenderPostingKey`] in `iter` to the sender `ledger`. + #[inline] + pub fn post_all<I>(iter: I, super_key: &L::SuperPostingKey, ledger: &mut L) + where + I: IntoIterator<Item = Self>, + { + ledger.spend_all( + iter.into_iter() + .map(move |k| (k.utxo_set_output, k.void_number)), + super_key, + ) + } } /// Receiver @@ -1253,12 +1303,50 @@ where /// /// This method can only be called once we check that `utxo` is not already stored on the /// ledger. See [`is_not_registered`](Self::is_not_registered) for more. + /// + /// # Implementation Note + /// + /// This method, by default, calls the [`register_all`] method on an iterator of length one + /// containing `(utxo, note)`. Either [`register`] or [`register_all`] can be implemented + /// depending on which is more efficient. + /// + /// [`register`]: Self::register + /// [`register_all`]: Self::register_all + #[inline] fn register( &mut self, utxo: Self::ValidUtxo, note: EncryptedNote<C>, super_key: &Self::SuperPostingKey, - ); + ) { + self.register_all(iter::once((utxo, note)), super_key) + } + + /// Posts all of the [`Utxo`] and [`EncryptedNote`] to the ledger, registering the assets. + /// + /// # Safety + /// + /// This method can only be called once we check that all the [`Utxo`] and [`EncryptedNote`] are + /// not already stored on the ledger. See [`is_not_registered`](Self::is_not_registered) for + /// more. + /// + /// # Implementation Note + /// + /// This method is an optimization path for multiple calls to [`register`] and by default just + /// iterates over `iter` calling [`register`] on each item returned. Either [`register`] or + /// [`register_all`] can be implemented depending on which is more efficient. + /// + /// [`register`]: Self::register + /// [`register_all`]: Self::register_all + #[inline] + fn register_all<I>(&mut self, iter: I, super_key: &Self::SuperPostingKey) + where + I: IntoIterator<Item = (Self::ValidUtxo, EncryptedNote<C>)>, + { + for (utxo, note) in iter { + self.register(utxo, note, super_key) + } + } } /// Receiver Post Error @@ -1361,6 +1449,15 @@ where pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) { ledger.register(self.utxo, self.note, super_key); } + + /// Posts all the of the [`ReceiverPostingKey`] in `iter` to the receiver `ledger`. + #[inline] + pub fn post_all<I>(iter: I, super_key: &L::SuperPostingKey, ledger: &mut L) + where + I: IntoIterator<Item = Self>, + { + ledger.register_all(iter.into_iter().map(move |k| (k.utxo, k.note)), super_key) + } } /// Transfer @@ -2121,12 +2218,8 @@ where #[inline] pub fn post(self, super_key: &TransferLedgerSuperPostingKey<C, L>, ledger: &mut L) -> L::Event { let proof = self.validity_proof; - for key in self.sender_posting_keys { - key.post(&(proof, *super_key), ledger); - } - for key in self.receiver_posting_keys { - key.post(&(proof, *super_key), ledger); - } + SenderPostingKey::post_all(self.sender_posting_keys, &(proof, *super_key), ledger); + ReceiverPostingKey::post_all(self.receiver_posting_keys, &(proof, *super_key), ledger); if let Some(asset_id) = self.asset_id { ledger.update_public_balances( asset_id, From 84f313dbec188cefb956bd41963d801bfea6fd1e Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 27 Jan 2022 15:11:47 -0500 Subject: [PATCH 210/275] feat: add more transfer sampling and generation methods --- manta-accounting/src/asset.rs | 34 +++++ manta-accounting/src/transfer/canonical.rs | 38 +++++- manta-accounting/src/transfer/mod.rs | 2 +- manta-accounting/src/transfer/test.rs | 144 ++++++++++++++++++--- manta-accounting/src/wallet/signer.rs | 8 +- 5 files changed, 202 insertions(+), 24 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 127574d5b..cc5c80150 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -196,6 +196,23 @@ impl AssetValue { } } +impl Add<AssetValueType> for AssetValue { + type Output = Self; + + #[inline] + fn add(mut self, rhs: AssetValueType) -> Self { + self.add_assign(rhs); + self + } +} + +impl AddAssign<AssetValueType> for AssetValue { + #[inline] + fn add_assign(&mut self, rhs: AssetValueType) { + self.0 += rhs; + } +} + impl From<AssetValue> for [u8; AssetValue::SIZE] { #[inline] fn from(entry: AssetValue) -> Self { @@ -223,6 +240,23 @@ where } } +impl Sub<AssetValueType> for AssetValue { + type Output = Self; + + #[inline] + fn sub(mut self, rhs: AssetValueType) -> Self { + self.sub_assign(rhs); + self + } +} + +impl SubAssign<AssetValueType> for AssetValue { + #[inline] + fn sub_assign(&mut self, rhs: AssetValueType) { + self.0 -= rhs; + } +} + impl<'a> Sum<&'a AssetValue> for AssetValue { #[inline] fn sum<I>(iter: I) -> Self diff --git a/manta-accounting/src/transfer/canonical.rs b/manta-accounting/src/transfer/canonical.rs index 1c46a6120..6e077e574 100644 --- a/manta-accounting/src/transfer/canonical.rs +++ b/manta-accounting/src/transfer/canonical.rs @@ -21,14 +21,14 @@ use crate::{ asset::{self, Asset, AssetValue}, transfer::{ - has_public_participants, Configuration, FullParameters, PreSender, ProofSystemError, - ProofSystemPublicParameters, ProvingContext, Receiver, ReceivingKey, Sender, Transfer, - TransferPost, VerifyingContext, + has_public_participants, Configuration, FullParameters, Parameters, PreSender, + ProofSystemError, ProofSystemPublicParameters, ProvingContext, Receiver, ReceivingKey, + Sender, SpendingKey, Transfer, TransferPost, VerifyingContext, }, }; use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; -use manta_crypto::rand::{CryptoRng, RngCore}; +use manta_crypto::rand::{CryptoRng, Rand, RngCore}; use manta_util::{create_seal, seal}; create_seal! {} @@ -101,6 +101,36 @@ where pub fn build(asset: Asset, receiver: Receiver<C>) -> Self { Self::new_unchecked(Some(asset.id), [asset.value], [], [receiver], []) } + + /// Builds a new [`Mint`] from a [`SpendingKey`] using [`SpendingKey::receiver`]. + #[inline] + pub fn from_spending_key<R>( + parameters: &Parameters<C>, + spending_key: &SpendingKey<C>, + asset: Asset, + rng: &mut R, + ) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + Self::build(asset, spending_key.receiver(parameters, rng.gen(), asset)) + } + + /// Builds a new [`Mint`] and [`PreSender`] pair from a [`SpendingKey`] using + /// [`SpendingKey::internal_pair`]. + #[inline] + pub fn internal_pair<R>( + parameters: &Parameters<C>, + spending_key: &SpendingKey<C>, + asset: Asset, + rng: &mut R, + ) -> (Self, PreSender<C>) + where + R: CryptoRng + RngCore + ?Sized, + { + let (receiver, pre_sender) = spending_key.internal_pair(parameters, rng.gen(), asset); + (Self::build(asset, receiver), pre_sender) + } } /// [`PrivateTransfer`] Transfer Shape diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index f448232b8..fdc356381 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -1000,7 +1000,7 @@ where /// [`spend_all`] can be implemented depending on which is more efficient. /// /// [`spend`]: Self::spend - /// [`spend_all`]: Self::Spend_all + /// [`spend_all`]: Self::spend_all #[inline] fn spend_all<I>(&mut self, iter: I, super_key: &Self::SuperPostingKey) where diff --git a/manta-accounting/src/transfer/test.rs b/manta-accounting/src/transfer/test.rs index 7579d55b2..260784b3a 100644 --- a/manta-accounting/src/transfer/test.rs +++ b/manta-accounting/src/transfer/test.rs @@ -19,9 +19,9 @@ use crate::{ asset::{Asset, AssetId, AssetValue, AssetValueType}, transfer::{ - has_public_participants, Configuration, FullParameters, Parameters, PreSender, - ProofSystemError, ProofSystemPublicParameters, ProvingContext, Receiver, Sender, Transfer, - Utxo, VerifyingContext, + canonical::Mint, has_public_participants, Configuration, FullParameters, Parameters, + PreSender, ProofSystemError, ProofSystemPublicParameters, ProvingContext, Receiver, Sender, + SpendingKey, Transfer, TransferPost, Utxo, VerifyingContext, }, }; use alloc::vec::Vec; @@ -122,11 +122,79 @@ where pub utxo_set: &'p mut A, } +impl<'p, C, A> From<FixedTransferDistribution<'p, C, A>> for TransferDistribution<'p, C, A> +where + C: Configuration, + A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, +{ + #[inline] + fn from(distribution: FixedTransferDistribution<'p, C, A>) -> Self { + distribution.base + } +} + +/// Fixed Transfer Distribution +/// +/// # Note +/// +/// This distribution does not check if the input sum is equal to the output sum, and lets the +/// [`Transfer`] mechanism check this itself. +pub struct FixedTransferDistribution<'p, C, A> +where + C: Configuration, + A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, +{ + /// Base Transfer Distribution + pub base: TransferDistribution<'p, C, A>, + + /// Asset Id for this Transfer + pub asset_id: AssetId, + + /// Source Asset Value Sum + pub source_sum: AssetValue, + + /// Sender Asset Value Sum + pub sender_sum: AssetValue, + + /// Receiver Asset Value Sum + pub receiver_sum: AssetValue, + + /// Sink Asset Value Sum + pub sink_sum: AssetValue, +} + impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, const SINKS: usize> Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS> where C: Configuration, { + /// Samples a [`TransferPost`] from `parameters` and `utxo_set` using `proving_context` and + /// `rng`. + #[inline] + pub fn sample_post<A, R>( + proving_context: &ProvingContext<C>, + parameters: &Parameters<C>, + utxo_set: &mut A, + rng: &mut R, + ) -> Result<TransferPost<C>, ProofSystemError<C>> + where + A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + R: CryptoRng + RngCore + ?Sized, + { + Self::sample( + TransferDistribution { + parameters, + utxo_set, + }, + rng, + ) + .into_post( + FullParameters::new(parameters, utxo_set.model()), + proving_context, + rng, + ) + } + /// Samples a new [`Transfer`] and builds a correctness proof for it, checking if it was /// validated. #[inline] @@ -168,18 +236,7 @@ where A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, R: CryptoRng + RngCore + ?Sized, { - let transfer = Self::sample( - TransferDistribution { - parameters, - utxo_set, - }, - rng, - ); - let post = transfer.into_post( - FullParameters::new(parameters, utxo_set.model()), - proving_context, - rng, - )?; + let post = Self::sample_post(proving_context, parameters, utxo_set, rng)?; C::ProofSystem::verify( &post.generate_proof_input(), &post.validity_proof, @@ -271,3 +328,60 @@ where ) } } + +impl< + C, + A, + const SOURCES: usize, + const SENDERS: usize, + const RECEIVERS: usize, + const SINKS: usize, + > Sample<FixedTransferDistribution<'_, C, A>> + for Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS> +where + C: Configuration, + A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, +{ + #[inline] + fn sample<R>(distribution: FixedTransferDistribution<'_, C, A>, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + let (senders, receivers) = sample_senders_and_receivers( + distribution.base.parameters, + distribution.asset_id, + &value_distribution(SENDERS, distribution.sender_sum, rng), + &value_distribution(RECEIVERS, distribution.receiver_sum, rng), + distribution.base.utxo_set, + rng, + ); + Self::new( + has_public_participants(SOURCES, SINKS).then(|| distribution.asset_id), + sample_asset_values(distribution.source_sum, rng), + into_array_unchecked(senders), + into_array_unchecked(receivers), + sample_asset_values(distribution.sink_sum, rng), + ) + } +} + +/// Samples a [`Mint`] transaction against `spending_key` and `asset` returning a [`TransferPost`] +/// and [`PreSender`]. +#[inline] +pub fn sample_mint<C, R>( + proving_context: &ProvingContext<C>, + full_parameters: FullParameters<C>, + spending_key: &SpendingKey<C>, + asset: Asset, + rng: &mut R, +) -> Result<(TransferPost<C>, PreSender<C>), ProofSystemError<C>> +where + C: Configuration, + R: CryptoRng + RngCore + ?Sized, +{ + let (mint, pre_sender) = Mint::internal_pair(full_parameters.base, spending_key, asset, rng); + Ok(( + mint.into_post(full_parameters, proving_context, rng)?, + pre_sender, + )) +} diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 1864aaa2c..84eac4c47 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -471,12 +471,12 @@ where .get_default() .default_keypair() .map_err(key::Error::KeyDerivationError)?; - let (receiver, pre_sender) = SpendingKey::new(keypair.spend, keypair.view).internal_pair( + Ok(Mint::internal_pair( parameters, - self.rng.gen(), + &SpendingKey::new(keypair.spend, keypair.view), asset, - ); - Ok((Mint::build(asset, receiver), pre_sender)) + &mut self.rng, + )) } /// Selects the pre-senders which collectively own at least `asset`, returning any change. From b2d80c5486366f3070bfd3114b39846682e0575a Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 27 Jan 2022 16:22:50 -0500 Subject: [PATCH 211/275] feat: add UTXO indpendence checks in (Pre)Sender/Receiver --- manta-accounting/src/transfer/mod.rs | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index fdc356381..f346f0bff 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -749,6 +749,16 @@ where None } } + + /// Returns `true` whenever `self.utxo` and `rhs.utxo` can be inserted in any order into the + /// `utxo_set`. + #[inline] + pub fn is_independent_from<A>(&self, rhs: &Self, utxo_set: &A) -> bool + where + A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + { + utxo_set.are_independent(&self.utxo, &rhs.utxo) + } } /// Sender Proof @@ -808,6 +818,16 @@ where self.asset.value } + /// Returns `true` whenever `self.utxo` and `rhs.utxo` can be inserted in any order into the + /// `utxo_set`. + #[inline] + pub fn is_independent_from<A>(&self, rhs: &Self, utxo_set: &A) -> bool + where + A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + { + utxo_set.are_independent(&self.utxo, &rhs.utxo) + } + /// Reverts `self` back into a [`PreSender`]. /// /// This method should be called if the [`Utxo`] membership proof attached to `self` was deemed @@ -1182,6 +1202,16 @@ where self.note.ephemeral_public_key() } + /// Returns `true` whenever `self.utxo` and `rhs.utxo` can be inserted in any order into the + /// `utxo_set`. + #[inline] + pub fn is_independent_from<A>(&self, rhs: &Self, utxo_set: &A) -> bool + where + A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + { + utxo_set.are_independent(&self.utxo, &rhs.utxo) + } + /// Extracts the ledger posting data from `self`. #[inline] pub fn into_post(self) -> ReceiverPost<C> { From e45d4794d5c9e7844af7018af3f7b5158ffaee12 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 28 Jan 2022 14:27:41 -0500 Subject: [PATCH 212/275] feat: add message-passing interface abstraction --- manta-pay/src/wallet/signer.rs | 176 ++++++++++++++++++++++---- manta-util/src/convert.rs | 73 +++++++++++ manta-util/src/lib.rs | 1 + manta-util/src/message.rs | 218 +++++++++++++++++++++++++++++++++ 4 files changed, 446 insertions(+), 22 deletions(-) create mode 100644 manta-util/src/message.rs diff --git a/manta-pay/src/wallet/signer.rs b/manta-pay/src/wallet/signer.rs index ef7a69f18..aab32ddc1 100644 --- a/manta-pay/src/wallet/signer.rs +++ b/manta-pay/src/wallet/signer.rs @@ -20,50 +20,169 @@ use crate::{config::Config, wallet::SignerBase}; use manta_accounting::{ - transfer::{canonical::Transaction, EncryptedNote, Utxo, VoidNumber}, - wallet::{ - signer, - signer::{ReceivingKeyResult, SignResult, SyncResult}, + transfer::{canonical::Transaction, EncryptedNote, ReceivingKey, Utxo, VoidNumber}, + wallet::signer::{ + self, ReceivingKeyResult, SignResponse, SignResult, SyncResponse, SyncResult, }, }; +use manta_util::{ + convert::{From, TryFrom}, + message::{ChannelError, MessageProtocol, ParsingError, UniformChannel}, +}; use std::net::TcpStream; use tungstenite::{ client::IntoClientRequest, handshake::server::{NoCallback, ServerHandshake}, stream::MaybeTlsStream, + Message, }; /// Stream Type pub type Stream = MaybeTlsStream<TcpStream>; -/// Web Socket Connection Type -pub type WebSocket = tungstenite::WebSocket<Stream>; +/// Error Type +pub type Error = tungstenite::error::Error; /// Handshake Error Type pub type HandshakeError = tungstenite::handshake::HandshakeError<ServerHandshake<Stream, NoCallback>>; +/// +pub struct WebSocket(tungstenite::WebSocket<Stream>); + +impl UniformChannel for WebSocket { + type Message = Message; + type ReadError = Error; + type WriteError = Error; + + #[inline] + fn read(&mut self) -> Result<Self::Message, Self::ReadError> { + self.0.read_message() + } + + #[inline] + fn write(&mut self, message: Self::Message) -> Result<(), Self::WriteError> { + self.0.write_message(message) + } +} + +/// +pub struct Synchronize; + +/// +pub struct SyncRequest { + /// + starting_index: usize, + + /// + inserts: Vec<(Utxo<Config>, EncryptedNote<Config>)>, + + /// + removes: Vec<VoidNumber<Config>>, +} + +impl From<SyncRequest, WebSocket> for Message { + #[inline] + fn from(request: SyncRequest) -> Self { + todo!() + } +} + +impl TryFrom<Message, WebSocket> for SyncRequest { + type Error = (); + + #[inline] + fn try_from(message: Message) -> Result<Self, Self::Error> { + todo!() + } +} + +impl From<SyncResponse, WebSocket> for Message { + #[inline] + fn from(response: SyncResponse) -> Self { + todo!() + } +} + +impl TryFrom<Message, WebSocket> for SyncResponse { + type Error = (); + + #[inline] + fn try_from(message: Message) -> Result<Self, Self::Error> { + todo!() + } +} + +impl MessageProtocol for Synchronize { + type Request = SyncRequest; + type Response = SyncResponse; + type Client = WebSocket; + type Server = WebSocket; +} + +/// +pub struct Sign; + +impl From<Transaction<Config>, WebSocket> for Message { + #[inline] + fn from(request: Transaction<Config>) -> Message { + todo!() + } +} + +impl TryFrom<Message, WebSocket> for Transaction<Config> { + type Error = (); + + #[inline] + fn try_from(message: Message) -> Result<Self, Self::Error> { + todo!() + } +} + +impl From<SignResponse<Config>, WebSocket> for Message { + #[inline] + fn from(response: SignResponse<Config>) -> Self { + todo!() + } +} + +impl TryFrom<Message, WebSocket> for SignResponse<Config> { + type Error = (); + + #[inline] + fn try_from(message: Message) -> Result<Self, Self::Error> { + todo!() + } +} + +impl MessageProtocol for Sign { + type Request = Transaction<Config>; + type Response = SignResponse<Config>; + type Client = WebSocket; + type Server = WebSocket; +} + /// Client pub struct Client { - /// Underlying Client Connection + /// Underlying Connection connection: WebSocket, } impl Client { /// Builds a new [`Client`] from Web Socket `url`. #[inline] - pub fn new<U>(url: U) -> Result<Self, tungstenite::error::Error> + pub fn new<U>(url: U) -> Result<Self, Error> where U: IntoClientRequest, { Ok(Self { - connection: tungstenite::connect(url)?.0, + connection: WebSocket(tungstenite::connect(url)?.0), }) } } impl signer::Connection<Config> for Client { - type Error = (); + type Error = ParsingError<Error, ()>; #[inline] fn sync<I, R>( @@ -76,12 +195,20 @@ impl signer::Connection<Config> for Client { I: IntoIterator<Item = (Utxo<Config>, EncryptedNote<Config>)>, R: IntoIterator<Item = VoidNumber<Config>>, { - todo!() + Synchronize::send( + &mut self.connection, + SyncRequest { + starting_index, + inserts: inserts.into_iter().collect(), + removes: removes.into_iter().collect(), + }, + ) + .map_err(ChannelError::into_parsing_error) } #[inline] fn sign(&mut self, transaction: Transaction<Config>) -> SignResult<Config, Self> { - todo!() + Sign::send(&mut self.connection, transaction).map_err(ChannelError::into_parsing_error) } #[inline] @@ -95,19 +222,24 @@ pub struct Server { /// Signer Base base: SignerBase, - /// Underlying Server Connection - connection: WebSocket, + /// Underlying Connection + connection: Option<WebSocket>, } impl Server { - /// Builds a new [`Server`] from a [`Stream`]. + /// Builds a new [`Server`] from `base`. #[inline] - pub fn new(stream: Stream) -> Result<Self, HandshakeError> { - /* TODO: - Ok(Self { - connection: tungstenite::accept(stream)?, - }) - */ - todo!() + pub fn new(base: SignerBase) -> Self { + Self { + base, + connection: None, + } + } + + /// + #[inline] + pub fn connect(&mut self, stream: Stream) -> Result<(), HandshakeError> { + self.connection = Some(WebSocket(tungstenite::accept(stream)?)); + Ok(()) } } diff --git a/manta-util/src/convert.rs b/manta-util/src/convert.rs index 47d5d708e..8c843bbe1 100644 --- a/manta-util/src/convert.rs +++ b/manta-util/src/convert.rs @@ -29,3 +29,76 @@ pub type Never = Infallible; pub fn never<T>(_: Never) -> T { unreachable!("This type never has any values, so this promotion is safe.") } + +/// Contextual Conversion Equivalent of [`From`](core::convert::From) +pub trait From<T, CONTEXT = ()>: Sized { + /// Performs the conversion from `t` to an element of type `Self`. + fn from(t: T) -> Self; +} + +impl<T, CONTEXT> From<T, CONTEXT> for T { + #[inline] + fn from(t: T) -> Self { + t + } +} + +/// Contextual Conversion Equivalent of [`Into`](core::convert::Into) +pub trait Into<T, CONTEXT = ()>: Sized { + /// Performs the conversion from `self` to an element of type `T`. + fn into(self) -> T; +} + +impl<A, B, CONTEXT> Into<B, CONTEXT> for A +where + B: From<A, CONTEXT>, +{ + #[inline] + fn into(self) -> B { + B::from(self) + } +} + +/// Contextual Conversion Equivalent of [`TryFrom`](core::convert::TryFrom) +pub trait TryFrom<T, CONTEXT = ()>: Sized { + /// Conversion Error Type + type Error; + + /// Tries to perform the conversion from `t` to an element of type `Self` but may fail + /// with [`Self::Error`]. + fn try_from(t: T) -> Result<Self, Self::Error>; +} + +impl<A, B, CONTEXT> TryFrom<A, CONTEXT> for B +where + A: Into<B, CONTEXT>, +{ + type Error = Infallible; + + #[inline] + fn try_from(a: A) -> Result<Self, Self::Error> { + Ok(a.into()) + } +} + +/// Contextual Conversion Equivalent of [`TryInto`](core::convert::TryInto) +pub trait TryInto<T, CONTEXT = ()>: Sized { + /// Conversion Error Type + type Error; + + /// Tries to perform the conversion from `self` to an element of type `T` but may fail with + /// [`Self::Error`]. + fn try_into(self) -> Result<T, Self::Error>; +} + +impl<A, B, CONTEXT> TryInto<B, CONTEXT> for A +where + B: TryFrom<A, CONTEXT>, +{ + type Error = B::Error; + + #[inline] + fn try_into(self) -> Result<B, Self::Error> { + TryFrom::try_from(self) + } +} diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index 0c0ba0cbe..f0c5e90b5 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -31,6 +31,7 @@ pub mod cache; pub mod codec; pub mod convert; pub mod iter; +pub mod message; pub mod persistance; pub mod pointer; diff --git a/manta-util/src/message.rs b/manta-util/src/message.rs new file mode 100644 index 000000000..6726e2c73 --- /dev/null +++ b/manta-util/src/message.rs @@ -0,0 +1,218 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Message-Passing Utilities + +use crate::convert::{Into, TryInto}; + +/// Message-Passing Channel +pub trait Channel<R, W> { + /// Read Error Type + type ReadError; + + /// Write Error Type + type WriteError; + + /// Reads a message of type `R` from the channel, or a [`ReadError`](Self::ReadError) if the + /// read failed. + fn read(&mut self) -> Result<R, Self::ReadError>; + + /// Writes a `message` of type `W` to the channel, or a [`WriteError`](Self::WriteError) if the + /// write failed. + fn write(&mut self, message: W) -> Result<(), Self::WriteError>; +} + +/// Channel Error +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum ChannelError<R, W> { + /// Read Error + Read(R), + + /// Write Error + Write(W), +} + +impl<R, W> ChannelError<R, W> { + /// Converts `self` into an `Option` over [`R`](Channel::ReadError). + #[inline] + pub fn read(self) -> Option<R> { + match self { + Self::Read(err) => Some(err), + _ => None, + } + } + + /// Converts `self` into an `Option` over [`W`](Channel::WriteError). + #[inline] + pub fn write(self) -> Option<W> { + match self { + Self::Write(err) => Some(err), + _ => None, + } + } +} + +impl<E, P> ChannelError<ParsingReadError<E, P>, E> { + /// Unwraps the inner error. + #[inline] + pub fn into_parsing_error(self) -> ParsingError<E, P> { + match self { + Self::Read(err) => match err { + ParsingReadError::Read(err) => ParsingError::Error(err), + ParsingReadError::Parse(err) => ParsingError::Parse(err), + }, + Self::Write(err) => ParsingError::Error(err), + } + } +} + +impl<E> ChannelError<E, E> { + /// Unwraps the inner error. + #[inline] + pub fn into_inner(self) -> E { + match self { + Self::Read(err) => err, + Self::Write(err) => err, + } + } +} + +/// Parsing Error +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum ParsingError<E, P> { + /// Base Error + Error(E), + + /// Parse Error + Parse(P), +} + +/// Uniform Message-Passing Channel +pub trait UniformChannel { + /// Message Type + type Message; + + /// Read Error Type + type ReadError; + + /// Write Error Type + type WriteError; + + /// Reads a message from the channel, or a [`ReadError`](Self::ReadError) if the read failed. + fn read(&mut self) -> Result<Self::Message, Self::ReadError>; + + /// Writes a `message` to the channel, or a [`WriteError`](Self::WriteError) if the write + /// failed. + fn write(&mut self, message: Self::Message) -> Result<(), Self::WriteError>; +} + +impl<C, R, W> Channel<R, W> for C +where + C: UniformChannel, + C::Message: TryInto<R, C>, + W: Into<C::Message, C>, +{ + type ReadError = ParsingReadError<C::ReadError, <C::Message as TryInto<R, C>>::Error>; + type WriteError = C::WriteError; + + #[inline] + fn read(&mut self) -> Result<R, Self::ReadError> { + TryInto::try_into(self.read().map_err(ParsingReadError::Read)?) + .map_err(ParsingReadError::Parse) + } + + #[inline] + fn write(&mut self, message: W) -> Result<(), Self::WriteError> { + self.write(Into::into(message)) + } +} + +/// Parsing Read Error +/// +/// This `enum` is the error state for the [`Channel`] implementation of [`UniformChannel`] which +/// can either be a reading error or a parsing error when transforming a message from the uniform +/// format to the specific read type of the [`Channel`]. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum ParsingReadError<R, P> { + /// Read Error + Read(R), + + /// Parse Error + Parse(P), +} + +/// Client Error Type +pub type ClientError<P> = ChannelError< + <<P as MessageProtocol>::Client as Channel< + <P as MessageProtocol>::Response, + <P as MessageProtocol>::Request, + >>::ReadError, + <<P as MessageProtocol>::Client as Channel< + <P as MessageProtocol>::Response, + <P as MessageProtocol>::Request, + >>::WriteError, +>; + +/// Server Error Type +pub type ServerError<P> = ChannelError< + <<P as MessageProtocol>::Server as Channel< + <P as MessageProtocol>::Request, + <P as MessageProtocol>::Response, + >>::ReadError, + <<P as MessageProtocol>::Server as Channel< + <P as MessageProtocol>::Request, + <P as MessageProtocol>::Response, + >>::WriteError, +>; + +/// Message-Passing Protocol +pub trait MessageProtocol { + /// Request Type + type Request; + + /// Response Type + type Response; + + /// Client Channel Type + type Client: Channel<Self::Response, Self::Request>; + + /// Server Channel Type + type Server: Channel<Self::Request, Self::Response>; + + /// Sends the `request` from the `client` using [`write`](Channel::write), waiting for a + /// [`read`](Channel::read) from the [`Server`](Self::Server) on the other end. + #[inline] + fn send( + client: &mut Self::Client, + request: Self::Request, + ) -> Result<Self::Response, ClientError<Self>> { + client.write(request).map_err(ChannelError::Write)?; + client.read().map_err(ChannelError::Read) + } + + /// Waits on the result of a [`read`](Channel::read) on the `server` end of the channel, then + /// processes the result using `process` and sends the response using a + /// [`write`](Channel::write). + #[inline] + fn recv<F>(server: &mut Self::Server, process: F) -> Result<(), ServerError<Self>> + where + F: FnOnce(&mut Self::Server, Self::Request) -> Self::Response, + { + let request = server.read().map_err(ChannelError::Read)?; + let response = process(server, request); + server.write(response).map_err(ChannelError::Write) + } +} From 3b28ac28312fb9ec37af553d204833e431598a1b Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 28 Jan 2022 17:36:15 -0500 Subject: [PATCH 213/275] wip: start building server/client abstractions for signer --- .../src/wallet/{signer.rs => signer/base.rs} | 49 ++--- manta-accounting/src/wallet/signer/client.rs | 196 ++++++++++++++++++ manta-accounting/src/wallet/signer/mod.rs | 24 +++ manta-accounting/src/wallet/signer/server.rs | 76 +++++++ manta-pay/src/wallet/signer.rs | 163 +++------------ manta-util/src/convert.rs | 73 ------- manta-util/src/message.rs | 175 +++++++--------- 7 files changed, 419 insertions(+), 337 deletions(-) rename manta-accounting/src/wallet/{signer.rs => signer/base.rs} (95%) create mode 100644 manta-accounting/src/wallet/signer/client.rs create mode 100644 manta-accounting/src/wallet/signer/mod.rs create mode 100644 manta-accounting/src/wallet/signer/server.rs diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer/base.rs similarity index 95% rename from manta-accounting/src/wallet/signer.rs rename to manta-accounting/src/wallet/signer/base.rs index 84eac4c47..8efa4634c 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer/base.rs @@ -74,33 +74,23 @@ where /// Pushes updates from the ledger to the wallet, synchronizing it with the ledger state and /// returning an updated asset distribution. - fn sync<I, R>(&mut self, starting_index: usize, inserts: I, removes: R) -> SyncResult<C, Self> + fn sync<I, R>( + &mut self, + starting_index: usize, + inserts: I, + removes: R, + ) -> Result<SyncResponse, Self::Error> where I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, R: IntoIterator<Item = VoidNumber<C>>; /// Signs a `transaction` and returns the ledger transfer posts if successful. - fn sign(&mut self, transaction: Transaction<C>) -> SignResult<C, Self>; + fn sign(&mut self, transaction: Transaction<C>) -> Result<SignResponse<C>, Self::Error>; /// Returns a new [`ReceivingKey`] for `self` to receive assets. - fn receiving_key(&mut self) -> ReceivingKeyResult<C, Self>; + fn receiving_key(&mut self) -> Result<ReceivingKey<C>, Self::Error>; } -/// Synchronization Result -/// -/// See the [`sync`](Connection::sync) method on [`Connection`] for more. -pub type SyncResult<C, S> = Result<SyncResponse, <S as Connection<C>>::Error>; - -/// Signing Result -/// -/// See the [`sign`](Connection::sign) method on [`Connection`] for more. -pub type SignResult<C, S> = Result<SignResponse<C>, <S as Connection<C>>::Error>; - -/// Receving Key Result -/// -/// See the [`receiving_key`](Connection::receiving_key) method on [`Connection`] for more. -pub type ReceivingKeyResult<C, S> = Result<ReceivingKey<C>, <S as Connection<C>>::Error>; - /// Signer Synchronization Response /// /// This `enum` is created by the [`sync`](Connection::sync) method on [`Connection`]. @@ -378,7 +368,7 @@ where inserts: I, mut void_numbers: Vec<VoidNumber<C>>, is_partial: bool, - ) -> SyncResult<C, Signer<C>> + ) -> Result<SyncResponse, Error<C>> where I: Iterator<Item = (Utxo<C>, EncryptedNote<C>)>, { @@ -765,7 +755,7 @@ where starting_index: usize, inserts: I, removes: R, - ) -> SyncResult<C, Self> + ) -> Result<SyncResponse, Error<C>> where I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, R: IntoIterator<Item = VoidNumber<C>>, @@ -800,7 +790,7 @@ where &mut self, asset: Asset, receiver: Option<ReceivingKey<C>>, - ) -> SignResult<C, Self> { + ) -> Result<SignResponse<C>, Error<C>> { let selection = self.state.select(&self.parameters.parameters, asset)?; let change = self .state @@ -838,7 +828,7 @@ where /// Signs the `transaction`, generating transfer posts without releasing resources. #[inline] - fn sign_internal(&mut self, transaction: Transaction<C>) -> SignResult<C, Self> { + fn sign_internal(&mut self, transaction: Transaction<C>) -> Result<SignResponse<C>, Error<C>> { match transaction { Transaction::Mint(asset) => { let receiver = self @@ -863,7 +853,7 @@ where /// Signs the `transaction`, generating transfer posts. #[inline] - pub fn sign(&mut self, transaction: Transaction<C>) -> SignResult<C, Self> { + pub fn sign(&mut self, transaction: Transaction<C>) -> Result<SignResponse<C>, Error<C>> { // TODO: Should we do a time-based release mechanism to amortize the cost of reading // from the proving context cache? let result = self.sign_internal(transaction); @@ -874,7 +864,7 @@ where /// Returns a new [`ReceivingKey`] for `self` to receive assets. #[inline] - pub fn receiving_key(&mut self) -> ReceivingKeyResult<C, Self> { + pub fn receiving_key(&mut self) -> Result<ReceivingKey<C>, Error<C>> { let keypair = self .state .accounts @@ -893,7 +883,12 @@ where type Error = Error<C>; #[inline] - fn sync<I, R>(&mut self, starting_index: usize, inserts: I, removes: R) -> SyncResult<C, Self> + fn sync<I, R>( + &mut self, + starting_index: usize, + inserts: I, + removes: R, + ) -> Result<SyncResponse, Self::Error> where I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, R: IntoIterator<Item = VoidNumber<C>>, @@ -902,12 +897,12 @@ where } #[inline] - fn sign(&mut self, transaction: Transaction<C>) -> SignResult<C, Self> { + fn sign(&mut self, transaction: Transaction<C>) -> Result<SignResponse<C>, Self::Error> { self.sign(transaction) } #[inline] - fn receiving_key(&mut self) -> ReceivingKeyResult<C, Self> { + fn receiving_key(&mut self) -> Result<ReceivingKey<C>, Self::Error> { self.receiving_key() } } diff --git a/manta-accounting/src/wallet/signer/client.rs b/manta-accounting/src/wallet/signer/client.rs new file mode 100644 index 000000000..f35675bfe --- /dev/null +++ b/manta-accounting/src/wallet/signer/client.rs @@ -0,0 +1,196 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Signer Client Abstraction + +use crate::{ + transfer::{ + canonical::Transaction, Configuration, EncryptedNote, ReceivingKey, Utxo, VoidNumber, + }, + wallet::signer::{self, SignResponse, SyncResponse}, +}; +use core::marker::PhantomData; +use manta_util::message::{Channel, ChannelError, Input, Output, ParsingReadError, UniformChannel}; + +/// Parsing Error +pub enum ParsingError<SY, SI, RE> { + /// Sync Response Error + SyncResponse(SY), + + /// Sign Response Error + SignResponse(SI), + + /// Receiving Key Response Error + ReceivingKey(RE), +} + +/// Error +pub enum Error<E, W, R, SY, SI, RE> { + /// Signer Error + Signer(E), + + /// Channel Error + Channel(ChannelError<W, R>), + + /// Parsing Error + Parse(ParsingError<SY, SI, RE>), +} + +impl<E, W, R, SY, SI, RE> Error<E, W, R, SY, SI, RE> { + /// + #[inline] + pub fn convert_sync<T>( + result: Result<Result<T, E>, ChannelError<W, ParsingReadError<R, SY>>>, + ) -> Result<T, Self> { + match result { + Ok(Ok(value)) => Ok(value), + Ok(Err(err)) => Err(Self::Signer(err)), + Err(ChannelError::Write(err)) => Err(Self::Channel(ChannelError::Write(err))), + Err(ChannelError::Read(ParsingReadError::Read(err))) => { + Err(Self::Channel(ChannelError::Read(err))) + } + Err(ChannelError::Read(ParsingReadError::Parse(err))) => { + Err(Self::Parse(ParsingError::SyncResponse(err))) + } + } + } + + /// + #[inline] + pub fn convert_sign<T>( + result: Result<Result<T, E>, ChannelError<W, ParsingReadError<R, SI>>>, + ) -> Result<T, Self> { + match result { + Ok(Ok(value)) => Ok(value), + Ok(Err(err)) => Err(Self::Signer(err)), + Err(ChannelError::Write(err)) => Err(Self::Channel(ChannelError::Write(err))), + Err(ChannelError::Read(ParsingReadError::Read(err))) => { + Err(Self::Channel(ChannelError::Read(err))) + } + Err(ChannelError::Read(ParsingReadError::Parse(err))) => { + Err(Self::Parse(ParsingError::SignResponse(err))) + } + } + } + + /// + #[inline] + pub fn convert_receiving_key<T>( + result: Result<Result<T, E>, ChannelError<W, ParsingReadError<R, RE>>>, + ) -> Result<T, Self> { + match result { + Ok(Ok(value)) => Ok(value), + Ok(Err(err)) => Err(Self::Signer(err)), + Err(ChannelError::Write(err)) => Err(Self::Channel(ChannelError::Write(err))), + Err(ChannelError::Read(ParsingReadError::Read(err))) => { + Err(Self::Channel(ChannelError::Read(err))) + } + Err(ChannelError::Read(ParsingReadError::Parse(err))) => { + Err(Self::Parse(ParsingError::ReceivingKey(err))) + } + } + } +} + +/// Sync Request +pub struct SyncRequest<C> +where + C: Configuration, +{ + /// Starting Index + pub starting_index: usize, + + /// Balance Insertions + pub inserts: Vec<(Utxo<C>, EncryptedNote<C>)>, + + /// Balance Removals + pub removes: Vec<VoidNumber<C>>, +} + +/// Client Connection +pub struct Client<C, E, H> +where + C: Configuration, +{ + /// Communication Channel + channel: H, + + /// Type Parameter Marker + __: PhantomData<(C, E)>, +} + +impl<C, E, H> Client<C, E, H> +where + C: Configuration, +{ + /// Builds a new [`Client`] from `channel`. + #[inline] + pub fn new(channel: H) -> Self { + Self { + channel, + __: PhantomData, + } + } +} + +impl<C, E, H> signer::Connection<C> for Client<C, E, H> +where + C: signer::Configuration, + H: UniformChannel + + Input<SyncRequest<C>> + + Input<Transaction<C>> + + Input<()> + + Output<Result<SyncResponse, E>> + + Output<Result<SignResponse<C>, E>> + + Output<Result<ReceivingKey<C>, E>>, +{ + type Error = Error< + E, + <H as UniformChannel>::WriteError, + <H as UniformChannel>::ReadError, + <H as Output<Result<SyncResponse, E>>>::Error, + <H as Output<Result<SignResponse<C>, E>>>::Error, + <H as Output<Result<ReceivingKey<C>, E>>>::Error, + >; + + #[inline] + fn sync<I, R>( + &mut self, + starting_index: usize, + inserts: I, + removes: R, + ) -> Result<SyncResponse, Self::Error> + where + I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, + R: IntoIterator<Item = VoidNumber<C>>, + { + Error::convert_sync(self.channel.request(SyncRequest { + starting_index, + inserts: inserts.into_iter().collect(), + removes: removes.into_iter().collect(), + })) + } + + #[inline] + fn sign(&mut self, transaction: Transaction<C>) -> Result<SignResponse<C>, Self::Error> { + Error::convert_sign(self.channel.request(transaction)) + } + + #[inline] + fn receiving_key(&mut self) -> Result<ReceivingKey<C>, Self::Error> { + Error::convert_receiving_key(self.channel.request(())) + } +} diff --git a/manta-accounting/src/wallet/signer/mod.rs b/manta-accounting/src/wallet/signer/mod.rs new file mode 100644 index 000000000..cb6edccbe --- /dev/null +++ b/manta-accounting/src/wallet/signer/mod.rs @@ -0,0 +1,24 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Signer Abstractions + +mod base; + +pub mod client; +pub mod server; + +pub use base::*; diff --git a/manta-accounting/src/wallet/signer/server.rs b/manta-accounting/src/wallet/signer/server.rs new file mode 100644 index 000000000..7dbe23cac --- /dev/null +++ b/manta-accounting/src/wallet/signer/server.rs @@ -0,0 +1,76 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Signer Server Abstraction + +use crate::{ + transfer::canonical::Transaction, + wallet::signer::{Configuration, Error, SignResponse, Signer}, +}; +use manta_util::message::{Channel, ChannelError}; + +/// +pub struct Server<C, H> +where + C: Configuration, +{ + /// Base Signer + base: Signer<C>, + + /// Communication Channel + channel: Option<H>, +} + +impl<C, H> Server<C, H> +where + C: Configuration, +{ + /// + #[inline] + pub fn new(base: Signer<C>) -> Self { + Self { + base, + channel: None, + } + } + + /// + #[inline] + pub fn connect(&mut self, channel: H) { + self.channel = Some(channel); + } + + /// + #[inline] + pub fn disconnect(&mut self) { + self.channel = None; + } + + /// + #[inline] + pub fn sign(&mut self) -> Result<bool, ChannelError<H::WriteError, H::ReadError>> + where + H: Channel<Result<SignResponse<C>, Error<C>>, Transaction<C>>, + { + if let Some(channel) = self.channel.as_mut() { + channel + .listen(|_, transaction| self.base.sign(transaction)) + .map(move |_| true) + } else { + Ok(false) + } + } +} diff --git a/manta-pay/src/wallet/signer.rs b/manta-pay/src/wallet/signer.rs index aab32ddc1..d32a697c5 100644 --- a/manta-pay/src/wallet/signer.rs +++ b/manta-pay/src/wallet/signer.rs @@ -21,14 +21,9 @@ use crate::{config::Config, wallet::SignerBase}; use manta_accounting::{ transfer::{canonical::Transaction, EncryptedNote, ReceivingKey, Utxo, VoidNumber}, - wallet::signer::{ - self, ReceivingKeyResult, SignResponse, SignResult, SyncResponse, SyncResult, - }, -}; -use manta_util::{ - convert::{From, TryFrom}, - message::{ChannelError, MessageProtocol, ParsingError, UniformChannel}, + wallet::signer::{self, client::SyncRequest, SignResponse, SyncResponse}, }; +use manta_util::message::{ChannelError, ParsingError, UniformChannel}; use std::net::TcpStream; use tungstenite::{ client::IntoClientRequest, @@ -67,101 +62,9 @@ impl UniformChannel for WebSocket { } /// -pub struct Synchronize; - -/// -pub struct SyncRequest { - /// - starting_index: usize, - - /// - inserts: Vec<(Utxo<Config>, EncryptedNote<Config>)>, - - /// - removes: Vec<VoidNumber<Config>>, -} - -impl From<SyncRequest, WebSocket> for Message { - #[inline] - fn from(request: SyncRequest) -> Self { - todo!() - } -} - -impl TryFrom<Message, WebSocket> for SyncRequest { - type Error = (); - - #[inline] - fn try_from(message: Message) -> Result<Self, Self::Error> { - todo!() - } -} - -impl From<SyncResponse, WebSocket> for Message { - #[inline] - fn from(response: SyncResponse) -> Self { - todo!() - } -} - -impl TryFrom<Message, WebSocket> for SyncResponse { - type Error = (); - - #[inline] - fn try_from(message: Message) -> Result<Self, Self::Error> { - todo!() - } -} - -impl MessageProtocol for Synchronize { - type Request = SyncRequest; - type Response = SyncResponse; - type Client = WebSocket; - type Server = WebSocket; -} - -/// -pub struct Sign; - -impl From<Transaction<Config>, WebSocket> for Message { - #[inline] - fn from(request: Transaction<Config>) -> Message { - todo!() - } -} - -impl TryFrom<Message, WebSocket> for Transaction<Config> { - type Error = (); - - #[inline] - fn try_from(message: Message) -> Result<Self, Self::Error> { - todo!() - } -} - -impl From<SignResponse<Config>, WebSocket> for Message { - #[inline] - fn from(response: SignResponse<Config>) -> Self { - todo!() - } -} - -impl TryFrom<Message, WebSocket> for SignResponse<Config> { - type Error = (); - - #[inline] - fn try_from(message: Message) -> Result<Self, Self::Error> { - todo!() - } -} - -impl MessageProtocol for Sign { - type Request = Transaction<Config>; - type Response = SignResponse<Config>; - type Client = WebSocket; - type Server = WebSocket; -} +pub type Client = signer::client::Client<Config, signer::Error<Config>, WebSocket>; +/* /// Client pub struct Client { /// Underlying Connection @@ -180,42 +83,7 @@ impl Client { }) } } - -impl signer::Connection<Config> for Client { - type Error = ParsingError<Error, ()>; - - #[inline] - fn sync<I, R>( - &mut self, - starting_index: usize, - inserts: I, - removes: R, - ) -> SyncResult<Config, Self> - where - I: IntoIterator<Item = (Utxo<Config>, EncryptedNote<Config>)>, - R: IntoIterator<Item = VoidNumber<Config>>, - { - Synchronize::send( - &mut self.connection, - SyncRequest { - starting_index, - inserts: inserts.into_iter().collect(), - removes: removes.into_iter().collect(), - }, - ) - .map_err(ChannelError::into_parsing_error) - } - - #[inline] - fn sign(&mut self, transaction: Transaction<Config>) -> SignResult<Config, Self> { - Sign::send(&mut self.connection, transaction).map_err(ChannelError::into_parsing_error) - } - - #[inline] - fn receiving_key(&mut self) -> ReceivingKeyResult<Config, Self> { - todo!() - } -} +*/ /// Signer Server pub struct Server { @@ -242,4 +110,25 @@ impl Server { self.connection = Some(WebSocket(tungstenite::accept(stream)?)); Ok(()) } + + /* + /// + #[inline] + pub fn sync(&mut self, request: SyncRequest) -> SyncResult<Config, SignerBase> { + self.base + .sync(request.starting_index, request.inserts, request.removes) + } + + /// + #[inline] + pub fn sign(&mut self, transaction: Transaction<Config>) -> SignResult<Config, SignerBase> { + self.base.sign(transaction) + } + + /// + #[inline] + pub fn receiving_key(&mut self) -> ReceivingKeyResult<Config, SignerBase> { + self.base.receiving_key() + } + */ } diff --git a/manta-util/src/convert.rs b/manta-util/src/convert.rs index 8c843bbe1..47d5d708e 100644 --- a/manta-util/src/convert.rs +++ b/manta-util/src/convert.rs @@ -29,76 +29,3 @@ pub type Never = Infallible; pub fn never<T>(_: Never) -> T { unreachable!("This type never has any values, so this promotion is safe.") } - -/// Contextual Conversion Equivalent of [`From`](core::convert::From) -pub trait From<T, CONTEXT = ()>: Sized { - /// Performs the conversion from `t` to an element of type `Self`. - fn from(t: T) -> Self; -} - -impl<T, CONTEXT> From<T, CONTEXT> for T { - #[inline] - fn from(t: T) -> Self { - t - } -} - -/// Contextual Conversion Equivalent of [`Into`](core::convert::Into) -pub trait Into<T, CONTEXT = ()>: Sized { - /// Performs the conversion from `self` to an element of type `T`. - fn into(self) -> T; -} - -impl<A, B, CONTEXT> Into<B, CONTEXT> for A -where - B: From<A, CONTEXT>, -{ - #[inline] - fn into(self) -> B { - B::from(self) - } -} - -/// Contextual Conversion Equivalent of [`TryFrom`](core::convert::TryFrom) -pub trait TryFrom<T, CONTEXT = ()>: Sized { - /// Conversion Error Type - type Error; - - /// Tries to perform the conversion from `t` to an element of type `Self` but may fail - /// with [`Self::Error`]. - fn try_from(t: T) -> Result<Self, Self::Error>; -} - -impl<A, B, CONTEXT> TryFrom<A, CONTEXT> for B -where - A: Into<B, CONTEXT>, -{ - type Error = Infallible; - - #[inline] - fn try_from(a: A) -> Result<Self, Self::Error> { - Ok(a.into()) - } -} - -/// Contextual Conversion Equivalent of [`TryInto`](core::convert::TryInto) -pub trait TryInto<T, CONTEXT = ()>: Sized { - /// Conversion Error Type - type Error; - - /// Tries to perform the conversion from `self` to an element of type `T` but may fail with - /// [`Self::Error`]. - fn try_into(self) -> Result<T, Self::Error>; -} - -impl<A, B, CONTEXT> TryInto<B, CONTEXT> for A -where - B: TryFrom<A, CONTEXT>, -{ - type Error = B::Error; - - #[inline] - fn try_into(self) -> Result<B, Self::Error> { - TryFrom::try_from(self) - } -} diff --git a/manta-util/src/message.rs b/manta-util/src/message.rs index 6726e2c73..bdd95a78b 100644 --- a/manta-util/src/message.rs +++ b/manta-util/src/message.rs @@ -16,65 +16,90 @@ //! Message-Passing Utilities -use crate::convert::{Into, TryInto}; - /// Message-Passing Channel -pub trait Channel<R, W> { +pub trait Channel<W, R = W> { + /// Write Error Type + type WriteError; + /// Read Error Type type ReadError; - /// Write Error Type - type WriteError; + /// Writes a `message` of type `W` to the channel, or a [`WriteError`](Self::WriteError) if the + /// write failed. + fn write(&mut self, message: W) -> Result<(), Self::WriteError>; /// Reads a message of type `R` from the channel, or a [`ReadError`](Self::ReadError) if the /// read failed. fn read(&mut self) -> Result<R, Self::ReadError>; - /// Writes a `message` of type `W` to the channel, or a [`WriteError`](Self::WriteError) if the - /// write failed. - fn write(&mut self, message: W) -> Result<(), Self::WriteError>; + /// Sends a `request` of type `W` on the channel using [`write`](Self::write) waiting for a + /// response of type `R` on the channel using [`read`](Self::read). + #[inline] + fn request( + &mut self, + request: W, + ) -> Result<R, ChannelError<Self::WriteError, Self::ReadError>> { + self.write(request).map_err(ChannelError::Write)?; + self.read().map_err(ChannelError::Read) + } + + /// Listens on the channel for a request of type `R` using [`read`](Self::read), processes the + /// request using `process` and sends the response message back along the channel using + /// [`write`](Self::write). + #[inline] + fn listen<F>( + &mut self, + process: F, + ) -> Result<(), ChannelError<Self::WriteError, Self::ReadError>> + where + F: FnOnce(&mut Self, R) -> W, + { + let request = self.read().map_err(ChannelError::Read)?; + let response = process(self, request); + self.write(response).map_err(ChannelError::Write) + } } /// Channel Error #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum ChannelError<R, W> { - /// Read Error - Read(R), - +pub enum ChannelError<W, R> { /// Write Error Write(W), + + /// Read Error + Read(R), } -impl<R, W> ChannelError<R, W> { - /// Converts `self` into an `Option` over [`R`](Channel::ReadError). +impl<W, R> ChannelError<W, R> { + /// Converts `self` into an `Option` over [`W`](Channel::WriteError). #[inline] - pub fn read(self) -> Option<R> { + pub fn write(self) -> Option<W> { match self { - Self::Read(err) => Some(err), + Self::Write(err) => Some(err), _ => None, } } - /// Converts `self` into an `Option` over [`W`](Channel::WriteError). + /// Converts `self` into an `Option` over [`R`](Channel::ReadError). #[inline] - pub fn write(self) -> Option<W> { + pub fn read(self) -> Option<R> { match self { - Self::Write(err) => Some(err), + Self::Read(err) => Some(err), _ => None, } } } -impl<E, P> ChannelError<ParsingReadError<E, P>, E> { +impl<P, E> ChannelError<E, ParsingReadError<E, P>> { /// Unwraps the inner error. #[inline] pub fn into_parsing_error(self) -> ParsingError<E, P> { match self { + Self::Write(err) => ParsingError::Error(err), Self::Read(err) => match err { ParsingReadError::Read(err) => ParsingError::Error(err), ParsingReadError::Parse(err) => ParsingError::Parse(err), }, - Self::Write(err) => ParsingError::Error(err), } } } @@ -84,8 +109,8 @@ impl<E> ChannelError<E, E> { #[inline] pub fn into_inner(self) -> E { match self { - Self::Read(err) => err, Self::Write(err) => err, + Self::Read(err) => err, } } } @@ -100,43 +125,56 @@ pub enum ParsingError<E, P> { Parse(P), } +/// Uniform Channel Input +pub trait Input<T>: UniformChannel { + /// Converts `t` into a message that can be sent in the [`UniformChannel`]. + fn convert(t: T) -> Self::Message; +} + +/// Uniform Channel Output +pub trait Output<T>: UniformChannel { + /// Parsing Error + type Error; + + /// Parses an incoming message that was just output from the [`UniformChannel`] and tries to + /// convert it into `T`. + fn parse(message: Self::Message) -> Result<T, Self::Error>; +} + /// Uniform Message-Passing Channel pub trait UniformChannel { /// Message Type type Message; - /// Read Error Type - type ReadError; - /// Write Error Type type WriteError; - /// Reads a message from the channel, or a [`ReadError`](Self::ReadError) if the read failed. - fn read(&mut self) -> Result<Self::Message, Self::ReadError>; + /// Read Error Type + type ReadError; /// Writes a `message` to the channel, or a [`WriteError`](Self::WriteError) if the write /// failed. fn write(&mut self, message: Self::Message) -> Result<(), Self::WriteError>; + + /// Reads a message from the channel, or a [`ReadError`](Self::ReadError) if the read failed. + fn read(&mut self) -> Result<Self::Message, Self::ReadError>; } -impl<C, R, W> Channel<R, W> for C +impl<C, W, R> Channel<W, R> for C where - C: UniformChannel, - C::Message: TryInto<R, C>, - W: Into<C::Message, C>, + C: UniformChannel + Input<W> + Output<R>, { - type ReadError = ParsingReadError<C::ReadError, <C::Message as TryInto<R, C>>::Error>; type WriteError = C::WriteError; + type ReadError = ParsingReadError<C::ReadError, <C as Output<R>>::Error>; #[inline] - fn read(&mut self) -> Result<R, Self::ReadError> { - TryInto::try_into(self.read().map_err(ParsingReadError::Read)?) - .map_err(ParsingReadError::Parse) + fn write(&mut self, message: W) -> Result<(), Self::WriteError> { + self.write(Self::convert(message)) } #[inline] - fn write(&mut self, message: W) -> Result<(), Self::WriteError> { - self.write(Into::into(message)) + fn read(&mut self) -> Result<R, Self::ReadError> { + Self::parse(self.read().map_err(ParsingReadError::Read)?).map_err(ParsingReadError::Parse) } } @@ -153,66 +191,3 @@ pub enum ParsingReadError<R, P> { /// Parse Error Parse(P), } - -/// Client Error Type -pub type ClientError<P> = ChannelError< - <<P as MessageProtocol>::Client as Channel< - <P as MessageProtocol>::Response, - <P as MessageProtocol>::Request, - >>::ReadError, - <<P as MessageProtocol>::Client as Channel< - <P as MessageProtocol>::Response, - <P as MessageProtocol>::Request, - >>::WriteError, ->; - -/// Server Error Type -pub type ServerError<P> = ChannelError< - <<P as MessageProtocol>::Server as Channel< - <P as MessageProtocol>::Request, - <P as MessageProtocol>::Response, - >>::ReadError, - <<P as MessageProtocol>::Server as Channel< - <P as MessageProtocol>::Request, - <P as MessageProtocol>::Response, - >>::WriteError, ->; - -/// Message-Passing Protocol -pub trait MessageProtocol { - /// Request Type - type Request; - - /// Response Type - type Response; - - /// Client Channel Type - type Client: Channel<Self::Response, Self::Request>; - - /// Server Channel Type - type Server: Channel<Self::Request, Self::Response>; - - /// Sends the `request` from the `client` using [`write`](Channel::write), waiting for a - /// [`read`](Channel::read) from the [`Server`](Self::Server) on the other end. - #[inline] - fn send( - client: &mut Self::Client, - request: Self::Request, - ) -> Result<Self::Response, ClientError<Self>> { - client.write(request).map_err(ChannelError::Write)?; - client.read().map_err(ChannelError::Read) - } - - /// Waits on the result of a [`read`](Channel::read) on the `server` end of the channel, then - /// processes the result using `process` and sends the response using a - /// [`write`](Channel::write). - #[inline] - fn recv<F>(server: &mut Self::Server, process: F) -> Result<(), ServerError<Self>> - where - F: FnOnce(&mut Self::Server, Self::Request) -> Self::Response, - { - let request = server.read().map_err(ChannelError::Read)?; - let response = process(server, request); - server.write(response).map_err(ChannelError::Write) - } -} From 99c53e7a575f969382e6dcdd0dcc30d7c463e41a Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 28 Jan 2022 18:35:07 -0500 Subject: [PATCH 214/275] fix: move to sync-request system --- manta-accounting/src/wallet/signer/base.rs | 38 ++++++++-------- manta-accounting/src/wallet/signer/client.rs | 38 ++-------------- manta-accounting/src/wallet/signer/server.rs | 47 +++++++++++++++++--- manta-accounting/src/wallet/state.rs | 8 +++- 4 files changed, 68 insertions(+), 63 deletions(-) diff --git a/manta-accounting/src/wallet/signer/base.rs b/manta-accounting/src/wallet/signer/base.rs index 8efa4634c..abed5063e 100644 --- a/manta-accounting/src/wallet/signer/base.rs +++ b/manta-accounting/src/wallet/signer/base.rs @@ -74,15 +74,7 @@ where /// Pushes updates from the ledger to the wallet, synchronizing it with the ledger state and /// returning an updated asset distribution. - fn sync<I, R>( - &mut self, - starting_index: usize, - inserts: I, - removes: R, - ) -> Result<SyncResponse, Self::Error> - where - I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, - R: IntoIterator<Item = VoidNumber<C>>; + fn sync(&mut self, request: SyncRequest<C>) -> Result<SyncResponse, Self::Error>; /// Signs a `transaction` and returns the ledger transfer posts if successful. fn sign(&mut self, transaction: Transaction<C>) -> Result<SignResponse<C>, Self::Error>; @@ -91,6 +83,21 @@ where fn receiving_key(&mut self) -> Result<ReceivingKey<C>, Self::Error>; } +/// Signer Synchronization Request +pub struct SyncRequest<C> +where + C: transfer::Configuration, +{ + /// Starting Index + pub starting_index: usize, + + /// Balance Insertions + pub inserts: Vec<(Utxo<C>, EncryptedNote<C>)>, + + /// Balance Removals + pub removes: Vec<VoidNumber<C>>, +} + /// Signer Synchronization Response /// /// This `enum` is created by the [`sync`](Connection::sync) method on [`Connection`]. @@ -883,17 +890,8 @@ where type Error = Error<C>; #[inline] - fn sync<I, R>( - &mut self, - starting_index: usize, - inserts: I, - removes: R, - ) -> Result<SyncResponse, Self::Error> - where - I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, - R: IntoIterator<Item = VoidNumber<C>>, - { - self.sync(starting_index, inserts, removes) + fn sync(&mut self, request: SyncRequest<C>) -> Result<SyncResponse, Self::Error> { + self.sync(request.starting_index, request.inserts, request.removes) } #[inline] diff --git a/manta-accounting/src/wallet/signer/client.rs b/manta-accounting/src/wallet/signer/client.rs index f35675bfe..572dd5f6b 100644 --- a/manta-accounting/src/wallet/signer/client.rs +++ b/manta-accounting/src/wallet/signer/client.rs @@ -17,10 +17,8 @@ //! Signer Client Abstraction use crate::{ - transfer::{ - canonical::Transaction, Configuration, EncryptedNote, ReceivingKey, Utxo, VoidNumber, - }, - wallet::signer::{self, SignResponse, SyncResponse}, + transfer::{canonical::Transaction, Configuration, ReceivingKey}, + wallet::signer::{self, SignResponse, SyncRequest, SyncResponse}, }; use core::marker::PhantomData; use manta_util::message::{Channel, ChannelError, Input, Output, ParsingReadError, UniformChannel}; @@ -105,21 +103,6 @@ impl<E, W, R, SY, SI, RE> Error<E, W, R, SY, SI, RE> { } } -/// Sync Request -pub struct SyncRequest<C> -where - C: Configuration, -{ - /// Starting Index - pub starting_index: usize, - - /// Balance Insertions - pub inserts: Vec<(Utxo<C>, EncryptedNote<C>)>, - - /// Balance Removals - pub removes: Vec<VoidNumber<C>>, -} - /// Client Connection pub struct Client<C, E, H> where @@ -167,21 +150,8 @@ where >; #[inline] - fn sync<I, R>( - &mut self, - starting_index: usize, - inserts: I, - removes: R, - ) -> Result<SyncResponse, Self::Error> - where - I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, - R: IntoIterator<Item = VoidNumber<C>>, - { - Error::convert_sync(self.channel.request(SyncRequest { - starting_index, - inserts: inserts.into_iter().collect(), - removes: removes.into_iter().collect(), - })) + fn sync(&mut self, request: SyncRequest<C>) -> Result<SyncResponse, Self::Error> { + Error::convert_sync(self.channel.request(request)) } #[inline] diff --git a/manta-accounting/src/wallet/signer/server.rs b/manta-accounting/src/wallet/signer/server.rs index 7dbe23cac..a44b87170 100644 --- a/manta-accounting/src/wallet/signer/server.rs +++ b/manta-accounting/src/wallet/signer/server.rs @@ -17,12 +17,12 @@ //! Signer Server Abstraction use crate::{ - transfer::canonical::Transaction, - wallet::signer::{Configuration, Error, SignResponse, Signer}, + transfer::{canonical::Transaction, ReceivingKey}, + wallet::signer::{Configuration, Error, SignResponse, Signer, SyncRequest, SyncResponse}, }; use manta_util::message::{Channel, ChannelError}; -/// +/// Signer Server pub struct Server<C, H> where C: Configuration, @@ -38,7 +38,7 @@ impl<C, H> Server<C, H> where C: Configuration, { - /// + /// Builds a new [`Server`] from a given `base` signer. #[inline] pub fn new(base: Signer<C>) -> Self { Self { @@ -47,19 +47,37 @@ where } } - /// + /// Connects `self` along the given `channel`. #[inline] pub fn connect(&mut self, channel: H) { self.channel = Some(channel); } - /// + /// Disconnects `self` from the internal channel. #[inline] pub fn disconnect(&mut self) { self.channel = None; } - /// + /// Runs the `sync` command on the [`Signer`]. + #[inline] + pub fn sync(&mut self) -> Result<bool, ChannelError<H::WriteError, H::ReadError>> + where + H: Channel<Result<SyncResponse, Error<C>>, SyncRequest<C>>, + { + if let Some(channel) = self.channel.as_mut() { + channel + .listen(|_, request| { + self.base + .sync(request.starting_index, request.inserts, request.removes) + }) + .map(move |_| true) + } else { + Ok(false) + } + } + + /// Runs the `sign` command on the [`Signer`]. #[inline] pub fn sign(&mut self) -> Result<bool, ChannelError<H::WriteError, H::ReadError>> where @@ -73,4 +91,19 @@ where Ok(false) } } + + /// Runs the `receiving_key` command on the [`Signer`]. + #[inline] + pub fn receiving_key(&mut self) -> Result<bool, ChannelError<H::WriteError, H::ReadError>> + where + H: Channel<Result<ReceivingKey<C>, Error<C>>, ()>, + { + if let Some(channel) = self.channel.as_mut() { + channel + .listen(|_, _| self.base.receiving_key()) + .map(move |_| true) + } else { + Ok(false) + } + } } diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 9ce82f5d2..2a2145567 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -24,7 +24,7 @@ use crate::{ }, wallet::{ ledger::{self, Checkpoint, PullResponse, PushResponse}, - signer::{self, SignResponse, SyncResponse}, + signer::{self, SignResponse, SyncRequest, SyncResponse}, }, }; use alloc::collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}; @@ -287,7 +287,11 @@ where } match self .signer - .sync(self.checkpoint.receiver_index(), receivers, senders) + .sync(SyncRequest { + starting_index: self.checkpoint.receiver_index(), + inserts: receivers.into_iter().collect(), + removes: senders.into_iter().collect(), + }) .map_err(Error::SignerError)? { SyncResponse::Partial { deposit, withdraw } => { From c3c6713b1133df0c3c63ca0587a16530b729a94f Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 2 Feb 2022 13:04:25 -0500 Subject: [PATCH 215/275] fix: move to simpler hd wallet model --- manta-accounting/src/fs.rs | 12 +- manta-accounting/src/key.rs | 515 +++++------------- .../src/wallet/{signer/base.rs => signer.rs} | 213 ++++---- manta-accounting/src/wallet/signer/client.rs | 166 ------ manta-accounting/src/wallet/signer/server.rs | 109 ---- manta-accounting/src/wallet/state.rs | 53 +- manta-crypto/src/ecc.rs | 8 +- manta-crypto/src/merkle_tree/tree.rs | 16 +- manta-pay/Cargo.toml | 6 +- manta-pay/src/crypto/hash/poseidon.rs | 10 +- manta-pay/src/key.rs | 28 +- manta-pay/src/test/ledger.rs | 6 +- manta-pay/src/wallet/mod.rs | 17 +- manta-pay/src/wallet/signer.rs | 134 ----- manta-pay/src/wallet/signer/client/http.rs | 17 + manta-pay/src/wallet/signer/client/mod.rs | 23 + .../src/wallet/signer/client/websocket.rs | 80 +++ .../src/wallet/signer/mod.rs | 7 +- manta-util/Cargo.toml | 2 - manta-util/src/codec.rs | 290 +++++++--- manta-util/src/lib.rs | 1 - manta-util/src/message.rs | 193 ------- 22 files changed, 652 insertions(+), 1254 deletions(-) rename manta-accounting/src/wallet/{signer/base.rs => signer.rs} (86%) delete mode 100644 manta-accounting/src/wallet/signer/client.rs delete mode 100644 manta-accounting/src/wallet/signer/server.rs delete mode 100644 manta-pay/src/wallet/signer.rs create mode 100644 manta-pay/src/wallet/signer/client/http.rs create mode 100644 manta-pay/src/wallet/signer/client/mod.rs create mode 100644 manta-pay/src/wallet/signer/client/websocket.rs rename {manta-accounting => manta-pay}/src/wallet/signer/mod.rs (91%) delete mode 100644 manta-util/src/message.rs diff --git a/manta-accounting/src/fs.rs b/manta-accounting/src/fs.rs index f44b1ce1d..458e16a32 100644 --- a/manta-accounting/src/fs.rs +++ b/manta-accounting/src/fs.rs @@ -44,10 +44,10 @@ pub trait SaveEncrypted { /// Saves the `payload` to `path` after serializing using the `saving_key` to encrypt it. #[inline] - fn save<P, E, C>(path: P, saving_key: &Self::SavingKey, payload: &E) -> Result<(), Self::Error> + fn save<P, E>(path: P, saving_key: &Self::SavingKey, payload: &E) -> Result<(), Self::Error> where P: AsRef<Self::Path>, - E: Encode<C>, + E: Encode, { Self::save_bytes(path, saving_key, payload.to_vec()) } @@ -72,10 +72,10 @@ pub trait LoadDecrypted { /// Loads a vector of bytes from `path` using `loading_key` to decrypt them, then deserializing /// the bytes to a concrete value of type `D`. #[inline] - fn load<P, D, C>(path: P, loading_key: &Self::LoadingKey) -> Result<D, LoadError<Self, D, C>> + fn load<P, D>(path: P, loading_key: &Self::LoadingKey) -> Result<D, LoadError<Self, D>> where P: AsRef<Self::Path>, - D: Decode<C>, + D: Decode, { match Self::load_bytes(path, loading_key) { Ok(bytes) => D::from_vec(bytes).map_err(LoadError::Decode), @@ -94,10 +94,10 @@ pub trait LoadDecrypted { Hash(bound = "L::Error: Hash, D::Error: Hash"), PartialEq(bound = "L::Error: PartialEq, D::Error: PartialEq") )] -pub enum LoadError<L, D, C = ()> +pub enum LoadError<L, D> where L: LoadDecrypted + ?Sized, - D: Decode<C> + ?Sized, + D: Decode + ?Sized, { /// Payload Loading Error Loading(L::Error), diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index 82ed9cc48..6ea9ebc36 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -16,7 +16,7 @@ //! Hierarchical Key Derivation Schemes -use alloc::{vec, vec::Vec}; +use alloc::vec::Vec; use core::{ fmt::{self, Debug}, hash::Hash, @@ -51,12 +51,6 @@ impl<M> HierarchicalKeyDerivationParameter<M> { } } - /// Resets the index of `self` to zero. - #[inline] - fn reset(&mut self) { - self.index = 0; - } - /// Increments the index of `self` by one unit. #[inline] fn increment(&mut self) { @@ -70,16 +64,16 @@ impl<M> HierarchicalKeyDerivationParameter<M> { } } -/// Implements the [`HierarchicalKeyDerivationParameter`] subtype for `$kind` and `$index`. -macro_rules! impl_index_kind { - ($doc:expr, $fmt:expr, $kind:ident, $index:ident) => { +/// Implements the [`HierarchicalKeyDerivationParameter`] subtype for `$type` and `$index`. +macro_rules! impl_index_type { + ($doc:expr, $fmt:expr, $type:ident, $index:ident) => { #[doc = $doc] - #[doc = "Kind"] + #[doc = "Type"] #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct $kind; + pub struct $type; #[doc = $doc] - pub type $index = HierarchicalKeyDerivationParameter<$kind>; + pub type $index = HierarchicalKeyDerivationParameter<$type>; impl Debug for $index { #[inline] @@ -90,59 +84,51 @@ macro_rules! impl_index_kind { }; } -impl_index_kind!("Account Index", "AccountIndex", AccountKind, AccountIndex); -impl_index_kind!("Spend Index", "SpendIndex", SpendKind, SpendIndex); -impl_index_kind!("View Index", "ViewIndex", ViewKind, ViewIndex); +impl_index_type!( + "Account Index", + "AccountIndex", + AccountIndexType, + AccountIndex +); +impl_index_type!("Key Index", "KeyIndex", KeyIndexType, KeyIndex); + +/// Key Kind +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Kind { + /// Spend Key + Spend, + + /// View Key + View, +} /// Hierarchical Key Derivation Scheme pub trait HierarchicalKeyDerivationScheme { /// Secret Key Type type SecretKey; - /// Key Derivation Error Type - type Error; - - /// Derives a secret key for `account` with `spend` and optional `view`. - fn derive( - &self, - account: AccountIndex, - spend: SpendIndex, - view: Option<ViewIndex>, - ) -> Result<Self::SecretKey, Self::Error>; + /// Derives a secret key for `account` with `kind` and `index`. + fn derive(&self, account: AccountIndex, kind: Kind, index: KeyIndex) -> Self::SecretKey; - /// Derives a spend secret key for `account` using the `spend` index. + /// Derives a spend secret key for `account` using the spend `index`. #[inline] - fn derive_spend( - &self, - account: AccountIndex, - spend: SpendIndex, - ) -> Result<Self::SecretKey, Self::Error> { - self.derive(account, spend, None) + fn derive_spend(&self, account: AccountIndex, index: KeyIndex) -> Self::SecretKey { + self.derive(account, Kind::Spend, index) } - /// Derives a view secret key for `account` using the `spend` and `view` indices. + /// Derives a view secret key for `account` using the view `index`. #[inline] - fn derive_view( - &self, - account: AccountIndex, - spend: SpendIndex, - view: ViewIndex, - ) -> Result<Self::SecretKey, Self::Error> { - self.derive(account, spend, Some(view)) + fn derive_view(&self, account: AccountIndex, index: KeyIndex) -> Self::SecretKey { + self.derive(account, Kind::View, index) } - /// Derives a spend-view pair of secret keys for `account` using the `spend` and `view` indices. + /// Derives a spend-view pair of secret keys for `account` and `index`. #[inline] - fn derive_pair( - &self, - account: AccountIndex, - spend: SpendIndex, - view: ViewIndex, - ) -> Result<SecretKeyPair<Self>, Self::Error> { - Ok(SecretKeyPair::new( - self.derive_spend(account, spend)?, - self.derive_view(account, spend, view)?, - )) + fn derive_pair(&self, account: AccountIndex, index: KeyIndex) -> SecretKeyPair<Self> { + SecretKeyPair::new( + self.derive_spend(account, index), + self.derive_view(account, index), + ) } /// Maps `self` along a key derivation function. @@ -161,35 +147,20 @@ where H: HierarchicalKeyDerivationScheme + ?Sized, { type SecretKey = H::SecretKey; - type Error = H::Error; #[inline] - fn derive( - &self, - account: AccountIndex, - spend: SpendIndex, - view: Option<ViewIndex>, - ) -> Result<Self::SecretKey, Self::Error> { - (*self).derive(account, spend, view) + fn derive(&self, account: AccountIndex, kind: Kind, index: KeyIndex) -> Self::SecretKey { + (*self).derive(account, kind, index) } #[inline] - fn derive_spend( - &self, - account: AccountIndex, - spend: SpendIndex, - ) -> Result<Self::SecretKey, Self::Error> { - (*self).derive_spend(account, spend) + fn derive_spend(&self, account: AccountIndex, index: KeyIndex) -> Self::SecretKey { + (*self).derive_spend(account, index) } #[inline] - fn derive_view( - &self, - account: AccountIndex, - spend: SpendIndex, - view: ViewIndex, - ) -> Result<Self::SecretKey, Self::Error> { - (*self).derive_view(account, spend, view) + fn derive_view(&self, account: AccountIndex, index: KeyIndex) -> Self::SecretKey { + (*self).derive_view(account, index) } } @@ -229,41 +200,20 @@ where K: KeyDerivationFunction<Key = H::SecretKey>, { type SecretKey = K::Output; - type Error = H::Error; #[inline] - fn derive( - &self, - account: AccountIndex, - spend: SpendIndex, - view: Option<ViewIndex>, - ) -> Result<Self::SecretKey, Self::Error> { - self.base - .derive(account, spend, view) - .map(move |k| K::derive(&k)) + fn derive(&self, account: AccountIndex, kind: Kind, index: KeyIndex) -> Self::SecretKey { + K::derive(&self.base.derive(account, kind, index)) } #[inline] - fn derive_spend( - &self, - account: AccountIndex, - spend: SpendIndex, - ) -> Result<Self::SecretKey, Self::Error> { - self.base - .derive_spend(account, spend) - .map(move |k| K::derive(&k)) + fn derive_spend(&self, account: AccountIndex, index: KeyIndex) -> Self::SecretKey { + K::derive(&self.base.derive_spend(account, index)) } #[inline] - fn derive_view( - &self, - account: AccountIndex, - spend: SpendIndex, - view: ViewIndex, - ) -> Result<Self::SecretKey, Self::Error> { - self.base - .derive_view(account, spend, view) - .map(move |k| K::derive(&k)) + fn derive_view(&self, account: AccountIndex, index: KeyIndex) -> Self::SecretKey { + K::derive(&self.base.derive_view(account, index)) } } @@ -304,118 +254,10 @@ where } } -/// Error Type -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "H::Error: Clone"), - Copy(bound = "H::Error: Copy"), - Debug(bound = "H::Error: Debug"), - Eq(bound = "H::Error: Eq"), - Hash(bound = "H::Error: Hash"), - PartialEq(bound = "H::Error: PartialEq") -)] -pub enum Error<H> -where - H: HierarchicalKeyDerivationScheme + ?Sized, -{ - /// Exceeded Current Maximum Spend Index - /// - /// See the [`increment_spend`](AccountMap::increment_spend) method on [`AccountMap`] for more. - ExceedingCurrentMaximumSpendIndex, - - /// Exceeded Current Maximum View Index - /// - /// See the [`increment_view`](AccountMap::increment_view) method on [`AccountMap`] for more. - ExceedingCurrentMaximumViewIndex, - - /// Key Derivation Error - KeyDerivationError(H::Error), -} - -/// Key Index Type -#[derive(Clone, Copy, Default, Eq, Hash, PartialEq)] -pub struct Index { - /// Spend Part of the Key Index - pub spend: SpendIndex, - - /// View Part of the Key Index - pub view: ViewIndex, -} - -impl Index { - /// Builds a new [`Index`] using `spend` and `view`. - #[inline] - pub fn new(spend: SpendIndex, view: ViewIndex) -> Self { - Self { spend, view } - } -} - -impl Debug for Index { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Index") - .field("spend", &self.spend.index) - .field("view", &self.view.index) - .finish() - } -} - -/// Maximum Index Bounds -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub struct MaxIndex { - /// Maximum View Indices - /// - /// The maximum view indices are sorted in order of each spend index. Therefore, the maximum - /// spend key is one less than the length of this vector. - view: Vec<ViewIndex>, -} - -impl MaxIndex { - /// Returns the maximum spend index. - #[inline] - pub fn spend(&self) -> SpendIndex { - SpendIndex::new((self.view.len() - 1) as u32) - } - - /// Returns the set of maximum view indices for each spend key in the range given by - /// [`spend`](Self::spend). - /// - /// # Note - /// - /// This array is always inhabited since there is always at least one spend key we can build and - /// so its corresponding view key must have a maximal index. - #[inline] - pub fn view(&self) -> &[ViewIndex] { - &self.view - } - - /// Increments the maximum spend index. - #[inline] - fn increment_spend(&mut self) -> &Self { - self.view.push(Default::default()); - &*self - } - - /// Increments the maximum view index for the given `spend` index. - #[inline] - fn increment_view(&mut self, spend: SpendIndex) -> Option<&Self> { - self.view.get_mut(spend.index() as usize)?.increment(); - Some(&*self) - } -} - -impl Default for MaxIndex { - #[inline] - fn default() -> Self { - Self { - view: vec![Default::default()], - } - } -} - /// Account Keys #[derive(derivative::Derivative)] #[derivative( + Clone(bound = ""), Copy(bound = ""), Debug(bound = "H: Debug"), Eq(bound = "H: Eq"), @@ -432,8 +274,8 @@ where /// Account Index account: AccountIndex, - /// Maximum Indices - max_index: &'h MaxIndex, + /// Maximum Index + max_index: KeyIndex, } impl<'h, H> AccountKeys<'h, H> @@ -442,7 +284,7 @@ where { /// Builds a new [`AccountKeys`] from `keys`, `account`, and `max_index`. #[inline] - fn new(keys: &'h H, account: AccountIndex, max_index: &'h MaxIndex) -> Self { + fn new(keys: &'h H, account: AccountIndex, max_index: KeyIndex) -> Self { Self { keys, account, @@ -450,177 +292,99 @@ where } } - /// Performs the bounds check on `spend` and then runs `f`. + /// Performs the bounds check on `index` and then runs `f`. #[inline] - fn with_spend_bounds_check<T, F>(&self, spend: SpendIndex, f: F) -> Result<T, Error<H>> + fn with_bounds_check<T, F>(&self, index: KeyIndex, f: F) -> Option<T> where - F: FnOnce(&Self, SpendIndex) -> Result<T, H::Error>, + F: FnOnce(&Self, KeyIndex) -> T, { - if spend <= self.max_index.spend() { - f(self, spend).map_err(Error::KeyDerivationError) - } else { - Err(Error::ExceedingCurrentMaximumSpendIndex) - } + (index <= self.max_index).then(|| f(self, index)) } - /// Performs the bounds check on `view` and then runs `f`. + /// Derives the spend key for this account at `index` without performing bounds checks. #[inline] - fn with_view_bounds_check<T, F>( - &self, - spend: SpendIndex, - view: ViewIndex, - f: F, - ) -> Result<T, Error<H>> - where - F: FnOnce(&Self, SpendIndex, ViewIndex) -> Result<T, H::Error>, - { - if spend <= self.max_index.spend() { - if view <= self.max_index.view[spend.index() as usize] { - f(self, spend, view).map_err(Error::KeyDerivationError) - } else { - Err(Error::ExceedingCurrentMaximumViewIndex) - } - } else { - Err(Error::ExceedingCurrentMaximumSpendIndex) - } - } - - /// Derives the spend key for this account at `spend` without performing bounds checks. - #[inline] - fn derive_spend(&self, spend: SpendIndex) -> Result<H::SecretKey, H::Error> { - self.keys.derive_spend(self.account, spend) + fn derive_spend(&self, index: KeyIndex) -> H::SecretKey { + self.keys.derive_spend(self.account, index) } /// Returns the default spend key for this account. #[inline] - pub fn default_spend_key(&self) -> Result<H::SecretKey, H::Error> { + pub fn default_spend_key(&self) -> H::SecretKey { self.derive_spend(Default::default()) } - /// Returns the spend key for this account at the `spend` index, if it does not exceed the - /// maximum index. + /// Returns the spend key for this account at `index`, if it does not exceed the maximum index. #[inline] - pub fn spend_key(&self, spend: SpendIndex) -> Result<H::SecretKey, Error<H>> { - self.with_spend_bounds_check(spend, Self::derive_spend) + pub fn spend_key(&self, index: KeyIndex) -> Option<H::SecretKey> { + self.with_bounds_check(index, Self::derive_spend) } - /// Derives the view key for this account at the `spend` and `view` indices without performing - /// bounds checks. + /// Derives the view key for this account at `index` without performing bounds checks. #[inline] - fn derive_view(&self, spend: SpendIndex, view: ViewIndex) -> Result<H::SecretKey, H::Error> { - self.keys.derive_view(self.account, spend, view) + fn derive_view(&self, index: KeyIndex) -> H::SecretKey { + self.keys.derive_view(self.account, index) } /// Returns the default view key for this account. #[inline] - pub fn default_view_key(&self) -> Result<H::SecretKey, H::Error> { - self.derive_view(Default::default(), Default::default()) + pub fn default_view_key(&self) -> H::SecretKey { + self.derive_view(Default::default()) } /// Returns the view key for this account at `index`, if it does not exceed the maximum index. #[inline] - pub fn view_key(&self, index: Index) -> Result<H::SecretKey, Error<H>> { - self.view_key_with(index.spend, index.view) - } - - /// Returns the view key for this account at the `spend` and `view` indices, if it does not - /// exceed the maximum index. - #[inline] - pub fn view_key_with( - &self, - spend: SpendIndex, - view: ViewIndex, - ) -> Result<H::SecretKey, Error<H>> { - self.with_view_bounds_check(spend, view, Self::derive_view) + pub fn view_key(&self, index: KeyIndex) -> Option<H::SecretKey> { + self.with_bounds_check(index, Self::derive_view) } - /// Derives the secret key pair for this account at `spend` and `view` without performing bounds - /// checks. + /// Derives the secret key pair for this account at `index` without performing bounds checks. #[inline] - fn derive_pair( - &self, - spend: SpendIndex, - view: ViewIndex, - ) -> Result<SecretKeyPair<H>, H::Error> { - self.keys.derive_pair(self.account, spend, view) + fn derive_pair(&self, index: KeyIndex) -> SecretKeyPair<H> { + self.keys.derive_pair(self.account, index) } /// Returns the default secret key pair for this account. #[inline] - pub fn default_keypair(&self) -> Result<SecretKeyPair<H>, H::Error> { - self.derive_pair(Default::default(), Default::default()) - } - - /// Returns the key pair for this account at `index`, if it does not exceed the maximum index. - #[inline] - pub fn keypair(&self, index: Index) -> Result<SecretKeyPair<H>, Error<H>> { - self.keypair_with(index.spend, index.view) + pub fn default_keypair(&self) -> SecretKeyPair<H> { + self.derive_pair(Default::default()) } /// Returns the key pair for this account at the `spend` and `view` indices, if those indices /// do not exceed the maximum indices. #[inline] - pub fn keypair_with( - &self, - spend: SpendIndex, - view: ViewIndex, - ) -> Result<SecretKeyPair<H>, Error<H>> { - self.with_view_bounds_check(spend, view, Self::derive_pair) + pub fn keypair(&self, index: KeyIndex) -> Option<SecretKeyPair<H>> { + self.with_bounds_check(index, Self::derive_pair) } /// Applies `f` to the view keys generated by `self` returning the first non-`None` result with /// it's key index and key attached, or returns an error if the key derivation failed. #[inline] - pub fn find_index<T, F>(&self, mut f: F) -> Result<Option<ViewKeySelection<H, T>>, H::Error> + pub fn find_index<T, F>(&self, mut f: F) -> Option<ViewKeySelection<H, T>> where F: FnMut(&H::SecretKey) -> Option<T>, { - let mut index = Index::default(); + let mut index = KeyIndex::default(); loop { - loop { - match self.view_key(index) { - Ok(view_key) => { - if let Some(item) = f(&view_key) { - return Ok(Some(ViewKeySelection { - index, - keypair: SecretKeyPair::new( - self.derive_spend(index.spend)?, - view_key, - ), - item, - })); - } - } - Err(Error::ExceedingCurrentMaximumViewIndex) => break, - Err(Error::ExceedingCurrentMaximumSpendIndex) => return Ok(None), - Err(Error::KeyDerivationError(err)) => return Err(err), - } - index.view.increment(); + let view_key = self.view_key(index)?; + if let Some(item) = f(&view_key) { + return Some(ViewKeySelection { + index, + keypair: SecretKeyPair::new(self.derive_spend(index), view_key), + item, + }); } - index.spend.increment(); - index.view.reset(); + index.increment(); } } } -// NOTE: We need this because `derivative::Derivative` doesn't derive this trait properly. -impl<'h, H> Clone for AccountKeys<'h, H> -where - H: HierarchicalKeyDerivationScheme + ?Sized, -{ - #[inline] - fn clone(&self) -> Self { - Self::new(self.keys, self.account, self.max_index) - } -} - /// View Key Selection pub struct ViewKeySelection<H, T> where H: HierarchicalKeyDerivationScheme + ?Sized, { /// Selection Index - pub index: Index, + pub index: KeyIndex, /// Selection Key Pair pub keypair: SecretKeyPair<H>, @@ -631,26 +395,22 @@ where /// Account Map Trait pub trait AccountMap { - /// Builds a new [`AccountMap`] with a starting account with default max indices. + /// Builds a new [`AccountMap`] with a starting account with the default maximum index. fn new() -> Self; - /// Returns the maximum spend and view indices for `account`, if it exists. - fn max_index(&self, account: AccountIndex) -> Option<&MaxIndex>; + /// Returns the maximum key index for `account`, if it exists. + fn max_index(&self, account: AccountIndex) -> Option<KeyIndex>; - /// Adds a new account to the map, returning the new account parameter. + /// Adds a new account to the map, returning the new account index. fn create_account(&mut self) -> AccountIndex; - /// Increments the maximum spend index for `account`, if it exists, returning the current - /// maximum indices. - fn increment_spend(&mut self, account: AccountIndex) -> Option<&MaxIndex>; - - /// Increments the maximum view index for `account`, if it exists, returning the current - /// maximum indices. - fn increment_view(&mut self, account: AccountIndex, spend: SpendIndex) -> Option<&MaxIndex>; + /// Increments the maximum key index for `account`, if it exists, returning the new maximum + /// index. + fn increment_index(&mut self, account: AccountIndex) -> Option<KeyIndex>; } /// [`Vec`] Account Map Type -pub type VecAccountMap = Vec<MaxIndex>; +pub type VecAccountMap = Vec<KeyIndex>; impl AccountMap for VecAccountMap { #[inline] @@ -661,8 +421,8 @@ impl AccountMap for VecAccountMap { } #[inline] - fn max_index(&self, account: AccountIndex) -> Option<&MaxIndex> { - self.get(account.index() as usize) + fn max_index(&self, account: AccountIndex) -> Option<KeyIndex> { + self.get(account.index() as usize).copied() } #[inline] @@ -677,15 +437,11 @@ impl AccountMap for VecAccountMap { } #[inline] - fn increment_spend(&mut self, account: AccountIndex) -> Option<&MaxIndex> { - self.get_mut(account.index() as usize) - .map(MaxIndex::increment_spend) - } - - #[inline] - fn increment_view(&mut self, account: AccountIndex, spend: SpendIndex) -> Option<&MaxIndex> { - self.get_mut(account.index() as usize) - .and_then(move |index| index.increment_view(spend)) + fn increment_index(&mut self, account: AccountIndex) -> Option<KeyIndex> { + self.get_mut(account.index() as usize).map(|k| { + k.increment(); + *k + }) } } @@ -728,27 +484,15 @@ where Self { keys, accounts } } - /// Returns the key associated to `account` if it exists, using `index` if it does not exceed - /// the maximum index. + /// Returns the secret key pair associated to `account` if it exists, using `index` if it does + /// not exceed the maximum key index. #[inline] pub fn keypair( &self, account: AccountIndex, - index: Index, - ) -> Option<Result<SecretKeyPair<H>, Error<H>>> { - self.keypair_with(account, index.spend, index.view) - } - - /// Returns the key associated to `account` if it exists, using the `spend` and `view` indices - /// if they do not exceed the maximum indices. - #[inline] - pub fn keypair_with( - &self, - account: AccountIndex, - spend: SpendIndex, - view: ViewIndex, - ) -> Option<Result<SecretKeyPair<H>, Error<H>>> { - self.get(account).map(move |k| k.keypair_with(spend, view)) + index: KeyIndex, + ) -> Option<Option<SecretKeyPair<H>>> { + self.get(account).map(move |k| k.keypair(index)) } /// Returns the account keys for `account` if it exists. @@ -767,9 +511,9 @@ where self.get(Default::default()).unwrap() } - /// Returns the maximum spend and view indices for `account`, if it exists. + /// Returns the maximum key index for `account`, if it exists. #[inline] - pub fn max_index(&self, account: AccountIndex) -> Option<&MaxIndex> { + pub fn max_index(&self, account: AccountIndex) -> Option<KeyIndex> { self.accounts.max_index(account) } @@ -779,38 +523,25 @@ where self.accounts.create_account() } - /// Increments the maximum spend index for `account`, if it exists, returning the current - /// maximum indices. - #[inline] - pub fn increment_spend(&mut self, account: AccountIndex) -> Option<&MaxIndex> { - self.accounts.increment_spend(account) - } - - /// Increments the maximum view index for `account`, if it exists, returning the current - /// maximum indices. + /// Increments the maximum key index for `account`, if it exists, returning the current + /// maximum index. #[inline] - pub fn increment_view( - &mut self, - account: AccountIndex, - spend: SpendIndex, - ) -> Option<&MaxIndex> { - self.accounts.increment_view(account, spend) + pub fn increment_index(&mut self, account: AccountIndex) -> Option<KeyIndex> { + self.accounts.increment_index(account) } - /// Increments the spend index and returns the [`Index`] pair for the new spend index and its - /// first view key. + /// Increments the spend index and returns the [`KeyIndex`] for the new index. #[inline] - pub fn next_index(&mut self, account: AccountIndex) -> Option<Index> { - let max_index = self.increment_spend(account)?; - Some(Index::new(max_index.spend(), Default::default())) + pub fn next_index(&mut self, account: AccountIndex) -> Option<KeyIndex> { + let max_index = self.increment_index(account)?; + Some(max_index) } - /// Increments the spend index and returns the [`SecretKeyPair`] for the new spend index and - /// its first view key. + /// Increments the spend index and returns the [`SecretKeyPair`] for the new index. #[inline] - pub fn next(&mut self, account: AccountIndex) -> Option<Result<SecretKeyPair<H>, H::Error>> { + pub fn next(&mut self, account: AccountIndex) -> Option<SecretKeyPair<H>> { let index = self.next_index(account)?; - Some(self.keys.derive_pair(account, index.spend, index.view)) + Some(self.keys.derive_pair(account, index)) } } diff --git a/manta-accounting/src/wallet/signer/base.rs b/manta-accounting/src/wallet/signer.rs similarity index 86% rename from manta-accounting/src/wallet/signer/base.rs rename to manta-accounting/src/wallet/signer.rs index abed5063e..406510a5b 100644 --- a/manta-accounting/src/wallet/signer/base.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -35,7 +35,7 @@ use crate::{ asset::{Asset, AssetId, AssetMap, AssetValue}, - key::{self, HierarchicalKeyDerivationScheme, SpendIndex, ViewKeySelection}, + key::{self, HierarchicalKeyDerivationScheme, KeyIndex, ViewKeySelection}, transfer::{ self, batch::Join, @@ -58,8 +58,9 @@ use manta_crypto::{ rand::{CryptoRng, Rand, RngCore}, }; use manta_util::{ + array_map, cache::{CachedResource, CachedResourceError}, - fallible_array_map, into_array_unchecked, + into_array_unchecked, iter::IteratorExt, persistance::Rollback, }; @@ -70,14 +71,23 @@ where C: transfer::Configuration, { /// Error Type + /// + /// This is the error type for the connection itself, not for an error produced during one of + /// the signer methods. type Error; /// Pushes updates from the ledger to the wallet, synchronizing it with the ledger state and /// returning an updated asset distribution. - fn sync(&mut self, request: SyncRequest<C>) -> Result<SyncResponse, Self::Error>; + fn sync( + &mut self, + request: SyncRequest<C>, + ) -> Result<Result<SyncResponse, SyncError>, Self::Error>; /// Signs a `transaction` and returns the ledger transfer posts if successful. - fn sign(&mut self, transaction: Transaction<C>) -> Result<SignResponse<C>, Self::Error>; + fn sign( + &mut self, + transaction: Transaction<C>, + ) -> Result<Result<SignResponse<C>, SignError<C>>, Self::Error>; /// Returns a new [`ReceivingKey`] for `self` to receive assets. fn receiving_key(&mut self) -> Result<ReceivingKey<C>, Self::Error>; @@ -127,6 +137,19 @@ pub enum SyncResponse { }, } +/// Signer Synchronization Error +/// +/// This `enum` is the error state for the [`sync`](Connection::sync) method on [`Connection`]. +/// See its documentation for more. +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub enum SyncError { + /// Inconsistent Synchronization + InconsistentSynchronization { + /// Desired starting index to fix synchronization + starting_index: usize, + }, +} + /// Signer Signing Response /// /// This `struct` is created by the [`sign`](Connection::sign) method on [`Connection`]. @@ -150,6 +173,24 @@ where } } +/// Signer Signing Error +/// +/// This `enum` is the error state for the [`sign`](Connection::sign) method on [`Connection`]. +/// See its documentation for more. +pub enum SignError<C> +where + C: transfer::Configuration, +{ + /// Proving Context Cache Error + ProvingContextCacheError, + + /// Insufficient Balance + InsufficientBalance(Asset), + + /// Proof System Error + ProofSystemError(ProofSystemError<C>), +} + /// Signer Configuration pub trait Configuration: transfer::Configuration { /// Hierarchical Key Derivation Scheme @@ -178,59 +219,12 @@ pub trait Configuration: transfer::Configuration { pub type AccountTable<C> = key::AccountTable<<C as Configuration>::HierarchicalKeyDerivationScheme>; /// Asset Map Key Type -pub type AssetMapKey<C> = (SpendIndex, PublicKey<C>); +pub type AssetMapKey<C> = (KeyIndex, PublicKey<C>); /// Proving Context Cache Error Type pub type ProvingContextCacheError<C> = CachedResourceError<MultiProvingContext<C>, <C as Configuration>::ProvingContextCache>; -/// Signer Error -#[derive(derivative::Derivative)] -#[derivative(Debug(bound = r" - key::Error<C::HierarchicalKeyDerivationScheme>: Debug, - ProvingContextCacheError<C>: Debug, - ProofSystemError<C>: Debug, - CE: Debug -"))] -pub enum Error<C, CE = Infallible> -where - C: Configuration, -{ - /// Hierarchical Key Derivation Scheme Error - HierarchicalKeyDerivationSchemeError(key::Error<C::HierarchicalKeyDerivationScheme>), - - /// Proving Context Cache Error - ProvingContextCacheError(ProvingContextCacheError<C>), - - /// Missing [`Utxo`] Membership Proof - MissingUtxoMembershipProof, - - /// Insufficient Balance - InsufficientBalance(Asset), - - /// Inconsistent Synchronization - InconsistentSynchronization { - /// Desired starting index to fix synchronization - starting_index: usize, - }, - - /// Proof System Error - ProofSystemError(ProofSystemError<C>), - - /// Signer Connection Error - ConnectionError(CE), -} - -impl<C, CE> From<key::Error<C::HierarchicalKeyDerivationScheme>> for Error<C, CE> -where - C: Configuration, -{ - #[inline] - fn from(err: key::Error<C::HierarchicalKeyDerivationScheme>) -> Self { - Self::HierarchicalKeyDerivationSchemeError(err) - } -} - /// Signer Parameters pub struct SignerParameters<C> where @@ -295,7 +289,7 @@ where encrypted_note: EncryptedNote<C>, void_numbers: &mut Vec<VoidNumber<C>>, deposit: &mut Vec<Asset>, - ) -> Result<(), Error<C>> { + ) -> Result<(), SyncError> { let mut finder = DecryptedMessage::find(encrypted_note); if let Some(ViewKeySelection { index, @@ -309,7 +303,6 @@ where .accounts .get_default() .find_index(|k| finder.decrypt(&parameters.key_agreement, k)) - .map_err(key::Error::KeyDerivationError)? { if let Some(void_number) = C::check_full_asset( parameters, @@ -322,8 +315,7 @@ where void_numbers.remove(index); } else { self.utxo_set.insert(&utxo); - self.assets - .insert((index.spend, ephemeral_public_key), asset); + self.assets.insert((index, ephemeral_public_key), asset); if !asset.is_zero() { deposit.push(asset); } @@ -375,7 +367,7 @@ where inserts: I, mut void_numbers: Vec<VoidNumber<C>>, is_partial: bool, - ) -> Result<SyncResponse, Error<C>> + ) -> Result<SyncResponse, SyncError> where I: Iterator<Item = (Utxo<C>, EncryptedNote<C>)>, { @@ -393,7 +385,7 @@ where self.assets.retain(|(index, ephemeral_public_key), assets| { assets.retain( |asset| match self.accounts.get_default().spend_key(*index) { - Ok(secret_key) => Self::is_asset_unspent( + Some(secret_key) => Self::is_asset_unspent( parameters, &secret_key, ephemeral_public_key, @@ -426,11 +418,14 @@ where parameters: &Parameters<C>, key: AssetMapKey<C>, asset: Asset, - ) -> Result<PreSender<C>, Error<C>> { + ) -> Result<PreSender<C>, SignError<C>> { let (spend_index, ephemeral_key) = key; Ok(PreSender::new( parameters, - self.accounts.get_default().spend_key(spend_index)?, + self.accounts + .get_default() + .spend_key(spend_index) + .expect("Index is guaranteed to be within bounds."), ephemeral_key, asset, )) @@ -442,12 +437,8 @@ where &mut self, parameters: &Parameters<C>, asset: Asset, - ) -> Result<Receiver<C>, Error<C>> { - let keypair = self - .accounts - .get_default() - .default_keypair() - .map_err(key::Error::KeyDerivationError)?; + ) -> Result<Receiver<C>, SignError<C>> { + let keypair = self.accounts.get_default().default_keypair(); Ok(SpendingKey::new(keypair.spend, keypair.view).receiver( parameters, self.rng.gen(), @@ -461,13 +452,9 @@ where &mut self, parameters: &Parameters<C>, asset_id: AssetId, - ) -> Result<(Mint<C>, PreSender<C>), Error<C>> { + ) -> Result<(Mint<C>, PreSender<C>), SignError<C>> { let asset = Asset::zero(asset_id); - let keypair = self - .accounts - .get_default() - .default_keypair() - .map_err(key::Error::KeyDerivationError)?; + let keypair = self.accounts.get_default().default_keypair(); Ok(Mint::internal_pair( parameters, &SpendingKey::new(keypair.spend, keypair.view), @@ -482,10 +469,10 @@ where &mut self, parameters: &Parameters<C>, asset: Asset, - ) -> Result<Selection<C>, Error<C>> { + ) -> Result<Selection<C>, SignError<C>> { let selection = self.assets.select(asset); if !asset.is_zero() && selection.is_empty() { - return Err(Error::InsufficientBalance(asset)); + return Err(SignError::InsufficientBalance(asset)); } Selection::new(selection, move |k, v| { self.build_pre_sender(parameters, k, asset.id.with(v)) @@ -504,10 +491,10 @@ where proving_context: &ProvingContext<C>, transfer: Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS>, rng: &mut C::Rng, - ) -> Result<TransferPost<C>, Error<C>> { + ) -> Result<TransferPost<C>, SignError<C>> { transfer .into_post(parameters, proving_context, rng) - .map_err(Error::ProofSystemError) + .map_err(SignError::ProofSystemError) } /// Mints an asset with zero value for the given `asset_id`, returning the appropriate @@ -518,7 +505,7 @@ where parameters: &Parameters<C>, proving_context: &ProvingContext<C>, mint: Mint<C>, - ) -> Result<TransferPost<C>, Error<C>> { + ) -> Result<TransferPost<C>, SignError<C>> { Self::build_post( FullParameters::new(parameters, self.utxo_set.model()), proving_context, @@ -534,7 +521,7 @@ where parameters: &Parameters<C>, proving_context: &ProvingContext<C>, private_transfer: PrivateTransfer<C>, - ) -> Result<TransferPost<C>, Error<C>> { + ) -> Result<TransferPost<C>, SignError<C>> { Self::build_post( FullParameters::new(parameters, self.utxo_set.model()), proving_context, @@ -550,7 +537,7 @@ where parameters: &Parameters<C>, proving_context: &ProvingContext<C>, reclaim: Reclaim<C>, - ) -> Result<TransferPost<C>, Error<C>> { + ) -> Result<TransferPost<C>, SignError<C>> { Self::build_post( FullParameters::new(parameters, self.utxo_set.model()), proving_context, @@ -567,12 +554,8 @@ where parameters: &Parameters<C>, asset_id: AssetId, total: AssetValue, - ) -> Result<([Receiver<C>; PrivateTransferShape::RECEIVERS], Join<C>), Error<C>> { - let keypair = self - .accounts - .get_default() - .default_keypair() - .map_err(key::Error::KeyDerivationError)?; + ) -> Result<([Receiver<C>; PrivateTransferShape::RECEIVERS], Join<C>), SignError<C>> { + let keypair = self.accounts.get_default().default_keypair(); Ok(Join::new( parameters, asset_id.with(total), @@ -591,7 +574,7 @@ where mut new_zeroes: Vec<PreSender<C>>, pre_senders: &mut Vec<PreSender<C>>, posts: &mut Vec<TransferPost<C>>, - ) -> Result<(), Error<C>> { + ) -> Result<(), SignError<C>> { let mut needed_zeroes = PrivateTransferShape::SENDERS - pre_senders.len(); if needed_zeroes == 0 { return Ok(()); @@ -633,7 +616,7 @@ where asset_id: AssetId, mut pre_senders: Vec<PreSender<C>>, posts: &mut Vec<TransferPost<C>>, - ) -> Result<[Sender<C>; PrivateTransferShape::SENDERS], Error<C>> { + ) -> Result<[Sender<C>; PrivateTransferShape::SENDERS], SignError<C>> { let mut new_zeroes = Vec::new(); while pre_senders.len() > PrivateTransferShape::SENDERS { let mut joins = Vec::new(); @@ -641,10 +624,10 @@ where .into_iter() .chunk_by::<{ PrivateTransferShape::SENDERS }>(); for chunk in &mut iter { - let senders = fallible_array_map(chunk, |s| { + let senders = array_map(chunk, |s| { s.try_upgrade(&self.utxo_set) - .ok_or(Error::MissingUtxoMembershipProof) - })?; + .expect("Unable to upgrade expected UTXO.") + }); let (receivers, mut join) = self.next_join( parameters, asset_id, @@ -675,7 +658,7 @@ where .into_iter() .map(move |s| s.try_upgrade(&self.utxo_set)) .collect::<Option<Vec<_>>>() - .ok_or(Error::MissingUtxoMembershipProof)?, + .expect("Unable to upgrade expected UTXOs."), )) } @@ -762,7 +745,7 @@ where starting_index: usize, inserts: I, removes: R, - ) -> Result<SyncResponse, Error<C>> + ) -> Result<SyncResponse, SyncError> where I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, R: IntoIterator<Item = VoidNumber<C>>, @@ -785,7 +768,7 @@ where self.state.utxo_set.commit(); result } - _ => Err(Error::InconsistentSynchronization { + _ => Err(SyncError::InconsistentSynchronization { starting_index: utxo_set_len, }), } @@ -797,7 +780,7 @@ where &mut self, asset: Asset, receiver: Option<ReceivingKey<C>>, - ) -> Result<SignResponse<C>, Error<C>> { + ) -> Result<SignResponse<C>, SignError<C>> { let selection = self.state.select(&self.parameters.parameters, asset)?; let change = self .state @@ -805,7 +788,7 @@ where let (parameters, proving_context) = self .parameters .get() - .map_err(Error::ProvingContextCacheError)?; + .map_err(|_| SignError::ProvingContextCacheError)?; let mut posts = Vec::new(); let senders = self.state.compute_batched_transactions( parameters, @@ -835,7 +818,10 @@ where /// Signs the `transaction`, generating transfer posts without releasing resources. #[inline] - fn sign_internal(&mut self, transaction: Transaction<C>) -> Result<SignResponse<C>, Error<C>> { + fn sign_internal( + &mut self, + transaction: Transaction<C>, + ) -> Result<SignResponse<C>, SignError<C>> { match transaction { Transaction::Mint(asset) => { let receiver = self @@ -844,7 +830,7 @@ where let (parameters, proving_context) = self .parameters .get() - .map_err(Error::ProvingContextCacheError)?; + .map_err(|_| SignError::ProvingContextCacheError)?; Ok(SignResponse::new(vec![self.state.mint_post( parameters, &proving_context.mint, @@ -860,7 +846,7 @@ where /// Signs the `transaction`, generating transfer posts. #[inline] - pub fn sign(&mut self, transaction: Transaction<C>) -> Result<SignResponse<C>, Error<C>> { + pub fn sign(&mut self, transaction: Transaction<C>) -> Result<SignResponse<C>, SignError<C>> { // TODO: Should we do a time-based release mechanism to amortize the cost of reading // from the proving context cache? let result = self.sign_internal(transaction); @@ -871,15 +857,10 @@ where /// Returns a new [`ReceivingKey`] for `self` to receive assets. #[inline] - pub fn receiving_key(&mut self) -> Result<ReceivingKey<C>, Error<C>> { - let keypair = self - .state - .accounts - .next(Default::default()) - .unwrap() - .map_err(key::Error::KeyDerivationError)?; - Ok(SpendingKey::new(keypair.spend, keypair.view) - .derive(&self.parameters.parameters.key_agreement)) + pub fn receiving_key(&mut self) -> ReceivingKey<C> { + let keypair = self.state.accounts.next(Default::default()).unwrap(); + SpendingKey::new(keypair.spend, keypair.view) + .derive(&self.parameters.parameters.key_agreement) } } @@ -887,20 +868,26 @@ impl<C> Connection<C> for Signer<C> where C: Configuration, { - type Error = Error<C>; + type Error = Infallible; #[inline] - fn sync(&mut self, request: SyncRequest<C>) -> Result<SyncResponse, Self::Error> { - self.sync(request.starting_index, request.inserts, request.removes) + fn sync( + &mut self, + request: SyncRequest<C>, + ) -> Result<Result<SyncResponse, SyncError>, Self::Error> { + Ok(self.sync(request.starting_index, request.inserts, request.removes)) } #[inline] - fn sign(&mut self, transaction: Transaction<C>) -> Result<SignResponse<C>, Self::Error> { - self.sign(transaction) + fn sign( + &mut self, + transaction: Transaction<C>, + ) -> Result<Result<SignResponse<C>, SignError<C>>, Self::Error> { + Ok(self.sign(transaction)) } #[inline] fn receiving_key(&mut self) -> Result<ReceivingKey<C>, Self::Error> { - self.receiving_key() + Ok(self.receiving_key()) } } diff --git a/manta-accounting/src/wallet/signer/client.rs b/manta-accounting/src/wallet/signer/client.rs deleted file mode 100644 index 572dd5f6b..000000000 --- a/manta-accounting/src/wallet/signer/client.rs +++ /dev/null @@ -1,166 +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 <http://www.gnu.org/licenses/>. - -//! Signer Client Abstraction - -use crate::{ - transfer::{canonical::Transaction, Configuration, ReceivingKey}, - wallet::signer::{self, SignResponse, SyncRequest, SyncResponse}, -}; -use core::marker::PhantomData; -use manta_util::message::{Channel, ChannelError, Input, Output, ParsingReadError, UniformChannel}; - -/// Parsing Error -pub enum ParsingError<SY, SI, RE> { - /// Sync Response Error - SyncResponse(SY), - - /// Sign Response Error - SignResponse(SI), - - /// Receiving Key Response Error - ReceivingKey(RE), -} - -/// Error -pub enum Error<E, W, R, SY, SI, RE> { - /// Signer Error - Signer(E), - - /// Channel Error - Channel(ChannelError<W, R>), - - /// Parsing Error - Parse(ParsingError<SY, SI, RE>), -} - -impl<E, W, R, SY, SI, RE> Error<E, W, R, SY, SI, RE> { - /// - #[inline] - pub fn convert_sync<T>( - result: Result<Result<T, E>, ChannelError<W, ParsingReadError<R, SY>>>, - ) -> Result<T, Self> { - match result { - Ok(Ok(value)) => Ok(value), - Ok(Err(err)) => Err(Self::Signer(err)), - Err(ChannelError::Write(err)) => Err(Self::Channel(ChannelError::Write(err))), - Err(ChannelError::Read(ParsingReadError::Read(err))) => { - Err(Self::Channel(ChannelError::Read(err))) - } - Err(ChannelError::Read(ParsingReadError::Parse(err))) => { - Err(Self::Parse(ParsingError::SyncResponse(err))) - } - } - } - - /// - #[inline] - pub fn convert_sign<T>( - result: Result<Result<T, E>, ChannelError<W, ParsingReadError<R, SI>>>, - ) -> Result<T, Self> { - match result { - Ok(Ok(value)) => Ok(value), - Ok(Err(err)) => Err(Self::Signer(err)), - Err(ChannelError::Write(err)) => Err(Self::Channel(ChannelError::Write(err))), - Err(ChannelError::Read(ParsingReadError::Read(err))) => { - Err(Self::Channel(ChannelError::Read(err))) - } - Err(ChannelError::Read(ParsingReadError::Parse(err))) => { - Err(Self::Parse(ParsingError::SignResponse(err))) - } - } - } - - /// - #[inline] - pub fn convert_receiving_key<T>( - result: Result<Result<T, E>, ChannelError<W, ParsingReadError<R, RE>>>, - ) -> Result<T, Self> { - match result { - Ok(Ok(value)) => Ok(value), - Ok(Err(err)) => Err(Self::Signer(err)), - Err(ChannelError::Write(err)) => Err(Self::Channel(ChannelError::Write(err))), - Err(ChannelError::Read(ParsingReadError::Read(err))) => { - Err(Self::Channel(ChannelError::Read(err))) - } - Err(ChannelError::Read(ParsingReadError::Parse(err))) => { - Err(Self::Parse(ParsingError::ReceivingKey(err))) - } - } - } -} - -/// Client Connection -pub struct Client<C, E, H> -where - C: Configuration, -{ - /// Communication Channel - channel: H, - - /// Type Parameter Marker - __: PhantomData<(C, E)>, -} - -impl<C, E, H> Client<C, E, H> -where - C: Configuration, -{ - /// Builds a new [`Client`] from `channel`. - #[inline] - pub fn new(channel: H) -> Self { - Self { - channel, - __: PhantomData, - } - } -} - -impl<C, E, H> signer::Connection<C> for Client<C, E, H> -where - C: signer::Configuration, - H: UniformChannel - + Input<SyncRequest<C>> - + Input<Transaction<C>> - + Input<()> - + Output<Result<SyncResponse, E>> - + Output<Result<SignResponse<C>, E>> - + Output<Result<ReceivingKey<C>, E>>, -{ - type Error = Error< - E, - <H as UniformChannel>::WriteError, - <H as UniformChannel>::ReadError, - <H as Output<Result<SyncResponse, E>>>::Error, - <H as Output<Result<SignResponse<C>, E>>>::Error, - <H as Output<Result<ReceivingKey<C>, E>>>::Error, - >; - - #[inline] - fn sync(&mut self, request: SyncRequest<C>) -> Result<SyncResponse, Self::Error> { - Error::convert_sync(self.channel.request(request)) - } - - #[inline] - fn sign(&mut self, transaction: Transaction<C>) -> Result<SignResponse<C>, Self::Error> { - Error::convert_sign(self.channel.request(transaction)) - } - - #[inline] - fn receiving_key(&mut self) -> Result<ReceivingKey<C>, Self::Error> { - Error::convert_receiving_key(self.channel.request(())) - } -} diff --git a/manta-accounting/src/wallet/signer/server.rs b/manta-accounting/src/wallet/signer/server.rs deleted file mode 100644 index a44b87170..000000000 --- a/manta-accounting/src/wallet/signer/server.rs +++ /dev/null @@ -1,109 +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 <http://www.gnu.org/licenses/>. - -//! Signer Server Abstraction - -use crate::{ - transfer::{canonical::Transaction, ReceivingKey}, - wallet::signer::{Configuration, Error, SignResponse, Signer, SyncRequest, SyncResponse}, -}; -use manta_util::message::{Channel, ChannelError}; - -/// Signer Server -pub struct Server<C, H> -where - C: Configuration, -{ - /// Base Signer - base: Signer<C>, - - /// Communication Channel - channel: Option<H>, -} - -impl<C, H> Server<C, H> -where - C: Configuration, -{ - /// Builds a new [`Server`] from a given `base` signer. - #[inline] - pub fn new(base: Signer<C>) -> Self { - Self { - base, - channel: None, - } - } - - /// Connects `self` along the given `channel`. - #[inline] - pub fn connect(&mut self, channel: H) { - self.channel = Some(channel); - } - - /// Disconnects `self` from the internal channel. - #[inline] - pub fn disconnect(&mut self) { - self.channel = None; - } - - /// Runs the `sync` command on the [`Signer`]. - #[inline] - pub fn sync(&mut self) -> Result<bool, ChannelError<H::WriteError, H::ReadError>> - where - H: Channel<Result<SyncResponse, Error<C>>, SyncRequest<C>>, - { - if let Some(channel) = self.channel.as_mut() { - channel - .listen(|_, request| { - self.base - .sync(request.starting_index, request.inserts, request.removes) - }) - .map(move |_| true) - } else { - Ok(false) - } - } - - /// Runs the `sign` command on the [`Signer`]. - #[inline] - pub fn sign(&mut self) -> Result<bool, ChannelError<H::WriteError, H::ReadError>> - where - H: Channel<Result<SignResponse<C>, Error<C>>, Transaction<C>>, - { - if let Some(channel) = self.channel.as_mut() { - channel - .listen(|_, transaction| self.base.sign(transaction)) - .map(move |_| true) - } else { - Ok(false) - } - } - - /// Runs the `receiving_key` command on the [`Signer`]. - #[inline] - pub fn receiving_key(&mut self) -> Result<bool, ChannelError<H::WriteError, H::ReadError>> - where - H: Channel<Result<ReceivingKey<C>, Error<C>>, ()>, - { - if let Some(channel) = self.channel.as_mut() { - channel - .listen(|_, _| self.base.receiving_key()) - .map(move |_| true) - } else { - Ok(false) - } - } -} diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 2a2145567..87c0bd057 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -24,7 +24,7 @@ use crate::{ }, wallet::{ ledger::{self, Checkpoint, PullResponse, PushResponse}, - signer::{self, SignResponse, SyncRequest, SyncResponse}, + signer::{self, SignError, SignResponse, SyncError, SyncRequest, SyncResponse}, }, }; use alloc::collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}; @@ -266,14 +266,6 @@ where /// Pulls data from the `ledger`, synchronizing the wallet and balance state. #[inline] pub fn sync(&mut self) -> Result<(), Error<C, L, S>> { - // FIXME: What should be done when we receive an `InconsistentSynchronization` error from - // the signer? - // - One option is to do some sort of (exponential) backoff algorithm to find the - // point at which the signer and the wallet are able to synchronize again. The - // correct algorithm may be simply to exchange some checkpoints between the - // signer and the wallet until they can agree on a minimal one. - // - In the worst case we would have to recover the entire wallet. - // let PullResponse { checkpoint, receivers, @@ -294,14 +286,27 @@ where }) .map_err(Error::SignerError)? { - SyncResponse::Partial { deposit, withdraw } => { + Ok(SyncResponse::Partial { deposit, withdraw }) => { self.assets.deposit_all(deposit); self.assets.withdraw_all_unchecked(withdraw); } - SyncResponse::Full { assets } => { + Ok(SyncResponse::Full { assets }) => { self.assets.clear(); self.assets.deposit_all(assets); } + Err(SyncError::InconsistentSynchronization { starting_index }) => { + // FIXME: What should be done when we receive an `InconsistentSynchronization` error + // from the signer? + // - One option is to do some sort of (exponential) backoff algorithm to + // find the point at which the signer and the wallet are able to + // synchronize again. The correct algorithm may be simply to exchange + // some checkpoints between the signer and the wallet until they can + // agree on a minimal one. + // - In the worst case we would have to recover the entire wallet. + // + let _ = starting_index; + todo!() + } } self.checkpoint = checkpoint; Ok(()) @@ -341,9 +346,25 @@ where self.sync()?; self.check(&transaction) .map_err(Error::InsufficientBalance)?; - let SignResponse { posts } = self.signer.sign(transaction).map_err(Error::SignerError)?; - let PushResponse { success } = self.ledger.push(posts).map_err(Error::LedgerError)?; - Ok(success) + match self.signer.sign(transaction).map_err(Error::SignerError)? { + Ok(SignResponse { posts }) => { + let PushResponse { success } = + self.ledger.push(posts).map_err(Error::LedgerError)?; + Ok(success) + } + Err(SignError::ProvingContextCacheError) => { + // TODO: This kind of error should not bubble up to the wallet level. + todo!() + } + Err(SignError::InsufficientBalance(asset)) => { + // FIXME: If we reach this point, the wallet and signer are not synchronized. + todo!() + } + Err(SignError::ProofSystemError(err)) => { + // TODO: This kind of error should not bubble up to the wallet level. + todo!() + } + } } /// Returns a new [`ReceivingKey`] for `self` to receive assets. @@ -371,9 +392,9 @@ where /// Inconsistent Checkpoint Error InconsistentCheckpoint, - /// Ledger Error + /// Ledger Connection Error LedgerError(L::Error), - /// Signer Error + /// Signer Connection Error SignerError(S::Error), } diff --git a/manta-crypto/src/ecc.rs b/manta-crypto/src/ecc.rs index c5c105a7d..28d030d8f 100644 --- a/manta-crypto/src/ecc.rs +++ b/manta-crypto/src/ecc.rs @@ -105,9 +105,9 @@ where } } -impl<G, COM, CODEC> Decode<CODEC> for DiffieHellman<G, COM> +impl<G, COM> Decode for DiffieHellman<G, COM> where - G: Group<COM> + Decode<CODEC>, + G: Group<COM> + Decode, { type Error = G::Error; @@ -120,9 +120,9 @@ where } } -impl<G, COM, CODEC> Encode<CODEC> for DiffieHellman<G, COM> +impl<G, COM> Encode for DiffieHellman<G, COM> where - G: Group<COM> + Encode<CODEC>, + G: Group<COM> + Encode, { #[inline] fn encode<W>(&self, writer: W) -> Result<(), W::Error> diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index 1c3f90966..9f6c4db0b 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -649,16 +649,16 @@ pub enum ParameterDecodeError<L, I> { Inner(I), } -impl<C, COM, CODEC> Decode<CODEC> for Parameters<C, COM> +impl<C, COM> Decode for Parameters<C, COM> where C: HashConfiguration<COM> + ?Sized, - LeafHashParameters<C, COM>: Decode<CODEC>, - InnerHashParameters<C, COM>: Decode<CODEC>, + LeafHashParameters<C, COM>: Decode, + InnerHashParameters<C, COM>: Decode, { #[allow(clippy::type_complexity)] // NOTE: This is an implementation type so it doesn't matter. type Error = ParameterDecodeError< - <LeafHashParameters<C, COM> as Decode<CODEC>>::Error, - <InnerHashParameters<C, COM> as Decode<CODEC>>::Error, + <LeafHashParameters<C, COM> as Decode>::Error, + <InnerHashParameters<C, COM> as Decode>::Error, >; #[inline] @@ -679,11 +679,11 @@ where } } -impl<C, COM, CODEC> Encode<CODEC> for Parameters<C, COM> +impl<C, COM> Encode for Parameters<C, COM> where C: HashConfiguration<COM> + ?Sized, - LeafHashParameters<C, COM>: Encode<CODEC>, - InnerHashParameters<C, COM>: Encode<CODEC>, + LeafHashParameters<C, COM>: Encode, + InnerHashParameters<C, COM>: Encode, { #[inline] fn encode<W>(&self, mut writer: W) -> Result<(), W::Error> diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 562e347e5..2f6c95259 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -49,8 +49,8 @@ arkworks = [ # Enable Groth16 ZKP System groth16 = ["arkworks", "ark-groth16"] -# Enable PLONK ZKP System -plonk = ["zk-garage-plonk"] +# TODO: Enable PLONK ZKP System +# TODO: plonk = ["zk-garage-plonk"] # Testing Frameworks test = [ @@ -95,7 +95,7 @@ rayon = { version = "1.5.1", optional = true, default-features = false } scale-codec = { package = "parity-scale-codec", version = "2.3.1", default-features = false, features = ["derive"] } statrs = { version = "0.15.0", optional = true, default-features = false } tungstenite = { version = "0.16.0", optional = true, default-features = false, features = ["native-tls"] } -zk-garage-plonk = { package = "plonk", git = "https://github.com/zk-garage/plonk", optional = true, default-features = false } +# TODO: zk-garage-plonk = { package = "plonk", git = "https://github.com/zk-garage/plonk", optional = true, default-features = false } [dev-dependencies] criterion = "0.3.5" diff --git a/manta-pay/src/crypto/hash/poseidon.rs b/manta-pay/src/crypto/hash/poseidon.rs index c1f1791cc..eb810d35b 100644 --- a/manta-pay/src/crypto/hash/poseidon.rs +++ b/manta-pay/src/crypto/hash/poseidon.rs @@ -259,12 +259,12 @@ where } } -impl<S, COM, CODEC, const ARITY: usize> Decode<CODEC> for Hash<S, COM, ARITY> +impl<S, COM, const ARITY: usize> Decode for Hash<S, COM, ARITY> where S: Specification<COM>, - S::Field: Decode<CODEC>, + S::Field: Decode, { - type Error = <S::Field as Decode<CODEC>>::Error; + type Error = <S::Field as Decode>::Error; #[inline] fn decode<R>(mut reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> @@ -282,10 +282,10 @@ where } } -impl<S, COM, CODEC, const ARITY: usize> Encode<CODEC> for Hash<S, COM, ARITY> +impl<S, COM, const ARITY: usize> Encode for Hash<S, COM, ARITY> where S: Specification<COM>, - S::Field: Encode<CODEC>, + S::Field: Encode, { #[inline] fn encode<W>(&self, mut writer: W) -> Result<(), W::Error> diff --git a/manta-pay/src/key.rs b/manta-pay/src/key.rs index c80225214..99cf5bfd1 100644 --- a/manta-pay/src/key.rs +++ b/manta-pay/src/key.rs @@ -27,13 +27,11 @@ use alloc::{format, string::String}; use bip32::{Seed, XPrv}; use core::marker::PhantomData; -use manta_accounting::key::{ - self, AccountIndex, HierarchicalKeyDerivationScheme, SpendIndex, ViewIndex, -}; +use manta_accounting::key::{self, AccountIndex, HierarchicalKeyDerivationScheme, KeyIndex, Kind}; use manta_crypto::rand::{CryptoRng, RngCore, Sample, Standard}; use manta_util::{create_seal, seal}; -pub use bip32::{Error, Language, Mnemonic}; +pub use bip32::{Language, Mnemonic}; create_seal! {} @@ -179,7 +177,7 @@ where /// [`BIP-0044`]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki #[inline] #[must_use] -pub fn path_string<C>(account: AccountIndex, spend: SpendIndex, view: Option<ViewIndex>) -> String +pub fn path_string<C>(account: AccountIndex, kind: Kind, index: KeyIndex) -> String where C: CoinType, { @@ -189,8 +187,8 @@ where BIP_44_PURPOSE_ID, C::COIN_TYPE_ID, account.index(), - spend.index(), - view.map(move |v| v.index() + 1).unwrap_or_default(), + kind as u8, + index.index(), ) } @@ -199,15 +197,15 @@ where C: CoinType, { type SecretKey = XPrv; - type Error = Error; #[inline] - fn derive( - &self, - account: AccountIndex, - spend: SpendIndex, - view: Option<ViewIndex>, - ) -> Result<Self::SecretKey, Self::Error> { - XPrv::derive_from_path(&self.seed, &path_string::<C>(account, spend, view).parse()?) + fn derive(&self, account: AccountIndex, kind: Kind, index: KeyIndex) -> Self::SecretKey { + XPrv::derive_from_path( + &self.seed, + &path_string::<C>(account, kind, index) + .parse() + .expect("Path string is valid by construction."), + ) + .expect("Unable to generate secret key for valid seed and path string.") } } diff --git a/manta-pay/src/test/ledger.rs b/manta-pay/src/test/ledger.rs index 5097fee06..c60cc4e63 100644 --- a/manta-pay/src/test/ledger.rs +++ b/manta-pay/src/test/ledger.rs @@ -456,7 +456,7 @@ mod test { use super::*; use crate::{ config::FullParameters, - wallet::{self, cache::OnDiskMultiProvingContext, SignerBase}, + wallet::{self, cache::OnDiskMultiProvingContext, Signer}, }; use manta_accounting::{ asset::{Asset, AssetList}, @@ -497,13 +497,13 @@ mod test { parameters: &transfer::Parameters<Config>, utxo_set_model: &UtxoSetModel, rng: &mut R, - ) -> Wallet<Config, LedgerConnection, SignerBase> + ) -> Wallet<Config, LedgerConnection, Signer> where R: CryptoRng + RngCore + ?Sized, { Wallet::empty( LedgerConnection::new(account, ledger.clone()), - SignerBase::new( + Signer::new( AccountTable::new(rng.gen()), cache.clone(), parameters.clone(), diff --git a/manta-pay/src/wallet/mod.rs b/manta-pay/src/wallet/mod.rs index 1fdc5688d..9c8f873bc 100644 --- a/manta-pay/src/wallet/mod.rs +++ b/manta-pay/src/wallet/mod.rs @@ -28,15 +28,12 @@ use ark_ff::PrimeField; use manta_accounting::{ asset::HashAssetMap, key::{self, HierarchicalKeyDerivationScheme}, - wallet::{self, signer::AssetMapKey}, + wallet::signer::AssetMapKey, }; use manta_crypto::{key::KeyDerivationFunction, merkle_tree}; use manta_util::pointer::ThreadSafe; pub mod cache; - -#[cfg(feature = "tungstenite")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "tungstenite")))] pub mod signer; /// Hierarchical Key Derivation Function @@ -79,14 +76,4 @@ impl manta_accounting::wallet::signer::Configuration for Config { } /// Signer Base Type -pub type SignerBase = manta_accounting::wallet::signer::Signer<Config>; - -/// Signer Server -#[cfg(feature = "tungstenite")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "tungstenite")))] -pub type Signer = signer::Server; - -/// Wallet -#[cfg(feature = "tungstenite")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "tungstenite")))] -pub type Wallet<L> = wallet::Wallet<Config, L, signer::Client>; +pub type Signer = manta_accounting::wallet::signer::Signer<Config>; diff --git a/manta-pay/src/wallet/signer.rs b/manta-pay/src/wallet/signer.rs deleted file mode 100644 index d32a697c5..000000000 --- a/manta-pay/src/wallet/signer.rs +++ /dev/null @@ -1,134 +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 <http://www.gnu.org/licenses/>. - -//! Signer Client and Server - -// TODO: Make this generic over the `Config`. - -use crate::{config::Config, wallet::SignerBase}; -use manta_accounting::{ - transfer::{canonical::Transaction, EncryptedNote, ReceivingKey, Utxo, VoidNumber}, - wallet::signer::{self, client::SyncRequest, SignResponse, SyncResponse}, -}; -use manta_util::message::{ChannelError, ParsingError, UniformChannel}; -use std::net::TcpStream; -use tungstenite::{ - client::IntoClientRequest, - handshake::server::{NoCallback, ServerHandshake}, - stream::MaybeTlsStream, - Message, -}; - -/// Stream Type -pub type Stream = MaybeTlsStream<TcpStream>; - -/// Error Type -pub type Error = tungstenite::error::Error; - -/// Handshake Error Type -pub type HandshakeError = - tungstenite::handshake::HandshakeError<ServerHandshake<Stream, NoCallback>>; - -/// -pub struct WebSocket(tungstenite::WebSocket<Stream>); - -impl UniformChannel for WebSocket { - type Message = Message; - type ReadError = Error; - type WriteError = Error; - - #[inline] - fn read(&mut self) -> Result<Self::Message, Self::ReadError> { - self.0.read_message() - } - - #[inline] - fn write(&mut self, message: Self::Message) -> Result<(), Self::WriteError> { - self.0.write_message(message) - } -} - -/// -pub type Client = signer::client::Client<Config, signer::Error<Config>, WebSocket>; - -/* -/// Client -pub struct Client { - /// Underlying Connection - connection: WebSocket, -} - -impl Client { - /// Builds a new [`Client`] from Web Socket `url`. - #[inline] - pub fn new<U>(url: U) -> Result<Self, Error> - where - U: IntoClientRequest, - { - Ok(Self { - connection: WebSocket(tungstenite::connect(url)?.0), - }) - } -} -*/ - -/// Signer Server -pub struct Server { - /// Signer Base - base: SignerBase, - - /// Underlying Connection - connection: Option<WebSocket>, -} - -impl Server { - /// Builds a new [`Server`] from `base`. - #[inline] - pub fn new(base: SignerBase) -> Self { - Self { - base, - connection: None, - } - } - - /// - #[inline] - pub fn connect(&mut self, stream: Stream) -> Result<(), HandshakeError> { - self.connection = Some(WebSocket(tungstenite::accept(stream)?)); - Ok(()) - } - - /* - /// - #[inline] - pub fn sync(&mut self, request: SyncRequest) -> SyncResult<Config, SignerBase> { - self.base - .sync(request.starting_index, request.inserts, request.removes) - } - - /// - #[inline] - pub fn sign(&mut self, transaction: Transaction<Config>) -> SignResult<Config, SignerBase> { - self.base.sign(transaction) - } - - /// - #[inline] - pub fn receiving_key(&mut self) -> ReceivingKeyResult<Config, SignerBase> { - self.base.receiving_key() - } - */ -} diff --git a/manta-pay/src/wallet/signer/client/http.rs b/manta-pay/src/wallet/signer/client/http.rs new file mode 100644 index 000000000..ef9fe3171 --- /dev/null +++ b/manta-pay/src/wallet/signer/client/http.rs @@ -0,0 +1,17 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Signer HTTP Client Implementation diff --git a/manta-pay/src/wallet/signer/client/mod.rs b/manta-pay/src/wallet/signer/client/mod.rs new file mode 100644 index 000000000..dcf69e952 --- /dev/null +++ b/manta-pay/src/wallet/signer/client/mod.rs @@ -0,0 +1,23 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Signer Client Implementations + +pub mod http; + +#[cfg(feature = "tungstenite")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "tungstenite")))] +pub mod websocket; diff --git a/manta-pay/src/wallet/signer/client/websocket.rs b/manta-pay/src/wallet/signer/client/websocket.rs new file mode 100644 index 000000000..a783565a0 --- /dev/null +++ b/manta-pay/src/wallet/signer/client/websocket.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 <http://www.gnu.org/licenses/>. + +//! Signer WebSocket Client Implementation + +use crate::config::Config; +use manta_accounting::{ + transfer::{canonical::Transaction, ReceivingKey}, + wallet::signer::{self, SignError, SignResponse, SyncError, SyncRequest, SyncResponse}, +}; +use manta_util::codec::Encode; +use std::net::TcpStream; +use tungstenite::{ + client::IntoClientRequest, + handshake::server::{NoCallback, ServerHandshake}, + stream::MaybeTlsStream, + Message, +}; + +/// Stream Type +pub type Stream = MaybeTlsStream<TcpStream>; + +/// Error Type +pub type Error = tungstenite::error::Error; + +/// Handshake Error Type +pub type HandshakeError = + tungstenite::handshake::HandshakeError<ServerHandshake<Stream, NoCallback>>; + +/// WebSocket Client +pub struct Client(tungstenite::WebSocket<Stream>); + +impl Client { + /// Builds a new [`Client`] from `url`. + #[inline] + pub fn new<U>(url: U) -> Result<Self, Error> + where + U: IntoClientRequest, + { + Ok(Self(tungstenite::connect(url)?.0)) + } +} + +impl signer::Connection<Config> for Client { + type Error = (); + + #[inline] + fn sync( + &mut self, + request: SyncRequest<Config>, + ) -> Result<Result<SyncResponse, SyncError>, Self::Error> { + todo!() + } + + #[inline] + fn sign( + &mut self, + transaction: Transaction<Config>, + ) -> Result<Result<SignResponse<Config>, SignError<Config>>, Self::Error> { + todo!() + } + + #[inline] + fn receiving_key(&mut self) -> Result<ReceivingKey<Config>, Self::Error> { + todo!() + } +} diff --git a/manta-accounting/src/wallet/signer/mod.rs b/manta-pay/src/wallet/signer/mod.rs similarity index 91% rename from manta-accounting/src/wallet/signer/mod.rs rename to manta-pay/src/wallet/signer/mod.rs index cb6edccbe..5349ac719 100644 --- a/manta-accounting/src/wallet/signer/mod.rs +++ b/manta-pay/src/wallet/signer/mod.rs @@ -14,11 +14,6 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Signer Abstractions - -mod base; +//! Signer Client and Server Implementations pub mod client; -pub mod server; - -pub use base::*; diff --git a/manta-util/Cargo.toml b/manta-util/Cargo.toml index a9429d8c1..25e27bcf2 100644 --- a/manta-util/Cargo.toml +++ b/manta-util/Cargo.toml @@ -31,5 +31,3 @@ alloc = [] # Standard Library std = ["alloc"] -[dependencies] -derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } diff --git a/manta-util/src/codec.rs b/manta-util/src/codec.rs index 7448b7a1f..1e79874ca 100644 --- a/manta-util/src/codec.rs +++ b/manta-util/src/codec.rs @@ -19,7 +19,7 @@ // TODO: Add `ReadFrom` and `WriteInto` traits for conversion between different serde/codec impls // which are specialized so that you can automatically convert between a type and itself. -use core::{convert::Infallible, fmt::Debug, hash::Hash}; +use core::{convert::Infallible, fmt::Debug, hash::Hash, marker::PhantomData}; #[cfg(feature = "alloc")] use alloc::vec::Vec; @@ -37,10 +37,24 @@ pub trait Read { /// Reads bytes from `self`, pushing them to `output` until exhausting the buffer inside of /// `output`. - fn read_exact<T>(&mut self, output: &mut T) -> Result<(), ReadExactError<Self>> + fn read_exact<T>(&mut self, output: &mut T) -> Result<(), ReadExactError<Self::Error>> where T: AsMut<[u8]> + ?Sized; + /// Reads exactly one byte from `self`. + /// + /// # Implementation Note + /// + /// This method is an optimization path for [`read_exact`](Self::read_exact) when the desired + /// output sequence is exactly one byte. In some cases, there may be an implemention of this + /// method which is more efficient than calling [`read_exact`](Self::read_exact) directly. + #[inline] + fn read_byte(&mut self) -> Result<u8, ReadExactError<Self::Error>> { + let mut byte = [0; 1]; + self.read_exact(&mut byte)?; + Ok(byte[0]) + } + /// Creates a “by mutable reference” adaptor for this instance of [`Read`]. #[inline] fn by_ref(&mut self) -> &mut Self { @@ -72,11 +86,16 @@ where } #[inline] - fn read_exact<T>(&mut self, output: &mut T) -> Result<(), ReadExactError<Self>> + fn read_exact<T>(&mut self, output: &mut T) -> Result<(), ReadExactError<Self::Error>> where T: AsMut<[u8]> + ?Sized, { - (*self).read_exact(output).map_err(ReadExactError::map_same) + (*self).read_exact(output) + } + + #[inline] + fn read_byte(&mut self) -> Result<u8, ReadExactError<Self::Error>> { + (*self).read_byte() } } @@ -108,7 +127,7 @@ impl Read for &[u8] { } #[inline] - fn read_exact<T>(&mut self, output: &mut T) -> Result<(), ReadExactError<Self>> + fn read_exact<T>(&mut self, output: &mut T) -> Result<(), ReadExactError<Self::Error>> where T: AsMut<[u8]> + ?Sized, { @@ -148,7 +167,7 @@ impl<const N: usize> Read for [u8; N] { } #[inline] - fn read_exact<T>(&mut self, output: &mut T) -> Result<(), ReadExactError<Self>> + fn read_exact<T>(&mut self, output: &mut T) -> Result<(), ReadExactError<Self::Error>> where T: AsMut<[u8]> + ?Sized, { @@ -190,12 +209,12 @@ impl Read for Vec<u8> { } #[inline] - fn read_exact<T>(&mut self, output: &mut T) -> Result<(), ReadExactError<Self>> + fn read_exact<T>(&mut self, output: &mut T) -> Result<(), ReadExactError<Self::Error>> where T: AsMut<[u8]> + ?Sized, { let mut slice = self.as_slice(); - slice.read_exact(output).map_err(ReadExactError::map_same)?; + slice.read_exact(output)?; let len = slice.len(); self.drain(..(self.len() - len)); Ok(()) @@ -239,7 +258,7 @@ where } #[inline] - fn read_exact<T>(&mut self, output: &mut T) -> Result<(), ReadExactError<Self>> + fn read_exact<T>(&mut self, output: &mut T) -> Result<(), ReadExactError<Self::Error>> where T: AsMut<[u8]> + ?Sized, { @@ -267,19 +286,8 @@ where /// /// This `enum` is the error state for the [`read_exact`](Read::read_exact) method of [`Read`]. /// See its documentation for more. -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "R::Error: Clone"), - Copy(bound = "R::Error: Copy"), - Debug(bound = "R::Error: Debug"), - Eq(bound = "R::Error: Eq"), - Hash(bound = "R::Error: Hash"), - PartialEq(bound = "R::Error: PartialEq") -)] -pub enum ReadExactError<R> -where - R: Read + ?Sized, -{ +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum ReadExactError<R> { /// Unexpected End of Reader /// /// The reader finished producing bytes before the output buffer was filled. The amount @@ -287,35 +295,7 @@ where UnexpectedEnd(usize), /// Reading Error - Read(R::Error), -} - -impl<R> ReadExactError<R> -where - R: Read + ?Sized, -{ - /// Maps `self` along `f` for the [`Self::Read`] variant into another [`ReadExactError`]. - #[inline] - pub fn map<S, F>(self, f: F) -> ReadExactError<S> - where - S: Read + ?Sized, - F: FnOnce(R::Error) -> S::Error, - { - match self { - Self::UnexpectedEnd(remaining) => ReadExactError::UnexpectedEnd(remaining), - Self::Read(err) => ReadExactError::Read(f(err)), - } - } - - /// Maps `self` along `f` for the [`Self::Read`] variant into another [`ReadExactError`] - /// using the same error value. - #[inline] - pub fn map_same<S>(self) -> ReadExactError<S> - where - S: Read<Error = R::Error> + ?Sized, - { - self.map(core::convert::identity) - } + Read(R), } /// Writer @@ -482,7 +462,7 @@ where /// Reads bytes from `self`, pushing them to `output` until exhausting the buffer inside of /// `output`. #[inline] - pub fn read_exact<T>(mut self, output: &mut T) -> Result<Self, ReadExactError<R>> + pub fn read_exact<T>(mut self, output: &mut T) -> Result<Self, ReadExactError<R::Error>> where T: AsMut<[u8]> + ?Sized, { @@ -545,7 +525,7 @@ where } /// Encoding -pub trait Encode<C = ()> { +pub trait Encode { /// Appends representation of `self` in bytes to `buffer`. fn encode<W>(&self, writer: W) -> Result<(), W::Error> where @@ -563,7 +543,18 @@ pub trait Encode<C = ()> { } } -impl<C> Encode<C> for () { +impl Encode for () { + #[inline] + fn encode<W>(&self, writer: W) -> Result<(), W::Error> + where + W: Write, + { + let _ = writer; + Ok(()) + } +} + +impl<T> Encode for PhantomData<T> { #[inline] fn encode<W>(&self, writer: W) -> Result<(), W::Error> where @@ -585,9 +576,9 @@ impl Encode for u8 { } } -impl<T, C> Encode<C> for [T] +impl<T> Encode for [T] where - T: Encode<C>, + T: Encode, { #[inline] fn encode<W>(&self, mut writer: W) -> Result<(), W::Error> @@ -601,9 +592,9 @@ where } } -impl<T, C, const N: usize> Encode<C> for [T; N] +impl<T, const N: usize> Encode for [T; N] where - T: Encode<C>, + T: Encode, { #[inline] fn encode<W>(&self, mut writer: W) -> Result<(), W::Error> @@ -617,8 +608,50 @@ where } } +impl<T> Encode for Option<T> +where + T: Encode, +{ + #[inline] + fn encode<W>(&self, mut writer: W) -> Result<(), W::Error> + where + W: Write, + { + match self { + Some(value) => { + 1u8.encode(&mut writer)?; + value.encode(&mut writer) + } + _ => 0u8.encode(&mut writer), + } + } +} + +impl<T, E> Encode for Result<T, E> +where + T: Encode, + E: Encode, +{ + #[inline] + fn encode<W>(&self, mut writer: W) -> Result<(), W::Error> + where + W: Write, + { + match self { + Ok(value) => { + 1u8.encode(&mut writer)?; + value.encode(&mut writer) + } + Err(err) => { + 0u8.encode(&mut writer)?; + err.encode(&mut writer) + } + } + } +} + /// Exact Size Encoding -pub trait EncodeExactSize<C, const N: usize>: Encode<C> { +pub trait EncodeExactSize<const N: usize>: Encode { /// Converts `self` into an exactly known byte array. #[inline] fn to_array(&self) -> [u8; N] { @@ -630,7 +663,7 @@ pub trait EncodeExactSize<C, const N: usize>: Encode<C> { } /// Decoding -pub trait Decode<C = ()>: Sized { +pub trait Decode: Sized { /// Error Type type Error; @@ -649,7 +682,7 @@ pub trait Decode<C = ()>: Sized { } } -impl<C> Decode<C> for () { +impl Decode for () { type Error = Infallible; #[inline] @@ -662,8 +695,115 @@ impl<C> Decode<C> for () { } } +impl<T> Decode for PhantomData<T> { + type Error = Infallible; + + #[inline] + fn decode<R>(reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> + where + R: Read, + { + let _ = reader; + Ok(PhantomData) + } +} + +impl Decode for u8 { + type Error = (); + + #[inline] + fn decode<R>(mut reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> + where + R: Read, + { + match reader.read_byte() { + Ok(byte) => Ok(byte), + Err(ReadExactError::Read(err)) => Err(DecodeError::Read(err)), + _ => Err(DecodeError::Decode(())), + } + } +} + +/// Option [`Decode`] Error +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum OptionDecodeError<T> { + /// Missing Byte + MissingByte, + + /// Invalid Byte + InvalidByte(u8), + + /// `Some` Variant Error + SomeError(T), +} + +impl<T> Decode for Option<T> +where + T: Decode, +{ + type Error = OptionDecodeError<T::Error>; + + #[inline] + fn decode<R>(mut reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> + where + R: Read, + { + match u8::decode(&mut reader) + .map_err(move |err| err.map_decode(move |_| OptionDecodeError::MissingByte))? + { + 0 => Ok(Some(T::decode(&mut reader).map_err(move |err| { + err.map_decode(OptionDecodeError::SomeError) + })?)), + 1 => Ok(None), + b => Err(DecodeError::Decode(OptionDecodeError::InvalidByte(b))), + } + } +} + +/// Result [`Decode`] Error +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum ResultDecodeError<T, E> { + /// Missing Byte + MissingByte, + + /// Invalid Byte + InvalidByte(u8), + + /// `Ok` Variant Error + OkError(T), + + /// `Some` Variant Error + ErrError(E), +} + +impl<T, E> Decode for Result<T, E> +where + T: Decode, + E: Decode, +{ + type Error = ResultDecodeError<T::Error, E::Error>; + + #[inline] + fn decode<R>(mut reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> + where + R: Read, + { + match u8::decode(&mut reader) + .map_err(move |err| err.map_decode(move |_| ResultDecodeError::MissingByte))? + { + 0 => Ok(Ok(T::decode(&mut reader).map_err(move |err| { + err.map_decode(ResultDecodeError::OkError) + })?)), + 1 => Ok(Err(E::decode(&mut reader).map_err(move |err| { + err.map_decode(ResultDecodeError::ErrError) + })?)), + b => Err(DecodeError::Decode(ResultDecodeError::InvalidByte(b))), + } + } +} + /// Exact Size Decoding -pub trait DecodeExactSize<C, const N: usize>: Decode<C> { +pub trait DecodeExactSize<const N: usize>: Decode { /// Converts a fixed-length byte array into a concrete value of type `Self`. #[inline] fn from_array(buffer: [u8; N]) -> Self { @@ -673,7 +813,7 @@ pub trait DecodeExactSize<C, const N: usize>: Decode<C> { } } -impl<C> DecodeExactSize<C, 0> for () { +impl DecodeExactSize<0> for () { #[inline] fn from_array(buffer: [u8; 0]) -> Self { let _ = buffer; @@ -712,4 +852,28 @@ impl<R, D> DecodeError<R, D> { _ => None, } } + + /// Maps the [`Read`](Self::Read) variant over `f`. + #[inline] + pub fn map_read<T, F>(self, f: F) -> DecodeError<T, D> + where + F: FnOnce(R) -> T, + { + match self { + Self::Read(err) => DecodeError::Read(f(err)), + Self::Decode(err) => DecodeError::Decode(err), + } + } + + /// Maps the [`Decode`](Self::Decode) variant over `f`. + #[inline] + pub fn map_decode<T, F>(self, f: F) -> DecodeError<R, T> + where + F: FnOnce(D) -> T, + { + match self { + Self::Read(err) => DecodeError::Read(err), + Self::Decode(err) => DecodeError::Decode(f(err)), + } + } } diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index f0c5e90b5..0c0ba0cbe 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -31,7 +31,6 @@ pub mod cache; pub mod codec; pub mod convert; pub mod iter; -pub mod message; pub mod persistance; pub mod pointer; diff --git a/manta-util/src/message.rs b/manta-util/src/message.rs deleted file mode 100644 index bdd95a78b..000000000 --- a/manta-util/src/message.rs +++ /dev/null @@ -1,193 +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 <http://www.gnu.org/licenses/>. - -//! Message-Passing Utilities - -/// Message-Passing Channel -pub trait Channel<W, R = W> { - /// Write Error Type - type WriteError; - - /// Read Error Type - type ReadError; - - /// Writes a `message` of type `W` to the channel, or a [`WriteError`](Self::WriteError) if the - /// write failed. - fn write(&mut self, message: W) -> Result<(), Self::WriteError>; - - /// Reads a message of type `R` from the channel, or a [`ReadError`](Self::ReadError) if the - /// read failed. - fn read(&mut self) -> Result<R, Self::ReadError>; - - /// Sends a `request` of type `W` on the channel using [`write`](Self::write) waiting for a - /// response of type `R` on the channel using [`read`](Self::read). - #[inline] - fn request( - &mut self, - request: W, - ) -> Result<R, ChannelError<Self::WriteError, Self::ReadError>> { - self.write(request).map_err(ChannelError::Write)?; - self.read().map_err(ChannelError::Read) - } - - /// Listens on the channel for a request of type `R` using [`read`](Self::read), processes the - /// request using `process` and sends the response message back along the channel using - /// [`write`](Self::write). - #[inline] - fn listen<F>( - &mut self, - process: F, - ) -> Result<(), ChannelError<Self::WriteError, Self::ReadError>> - where - F: FnOnce(&mut Self, R) -> W, - { - let request = self.read().map_err(ChannelError::Read)?; - let response = process(self, request); - self.write(response).map_err(ChannelError::Write) - } -} - -/// Channel Error -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum ChannelError<W, R> { - /// Write Error - Write(W), - - /// Read Error - Read(R), -} - -impl<W, R> ChannelError<W, R> { - /// Converts `self` into an `Option` over [`W`](Channel::WriteError). - #[inline] - pub fn write(self) -> Option<W> { - match self { - Self::Write(err) => Some(err), - _ => None, - } - } - - /// Converts `self` into an `Option` over [`R`](Channel::ReadError). - #[inline] - pub fn read(self) -> Option<R> { - match self { - Self::Read(err) => Some(err), - _ => None, - } - } -} - -impl<P, E> ChannelError<E, ParsingReadError<E, P>> { - /// Unwraps the inner error. - #[inline] - pub fn into_parsing_error(self) -> ParsingError<E, P> { - match self { - Self::Write(err) => ParsingError::Error(err), - Self::Read(err) => match err { - ParsingReadError::Read(err) => ParsingError::Error(err), - ParsingReadError::Parse(err) => ParsingError::Parse(err), - }, - } - } -} - -impl<E> ChannelError<E, E> { - /// Unwraps the inner error. - #[inline] - pub fn into_inner(self) -> E { - match self { - Self::Write(err) => err, - Self::Read(err) => err, - } - } -} - -/// Parsing Error -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum ParsingError<E, P> { - /// Base Error - Error(E), - - /// Parse Error - Parse(P), -} - -/// Uniform Channel Input -pub trait Input<T>: UniformChannel { - /// Converts `t` into a message that can be sent in the [`UniformChannel`]. - fn convert(t: T) -> Self::Message; -} - -/// Uniform Channel Output -pub trait Output<T>: UniformChannel { - /// Parsing Error - type Error; - - /// Parses an incoming message that was just output from the [`UniformChannel`] and tries to - /// convert it into `T`. - fn parse(message: Self::Message) -> Result<T, Self::Error>; -} - -/// Uniform Message-Passing Channel -pub trait UniformChannel { - /// Message Type - type Message; - - /// Write Error Type - type WriteError; - - /// Read Error Type - type ReadError; - - /// Writes a `message` to the channel, or a [`WriteError`](Self::WriteError) if the write - /// failed. - fn write(&mut self, message: Self::Message) -> Result<(), Self::WriteError>; - - /// Reads a message from the channel, or a [`ReadError`](Self::ReadError) if the read failed. - fn read(&mut self) -> Result<Self::Message, Self::ReadError>; -} - -impl<C, W, R> Channel<W, R> for C -where - C: UniformChannel + Input<W> + Output<R>, -{ - type WriteError = C::WriteError; - type ReadError = ParsingReadError<C::ReadError, <C as Output<R>>::Error>; - - #[inline] - fn write(&mut self, message: W) -> Result<(), Self::WriteError> { - self.write(Self::convert(message)) - } - - #[inline] - fn read(&mut self) -> Result<R, Self::ReadError> { - Self::parse(self.read().map_err(ParsingReadError::Read)?).map_err(ParsingReadError::Parse) - } -} - -/// Parsing Read Error -/// -/// This `enum` is the error state for the [`Channel`] implementation of [`UniformChannel`] which -/// can either be a reading error or a parsing error when transforming a message from the uniform -/// format to the specific read type of the [`Channel`]. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum ParsingReadError<R, P> { - /// Read Error - Read(R), - - /// Parse Error - Parse(P), -} From 0def7a0896cfd32361ce032dbee43e45b960fda2 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 3 Feb 2022 15:55:51 -0500 Subject: [PATCH 216/275] feat: start moving to serde-based encoding system --- manta-accounting/Cargo.toml | 21 +- manta-accounting/src/asset.rs | 31 ++- manta-accounting/src/fs.rs | 303 ++++++++++++++------- manta-accounting/src/key.rs | 38 ++- manta-accounting/src/transfer/mod.rs | 85 ++++++ manta-accounting/src/wallet/signer.rs | 95 ++++++- manta-crypto/Cargo.toml | 2 + manta-crypto/src/constraint.rs | 1 + manta-crypto/src/ecc.rs | 1 + manta-crypto/src/encryption.rs | 33 +++ manta-crypto/src/key.rs | 2 + manta-crypto/src/merkle_tree/forest.rs | 36 ++- manta-crypto/src/merkle_tree/full.rs | 14 + manta-crypto/src/merkle_tree/inner_tree.rs | 46 ++++ manta-crypto/src/merkle_tree/node.rs | 18 ++ manta-crypto/src/merkle_tree/partial.rs | 14 + manta-crypto/src/merkle_tree/path.rs | 47 ++++ manta-crypto/src/merkle_tree/tree.rs | 58 +++- manta-crypto/src/rand.rs | 33 +++ manta-pay/Cargo.toml | 2 +- manta-util/src/codec.rs | 125 +++++++++ 21 files changed, 866 insertions(+), 139 deletions(-) diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index a026ddde9..bdeb9b181 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -25,33 +25,38 @@ is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } [features] -# Cocoon Filesystem Adapters -cocoon-fs = ["std", "zeroize"] +# Cocoon Filesystem Adapter +cocoon-fs = [ + "cocoon/std", + "manta-crypto/getrandom", + "rand_chacha", + "std", +] # Parallel Execution parallel = ["crossbeam/std", "rayon"] # Standard Library -std = [ - "cocoon/std", - "manta-crypto/std", - "manta-util/std", -] +std = ["manta-crypto/std", "manta-util/std"] # Testing Frameworks test = ["indexmap", "parking_lot", "rand/alloc", "statrs"] [dependencies] +argonautica = { version = "0.2.0", optional = true, default-features = false } +bitflags = { version = "1.3.2", optional = true, default-features = false } cocoon = { version = "0.3.0", optional = true, default-features = false } crossbeam = { version = "0.8.1", optional = true, default-features = false } derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } -derive_more = { version = "0.99.16", default-features = false, features = ["add", "add_assign", "display", "from", "mul", "mul_assign", "sum"] } +derive_more = { version = "0.99.16", default-features = false, features = ["add", "add_assign", "display", "from", "sum"] } indexmap = { version = "1.8.0", optional = true, default-features = false } manta-crypto = { path = "../manta-crypto", default-features = false } manta-util = { path = "../manta-util", features = ["alloc"] } parking_lot = { version = "0.11.2", optional = true, default-features = false } rand = { version = "0.8.4", optional = true, default-features = false } +rand_chacha = { version = "0.3.1", optional = true, default-features = false } rayon = { version = "1.5.1", optional = true, default-features = false } +serde = { version = "1.0.136", optional = true, default-features = false, features = ["alloc", "derive"] } statrs = { version = "0.15.0", optional = true, default-features = false } zeroize = { version = "1.4.3", optional = true, default-features = false, features = ["alloc"] } diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index cc5c80150..5c24c95aa 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -29,9 +29,7 @@ use core::{ ops::{Add, AddAssign, Deref, Sub, SubAssign}, slice, }; -use derive_more::{ - Add, AddAssign, Display, Div, DivAssign, From, Mul, MulAssign, Sub, SubAssign, Sum, -}; +use derive_more::{Add, AddAssign, Display, From, Sub, SubAssign, Sum}; use manta_crypto::{ constraint::{Allocator, Secret, ValueSource, Variable}, rand::{CryptoRng, Rand, RngCore, Sample, Standard}, @@ -44,10 +42,18 @@ use std::{ hash::BuildHasher, }; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + /// [`AssetId`] Base Type pub type AssetIdType = u32; /// Asset Id Type +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Default, Display, Eq, From, Hash, Ord, PartialEq, PartialOrd)] #[from(forward)] pub struct AssetId( @@ -118,6 +124,11 @@ where pub type AssetValueType = u128; /// Asset Value Type +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(deny_unknown_fields) +)] #[derive( Add, AddAssign, @@ -126,13 +137,9 @@ pub type AssetValueType = u128; Debug, Default, Display, - Div, - DivAssign, Eq, From, Hash, - Mul, - MulAssign, Ord, PartialEq, PartialOrd, @@ -268,6 +275,11 @@ impl<'a> Sum<&'a AssetValue> for AssetValue { } /// Asset +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Default, Display, Eq, From, Hash, Ord, PartialEq, PartialOrd)] #[display(fmt = "{{id: {}, value: {}}}", id, value)] pub struct Asset<I = AssetId, V = AssetValue> { @@ -473,6 +485,11 @@ where /// /// Stores assets sorted by [`AssetId`] as a flat key-value vector. This type can be relied on to /// maintain sorted order for iterating. +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(deny_unknown_fields) +)] #[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] pub struct AssetList { /// Sorted Asset Vector diff --git a/manta-accounting/src/fs.rs b/manta-accounting/src/fs.rs index 458e16a32..2d3d49018 100644 --- a/manta-accounting/src/fs.rs +++ b/manta-accounting/src/fs.rs @@ -16,184 +16,283 @@ //! Encrypted Filesystem Primitives -// FIXME: Add streaming interfaces. - use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; -use manta_util::codec::{Decode, Encode}; -/// Filesystem Encrypted Saving -pub trait SaveEncrypted { - /// Path Type - type Path: ?Sized; +/// Open Options +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +pub struct OpenOptions { + /// + read: bool, - /// Saving Key Type - type SavingKey: ?Sized; + /// + write: bool, - /// Saving Error - type Error; + /// + append: bool, - /// Saves the `payload` to `path` using the `saving_key` to encrypt it. - fn save_bytes<P>( - path: P, - saving_key: &Self::SavingKey, - payload: Vec<u8>, - ) -> Result<(), Self::Error> - where - P: AsRef<Self::Path>; + /// + truncate: bool, + + /// + create: bool, - /// Saves the `payload` to `path` after serializing using the `saving_key` to encrypt it. + /// + create_new: bool, +} + +impl OpenOptions { + /// Builds a new default [`OpenOptions`]. #[inline] - fn save<P, E>(path: P, saving_key: &Self::SavingKey, payload: &E) -> Result<(), Self::Error> + pub fn new() -> Self { + Self::default() + } + + /// + #[inline] + pub fn read(mut self, read: bool) -> Self { + self.read = read; + self + } + + /// + #[inline] + pub fn write(mut self, write: bool) -> Self { + self.write = write; + self + } + + /// + #[inline] + pub fn append(mut self, append: bool) -> Self { + self.append = append; + self + } + + /// + #[inline] + pub fn truncate(mut self, truncate: bool) -> Self { + self.truncate = truncate; + self + } + + /// + #[inline] + pub fn create(mut self, create: bool) -> Self { + self.create = create; + self + } + + /// + #[inline] + pub fn create_new(mut self, create_new: bool) -> Self { + self.create_new = create_new; + self + } + + /// + #[inline] + pub fn open<F, P>(&self, path: P, password: &[u8]) -> Result<F, F::Error> where - P: AsRef<Self::Path>, - E: Encode, + F: File, + P: AsRef<F::Path>, { - Self::save_bytes(path, saving_key, payload.to_vec()) + F::open(path, password, self) + } +} + +/// Data Block +pub struct Block { + /// Block Data + data: Box<[u8; 8192]>, +} + +impl Block { + /// Builds a new [`Block`] from an owned collection of bytes. + #[inline] + pub fn new(data: Vec<u8>) -> Option<Self> { + Some(Self { + data: data.into_boxed_slice().try_into().ok()?, + }) } } -/// Filesystem Decrypted Loading -pub trait LoadDecrypted { +/// Encrypted File +pub trait File: Sized { /// Path Type type Path: ?Sized; - /// Loading Key Type - type LoadingKey: ?Sized; - - /// Loading Error Type + /// Error Type type Error; - /// Loads a vector of bytes from `path` using `loading_key` to decrypt them. - fn load_bytes<P>(path: P, loading_key: &Self::LoadingKey) -> Result<Vec<u8>, Self::Error> + /// Opens a new file at `path` with `password` and `options`. + fn open<P>(path: P, password: &[u8], options: &OpenOptions) -> Result<Self, Self::Error> where P: AsRef<Self::Path>; - /// Loads a vector of bytes from `path` using `loading_key` to decrypt them, then deserializing - /// the bytes to a concrete value of type `D`. + /// Creates a new file at `path` with `password`. #[inline] - fn load<P, D>(path: P, loading_key: &Self::LoadingKey) -> Result<D, LoadError<Self, D>> + fn create<P>(path: P, password: &[u8]) -> Result<Self, Self::Error> where P: AsRef<Self::Path>, - D: Decode, { - match Self::load_bytes(path, loading_key) { - Ok(bytes) => D::from_vec(bytes).map_err(LoadError::Decode), - Err(err) => Err(LoadError::Loading(err)), - } + OpenOptions::new().create(true).open(path, password) + } + + /// Writes `block` to `self` after encrypting it. + fn write(&mut self, block: Block) -> Result<(), Self::Error>; + + /// Reads a [`Block`] from `self` after decrypting it. + fn read(&mut self) -> Result<Block, Self::Error>; +} + +/// Encrypting Serializer +pub struct Serializer<'f, F> +where + F: File, +{ + /// Encrypted File + file: &'f mut F, +} + +impl<'f, F> Serializer<'f, F> +where + F: File, +{ + /// + #[inline] + pub fn new(file: &'f mut F) -> Self { + Self { file } } } -/// Loading Error -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "L::Error: Clone, D::Error: Clone"), - Copy(bound = "L::Error: Copy, D::Error: Copy"), - Debug(bound = "L::Error: Debug, D::Error: Debug"), - Eq(bound = "L::Error: Eq, D::Error: Eq"), - Hash(bound = "L::Error: Hash, D::Error: Hash"), - PartialEq(bound = "L::Error: PartialEq, D::Error: PartialEq") -)] -pub enum LoadError<L, D> +// TODO: impl<'f, F> serde::Serializer for Serializer<'f, F> where F: File {} + +/// Decrypting Deserializer +pub struct Deserializer<'f, F> where - L: LoadDecrypted + ?Sized, - D: Decode + ?Sized, + F: File, { - /// Payload Loading Error - Loading(L::Error), + /// Encrypted File + file: &'f mut F, +} - /// Decoding Error - Decode(D::Error), +impl<'f, F> Deserializer<'f, F> +where + F: File, +{ + /// + #[inline] + pub fn new(file: &'f mut F) -> Self { + Self { file } + } } -/// Cocoon Adapters +// TODO: impl<'de, 'f, F> serde::Deserializer<'de> for Deserializer<'f, F> where F: File + Read {} + +/// Cocoon Encrypted File System Adapter #[cfg(feature = "cocoon-fs")] #[cfg_attr(doc_cfg, doc(cfg(feature = "cocoon-fs")))] pub mod cocoon { use super::*; - use cocoon_crate::{Cocoon, Error as CocoonError}; + use cocoon_crate::{Error as CocoonError, MiniCocoon}; use core::fmt; + use manta_crypto::rand::{Rand, SeedableRng}; use manta_util::from_variant_impl; + use rand_chacha::ChaCha20Rng; use std::{ - fs::OpenOptions, - io::{Error as IoError, Read, Write}, + fs::{self, OpenOptions}, + io::Error as IoError, path::Path, }; - use zeroize::Zeroizing; /// Cocoon Loading/Saving Error #[derive(Debug)] pub enum Error { - /// File Opening Error - UnableToOpenFile(IoError), + /// I/O Error + IoError(IoError), /// Cocoon Error Cocoon(CocoonError), + + /// Invalid Block Size + InvalidBlockSize, } - from_variant_impl!(Error, UnableToOpenFile, IoError); + from_variant_impl!(Error, IoError, IoError); from_variant_impl!(Error, Cocoon, CocoonError); impl fmt::Display for Error { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - Self::UnableToOpenFile(err) => write!(f, "File Opening Error: {}", err), + Self::IoError(err) => write!(f, "File I/O Error: {}", err), Self::Cocoon(err) => write!(f, "Cocoon Error: {:?}", err), + Self::InvalidBlockSize => write!(f, "Invalid Block Size"), } } } impl std::error::Error for Error {} - /// Cocoon [`SaveEncrypted`] Adapter - #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct Save; + /// Encrypted File + pub struct File { + /// File Pointer + file: fs::File, - impl SaveEncrypted for Save { - type Path = Path; - type SavingKey = [u8]; - type Error = Error; + /// Encrypting Device + cocoon: MiniCocoon, + } + impl File { + /// Builds a new [`File`] for encrypted data storage with `password`. #[inline] - fn save_bytes<P>( - path: P, - saving_key: &Self::SavingKey, - payload: Vec<u8>, - ) -> Result<(), Self::Error> - where - P: AsRef<Self::Path>, - { - let mut buffer = Zeroizing::new(Vec::new()); - Cocoon::new(saving_key).dump(payload, &mut buffer.as_mut_slice())?; - OpenOptions::new() - .create(true) - .truncate(true) - .write(true) - .open(path)? - .write_all(&buffer)?; - Ok(()) + fn new( + path: &Path, + password: &[u8], + options: &super::OpenOptions, + ) -> Result<Self, IoError> { + Ok(Self { + file: OpenOptions::new() + .read(options.read) + .write(options.write) + .append(options.append) + .truncate(options.truncate) + .create(options.create) + .create_new(options.create_new) + .open(path)?, + cocoon: MiniCocoon::from_password( + password, + &ChaCha20Rng::from_entropy().gen::<_, [u8; 32]>(), + ), + }) } } - /// Cocoon [`LoadDecrypted`] Adapter - #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct Load; - - impl LoadDecrypted for Load { + impl super::File for File { type Path = Path; - type LoadingKey = [u8]; type Error = Error; #[inline] - fn load_bytes<P>(path: P, loading_key: &Self::LoadingKey) -> Result<Vec<u8>, Self::Error> + fn open<P>( + path: P, + password: &[u8], + options: &super::OpenOptions, + ) -> Result<Self, Self::Error> where - P: AsRef<Self::Path>, + P: AsRef<Path>, { - let mut buffer = Zeroizing::new(Vec::new()); - let mut file = OpenOptions::new().read(true).open(path)?; - file.read_to_end(&mut buffer)?; - Ok(Cocoon::parse_only(loading_key).parse(&mut buffer.as_slice())?) + Ok(Self::new(path.as_ref(), password, options)?) + } + + #[inline] + fn write(&mut self, block: Block) -> Result<(), Self::Error> { + Ok(self.cocoon.dump(block.data.to_vec(), &mut self.file)?) + } + + #[inline] + fn read(&mut self) -> Result<Block, Self::Error> { + Block::new(self.cocoon.parse(&mut self.file)?).ok_or(Error::InvalidBlockSize) } } } diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index 6ea9ebc36..298553098 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -31,6 +31,11 @@ use manta_crypto::{ pub type IndexType = u32; /// Hierarchical Key Derivation Parameter +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(deny_unknown_fields) +)] #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct HierarchicalKeyDerivationParameter<M> { @@ -93,6 +98,11 @@ impl_index_type!( impl_index_type!("Key Index", "KeyIndex", KeyIndexType, KeyIndex); /// Key Kind +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Kind { /// Spend Key @@ -165,6 +175,11 @@ where } /// Mapping Hierarchical Key Derivation Scheme +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(deny_unknown_fields) +)] #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct Map<H, K> @@ -398,6 +413,9 @@ pub trait AccountMap { /// Builds a new [`AccountMap`] with a starting account with the default maximum index. fn new() -> Self; + /// Returns the last account index stored in the map. + fn last_account(&self) -> AccountIndex; + /// Returns the maximum key index for `account`, if it exists. fn max_index(&self, account: AccountIndex) -> Option<KeyIndex>; @@ -420,6 +438,15 @@ impl AccountMap for VecAccountMap { this } + #[inline] + fn last_account(&self) -> AccountIndex { + AccountIndex::new( + (self.len() - 1) + .try_into() + .expect("AccountIndex is not allowed to exceed IndexType::MAX."), + ) + } + #[inline] fn max_index(&self, account: AccountIndex) -> Option<KeyIndex> { self.get(account.index() as usize).copied() @@ -438,14 +465,19 @@ impl AccountMap for VecAccountMap { #[inline] fn increment_index(&mut self, account: AccountIndex) -> Option<KeyIndex> { - self.get_mut(account.index() as usize).map(|k| { - k.increment(); - *k + self.get_mut(account.index() as usize).map(|m| { + m.increment(); + *m }) } } /// Account Table +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(deny_unknown_fields) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "H: Clone, M: Clone"), diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index f346f0bff..77d4ad288 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -32,6 +32,9 @@ use manta_crypto::{ rand::{CryptoRng, RngCore, Sample}, }; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + pub mod batch; pub mod canonical; @@ -607,6 +610,17 @@ where } /// Receiving Key +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "PublicKey<C>: Deserialize<'de>", + serialize = "PublicKey<C>: Serialize" + ), + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = ""), @@ -1033,6 +1047,11 @@ where } /// Sender Post Error +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum SenderPostError { /// Asset Spent Error @@ -1047,6 +1066,17 @@ pub enum SenderPostError { } /// Sender Post +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "UtxoSetOutput<C>: Deserialize<'de>, VoidNumber<C>: Deserialize<'de>", + serialize = "UtxoSetOutput<C>: Serialize, VoidNumber<C>: Serialize", + ), + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "UtxoSetOutput<C>: Clone, VoidNumber<C>: Clone"), @@ -1380,6 +1410,11 @@ where } /// Receiver Post Error +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum ReceiverPostError { /// Asset Registered Error @@ -1389,6 +1424,17 @@ pub enum ReceiverPostError { } /// Receiver Post +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "Utxo<C>: Deserialize<'de>, EncryptedNote<C>: Deserialize<'de>", + serialize = "Utxo<C>: Serialize, EncryptedNote<C>: Serialize", + ), + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "Utxo<C>: Clone, EncryptedNote<C>: Clone"), @@ -1906,6 +1952,11 @@ pub type SinkPostingKey<C, L> = <L as TransferLedger<C>>::ValidSinkAccount; pub type TransferLedgerSuperPostingKey<C, L> = <L as TransferLedger<C>>::SuperPostingKey; /// Account Balance +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum AccountBalance { /// Known Balance @@ -1919,6 +1970,11 @@ pub enum AccountBalance { /// /// This `struct` is the error state of the [`TransferLedger::check_source_accounts`] method. See /// its documentation for more. +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct InvalidSourceAccount<AccountId> { /// Account Id @@ -1935,6 +1991,11 @@ pub struct InvalidSourceAccount<AccountId> { /// /// This `struct` is the error state of the [`TransferLedger::check_sink_accounts`] method. See its /// documentation for more. +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct InvalidSinkAccount<AccountId> { /// Account Id @@ -1945,6 +2006,11 @@ pub struct InvalidSinkAccount<AccountId> { /// /// This `enum` is the error state of the [`TransferPost::validate`] method. See its documentation /// for more. +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum TransferPostError<AccountId> { /// Invalid Transfer Post Shape @@ -2003,6 +2069,25 @@ impl<AccountId> From<ReceiverPostError> for TransferPostError<AccountId> { } /// Transfer Post +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = r" + SenderPost<C>: Deserialize<'de>, + ReceiverPost<C>: Deserialize<'de>, + Proof<C>: Deserialize<'de>, + ", + serialize = r" + SenderPost<C>: Serialize, + ReceiverPost<C>: Serialize, + Proof<C>: Serialize, + ", + ), + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "SenderPost<C>: Clone, ReceiverPost<C>: Clone, Proof<C>: Clone"), diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 406510a5b..d32c4d0ec 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -35,6 +35,7 @@ use crate::{ asset::{Asset, AssetId, AssetMap, AssetValue}, + fs::{self, File}, key::{self, HierarchicalKeyDerivationScheme, KeyIndex, ViewKeySelection}, transfer::{ self, @@ -55,7 +56,7 @@ use manta_crypto::{ Accumulator, ConstantCapacityAccumulator, ExactSizeAccumulator, OptimizedAccumulator, }, encryption::DecryptedMessage, - rand::{CryptoRng, Rand, RngCore}, + rand::{CryptoRng, FromEntropy, Rand, RngCore}, }; use manta_util::{ array_map, @@ -65,6 +66,9 @@ use manta_util::{ persistance::Rollback, }; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + /// Signer Connection pub trait Connection<C> where @@ -212,7 +216,7 @@ pub trait Configuration: transfer::Configuration { type ProvingContextCache: CachedResource<MultiProvingContext<Self>>; /// Random Number Generator Type - type Rng: CryptoRng + RngCore; + type Rng: CryptoRng + FromEntropy + RngCore; } /// Account Table Type @@ -252,7 +256,26 @@ where } /// Signer State -struct SignerState<C> +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = r" + AccountTable<C>: Deserialize<'de>, + C::UtxoSet: Deserialize<'de>, + C::AssetMap: Deserialize<'de> + ", + serialize = r" + AccountTable<C>: Serialize, + C::UtxoSet: Serialize, + C::AssetMap: Serialize + ", + ), + deny_unknown_fields + ) +)] +pub struct SignerState<C> where C: Configuration, { @@ -272,6 +295,7 @@ where assets: C::AssetMap, /// Random Number Generator + #[cfg_attr(feature = "serde", serde(skip, default = "FromEntropy::from_entropy"))] rng: C::Rng, } @@ -279,6 +303,32 @@ impl<C> SignerState<C> where C: Configuration, { + /// Saves the state of `self` to the encrypted `file`. + #[cfg(feature = "serde")] + #[inline] + fn save<F>(&self, file: &mut F) -> Result<(), F::Error> + where + Self: Serialize, + F: File, + { + let _ = fs::Serializer::new(file); + // TODO: let _ = self.serialize(serializer); + todo!() + } + + /// Loads an encrypted [`SignerState`] from the encrypted `file`. + #[cfg(feature = "serde")] + #[inline] + fn load<'de, F>(file: &mut F) -> Result<Self, F::Error> + where + Self: Deserialize<'de>, + F: File, + { + let _ = fs::Deserializer::new(file); + // TODO: let _ = Self::deserialize(deserializer); + todo!() + } + /// Inserts the new `utxo`-`encrypted_note` pair if a known key can decrypt the note and /// validate the utxo. #[inline] @@ -399,7 +449,6 @@ where ); !assets.is_empty() }); - // TODO: Whenever we are doing a full update, don't even build the `deposit` and `withdraw` // vectors, since we won't be needing them. if is_partial { @@ -690,6 +739,12 @@ impl<C> Signer<C> where C: Configuration, { + /// Builds a new [`Signer`] from `parameters` and `state`. + #[inline] + fn from_parts(parameters: SignerParameters<C>, state: SignerState<C>) -> Self { + Self { parameters, state } + } + /// Builds a new [`Signer`]. #[inline] fn new_inner( @@ -700,18 +755,18 @@ where assets: C::AssetMap, rng: C::Rng, ) -> Self { - Self { - parameters: SignerParameters { + Self::from_parts( + SignerParameters { parameters, proving_context, }, - state: SignerState { + SignerState { accounts, utxo_set, assets, rng, }, - } + ) } /// Builds a new [`Signer`] from a fresh set of `accounts`. @@ -738,6 +793,30 @@ where ) } + /// Saves the state of `self` to the encrypted `file`. + #[cfg(feature = "serde")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] + #[inline] + pub fn save<F>(&self, file: &mut F) -> Result<(), F::Error> + where + SignerState<C>: Serialize, + F: File, + { + self.state.save(file) + } + + /// Loads an encrypted [`Signer`] state from the encrypted `file`. + #[cfg(feature = "serde")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] + #[inline] + pub fn load<'de, F>(parameters: SignerParameters<C>, file: &mut F) -> Result<Self, F::Error> + where + SignerState<C>: Deserialize<'de>, + F: File, + { + Ok(Self::from_parts(parameters, SignerState::load(file)?)) + } + /// Updates the internal ledger state, returning the new asset distribution. #[inline] pub fn sync<I, R>( diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index 0eaf34627..1d7e04453 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -38,6 +38,8 @@ test = [] derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } manta-util = { path = "../manta-util", features = ["alloc"] } rand_core = { version = "0.6.3", default-features = false } +serde = { version = "1.0.136", optional = true, default-features = false, features = ["alloc", "derive"] } +serde_with = { version = "1.11.0", optional = true, default-features = false, features = ["macros"] } [dev-dependencies] rand = "0.8.4" diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index 56874dbdf..7f53bbec6 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -527,6 +527,7 @@ pub mod measure { } /// Constraint System Size Measurement Report + #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct SizeReport { /// Number of Constraints diff --git a/manta-crypto/src/ecc.rs b/manta-crypto/src/ecc.rs index 28d030d8f..1e01a768c 100644 --- a/manta-crypto/src/ecc.rs +++ b/manta-crypto/src/ecc.rs @@ -53,6 +53,7 @@ pub trait Group<COM = ()>: Sized { } /// Elliptic-Curve Diffie Hellman Key Exchange +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct DiffieHellman<G, COM = ()> diff --git a/manta-crypto/src/encryption.rs b/manta-crypto/src/encryption.rs index ece24c270..e57055584 100644 --- a/manta-crypto/src/encryption.rs +++ b/manta-crypto/src/encryption.rs @@ -19,6 +19,9 @@ use crate::key::{KeyAgreementScheme, KeyDerivationFunction}; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + /// Symmetric Key Encryption Scheme /// /// # Specification @@ -54,6 +57,7 @@ pub mod symmetric { use super::*; /// Mapped Symmetric Encryption Scheme + #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Map<S, P = <S as SymmetricKeyEncryptionScheme>::Plaintext>(PhantomData<(S, P)>) @@ -128,6 +132,7 @@ pub type PublicKey<H> = /// implemented. /// /// [`agree_derive`]: HybridPublicKeyEncryptionScheme::agree_derive +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Hybrid<K, S, F>(PhantomData<(K, S, F)>) @@ -168,6 +173,17 @@ where } /// Encrypted Message +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "H::Ciphertext: Deserialize<'de>, PublicKey<H>: Deserialize<'de>", + serialize = "H::Ciphertext: Serialize, PublicKey<H>: Serialize" + ), + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "H::Ciphertext: Clone, PublicKey<H>: Clone"), @@ -235,6 +251,17 @@ where } /// Decrypted Message +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "H::Plaintext: Deserialize<'de>, PublicKey<H>: Deserialize<'de>", + serialize = "H::Plaintext: Serialize, PublicKey<H>: Serialize" + ), + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "H::Plaintext: Clone, PublicKey<H>: Clone"), @@ -318,6 +345,12 @@ where } None } + + /// Extracts the possible encrypted message which has not yet been decrypted. + #[inline] + pub fn into_inner(self) -> Option<EncryptedMessage<H>> { + self.encrypted_message + } } /// Testing Framework diff --git a/manta-crypto/src/key.rs b/manta-crypto/src/key.rs index 02a14e3a3..cbc39ab1a 100644 --- a/manta-crypto/src/key.rs +++ b/manta-crypto/src/key.rs @@ -37,6 +37,7 @@ pub mod kdf { use alloc::vec::Vec; /// From Byte Slice Reference Adapter + #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct FromByteSliceRef<T, F>(PhantomData<(T, F)>) @@ -65,6 +66,7 @@ pub mod kdf { } /// From Byte Vector Adapter + #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct FromByteVector<T, F>(PhantomData<(T, F)>) diff --git a/manta-crypto/src/merkle_tree/forest.rs b/manta-crypto/src/merkle_tree/forest.rs index 2789a4a43..808703435 100644 --- a/manta-crypto/src/merkle_tree/forest.rs +++ b/manta-crypto/src/merkle_tree/forest.rs @@ -38,6 +38,9 @@ use alloc::{boxed::Box, vec::Vec}; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use manta_util::{into_boxed_array_unchecked, persistance::Rollback, pointer::PointerFamily}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + /// Merkle Forest Configuration pub trait Configuration: tree::Configuration { /// Tree Index Type @@ -171,6 +174,17 @@ where } /// Merkle Forest +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "Parameters<C>: Deserialize<'de>, F: Deserialize<'de>", + serialize = "Parameters<C>: Serialize, F: Serialize" + ), + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "Parameters<C>: Clone, F: Clone"), @@ -186,11 +200,11 @@ where C: Configuration + ?Sized, F: Forest<C>, { - /// Underlying Forest Structure - pub forest: F, - /// Merkle Forest Parameters pub parameters: Parameters<C>, + + /// Underlying Forest Structure + pub forest: F, } impl<C, F> MerkleForest<C, F> @@ -209,7 +223,7 @@ where /// Builds a new [`MerkleForest`] from a pre-constructed `forest` and `parameters`. #[inline] pub fn from_forest(forest: F, parameters: Parameters<C>) -> Self { - Self { forest, parameters } + Self { parameters, forest } } /// Returns a shared reference to the parameters used by this merkle forest. @@ -374,6 +388,7 @@ where } /// [`SingleTree`] Merkle Forest Index +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct SingleTreeIndex; @@ -394,6 +409,7 @@ impl From<SingleTreeIndex> for usize { } /// Single Tree Merkle Forest +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct SingleTree<C, COM = ()>(PhantomData<(C, COM)>) where @@ -431,6 +447,14 @@ where pub type TreeArrayMerkleForest<C, T, const N: usize> = MerkleForest<C, TreeArray<C, T, N>>; /// Tree Array +#[cfg_attr( + all(feature = "serde", feature = "serde_with"), + derive(Deserialize, Serialize), + serde( + bound(deserialize = "T: Deserialize<'de>", serialize = "T: Serialize"), + deny_unknown_fields, + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "T: Clone"), @@ -449,6 +473,10 @@ where /// /// Typically, even for reasonable `N`, the size of this array is too big to fit on the stack, /// so we wrap it in a box to store on the heap. + #[cfg_attr( + all(feature = "serde", feature = "serde_with"), + serde(with = "serde_with::As::<Box<[serde_with::Same; N]>>") + )] array: Box<[T; N]>, /// Type Parameter Marker diff --git a/manta-crypto/src/merkle_tree/full.rs b/manta-crypto/src/merkle_tree/full.rs index f6d15dd59..7f336397c 100644 --- a/manta-crypto/src/merkle_tree/full.rs +++ b/manta-crypto/src/merkle_tree/full.rs @@ -27,10 +27,24 @@ use crate::merkle_tree::{ use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + /// Full Merkle Tree Type pub type FullMerkleTree<C, M = BTreeMap<C>> = MerkleTree<C, Full<C, M>>; /// Full Merkle Tree Backing Structure +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "LeafDigest<C>: Deserialize<'de>, InnerDigest<C>: Deserialize<'de>, M: Deserialize<'de>", + serialize = "LeafDigest<C>: Serialize, InnerDigest<C>: Serialize, M: Serialize" + ), + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone, M: Clone"), diff --git a/manta-crypto/src/merkle_tree/inner_tree.rs b/manta-crypto/src/merkle_tree/inner_tree.rs index 0b5608d02..69d206f61 100644 --- a/manta-crypto/src/merkle_tree/inner_tree.rs +++ b/manta-crypto/src/merkle_tree/inner_tree.rs @@ -30,7 +30,15 @@ use core::{fmt::Debug, hash::Hash, iter::FusedIterator, marker::PhantomData, ops #[cfg(feature = "std")] use std::{collections::hash_map, hash::BuildHasher}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + /// Inner Tree Node +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct InnerNode { /// Depth @@ -135,6 +143,11 @@ impl From<InnerNode> for Node { /// /// This `struct` is created by the [`iter`](InnerNode::iter) method on [`InnerNode`]. /// See its documentation for more. +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct InnerNodeIter { /// Current Node @@ -312,6 +325,17 @@ where } /// Sentinel Source for a Single Sentinel Value +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "InnerDigest<C>: Deserialize<'de>", + serialize = "InnerDigest<C>: Serialize" + ), + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "InnerDigest<C>: Clone"), @@ -352,6 +376,17 @@ where /// /// [`Tree`]: crate::merkle_tree::Tree /// [`Full`]: crate::merkle_tree::full::Full +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "M: Deserialize<'de>, S: Deserialize<'de>", + serialize = "M: Serialize, S: Serialize" + ), + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "M: Clone, S: Clone"), @@ -620,6 +655,17 @@ where /// leaf digests. /// /// [`Tree`]: crate::merkle_tree::Tree +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "M: Deserialize<'de>, S: Deserialize<'de>", + serialize = "M: Serialize, S: Serialize" + ), + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "M: Clone, S: Clone"), diff --git a/manta-crypto/src/merkle_tree/node.rs b/manta-crypto/src/merkle_tree/node.rs index 12ccb97b8..428dbc7bf 100644 --- a/manta-crypto/src/merkle_tree/node.rs +++ b/manta-crypto/src/merkle_tree/node.rs @@ -22,7 +22,15 @@ use core::{ ops::{Add, Sub}, }; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + /// Parity of a Subtree +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub enum Parity { /// Left Side of the Subtree @@ -146,6 +154,11 @@ impl Default for Parity { } /// Node Index +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct Node<Idx = usize>( /// Level-wise Index to a node in a Binary Tree @@ -365,6 +378,11 @@ where /// /// This `struct` is created by the [`parents`](Node::parents) method on [`Node`]. /// See its documentation for more. +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct NodeParents { /// Current Index diff --git a/manta-crypto/src/merkle_tree/partial.rs b/manta-crypto/src/merkle_tree/partial.rs index b70142c4f..eb2190063 100644 --- a/manta-crypto/src/merkle_tree/partial.rs +++ b/manta-crypto/src/merkle_tree/partial.rs @@ -27,10 +27,24 @@ use crate::merkle_tree::{ use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + /// Partial Merkle Tree Type pub type PartialMerkleTree<C, M = BTreeMap<C>> = MerkleTree<C, Partial<C, M>>; /// Partial Merkle Tree Backing Structure +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "LeafDigest<C>: Deserialize<'de>, InnerDigest<C>: Deserialize<'de>, M: Deserialize<'de>", + serialize = "LeafDigest<C>: Serialize, InnerDigest<C>: Serialize, M: Serialize" + ), + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone, M: Clone"), diff --git a/manta-crypto/src/merkle_tree/path.rs b/manta-crypto/src/merkle_tree/path.rs index 8357ec19c..c41ff07f4 100644 --- a/manta-crypto/src/merkle_tree/path.rs +++ b/manta-crypto/src/merkle_tree/path.rs @@ -34,12 +34,26 @@ use core::{ slice::SliceIndex, }; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + pub(super) mod prelude { #[doc(inline)] pub use super::{CurrentPath, Path}; } /// Merkle Tree Inner Path +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "InnerDigest<C>: Deserialize<'de>", + serialize = "InnerDigest<C>: Serialize" + ), + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "InnerDigest<C>: Clone"), @@ -224,6 +238,17 @@ where } /// Merkle Tree Current Inner Path +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "InnerDigest<C>: Deserialize<'de>", + serialize = "InnerDigest<C>: Serialize" + ), + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "InnerDigest<C>: Clone"), @@ -592,6 +617,17 @@ impl<C> ExactSizeIterator for CurrentInnerPathNodeIter<C> where C: Configuration impl<C> FusedIterator for CurrentInnerPathNodeIter<C> where C: Configuration + ?Sized {} /// Merkle Tree Path +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "LeafDigest<C>: Deserialize<'de>, InnerDigest<C>: Deserialize<'de>", + serialize = "LeafDigest<C>: Serialize, InnerDigest<C>: Serialize" + ), + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), @@ -730,6 +766,17 @@ where } /// Merkle Tree Current Path +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "LeafDigest<C>: Deserialize<'de>, InnerDigest<C>: Deserialize<'de>", + serialize = "LeafDigest<C>: Serialize, InnerDigest<C>: Serialize" + ), + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone"), diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index 9f6c4db0b..0fef1e5e8 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -43,6 +43,9 @@ use manta_util::{ pointer::PointerFamily, }; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + /// Merkle Tree Leaf Hash pub trait LeafHash<COM = ()> { /// Leaf Type @@ -77,6 +80,11 @@ pub trait LeafHash<COM = ()> { /// /// This implementation of [`LeafHash`] should only be used when users cannot control the value of a /// leaf itself, otherwise, using this implementation may not be safe. +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(deny_unknown_fields) +)] #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct IdentityLeafHash<L, COM = ()>(PhantomData<(L, COM)>) @@ -182,6 +190,11 @@ pub trait Configuration<COM = ()>: HashConfiguration<COM> { /// /// Since this `struct` is meant to be used as a type parameter, any values of this type have no /// meaning, just like values of type [`HashConfiguration`] or [`Configuration`]. +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Config<C, COM, const HEIGHT: usize>(PhantomData<(COM, C)>) where @@ -414,8 +427,13 @@ where /// Path Error /// -/// This `struct` is returned by the [`path`](WithProofs::path) method of the [`WithProofs`] trait. -/// See its documentation for more. +/// This `enum` is the error state of the [`path`](WithProofs::path) method of the [`WithProofs`] +/// trait. See its documentation for more. +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum PathError { /// Path for the given index was not stored in the tree @@ -506,6 +524,23 @@ where } /// Merkle Tree Parameters +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = r" + LeafHashParameters<C, COM>: Deserialize<'de>, + InnerHashParameters<C, COM>: Deserialize<'de> + ", + serialize = r" + LeafHashParameters<C, COM>: Serialize, + InnerHashParameters<C, COM>: Serialize, + " + ), + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "LeafHashParameters<C, COM>: Clone, InnerHashParameters<C, COM>: Clone"), @@ -746,6 +781,17 @@ where } /// Merkle Tree +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "Parameters<C>: Deserialize<'de>, T: Deserialize<'de>", + serialize = "Parameters<C>: Serialize, T: Serialize" + ), + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "Parameters<C>: Clone, T: Clone"), @@ -761,11 +807,11 @@ where C: Configuration + ?Sized, T: Tree<C>, { - /// Underlying Tree Structure - pub tree: T, - /// Merkle Tree Parameters pub parameters: Parameters<C>, + + /// Underlying Tree Structure + pub tree: T, } impl<C, T> MerkleTree<C, T> @@ -813,7 +859,7 @@ where /// Builds a new [`MerkleTree`] from a pre-constructed `tree` and `parameters`. #[inline] pub fn from_tree(tree: T, parameters: Parameters<C>) -> Self { - Self { tree, parameters } + Self { parameters, tree } } /// Builds a new [`MerkleTree`] from a `trunk` and `parameters`. diff --git a/manta-crypto/src/rand.rs b/manta-crypto/src/rand.rs index 1c8b8062c..58d43cee1 100644 --- a/manta-crypto/src/rand.rs +++ b/manta-crypto/src/rand.rs @@ -25,6 +25,11 @@ use manta_util::into_array_unchecked; pub use rand_core::{block, CryptoRng, Error, RngCore, SeedableRng}; /// Random Number Generator Sized Wrapper +#[cfg_attr( + feature = "serde", + derive(serde::Serialize), + serde(deny_unknown_fields) +)] #[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct SizedRng<'r, R>( /// Mutable Reference to Random Number Generator @@ -74,6 +79,11 @@ where } /// Seed Into Random Number Generator +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(deny_unknown_fields) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "R: Clone"), @@ -175,7 +185,30 @@ where } } +/// Entropy Seedable PRNG +/// +/// This `trait` is automatically implemented for all [`SeedableRng`] whenever the `getrandom` crate +/// is in scope. This `trait` is used to capture the behavior of seeding from an entropy source even +/// if the crate is not imported. +pub trait FromEntropy { + /// Creates a new instance of `Self` seeded via some entropy source. + fn from_entropy() -> Self; +} + +#[cfg(feature = "getrandom")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "getrandom")))] +impl<R> FromEntropy for R +where + R: SeedableRng, +{ + #[inline] + fn from_entropy() -> Self { + SeedableRng::from_entropy() + } +} + /// Standard Distribution +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Standard; diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 2f6c95259..8599a2deb 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -86,7 +86,7 @@ derivative = { version = "2.2.0", default-features = false } generic-array = { version = "0.14.4", default-features = false } indexmap = { version = "1.8.0", default-features = false, optional = true } manta-accounting = { path = "../manta-accounting", default-features = false } -manta-crypto = { path = "../manta-crypto", default-features = false } +manta-crypto = { path = "../manta-crypto", default-features = false, features = ["getrandom"] } manta-util = { path = "../manta-util", default-features = false } parking_lot = { version = "0.11.2", optional = true, default-features = false } rand = { version = "0.8.4", optional = true } diff --git a/manta-util/src/codec.rs b/manta-util/src/codec.rs index 1e79874ca..4bcf55bf6 100644 --- a/manta-util/src/codec.rs +++ b/manta-util/src/codec.rs @@ -19,6 +19,7 @@ // TODO: Add `ReadFrom` and `WriteInto` traits for conversion between different serde/codec impls // which are specialized so that you can automatically convert between a type and itself. +use crate::into_array_unchecked; use core::{convert::Infallible, fmt::Debug, hash::Hash, marker::PhantomData}; #[cfg(feature = "alloc")] @@ -576,6 +577,39 @@ impl Encode for u8 { } } +impl Encode for u16 { + #[inline] + fn encode<W>(&self, mut writer: W) -> Result<(), W::Error> + where + W: Write, + { + writer.write_ref(&self.to_le_bytes())?; + Ok(()) + } +} + +impl Encode for u32 { + #[inline] + fn encode<W>(&self, mut writer: W) -> Result<(), W::Error> + where + W: Write, + { + writer.write_ref(&self.to_le_bytes())?; + Ok(()) + } +} + +impl Encode for u64 { + #[inline] + fn encode<W>(&self, mut writer: W) -> Result<(), W::Error> + where + W: Write, + { + writer.write_ref(&self.to_le_bytes())?; + Ok(()) + } +} + impl<T> Encode for [T] where T: Encode, @@ -585,6 +619,7 @@ where where W: Write, { + (self.len() as u64).encode(&mut writer)?; for item in self { item.encode(&mut writer)?; } @@ -724,6 +759,96 @@ impl Decode for u8 { } } +impl Decode for u16 { + type Error = (); + + #[inline] + fn decode<R>(mut reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> + where + R: Read, + { + let mut bytes = [0; 2]; + match reader.read_exact(&mut bytes) { + Ok(()) => Ok(Self::from_le_bytes(bytes)), + Err(ReadExactError::Read(err)) => Err(DecodeError::Read(err)), + _ => Err(DecodeError::Decode(())), + } + } +} + +impl Decode for u32 { + type Error = (); + + #[inline] + fn decode<R>(mut reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> + where + R: Read, + { + let mut bytes = [0; 4]; + match reader.read_exact(&mut bytes) { + Ok(()) => Ok(Self::from_le_bytes(bytes)), + Err(ReadExactError::Read(err)) => Err(DecodeError::Read(err)), + _ => Err(DecodeError::Decode(())), + } + } +} + +impl Decode for u64 { + type Error = (); + + #[inline] + fn decode<R>(mut reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> + where + R: Read, + { + let mut bytes = [0; 8]; + match reader.read_exact(&mut bytes) { + Ok(()) => Ok(Self::from_le_bytes(bytes)), + Err(ReadExactError::Read(err)) => Err(DecodeError::Read(err)), + _ => Err(DecodeError::Decode(())), + } + } +} + +impl<T, const N: usize> Decode for [T; N] +where + T: Decode, +{ + type Error = Option<T::Error>; + + #[inline] + fn decode<R>(mut reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> + where + R: Read, + { + let mut results = Vec::with_capacity(N); + for _ in 0..N { + results.push(T::decode(&mut reader).map_err(|err| err.map_decode(Some))?); + } + Ok(into_array_unchecked(results)) + } +} + +impl<T> Decode for Vec<T> +where + T: Decode, +{ + type Error = Option<T::Error>; + + #[inline] + fn decode<R>(mut reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> + where + R: Read, + { + let len = u64::decode(&mut reader).map_err(|err| err.map_decode(|_| None))?; + let mut results = Vec::with_capacity(len as usize); + for _ in 0..len { + results.push(T::decode(&mut reader).map_err(|err| err.map_decode(Some))?); + } + Ok(results) + } +} + /// Option [`Decode`] Error #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum OptionDecodeError<T> { From 5d3ebf71cfc6d52fc8fc6cb375c0224f621f47dc Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sat, 5 Feb 2022 17:17:51 -0500 Subject: [PATCH 217/275] wip: add more serde structure and improve ecc interface --- manta-accounting/Cargo.toml | 7 +- manta-accounting/src/asset.rs | 16 +- manta-accounting/src/key.rs | 20 +- manta-accounting/src/transfer/mod.rs | 24 +- manta-accounting/src/transfer/test.rs | 2 +- manta-accounting/src/wallet/signer.rs | 5 +- manta-accounting/src/wallet/state.rs | 44 ++-- manta-accounting/src/wallet/test/mod.rs | 2 +- manta-crypto/Cargo.toml | 4 +- manta-crypto/src/constraint.rs | 22 +- manta-crypto/src/ecc.rs | 218 ++++++++++++++---- manta-crypto/src/encryption.rs | 16 +- manta-crypto/src/key.rs | 15 +- manta-crypto/src/merkle_tree/forest.rs | 18 +- manta-crypto/src/merkle_tree/full.rs | 9 +- manta-crypto/src/merkle_tree/inner_tree.rs | 29 +-- manta-crypto/src/merkle_tree/node.rs | 8 +- manta-crypto/src/merkle_tree/partial.rs | 3 +- manta-crypto/src/merkle_tree/path.rs | 6 +- manta-crypto/src/merkle_tree/tree.rs | 10 +- manta-crypto/src/rand.rs | 23 +- manta-pay/Cargo.toml | 7 +- manta-pay/src/bin/generate_parameters.rs | 6 +- manta-pay/src/config.rs | 2 +- .../constraint/arkworks/proof_system.rs | 9 +- manta-pay/src/crypto/ecc.rs | 21 +- manta-pay/src/test/ledger.rs | 2 +- manta-pay/src/test/transfer.rs | 6 +- manta-pay/src/wallet/signer/client/http.rs | 103 +++++++++ manta-pay/src/wallet/signer/client/mod.rs | 2 + .../src/wallet/signer/client/websocket.rs | 1 - manta-util/Cargo.toml | 5 + manta-util/src/lib.rs | 3 + 33 files changed, 466 insertions(+), 202 deletions(-) diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index bdeb9b181..e68595e02 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -36,6 +36,9 @@ cocoon-fs = [ # Parallel Execution parallel = ["crossbeam/std", "rayon"] +# Serde +serde = ["manta-crypto/serde"] + # Standard Library std = ["manta-crypto/std", "manta-util/std"] @@ -43,8 +46,6 @@ std = ["manta-crypto/std", "manta-util/std"] test = ["indexmap", "parking_lot", "rand/alloc", "statrs"] [dependencies] -argonautica = { version = "0.2.0", optional = true, default-features = false } -bitflags = { version = "1.3.2", optional = true, default-features = false } cocoon = { version = "0.3.0", optional = true, default-features = false } crossbeam = { version = "0.8.1", optional = true, default-features = false } derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } @@ -56,9 +57,7 @@ parking_lot = { version = "0.11.2", optional = true, default-features = false } rand = { version = "0.8.4", optional = true, default-features = false } rand_chacha = { version = "0.3.1", optional = true, default-features = false } rayon = { version = "1.5.1", optional = true, default-features = false } -serde = { version = "1.0.136", optional = true, default-features = false, features = ["alloc", "derive"] } statrs = { version = "0.15.0", optional = true, default-features = false } -zeroize = { version = "1.4.3", optional = true, default-features = false, features = ["alloc"] } [dev-dependencies] rand = "0.8.4" diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 5c24c95aa..5e692141a 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -16,6 +16,8 @@ //! Assets +// FIXME: Make sure that the `AssetList` invariants are safe when deserializing. + use alloc::{ collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}, vec, @@ -36,15 +38,15 @@ use manta_crypto::{ }; use manta_util::into_array_unchecked; +#[cfg(feature = "serde")] +use manta_util::serde::{Deserialize, Serialize}; + #[cfg(feature = "std")] use std::{ collections::hash_map::{Entry as HashMapEntry, HashMap, RandomState}, hash::BuildHasher, }; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - /// [`AssetId`] Base Type pub type AssetIdType = u32; @@ -52,7 +54,7 @@ pub type AssetIdType = u32; #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde(deny_unknown_fields) + serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(Clone, Copy, Debug, Default, Display, Eq, From, Hash, Ord, PartialEq, PartialOrd)] #[from(forward)] @@ -127,7 +129,7 @@ pub type AssetValueType = u128; #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde(deny_unknown_fields) + serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive( Add, @@ -278,7 +280,7 @@ impl<'a> Sum<&'a AssetValue> for AssetValue { #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde(deny_unknown_fields) + serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(Clone, Copy, Debug, Default, Display, Eq, From, Hash, Ord, PartialEq, PartialOrd)] #[display(fmt = "{{id: {}, value: {}}}", id, value)] @@ -488,7 +490,7 @@ where #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde(deny_unknown_fields) + serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] pub struct AssetList { diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index 298553098..df9e123f8 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -27,14 +27,17 @@ use manta_crypto::{ rand::{CryptoRng, RngCore, Sample}, }; +#[cfg(feature = "serde")] +use manta_util::serde::{Deserialize, Serialize}; + /// Hierarchical Key Derivation Parameter Type pub type IndexType = u32; /// Hierarchical Key Derivation Parameter #[cfg_attr( feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(deny_unknown_fields) + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -95,13 +98,14 @@ impl_index_type!( AccountIndexType, AccountIndex ); + impl_index_type!("Key Index", "KeyIndex", KeyIndexType, KeyIndex); /// Key Kind #[cfg_attr( feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(deny_unknown_fields) + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Kind { @@ -177,8 +181,8 @@ where /// Mapping Hierarchical Key Derivation Scheme #[cfg_attr( feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(deny_unknown_fields) + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] @@ -475,8 +479,8 @@ impl AccountMap for VecAccountMap { /// Account Table #[cfg_attr( feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(deny_unknown_fields) + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(derivative::Derivative)] #[derivative( diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index 77d4ad288..a676304a6 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -33,7 +33,7 @@ use manta_crypto::{ }; #[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; +use manta_util::serde::{Deserialize, Serialize}; pub mod batch; pub mod canonical; @@ -158,7 +158,7 @@ pub trait Configuration { type Compiler: ConstraintSystem; /// Proof System Type - type ProofSystem: ProofSystem<ConstraintSystem = Self::Compiler, Verification = bool> + type ProofSystem: ProofSystem<ConstraintSystem = Self::Compiler> + ProofSystemInput<AssetId> + ProofSystemInput<AssetValue> + ProofSystemInput<UtxoSetOutput<Self>> @@ -618,6 +618,7 @@ where deserialize = "PublicKey<C>: Deserialize<'de>", serialize = "PublicKey<C>: Serialize" ), + crate = "manta_util::serde", deny_unknown_fields ) )] @@ -1050,7 +1051,7 @@ where #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde(deny_unknown_fields) + serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum SenderPostError { @@ -1074,6 +1075,7 @@ pub enum SenderPostError { deserialize = "UtxoSetOutput<C>: Deserialize<'de>, VoidNumber<C>: Deserialize<'de>", serialize = "UtxoSetOutput<C>: Serialize, VoidNumber<C>: Serialize", ), + crate = "manta_util::serde", deny_unknown_fields ) )] @@ -1413,7 +1415,7 @@ where #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde(deny_unknown_fields) + serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum ReceiverPostError { @@ -1432,6 +1434,7 @@ pub enum ReceiverPostError { deserialize = "Utxo<C>: Deserialize<'de>, EncryptedNote<C>: Deserialize<'de>", serialize = "Utxo<C>: Serialize, EncryptedNote<C>: Serialize", ), + crate = "manta_util::serde", deny_unknown_fields ) )] @@ -1674,8 +1677,8 @@ where R: CryptoRng + RngCore + ?Sized, { C::ProofSystem::generate_context( - Self::unknown_constraints(parameters), public_parameters, + Self::unknown_constraints(parameters), rng, ) } @@ -1693,8 +1696,8 @@ where { Ok(TransferPost { validity_proof: C::ProofSystem::prove( - self.known_constraints(parameters), context, + self.known_constraints(parameters), rng, )?, asset_id: self.asset_id, @@ -1955,7 +1958,7 @@ pub type TransferLedgerSuperPostingKey<C, L> = <L as TransferLedger<C>>::SuperPo #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde(deny_unknown_fields) + serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum AccountBalance { @@ -1973,7 +1976,7 @@ pub enum AccountBalance { #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde(deny_unknown_fields) + serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct InvalidSourceAccount<AccountId> { @@ -1994,7 +1997,7 @@ pub struct InvalidSourceAccount<AccountId> { #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde(deny_unknown_fields) + serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct InvalidSinkAccount<AccountId> { @@ -2009,7 +2012,7 @@ pub struct InvalidSinkAccount<AccountId> { #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde(deny_unknown_fields) + serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum TransferPostError<AccountId> { @@ -2085,6 +2088,7 @@ impl<AccountId> From<ReceiverPostError> for TransferPostError<AccountId> { Proof<C>: Serialize, ", ), + crate = "manta_util::serde", deny_unknown_fields ) )] diff --git a/manta-accounting/src/transfer/test.rs b/manta-accounting/src/transfer/test.rs index 260784b3a..6aa510118 100644 --- a/manta-accounting/src/transfer/test.rs +++ b/manta-accounting/src/transfer/test.rs @@ -238,9 +238,9 @@ where { let post = Self::sample_post(proving_context, parameters, utxo_set, rng)?; C::ProofSystem::verify( + verifying_context, &post.generate_proof_input(), &post.validity_proof, - verifying_context, ) } } diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index d32c4d0ec..6ecb29637 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -67,7 +67,7 @@ use manta_util::{ }; #[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; +use manta_util::serde::{Deserialize, Serialize}; /// Signer Connection pub trait Connection<C> @@ -181,6 +181,8 @@ where /// /// This `enum` is the error state for the [`sign`](Connection::sign) method on [`Connection`]. /// See its documentation for more. +#[derive(derivative::Derivative)] +#[derivative(Debug(bound = "ProofSystemError<C>: Debug"))] pub enum SignError<C> where C: transfer::Configuration, @@ -272,6 +274,7 @@ where C::AssetMap: Serialize ", ), + crate = "manta_util::serde", deny_unknown_fields ) )] diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 87c0bd057..1c3e22fea 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -273,7 +273,7 @@ where } = self .ledger .pull(&self.checkpoint) - .map_err(Error::LedgerError)?; + .map_err(Error::LedgerConnectionError)?; if checkpoint < self.checkpoint { return Err(Error::InconsistentCheckpoint); } @@ -284,7 +284,7 @@ where inserts: receivers.into_iter().collect(), removes: senders.into_iter().collect(), }) - .map_err(Error::SignerError)? + .map_err(Error::SignerConnectionError)? { Ok(SyncResponse::Partial { deposit, withdraw }) => { self.assets.deposit_all(deposit); @@ -346,25 +346,16 @@ where self.sync()?; self.check(&transaction) .map_err(Error::InsufficientBalance)?; - match self.signer.sign(transaction).map_err(Error::SignerError)? { - Ok(SignResponse { posts }) => { - let PushResponse { success } = - self.ledger.push(posts).map_err(Error::LedgerError)?; - Ok(success) - } - Err(SignError::ProvingContextCacheError) => { - // TODO: This kind of error should not bubble up to the wallet level. - todo!() - } - Err(SignError::InsufficientBalance(asset)) => { - // FIXME: If we reach this point, the wallet and signer are not synchronized. - todo!() - } - Err(SignError::ProofSystemError(err)) => { - // TODO: This kind of error should not bubble up to the wallet level. - todo!() - } - } + let SignResponse { posts } = self + .signer + .sign(transaction) + .map_err(Error::SignerConnectionError)? + .map_err(Error::SignError)?; + let PushResponse { success } = self + .ledger + .push(posts) + .map_err(Error::LedgerConnectionError)?; + Ok(success) } /// Returns a new [`ReceivingKey`] for `self` to receive assets. @@ -379,7 +370,7 @@ where /// This `enum` is the error state for [`Wallet`] methods. See [`sync`](Wallet::sync) and /// [`post`](Wallet::post) for more. #[derive(derivative::Derivative)] -#[derivative(Debug(bound = "L::Error: Debug, S::Error: Debug"))] +#[derivative(Debug(bound = "SignError<C>: Debug, S::Error: Debug, L::Error: Debug"))] pub enum Error<C, L, S> where C: Configuration, @@ -392,9 +383,12 @@ where /// Inconsistent Checkpoint Error InconsistentCheckpoint, - /// Ledger Connection Error - LedgerError(L::Error), + /// Signing Error + SignError(SignError<C>), /// Signer Connection Error - SignerError(S::Error), + SignerConnectionError(S::Error), + + /// Ledger Connection Error + LedgerConnectionError(L::Error), } diff --git a/manta-accounting/src/wallet/test/mod.rs b/manta-accounting/src/wallet/test/mod.rs index e870c0e8d..e7967c970 100644 --- a/manta-accounting/src/wallet/test/mod.rs +++ b/manta-accounting/src/wallet/test/mod.rs @@ -403,7 +403,7 @@ where self.public_keys.write().insert(key); Ok(true) } - Err(err) => Err(wallet::Error::SignerError(err)), + Err(err) => Err(wallet::Error::SignerConnectionError(err)), }, }, } diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index 1d7e04453..b905bf480 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -28,6 +28,9 @@ maintenance = { status = "actively-developed" } # Enable `getrandom` Entropy Source getrandom = ["rand_core/getrandom"] +# Serde +serde = ["manta-util/serde-alloc", "serde_with"] + # Standard Library std = ["manta-util/std"] @@ -38,7 +41,6 @@ test = [] derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } manta-util = { path = "../manta-util", features = ["alloc"] } rand_core = { version = "0.6.3", default-features = false } -serde = { version = "1.0.136", optional = true, default-features = false, features = ["alloc", "derive"] } serde_with = { version = "1.11.0", optional = true, default-features = false, features = ["macros"] } [dev-dependencies] diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index 7f53bbec6..5d32240ce 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -436,11 +436,6 @@ pub trait ProofSystem { /// Proof Type type Proof; - /// Verification Type - /// - /// For non-recursive proof systems this is just `bool`. - type Verification; - /// Error Type type Error; @@ -454,8 +449,8 @@ pub trait ProofSystem { /// Returns proving and verifying contexts for the constraints contained in `compiler`. fn generate_context<R>( - compiler: Self::ConstraintSystem, public_parameters: &Self::PublicParameters, + compiler: Self::ConstraintSystem, rng: &mut R, ) -> Result<(Self::ProvingContext, Self::VerifyingContext), Self::Error> where @@ -463,8 +458,8 @@ pub trait ProofSystem { /// Returns a proof that the constraint system `compiler` is consistent. fn prove<R>( - compiler: Self::ConstraintSystem, context: &Self::ProvingContext, + compiler: Self::ConstraintSystem, rng: &mut R, ) -> Result<Self::Proof, Self::Error> where @@ -472,10 +467,10 @@ pub trait ProofSystem { /// Verifies that a proof generated from this proof system is valid. fn verify( + context: &Self::VerifyingContext, input: &Self::Input, proof: &Self::Proof, - context: &Self::VerifyingContext, - ) -> Result<Self::Verification, Self::Error>; + ) -> Result<bool, Self::Error>; } /// Proof System Input @@ -491,6 +486,9 @@ where pub mod measure { use super::*; + #[cfg(feature = "serde")] + use manta_util::serde::{Deserialize, Serialize}; + /// Constraint System Measurement pub trait Measure { /// Returns the number of constraints stored in `self`. @@ -527,7 +525,11 @@ pub mod measure { } /// Constraint System Size Measurement Report - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) + )] #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct SizeReport { /// Number of Constraints diff --git a/manta-crypto/src/ecc.rs b/manta-crypto/src/ecc.rs index 1e01a768c..38eaaabef 100644 --- a/manta-crypto/src/ecc.rs +++ b/manta-crypto/src/ecc.rs @@ -16,24 +16,36 @@ //! Elliptic Curve Cryptography -// TODO: Improve ECC abstractions over arkworks. +// TODO: Make sure we can use `PreprocessedScalarMulTable<G, _>` as a drop-in replacement for `G`. use crate::{ constraint::Constant, key::KeyAgreementScheme, rand::{CryptoRng, RngCore, Sample}, }; -use core::marker::PhantomData; +use alloc::boxed::Box; use manta_util::codec::{Decode, DecodeError, Encode, Read, Write}; -/// Elliptic Curve Group -pub trait Group<COM = ()>: Sized { - /// Scalar Field - type Scalar; +#[cfg(feature = "serde")] +use manta_util::serde::{Deserialize, Serialize}; + +/// Elliptic Curve Point Doubling Operation +pub trait PointDouble<COM = ()> { + /// Output Type + type Output; + + /// Performs a point doubling of `self` in `compiler`. + fn double(&self, compiler: &mut COM) -> Self::Output; +} + +/// Elliptic Curve Point Addition Operation +pub trait PointAdd<COM = ()> { + /// Output Type + type Output; /// Adds `rhs` to `self` in `compiler`, returning a new group element. #[must_use] - fn add(&self, rhs: &Self, compiler: &mut COM) -> Self; + fn add(&self, rhs: &Self, compiler: &mut COM) -> Self::Output; /// Adds an owned `rhs` value to `self` in `compiler`, returning a new group element. /// @@ -43,41 +55,169 @@ pub trait Group<COM = ()>: Sized { /// values is more efficient than with shared references. #[inline] #[must_use] - fn add_owned(self, rhs: Self, compiler: &mut COM) -> Self { + fn add_owned(self, rhs: Self, compiler: &mut COM) -> Self::Output + where + Self: Sized, + { self.add(&rhs, compiler) } + /// Adds `rhs` to `self` in `compiler`, modifying `self` in-place. + #[inline] + fn add_assign(&mut self, rhs: &Self, compiler: &mut COM) + where + Self: PointAdd<COM, Output = Self> + Sized, + { + *self = self.add(rhs, compiler); + } +} + +/// Elliptic Curve Scalar Multiplication Operation +pub trait ScalarMul<COM = ()> { + /// Scalar Field + type Scalar; + + /// Output Type + type Output; + /// Multiplies `self` by `scalar` in `compiler`, returning a new group element. #[must_use] - fn scalar_mul(&self, scalar: &Self::Scalar, compiler: &mut COM) -> Self; + fn scalar_mul(&self, scalar: &Self::Scalar, compiler: &mut COM) -> Self::Output; } -/// Elliptic-Curve Diffie Hellman Key Exchange -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -#[derive(derivative::Derivative)] -#[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] -pub struct DiffieHellman<G, COM = ()> +/// Elliptic Curve Pre-processed Scalar Multiplication Operation +pub trait PreprocessedScalarMul<COM = (), const N: usize = 1>: ScalarMul<COM> + Sized { + /// Performs the scalar multiplication against a pre-computed table. + #[must_use] + fn preprocessed_scalar_mul( + table: &[Self; N], + scalar: &Self::Scalar, + compiler: &mut COM, + ) -> Self::Output; +} + +impl<G, COM> PreprocessedScalarMul<COM> for G where - G: Group<COM>, + G: ScalarMul<COM>, { - /// Base Generator - generator: G, + #[inline] + fn preprocessed_scalar_mul( + table: &[Self; 1], + scalar: &Self::Scalar, + compiler: &mut COM, + ) -> Self::Output { + table[0].scalar_mul(scalar, compiler) + } +} + +/// Elliptic Curve Group +pub trait Group<COM = ()>: + PointAdd<COM> + PointDouble<COM> + PreprocessedScalarMul<COM> + ScalarMul<COM> +{ +} + +/// Pre-processed Scalar Multiplication Table +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound(deserialize = "G: Deserialize<'de>", serialize = "G: Serialize"), + crate = "manta_util::serde", + deny_unknown_fields + ) +)] +#[derive(derivative::Derivative)] +#[derivative(Clone, Debug, Eq, Hash, PartialEq)] +pub struct PreprocessedScalarMulTable<G, const N: usize = 1> { + /// Pre-computed Table + #[cfg_attr( + feature = "serde", + serde(with = "serde_with::As::<Box<[serde_with::Same; N]>>") + )] + table: Box<[G; N]>, +} + +impl<G, const N: usize> PreprocessedScalarMulTable<G, N> { + /// Builds a new [`PreprocessedScalarMulTable`] collection from `base`. + #[inline] + pub fn from_base<COM>(mut base: G, compiler: &mut COM) -> Self + where + G: Clone + PointAdd<COM, Output = G> + PointDouble<COM, Output = G>, + { + let mut powers = Vec::with_capacity(N); + let double = base.double(compiler); + for _ in 0..N { + powers.push(base.clone()); + base.add_assign(&double, compiler); + } + Self::from_powers_unchecked( + powers + .into_boxed_slice() + .try_into() + .ok() + .expect("The size is correct because we perform `N` insertions."), + ) + } - /// Type Parameter Marker - __: PhantomData<COM>, + /// Builds a new [`PreprocessedScalarMulTable`] collection from a known `table` set without + /// checking if the table is consistent. + #[inline] + pub fn from_powers_unchecked(table: Box<[G; N]>) -> Self { + Self { table } + } } -impl<G, COM> DiffieHellman<G, COM> +impl<G, const N: usize> AsRef<[G; N]> for PreprocessedScalarMulTable<G, N> { + #[inline] + fn as_ref(&self) -> &[G; N] { + &self.table + } +} + +impl<D, G, const N: usize> Sample<D> for PreprocessedScalarMulTable<G, N> where - G: Group<COM>, + G: Clone + PointAdd<Output = G> + PointDouble<Output = G> + Sample<D>, { + #[inline] + fn sample<R>(distribution: D, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + Self::from_base(G::sample(distribution, rng), &mut ()) + } +} + +impl<G, COM, const N: usize> ScalarMul<COM> for PreprocessedScalarMulTable<G, N> +where + G: PreprocessedScalarMul<COM, N>, +{ + type Scalar = G::Scalar; + type Output = G::Output; + + #[inline] + fn scalar_mul(&self, scalar: &Self::Scalar, compiler: &mut COM) -> Self::Output { + G::preprocessed_scalar_mul(&self.table, scalar, compiler) + } +} + +/// Elliptic-Curve Diffie Hellman Key Exchange +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +pub struct DiffieHellman<G> { + /// Base Generator + generator: G, +} + +impl<G> DiffieHellman<G> { /// Builds a new [`DiffieHellman`] protocol structure from `generator`. #[inline] pub fn new(generator: G) -> Self { - Self { - generator, - __: PhantomData, - } + Self { generator } } /// Returns a shared reference to the generator for this protocol. @@ -93,10 +233,9 @@ where } } -impl<G, COM> Constant<COM> for DiffieHellman<G, COM> +impl<G, COM> Constant<COM> for DiffieHellman<G> where - G: Group<COM> + Constant<COM>, - G::Type: Group, + G: Constant<COM>, { type Type = DiffieHellman<G::Type>; @@ -106,9 +245,9 @@ where } } -impl<G, COM> Decode for DiffieHellman<G, COM> +impl<G> Decode for DiffieHellman<G> where - G: Group<COM> + Decode, + G: Decode, { type Error = G::Error; @@ -121,9 +260,9 @@ where } } -impl<G, COM> Encode for DiffieHellman<G, COM> +impl<G> Encode for DiffieHellman<G> where - G: Group<COM> + Encode, + G: Encode, { #[inline] fn encode<W>(&self, writer: W) -> Result<(), W::Error> @@ -134,17 +273,18 @@ where } } -impl<G, COM> KeyAgreementScheme<COM> for DiffieHellman<G, COM> +impl<G, COM> KeyAgreementScheme<COM> for DiffieHellman<G> where - G: Group<COM>, + G: ScalarMul<COM>, + G::Output: ScalarMul<COM, Scalar = G::Scalar, Output = G::Output>, { type SecretKey = G::Scalar; - type PublicKey = G; - type SharedSecret = G; + type PublicKey = G::Output; + type SharedSecret = G::Output; #[inline] fn derive_in(&self, secret_key: &Self::SecretKey, compiler: &mut COM) -> Self::PublicKey { - self.agree_in(secret_key, &self.generator, compiler) + self.generator.scalar_mul(secret_key, compiler) } #[inline] @@ -158,9 +298,9 @@ where } } -impl<D, G, COM> Sample<D> for DiffieHellman<G, COM> +impl<D, G> Sample<D> for DiffieHellman<G> where - G: Group<COM> + Sample<D>, + G: Sample<D>, { #[inline] fn sample<R>(distribution: D, rng: &mut R) -> Self diff --git a/manta-crypto/src/encryption.rs b/manta-crypto/src/encryption.rs index e57055584..ba7623399 100644 --- a/manta-crypto/src/encryption.rs +++ b/manta-crypto/src/encryption.rs @@ -20,7 +20,7 @@ use crate::key::{KeyAgreementScheme, KeyDerivationFunction}; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; #[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; +use manta_util::serde::{Deserialize, Serialize}; /// Symmetric Key Encryption Scheme /// @@ -57,7 +57,11 @@ pub mod symmetric { use super::*; /// Mapped Symmetric Encryption Scheme - #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] + #[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde") + )] #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Map<S, P = <S as SymmetricKeyEncryptionScheme>::Plaintext>(PhantomData<(S, P)>) @@ -132,7 +136,11 @@ pub type PublicKey<H> = /// implemented. /// /// [`agree_derive`]: HybridPublicKeyEncryptionScheme::agree_derive -#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde") +)] #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Hybrid<K, S, F>(PhantomData<(K, S, F)>) @@ -181,6 +189,7 @@ where deserialize = "H::Ciphertext: Deserialize<'de>, PublicKey<H>: Deserialize<'de>", serialize = "H::Ciphertext: Serialize, PublicKey<H>: Serialize" ), + crate = "manta_util::serde", deny_unknown_fields ) )] @@ -259,6 +268,7 @@ where deserialize = "H::Plaintext: Deserialize<'de>, PublicKey<H>: Deserialize<'de>", serialize = "H::Plaintext: Serialize, PublicKey<H>: Serialize" ), + crate = "manta_util::serde", deny_unknown_fields ) )] diff --git a/manta-crypto/src/key.rs b/manta-crypto/src/key.rs index cbc39ab1a..a613b75c3 100644 --- a/manta-crypto/src/key.rs +++ b/manta-crypto/src/key.rs @@ -36,8 +36,15 @@ pub mod kdf { use super::*; use alloc::vec::Vec; + #[cfg(feature = "serde")] + use manta_util::serde::{Deserialize, Serialize}; + /// From Byte Slice Reference Adapter - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde") + )] #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct FromByteSliceRef<T, F>(PhantomData<(T, F)>) @@ -66,7 +73,11 @@ pub mod kdf { } /// From Byte Vector Adapter - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde") + )] #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct FromByteVector<T, F>(PhantomData<(T, F)>) diff --git a/manta-crypto/src/merkle_tree/forest.rs b/manta-crypto/src/merkle_tree/forest.rs index 808703435..6f8f649b9 100644 --- a/manta-crypto/src/merkle_tree/forest.rs +++ b/manta-crypto/src/merkle_tree/forest.rs @@ -39,7 +39,7 @@ use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use manta_util::{into_boxed_array_unchecked, persistance::Rollback, pointer::PointerFamily}; #[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; +use manta_util::serde::{Deserialize, Serialize}; /// Merkle Forest Configuration pub trait Configuration: tree::Configuration { @@ -182,6 +182,7 @@ where deserialize = "Parameters<C>: Deserialize<'de>, F: Deserialize<'de>", serialize = "Parameters<C>: Serialize, F: Serialize" ), + crate = "manta_util::serde", deny_unknown_fields ) )] @@ -388,7 +389,11 @@ where } /// [`SingleTree`] Merkle Forest Index -#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[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 SingleTreeIndex; @@ -409,7 +414,11 @@ impl From<SingleTreeIndex> for usize { } /// Single Tree Merkle Forest -#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[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 SingleTree<C, COM = ()>(PhantomData<(C, COM)>) where @@ -452,6 +461,7 @@ pub type TreeArrayMerkleForest<C, T, const N: usize> = MerkleForest<C, TreeArray derive(Deserialize, Serialize), serde( bound(deserialize = "T: Deserialize<'de>", serialize = "T: Serialize"), + crate = "manta_util::serde", deny_unknown_fields, ) )] @@ -474,7 +484,7 @@ where /// Typically, even for reasonable `N`, the size of this array is too big to fit on the stack, /// so we wrap it in a box to store on the heap. #[cfg_attr( - all(feature = "serde", feature = "serde_with"), + feature = "serde", serde(with = "serde_with::As::<Box<[serde_with::Same; N]>>") )] array: Box<[T; N]>, diff --git a/manta-crypto/src/merkle_tree/full.rs b/manta-crypto/src/merkle_tree/full.rs index 7f336397c..63c27b2d1 100644 --- a/manta-crypto/src/merkle_tree/full.rs +++ b/manta-crypto/src/merkle_tree/full.rs @@ -28,7 +28,7 @@ use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; #[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; +use manta_util::serde::{Deserialize, Serialize}; /// Full Merkle Tree Type pub type FullMerkleTree<C, M = BTreeMap<C>> = MerkleTree<C, Full<C, M>>; @@ -39,9 +39,14 @@ pub type FullMerkleTree<C, M = BTreeMap<C>> = MerkleTree<C, Full<C, M>>; derive(Deserialize, Serialize), serde( bound( - deserialize = "LeafDigest<C>: Deserialize<'de>, InnerDigest<C>: Deserialize<'de>, M: Deserialize<'de>", + deserialize = r" + LeafDigest<C>: Deserialize<'de>, + InnerDigest<C>: Deserialize<'de>, + M: Deserialize<'de> + ", serialize = "LeafDigest<C>: Serialize, InnerDigest<C>: Serialize, M: Serialize" ), + crate = "manta_util::serde", deny_unknown_fields ) )] diff --git a/manta-crypto/src/merkle_tree/inner_tree.rs b/manta-crypto/src/merkle_tree/inner_tree.rs index 69d206f61..aa37001a9 100644 --- a/manta-crypto/src/merkle_tree/inner_tree.rs +++ b/manta-crypto/src/merkle_tree/inner_tree.rs @@ -27,17 +27,17 @@ use crate::merkle_tree::{ use alloc::collections::btree_map; use core::{fmt::Debug, hash::Hash, iter::FusedIterator, marker::PhantomData, ops::Index}; +#[cfg(feature = "serde")] +use manta_util::serde::{Deserialize, Serialize}; + #[cfg(feature = "std")] use std::{collections::hash_map, hash::BuildHasher}; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - /// Inner Tree Node #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde(deny_unknown_fields) + serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct InnerNode { @@ -146,7 +146,7 @@ impl From<InnerNode> for Node { #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde(deny_unknown_fields) + serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct InnerNodeIter { @@ -333,6 +333,7 @@ where deserialize = "InnerDigest<C>: Deserialize<'de>", serialize = "InnerDigest<C>: Serialize" ), + crate = "manta_util::serde", deny_unknown_fields ) )] @@ -379,23 +380,10 @@ where #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde( - bound( - deserialize = "M: Deserialize<'de>, S: Deserialize<'de>", - serialize = "M: Serialize, S: Serialize" - ), - deny_unknown_fields - ) + serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "M: Clone, S: Clone"), - Debug(bound = "M: Debug, S: Debug"), - Default(bound = "M: Default, S: Default"), - Eq(bound = "M: Eq, S: Eq"), - Hash(bound = "M: Hash, S: Hash"), - PartialEq(bound = "M: PartialEq, S: PartialEq") -)] +#[derivative(Clone, Debug, Default, Eq, Hash, PartialEq)] pub struct InnerTree<C, M = BTreeMap<C>, S = Sentinel<C>> where C: Configuration + ?Sized, @@ -663,6 +651,7 @@ where deserialize = "M: Deserialize<'de>, S: Deserialize<'de>", serialize = "M: Serialize, S: Serialize" ), + crate = "manta_util::serde", deny_unknown_fields ) )] diff --git a/manta-crypto/src/merkle_tree/node.rs b/manta-crypto/src/merkle_tree/node.rs index 428dbc7bf..55bede1f0 100644 --- a/manta-crypto/src/merkle_tree/node.rs +++ b/manta-crypto/src/merkle_tree/node.rs @@ -23,13 +23,13 @@ use core::{ }; #[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; +use manta_util::serde::{Deserialize, Serialize}; /// Parity of a Subtree #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde(deny_unknown_fields) + serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub enum Parity { @@ -157,7 +157,7 @@ impl Default for Parity { #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde(deny_unknown_fields) + serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct Node<Idx = usize>( @@ -381,7 +381,7 @@ where #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde(deny_unknown_fields) + serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct NodeParents { diff --git a/manta-crypto/src/merkle_tree/partial.rs b/manta-crypto/src/merkle_tree/partial.rs index eb2190063..e5791b21f 100644 --- a/manta-crypto/src/merkle_tree/partial.rs +++ b/manta-crypto/src/merkle_tree/partial.rs @@ -28,7 +28,7 @@ use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; #[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; +use manta_util::serde::{Deserialize, Serialize}; /// Partial Merkle Tree Type pub type PartialMerkleTree<C, M = BTreeMap<C>> = MerkleTree<C, Partial<C, M>>; @@ -42,6 +42,7 @@ pub type PartialMerkleTree<C, M = BTreeMap<C>> = MerkleTree<C, Partial<C, M>>; deserialize = "LeafDigest<C>: Deserialize<'de>, InnerDigest<C>: Deserialize<'de>, M: Deserialize<'de>", serialize = "LeafDigest<C>: Serialize, InnerDigest<C>: Serialize, M: Serialize" ), + crate = "manta_util::serde", deny_unknown_fields ) )] diff --git a/manta-crypto/src/merkle_tree/path.rs b/manta-crypto/src/merkle_tree/path.rs index c41ff07f4..f2714275d 100644 --- a/manta-crypto/src/merkle_tree/path.rs +++ b/manta-crypto/src/merkle_tree/path.rs @@ -35,7 +35,7 @@ use core::{ }; #[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; +use manta_util::serde::{Deserialize, Serialize}; pub(super) mod prelude { #[doc(inline)] @@ -51,6 +51,7 @@ pub(super) mod prelude { deserialize = "InnerDigest<C>: Deserialize<'de>", serialize = "InnerDigest<C>: Serialize" ), + crate = "manta_util::serde", deny_unknown_fields ) )] @@ -246,6 +247,7 @@ where deserialize = "InnerDigest<C>: Deserialize<'de>", serialize = "InnerDigest<C>: Serialize" ), + crate = "manta_util::serde", deny_unknown_fields ) )] @@ -625,6 +627,7 @@ impl<C> FusedIterator for CurrentInnerPathNodeIter<C> where C: Configuration + ? deserialize = "LeafDigest<C>: Deserialize<'de>, InnerDigest<C>: Deserialize<'de>", serialize = "LeafDigest<C>: Serialize, InnerDigest<C>: Serialize" ), + crate = "manta_util::serde", deny_unknown_fields ) )] @@ -774,6 +777,7 @@ where deserialize = "LeafDigest<C>: Deserialize<'de>, InnerDigest<C>: Deserialize<'de>", serialize = "LeafDigest<C>: Serialize, InnerDigest<C>: Serialize" ), + crate = "manta_util::serde", deny_unknown_fields ) )] diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index 0fef1e5e8..9f30d953c 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -44,7 +44,7 @@ use manta_util::{ }; #[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; +use manta_util::serde::{Deserialize, Serialize}; /// Merkle Tree Leaf Hash pub trait LeafHash<COM = ()> { @@ -83,7 +83,7 @@ pub trait LeafHash<COM = ()> { #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde(deny_unknown_fields) + serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -193,7 +193,7 @@ pub trait Configuration<COM = ()>: HashConfiguration<COM> { #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde(deny_unknown_fields) + serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Config<C, COM, const HEIGHT: usize>(PhantomData<(COM, C)>) @@ -432,7 +432,7 @@ where #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde(deny_unknown_fields) + serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum PathError { @@ -538,6 +538,7 @@ where InnerHashParameters<C, COM>: Serialize, " ), + crate = "manta_util::serde", deny_unknown_fields ) )] @@ -789,6 +790,7 @@ where deserialize = "Parameters<C>: Deserialize<'de>, T: Deserialize<'de>", serialize = "Parameters<C>: Serialize, T: Serialize" ), + crate = "manta_util::serde", deny_unknown_fields ) )] diff --git a/manta-crypto/src/rand.rs b/manta-crypto/src/rand.rs index 58d43cee1..633ddb6e7 100644 --- a/manta-crypto/src/rand.rs +++ b/manta-crypto/src/rand.rs @@ -25,11 +25,6 @@ use manta_util::into_array_unchecked; pub use rand_core::{block, CryptoRng, Error, RngCore, SeedableRng}; /// Random Number Generator Sized Wrapper -#[cfg_attr( - feature = "serde", - derive(serde::Serialize), - serde(deny_unknown_fields) -)] #[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct SizedRng<'r, R>( /// Mutable Reference to Random Number Generator @@ -79,23 +74,8 @@ where } /// Seed Into Random Number Generator -#[cfg_attr( - feature = "serde", - derive(serde::Deserialize, serde::Serialize), - serde(deny_unknown_fields) -)] #[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "R: Clone"), - Copy(bound = "R: Copy"), - Debug(bound = "R: Debug"), - Default(bound = "R: Default"), - Eq(bound = "R: Eq"), - Hash(bound = "R: Hash"), - Ord(bound = "R: Ord"), - PartialEq(bound = "R: PartialEq"), - PartialOrd(bound = "R: PartialOrd") -)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct SeedIntoRng<S, R> { /// Inner Rng inner: R, @@ -208,7 +188,6 @@ where } /// Standard Distribution -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Standard; diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 8599a2deb..1d6642820 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -85,13 +85,14 @@ blake2 = { version = "0.10.0", default-features = false } derivative = { version = "2.2.0", default-features = false } generic-array = { version = "0.14.4", default-features = false } indexmap = { version = "1.8.0", default-features = false, optional = true } -manta-accounting = { path = "../manta-accounting", default-features = false } -manta-crypto = { path = "../manta-crypto", default-features = false, features = ["getrandom"] } -manta-util = { path = "../manta-util", default-features = false } +manta-accounting = { path = "../manta-accounting", default-features = false, features = ["serde"] } +manta-crypto = { path = "../manta-crypto", default-features = false, features = ["getrandom", "serde"] } +manta-util = { path = "../manta-util", default-features = false, features = ["serde-alloc"] } parking_lot = { version = "0.11.2", optional = true, default-features = false } rand = { version = "0.8.4", optional = true } rand_chacha = { version = "0.3.1", default-features = false } rayon = { version = "1.5.1", optional = true, default-features = false } +reqwest = { version = "0.11.9", optional = true, default-features = false, features = ["blocking", "json"] } scale-codec = { package = "parity-scale-codec", version = "2.3.1", default-features = false, features = ["derive"] } statrs = { version = "0.15.0", optional = true, default-features = false } tungstenite = { version = "0.16.0", optional = true, default-features = false, features = ["native-tls"] } diff --git a/manta-pay/src/bin/generate_parameters.rs b/manta-pay/src/bin/generate_parameters.rs index 25a8f0bbd..6feeaa9c3 100644 --- a/manta-pay/src/bin/generate_parameters.rs +++ b/manta-pay/src/bin/generate_parameters.rs @@ -126,7 +126,7 @@ pub fn main() -> io::Result<()> { let cs = Mint::unknown_constraints(full_parameters); let (proving_context, verifying_context) = - ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); + ProofSystem::generate_context(&(), cs, &mut rng).unwrap(); proving_context .encode(IoWriter( OpenOptions::new() @@ -146,7 +146,7 @@ pub fn main() -> io::Result<()> { let cs = PrivateTransfer::unknown_constraints(full_parameters); let (proving_context, verifying_context) = - ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); + ProofSystem::generate_context(&(), cs, &mut rng).unwrap(); proving_context .encode(IoWriter( OpenOptions::new() @@ -166,7 +166,7 @@ pub fn main() -> io::Result<()> { let cs = Reclaim::unknown_constraints(full_parameters); let (proving_context, verifying_context) = - ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); + ProofSystem::generate_context(&(), cs, &mut rng).unwrap(); proving_context .encode(IoWriter( OpenOptions::new() diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index c8c09df18..1052e3c69 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -122,7 +122,7 @@ pub type SecretKey = <KeyAgreementScheme as key::KeyAgreementScheme>::SecretKey; pub type PublicKey = <KeyAgreementScheme as key::KeyAgreementScheme>::PublicKey; /// Key Agreement Scheme Variable Type -pub type KeyAgreementSchemeVar = DiffieHellman<GroupVar, Compiler>; +pub type KeyAgreementSchemeVar = DiffieHellman<GroupVar>; /// Unspent Transaction Output Type pub type Utxo = Fp<ConstraintField>; diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs index e30c6d197..cb923a695 100644 --- a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs +++ b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs @@ -560,7 +560,6 @@ pub mod groth16 { type VerifyingContext = VerifyingContext<E>; type Input = Vec<E::Fr>; type Proof = Proof<E>; - type Verification = bool; type Error = SynthesisError; #[inline] @@ -575,8 +574,8 @@ pub mod groth16 { #[inline] fn generate_context<R>( - cs: Self::ConstraintSystem, public_parameters: &Self::PublicParameters, + cs: Self::ConstraintSystem, rng: &mut R, ) -> Result<(Self::ProvingContext, Self::VerifyingContext), Self::Error> where @@ -595,8 +594,8 @@ pub mod groth16 { #[inline] fn prove<R>( - cs: Self::ConstraintSystem, context: &Self::ProvingContext, + cs: Self::ConstraintSystem, rng: &mut R, ) -> Result<Self::Proof, Self::Error> where @@ -612,10 +611,10 @@ pub mod groth16 { #[inline] fn verify( + context: &Self::VerifyingContext, input: &Self::Input, proof: &Self::Proof, - context: &Self::VerifyingContext, - ) -> Result<Self::Verification, Self::Error> { + ) -> Result<bool, Self::Error> { ArkGroth16::verify_with_processed_vk(&context.0, input, &proof.0) } } diff --git a/manta-pay/src/crypto/ecc.rs b/manta-pay/src/crypto/ecc.rs index 861260e00..35167be56 100644 --- a/manta-pay/src/crypto/ecc.rs +++ b/manta-pay/src/crypto/ecc.rs @@ -188,19 +188,15 @@ pub mod arkworks { } } - impl<C> ecc::Group for Group<C> + impl<C> ecc::ScalarMul for Group<C> where C: ProjectiveCurve, { type Scalar = Scalar<C>; + type Output = Self; #[inline] - fn add(&self, rhs: &Self, _: &mut ()) -> Self { - Self(self.0 + rhs.0) - } - - #[inline] - fn scalar_mul(&self, scalar: &Self::Scalar, _: &mut ()) -> Self { + fn scalar_mul(&self, scalar: &Self::Scalar, _: &mut ()) -> Self::Output { Self(self.0.mul(scalar.0.into_repr()).into()) } } @@ -309,21 +305,16 @@ pub mod arkworks { } } - impl<C, CV> ecc::Group<Compiler<C>> for GroupVar<C, CV> + impl<C, CV> ecc::ScalarMul<Compiler<C>> for GroupVar<C, CV> where C: ProjectiveCurve, CV: CurveVar<C, ConstraintField<C>>, { type Scalar = ScalarVar<C, CV>; + type Output = Self; #[inline] - fn add(&self, rhs: &Self, compiler: &mut Compiler<C>) -> Self { - let _ = compiler; - Self(self.0.clone() + &rhs.0, PhantomData) - } - - #[inline] - fn scalar_mul(&self, scalar: &Self::Scalar, compiler: &mut Compiler<C>) -> Self { + fn scalar_mul(&self, scalar: &Self::Scalar, compiler: &mut Compiler<C>) -> Self::Output { let _ = compiler; Self( self.0 diff --git a/manta-pay/src/test/ledger.rs b/manta-pay/src/test/ledger.rs index c60cc4e63..527aba8ec 100644 --- a/manta-pay/src/test/ledger.rs +++ b/manta-pay/src/test/ledger.rs @@ -290,9 +290,9 @@ impl TransferLedger<Config> for Ledger { sinks.len(), )?); ProofSystem::verify( + verifying_context, &TransferPostingKey::generate_proof_input(asset_id, sources, senders, receivers, sinks), &proof, - verifying_context, ) .ok()? .then(move || (Wrap(()), ())) diff --git a/manta-pay/src/test/transfer.rs b/manta-pay/src/test/transfer.rs index a4d53f4b8..df35b673c 100644 --- a/manta-pay/src/test/transfer.rs +++ b/manta-pay/src/test/transfer.rs @@ -34,7 +34,7 @@ fn sample_mint_context() { let mut rng = thread_rng(); let cs = Mint::unknown_constraints(FullParameters::new(&rng.gen(), &rng.gen())); println!("Mint: {:?}", cs.measure()); - ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); + ProofSystem::generate_context(&(), cs, &mut rng).unwrap(); } /// Tests the generation of proving/verifying contexts for [`PrivateTransfer`]. @@ -43,7 +43,7 @@ fn sample_private_transfer_context() { let mut rng = thread_rng(); let cs = PrivateTransfer::unknown_constraints(FullParameters::new(&rng.gen(), &rng.gen())); println!("PrivateTransfer: {:?}", cs.measure()); - ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); + ProofSystem::generate_context(&(), cs, &mut rng).unwrap(); } /// Tests the generation of proving/verifying contexts for [`Reclaim`]. @@ -52,7 +52,7 @@ fn sample_reclaim_context() { let mut rng = thread_rng(); let cs = Reclaim::unknown_constraints(FullParameters::new(&rng.gen(), &rng.gen())); println!("Reclaim: {:?}", cs.measure()); - ProofSystem::generate_context(cs, &(), &mut rng).unwrap(); + ProofSystem::generate_context(&(), cs, &mut rng).unwrap(); } /// Tests the generation of a [`Mint`]. diff --git a/manta-pay/src/wallet/signer/client/http.rs b/manta-pay/src/wallet/signer/client/http.rs index ef9fe3171..03bbf7f1b 100644 --- a/manta-pay/src/wallet/signer/client/http.rs +++ b/manta-pay/src/wallet/signer/client/http.rs @@ -15,3 +15,106 @@ // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. //! Signer HTTP Client Implementation + +use crate::config::Config; +use manta_accounting::{ + transfer::{canonical::Transaction, ReceivingKey}, + wallet::signer::{self, SignError, SignResponse, SyncError, SyncRequest, SyncResponse}, +}; +use manta_util::serde::Serialize; +use reqwest::{ + blocking::{Client as BaseClient, Response}, + Error, IntoUrl, Method, Url, +}; + +/// HTTP Client +pub struct Client { + /// Server URL + server_url: Url, + + /// Base HTTP Client + client: BaseClient, +} + +impl Client { + /// Builds a new HTTP [`Client`] that connects to `server_url`. + #[inline] + pub fn new<U>(server_url: U) -> Result<Self, Error> + where + U: IntoUrl, + { + Ok(Self { + client: BaseClient::builder().build()?, + server_url: server_url.into_url()?, + }) + } + + /// Sends a new request of type `command` with body `request`. + #[inline] + fn request<T>(&self, method: Method, command: &str, request: T) -> Result<Response, Error> + where + T: Serialize, + { + self.client + .request( + method, + self.server_url + .join(command) + .expect("This error branch is not allowed to happen."), + ) + .json(&request) + .send() + } + + /// Sends a GET request of type `command` with body `request`. + #[inline] + fn get<T>(&self, command: &str, request: T) -> Result<Response, Error> + where + T: Serialize, + { + self.request(Method::GET, command, request) + } + + /// Sends a POST request of type `command` with body `request`. + #[inline] + fn post<T>(&self, command: &str, request: T) -> Result<Response, Error> + where + T: Serialize, + { + self.request(Method::POST, command, request) + } +} + +impl signer::Connection<Config> for Client { + type Error = Error; + + #[inline] + fn sync( + &mut self, + request: SyncRequest<Config>, + ) -> Result<Result<SyncResponse, SyncError>, Self::Error> { + // NOTE: The synchronization command modifies the signer so it must be a POST command + // to match the HTTP semantics. + // TODO: Ok(self.post("sync", request)?.json()?) + todo!() + } + + #[inline] + fn sign( + &mut self, + transaction: Transaction<Config>, + ) -> Result<Result<SignResponse<Config>, SignError<Config>>, Self::Error> { + // NOTE: The signing command does not modify the signer so it must be a GET command to match + // the HTTP semantics. + // TODO: Ok(self.get("sign", transaction)?.json()?) + todo!() + } + + #[inline] + fn receiving_key(&mut self) -> Result<ReceivingKey<Config>, Self::Error> { + // NOTE: The receiving key command modifies the signer so it must be a POST command to match + // the HTTP semantics. + // TODO: Ok(self.post("receivingKey", ())?.json()?) + todo!() + } +} diff --git a/manta-pay/src/wallet/signer/client/mod.rs b/manta-pay/src/wallet/signer/client/mod.rs index dcf69e952..d2b0f1f29 100644 --- a/manta-pay/src/wallet/signer/client/mod.rs +++ b/manta-pay/src/wallet/signer/client/mod.rs @@ -16,6 +16,8 @@ //! Signer Client Implementations +#[cfg(feature = "reqwest")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "reqwest")))] pub mod http; #[cfg(feature = "tungstenite")] diff --git a/manta-pay/src/wallet/signer/client/websocket.rs b/manta-pay/src/wallet/signer/client/websocket.rs index a783565a0..6afbbb93c 100644 --- a/manta-pay/src/wallet/signer/client/websocket.rs +++ b/manta-pay/src/wallet/signer/client/websocket.rs @@ -21,7 +21,6 @@ use manta_accounting::{ transfer::{canonical::Transaction, ReceivingKey}, wallet::signer::{self, SignError, SignResponse, SyncError, SyncRequest, SyncResponse}, }; -use manta_util::codec::Encode; use std::net::TcpStream; use tungstenite::{ client::IntoClientRequest, diff --git a/manta-util/Cargo.toml b/manta-util/Cargo.toml index 25e27bcf2..8452d3ea5 100644 --- a/manta-util/Cargo.toml +++ b/manta-util/Cargo.toml @@ -28,6 +28,11 @@ maintenance = { status = "actively-developed" } # Allocation alloc = [] +# Serde for Alloc Types +serde-alloc = ["alloc", "serde/alloc"] + # Standard Library std = ["alloc"] +[dependencies] +serde = { version = "1.0.136", optional = true, default-features = false, features = ["derive"] } diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index 0c0ba0cbe..ab604276f 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -37,6 +37,9 @@ pub mod pointer; pub use array::*; pub use sealed::*; +#[doc(inline)] +pub use serde; + /// Implements [`From`]`<$from>` for an enum `$to`, choosing the `$kind` variant. // TODO: add `where` clauses #[macro_export] From fdf63b774aeebb38622fa06b092b83741220d424 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sat, 5 Feb 2022 18:35:23 -0500 Subject: [PATCH 218/275] fix: add Box import --- manta-accounting/src/fs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manta-accounting/src/fs.rs b/manta-accounting/src/fs.rs index 2d3d49018..a4390b80b 100644 --- a/manta-accounting/src/fs.rs +++ b/manta-accounting/src/fs.rs @@ -16,7 +16,7 @@ //! Encrypted Filesystem Primitives -use alloc::vec::Vec; +use alloc::{boxed::Box, vec::Vec}; use core::{fmt::Debug, hash::Hash}; /// Open Options From 929dd97a71e0990f0902e41ddc01702f694fe6a3 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sat, 5 Feb 2022 18:37:36 -0500 Subject: [PATCH 219/275] fix: add Vec import --- manta-crypto/src/ecc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manta-crypto/src/ecc.rs b/manta-crypto/src/ecc.rs index 38eaaabef..aa7b9c6ab 100644 --- a/manta-crypto/src/ecc.rs +++ b/manta-crypto/src/ecc.rs @@ -23,7 +23,7 @@ use crate::{ key::KeyAgreementScheme, rand::{CryptoRng, RngCore, Sample}, }; -use alloc::boxed::Box; +use alloc::{boxed::Box, vec::Vec}; use manta_util::codec::{Decode, DecodeError, Encode, Read, Write}; #[cfg(feature = "serde")] From 05733f1bc40c45d3fd5f619e82e569b66ef3b5c5 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sat, 5 Feb 2022 19:30:30 -0500 Subject: [PATCH 220/275] fix: simplify and correct dependency/feature structure --- manta-pay/Cargo.toml | 22 ++++++++++++++-------- manta-pay/src/lib.rs | 21 ++++----------------- manta-util/src/lib.rs | 1 + 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 1d6642820..d7ae297f2 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -52,6 +52,15 @@ groth16 = ["arkworks", "ark-groth16"] # TODO: Enable PLONK ZKP System # TODO: plonk = ["zk-garage-plonk"] +# Serde +serde = ["manta-accounting/serde", "manta-crypto/serde", "manta-util/serde-alloc"] + +# Simulation Framework +simulation = ["rand", "statrs"] + +# Standard Library +std = ["manta-accounting/std", "manta-util/std"] + # Testing Frameworks test = [ "indexmap", @@ -62,11 +71,8 @@ test = [ "rayon", ] -# Simulation Framework -simulation = ["rand", "statrs"] - -# Standard Library -std = ["manta-accounting/std", "manta-util/std"] +# Wallet +wallet = ["bip32", "manta-crypto/getrandom", "std"] [dependencies] aes-gcm = { version = "0.9.4", default-features = false, features = ["aes", "alloc"] } @@ -85,9 +91,9 @@ blake2 = { version = "0.10.0", default-features = false } derivative = { version = "2.2.0", default-features = false } generic-array = { version = "0.14.4", default-features = false } indexmap = { version = "1.8.0", default-features = false, optional = true } -manta-accounting = { path = "../manta-accounting", default-features = false, features = ["serde"] } -manta-crypto = { path = "../manta-crypto", default-features = false, features = ["getrandom", "serde"] } -manta-util = { path = "../manta-util", default-features = false, features = ["serde-alloc"] } +manta-accounting = { path = "../manta-accounting", default-features = false } +manta-crypto = { path = "../manta-crypto", default-features = false } +manta-util = { path = "../manta-util", default-features = false } parking_lot = { version = "0.11.2", optional = true, default-features = false } rand = { version = "0.8.4", optional = true } rand_chacha = { version = "0.3.1", default-features = false } diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index f665553a5..e96ad1298 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -32,23 +32,10 @@ pub mod crypto; #[cfg_attr(doc_cfg, doc(cfg(feature = "bip32")))] pub mod key; -#[cfg(all(feature = "arkworks", feature = "groth16"))] -#[cfg_attr(doc_cfg, doc(cfg(all(feature = "arkworks", feature = "groth16"))))] +#[cfg(feature = "groth16")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "groth16")))] pub mod config; -#[cfg(all( - feature = "arkworks", - feature = "groth16", - feature = "bip32", - feature = "std", -))] -#[cfg_attr( - doc_cfg, - doc(cfg(all( - feature = "arkworks", - feature = "groth16", - feature = "bip32", - feature = "std", - ))) -)] +#[cfg(all(feature = "groth16", feature = "wallet",))] +#[cfg_attr(doc_cfg, doc(cfg(all(feature = "groth16", feature = "wallet",))))] pub mod wallet; diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index ab604276f..a3bfcc1d6 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -37,6 +37,7 @@ pub mod pointer; pub use array::*; pub use sealed::*; +#[cfg(feature = "serde")] #[doc(inline)] pub use serde; From c3dcf060982d1faa4f4b5381f65461a0b7de39d6 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 6 Feb 2022 16:46:19 -0500 Subject: [PATCH 221/275] feat: improve wallet error states and docs --- manta-accounting/src/wallet/signer.rs | 97 ++++++++++++-- manta-accounting/src/wallet/state.rs | 176 +++++++++++++++++++------- 2 files changed, 214 insertions(+), 59 deletions(-) diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 6ecb29637..933c3f7de 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -21,17 +21,12 @@ // TODO: Should have a mode on the signer where we return a generic error which reveals no detail // about what went wrong during signing. The kind of error returned from a signing could // reveal information about the internal state (privacy leak, not a secrecy leak). -// TODO: Review which subset of the errors are actually recoverable or not. For example, the -// non-existence of a membership proof may be a non-recoverable error since it only relies -// on invariants that must be maintained by the `Signer` and not by external sources; i.e. -// even if the `Signer` is fed malicious UTXOs, it should not error when looking for -// membership proofs and so if this error condition is met, the `Signer` has a bug in it. +// TODO: Review which subset of the errors are actually recoverable or not. // TODO: Setup multi-account wallets using `crate::key::AccountTable`. -// TODO: Move `sync` to a stream-based algorithm instead of iterator-based. +// TODO: Move `sync` to a streaming algorithm. // TODO: Save/Load `SignerState` to/from disk. // TODO: Add self-destruct feature for clearing all secret and private data. // TODO: Compress the `SyncResponse` data before sending (improves privacy and bandwidth). -// TODO: Should we split the errors into two groups, one for `sync` and one for `sign`? use crate::{ asset::{Asset, AssetId, AssetMap, AssetValue}, @@ -50,11 +45,9 @@ use crate::{ }, }; use alloc::{vec, vec::Vec}; -use core::{convert::Infallible, fmt::Debug}; +use core::{convert::Infallible, fmt::Debug, hash::Hash}; use manta_crypto::{ - accumulator::{ - Accumulator, ConstantCapacityAccumulator, ExactSizeAccumulator, OptimizedAccumulator, - }, + accumulator::{Accumulator, ExactSizeAccumulator, OptimizedAccumulator}, encryption::DecryptedMessage, rand::{CryptoRng, FromEntropy, Rand, RngCore}, }; @@ -98,6 +91,34 @@ where } /// Signer Synchronization Request +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = r" + Utxo<C>: Deserialize<'de>, + EncryptedNote<C>: Deserialize<'de>, + VoidNumber<C>: Deserialize<'de> + ", + serialize = r" + Utxo<C>: Serialize, + EncryptedNote<C>: Serialize, + VoidNumber<C>: Serialize + ", + ), + crate = "manta_util::serde", + deny_unknown_fields + ) +)] +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "Utxo<C>: Clone, EncryptedNote<C>: Clone, VoidNumber<C>: Clone"), + Debug(bound = "Utxo<C>: Debug, EncryptedNote<C>: Debug, VoidNumber<C>: Debug"), + Eq(bound = "Utxo<C>: Eq, EncryptedNote<C>: Eq, VoidNumber<C>: Eq"), + Hash(bound = "Utxo<C>: Hash, EncryptedNote<C>: Hash, VoidNumber<C>: Hash"), + PartialEq(bound = "Utxo<C>: PartialEq, EncryptedNote<C>: PartialEq, VoidNumber<C>: PartialEq") +)] pub struct SyncRequest<C> where C: transfer::Configuration, @@ -116,6 +137,11 @@ where /// /// This `enum` is created by the [`sync`](Connection::sync) method on [`Connection`]. /// See its documentation for more. +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub enum SyncResponse { /// Partial Update @@ -145,7 +171,12 @@ pub enum SyncResponse { /// /// This `enum` is the error state for the [`sync`](Connection::sync) method on [`Connection`]. /// See its documentation for more. -#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum SyncError { /// Inconsistent Synchronization InconsistentSynchronization { @@ -158,6 +189,26 @@ pub enum SyncError { /// /// This `struct` is created by the [`sign`](Connection::sign) method on [`Connection`]. /// See its documentation for more. +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "TransferPost<C>: Deserialize<'de>", + serialize = "TransferPost<C>: Serialize" + ), + crate = "manta_util::serde", + deny_unknown_fields + ) +)] +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "TransferPost<C>: Clone"), + Debug(bound = "TransferPost<C>: Debug"), + Eq(bound = "TransferPost<C>: Eq"), + Hash(bound = "TransferPost<C>: Hash"), + PartialEq(bound = "TransferPost<C>: PartialEq") +)] pub struct SignResponse<C> where C: transfer::Configuration, @@ -181,8 +232,27 @@ where /// /// This `enum` is the error state for the [`sign`](Connection::sign) method on [`Connection`]. /// See its documentation for more. +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "ProofSystemError<C>: Deserialize<'de>", + serialize = "ProofSystemError<C>: Serialize" + ), + crate = "manta_util::serde", + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] -#[derivative(Debug(bound = "ProofSystemError<C>: Debug"))] +#[derivative( + Clone(bound = "ProofSystemError<C>: Clone"), + Copy(bound = "ProofSystemError<C>: Copy"), + Debug(bound = "ProofSystemError<C>: Debug"), + Eq(bound = "ProofSystemError<C>: Eq"), + Hash(bound = "ProofSystemError<C>: Hash"), + PartialEq(bound = "ProofSystemError<C>: PartialEq") +)] pub enum SignError<C> where C: transfer::Configuration, @@ -206,7 +276,6 @@ pub trait Configuration: transfer::Configuration { /// [`Utxo`] Accumulator Type type UtxoSet: Accumulator<Item = Self::Utxo, Model = Self::UtxoSetModel> - + ConstantCapacityAccumulator + ExactSizeAccumulator + OptimizedAccumulator + Rollback; diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 1c3e22fea..bc0ee55ef 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -30,6 +30,9 @@ use crate::{ use alloc::collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}; use core::{fmt::Debug, marker::PhantomData}; +#[cfg(feature = "serde")] +use manta_util::serde::{Deserialize, Serialize}; + #[cfg(feature = "std")] use std::{ collections::hash_map::{Entry as HashMapEntry, HashMap, RandomState}, @@ -60,29 +63,22 @@ pub trait BalanceState: Default { assets.into_iter().for_each(move |a| self.deposit(a)); } - /// Withdraws `asset` from the balance state without checking if it would overdraw. - /// - /// # Panics - /// - /// This method does not check if withdrawing `asset` from the balance state would cause an - /// overdraw, but if it were to overdraw, this method must panic. - fn withdraw_unchecked(&mut self, asset: Asset); + /// Withdraws `asset` from the balance state returning `false` if it would overdraw the balance. + fn withdraw(&mut self, asset: Asset) -> bool; - /// Withdraws every asset in `assets` from the balance state without checking if it would - /// overdraw. - /// - /// # Panics - /// - /// This method does not check if withdrawing `asset` from the balance state would cause an - /// overdraw, but if it were to overdraw, this method must panic. + /// Withdraws every asset in `assets` from the balance state, returning `false` if it would + /// overdraw the balance. #[inline] - fn withdraw_all_unchecked<I>(&mut self, assets: I) + fn withdraw_all<I>(&mut self, assets: I) -> bool where I: IntoIterator<Item = Asset>, { - assets - .into_iter() - .for_each(move |a| self.withdraw_unchecked(a)) + for asset in AssetList::from_iter(assets) { + if !self.withdraw(asset) { + return false; + } + } + true } /// Clears the entire balance state. @@ -101,8 +97,8 @@ impl BalanceState for AssetList { } #[inline] - fn withdraw_unchecked(&mut self, asset: Asset) { - self.withdraw_unchecked(asset) + fn withdraw(&mut self, asset: Asset) -> bool { + self.withdraw(asset) } #[inline] @@ -111,19 +107,18 @@ impl BalanceState for AssetList { } } -/// Performs an unchecked withdraw on `balance`, panicking on overflow. +/// Performs a withdraw on `balance` returning `false` if it would overflow. #[inline] -fn withdraw_unchecked(balance: Option<&mut AssetValue>, withdraw: AssetValue) { - let balance = match balance { - Some(balance) => balance, - _ => panic!("Trying to withdraw `{:?}` from a zero balance.", withdraw), - }; - *balance = match balance.checked_sub(withdraw) { - Some(balance) => balance, - _ => panic!( - "Overdrawn balance state. Tried to subtract `{:?}` from `{:?}`.", - withdraw, balance - ), +fn withdraw(balance: Option<&mut AssetValue>, withdraw: AssetValue) -> bool { + match balance { + Some(balance) => { + *balance = match balance.checked_sub(withdraw) { + Some(balance) => balance, + _ => return false, + }; + true + } + _ => false, } } @@ -149,9 +144,11 @@ macro_rules! impl_balance_state_map_body { } #[inline] - fn withdraw_unchecked(&mut self, asset: Asset) { + fn withdraw(&mut self, asset: Asset) -> bool { if !asset.is_zero() { - withdraw_unchecked(self.get_mut(&asset.id), asset.value); + withdraw(self.get_mut(&asset.id), asset.value) + } else { + true } } @@ -214,9 +211,10 @@ where S: signer::Connection<C>, B: BalanceState, { - /// Builds a new [`Wallet`]. + /// Builds a new [`Wallet`] without checking if `ledger`, `checkpoint`, `signer`, and `assets` + /// are properly synchronized. #[inline] - pub fn new(ledger: L, checkpoint: L::Checkpoint, signer: S, assets: B) -> Self { + fn new_unchecked(ledger: L, checkpoint: L::Checkpoint, signer: S, assets: B) -> Self { Self { ledger, checkpoint, @@ -226,10 +224,20 @@ where } } - /// Starts a new [`Wallet`] from `signer` and `ledger` connections. + /// Starts a new [`Wallet`] from existing `signer` and `ledger` connections. + /// + /// # Setting Up the Wallet + /// + /// Creating a [`Wallet`] using this method should be followed with a call to [`sync`] or + /// [`recover`] to retrieve the current checkpoint and balance for this [`Wallet`]. If the + /// backing `signer` is known to be already initialized, a call to [`sync`] is enough, + /// otherwise, a call to [`recover`] is necessary to retrieve the full balance state. + /// + /// [`sync`]: Self::sync + /// [`recover`]: Self::recover #[inline] - pub fn empty(ledger: L, signer: S) -> Self { - Self::new(ledger, Default::default(), signer, Default::default()) + pub fn new(ledger: L, signer: S) -> Self { + Self::new_unchecked(ledger, Default::default(), signer, Default::default()) } /// Returns the current balance associated with this `id`. @@ -263,7 +271,34 @@ where &self.checkpoint } + /// Resets `self` to the default checkpoint and no balance. A call to this method should be + /// followed by a call to [`sync`](Self::sync) to retrieve the correct checkpoint and balance. + /// + /// # Note + /// + /// This is not a "full wallet recovery" which would involve resetting the signer as well as + /// this wallet state. See the [`recover`](Self::recover) method for more. + #[inline] + pub fn reset(&mut self) { + self.checkpoint = Default::default(); + self.assets = Default::default(); + } + + /// Performs full wallet recovery. + #[inline] + pub fn recover(&mut self) { + // TODO: Can we just call `sync` with some extra flag? + todo!() + } + /// Pulls data from the `ledger`, synchronizing the wallet and balance state. + /// + /// # Failure Conditions + /// + /// This method returns an element of type [`Error`] on failure, which can result from any + /// number of synchronization issues between the wallet, the ledger, and the signer. See the + /// [`InconsistencyError`] type for more information on the kinds of errors that can occur and + /// how to resolve them. #[inline] pub fn sync(&mut self) -> Result<(), Error<C, L, S>> { let PullResponse { @@ -275,7 +310,7 @@ where .pull(&self.checkpoint) .map_err(Error::LedgerConnectionError)?; if checkpoint < self.checkpoint { - return Err(Error::InconsistentCheckpoint); + return Err(Error::Inconsistency(InconsistencyError::LedgerCheckpoint)); } match self .signer @@ -288,7 +323,9 @@ where { Ok(SyncResponse::Partial { deposit, withdraw }) => { self.assets.deposit_all(deposit); - self.assets.withdraw_all_unchecked(withdraw); + if !self.assets.withdraw_all(withdraw) { + return Err(Error::Inconsistency(InconsistencyError::WalletBalance)); + } } Ok(SyncResponse::Full { assets }) => { self.assets.clear(); @@ -302,10 +339,13 @@ where // synchronize again. The correct algorithm may be simply to exchange // some checkpoints between the signer and the wallet until they can // agree on a minimal one. - // - In the worst case we would have to recover the entire wallet. + // - In the worst case we would have to recover the wallet (not necessarily + // the signer), which is what the docs currently recommend. // let _ = starting_index; - todo!() + return Err(Error::Inconsistency( + InconsistencyError::SignerSynchronization, + )); } } self.checkpoint = checkpoint; @@ -365,6 +405,50 @@ where } } +/// Inconsistency Error +/// +/// This `enum` is the error state for the [`sync`](Wallet::sync) method on [`Wallet`]. See its +/// documentation for more. The variants below describe their error conditions and how to solve the +/// issue whenever they arise. +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum InconsistencyError { + /// Ledger Checkpoint Inconsistency + /// + /// This error state arises when the ledger checkpoint is behind the checkpoint of the wallet. + /// To resolve this error, ensure that the ledger connection is correct and try again. This + /// error does not result in a bad wallet state. + LedgerCheckpoint, + + /// Wallet Balance Inconsistency + /// + /// ⚠️ This error causes the wallet system to enter an inconsistent state. ⚠️ + /// + /// This error state arises whenever the signer requests a withdraw from the wallet that would + /// overdraw the balance. To resolve this error, ensure that the signer connection is correct + /// and perform a wallet reset by resetting the checkpoint and balance state with a call to + /// [`reset`](Wallet::reset). If other errors continue or if there is reason to suspect that the + /// signer or ledger connections (or their true state) are corrupted, a full recovery is + /// required. See the [`recover`](Wallet::recover) method for more. + WalletBalance, + + /// Signer Synchronization Inconsistency + /// + /// ⚠️ This error causes the wallet system to enter an inconsistent state. ⚠️ + /// + /// This error state arises whenever the signer gets behind the wallet checkpoint. To resolve + /// this error, ensure that the signer connection is correct and perform a wallet reset by + /// resetting the checkpoint and balance state with a call to [`reset`](Wallet::reset). If other + /// errors continue or if there is reason to suspect that the signer or ledger connections (or + /// their true state) are corrupted, a full recovery is required. See the + /// [`recover`](Wallet::recover) method for more. + SignerSynchronization, +} + /// Wallet Error /// /// This `enum` is the error state for [`Wallet`] methods. See [`sync`](Wallet::sync) and @@ -380,8 +464,10 @@ where /// Insufficient Balance InsufficientBalance(Asset), - /// Inconsistent Checkpoint Error - InconsistentCheckpoint, + /// Inconsistency Error + /// + /// See the documentation of [`InconsistencyError`] for more. + Inconsistency(InconsistencyError), /// Signing Error SignError(SignError<C>), From 3913c696c86728e94f895604f1a5e287a5ea42bd Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 6 Feb 2022 16:50:29 -0500 Subject: [PATCH 222/275] fix: use new wallet creation interface --- manta-pay/src/test/ledger.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manta-pay/src/test/ledger.rs b/manta-pay/src/test/ledger.rs index 527aba8ec..f29d967ce 100644 --- a/manta-pay/src/test/ledger.rs +++ b/manta-pay/src/test/ledger.rs @@ -501,7 +501,7 @@ mod test { where R: CryptoRng + RngCore + ?Sized, { - Wallet::empty( + Wallet::new( LedgerConnection::new(account, ledger.clone()), Signer::new( AccountTable::new(rng.gen()), From 243d79ac2d35b6c10c0fda038b5a5c311ea24174 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 6 Feb 2022 22:58:08 -0500 Subject: [PATCH 223/275] feat: add serde-compatible array wrappers --- manta-accounting/src/asset.rs | 30 ++- manta-accounting/src/transfer/canonical.rs | 21 ++ manta-crypto/Cargo.toml | 3 +- manta-crypto/src/ecc.rs | 15 +- manta-crypto/src/merkle_tree/forest.rs | 35 +-- manta-pay/Cargo.toml | 2 +- manta-pay/src/crypto/encryption.rs | 12 +- manta-util/Cargo.toml | 5 + manta-util/src/array.rs | 241 ++++++++++++++++++++- manta-util/src/codec.rs | 10 +- 10 files changed, 329 insertions(+), 45 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 5e692141a..2ed6fdf72 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -16,8 +16,6 @@ //! Assets -// FIXME: Make sure that the `AssetList` invariants are safe when deserializing. - use alloc::{ collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}, vec, @@ -36,7 +34,7 @@ use manta_crypto::{ constraint::{Allocator, Secret, ValueSource, Variable}, rand::{CryptoRng, Rand, RngCore, Sample, Standard}, }; -use manta_util::into_array_unchecked; +use manta_util::{into_array_unchecked, Array}; #[cfg(feature = "serde")] use manta_util::serde::{Deserialize, Serialize}; @@ -97,8 +95,8 @@ impl AssetId { impl From<AssetId> for [u8; AssetId::SIZE] { #[inline] - fn from(entry: AssetId) -> Self { - entry.into_bytes() + fn from(id: AssetId) -> Self { + id.into_bytes() } } @@ -224,8 +222,8 @@ impl AddAssign<AssetValueType> for AssetValue { impl From<AssetValue> for [u8; AssetValue::SIZE] { #[inline] - fn from(entry: AssetValue) -> Self { - entry.into_bytes() + fn from(value: AssetValue) -> Self { + value.into_bytes() } } @@ -417,10 +415,24 @@ impl From<[u8; Self::SIZE]> for Asset { } } +impl From<Array<u8, { Self::SIZE }>> for Asset { + #[inline] + fn from(array: Array<u8, { Self::SIZE }>) -> Self { + array.0.into() + } +} + impl From<Asset> for [u8; Asset::SIZE] { #[inline] - fn from(entry: Asset) -> Self { - entry.into_bytes() + fn from(asset: Asset) -> Self { + asset.into_bytes() + } +} + +impl From<Asset> for Array<u8, { Asset::SIZE }> { + #[inline] + fn from(asset: Asset) -> Self { + Self(asset.into_bytes()) } } diff --git a/manta-accounting/src/transfer/canonical.rs b/manta-accounting/src/transfer/canonical.rs index 6e077e574..b1034ef04 100644 --- a/manta-accounting/src/transfer/canonical.rs +++ b/manta-accounting/src/transfer/canonical.rs @@ -31,6 +31,9 @@ use core::{fmt::Debug, hash::Hash}; use manta_crypto::rand::{CryptoRng, Rand, RngCore}; use manta_util::{create_seal, seal}; +#[cfg(feature = "serde")] +use manta_util::serde::{Deserialize, Serialize}; + create_seal! {} /// Transfer Shapes @@ -198,6 +201,12 @@ where } /// Transfer Shape +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum TransferShape { /// [`Mint`] Transfer Mint, @@ -268,6 +277,18 @@ impl TransferShape { } /// Canonical Transaction Type +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "ReceivingKey<C>: Deserialize<'de>", + serialize = "ReceivingKey<C>: Serialize" + ), + crate = "manta_util::serde", + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "ReceivingKey<C>: Clone"), diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index b905bf480..b49bac0db 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -29,7 +29,7 @@ maintenance = { status = "actively-developed" } getrandom = ["rand_core/getrandom"] # Serde -serde = ["manta-util/serde-alloc", "serde_with"] +serde = ["manta-util/serde-alloc", "manta-util/serde-array"] # Standard Library std = ["manta-util/std"] @@ -41,7 +41,6 @@ test = [] derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } manta-util = { path = "../manta-util", features = ["alloc"] } rand_core = { version = "0.6.3", default-features = false } -serde_with = { version = "1.11.0", optional = true, default-features = false, features = ["macros"] } [dev-dependencies] rand = "0.8.4" diff --git a/manta-crypto/src/ecc.rs b/manta-crypto/src/ecc.rs index aa7b9c6ab..339fb42e5 100644 --- a/manta-crypto/src/ecc.rs +++ b/manta-crypto/src/ecc.rs @@ -24,7 +24,10 @@ use crate::{ rand::{CryptoRng, RngCore, Sample}, }; use alloc::{boxed::Box, vec::Vec}; -use manta_util::codec::{Decode, DecodeError, Encode, Read, Write}; +use manta_util::{ + codec::{Decode, DecodeError, Encode, Read, Write}, + BoxArray, +}; #[cfg(feature = "serde")] use manta_util::serde::{Deserialize, Serialize}; @@ -130,11 +133,7 @@ pub trait Group<COM = ()>: #[derivative(Clone, Debug, Eq, Hash, PartialEq)] pub struct PreprocessedScalarMulTable<G, const N: usize = 1> { /// Pre-computed Table - #[cfg_attr( - feature = "serde", - serde(with = "serde_with::As::<Box<[serde_with::Same; N]>>") - )] - table: Box<[G; N]>, + table: BoxArray<G, N>, } impl<G, const N: usize> PreprocessedScalarMulTable<G, N> { @@ -163,7 +162,9 @@ impl<G, const N: usize> PreprocessedScalarMulTable<G, N> { /// checking if the table is consistent. #[inline] pub fn from_powers_unchecked(table: Box<[G; N]>) -> Self { - Self { table } + Self { + table: BoxArray(table), + } } } diff --git a/manta-crypto/src/merkle_tree/forest.rs b/manta-crypto/src/merkle_tree/forest.rs index 6f8f649b9..78590dcfe 100644 --- a/manta-crypto/src/merkle_tree/forest.rs +++ b/manta-crypto/src/merkle_tree/forest.rs @@ -36,7 +36,7 @@ use crate::{ }; use alloc::{boxed::Box, vec::Vec}; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; -use manta_util::{into_boxed_array_unchecked, persistance::Rollback, pointer::PointerFamily}; +use manta_util::{persistance::Rollback, pointer::PointerFamily, BoxArray}; #[cfg(feature = "serde")] use manta_util::serde::{Deserialize, Serialize}; @@ -457,7 +457,7 @@ pub type TreeArrayMerkleForest<C, T, const N: usize> = MerkleForest<C, TreeArray /// Tree Array #[cfg_attr( - all(feature = "serde", feature = "serde_with"), + feature = "serde", derive(Deserialize, Serialize), serde( bound(deserialize = "T: Deserialize<'de>", serialize = "T: Serialize"), @@ -483,16 +483,28 @@ where /// /// Typically, even for reasonable `N`, the size of this array is too big to fit on the stack, /// so we wrap it in a box to store on the heap. - #[cfg_attr( - feature = "serde", - serde(with = "serde_with::As::<Box<[serde_with::Same; N]>>") - )] - array: Box<[T; N]>, + array: BoxArray<T, N>, /// Type Parameter Marker __: PhantomData<C>, } +impl<C, T, const N: usize> TreeArray<C, T, N> +where + C: Configuration + ?Sized, + C::Index: FixedIndex<N>, + T: Tree<C>, +{ + /// Builds a new [`TreeArray`] from `array`. + #[inline] + fn new(array: BoxArray<T, N>) -> Self { + Self { + array, + __: PhantomData, + } + } +} + impl<C, T, const N: usize> AsRef<[T; N]> for TreeArray<C, T, N> where C: Configuration + ?Sized, @@ -525,7 +537,7 @@ where { #[inline] fn default() -> Self { - Self::from(into_boxed_array_unchecked( + Self::new(BoxArray::from_unchecked( (0..N) .into_iter() .map(move |_| Default::default()) @@ -545,7 +557,7 @@ where #[inline] fn new(parameters: &Parameters<C>) -> Self { - Self::from(into_boxed_array_unchecked( + Self::new(BoxArray::from_unchecked( (0..N) .into_iter() .map(move |_| T::new(parameters)) @@ -604,10 +616,7 @@ where { #[inline] fn from(array: Box<[T; N]>) -> Self { - Self { - array, - __: PhantomData, - } + Self::new(BoxArray(array)) } } diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index d7ae297f2..3ab9ce87a 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -53,7 +53,7 @@ groth16 = ["arkworks", "ark-groth16"] # TODO: plonk = ["zk-garage-plonk"] # Serde -serde = ["manta-accounting/serde", "manta-crypto/serde", "manta-util/serde-alloc"] +serde = ["manta-accounting/serde", "manta-crypto/serde"] # Simulation Framework simulation = ["rand", "statrs"] diff --git a/manta-pay/src/crypto/encryption.rs b/manta-pay/src/crypto/encryption.rs index 3ee6aa485..f995cf995 100644 --- a/manta-pay/src/crypto/encryption.rs +++ b/manta-pay/src/crypto/encryption.rs @@ -26,7 +26,7 @@ pub mod aes { }; use generic_array::GenericArray; use manta_crypto::encryption::SymmetricKeyEncryptionScheme; - use manta_util::into_array_unchecked; + use manta_util::Array; /// AES-GCM Authentication Tag Size #[allow(clippy::cast_possible_truncation)] // NOTE: GCM Tag Size should be smaller than `2^32`. @@ -49,13 +49,13 @@ pub mod aes { impl<const P: usize, const C: usize> SymmetricKeyEncryptionScheme for AesGcm<P, C> { type Key = [u8; 32]; - type Plaintext = [u8; P]; - type Ciphertext = [u8; C]; + type Plaintext = Array<u8, P>; + type Ciphertext = Array<u8, C>; #[inline] fn encrypt(key: Self::Key, plaintext: Self::Plaintext) -> Self::Ciphertext { // SAFETY: Using a deterministic nonce is ok since we never reuse keys. - into_array_unchecked( + Array::from_unchecked( Aes256Gcm::new(GenericArray::from_slice(&key)) .encrypt(Nonce::from_slice(Self::NONCE), plaintext.as_ref()) .expect("Symmetric encryption is not allowed to fail."), @@ -68,7 +68,7 @@ pub mod aes { Aes256Gcm::new(GenericArray::from_slice(&key)) .decrypt(Nonce::from_slice(Self::NONCE), ciphertext.as_ref()) .ok() - .map(into_array_unchecked) + .map(Array::from_unchecked) } } @@ -90,7 +90,7 @@ pub mod aes { rng.fill_bytes(&mut plaintext); encryption::test::symmetric_encryption::< AesGcm<{ Asset::SIZE }, { ciphertext_size(Asset::SIZE) }>, - >(key, plaintext); + >(key, Array(plaintext)); } } } diff --git a/manta-util/Cargo.toml b/manta-util/Cargo.toml index 8452d3ea5..325869630 100644 --- a/manta-util/Cargo.toml +++ b/manta-util/Cargo.toml @@ -31,8 +31,13 @@ alloc = [] # Serde for Alloc Types serde-alloc = ["alloc", "serde/alloc"] +# Serde for Arrays +serde-array = ["serde", "serde_with"] + # Standard Library std = ["alloc"] [dependencies] serde = { version = "1.0.136", optional = true, default-features = false, features = ["derive"] } +serde_with = { version = "1.11.0", optional = true, default-features = false, features = ["macros"] } + diff --git a/manta-util/src/array.rs b/manta-util/src/array.rs index 96e540fb5..334f811a5 100644 --- a/manta-util/src/array.rs +++ b/manta-util/src/array.rs @@ -16,11 +16,18 @@ //! Array Utilities -use core::convert::TryInto; +use core::{ + borrow::{Borrow, BorrowMut}, + convert::TryInto, + ops::{Deref, DerefMut}, +}; #[cfg(feature = "alloc")] use alloc::{boxed::Box, vec::Vec}; +#[cfg(feature = "serde-array")] +use crate::serde::{Deserialize, Serialize}; + /// Performs the [`TryInto`] conversion into an array without checking if the conversion succeeded. #[inline] pub fn into_array_unchecked<T, V, const N: usize>(v: V) -> [T; N] @@ -54,7 +61,7 @@ where } } -/// Maps `f` over the `array`. +/// Maps `f` over the `array` using allocation. #[cfg(feature = "alloc")] #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] #[inline] @@ -65,7 +72,7 @@ where into_array_unchecked(array.into_iter().map(f).collect::<Vec<_>>()) } -/// Maps `f` over the `array` by reference. +/// Maps `f` over the `array` by reference using allocation. #[cfg(feature = "alloc")] #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] #[inline] @@ -103,3 +110,231 @@ where array.iter().map(f).collect::<Result<Vec<_>, _>>()?, )) } + +/// Implements some traits for array wrapper types. +macro_rules! impl_array_traits { + ($type:tt) => { + impl<T, const N: usize> AsMut<[T; N]> for $type<T, N> { + #[inline] + fn as_mut(&mut self) -> &mut [T; N] { + &mut self.0 + } + } + + impl<T, const N: usize> AsMut<[T]> for $type<T, N> { + #[inline] + fn as_mut(&mut self) -> &mut [T] { + self.0.as_mut() + } + } + + impl<T, const N: usize> AsRef<[T; N]> for $type<T, N> { + #[inline] + fn as_ref(&self) -> &[T; N] { + &self.0 + } + } + + impl<T, const N: usize> AsRef<[T]> for $type<T, N> { + #[inline] + fn as_ref(&self) -> &[T] { + self.0.as_ref() + } + } + + impl<T, const N: usize> Borrow<[T; N]> for $type<T, N> { + #[inline] + fn borrow(&self) -> &[T; N] { + &self.0 + } + } + + impl<T, const N: usize> BorrowMut<[T; N]> for $type<T, N> { + #[inline] + fn borrow_mut(&mut self) -> &mut [T; N] { + &mut self.0 + } + } + + impl<T, const N: usize> Borrow<[T]> for $type<T, N> { + #[inline] + fn borrow(&self) -> &[T] { + self.0.as_ref() + } + } + + impl<T, const N: usize> BorrowMut<[T]> for $type<T, N> { + #[inline] + fn borrow_mut(&mut self) -> &mut [T] { + self.0.as_mut() + } + } + + impl<T, const N: usize> Deref for $type<T, N> { + type Target = [T; N]; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl<T, const N: usize> DerefMut for $type<T, N> { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + impl<T, const N: usize> From<[T; N]> for $type<T, N> { + #[inline] + fn from(array: [T; N]) -> Self { + Self(array.into()) + } + } + }; +} + +/// Array +/// +/// This type wraps a standard Rust array but provides some additional methods and optional +/// compatibility with `serde`. The type `Array<T, N>` is mostly a drop-in replacement for `[T; N]`. +#[cfg_attr( + feature = "serde-array", + derive(Deserialize, Serialize), + serde( + bound(deserialize = "T: Deserialize<'de>", serialize = "T: Serialize"), + crate = "crate::serde", + deny_unknown_fields + ) +)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[repr(transparent)] +pub struct Array<T, const N: usize>( + /// Array Data + #[cfg_attr( + feature = "serde-array", + serde(with = "serde_with::As::<[serde_with::Same; N]>") + )] + pub [T; N], +); + +impl<T, const N: usize> Array<T, N> { + /// Performs the [`TryInto`] conversion into an array without checking if the conversion + /// succeeded. See [`into_array_unchecked`] for more. + #[inline] + pub fn from_unchecked<V>(v: V) -> Self + where + V: TryInto<[T; N]>, + { + Self(into_array_unchecked(v)) + } + + /// Maps `f` over `self` using allocation. + #[cfg(feature = "alloc")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] + #[inline] + pub fn map<U, F>(self, f: F) -> Array<U, N> + where + F: FnMut(T) -> U, + { + Array(array_map(self.0, f)) + } + + /// Maps `f` over `self` by reference using allocation. + #[cfg(feature = "alloc")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] + #[inline] + pub fn map_ref<U, F>(&self, f: F) -> Array<U, N> + where + F: FnMut(&T) -> U, + { + Array(array_map_ref(&self.0, f)) + } + + /// Maps `f` over `self` returning the target array if all of the mappings succeeded, or + /// returning the first error that occurs. + #[cfg(feature = "alloc")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] + #[inline] + pub fn fallible_array_map<U, E, F>(self, f: F) -> Result<Array<U, N>, E> + where + F: FnMut(T) -> Result<U, E>, + { + fallible_array_map(self.0, f).map(Array) + } + + /// Maps `f` over `self` by reference returning the target array if all of the mappings + /// succeeded, or returning the first error that occurs. + #[cfg(feature = "alloc")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] + #[inline] + pub fn fallible_array_map_ref<U, E, F>(&self, f: F) -> Result<Array<U, N>, E> + where + F: FnMut(&T) -> Result<U, E>, + { + fallible_array_map_ref(&self.0, f).map(Array) + } +} + +impl_array_traits!(Array); + +impl<T, const N: usize> Default for Array<T, N> +where + T: Copy + Default, +{ + #[inline] + fn default() -> Self { + Self([T::default(); N]) + } +} + +impl<T, const N: usize> From<Array<T, N>> for [T; N] { + #[inline] + fn from(array: Array<T, N>) -> Self { + array.0 + } +} + +/// Boxed Array +/// +/// This type wraps a boxed standard Rust array but provides some additional methods and optional +/// compatibility with `serde`. The type `BoxArray<T, N>` is mostly a drop-in replacement for +/// `Box<[T; N]>`. +#[cfg(feature = "alloc")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] +#[cfg_attr( + all(feature = "serde-alloc", feature = "serde-array"), + derive(Deserialize, Serialize), + serde( + bound(deserialize = "T: Deserialize<'de>", serialize = "T: Serialize"), + crate = "crate::serde", + deny_unknown_fields + ) +)] +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[repr(transparent)] +pub struct BoxArray<T, const N: usize>( + /// Array Data + #[cfg_attr( + all(feature = "serde-alloc", feature = "serde-array"), + serde(with = "serde_with::As::<Box<[serde_with::Same; N]>>") + )] + pub Box<[T; N]>, +); + +#[cfg(feature = "alloc")] +impl<T, const N: usize> BoxArray<T, N> { + /// Performs the [`TryInto`] conversion into a boxed array without checking if the conversion + /// succeeded. See [`into_boxed_array_unchecked`] for more. + #[inline] + pub fn from_unchecked<V>(v: V) -> Self + where + V: TryInto<Box<[T; N]>>, + { + Self(into_boxed_array_unchecked(v)) + } +} + +#[cfg(feature = "alloc")] +impl_array_traits!(BoxArray); diff --git a/manta-util/src/codec.rs b/manta-util/src/codec.rs index 4bcf55bf6..cd9f3067e 100644 --- a/manta-util/src/codec.rs +++ b/manta-util/src/codec.rs @@ -16,14 +16,12 @@ //! Encoding and Decoding Utilities -// TODO: Add `ReadFrom` and `WriteInto` traits for conversion between different serde/codec impls -// which are specialized so that you can automatically convert between a type and itself. +// TODO: Deprecate this in favor of pure `serde`. -use crate::into_array_unchecked; use core::{convert::Infallible, fmt::Debug, hash::Hash, marker::PhantomData}; #[cfg(feature = "alloc")] -use alloc::vec::Vec; +use {crate::into_array_unchecked, alloc::vec::Vec}; /// Reader pub trait Read { @@ -810,6 +808,8 @@ impl Decode for u64 { } } +#[cfg(feature = "alloc")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] impl<T, const N: usize> Decode for [T; N] where T: Decode, @@ -829,6 +829,8 @@ where } } +#[cfg(feature = "alloc")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] impl<T> Decode for Vec<T> where T: Decode, From 88be0ed1c09de572da466bed3ce2ee67f0683500 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 7 Feb 2022 00:25:03 -0500 Subject: [PATCH 224/275] fix: clean up manta-pay arkworks directory organization --- .../src/crypto/constraint/arkworks/codec.rs | 192 ++++++ .../constraint/arkworks/constraint_system.rs | 548 ---------------- .../src/crypto/constraint/arkworks/groth16.rs | 428 ++++++++++++ .../src/crypto/constraint/arkworks/mod.rs | 381 ++++++++++- .../src/crypto/constraint/arkworks/pairing.rs | 177 +++++ .../constraint/arkworks/proof_system.rs | 621 ------------------ manta-pay/src/crypto/ecc.rs | 423 ------------ manta-pay/src/crypto/ecc/arkworks.rs | 418 ++++++++++++ manta-pay/src/crypto/ecc/mod.rs | 21 + manta-util/src/array.rs | 7 +- 10 files changed, 1617 insertions(+), 1599 deletions(-) create mode 100644 manta-pay/src/crypto/constraint/arkworks/codec.rs delete mode 100644 manta-pay/src/crypto/constraint/arkworks/constraint_system.rs create mode 100644 manta-pay/src/crypto/constraint/arkworks/groth16.rs create mode 100644 manta-pay/src/crypto/constraint/arkworks/pairing.rs delete mode 100644 manta-pay/src/crypto/constraint/arkworks/proof_system.rs delete mode 100644 manta-pay/src/crypto/ecc.rs create mode 100644 manta-pay/src/crypto/ecc/arkworks.rs create mode 100644 manta-pay/src/crypto/ecc/mod.rs diff --git a/manta-pay/src/crypto/constraint/arkworks/codec.rs b/manta-pay/src/crypto/constraint/arkworks/codec.rs new file mode 100644 index 000000000..7ae02456a --- /dev/null +++ b/manta-pay/src/crypto/constraint/arkworks/codec.rs @@ -0,0 +1,192 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Codec Utilities + +use ark_std::io::{self, Error, ErrorKind}; +use manta_util::codec::{Read, ReadExactError, Write}; +use scale_codec::Input; + +pub use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; + +/// Scale-Codec Input as Reader Wrapper +#[derive(Debug, Eq, Hash, PartialEq)] +pub struct ScaleCodecReader<'i, I>(pub &'i mut I) +where + I: Input; + +impl<I> io::Read for ScaleCodecReader<'_, I> +where + I: Input, +{ + #[inline] + fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> { + let len = buf.len(); + self.read_exact(buf).map(|_| len) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Error> { + Input::read(self.0, buf).map_err(|_| ErrorKind::Other.into()) + } +} + +/// Serialization Hook +pub trait HasSerialization<'s>: 's { + /// Serialize Type + type Serialize: CanonicalSerialize + From<&'s Self>; +} + +/// Deserialization Hook +pub trait HasDeserialization: Sized { + /// Deserialize Type + type Deserialize: CanonicalDeserialize + Into<Self>; +} + +/// Arkworks Reader +pub struct ArkReader<R> +where + R: Read, +{ + /// Reader State + state: Result<R, R::Error>, +} + +impl<R> ArkReader<R> +where + R: Read, +{ + /// Builds a new [`ArkReader`] from `reader`. + #[inline] + pub fn new(reader: R) -> Self { + Self { state: Ok(reader) } + } + + /// Updates the internal reader state by performing the `f` computation. + #[inline] + fn update<T, F>(&mut self, f: F) -> Option<T> + where + F: FnOnce(&mut R) -> Result<T, R::Error>, + { + if let Ok(reader) = self.state.as_mut() { + match f(reader) { + Ok(value) => return Some(value), + Err(err) => self.state = Err(err), + } + } + None + } + + /// Returns the reader state back or an error if it occured during any [`Read`](io::Read) + /// methods. + #[inline] + pub fn finish(self) -> Result<R, R::Error> { + self.state + } +} + +impl<R> io::Read for ArkReader<R> +where + R: Read, +{ + #[inline] + fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> { + self.update(|reader| reader.read(buf)) + .ok_or_else(|| Error::new(ErrorKind::Other, "Reading Error")) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Error> { + match self.update(|reader| match reader.read_exact(buf) { + Ok(value) => Ok(Ok(value)), + Err(ReadExactError::Read(err)) => Err(err), + Err(ReadExactError::UnexpectedEnd(err)) => Ok(Err(err)), + }) { + Some(Ok(_)) => Ok(()), + Some(Err(_)) => Err(Error::new( + ErrorKind::UnexpectedEof, + "Unexpected end of buffer.", + )), + _ => Err(Error::new(ErrorKind::Other, "Reading Error")), + } + } +} + +/// Arkworks Writer +pub struct ArkWriter<W> +where + W: Write, +{ + /// Writer State + state: Result<W, W::Error>, +} + +impl<W> ArkWriter<W> +where + W: Write, +{ + /// Builds a new [`ArkWriter`] from `writer`. + #[inline] + pub fn new(writer: W) -> Self { + Self { state: Ok(writer) } + } + + /// Updates the internal writer state by performing the `f` computation. + #[inline] + fn update<T, F>(&mut self, f: F) -> Option<T> + where + F: FnOnce(&mut W) -> Result<T, W::Error>, + { + if let Ok(writer) = self.state.as_mut() { + match f(writer) { + Ok(value) => return Some(value), + Err(err) => self.state = Err(err), + } + } + None + } + + /// Returns the writer state back or an error if it occured during any [`Write`](io::Write) + /// methods. + #[inline] + pub fn finish(self) -> Result<W, W::Error> { + self.state + } +} + +impl<W> io::Write for ArkWriter<W> +where + W: Write, +{ + #[inline] + fn write(&mut self, mut buf: &[u8]) -> Result<usize, Error> { + self.update(|writer| writer.write(&mut buf)) + .ok_or_else(|| Error::new(ErrorKind::Other, "Writing Error")) + } + + #[inline] + fn flush(&mut self) -> Result<(), Error> { + // NOTE: We can't necessarily do better than this for now, unfortunately. + Ok(()) + } + + #[inline] + fn write_all(&mut self, mut buf: &[u8]) -> Result<(), Error> { + self.update(|writer| writer.write(&mut buf)) + .map(|_| ()) + .ok_or_else(|| Error::new(ErrorKind::Other, "Writing Error")) + } +} diff --git a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs b/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs deleted file mode 100644 index a39204c13..000000000 --- a/manta-pay/src/crypto/constraint/arkworks/constraint_system.rs +++ /dev/null @@ -1,548 +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 <http://www.gnu.org/licenses/>. - -//! Arkworks Constraint System Implementation - -use alloc::vec::Vec; -use ark_ff::PrimeField; -use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget, select::CondSelectGadget}; -use ark_relations::{ns, r1cs as ark_r1cs}; -use manta_crypto::{ - constraint::{ - measure::Measure, Add, ConditionalSelect, Constant, ConstraintSystem, Equal, Public, - Secret, Variable, - }, - rand::{CryptoRng, RngCore, Sample, Standard}, -}; -use manta_util::codec::{Decode, DecodeError, Encode, Read, Write}; - -pub use ark_r1cs::SynthesisError; -pub use ark_r1cs_std::{bits::boolean::Boolean, fields::fp::FpVar}; - -/// Prime Field Element -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Fp<F>(pub F) -where - F: PrimeField; - -impl<F> Decode for Fp<F> -where - F: PrimeField, -{ - type Error = codec::SerializationError; - - #[inline] - fn decode<R>(reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> - where - R: Read, - { - let mut reader = codec::ArkReader::new(reader); - match codec::CanonicalDeserialize::deserialize(&mut reader) { - Ok(value) => reader - .finish() - .map(move |_| Self(value)) - .map_err(DecodeError::Read), - Err(err) => Err(DecodeError::Decode(err)), - } - } -} - -impl<F> Encode for Fp<F> -where - F: PrimeField, -{ - #[inline] - fn encode<W>(&self, writer: W) -> Result<(), W::Error> - where - W: Write, - { - let mut writer = codec::ArkWriter::new(writer); - let _ = self.0.serialize(&mut writer); - writer.finish().map(move |_| ()) - } -} - -impl<F> scale_codec::Decode for Fp<F> -where - F: PrimeField, -{ - #[inline] - fn decode<I>(input: &mut I) -> Result<Self, scale_codec::Error> - where - I: scale_codec::Input, - { - Ok(Self( - codec::CanonicalDeserialize::deserialize(codec::ScaleCodecReader(input)) - .map_err(|_| "Deserialization Error")?, - )) - } -} - -impl<F> scale_codec::Encode for Fp<F> -where - F: PrimeField, -{ - #[inline] - fn using_encoded<R, Encoder>(&self, f: Encoder) -> R - where - Encoder: FnOnce(&[u8]) -> R, - { - let mut buffer = Vec::new(); - self.0 - .serialize(&mut buffer) - .expect("Encoding is not allowed to fail."); - f(&buffer) - } -} - -impl<F> scale_codec::EncodeLike for Fp<F> where F: PrimeField {} - -impl<F> Sample for Fp<F> -where - F: PrimeField, -{ - #[inline] - fn sample<R>(distribution: Standard, rng: &mut R) -> Self - where - R: CryptoRng + RngCore + ?Sized, - { - let _ = distribution; - Self(F::rand(rng)) - } -} - -/// Synthesis Result -pub type SynthesisResult<T = ()> = Result<T, SynthesisError>; - -/// Returns an empty variable assignment for setup mode. -/// -/// # Warning -/// -/// This does not work for all variable assignments! For some assignemnts, the variable inherits -/// some structure from its input, like its length or number of bits, which are only known at -/// run-time. For those cases, some mocking is required and this function can not be used directly. -#[inline] -pub fn empty<T>() -> SynthesisResult<T> { - Err(SynthesisError::AssignmentMissing) -} - -/// Returns a filled variable assignment with the given `value`. -#[inline] -pub fn full<T>(value: T) -> impl FnOnce() -> SynthesisResult<T> { - move || Ok(value) -} - -/// Arkworks Rank-1 Constraint System -pub struct R1CS<F> -where - F: PrimeField, -{ - /// Constraint System - pub(crate) cs: ark_r1cs::ConstraintSystemRef<F>, -} - -impl<F> R1CS<F> -where - F: PrimeField, -{ - /// Constructs a new constraint system which is ready for unknown variables. - #[inline] - pub fn for_unknown() -> Self { - // FIXME: This might not be the right setup for all proof systems. - let cs = ark_r1cs::ConstraintSystem::new_ref(); - cs.set_optimization_goal(ark_r1cs::OptimizationGoal::Constraints); - cs.set_mode(ark_r1cs::SynthesisMode::Setup); - Self { cs } - } - - /// Constructs a new constraint system which is ready for known variables. - #[inline] - pub fn for_known() -> Self { - // FIXME: This might not be the right setup for all proof systems. - let cs = ark_r1cs::ConstraintSystem::new_ref(); - cs.set_optimization_goal(ark_r1cs::OptimizationGoal::Constraints); - Self { cs } - } -} - -impl<F> ConstraintSystem for R1CS<F> -where - F: PrimeField, -{ - type Bool = Boolean<F>; - - #[inline] - fn assert(&mut self, b: Self::Bool) { - b.enforce_equal(&Boolean::TRUE) - .expect("Enforcing equality is not allowed to fail."); - } -} - -impl<F> Measure for R1CS<F> -where - F: PrimeField, -{ - #[inline] - fn constraint_count(&self) -> usize { - self.cs.num_constraints() - } - - #[inline] - fn public_variable_count(&self) -> Option<usize> { - Some(self.cs.num_instance_variables()) - } - - #[inline] - fn secret_variable_count(&self) -> Option<usize> { - Some(self.cs.num_witness_variables()) - } -} - -impl<F> Constant<R1CS<F>> for Boolean<F> -where - F: PrimeField, -{ - type Type = bool; - - #[inline] - fn new_constant(this: &Self::Type, compiler: &mut R1CS<F>) -> Self { - AllocVar::new_constant(ns!(compiler.cs, "boolean constant"), this) - .expect("Variable allocation is not allowed to fail.") - } -} - -impl<F> Variable<Public, R1CS<F>> for Boolean<F> -where - F: PrimeField, -{ - type Type = bool; - - #[inline] - fn new_known(this: &Self::Type, compiler: &mut R1CS<F>) -> Self { - Self::new_input(ns!(compiler.cs, "boolean public input"), full(this)) - .expect("Variable allocation is not allowed to fail.") - } - - #[inline] - fn new_unknown(compiler: &mut R1CS<F>) -> Self { - Self::new_input(ns!(compiler.cs, "boolean public input"), empty::<bool>) - .expect("Variable allocation is not allowed to fail.") - } -} - -impl<F> Variable<Secret, R1CS<F>> for Boolean<F> -where - F: PrimeField, -{ - type Type = bool; - - #[inline] - fn new_known(this: &Self::Type, compiler: &mut R1CS<F>) -> Self { - Self::new_witness(ns!(compiler.cs, "boolean secret witness"), full(this)) - .expect("Variable allocation is not allowed to fail.") - } - - #[inline] - fn new_unknown(compiler: &mut R1CS<F>) -> Self { - Self::new_witness(ns!(compiler.cs, "boolean secret witness"), empty::<bool>) - .expect("Variable allocation is not allowed to fail.") - } -} - -impl<F> Equal<R1CS<F>> for Boolean<F> -where - F: PrimeField, -{ - #[inline] - fn eq(lhs: &Self, rhs: &Self, compiler: &mut R1CS<F>) -> Boolean<F> { - let _ = compiler; - lhs.is_eq(rhs) - .expect("Equality checking is not allowed to fail.") - } -} - -impl<F> Constant<R1CS<F>> for FpVar<F> -where - F: PrimeField, -{ - type Type = Fp<F>; - - #[inline] - fn new_constant(this: &Self::Type, compiler: &mut R1CS<F>) -> Self { - AllocVar::new_constant(ns!(compiler.cs, "field constant"), this.0) - .expect("Variable allocation is not allowed to fail.") - } -} - -impl<F> Variable<Public, R1CS<F>> for FpVar<F> -where - F: PrimeField, -{ - type Type = Fp<F>; - - #[inline] - fn new_known(this: &Self::Type, compiler: &mut R1CS<F>) -> Self { - Self::new_input(ns!(compiler.cs, "field public input"), full(this.0)) - .expect("Variable allocation is not allowed to fail.") - } - - #[inline] - fn new_unknown(compiler: &mut R1CS<F>) -> Self { - Self::new_input(ns!(compiler.cs, "field public input"), empty::<F>) - .expect("Variable allocation is not allowed to fail.") - } -} - -impl<F> Variable<Secret, R1CS<F>> for FpVar<F> -where - F: PrimeField, -{ - type Type = Fp<F>; - - #[inline] - fn new_known(this: &Self::Type, compiler: &mut R1CS<F>) -> Self { - Self::new_witness(ns!(compiler.cs, "field secret witness"), full(this.0)) - .expect("Variable allocation is not allowed to fail.") - } - - #[inline] - fn new_unknown(compiler: &mut R1CS<F>) -> Self { - Self::new_witness(ns!(compiler.cs, "field secret witness"), empty::<F>) - .expect("Variable allocation is not allowed to fail.") - } -} - -impl<F> Equal<R1CS<F>> for FpVar<F> -where - F: PrimeField, -{ - #[inline] - fn eq(lhs: &Self, rhs: &Self, compiler: &mut R1CS<F>) -> Boolean<F> { - let _ = compiler; - lhs.is_eq(rhs) - .expect("Equality checking is not allowed to fail.") - } -} - -impl<F> ConditionalSelect<R1CS<F>> for FpVar<F> -where - F: PrimeField, -{ - #[inline] - fn select( - bit: &Boolean<F>, - true_value: &Self, - false_value: &Self, - compiler: &mut R1CS<F>, - ) -> Self { - let _ = compiler; - Self::conditionally_select(bit, true_value, false_value) - .expect("Conditionally selecting from two values is not allowed to fail.") - } -} - -impl<F> Add<R1CS<F>> for FpVar<F> -where - F: PrimeField, -{ - #[inline] - fn add(lhs: Self, rhs: Self, compiler: &mut R1CS<F>) -> Self { - let _ = compiler; - lhs + rhs - } -} - -/// Codec Utilities -pub mod codec { - use ark_std::io::{self, Error, ErrorKind}; - use manta_util::codec::{Read, ReadExactError, Write}; - use scale_codec::Input; - - pub use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; - - /// Arkworks Encoding Marker - #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct Ark; - - /// Scale-Codec Input as Reader Wrapper - #[derive(Debug, Eq, Hash, PartialEq)] - pub struct ScaleCodecReader<'i, I>(pub &'i mut I) - where - I: Input; - - impl<I> io::Read for ScaleCodecReader<'_, I> - where - I: Input, - { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> { - let len = buf.len(); - self.read_exact(buf).map(|_| len) - } - - #[inline] - fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Error> { - Input::read(self.0, buf).map_err(|_| ErrorKind::Other.into()) - } - } - - /// Serialization Hook - pub trait HasSerialization<'s>: 's { - /// Serialize Type - type Serialize: CanonicalSerialize + From<&'s Self>; - } - - /// Deserialization Hook - pub trait HasDeserialization: Sized { - /// Deserialize Type - type Deserialize: CanonicalDeserialize + Into<Self>; - } - - /// Arkworks Reader - pub struct ArkReader<R> - where - R: Read, - { - /// Reader State - state: Result<R, R::Error>, - } - - impl<R> ArkReader<R> - where - R: Read, - { - /// Builds a new [`ArkReader`] from `reader`. - #[inline] - pub fn new(reader: R) -> Self { - Self { state: Ok(reader) } - } - - /// Updates the internal reader state by performing the `f` computation. - #[inline] - fn update<T, F>(&mut self, f: F) -> Option<T> - where - F: FnOnce(&mut R) -> Result<T, R::Error>, - { - if let Ok(reader) = self.state.as_mut() { - match f(reader) { - Ok(value) => return Some(value), - Err(err) => self.state = Err(err), - } - } - None - } - - /// Returns the reader state back or an error if it occured during any [`Read`](io::Read) - /// methods. - #[inline] - pub fn finish(self) -> Result<R, R::Error> { - self.state - } - } - - impl<R> io::Read for ArkReader<R> - where - R: Read, - { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> { - self.update(|reader| reader.read(buf)) - .ok_or_else(|| Error::new(ErrorKind::Other, "Reading Error")) - } - - #[inline] - fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Error> { - match self.update(|reader| match reader.read_exact(buf) { - Ok(value) => Ok(Ok(value)), - Err(ReadExactError::Read(err)) => Err(err), - Err(ReadExactError::UnexpectedEnd(err)) => Ok(Err(err)), - }) { - Some(Ok(_)) => Ok(()), - Some(Err(_)) => Err(Error::new( - ErrorKind::UnexpectedEof, - "Unexpected end of buffer.", - )), - _ => Err(Error::new(ErrorKind::Other, "Reading Error")), - } - } - } - - /// Arkworks Writer - pub struct ArkWriter<W> - where - W: Write, - { - /// Writer State - state: Result<W, W::Error>, - } - - impl<W> ArkWriter<W> - where - W: Write, - { - /// Builds a new [`ArkWriter`] from `writer`. - #[inline] - pub fn new(writer: W) -> Self { - Self { state: Ok(writer) } - } - - /// Updates the internal writer state by performing the `f` computation. - #[inline] - fn update<T, F>(&mut self, f: F) -> Option<T> - where - F: FnOnce(&mut W) -> Result<T, W::Error>, - { - if let Ok(writer) = self.state.as_mut() { - match f(writer) { - Ok(value) => return Some(value), - Err(err) => self.state = Err(err), - } - } - None - } - - /// Returns the writer state back or an error if it occured during any [`Write`](io::Write) - /// methods. - #[inline] - pub fn finish(self) -> Result<W, W::Error> { - self.state - } - } - - impl<W> io::Write for ArkWriter<W> - where - W: Write, - { - #[inline] - fn write(&mut self, mut buf: &[u8]) -> Result<usize, Error> { - self.update(|writer| writer.write(&mut buf)) - .ok_or_else(|| Error::new(ErrorKind::Other, "Writing Error")) - } - - #[inline] - fn flush(&mut self) -> Result<(), Error> { - // NOTE: We can't necessarily do better than this for now, unfortunately. - Ok(()) - } - - #[inline] - fn write_all(&mut self, mut buf: &[u8]) -> Result<(), Error> { - self.update(|writer| writer.write(&mut buf)) - .map(|_| ()) - .ok_or_else(|| Error::new(ErrorKind::Other, "Writing Error")) - } - } -} diff --git a/manta-pay/src/crypto/constraint/arkworks/groth16.rs b/manta-pay/src/crypto/constraint/arkworks/groth16.rs new file mode 100644 index 000000000..4ba5ca614 --- /dev/null +++ b/manta-pay/src/crypto/constraint/arkworks/groth16.rs @@ -0,0 +1,428 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Groth-16 Proving System + +use crate::crypto::constraint::arkworks::{ + self, + codec::{ArkReader, ArkWriter, HasDeserialization, HasSerialization}, + R1CS, +}; +use alloc::vec::Vec; +use ark_crypto_primitives::SNARK; +use ark_ec::PairingEngine; +use ark_groth16::{Groth16 as ArkGroth16, PreparedVerifyingKey, ProvingKey}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write}; +use core::marker::PhantomData; +use manta_crypto::{ + constraint::ProofSystem, + rand::{CryptoRng, RngCore, SizedRng}, +}; +use manta_util::codec::{self, DecodeError}; + +#[cfg(feature = "serde")] +use manta_util::serde::{Deserialize, Serialize}; + +/// Proof System Error +/// +/// This is the error state of the [`Groth16`] proof system methods. This type is intentionally +/// opaque so that error details are not revealed. +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +pub struct Error; + +/// Groth16 Proof +#[derive(derivative::Derivative)] +#[derivative(Clone, Debug, Default, Eq, PartialEq)] +pub struct Proof<E>(pub ark_groth16::Proof<E>) +where + E: PairingEngine; + +impl<E> scale_codec::Decode for Proof<E> +where + E: PairingEngine, +{ + #[inline] + fn decode<I>(input: &mut I) -> Result<Self, scale_codec::Error> + where + I: scale_codec::Input, + { + Ok(Self( + CanonicalDeserialize::deserialize(arkworks::codec::ScaleCodecReader(input)) + .map_err(|_| "Deserialization Error")?, + )) + } +} + +impl<E> scale_codec::Encode for Proof<E> +where + E: PairingEngine, +{ + #[inline] + fn using_encoded<R, Encoder>(&self, f: Encoder) -> R + where + Encoder: FnOnce(&[u8]) -> R, + { + let mut buffer = Vec::new(); + self.0 + .serialize(&mut buffer) + .expect("Encoding is not allowed to fail."); + f(&buffer) + } +} + +impl<E> scale_codec::EncodeLike for Proof<E> where E: PairingEngine {} + +/// Proving Context +#[derive(derivative::Derivative, CanonicalSerialize, CanonicalDeserialize)] +#[derivative(Clone, Debug, Eq, PartialEq)] +pub struct ProvingContext<E>(pub ProvingKey<E>) +where + E: PairingEngine; + +impl<E> ProvingContext<E> +where + E: PairingEngine, +{ + /// Builds a new [`ProvingContext`] from `proving_key`. + #[inline] + pub fn new(proving_key: ProvingKey<E>) -> Self { + Self(proving_key) + } +} + +impl<E> codec::Decode for ProvingContext<E> +where + E: PairingEngine, +{ + type Error = SerializationError; + + #[inline] + fn decode<R>(reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> + where + R: codec::Read, + { + let mut reader = ArkReader::new(reader); + match CanonicalDeserialize::deserialize_unchecked(&mut reader) { + Ok(value) => reader + .finish() + .map(move |_| Self(value)) + .map_err(DecodeError::Read), + Err(err) => Err(DecodeError::Decode(err)), + } + } +} + +impl<E> codec::Encode for ProvingContext<E> +where + E: PairingEngine, +{ + #[inline] + fn encode<W>(&self, writer: W) -> Result<(), W::Error> + where + W: codec::Write, + { + let mut writer = ArkWriter::new(writer); + let _ = self.0.serialize_unchecked(&mut writer); + writer.finish().map(move |_| ()) + } +} + +/// Verifying Context +#[derive(derivative::Derivative)] +#[derivative(Clone, Debug, Default)] +pub struct VerifyingContext<E>(pub PreparedVerifyingKey<E>) +where + E: PairingEngine; + +impl<E> CanonicalSerialize for VerifyingContext<E> +where + E: PairingEngine, + for<'s> E::G2Prepared: HasSerialization<'s>, +{ + #[inline] + fn serialize<W>(&self, mut writer: W) -> Result<(), SerializationError> + where + W: Write, + { + let PreparedVerifyingKey { + vk, + alpha_g1_beta_g2, + gamma_g2_neg_pc, + delta_g2_neg_pc, + } = &self.0; + vk.serialize(&mut writer)?; + alpha_g1_beta_g2.serialize(&mut writer)?; + <E::G2Prepared as HasSerialization>::Serialize::from(gamma_g2_neg_pc) + .serialize(&mut writer)?; + <E::G2Prepared as HasSerialization>::Serialize::from(delta_g2_neg_pc) + .serialize(&mut writer)?; + Ok(()) + } + + #[inline] + fn serialized_size(&self) -> usize { + let PreparedVerifyingKey { + vk, + alpha_g1_beta_g2, + gamma_g2_neg_pc, + delta_g2_neg_pc, + } = &self.0; + vk.serialized_size() + + alpha_g1_beta_g2.serialized_size() + + <E::G2Prepared as HasSerialization>::Serialize::from(gamma_g2_neg_pc) + .serialized_size() + + <E::G2Prepared as HasSerialization>::Serialize::from(delta_g2_neg_pc) + .serialized_size() + } + + #[inline] + fn serialize_uncompressed<W>(&self, mut writer: W) -> Result<(), SerializationError> + where + W: Write, + { + let PreparedVerifyingKey { + vk, + alpha_g1_beta_g2, + gamma_g2_neg_pc, + delta_g2_neg_pc, + } = &self.0; + vk.serialize_uncompressed(&mut writer)?; + alpha_g1_beta_g2.serialize_uncompressed(&mut writer)?; + <E::G2Prepared as HasSerialization>::Serialize::from(gamma_g2_neg_pc) + .serialize_uncompressed(&mut writer)?; + <E::G2Prepared as HasSerialization>::Serialize::from(delta_g2_neg_pc) + .serialize_uncompressed(&mut writer)?; + Ok(()) + } + + #[inline] + fn serialize_unchecked<W>(&self, mut writer: W) -> Result<(), SerializationError> + where + W: Write, + { + let PreparedVerifyingKey { + vk, + alpha_g1_beta_g2, + gamma_g2_neg_pc, + delta_g2_neg_pc, + } = &self.0; + vk.serialize_unchecked(&mut writer)?; + alpha_g1_beta_g2.serialize_unchecked(&mut writer)?; + <E::G2Prepared as HasSerialization>::Serialize::from(gamma_g2_neg_pc) + .serialize_unchecked(&mut writer)?; + <E::G2Prepared as HasSerialization>::Serialize::from(delta_g2_neg_pc) + .serialize_unchecked(&mut writer)?; + Ok(()) + } + + #[inline] + fn uncompressed_size(&self) -> usize { + let PreparedVerifyingKey { + vk, + alpha_g1_beta_g2, + gamma_g2_neg_pc, + delta_g2_neg_pc, + } = &self.0; + vk.uncompressed_size() + + alpha_g1_beta_g2.uncompressed_size() + + <E::G2Prepared as HasSerialization>::Serialize::from(gamma_g2_neg_pc) + .uncompressed_size() + + <E::G2Prepared as HasSerialization>::Serialize::from(delta_g2_neg_pc) + .uncompressed_size() + } +} + +impl<E> CanonicalDeserialize for VerifyingContext<E> +where + E: PairingEngine, + E::G2Prepared: HasDeserialization, +{ + #[inline] + fn deserialize<R>(mut reader: R) -> Result<Self, SerializationError> + where + R: Read, + { + Ok(Self(PreparedVerifyingKey { + vk: CanonicalDeserialize::deserialize(&mut reader)?, + alpha_g1_beta_g2: CanonicalDeserialize::deserialize(&mut reader)?, + gamma_g2_neg_pc: <E::G2Prepared as HasDeserialization>::Deserialize::deserialize( + &mut reader, + )? + .into(), + delta_g2_neg_pc: <E::G2Prepared as HasDeserialization>::Deserialize::deserialize( + &mut reader, + )? + .into(), + })) + } + + #[inline] + fn deserialize_uncompressed<R>(mut reader: R) -> Result<Self, SerializationError> + where + R: Read, + { + Ok(Self(PreparedVerifyingKey { + vk: CanonicalDeserialize::deserialize_uncompressed(&mut reader)?, + alpha_g1_beta_g2: CanonicalDeserialize::deserialize_uncompressed(&mut reader)?, + gamma_g2_neg_pc: + <E::G2Prepared as HasDeserialization>::Deserialize::deserialize_uncompressed( + &mut reader, + )? + .into(), + delta_g2_neg_pc: + <E::G2Prepared as HasDeserialization>::Deserialize::deserialize_uncompressed( + &mut reader, + )? + .into(), + })) + } + + #[inline] + fn deserialize_unchecked<R>(mut reader: R) -> Result<Self, SerializationError> + where + R: Read, + { + Ok(Self(PreparedVerifyingKey { + vk: CanonicalDeserialize::deserialize_unchecked(&mut reader)?, + alpha_g1_beta_g2: CanonicalDeserialize::deserialize_unchecked(&mut reader)?, + gamma_g2_neg_pc: + <E::G2Prepared as HasDeserialization>::Deserialize::deserialize_unchecked( + &mut reader, + )? + .into(), + delta_g2_neg_pc: + <E::G2Prepared as HasDeserialization>::Deserialize::deserialize_unchecked( + &mut reader, + )? + .into(), + })) + } +} + +impl<E> codec::Decode for VerifyingContext<E> +where + E: PairingEngine, + E::G2Prepared: HasDeserialization, +{ + type Error = SerializationError; + + #[inline] + fn decode<R>(reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> + where + R: codec::Read, + { + let mut reader = ArkReader::new(reader); + match CanonicalDeserialize::deserialize(&mut reader) { + Ok(value) => reader + .finish() + .map(move |_| value) + .map_err(DecodeError::Read), + Err(err) => Err(DecodeError::Decode(err)), + } + } +} + +impl<E> codec::Encode for VerifyingContext<E> +where + E: PairingEngine, + for<'s> E::G2Prepared: HasSerialization<'s>, +{ + #[inline] + fn encode<W>(&self, writer: W) -> Result<(), W::Error> + where + W: codec::Write, + { + let mut writer = ArkWriter::new(writer); + let _ = self.serialize(&mut writer); + writer.finish().map(move |_| ()) + } +} + +/// Arkworks Groth16 Proof System +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Groth16<E>(PhantomData<E>) +where + E: PairingEngine; + +impl<E> ProofSystem for Groth16<E> +where + E: PairingEngine, +{ + type ConstraintSystem = R1CS<E::Fr>; + type PublicParameters = (); + type ProvingContext = ProvingContext<E>; + type VerifyingContext = VerifyingContext<E>; + type Input = Vec<E::Fr>; + type Proof = Proof<E>; + type Error = Error; + + #[inline] + fn for_unknown() -> Self::ConstraintSystem { + Self::ConstraintSystem::for_unknown() + } + + #[inline] + fn for_known() -> Self::ConstraintSystem { + Self::ConstraintSystem::for_known() + } + + #[inline] + fn generate_context<R>( + public_parameters: &Self::PublicParameters, + cs: Self::ConstraintSystem, + rng: &mut R, + ) -> Result<(Self::ProvingContext, Self::VerifyingContext), Self::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + let _ = public_parameters; + let (proving_key, verifying_key) = + ArkGroth16::circuit_specific_setup(cs, &mut SizedRng(rng)).map_err(|_| Error)?; + Ok(( + ProvingContext(proving_key), + VerifyingContext(ArkGroth16::process_vk(&verifying_key).map_err(|_| Error)?), + )) + } + + #[inline] + fn prove<R>( + context: &Self::ProvingContext, + cs: Self::ConstraintSystem, + rng: &mut R, + ) -> Result<Self::Proof, Self::Error> + where + R: CryptoRng + RngCore + ?Sized, + { + ArkGroth16::prove(&context.0, cs, &mut SizedRng(rng)) + .map(Proof) + .map_err(|_| Error) + } + + #[inline] + fn verify( + context: &Self::VerifyingContext, + input: &Self::Input, + proof: &Self::Proof, + ) -> Result<bool, Self::Error> { + ArkGroth16::verify_with_processed_vk(&context.0, input, &proof.0).map_err(|_| Error) + } +} diff --git a/manta-pay/src/crypto/constraint/arkworks/mod.rs b/manta-pay/src/crypto/constraint/arkworks/mod.rs index 763d6eb8f..779d5a220 100644 --- a/manta-pay/src/crypto/constraint/arkworks/mod.rs +++ b/manta-pay/src/crypto/constraint/arkworks/mod.rs @@ -16,8 +16,381 @@ //! Arkworks Constraint System and Proof System Implementations -mod constraint_system; -mod proof_system; +use alloc::vec::Vec; +use ark_ff::PrimeField; +use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget, select::CondSelectGadget}; +use ark_relations::{ + ns, r1cs as ark_r1cs, + r1cs::{ConstraintSynthesizer, ConstraintSystemRef}, +}; +use manta_crypto::{ + constraint::{ + measure::Measure, Add, ConditionalSelect, Constant, ConstraintSystem, Equal, Public, + Secret, Variable, + }, + rand::{CryptoRng, RngCore, Sample, Standard}, +}; +use manta_util::codec::{Decode, DecodeError, Encode, Read, Write}; -pub use constraint_system::*; -pub use proof_system::*; +pub use ark_r1cs::SynthesisError; +pub use ark_r1cs_std::{bits::boolean::Boolean, fields::fp::FpVar}; + +pub mod codec; +pub mod pairing; + +#[cfg(feature = "groth16")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "groth16")))] +pub mod groth16; + +/// Prime Field Element +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Fp<F>(pub F) +where + F: PrimeField; + +impl<F> Decode for Fp<F> +where + F: PrimeField, +{ + type Error = codec::SerializationError; + + #[inline] + fn decode<R>(reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> + where + R: Read, + { + let mut reader = codec::ArkReader::new(reader); + match codec::CanonicalDeserialize::deserialize(&mut reader) { + Ok(value) => reader + .finish() + .map(move |_| Self(value)) + .map_err(DecodeError::Read), + Err(err) => Err(DecodeError::Decode(err)), + } + } +} + +impl<F> Encode for Fp<F> +where + F: PrimeField, +{ + #[inline] + fn encode<W>(&self, writer: W) -> Result<(), W::Error> + where + W: Write, + { + let mut writer = codec::ArkWriter::new(writer); + let _ = self.0.serialize(&mut writer); + writer.finish().map(move |_| ()) + } +} + +impl<F> scale_codec::Decode for Fp<F> +where + F: PrimeField, +{ + #[inline] + fn decode<I>(input: &mut I) -> Result<Self, scale_codec::Error> + where + I: scale_codec::Input, + { + Ok(Self( + codec::CanonicalDeserialize::deserialize(codec::ScaleCodecReader(input)) + .map_err(|_| "Deserialization Error")?, + )) + } +} + +impl<F> scale_codec::Encode for Fp<F> +where + F: PrimeField, +{ + #[inline] + fn using_encoded<R, Encoder>(&self, f: Encoder) -> R + where + Encoder: FnOnce(&[u8]) -> R, + { + let mut buffer = Vec::new(); + self.0 + .serialize(&mut buffer) + .expect("Encoding is not allowed to fail."); + f(&buffer) + } +} + +impl<F> scale_codec::EncodeLike for Fp<F> where F: PrimeField {} + +impl<F> Sample for Fp<F> +where + F: PrimeField, +{ + #[inline] + fn sample<R>(distribution: Standard, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + let _ = distribution; + Self(F::rand(rng)) + } +} + +/// Synthesis Result +pub type SynthesisResult<T = ()> = Result<T, SynthesisError>; + +/// Returns an empty variable assignment for setup mode. +/// +/// # Warning +/// +/// This does not work for all variable assignments! For some assignemnts, the variable inherits +/// some structure from its input, like its length or number of bits, which are only known at +/// run-time. For those cases, some mocking is required and this function can not be used directly. +#[inline] +pub fn empty<T>() -> SynthesisResult<T> { + Err(SynthesisError::AssignmentMissing) +} + +/// Returns a filled variable assignment with the given `value`. +#[inline] +pub fn full<T>(value: T) -> impl FnOnce() -> SynthesisResult<T> { + move || Ok(value) +} + +/// Arkworks Rank-1 Constraint System +pub struct R1CS<F> +where + F: PrimeField, +{ + /// Constraint System + pub(crate) cs: ark_r1cs::ConstraintSystemRef<F>, +} + +impl<F> R1CS<F> +where + F: PrimeField, +{ + /// Constructs a new constraint system which is ready for unknown variables. + #[inline] + pub fn for_unknown() -> Self { + // FIXME: This might not be the right setup for all proof systems. + let cs = ark_r1cs::ConstraintSystem::new_ref(); + cs.set_optimization_goal(ark_r1cs::OptimizationGoal::Constraints); + cs.set_mode(ark_r1cs::SynthesisMode::Setup); + Self { cs } + } + + /// Constructs a new constraint system which is ready for known variables. + #[inline] + pub fn for_known() -> Self { + // FIXME: This might not be the right setup for all proof systems. + let cs = ark_r1cs::ConstraintSystem::new_ref(); + cs.set_optimization_goal(ark_r1cs::OptimizationGoal::Constraints); + Self { cs } + } +} + +impl<F> ConstraintSystem for R1CS<F> +where + F: PrimeField, +{ + type Bool = Boolean<F>; + + #[inline] + fn assert(&mut self, b: Self::Bool) { + b.enforce_equal(&Boolean::TRUE) + .expect("Enforcing equality is not allowed to fail."); + } +} + +impl<F> Measure for R1CS<F> +where + F: PrimeField, +{ + #[inline] + fn constraint_count(&self) -> usize { + self.cs.num_constraints() + } + + #[inline] + fn public_variable_count(&self) -> Option<usize> { + Some(self.cs.num_instance_variables()) + } + + #[inline] + fn secret_variable_count(&self) -> Option<usize> { + Some(self.cs.num_witness_variables()) + } +} + +impl<F> ConstraintSynthesizer<F> for R1CS<F> +where + F: PrimeField, +{ + /// Generates constraints for `self` by copying them into `cs`. This method is necessary to hook + /// into the proof system traits defined in `arkworks`. + #[inline] + fn generate_constraints(self, cs: ConstraintSystemRef<F>) -> SynthesisResult { + let precomputed_cs = self + .cs + .into_inner() + .expect("We own this constraint system so we can consume it."); + let mut target_cs = cs + .borrow_mut() + .expect("This is given to us to mutate so it can't be borrowed by anyone else."); + *target_cs = precomputed_cs; + Ok(()) + } +} + +impl<F> Constant<R1CS<F>> for Boolean<F> +where + F: PrimeField, +{ + type Type = bool; + + #[inline] + fn new_constant(this: &Self::Type, compiler: &mut R1CS<F>) -> Self { + AllocVar::new_constant(ns!(compiler.cs, "boolean constant"), this) + .expect("Variable allocation is not allowed to fail.") + } +} + +impl<F> Variable<Public, R1CS<F>> for Boolean<F> +where + F: PrimeField, +{ + type Type = bool; + + #[inline] + fn new_known(this: &Self::Type, compiler: &mut R1CS<F>) -> Self { + Self::new_input(ns!(compiler.cs, "boolean public input"), full(this)) + .expect("Variable allocation is not allowed to fail.") + } + + #[inline] + fn new_unknown(compiler: &mut R1CS<F>) -> Self { + Self::new_input(ns!(compiler.cs, "boolean public input"), empty::<bool>) + .expect("Variable allocation is not allowed to fail.") + } +} + +impl<F> Variable<Secret, R1CS<F>> for Boolean<F> +where + F: PrimeField, +{ + type Type = bool; + + #[inline] + fn new_known(this: &Self::Type, compiler: &mut R1CS<F>) -> Self { + Self::new_witness(ns!(compiler.cs, "boolean secret witness"), full(this)) + .expect("Variable allocation is not allowed to fail.") + } + + #[inline] + fn new_unknown(compiler: &mut R1CS<F>) -> Self { + Self::new_witness(ns!(compiler.cs, "boolean secret witness"), empty::<bool>) + .expect("Variable allocation is not allowed to fail.") + } +} + +impl<F> Equal<R1CS<F>> for Boolean<F> +where + F: PrimeField, +{ + #[inline] + fn eq(lhs: &Self, rhs: &Self, compiler: &mut R1CS<F>) -> Boolean<F> { + let _ = compiler; + lhs.is_eq(rhs) + .expect("Equality checking is not allowed to fail.") + } +} + +impl<F> Constant<R1CS<F>> for FpVar<F> +where + F: PrimeField, +{ + type Type = Fp<F>; + + #[inline] + fn new_constant(this: &Self::Type, compiler: &mut R1CS<F>) -> Self { + AllocVar::new_constant(ns!(compiler.cs, "field constant"), this.0) + .expect("Variable allocation is not allowed to fail.") + } +} + +impl<F> Variable<Public, R1CS<F>> for FpVar<F> +where + F: PrimeField, +{ + type Type = Fp<F>; + + #[inline] + fn new_known(this: &Self::Type, compiler: &mut R1CS<F>) -> Self { + Self::new_input(ns!(compiler.cs, "field public input"), full(this.0)) + .expect("Variable allocation is not allowed to fail.") + } + + #[inline] + fn new_unknown(compiler: &mut R1CS<F>) -> Self { + Self::new_input(ns!(compiler.cs, "field public input"), empty::<F>) + .expect("Variable allocation is not allowed to fail.") + } +} + +impl<F> Variable<Secret, R1CS<F>> for FpVar<F> +where + F: PrimeField, +{ + type Type = Fp<F>; + + #[inline] + fn new_known(this: &Self::Type, compiler: &mut R1CS<F>) -> Self { + Self::new_witness(ns!(compiler.cs, "field secret witness"), full(this.0)) + .expect("Variable allocation is not allowed to fail.") + } + + #[inline] + fn new_unknown(compiler: &mut R1CS<F>) -> Self { + Self::new_witness(ns!(compiler.cs, "field secret witness"), empty::<F>) + .expect("Variable allocation is not allowed to fail.") + } +} + +impl<F> Equal<R1CS<F>> for FpVar<F> +where + F: PrimeField, +{ + #[inline] + fn eq(lhs: &Self, rhs: &Self, compiler: &mut R1CS<F>) -> Boolean<F> { + let _ = compiler; + lhs.is_eq(rhs) + .expect("Equality checking is not allowed to fail.") + } +} + +impl<F> ConditionalSelect<R1CS<F>> for FpVar<F> +where + F: PrimeField, +{ + #[inline] + fn select( + bit: &Boolean<F>, + true_value: &Self, + false_value: &Self, + compiler: &mut R1CS<F>, + ) -> Self { + let _ = compiler; + Self::conditionally_select(bit, true_value, false_value) + .expect("Conditionally selecting from two values is not allowed to fail.") + } +} + +impl<F> Add<R1CS<F>> for FpVar<F> +where + F: PrimeField, +{ + #[inline] + fn add(lhs: Self, rhs: Self, compiler: &mut R1CS<F>) -> Self { + let _ = compiler; + lhs + rhs + } +} diff --git a/manta-pay/src/crypto/constraint/arkworks/pairing.rs b/manta-pay/src/crypto/constraint/arkworks/pairing.rs new file mode 100644 index 000000000..88336c19d --- /dev/null +++ b/manta-pay/src/crypto/constraint/arkworks/pairing.rs @@ -0,0 +1,177 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Pairing Engine Utilities + +/// BLS-12 Utilities +pub mod bls12 { + use crate::crypto::constraint::arkworks::codec::{HasDeserialization, HasSerialization}; + use alloc::vec::Vec; + use ark_ec::models::bls12::{g2, Bls12Parameters}; + use ark_ff::Fp2; + use ark_serialize::{ + CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write, + }; + + /// Line Evaluation Coefficients + pub type EllCoeff<F> = (F, F, F); + + /// G2 Prepared Point + #[derive(derivative::Derivative, CanonicalSerialize, CanonicalDeserialize)] + #[derivative(Clone, Default, Debug, Eq, PartialEq)] + pub struct G2Prepared<P> + where + P: Bls12Parameters, + { + /// Coefficients + pub ell_coeffs: Vec<EllCoeff<Fp2<P::Fp2Params>>>, + + /// Infinity Flag + pub infinity: bool, + } + + impl<P> From<g2::G2Prepared<P>> for G2Prepared<P> + where + P: Bls12Parameters, + { + #[inline] + fn from(point: g2::G2Prepared<P>) -> Self { + Self { + ell_coeffs: point.ell_coeffs, + infinity: point.infinity, + } + } + } + + impl<P> From<G2Prepared<P>> for g2::G2Prepared<P> + where + P: Bls12Parameters, + { + #[inline] + fn from(point: G2Prepared<P>) -> Self { + Self { + ell_coeffs: point.ell_coeffs, + infinity: point.infinity, + } + } + } + + /// G2 Prepared Point Reference + #[derive(derivative::Derivative)] + #[derivative(Debug, Eq, PartialEq)] + pub struct G2PreparedRef<'p, P>(pub &'p g2::G2Prepared<P>) + where + P: Bls12Parameters; + + impl<'p, P> CanonicalSerialize for G2PreparedRef<'p, P> + where + P: Bls12Parameters, + { + #[inline] + fn serialize<W>(&self, mut writer: W) -> Result<(), SerializationError> + where + W: Write, + { + let g2::G2Prepared { + ell_coeffs, + infinity, + } = &self.0; + ell_coeffs.serialize(&mut writer)?; + infinity.serialize(&mut writer)?; + Ok(()) + } + + #[inline] + fn serialized_size(&self) -> usize { + let g2::G2Prepared { + ell_coeffs, + infinity, + } = &self.0; + ell_coeffs.serialized_size() + infinity.serialized_size() + } + + #[inline] + fn serialize_uncompressed<W>(&self, mut writer: W) -> Result<(), SerializationError> + where + W: Write, + { + let g2::G2Prepared { + ell_coeffs, + infinity, + } = &self.0; + ell_coeffs.serialize_uncompressed(&mut writer)?; + infinity.serialize_uncompressed(&mut writer)?; + Ok(()) + } + + #[inline] + fn serialize_unchecked<W>(&self, mut writer: W) -> Result<(), SerializationError> + where + W: Write, + { + let g2::G2Prepared { + ell_coeffs, + infinity, + } = &self.0; + ell_coeffs.serialize_unchecked(&mut writer)?; + infinity.serialize_unchecked(&mut writer)?; + Ok(()) + } + + #[inline] + fn uncompressed_size(&self) -> usize { + let g2::G2Prepared { + ell_coeffs, + infinity, + } = &self.0; + ell_coeffs.uncompressed_size() + infinity.uncompressed_size() + } + } + + impl<'p, P> From<&'p g2::G2Prepared<P>> for G2PreparedRef<'p, P> + where + P: Bls12Parameters, + { + #[inline] + fn from(point: &'p g2::G2Prepared<P>) -> Self { + Self(point) + } + } + + impl<'p, P> From<G2PreparedRef<'p, P>> for &'p g2::G2Prepared<P> + where + P: Bls12Parameters, + { + #[inline] + fn from(point: G2PreparedRef<'p, P>) -> Self { + point.0 + } + } + + impl<'p, P> HasSerialization<'p> for g2::G2Prepared<P> + where + P: Bls12Parameters, + { + type Serialize = G2PreparedRef<'p, P>; + } + + impl<P> HasDeserialization for g2::G2Prepared<P> + where + P: Bls12Parameters, + { + type Deserialize = G2Prepared<P>; + } +} diff --git a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs b/manta-pay/src/crypto/constraint/arkworks/proof_system.rs deleted file mode 100644 index cb923a695..000000000 --- a/manta-pay/src/crypto/constraint/arkworks/proof_system.rs +++ /dev/null @@ -1,621 +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 <http://www.gnu.org/licenses/>. - -//! Arkworks Proof System Implementations - -use crate::crypto::constraint::arkworks::{SynthesisResult, R1CS}; -use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef}; - -/// Constraint Synthesizer Wrapper -/// -/// This wraps an [`R1CS`] constraint system and allows it to be used as a [`ConstraintSynthesizer`] -/// for building proofs using arkworks proof systems. -pub struct ConstraintSynthesizerWrapper<F>(pub R1CS<F>) -where - F: ark_ff::PrimeField; - -impl<F> ConstraintSynthesizer<F> for ConstraintSynthesizerWrapper<F> -where - F: ark_ff::PrimeField, -{ - #[inline] - fn generate_constraints(self, cs: ConstraintSystemRef<F>) -> SynthesisResult { - let precomputed_cs = self - .0 - .cs - .into_inner() - .expect("We own this constraint system so we can consume it."); - let mut target_cs = cs - .borrow_mut() - .expect("This is given to us to mutate so it can't be borrowed by anyone else."); - *target_cs = precomputed_cs; - Ok(()) - } -} - -/// Pairing Engine Utilities -pub mod pairing { - /// BLS-12 Utilities - pub mod bls12 { - use crate::crypto::constraint::arkworks::codec::{HasDeserialization, HasSerialization}; - use alloc::vec::Vec; - use ark_ec::models::bls12::{g2, Bls12Parameters}; - use ark_ff::Fp2; - use ark_serialize::{ - CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write, - }; - - /// Line Evaluation Coefficients - pub type EllCoeff<F> = (F, F, F); - - /// G2 Prepared Point - #[derive(derivative::Derivative, CanonicalSerialize, CanonicalDeserialize)] - #[derivative(Clone, Default, Debug, Eq, PartialEq)] - pub struct G2Prepared<P> - where - P: Bls12Parameters, - { - /// Coefficients - pub ell_coeffs: Vec<EllCoeff<Fp2<P::Fp2Params>>>, - - /// Infinity Flag - pub infinity: bool, - } - - impl<P> From<g2::G2Prepared<P>> for G2Prepared<P> - where - P: Bls12Parameters, - { - #[inline] - fn from(point: g2::G2Prepared<P>) -> Self { - Self { - ell_coeffs: point.ell_coeffs, - infinity: point.infinity, - } - } - } - - impl<P> From<G2Prepared<P>> for g2::G2Prepared<P> - where - P: Bls12Parameters, - { - #[inline] - fn from(point: G2Prepared<P>) -> Self { - Self { - ell_coeffs: point.ell_coeffs, - infinity: point.infinity, - } - } - } - - /// G2 Prepared Point Reference - #[derive(derivative::Derivative)] - #[derivative(Debug, Eq, PartialEq)] - pub struct G2PreparedRef<'p, P>(pub &'p g2::G2Prepared<P>) - where - P: Bls12Parameters; - - impl<'p, P> CanonicalSerialize for G2PreparedRef<'p, P> - where - P: Bls12Parameters, - { - #[inline] - fn serialize<W>(&self, mut writer: W) -> Result<(), SerializationError> - where - W: Write, - { - let g2::G2Prepared { - ell_coeffs, - infinity, - } = &self.0; - ell_coeffs.serialize(&mut writer)?; - infinity.serialize(&mut writer)?; - Ok(()) - } - - #[inline] - fn serialized_size(&self) -> usize { - let g2::G2Prepared { - ell_coeffs, - infinity, - } = &self.0; - ell_coeffs.serialized_size() + infinity.serialized_size() - } - - #[inline] - fn serialize_uncompressed<W>(&self, mut writer: W) -> Result<(), SerializationError> - where - W: Write, - { - let g2::G2Prepared { - ell_coeffs, - infinity, - } = &self.0; - ell_coeffs.serialize_uncompressed(&mut writer)?; - infinity.serialize_uncompressed(&mut writer)?; - Ok(()) - } - - #[inline] - fn serialize_unchecked<W>(&self, mut writer: W) -> Result<(), SerializationError> - where - W: Write, - { - let g2::G2Prepared { - ell_coeffs, - infinity, - } = &self.0; - ell_coeffs.serialize_unchecked(&mut writer)?; - infinity.serialize_unchecked(&mut writer)?; - Ok(()) - } - - #[inline] - fn uncompressed_size(&self) -> usize { - let g2::G2Prepared { - ell_coeffs, - infinity, - } = &self.0; - ell_coeffs.uncompressed_size() + infinity.uncompressed_size() - } - } - - impl<'p, P> From<&'p g2::G2Prepared<P>> for G2PreparedRef<'p, P> - where - P: Bls12Parameters, - { - #[inline] - fn from(point: &'p g2::G2Prepared<P>) -> Self { - Self(point) - } - } - - impl<'p, P> From<G2PreparedRef<'p, P>> for &'p g2::G2Prepared<P> - where - P: Bls12Parameters, - { - #[inline] - fn from(point: G2PreparedRef<'p, P>) -> Self { - point.0 - } - } - - impl<'p, P> HasSerialization<'p> for g2::G2Prepared<P> - where - P: Bls12Parameters, - { - type Serialize = G2PreparedRef<'p, P>; - } - - impl<P> HasDeserialization for g2::G2Prepared<P> - where - P: Bls12Parameters, - { - type Deserialize = G2Prepared<P>; - } - } -} - -/// Groth16 Proving System -#[cfg(feature = "groth16")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "groth16")))] -pub mod groth16 { - use super::*; - use crate::crypto::constraint::arkworks::{ - self, - codec::{ArkReader, ArkWriter, HasDeserialization, HasSerialization}, - SynthesisError, - }; - use alloc::vec::Vec; - use ark_crypto_primitives::SNARK; - use ark_ec::PairingEngine; - use ark_groth16::{Groth16 as ArkGroth16, PreparedVerifyingKey}; - use ark_serialize::{ - CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write, - }; - use core::marker::PhantomData; - use manta_crypto::{ - constraint::ProofSystem, - rand::{CryptoRng, RngCore, SizedRng}, - }; - use manta_util::codec::{self, DecodeError}; - - pub use ark_groth16::ProvingKey; - - /// Groth16 Proof - #[derive(derivative::Derivative)] - #[derivative(Clone, Debug, Default, Eq, PartialEq)] - pub struct Proof<E>(pub ark_groth16::Proof<E>) - where - E: PairingEngine; - - impl<E> scale_codec::Decode for Proof<E> - where - E: PairingEngine, - { - #[inline] - fn decode<I>(input: &mut I) -> Result<Self, scale_codec::Error> - where - I: scale_codec::Input, - { - Ok(Self( - ark_groth16::Proof::deserialize(arkworks::codec::ScaleCodecReader(input)) - .map_err(|_| "Deserialization Error")?, - )) - } - } - - impl<E> scale_codec::Encode for Proof<E> - where - E: PairingEngine, - { - #[inline] - fn using_encoded<R, Encoder>(&self, f: Encoder) -> R - where - Encoder: FnOnce(&[u8]) -> R, - { - let mut buffer = Vec::new(); - self.0 - .serialize(&mut buffer) - .expect("Encoding is not allowed to fail."); - f(&buffer) - } - } - - impl<E> scale_codec::EncodeLike for Proof<E> where E: PairingEngine {} - - /// Proving Context - #[derive(derivative::Derivative, CanonicalSerialize, CanonicalDeserialize)] - #[derivative(Clone, Debug, Eq, PartialEq)] - pub struct ProvingContext<E>(pub ProvingKey<E>) - where - E: PairingEngine; - - impl<E> ProvingContext<E> - where - E: PairingEngine, - { - /// Builds a new [`ProvingContext`] from `proving_key`. - #[inline] - pub fn new(proving_key: ProvingKey<E>) -> Self { - Self(proving_key) - } - } - - impl<E> codec::Decode for ProvingContext<E> - where - E: PairingEngine, - { - type Error = SerializationError; - - #[inline] - fn decode<R>(reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> - where - R: codec::Read, - { - let mut reader = ArkReader::new(reader); - match CanonicalDeserialize::deserialize_unchecked(&mut reader) { - Ok(value) => reader - .finish() - .map(move |_| Self(value)) - .map_err(DecodeError::Read), - Err(err) => Err(DecodeError::Decode(err)), - } - } - } - - impl<E> codec::Encode for ProvingContext<E> - where - E: PairingEngine, - { - #[inline] - fn encode<W>(&self, writer: W) -> Result<(), W::Error> - where - W: codec::Write, - { - let mut writer = ArkWriter::new(writer); - let _ = self.0.serialize_unchecked(&mut writer); - writer.finish().map(move |_| ()) - } - } - - /// Verifying Context - #[derive(derivative::Derivative)] - #[derivative(Clone, Debug, Default)] - pub struct VerifyingContext<E>(pub PreparedVerifyingKey<E>) - where - E: PairingEngine; - - impl<E> CanonicalSerialize for VerifyingContext<E> - where - E: PairingEngine, - for<'s> E::G2Prepared: HasSerialization<'s>, - { - #[inline] - fn serialize<W>(&self, mut writer: W) -> Result<(), SerializationError> - where - W: Write, - { - let PreparedVerifyingKey { - vk, - alpha_g1_beta_g2, - gamma_g2_neg_pc, - delta_g2_neg_pc, - } = &self.0; - vk.serialize(&mut writer)?; - alpha_g1_beta_g2.serialize(&mut writer)?; - <E::G2Prepared as HasSerialization>::Serialize::from(gamma_g2_neg_pc) - .serialize(&mut writer)?; - <E::G2Prepared as HasSerialization>::Serialize::from(delta_g2_neg_pc) - .serialize(&mut writer)?; - Ok(()) - } - - #[inline] - fn serialized_size(&self) -> usize { - let PreparedVerifyingKey { - vk, - alpha_g1_beta_g2, - gamma_g2_neg_pc, - delta_g2_neg_pc, - } = &self.0; - vk.serialized_size() - + alpha_g1_beta_g2.serialized_size() - + <E::G2Prepared as HasSerialization>::Serialize::from(gamma_g2_neg_pc) - .serialized_size() - + <E::G2Prepared as HasSerialization>::Serialize::from(delta_g2_neg_pc) - .serialized_size() - } - - #[inline] - fn serialize_uncompressed<W>(&self, mut writer: W) -> Result<(), SerializationError> - where - W: Write, - { - let PreparedVerifyingKey { - vk, - alpha_g1_beta_g2, - gamma_g2_neg_pc, - delta_g2_neg_pc, - } = &self.0; - vk.serialize_uncompressed(&mut writer)?; - alpha_g1_beta_g2.serialize_uncompressed(&mut writer)?; - <E::G2Prepared as HasSerialization>::Serialize::from(gamma_g2_neg_pc) - .serialize_uncompressed(&mut writer)?; - <E::G2Prepared as HasSerialization>::Serialize::from(delta_g2_neg_pc) - .serialize_uncompressed(&mut writer)?; - Ok(()) - } - - #[inline] - fn serialize_unchecked<W>(&self, mut writer: W) -> Result<(), SerializationError> - where - W: Write, - { - let PreparedVerifyingKey { - vk, - alpha_g1_beta_g2, - gamma_g2_neg_pc, - delta_g2_neg_pc, - } = &self.0; - vk.serialize_unchecked(&mut writer)?; - alpha_g1_beta_g2.serialize_unchecked(&mut writer)?; - <E::G2Prepared as HasSerialization>::Serialize::from(gamma_g2_neg_pc) - .serialize_unchecked(&mut writer)?; - <E::G2Prepared as HasSerialization>::Serialize::from(delta_g2_neg_pc) - .serialize_unchecked(&mut writer)?; - Ok(()) - } - - #[inline] - fn uncompressed_size(&self) -> usize { - let PreparedVerifyingKey { - vk, - alpha_g1_beta_g2, - gamma_g2_neg_pc, - delta_g2_neg_pc, - } = &self.0; - vk.uncompressed_size() - + alpha_g1_beta_g2.uncompressed_size() - + <E::G2Prepared as HasSerialization>::Serialize::from(gamma_g2_neg_pc) - .uncompressed_size() - + <E::G2Prepared as HasSerialization>::Serialize::from(delta_g2_neg_pc) - .uncompressed_size() - } - } - - impl<E> CanonicalDeserialize for VerifyingContext<E> - where - E: PairingEngine, - E::G2Prepared: HasDeserialization, - { - #[inline] - fn deserialize<R>(mut reader: R) -> Result<Self, SerializationError> - where - R: Read, - { - Ok(Self(PreparedVerifyingKey { - vk: CanonicalDeserialize::deserialize(&mut reader)?, - alpha_g1_beta_g2: CanonicalDeserialize::deserialize(&mut reader)?, - gamma_g2_neg_pc: <E::G2Prepared as HasDeserialization>::Deserialize::deserialize( - &mut reader, - )? - .into(), - delta_g2_neg_pc: <E::G2Prepared as HasDeserialization>::Deserialize::deserialize( - &mut reader, - )? - .into(), - })) - } - - #[inline] - fn deserialize_uncompressed<R>(mut reader: R) -> Result<Self, SerializationError> - where - R: Read, - { - Ok(Self(PreparedVerifyingKey { - vk: CanonicalDeserialize::deserialize_uncompressed(&mut reader)?, - alpha_g1_beta_g2: CanonicalDeserialize::deserialize_uncompressed(&mut reader)?, - gamma_g2_neg_pc: - <E::G2Prepared as HasDeserialization>::Deserialize::deserialize_uncompressed( - &mut reader, - )? - .into(), - delta_g2_neg_pc: - <E::G2Prepared as HasDeserialization>::Deserialize::deserialize_uncompressed( - &mut reader, - )? - .into(), - })) - } - - #[inline] - fn deserialize_unchecked<R>(mut reader: R) -> Result<Self, SerializationError> - where - R: Read, - { - Ok(Self(PreparedVerifyingKey { - vk: CanonicalDeserialize::deserialize_unchecked(&mut reader)?, - alpha_g1_beta_g2: CanonicalDeserialize::deserialize_unchecked(&mut reader)?, - gamma_g2_neg_pc: - <E::G2Prepared as HasDeserialization>::Deserialize::deserialize_unchecked( - &mut reader, - )? - .into(), - delta_g2_neg_pc: - <E::G2Prepared as HasDeserialization>::Deserialize::deserialize_unchecked( - &mut reader, - )? - .into(), - })) - } - } - - impl<E> codec::Decode for VerifyingContext<E> - where - E: PairingEngine, - E::G2Prepared: HasDeserialization, - { - type Error = SerializationError; - - #[inline] - fn decode<R>(reader: R) -> Result<Self, DecodeError<R::Error, Self::Error>> - where - R: codec::Read, - { - let mut reader = ArkReader::new(reader); - match CanonicalDeserialize::deserialize(&mut reader) { - Ok(value) => reader - .finish() - .map(move |_| value) - .map_err(DecodeError::Read), - Err(err) => Err(DecodeError::Decode(err)), - } - } - } - - impl<E> codec::Encode for VerifyingContext<E> - where - E: PairingEngine, - for<'s> E::G2Prepared: HasSerialization<'s>, - { - #[inline] - fn encode<W>(&self, writer: W) -> Result<(), W::Error> - where - W: codec::Write, - { - let mut writer = ArkWriter::new(writer); - let _ = self.serialize(&mut writer); - writer.finish().map(move |_| ()) - } - } - - /// Arkworks Groth16 Proof System - #[derive(derivative::Derivative)] - #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct Groth16<E>(PhantomData<E>) - where - E: PairingEngine; - - impl<E> ProofSystem for Groth16<E> - where - E: PairingEngine, - { - type ConstraintSystem = R1CS<E::Fr>; - type PublicParameters = (); - type ProvingContext = ProvingContext<E>; - type VerifyingContext = VerifyingContext<E>; - type Input = Vec<E::Fr>; - type Proof = Proof<E>; - type Error = SynthesisError; - - #[inline] - fn for_unknown() -> Self::ConstraintSystem { - Self::ConstraintSystem::for_unknown() - } - - #[inline] - fn for_known() -> Self::ConstraintSystem { - Self::ConstraintSystem::for_known() - } - - #[inline] - fn generate_context<R>( - public_parameters: &Self::PublicParameters, - cs: Self::ConstraintSystem, - rng: &mut R, - ) -> Result<(Self::ProvingContext, Self::VerifyingContext), Self::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - let _ = public_parameters; - let (proving_key, verifying_key) = ArkGroth16::circuit_specific_setup( - ConstraintSynthesizerWrapper(cs), - &mut SizedRng(rng), - )?; - Ok(( - ProvingContext(proving_key), - VerifyingContext(ArkGroth16::process_vk(&verifying_key)?), - )) - } - - #[inline] - fn prove<R>( - context: &Self::ProvingContext, - cs: Self::ConstraintSystem, - rng: &mut R, - ) -> Result<Self::Proof, Self::Error> - where - R: CryptoRng + RngCore + ?Sized, - { - ArkGroth16::prove( - &context.0, - ConstraintSynthesizerWrapper(cs), - &mut SizedRng(rng), - ) - .map(Proof) - } - - #[inline] - fn verify( - context: &Self::VerifyingContext, - input: &Self::Input, - proof: &Self::Proof, - ) -> Result<bool, Self::Error> { - ArkGroth16::verify_with_processed_vk(&context.0, input, &proof.0) - } - } -} diff --git a/manta-pay/src/crypto/ecc.rs b/manta-pay/src/crypto/ecc.rs deleted file mode 100644 index 35167be56..000000000 --- a/manta-pay/src/crypto/ecc.rs +++ /dev/null @@ -1,423 +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 <http://www.gnu.org/licenses/>. - -//! Elliptic Curve Primitives - -/// Arkworks Backend -#[cfg(feature = "arkworks")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] -pub mod arkworks { - use crate::crypto::constraint::arkworks::{ - codec::{ArkReader, ArkWriter, ScaleCodecReader}, - empty, full, Boolean, Fp, FpVar, R1CS, - }; - use alloc::vec::Vec; - use ark_ff::{BigInteger, Field, FpParameters, PrimeField}; - use ark_r1cs_std::ToBitsGadget; - use ark_relations::ns; - use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; - use core::marker::PhantomData; - use manta_crypto::{ - constraint::{Allocator, Constant, Equal, Public, Secret, ValueSource, Variable}, - ecc, - key::kdf, - rand::{CryptoRng, RngCore, Sample, Standard}, - }; - use manta_util::codec; - - pub use ark_ec::{AffineCurve, ProjectiveCurve}; - pub use ark_r1cs_std::groups::CurveVar; - - /// Constraint Field Type - type ConstraintField<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField; - - /// Compiler Type - type Compiler<C> = R1CS<ConstraintField<C>>; - - /// Scalar Field Element - pub type Scalar<C> = Fp<<C as ProjectiveCurve>::ScalarField>; - - /// Converts `scalar` to the bit representation of `O`. - #[inline] - pub fn convert_bits<T, O>(scalar: T) -> O::BigInt - where - T: BigInteger, - O: PrimeField, - { - O::BigInt::from_bits_le(&scalar.to_bits_le()) - } - - /// Checks that the modulus of `A` is smaller than that of `B`. - #[inline] - pub fn modulus_is_smaller<A, B>() -> bool - where - A: PrimeField, - B: PrimeField, - { - let modulus_a = A::Params::MODULUS; - let modulus_b = B::Params::MODULUS; - if modulus_a.num_bits() <= modulus_b.num_bits() { - convert_bits::<_, B>(modulus_a) < modulus_b - } else { - modulus_a < convert_bits::<_, A>(modulus_b) - } - } - - /// Lifts an embedded scalar to an outer scalar. - /// - /// # Safety - /// - /// This can only be used whenver the embedded scalar field is **smaller** than the outer scalar - /// field. - #[inline] - pub fn lift_embedded_scalar<C>(scalar: &Scalar<C>) -> Fp<ConstraintField<C>> - where - C: ProjectiveCurve, - { - assert!( - modulus_is_smaller::<C::ScalarField, ConstraintField<C>>(), - "The modulus of the embedded scalar field is larger than that of the constraint field." - ); - Fp(ConstraintField::<C>::from_le_bytes_mod_order( - &scalar.0.into_repr().to_bytes_le(), - )) - } - - /// Elliptic Curve Group Element - #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct Group<C>(pub(crate) C::Affine) - where - C: ProjectiveCurve; - - impl<C> codec::Decode for Group<C> - where - C: ProjectiveCurve, - { - type Error = SerializationError; - - #[inline] - fn decode<R>(reader: R) -> Result<Self, codec::DecodeError<R::Error, Self::Error>> - where - R: codec::Read, - { - let mut reader = ArkReader::new(reader); - match CanonicalDeserialize::deserialize(&mut reader) { - Ok(value) => reader - .finish() - .map(move |_| Self(value)) - .map_err(codec::DecodeError::Read), - Err(err) => Err(codec::DecodeError::Decode(err)), - } - } - } - - impl<C> codec::Encode for Group<C> - where - C: ProjectiveCurve, - { - #[inline] - fn encode<W>(&self, writer: W) -> Result<(), W::Error> - where - W: codec::Write, - { - let mut writer = ArkWriter::new(writer); - let _ = self.0.serialize(&mut writer); - writer.finish().map(|_| ()) - } - } - - impl<C> scale_codec::Decode for Group<C> - where - C: ProjectiveCurve, - { - #[inline] - fn decode<I>(input: &mut I) -> Result<Self, scale_codec::Error> - where - I: scale_codec::Input, - { - Ok(Self( - CanonicalDeserialize::deserialize(ScaleCodecReader(input)) - .map_err(|_| "Deserialization Error")?, - )) - } - } - - impl<C> scale_codec::Encode for Group<C> - where - C: ProjectiveCurve, - { - #[inline] - fn using_encoded<R, Encoder>(&self, f: Encoder) -> R - where - Encoder: FnOnce(&[u8]) -> R, - { - let mut buffer = Vec::new(); - self.0 - .serialize(&mut buffer) - .expect("Encoding is not allowed to fail."); - f(&buffer) - } - } - - impl<C> scale_codec::EncodeLike for Group<C> where C: ProjectiveCurve {} - - impl<C> kdf::AsBytes for Group<C> - where - C: ProjectiveCurve, - { - #[inline] - fn as_bytes(&self) -> Vec<u8> { - let mut buffer = Vec::new(); - self.0 - .serialize_unchecked(&mut buffer) - .expect("Serialization is not allowed to fail."); - buffer - } - } - - impl<C> ecc::ScalarMul for Group<C> - where - C: ProjectiveCurve, - { - type Scalar = Scalar<C>; - type Output = Self; - - #[inline] - fn scalar_mul(&self, scalar: &Self::Scalar, _: &mut ()) -> Self::Output { - Self(self.0.mul(scalar.0.into_repr()).into()) - } - } - - impl<C> Sample for Group<C> - where - C: ProjectiveCurve, - { - #[inline] - fn sample<R>(distribution: Standard, rng: &mut R) -> Self - where - R: CryptoRng + RngCore + ?Sized, - { - let _ = distribution; - Self(C::rand(rng).into()) - } - } - - /// Elliptic Curve Scalar Element Variable - /// - /// # Safety - /// - /// This type can only be used whenever the embedded scalar field is **smaller** than the - /// outer scalar field. - pub struct ScalarVar<C, CV>(pub(crate) FpVar<ConstraintField<C>>, PhantomData<CV>) - where - C: ProjectiveCurve, - CV: CurveVar<C, ConstraintField<C>>; - - impl<C, CV> ScalarVar<C, CV> - where - C: ProjectiveCurve, - CV: CurveVar<C, ConstraintField<C>>, - { - /// Builds a new [`ScalarVar`] from a given `scalar`. - #[inline] - fn new(scalar: FpVar<ConstraintField<C>>) -> Self { - Self(scalar, PhantomData) - } - } - - impl<C, CV> Constant<Compiler<C>> for ScalarVar<C, CV> - where - C: ProjectiveCurve, - CV: CurveVar<C, ConstraintField<C>>, - { - type Type = Scalar<C>; - - #[inline] - fn new_constant(this: &Self::Type, compiler: &mut Compiler<C>) -> Self { - Self::new(lift_embedded_scalar::<C>(this).as_constant(compiler)) - } - } - - impl<C, CV> Variable<Public, Compiler<C>> for ScalarVar<C, CV> - where - C: ProjectiveCurve, - CV: CurveVar<C, ConstraintField<C>>, - { - type Type = Scalar<C>; - - #[inline] - fn new_known(this: &Self::Type, compiler: &mut Compiler<C>) -> Self { - Self::new(lift_embedded_scalar::<C>(this).as_known::<Public, _>(compiler)) - } - - #[inline] - fn new_unknown(compiler: &mut Compiler<C>) -> Self { - Self::new(compiler.allocate_unknown::<Public, _>()) - } - } - - impl<C, CV> Variable<Secret, Compiler<C>> for ScalarVar<C, CV> - where - C: ProjectiveCurve, - CV: CurveVar<C, ConstraintField<C>>, - { - type Type = Scalar<C>; - - #[inline] - fn new_known(this: &Self::Type, compiler: &mut Compiler<C>) -> Self { - Self::new(lift_embedded_scalar::<C>(this).as_known::<Secret, _>(compiler)) - } - - #[inline] - fn new_unknown(compiler: &mut Compiler<C>) -> Self { - Self::new(compiler.allocate_unknown::<Secret, _>()) - } - } - - /// Elliptic Curve Group Element Variable - pub struct GroupVar<C, CV>(pub(crate) CV, PhantomData<C>) - where - C: ProjectiveCurve, - CV: CurveVar<C, ConstraintField<C>>; - - impl<C, CV> GroupVar<C, CV> - where - C: ProjectiveCurve, - CV: CurveVar<C, ConstraintField<C>>, - { - /// Builds a new [`GroupVar`] from a given `point`. - #[inline] - fn new(point: CV) -> Self { - Self(point, PhantomData) - } - } - - impl<C, CV> ecc::ScalarMul<Compiler<C>> for GroupVar<C, CV> - where - C: ProjectiveCurve, - CV: CurveVar<C, ConstraintField<C>>, - { - type Scalar = ScalarVar<C, CV>; - type Output = Self; - - #[inline] - fn scalar_mul(&self, scalar: &Self::Scalar, compiler: &mut Compiler<C>) -> Self::Output { - let _ = compiler; - Self( - self.0 - .scalar_mul_le( - scalar - .0 - .to_bits_le() - .expect("Bit decomposition is not allowed to fail.") - .iter(), - ) - .expect("Scalar multiplication is not allowed to fail."), - PhantomData, - ) - } - } - - impl<C, CV> Equal<Compiler<C>> for GroupVar<C, CV> - where - C: ProjectiveCurve, - CV: CurveVar<C, ConstraintField<C>>, - { - #[inline] - fn eq(lhs: &Self, rhs: &Self, compiler: &mut Compiler<C>) -> Boolean<ConstraintField<C>> { - let _ = compiler; - lhs.0 - .is_eq(&rhs.0) - .expect("Equality checking is not allowed to fail.") - } - } - - impl<C, CV> Constant<Compiler<C>> for GroupVar<C, CV> - where - C: ProjectiveCurve, - CV: CurveVar<C, ConstraintField<C>>, - { - type Type = Group<C>; - - #[inline] - fn new_constant(this: &Self::Type, compiler: &mut Compiler<C>) -> Self { - Self::new( - CV::new_constant(ns!(compiler.cs, "embedded curve point constant"), this.0) - .expect("Variable allocation is not allowed to fail."), - ) - } - } - - impl<C, CV> Variable<Public, Compiler<C>> for GroupVar<C, CV> - where - C: ProjectiveCurve, - CV: CurveVar<C, ConstraintField<C>>, - { - type Type = Group<C>; - - #[inline] - fn new_known(this: &Self::Type, compiler: &mut Compiler<C>) -> Self { - Self::new( - CV::new_input( - ns!(compiler.cs, "embedded curve point public input"), - full(this.0), - ) - .expect("Variable allocation is not allowed to fail."), - ) - } - - #[inline] - fn new_unknown(compiler: &mut Compiler<C>) -> Self { - Self::new( - CV::new_input( - ns!(compiler.cs, "embedded curve point public input"), - empty::<C>, - ) - .expect("Variable allocation is not allowed to fail."), - ) - } - } - - impl<C, CV> Variable<Secret, Compiler<C>> for GroupVar<C, CV> - where - C: ProjectiveCurve, - CV: CurveVar<C, ConstraintField<C>>, - { - type Type = Group<C>; - - #[inline] - fn new_known(this: &Self::Type, compiler: &mut Compiler<C>) -> Self { - Self::new( - CV::new_witness( - ns!(compiler.cs, "embedded curve point secret witness"), - full(this.0), - ) - .expect("Variable allocation is not allowed to fail."), - ) - } - - #[inline] - fn new_unknown(compiler: &mut Compiler<C>) -> Self { - Self::new( - CV::new_witness( - ns!(compiler.cs, "embedded curve point secret witness"), - empty::<C>, - ) - .expect("Variable allocation is not allowed to fail."), - ) - } - } -} diff --git a/manta-pay/src/crypto/ecc/arkworks.rs b/manta-pay/src/crypto/ecc/arkworks.rs new file mode 100644 index 000000000..c2d63245e --- /dev/null +++ b/manta-pay/src/crypto/ecc/arkworks.rs @@ -0,0 +1,418 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Arkworks Elliptic Curve Primitives + +use crate::crypto::constraint::arkworks::{ + codec::{ArkReader, ArkWriter, ScaleCodecReader}, + empty, full, Boolean, Fp, FpVar, R1CS, +}; +use alloc::vec::Vec; +use ark_ff::{BigInteger, Field, FpParameters, PrimeField}; +use ark_r1cs_std::ToBitsGadget; +use ark_relations::ns; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; +use core::marker::PhantomData; +use manta_crypto::{ + constraint::{Allocator, Constant, Equal, Public, Secret, ValueSource, Variable}, + ecc, + key::kdf, + rand::{CryptoRng, RngCore, Sample, Standard}, +}; +use manta_util::codec; + +pub use ark_ec::{AffineCurve, ProjectiveCurve}; +pub use ark_r1cs_std::groups::CurveVar; + +/// Constraint Field Type +type ConstraintField<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField; + +/// Compiler Type +type Compiler<C> = R1CS<ConstraintField<C>>; + +/// Scalar Field Element +pub type Scalar<C> = Fp<<C as ProjectiveCurve>::ScalarField>; + +/// Converts `scalar` to the bit representation of `O`. +#[inline] +pub fn convert_bits<T, O>(scalar: T) -> O::BigInt +where + T: BigInteger, + O: PrimeField, +{ + O::BigInt::from_bits_le(&scalar.to_bits_le()) +} + +/// Checks that the modulus of `A` is smaller than that of `B`. +#[inline] +pub fn modulus_is_smaller<A, B>() -> bool +where + A: PrimeField, + B: PrimeField, +{ + let modulus_a = A::Params::MODULUS; + let modulus_b = B::Params::MODULUS; + if modulus_a.num_bits() <= modulus_b.num_bits() { + convert_bits::<_, B>(modulus_a) < modulus_b + } else { + modulus_a < convert_bits::<_, A>(modulus_b) + } +} + +/// Lifts an embedded scalar to an outer scalar. +/// +/// # Safety +/// +/// This can only be used whenver the embedded scalar field is **smaller** than the outer scalar +/// field. +#[inline] +pub fn lift_embedded_scalar<C>(scalar: &Scalar<C>) -> Fp<ConstraintField<C>> +where + C: ProjectiveCurve, +{ + assert!( + modulus_is_smaller::<C::ScalarField, ConstraintField<C>>(), + "The modulus of the embedded scalar field is larger than that of the constraint field." + ); + Fp(ConstraintField::<C>::from_le_bytes_mod_order( + &scalar.0.into_repr().to_bytes_le(), + )) +} + +/// Elliptic Curve Group Element +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Group<C>(pub(crate) C::Affine) +where + C: ProjectiveCurve; + +impl<C> codec::Decode for Group<C> +where + C: ProjectiveCurve, +{ + type Error = SerializationError; + + #[inline] + fn decode<R>(reader: R) -> Result<Self, codec::DecodeError<R::Error, Self::Error>> + where + R: codec::Read, + { + let mut reader = ArkReader::new(reader); + match CanonicalDeserialize::deserialize(&mut reader) { + Ok(value) => reader + .finish() + .map(move |_| Self(value)) + .map_err(codec::DecodeError::Read), + Err(err) => Err(codec::DecodeError::Decode(err)), + } + } +} + +impl<C> codec::Encode for Group<C> +where + C: ProjectiveCurve, +{ + #[inline] + fn encode<W>(&self, writer: W) -> Result<(), W::Error> + where + W: codec::Write, + { + let mut writer = ArkWriter::new(writer); + let _ = self.0.serialize(&mut writer); + writer.finish().map(|_| ()) + } +} + +impl<C> scale_codec::Decode for Group<C> +where + C: ProjectiveCurve, +{ + #[inline] + fn decode<I>(input: &mut I) -> Result<Self, scale_codec::Error> + where + I: scale_codec::Input, + { + Ok(Self( + CanonicalDeserialize::deserialize(ScaleCodecReader(input)) + .map_err(|_| "Deserialization Error")?, + )) + } +} + +impl<C> scale_codec::Encode for Group<C> +where + C: ProjectiveCurve, +{ + #[inline] + fn using_encoded<R, Encoder>(&self, f: Encoder) -> R + where + Encoder: FnOnce(&[u8]) -> R, + { + let mut buffer = Vec::new(); + self.0 + .serialize(&mut buffer) + .expect("Encoding is not allowed to fail."); + f(&buffer) + } +} + +impl<C> scale_codec::EncodeLike for Group<C> where C: ProjectiveCurve {} + +impl<C> kdf::AsBytes for Group<C> +where + C: ProjectiveCurve, +{ + #[inline] + fn as_bytes(&self) -> Vec<u8> { + let mut buffer = Vec::new(); + self.0 + .serialize_unchecked(&mut buffer) + .expect("Serialization is not allowed to fail."); + buffer + } +} + +impl<C> ecc::ScalarMul for Group<C> +where + C: ProjectiveCurve, +{ + type Scalar = Scalar<C>; + type Output = Self; + + #[inline] + fn scalar_mul(&self, scalar: &Self::Scalar, _: &mut ()) -> Self::Output { + Self(self.0.mul(scalar.0.into_repr()).into()) + } +} + +impl<C> Sample for Group<C> +where + C: ProjectiveCurve, +{ + #[inline] + fn sample<R>(distribution: Standard, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + let _ = distribution; + Self(C::rand(rng).into()) + } +} + +/// Elliptic Curve Scalar Element Variable +/// +/// # Safety +/// +/// This type can only be used whenever the embedded scalar field is **smaller** than the +/// outer scalar field. +pub struct ScalarVar<C, CV>(pub(crate) FpVar<ConstraintField<C>>, PhantomData<CV>) +where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>; + +impl<C, CV> ScalarVar<C, CV> +where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>, +{ + /// Builds a new [`ScalarVar`] from a given `scalar`. + #[inline] + fn new(scalar: FpVar<ConstraintField<C>>) -> Self { + Self(scalar, PhantomData) + } +} + +impl<C, CV> Constant<Compiler<C>> for ScalarVar<C, CV> +where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>, +{ + type Type = Scalar<C>; + + #[inline] + fn new_constant(this: &Self::Type, compiler: &mut Compiler<C>) -> Self { + Self::new(lift_embedded_scalar::<C>(this).as_constant(compiler)) + } +} + +impl<C, CV> Variable<Public, Compiler<C>> for ScalarVar<C, CV> +where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>, +{ + type Type = Scalar<C>; + + #[inline] + fn new_known(this: &Self::Type, compiler: &mut Compiler<C>) -> Self { + Self::new(lift_embedded_scalar::<C>(this).as_known::<Public, _>(compiler)) + } + + #[inline] + fn new_unknown(compiler: &mut Compiler<C>) -> Self { + Self::new(compiler.allocate_unknown::<Public, _>()) + } +} + +impl<C, CV> Variable<Secret, Compiler<C>> for ScalarVar<C, CV> +where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>, +{ + type Type = Scalar<C>; + + #[inline] + fn new_known(this: &Self::Type, compiler: &mut Compiler<C>) -> Self { + Self::new(lift_embedded_scalar::<C>(this).as_known::<Secret, _>(compiler)) + } + + #[inline] + fn new_unknown(compiler: &mut Compiler<C>) -> Self { + Self::new(compiler.allocate_unknown::<Secret, _>()) + } +} + +/// Elliptic Curve Group Element Variable +pub struct GroupVar<C, CV>(pub(crate) CV, PhantomData<C>) +where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>; + +impl<C, CV> GroupVar<C, CV> +where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>, +{ + /// Builds a new [`GroupVar`] from a given `point`. + #[inline] + fn new(point: CV) -> Self { + Self(point, PhantomData) + } +} + +impl<C, CV> ecc::ScalarMul<Compiler<C>> for GroupVar<C, CV> +where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>, +{ + type Scalar = ScalarVar<C, CV>; + type Output = Self; + + #[inline] + fn scalar_mul(&self, scalar: &Self::Scalar, compiler: &mut Compiler<C>) -> Self::Output { + let _ = compiler; + Self( + self.0 + .scalar_mul_le( + scalar + .0 + .to_bits_le() + .expect("Bit decomposition is not allowed to fail.") + .iter(), + ) + .expect("Scalar multiplication is not allowed to fail."), + PhantomData, + ) + } +} + +impl<C, CV> Equal<Compiler<C>> for GroupVar<C, CV> +where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>, +{ + #[inline] + fn eq(lhs: &Self, rhs: &Self, compiler: &mut Compiler<C>) -> Boolean<ConstraintField<C>> { + let _ = compiler; + lhs.0 + .is_eq(&rhs.0) + .expect("Equality checking is not allowed to fail.") + } +} + +impl<C, CV> Constant<Compiler<C>> for GroupVar<C, CV> +where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>, +{ + type Type = Group<C>; + + #[inline] + fn new_constant(this: &Self::Type, compiler: &mut Compiler<C>) -> Self { + Self::new( + CV::new_constant(ns!(compiler.cs, "embedded curve point constant"), this.0) + .expect("Variable allocation is not allowed to fail."), + ) + } +} + +impl<C, CV> Variable<Public, Compiler<C>> for GroupVar<C, CV> +where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>, +{ + type Type = Group<C>; + + #[inline] + fn new_known(this: &Self::Type, compiler: &mut Compiler<C>) -> Self { + Self::new( + CV::new_input( + ns!(compiler.cs, "embedded curve point public input"), + full(this.0), + ) + .expect("Variable allocation is not allowed to fail."), + ) + } + + #[inline] + fn new_unknown(compiler: &mut Compiler<C>) -> Self { + Self::new( + CV::new_input( + ns!(compiler.cs, "embedded curve point public input"), + empty::<C>, + ) + .expect("Variable allocation is not allowed to fail."), + ) + } +} + +impl<C, CV> Variable<Secret, Compiler<C>> for GroupVar<C, CV> +where + C: ProjectiveCurve, + CV: CurveVar<C, ConstraintField<C>>, +{ + type Type = Group<C>; + + #[inline] + fn new_known(this: &Self::Type, compiler: &mut Compiler<C>) -> Self { + Self::new( + CV::new_witness( + ns!(compiler.cs, "embedded curve point secret witness"), + full(this.0), + ) + .expect("Variable allocation is not allowed to fail."), + ) + } + + #[inline] + fn new_unknown(compiler: &mut Compiler<C>) -> Self { + Self::new( + CV::new_witness( + ns!(compiler.cs, "embedded curve point secret witness"), + empty::<C>, + ) + .expect("Variable allocation is not allowed to fail."), + ) + } +} diff --git a/manta-pay/src/crypto/ecc/mod.rs b/manta-pay/src/crypto/ecc/mod.rs new file mode 100644 index 000000000..353326543 --- /dev/null +++ b/manta-pay/src/crypto/ecc/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 <http://www.gnu.org/licenses/>. + +//! Elliptic Curve Primitives + +#[cfg(feature = "arkworks")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "arkworks")))] +pub mod arkworks; diff --git a/manta-util/src/array.rs b/manta-util/src/array.rs index 334f811a5..899bfae10 100644 --- a/manta-util/src/array.rs +++ b/manta-util/src/array.rs @@ -198,7 +198,8 @@ macro_rules! impl_array_traits { /// Array /// /// This type wraps a standard Rust array but provides some additional methods and optional -/// compatibility with `serde`. The type `Array<T, N>` is mostly a drop-in replacement for `[T; N]`. +/// compatibility with [`serde`](crate::serde). The type `Array<T, N>` is mostly a drop-in +/// replacement for `[T; N]`. #[cfg_attr( feature = "serde-array", derive(Deserialize, Serialize), @@ -299,8 +300,8 @@ impl<T, const N: usize> From<Array<T, N>> for [T; N] { /// Boxed Array /// /// This type wraps a boxed standard Rust array but provides some additional methods and optional -/// compatibility with `serde`. The type `BoxArray<T, N>` is mostly a drop-in replacement for -/// `Box<[T; N]>`. +/// compatibility with [`serde`](crate::serde). The type `BoxArray<T, N>` is mostly a drop-in +/// replacement for `Box<[T; N]>`. #[cfg(feature = "alloc")] #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] #[cfg_attr( From 7ad7ce57ac0529c0c3b29569005a347e3ba3206c Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 7 Feb 2022 01:17:33 -0500 Subject: [PATCH 225/275] fix: remove unstable default-const argument --- manta-crypto/src/ecc.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/manta-crypto/src/ecc.rs b/manta-crypto/src/ecc.rs index 339fb42e5..2684f79cb 100644 --- a/manta-crypto/src/ecc.rs +++ b/manta-crypto/src/ecc.rs @@ -89,7 +89,7 @@ pub trait ScalarMul<COM = ()> { } /// Elliptic Curve Pre-processed Scalar Multiplication Operation -pub trait PreprocessedScalarMul<COM = (), const N: usize = 1>: ScalarMul<COM> + Sized { +pub trait PreprocessedScalarMul<COM, const N: usize>: ScalarMul<COM> + Sized { /// Performs the scalar multiplication against a pre-computed table. #[must_use] fn preprocessed_scalar_mul( @@ -99,7 +99,7 @@ pub trait PreprocessedScalarMul<COM = (), const N: usize = 1>: ScalarMul<COM> + ) -> Self::Output; } -impl<G, COM> PreprocessedScalarMul<COM> for G +impl<G, COM> PreprocessedScalarMul<COM, 1> for G where G: ScalarMul<COM>, { @@ -114,10 +114,7 @@ where } /// Elliptic Curve Group -pub trait Group<COM = ()>: - PointAdd<COM> + PointDouble<COM> + PreprocessedScalarMul<COM> + ScalarMul<COM> -{ -} +pub trait Group<COM = ()>: PointAdd<COM> + PointDouble<COM> + ScalarMul<COM> {} /// Pre-processed Scalar Multiplication Table #[cfg_attr( @@ -131,7 +128,7 @@ pub trait Group<COM = ()>: )] #[derive(derivative::Derivative)] #[derivative(Clone, Debug, Eq, Hash, PartialEq)] -pub struct PreprocessedScalarMulTable<G, const N: usize = 1> { +pub struct PreprocessedScalarMulTable<G, const N: usize> { /// Pre-computed Table table: BoxArray<G, N>, } From 06b4e2a49d386d32d00e9f1e8aefadb13a96c835 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 7 Feb 2022 16:03:53 -0500 Subject: [PATCH 226/275] feat: add serde (de)serialization for arkworks types --- .../src/crypto/constraint/arkworks/groth16.rs | 64 ++++++++++++++--- .../src/crypto/constraint/arkworks/mod.rs | 64 +++++++++++++++-- manta-pay/src/crypto/ecc/arkworks.rs | 70 ++++++++++++++++--- manta-pay/src/wallet/signer/client/http.rs | 9 +-- .../src/wallet/signer/client/websocket.rs | 24 +++++-- 5 files changed, 195 insertions(+), 36 deletions(-) diff --git a/manta-pay/src/crypto/constraint/arkworks/groth16.rs b/manta-pay/src/crypto/constraint/arkworks/groth16.rs index 4ba5ca614..7efb324e1 100644 --- a/manta-pay/src/crypto/constraint/arkworks/groth16.rs +++ b/manta-pay/src/crypto/constraint/arkworks/groth16.rs @@ -18,14 +18,14 @@ use crate::crypto::constraint::arkworks::{ self, - codec::{ArkReader, ArkWriter, HasDeserialization, HasSerialization}, + codec::{ArkReader, ArkWriter, HasDeserialization, HasSerialization, SerializationError}, R1CS, }; use alloc::vec::Vec; use ark_crypto_primitives::SNARK; use ark_ec::PairingEngine; use ark_groth16::{Groth16 as ArkGroth16, PreparedVerifyingKey, ProvingKey}; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, Write}; use core::marker::PhantomData; use manta_crypto::{ constraint::ProofSystem, @@ -34,7 +34,7 @@ use manta_crypto::{ use manta_util::codec::{self, DecodeError}; #[cfg(feature = "serde")] -use manta_util::serde::{Deserialize, Serialize}; +use manta_util::serde::{Deserialize, Serialize, Serializer}; /// Proof System Error /// @@ -49,9 +49,23 @@ use manta_util::serde::{Deserialize, Serialize}; pub struct Error; /// Groth16 Proof +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound(deserialize = "", serialize = ""), + crate = "manta_util::serde", + deny_unknown_fields, + try_from = "Vec<u8>" + ) +)] #[derive(derivative::Derivative)] #[derivative(Clone, Debug, Default, Eq, PartialEq)] -pub struct Proof<E>(pub ark_groth16::Proof<E>) +pub struct Proof<E>( + /// Groth16 Proof + #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_proof::<E, _>"))] + pub ark_groth16::Proof<E>, +) where E: PairingEngine; @@ -80,16 +94,48 @@ where where Encoder: FnOnce(&[u8]) -> R, { - let mut buffer = Vec::new(); - self.0 - .serialize(&mut buffer) - .expect("Encoding is not allowed to fail."); - f(&buffer) + f(&proof_as_bytes::<E>(&self.0)) } } impl<E> scale_codec::EncodeLike for Proof<E> where E: PairingEngine {} +impl<E> TryFrom<Vec<u8>> for Proof<E> +where + E: PairingEngine, +{ + type Error = SerializationError; + + #[inline] + fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> { + CanonicalDeserialize::deserialize(&mut bytes.as_slice()).map(Self) + } +} + +/// Converts `proof` into its canonical byte-representation. +#[inline] +fn proof_as_bytes<E>(proof: &ark_groth16::Proof<E>) -> Vec<u8> +where + E: PairingEngine, +{ + let mut buffer = Vec::new(); + proof + .serialize(&mut buffer) + .expect("Serialization is not allowed to fail."); + buffer +} + +/// Uses `serializer` to serialize `proof`. +#[cfg(feature = "serde")] +#[inline] +fn serialize_proof<E, S>(proof: &ark_groth16::Proof<E>, serializer: S) -> Result<S::Ok, S::Error> +where + E: PairingEngine, + S: Serializer, +{ + serializer.serialize_bytes(&proof_as_bytes::<E>(proof)) +} + /// Proving Context #[derive(derivative::Derivative, CanonicalSerialize, CanonicalDeserialize)] #[derivative(Clone, Debug, Eq, PartialEq)] diff --git a/manta-pay/src/crypto/constraint/arkworks/mod.rs b/manta-pay/src/crypto/constraint/arkworks/mod.rs index 779d5a220..7e340467d 100644 --- a/manta-pay/src/crypto/constraint/arkworks/mod.rs +++ b/manta-pay/src/crypto/constraint/arkworks/mod.rs @@ -32,6 +32,9 @@ use manta_crypto::{ }; use manta_util::codec::{Decode, DecodeError, Encode, Read, Write}; +#[cfg(feature = "serde")] +use manta_util::serde::{Deserialize, Serialize, Serializer}; + pub use ark_r1cs::SynthesisError; pub use ark_r1cs_std::{bits::boolean::Boolean, fields::fp::FpVar}; @@ -43,8 +46,25 @@ pub mod pairing; pub mod groth16; /// Prime Field Element +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound(deserialize = "", serialize = ""), + crate = "manta_util::serde", + deny_unknown_fields, + try_from = "Vec<u8>" + ) +)] #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Fp<F>(pub F) +pub struct Fp<F>( + /// Field Point Element + #[cfg_attr( + feature = "serde", + serde(serialize_with = "serialize_field_element::<F, _>") + )] + pub F, +) where F: PrimeField; @@ -110,11 +130,7 @@ where where Encoder: FnOnce(&[u8]) -> R, { - let mut buffer = Vec::new(); - self.0 - .serialize(&mut buffer) - .expect("Encoding is not allowed to fail."); - f(&buffer) + f(&field_element_as_bytes::<F>(&self.0)) } } @@ -134,6 +150,42 @@ where } } +impl<F> TryFrom<Vec<u8>> for Fp<F> +where + F: PrimeField, +{ + type Error = codec::SerializationError; + + #[inline] + fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> { + F::deserialize(&mut bytes.as_slice()).map(Self) + } +} + +/// Converts `element` into its canonical byte-representation. +#[inline] +fn field_element_as_bytes<F>(element: &F) -> Vec<u8> +where + F: PrimeField, +{ + let mut buffer = Vec::new(); + element + .serialize(&mut buffer) + .expect("Serialization is not allowed to fail."); + buffer +} + +/// Uses `serializer` to serialize `element`. +#[cfg(feature = "serde")] +#[inline] +fn serialize_field_element<F, S>(element: &F, serializer: S) -> Result<S::Ok, S::Error> +where + F: PrimeField, + S: Serializer, +{ + serializer.serialize_bytes(&field_element_as_bytes(element)) +} + /// Synthesis Result pub type SynthesisResult<T = ()> = Result<T, SynthesisError>; diff --git a/manta-pay/src/crypto/ecc/arkworks.rs b/manta-pay/src/crypto/ecc/arkworks.rs index c2d63245e..205c0335a 100644 --- a/manta-pay/src/crypto/ecc/arkworks.rs +++ b/manta-pay/src/crypto/ecc/arkworks.rs @@ -34,6 +34,9 @@ use manta_crypto::{ }; use manta_util::codec; +#[cfg(feature = "serde")] +use manta_util::serde::{Deserialize, Serialize, Serializer}; + pub use ark_ec::{AffineCurve, ProjectiveCurve}; pub use ark_r1cs_std::groups::CurveVar; @@ -93,8 +96,25 @@ where } /// Elliptic Curve Group Element +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound(deserialize = "", serialize = ""), + crate = "manta_util::serde", + deny_unknown_fields, + try_from = "Vec<u8>" + ) +)] #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Group<C>(pub(crate) C::Affine) +pub struct Group<C>( + /// Affine Point Representation + #[cfg_attr( + feature = "serde", + serde(serialize_with = "serialize_group_element::<C, _>") + )] + pub(crate) C::Affine, +) where C: ProjectiveCurve; @@ -160,11 +180,7 @@ where where Encoder: FnOnce(&[u8]) -> R, { - let mut buffer = Vec::new(); - self.0 - .serialize(&mut buffer) - .expect("Encoding is not allowed to fail."); - f(&buffer) + f(&affine_point_as_bytes::<C>(&self.0)) } } @@ -176,11 +192,7 @@ where { #[inline] fn as_bytes(&self) -> Vec<u8> { - let mut buffer = Vec::new(); - self.0 - .serialize_unchecked(&mut buffer) - .expect("Serialization is not allowed to fail."); - buffer + affine_point_as_bytes::<C>(&self.0) } } @@ -211,6 +223,42 @@ where } } +impl<C> TryFrom<Vec<u8>> for Group<C> +where + C: ProjectiveCurve, +{ + type Error = SerializationError; + + #[inline] + fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> { + CanonicalDeserialize::deserialize(&mut bytes.as_slice()).map(Self) + } +} + +/// Converts `point` into its canonical byte-representation. +#[inline] +fn affine_point_as_bytes<C>(point: &C::Affine) -> Vec<u8> +where + C: ProjectiveCurve, +{ + let mut buffer = Vec::new(); + point + .serialize(&mut buffer) + .expect("Serialization is not allowed to fail."); + buffer +} + +/// Uses `serializer` to serialize `point`. +#[cfg(feature = "serde")] +#[inline] +fn serialize_group_element<C, S>(point: &C::Affine, serializer: S) -> Result<S::Ok, S::Error> +where + C: ProjectiveCurve, + S: Serializer, +{ + serializer.serialize_bytes(&affine_point_as_bytes::<C>(point)) +} + /// Elliptic Curve Scalar Element Variable /// /// # Safety diff --git a/manta-pay/src/wallet/signer/client/http.rs b/manta-pay/src/wallet/signer/client/http.rs index 03bbf7f1b..cf0aa3e7b 100644 --- a/manta-pay/src/wallet/signer/client/http.rs +++ b/manta-pay/src/wallet/signer/client/http.rs @@ -95,8 +95,7 @@ impl signer::Connection<Config> for Client { ) -> Result<Result<SyncResponse, SyncError>, Self::Error> { // NOTE: The synchronization command modifies the signer so it must be a POST command // to match the HTTP semantics. - // TODO: Ok(self.post("sync", request)?.json()?) - todo!() + self.post("sync", request)?.json() } #[inline] @@ -106,15 +105,13 @@ impl signer::Connection<Config> for Client { ) -> Result<Result<SignResponse<Config>, SignError<Config>>, Self::Error> { // NOTE: The signing command does not modify the signer so it must be a GET command to match // the HTTP semantics. - // TODO: Ok(self.get("sign", transaction)?.json()?) - todo!() + self.get("sign", transaction)?.json() } #[inline] fn receiving_key(&mut self) -> Result<ReceivingKey<Config>, Self::Error> { // NOTE: The receiving key command modifies the signer so it must be a POST command to match // the HTTP semantics. - // TODO: Ok(self.post("receivingKey", ())?.json()?) - todo!() + self.post("receivingKey", ())?.json() } } diff --git a/manta-pay/src/wallet/signer/client/websocket.rs b/manta-pay/src/wallet/signer/client/websocket.rs index 6afbbb93c..da04b491e 100644 --- a/manta-pay/src/wallet/signer/client/websocket.rs +++ b/manta-pay/src/wallet/signer/client/websocket.rs @@ -21,6 +21,7 @@ use manta_accounting::{ transfer::{canonical::Transaction, ReceivingKey}, wallet::signer::{self, SignError, SignResponse, SyncError, SyncRequest, SyncResponse}, }; +use manta_util::serde::{de::DeserializeOwned, Serialize}; use std::net::TcpStream; use tungstenite::{ client::IntoClientRequest, @@ -51,17 +52,32 @@ impl Client { { Ok(Self(tungstenite::connect(url)?.0)) } + + /// Sends a `request` for the given `command` along the websockets and waits for the response. + #[inline] + pub fn send<Request, Response>( + &mut self, + command: &str, + request: Request, + ) -> Result<Response, Error> + where + Request: Serialize, + Response: DeserializeOwned, + { + // TODO: self.0.read_message(self.0.write_message(request)?) + todo!() + } } impl signer::Connection<Config> for Client { - type Error = (); + type Error = Error; #[inline] fn sync( &mut self, request: SyncRequest<Config>, ) -> Result<Result<SyncResponse, SyncError>, Self::Error> { - todo!() + self.send("sync", request) } #[inline] @@ -69,11 +85,11 @@ impl signer::Connection<Config> for Client { &mut self, transaction: Transaction<Config>, ) -> Result<Result<SignResponse<Config>, SignError<Config>>, Self::Error> { - todo!() + self.send("sign", transaction) } #[inline] fn receiving_key(&mut self) -> Result<ReceivingKey<Config>, Self::Error> { - todo!() + self.send("receivingKey", ()) } } From 65fb850b6a1b49661ae8aa543a98c08909caea02 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 8 Feb 2022 13:26:16 -0500 Subject: [PATCH 227/275] chore: bump dependencies --- manta-accounting/Cargo.toml | 2 +- manta-pay/Cargo.toml | 4 ++-- manta-util/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index e68595e02..994983e7e 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -53,7 +53,7 @@ derive_more = { version = "0.99.16", default-features = false, features = ["add" indexmap = { version = "1.8.0", optional = true, default-features = false } manta-crypto = { path = "../manta-crypto", default-features = false } manta-util = { path = "../manta-util", features = ["alloc"] } -parking_lot = { version = "0.11.2", optional = true, default-features = false } +parking_lot = { version = "0.12.0", optional = true, default-features = false } rand = { version = "0.8.4", optional = true, default-features = false } rand_chacha = { version = "0.3.1", optional = true, default-features = false } rayon = { version = "1.5.1", optional = true, default-features = false } diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 3ab9ce87a..bdcd0c34d 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -94,12 +94,12 @@ indexmap = { version = "1.8.0", default-features = false, optional = true } manta-accounting = { path = "../manta-accounting", default-features = false } manta-crypto = { path = "../manta-crypto", default-features = false } manta-util = { path = "../manta-util", default-features = false } -parking_lot = { version = "0.11.2", optional = true, default-features = false } +parking_lot = { version = "0.12.0", optional = true, default-features = false } rand = { version = "0.8.4", optional = true } rand_chacha = { version = "0.3.1", default-features = false } rayon = { version = "1.5.1", optional = true, default-features = false } reqwest = { version = "0.11.9", optional = true, default-features = false, features = ["blocking", "json"] } -scale-codec = { package = "parity-scale-codec", version = "2.3.1", default-features = false, features = ["derive"] } +scale-codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } statrs = { version = "0.15.0", optional = true, default-features = false } tungstenite = { version = "0.16.0", optional = true, default-features = false, features = ["native-tls"] } # TODO: zk-garage-plonk = { package = "plonk", git = "https://github.com/zk-garage/plonk", optional = true, default-features = false } diff --git a/manta-util/Cargo.toml b/manta-util/Cargo.toml index 325869630..4e6e7e314 100644 --- a/manta-util/Cargo.toml +++ b/manta-util/Cargo.toml @@ -39,5 +39,5 @@ std = ["alloc"] [dependencies] serde = { version = "1.0.136", optional = true, default-features = false, features = ["derive"] } -serde_with = { version = "1.11.0", optional = true, default-features = false, features = ["macros"] } +serde_with = { version = "1.12.0", optional = true, default-features = false, features = ["macros"] } From abb372bf99bba93116bbf849a81499fb6e434fe6 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 8 Feb 2022 14:09:59 -0500 Subject: [PATCH 228/275] feat: optimize dependencies and features --- manta-accounting/Cargo.toml | 2 +- manta-accounting/src/wallet/signer.rs | 6 +- manta-crypto/Cargo.toml | 2 +- manta-pay/Cargo.toml | 19 +- .../src/crypto/constraint/arkworks/groth16.rs | 2 +- manta-pay/src/crypto/encryption.rs | 7 +- manta-pay/src/lib.rs | 6 +- manta-pay/src/signer/base.rs | 223 ++++++++++++++++++ .../src/{wallet => }/signer/client/http.rs | 0 .../src/{wallet => }/signer/client/mod.rs | 0 .../{wallet => }/signer/client/websocket.rs | 0 manta-pay/src/{wallet => }/signer/mod.rs | 6 +- manta-pay/src/test/ledger.rs | 13 +- manta-pay/src/wallet/cache.rs | 163 ------------- manta-pay/src/wallet/mod.rs | 79 ------- 15 files changed, 256 insertions(+), 272 deletions(-) create mode 100644 manta-pay/src/signer/base.rs rename manta-pay/src/{wallet => }/signer/client/http.rs (100%) rename manta-pay/src/{wallet => }/signer/client/mod.rs (100%) rename manta-pay/src/{wallet => }/signer/client/websocket.rs (100%) rename manta-pay/src/{wallet => }/signer/mod.rs (85%) delete mode 100644 manta-pay/src/wallet/cache.rs delete mode 100644 manta-pay/src/wallet/mod.rs diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 994983e7e..988544a5b 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -52,7 +52,7 @@ derivative = { version = "2.2.0", default-features = false, features = ["use_cor derive_more = { version = "0.99.16", default-features = false, features = ["add", "add_assign", "display", "from", "sum"] } indexmap = { version = "1.8.0", optional = true, default-features = false } manta-crypto = { path = "../manta-crypto", default-features = false } -manta-util = { path = "../manta-util", features = ["alloc"] } +manta-util = { path = "../manta-util", default-features = false, features = ["alloc"] } parking_lot = { version = "0.12.0", optional = true, default-features = false } rand = { version = "0.8.4", optional = true, default-features = false } rand_chacha = { version = "0.3.1", optional = true, default-features = false } diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 933c3f7de..7cc72c57c 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -30,7 +30,6 @@ use crate::{ asset::{Asset, AssetId, AssetMap, AssetValue}, - fs::{self, File}, key::{self, HierarchicalKeyDerivationScheme, KeyIndex, ViewKeySelection}, transfer::{ self, @@ -60,7 +59,10 @@ use manta_util::{ }; #[cfg(feature = "serde")] -use manta_util::serde::{Deserialize, Serialize}; +use { + crate::fs::{self, File}, + manta_util::serde::{Deserialize, Serialize}, +}; /// Signer Connection pub trait Connection<C> diff --git a/manta-crypto/Cargo.toml b/manta-crypto/Cargo.toml index b49bac0db..bf0719476 100644 --- a/manta-crypto/Cargo.toml +++ b/manta-crypto/Cargo.toml @@ -39,7 +39,7 @@ test = [] [dependencies] derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } -manta-util = { path = "../manta-util", features = ["alloc"] } +manta-util = { path = "../manta-util", default-features = false, features = ["alloc"] } rand_core = { version = "0.6.3", default-features = false } [dev-dependencies] diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index bdcd0c34d..6184da62a 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -30,13 +30,12 @@ harness = false [[bin]] name = "generate_parameters" -required-features = ["arkworks", "test"] +required-features = ["groth16", "manta-util/std", "test"] [features] # Enable Arkworks Backend arkworks = [ "ark-bls12-381", - "ark-crypto-primitives", "ark-ec", "ark-ed-on-bls12-381", "ark-ff", @@ -47,7 +46,7 @@ arkworks = [ ] # Enable Groth16 ZKP System -groth16 = ["arkworks", "ark-groth16"] +groth16 = ["ark-groth16", "ark-snark", "arkworks"] # TODO: Enable PLONK ZKP System # TODO: plonk = ["zk-garage-plonk"] @@ -77,7 +76,6 @@ wallet = ["bip32", "manta-crypto/getrandom", "std"] [dependencies] aes-gcm = { version = "0.9.4", default-features = false, features = ["aes", "alloc"] } ark-bls12-381 = { version = "0.3.0", optional = true, default-features = false, features = ["curve"] } -ark-crypto-primitives = { version = "0.3.0", optional = true, default-features = false, features = ["r1cs"] } ark-ec = { version = "0.3.0", optional = true, default-features = false } ark-ed-on-bls12-381 = { version = "0.3.0", optional = true, default-features = false, features = ["r1cs"] } ark-ff = { version = "0.3.0", optional = true, default-features = false } @@ -85,12 +83,12 @@ ark-groth16 = { version = "0.3.0", optional = true, default-features = false } ark-r1cs-std = { version = "0.3.1", optional = true, default-features = false } ark-relations = { version = "0.3.0", optional = true, default-features = false } ark-serialize = { version = "0.3.0", optional = true, default-features = false } +ark-snark = { version = "0.3.0", optional = true, default-features = false } ark-std = { version = "0.3.0", optional = true, default-features = false } -bip32 = { version = "0.3.0", default-features = false, features = ["bip39", "secp256k1"], optional = true } +bip32 = { version = "0.3.0", optional = true, default-features = false, features = ["bip39", "secp256k1"] } blake2 = { version = "0.10.0", default-features = false } -derivative = { version = "2.2.0", default-features = false } -generic-array = { version = "0.14.4", default-features = false } -indexmap = { version = "1.8.0", default-features = false, optional = true } +derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } +indexmap = { version = "1.8.0", optional = true, default-features = false } manta-accounting = { path = "../manta-accounting", default-features = false } manta-crypto = { path = "../manta-crypto", default-features = false } manta-util = { path = "../manta-util", default-features = false } @@ -106,8 +104,5 @@ tungstenite = { version = "0.16.0", optional = true, default-features = false, f [dev-dependencies] criterion = "0.3.5" -manta-accounting = { path = "../manta-accounting", features = ["test"] } -manta-crypto = { path = "../manta-crypto", features = ["test"] } -manta-pay = { path = ".", features = ["arkworks", "bip32", "groth16", "std", "test"] } -rand = "0.8.4" +manta-pay = { path = ".", features = ["wallet", "test"] } tempfile = "3.2.0" diff --git a/manta-pay/src/crypto/constraint/arkworks/groth16.rs b/manta-pay/src/crypto/constraint/arkworks/groth16.rs index 7efb324e1..301c7dcf1 100644 --- a/manta-pay/src/crypto/constraint/arkworks/groth16.rs +++ b/manta-pay/src/crypto/constraint/arkworks/groth16.rs @@ -22,10 +22,10 @@ use crate::crypto::constraint::arkworks::{ R1CS, }; use alloc::vec::Vec; -use ark_crypto_primitives::SNARK; use ark_ec::PairingEngine; use ark_groth16::{Groth16 as ArkGroth16, PreparedVerifyingKey, ProvingKey}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, Write}; +use ark_snark::SNARK; use core::marker::PhantomData; use manta_crypto::{ constraint::ProofSystem, diff --git a/manta-pay/src/crypto/encryption.rs b/manta-pay/src/crypto/encryption.rs index f995cf995..fe03107da 100644 --- a/manta-pay/src/crypto/encryption.rs +++ b/manta-pay/src/crypto/encryption.rs @@ -24,7 +24,6 @@ pub mod aes { aead::{Aead, NewAead}, Aes256Gcm, Nonce, }; - use generic_array::GenericArray; use manta_crypto::encryption::SymmetricKeyEncryptionScheme; use manta_util::Array; @@ -56,7 +55,8 @@ pub mod aes { fn encrypt(key: Self::Key, plaintext: Self::Plaintext) -> Self::Ciphertext { // SAFETY: Using a deterministic nonce is ok since we never reuse keys. Array::from_unchecked( - Aes256Gcm::new(GenericArray::from_slice(&key)) + Aes256Gcm::new_from_slice(&key) + .expect("The key has the correct size.") .encrypt(Nonce::from_slice(Self::NONCE), plaintext.as_ref()) .expect("Symmetric encryption is not allowed to fail."), ) @@ -65,7 +65,8 @@ pub mod aes { #[inline] fn decrypt(key: Self::Key, ciphertext: &Self::Ciphertext) -> Option<Self::Plaintext> { // SAFETY: Using a deterministic nonce is ok since we never reuse keys. - Aes256Gcm::new(GenericArray::from_slice(&key)) + Aes256Gcm::new_from_slice(&key) + .expect("The key has the correct size.") .decrypt(Nonce::from_slice(Self::NONCE), ciphertext.as_ref()) .ok() .map(Array::from_unchecked) diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index e96ad1298..05b6f2b97 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -36,6 +36,6 @@ pub mod key; #[cfg_attr(doc_cfg, doc(cfg(feature = "groth16")))] pub mod config; -#[cfg(all(feature = "groth16", feature = "wallet",))] -#[cfg_attr(doc_cfg, doc(cfg(all(feature = "groth16", feature = "wallet",))))] -pub mod wallet; +#[cfg(feature = "groth16")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "groth16")))] +pub mod signer; diff --git a/manta-pay/src/signer/base.rs b/manta-pay/src/signer/base.rs new file mode 100644 index 000000000..ef2751210 --- /dev/null +++ b/manta-pay/src/signer/base.rs @@ -0,0 +1,223 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Manta Pay Signer Configuration + +use crate::{ + config::{Bls12_381_Edwards, Config, MerkleTreeConfiguration, SecretKey}, + crypto::constraint::arkworks::Fp, + key::TestnetKeySecret, +}; +use ark_ec::ProjectiveCurve; +use ark_ff::PrimeField; +use manta_accounting::{ + asset::HashAssetMap, + key::{self, HierarchicalKeyDerivationScheme}, + wallet::signer::AssetMapKey, +}; +use manta_crypto::{key::KeyDerivationFunction, merkle_tree}; +use manta_util::pointer::ThreadSafe; + +/// Hierarchical Key Derivation Function +pub struct HierarchicalKeyDerivationFunction; + +impl KeyDerivationFunction for HierarchicalKeyDerivationFunction { + type Key = <TestnetKeySecret as HierarchicalKeyDerivationScheme>::SecretKey; + type Output = SecretKey; + + #[inline] + fn derive(secret_key: &Self::Key) -> Self::Output { + // FIXME: Check that this conversion is logical/safe. + let bytes: [u8; 32] = secret_key + .private_key() + .to_bytes() + .try_into() + .expect("The secret key has 32 bytes."); + Fp(<Bls12_381_Edwards as ProjectiveCurve>::ScalarField::from_le_bytes_mod_order(&bytes)) + } +} + +/// Signer UTXO Set +pub type UtxoSet = merkle_tree::forest::TreeArrayMerkleForest< + MerkleTreeConfiguration, + merkle_tree::fork::ForkedTree< + MerkleTreeConfiguration, + merkle_tree::full::Full<MerkleTreeConfiguration>, + ThreadSafe, + >, + { MerkleTreeConfiguration::FOREST_WIDTH }, +>; + +/// Proving Context Cache +pub mod cache { + use crate::config::{MultiProvingContext, ProvingContext}; + use core::marker::PhantomData; + use manta_util::{ + cache::CachedResource, + codec::{Decode, Encode, IoReader, IoWriter}, + }; + use std::{ + fs::{File, OpenOptions}, + io, + path::{Path, PathBuf}, + }; + + /// Caching Error + #[derive(Debug)] + pub enum Error { + /// Encoding Error + Encode, + + /// Decoding Error + Decode, + + /// I/O Error + Io(io::Error), + } + + impl From<io::Error> for Error { + #[inline] + fn from(err: io::Error) -> Self { + Self::Io(err) + } + } + + /// Cache Reading Key + pub struct ReadingKey(PhantomData<()>); + + impl ReadingKey { + #[inline] + fn new() -> Self { + Self(PhantomData) + } + } + + /// On-Disk Multi-Proving Context + pub struct OnDiskMultiProvingContext { + /// Source Directory + directory: PathBuf, + + /// Current Cached Context + context: Option<MultiProvingContext>, + } + + impl OnDiskMultiProvingContext { + /// Builds a new [`OnDiskMultiProvingContext`] setting the source directory to `directory`. + /// + /// To save the cache data to disk, use [`save`](Self::save). + #[inline] + pub fn new<P>(directory: P) -> Self + where + P: AsRef<Path>, + { + Self { + directory: directory.as_ref().to_owned(), + context: None, + } + } + + /// Returns the directory where `self` stores the [`MultiProvingContext`]. + #[inline] + pub fn directory(&self) -> &Path { + &self.directory + } + + /// Reads a single [`ProvingContext`] from `path`. + #[inline] + fn read_context<P>(path: P) -> Result<ProvingContext, Error> + where + P: AsRef<Path>, + { + File::open(path.as_ref()) + .map_err(Error::Io) + .and_then(move |f| ProvingContext::decode(IoReader(f)).map_err(|_| Error::Decode)) + } + + /// Writes `context` to `path`. + #[inline] + fn write_context<P>(path: P, context: ProvingContext) -> Result<(), Error> + where + P: AsRef<Path>, + { + OpenOptions::new() + .write(true) + .create(true) + .open(path.as_ref()) + .map_err(Error::Io) + .and_then(move |f| context.encode(IoWriter(f)).map_err(|_| Error::Encode)) + } + + /// Saves the `context` to the on-disk directory. This method _does not_ write `context` into + /// the cache. + #[inline] + pub fn save(&self, context: MultiProvingContext) -> Result<(), Error> { + Self::write_context(self.directory.join("mint.pk"), context.mint)?; + Self::write_context( + self.directory.join("private-transfer.pk"), + context.private_transfer, + )?; + Self::write_context(self.directory.join("reclaim.pk"), context.reclaim)?; + Ok(()) + } + } + + impl CachedResource<MultiProvingContext> for OnDiskMultiProvingContext { + type ReadingKey = ReadingKey; + type Error = Error; + + #[inline] + fn aquire(&mut self) -> Result<Self::ReadingKey, Self::Error> { + self.context = Some(MultiProvingContext { + mint: Self::read_context(self.directory.join("mint.pk"))?, + private_transfer: Self::read_context(self.directory.join("private-transfer.pk"))?, + reclaim: Self::read_context(self.directory.join("reclaim.pk"))?, + }); + Ok(ReadingKey::new()) + } + + #[inline] + fn read(&self, reading_key: Self::ReadingKey) -> &MultiProvingContext { + // SAFETY: Since `reading_key` is only given out when we know that `context` is `Some`, + // we can safely `unwrap` here. + let _ = reading_key; + self.context.as_ref().unwrap() + } + + #[inline] + fn release(&mut self) { + self.context.take(); + } + } + + impl Clone for OnDiskMultiProvingContext { + #[inline] + fn clone(&self) -> Self { + Self::new(&self.directory) + } + } +} + +impl manta_accounting::wallet::signer::Configuration for Config { + type HierarchicalKeyDerivationScheme = + key::Map<TestnetKeySecret, HierarchicalKeyDerivationFunction>; + type UtxoSet = UtxoSet; + type AssetMap = HashAssetMap<AssetMapKey<Self>>; + type ProvingContextCache = cache::OnDiskMultiProvingContext; + type Rng = rand_chacha::ChaCha20Rng; +} + +/// Signer Base Type +pub type Signer = manta_accounting::wallet::signer::Signer<Config>; diff --git a/manta-pay/src/wallet/signer/client/http.rs b/manta-pay/src/signer/client/http.rs similarity index 100% rename from manta-pay/src/wallet/signer/client/http.rs rename to manta-pay/src/signer/client/http.rs diff --git a/manta-pay/src/wallet/signer/client/mod.rs b/manta-pay/src/signer/client/mod.rs similarity index 100% rename from manta-pay/src/wallet/signer/client/mod.rs rename to manta-pay/src/signer/client/mod.rs diff --git a/manta-pay/src/wallet/signer/client/websocket.rs b/manta-pay/src/signer/client/websocket.rs similarity index 100% rename from manta-pay/src/wallet/signer/client/websocket.rs rename to manta-pay/src/signer/client/websocket.rs diff --git a/manta-pay/src/wallet/signer/mod.rs b/manta-pay/src/signer/mod.rs similarity index 85% rename from manta-pay/src/wallet/signer/mod.rs rename to manta-pay/src/signer/mod.rs index 5349ac719..dff4470cb 100644 --- a/manta-pay/src/wallet/signer/mod.rs +++ b/manta-pay/src/signer/mod.rs @@ -14,6 +14,10 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Signer Client and Server Implementations +//! Manta Pay Signer Tools pub mod client; + +#[cfg(feature = "wallet")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "wallet")))] +pub mod base; diff --git a/manta-pay/src/test/ledger.rs b/manta-pay/src/test/ledger.rs index f29d967ce..e0d841129 100644 --- a/manta-pay/src/test/ledger.rs +++ b/manta-pay/src/test/ledger.rs @@ -456,7 +456,7 @@ mod test { use super::*; use crate::{ config::FullParameters, - wallet::{self, cache::OnDiskMultiProvingContext, Signer}, + signer::base::{cache::OnDiskMultiProvingContext, Signer, UtxoSet}, }; use manta_accounting::{ asset::{Asset, AssetList}, @@ -471,7 +471,7 @@ mod test { }, }; use manta_crypto::rand::{CryptoRng, Rand, RngCore, SeedableRng}; - use rand::{rngs::StdRng, thread_rng}; + use rand_chacha::ChaCha20Rng; impl PublicBalanceOracle for LedgerConnection { #[inline] @@ -507,7 +507,7 @@ mod test { AccountTable::new(rng.gen()), cache.clone(), parameters.clone(), - wallet::UtxoSet::new(utxo_set_model.clone()), + UtxoSet::new(utxo_set_model.clone()), rng.seed_rng().expect("Failed to sample PRNG for signer."), ), ) @@ -519,7 +519,7 @@ mod test { let directory = tempfile::tempdir().expect("Unable to generate temporary test directory."); println!("[INFO] Temporary Directory: {:?}", directory); - let mut rng = thread_rng(); + let mut rng = ChaCha20Rng::from_entropy(); let parameters = rng.gen(); let utxo_set_model = rng.gen(); @@ -536,6 +536,7 @@ mod test { .expect("Unable to save proving context to disk."); const ACTOR_COUNT: usize = 10; + const ACTOR_LIFETIME: usize = 300; let mut ledger = Ledger::new(utxo_set_model.clone(), verifying_context); @@ -561,7 +562,7 @@ mod test { &mut rng, ), Default::default(), - rand::Rng::gen_range(&mut rng, 50..300), + ACTOR_LIFETIME, ) }) .collect::<Vec<_>>(); @@ -571,7 +572,7 @@ mod test { println!("[INFO] Starting Simulation\n"); rayon::in_place_scope(|scope| { - for event in simulator.run(move || StdRng::from_rng(&mut rng).unwrap(), scope) { + for event in simulator.run(move || ChaCha20Rng::from_rng(&mut rng).unwrap(), scope) { match event.event.action { ActionType::Skip | ActionType::GeneratePublicKey => {} _ => println!("{:?}", event), diff --git a/manta-pay/src/wallet/cache.rs b/manta-pay/src/wallet/cache.rs deleted file mode 100644 index f0aad17cc..000000000 --- a/manta-pay/src/wallet/cache.rs +++ /dev/null @@ -1,163 +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 <http://www.gnu.org/licenses/>. - -//! Proving Context Caching - -use crate::config::{MultiProvingContext, ProvingContext}; -use core::marker::PhantomData; -use manta_util::{ - cache::CachedResource, - codec::{Decode, Encode, IoReader, IoWriter}, -}; -use std::{ - fs::{File, OpenOptions}, - io, - path::{Path, PathBuf}, -}; - -/// Caching Error -#[derive(Debug)] -pub enum Error { - /// Encoding Error - Encode, - - /// Decoding Error - Decode, - - /// I/O Error - Io(io::Error), -} - -impl From<io::Error> for Error { - #[inline] - fn from(err: io::Error) -> Self { - Self::Io(err) - } -} - -/// Cache Reading Key -pub struct ReadingKey(PhantomData<()>); - -impl ReadingKey { - #[inline] - fn new() -> Self { - Self(PhantomData) - } -} - -/// On-Disk Multi-Proving Context -pub struct OnDiskMultiProvingContext { - /// Source Directory - directory: PathBuf, - - /// Current Cached Context - context: Option<MultiProvingContext>, -} - -impl OnDiskMultiProvingContext { - /// Builds a new [`OnDiskMultiProvingContext`] setting the source directory to `directory`. - /// - /// To save the cache data to disk, use [`save`](Self::save). - #[inline] - pub fn new<P>(directory: P) -> Self - where - P: AsRef<Path>, - { - Self { - directory: directory.as_ref().to_owned(), - context: None, - } - } - - /// Returns the directory where `self` stores the [`MultiProvingContext`]. - #[inline] - pub fn directory(&self) -> &Path { - &self.directory - } - - /// Reads a single [`ProvingContext`] from `path`. - #[inline] - fn read_context<P>(path: P) -> Result<ProvingContext, Error> - where - P: AsRef<Path>, - { - File::open(path.as_ref()) - .map_err(Error::Io) - .and_then(move |f| ProvingContext::decode(IoReader(f)).map_err(|_| Error::Decode)) - } - - /// Writes `context` to `path`. - #[inline] - fn write_context<P>(path: P, context: ProvingContext) -> Result<(), Error> - where - P: AsRef<Path>, - { - OpenOptions::new() - .write(true) - .create(true) - .open(path.as_ref()) - .map_err(Error::Io) - .and_then(move |f| context.encode(IoWriter(f)).map_err(|_| Error::Encode)) - } - - /// Saves the `context` to the on-disk directory. This method _does not_ write `context` into - /// the cache. - #[inline] - pub fn save(&self, context: MultiProvingContext) -> Result<(), Error> { - Self::write_context(self.directory.join("mint.pk"), context.mint)?; - Self::write_context( - self.directory.join("private-transfer.pk"), - context.private_transfer, - )?; - Self::write_context(self.directory.join("reclaim.pk"), context.reclaim)?; - Ok(()) - } -} - -impl CachedResource<MultiProvingContext> for OnDiskMultiProvingContext { - type ReadingKey = ReadingKey; - type Error = Error; - - #[inline] - fn aquire(&mut self) -> Result<Self::ReadingKey, Self::Error> { - self.context = Some(MultiProvingContext { - mint: Self::read_context(self.directory.join("mint.pk"))?, - private_transfer: Self::read_context(self.directory.join("private-transfer.pk"))?, - reclaim: Self::read_context(self.directory.join("reclaim.pk"))?, - }); - Ok(ReadingKey::new()) - } - - #[inline] - fn read(&self, reading_key: Self::ReadingKey) -> &MultiProvingContext { - // SAFETY: Since `reading_key` is only given out when we know that `context` is `Some`, - // we can safely `unwrap` here. - let _ = reading_key; - self.context.as_ref().unwrap() - } - - #[inline] - fn release(&mut self) { - self.context.take(); - } -} - -impl Clone for OnDiskMultiProvingContext { - #[inline] - fn clone(&self) -> Self { - Self::new(&self.directory) - } -} diff --git a/manta-pay/src/wallet/mod.rs b/manta-pay/src/wallet/mod.rs deleted file mode 100644 index 9c8f873bc..000000000 --- a/manta-pay/src/wallet/mod.rs +++ /dev/null @@ -1,79 +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 <http://www.gnu.org/licenses/>. - -//! Manta Pay Wallet Implementation - -// TODO: Build websockets wallet. - -use crate::{ - config::{Bls12_381_Edwards, Config, MerkleTreeConfiguration, SecretKey}, - crypto::constraint::arkworks::Fp, - key::TestnetKeySecret, -}; -use ark_ec::ProjectiveCurve; -use ark_ff::PrimeField; -use manta_accounting::{ - asset::HashAssetMap, - key::{self, HierarchicalKeyDerivationScheme}, - wallet::signer::AssetMapKey, -}; -use manta_crypto::{key::KeyDerivationFunction, merkle_tree}; -use manta_util::pointer::ThreadSafe; - -pub mod cache; -pub mod signer; - -/// Hierarchical Key Derivation Function -pub struct HierarchicalKeyDerivationFunction; - -impl KeyDerivationFunction for HierarchicalKeyDerivationFunction { - type Key = <TestnetKeySecret as HierarchicalKeyDerivationScheme>::SecretKey; - type Output = SecretKey; - - #[inline] - fn derive(secret_key: &Self::Key) -> Self::Output { - // FIXME: Check that this conversion is logical/safe. - let bytes: [u8; 32] = secret_key - .private_key() - .to_bytes() - .try_into() - .expect("The secret key has 32 bytes."); - Fp(<Bls12_381_Edwards as ProjectiveCurve>::ScalarField::from_le_bytes_mod_order(&bytes)) - } -} - -/// Signer UTXO Set -pub type UtxoSet = merkle_tree::forest::TreeArrayMerkleForest< - MerkleTreeConfiguration, - merkle_tree::fork::ForkedTree< - MerkleTreeConfiguration, - merkle_tree::full::Full<MerkleTreeConfiguration>, - ThreadSafe, - >, - { MerkleTreeConfiguration::FOREST_WIDTH }, ->; - -impl manta_accounting::wallet::signer::Configuration for Config { - type HierarchicalKeyDerivationScheme = - key::Map<TestnetKeySecret, HierarchicalKeyDerivationFunction>; - type UtxoSet = UtxoSet; - type AssetMap = HashAssetMap<AssetMapKey<Self>>; - type ProvingContextCache = cache::OnDiskMultiProvingContext; - type Rng = rand_chacha::ChaCha20Rng; -} - -/// Signer Base Type -pub type Signer = manta_accounting::wallet::signer::Signer<Config>; From cfe6ca93d7b02d537814b49476a7e90a825dc086 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 8 Feb 2022 14:19:48 -0500 Subject: [PATCH 229/275] chore: downgrade parity-scale-codec to 2.3.1 --- manta-pay/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 6184da62a..4c7eb9c20 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -97,7 +97,7 @@ rand = { version = "0.8.4", optional = true } rand_chacha = { version = "0.3.1", default-features = false } rayon = { version = "1.5.1", optional = true, default-features = false } reqwest = { version = "0.11.9", optional = true, default-features = false, features = ["blocking", "json"] } -scale-codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +scale-codec = { package = "parity-scale-codec", version = "2.3.1", default-features = false, features = ["derive"] } statrs = { version = "0.15.0", optional = true, default-features = false } tungstenite = { version = "0.16.0", optional = true, default-features = false, features = ["native-tls"] } # TODO: zk-garage-plonk = { package = "plonk", git = "https://github.com/zk-garage/plonk", optional = true, default-features = false } From c825a0970c68f2621bd004798386c3b68d26eaa2 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 8 Feb 2022 17:41:07 -0500 Subject: [PATCH 230/275] feat: upgrade receiving_keys API for more advanced queries --- manta-accounting/src/key.rs | 23 ++++++ manta-accounting/src/transfer/mod.rs | 18 +++++ manta-accounting/src/wallet/signer.rs | 90 +++++++++++++++++++++--- manta-accounting/src/wallet/state.rs | 14 ++-- manta-accounting/src/wallet/test/mod.rs | 17 +++-- manta-pay/src/signer/client/http.rs | 11 ++- manta-pay/src/signer/client/websocket.rs | 11 ++- 7 files changed, 160 insertions(+), 24 deletions(-) diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index df9e123f8..7643d8ae5 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -16,10 +16,13 @@ //! Hierarchical Key Derivation Schemes +// TODO: Build custom iterator types for [`keypairs`] and [`generate_keys`]. + use alloc::vec::Vec; use core::{ fmt::{self, Debug}, hash::Hash, + iter, marker::PhantomData, }; use manta_crypto::{ @@ -375,6 +378,17 @@ where self.with_bounds_check(index, Self::derive_pair) } + /// Returns an iterator over all the key pairs associated to `self`. + #[inline] + pub fn keypairs(&self) -> impl '_ + Iterator<Item = SecretKeyPair<H>> { + let mut index = KeyIndex::default(); + iter::from_fn(move || { + let next = self.keypair(index); + index.increment(); + next + }) + } + /// Applies `f` to the view keys generated by `self` returning the first non-`None` result with /// it's key index and key attached, or returns an error if the key derivation failed. #[inline] @@ -579,6 +593,15 @@ where let index = self.next_index(account)?; Some(self.keys.derive_pair(account, index)) } + + /// Returns an iterator over keys, generated by calling [`next`](Self::next) repeatedly. + #[inline] + pub fn generate_keys( + &mut self, + account: AccountIndex, + ) -> impl '_ + Iterator<Item = SecretKeyPair<H>> { + iter::from_fn(move || self.next(account)) + } } impl<H, M> Default for AccountTable<H, M> diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index a676304a6..fe5a06476 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -2264,6 +2264,24 @@ where event, }) } + + /// Validates `self` on the transfer `ledger` and then posts the updated state to the `ledger` + /// if validation succeeded. + #[inline] + pub fn post<L>( + self, + source_accounts: Vec<L::AccountId>, + sink_accounts: Vec<L::AccountId>, + super_key: &TransferLedgerSuperPostingKey<C, L>, + ledger: &mut L, + ) -> Result<L::Event, TransferPostError<L::AccountId>> + where + L: TransferLedger<C>, + { + Ok(self + .validate(source_accounts, sink_accounts, ledger)? + .post(super_key, ledger)) + } } /// Transfer Posting Key diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 7cc72c57c..eef83a338 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -30,7 +30,7 @@ use crate::{ asset::{Asset, AssetId, AssetMap, AssetValue}, - key::{self, HierarchicalKeyDerivationScheme, KeyIndex, ViewKeySelection}, + key::{self, HierarchicalKeyDerivationScheme, KeyIndex, SecretKeyPair, ViewKeySelection}, transfer::{ self, batch::Join, @@ -88,8 +88,11 @@ where transaction: Transaction<C>, ) -> Result<Result<SignResponse<C>, SignError<C>>, Self::Error>; - /// Returns a new [`ReceivingKey`] for `self` to receive assets. - fn receiving_key(&mut self) -> Result<ReceivingKey<C>, Self::Error>; + /// Returns public receiving keys according to the `request`. + fn receiving_keys( + &mut self, + request: ReceivingKeyRequest, + ) -> Result<Vec<ReceivingKey<C>>, Self::Error>; } /// Signer Synchronization Request @@ -269,6 +272,39 @@ where ProofSystemError(ProofSystemError<C>), } +/// Receiving Key Request +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum ReceivingKeyRequest { + /// Get Specific Key + /// + /// Requests the key at the specific `index`. If the signer's response is an empty key vector, + /// then the index was out of bounds. + Get { + /// Target Key Index + index: KeyIndex, + }, + + /// Get All Keys + /// + /// Requests all the public keys associated to the signer. The signer should always respond to + /// this request with at least one key, the default public key. + GetAll, + + /// New Keys + /// + /// Requests `count`-many new keys from the hierarchical key derivation scheme. The signer + /// should always respond with exactly `count`-many keys. + New { + /// Number of New Keys to Generate + count: usize, + }, +} + /// Signer Configuration pub trait Configuration: transfer::Configuration { /// Hierarchical Key Derivation Scheme @@ -326,6 +362,16 @@ where let reading_key = self.proving_context.aquire()?; Ok((&self.parameters, self.proving_context.read(reading_key))) } + + /// Converts `keypair` into a [`ReceivingKey`] by using the key-agreement scheme to derive the + /// public keys associated to `keypair`. + #[inline] + fn receiving_key( + &self, + keypair: SecretKeyPair<C::HierarchicalKeyDerivationScheme>, + ) -> ReceivingKey<C> { + SpendingKey::new(keypair.spend, keypair.view).derive(&self.parameters.key_agreement) + } } /// Signer State @@ -1008,12 +1054,33 @@ where result } - /// Returns a new [`ReceivingKey`] for `self` to receive assets. + /// Returns public receiving keys according to the `request`. #[inline] - pub fn receiving_key(&mut self) -> ReceivingKey<C> { - let keypair = self.state.accounts.next(Default::default()).unwrap(); - SpendingKey::new(keypair.spend, keypair.view) - .derive(&self.parameters.parameters.key_agreement) + pub fn receiving_keys(&mut self, request: ReceivingKeyRequest) -> Vec<ReceivingKey<C>> { + match request { + ReceivingKeyRequest::Get { index } => self + .state + .accounts + .get_default() + .keypair(index) + .into_iter() + .map(|k| self.parameters.receiving_key(k)) + .collect(), + ReceivingKeyRequest::GetAll => self + .state + .accounts + .get_default() + .keypairs() + .map(|k| self.parameters.receiving_key(k)) + .collect(), + ReceivingKeyRequest::New { count } => self + .state + .accounts + .generate_keys(Default::default()) + .take(count) + .map(|k| self.parameters.receiving_key(k)) + .collect(), + } } } @@ -1040,7 +1107,10 @@ where } #[inline] - fn receiving_key(&mut self) -> Result<ReceivingKey<C>, Self::Error> { - Ok(self.receiving_key()) + fn receiving_keys( + &mut self, + request: ReceivingKeyRequest, + ) -> Result<Vec<ReceivingKey<C>>, Self::Error> { + Ok(self.receiving_keys(request)) } } diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index bc0ee55ef..72ce3d05d 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -24,7 +24,10 @@ use crate::{ }, wallet::{ ledger::{self, Checkpoint, PullResponse, PushResponse}, - signer::{self, SignError, SignResponse, SyncError, SyncRequest, SyncResponse}, + signer::{ + self, ReceivingKeyRequest, SignError, SignResponse, SyncError, SyncRequest, + SyncResponse, + }, }, }; use alloc::collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}; @@ -398,10 +401,13 @@ where Ok(success) } - /// Returns a new [`ReceivingKey`] for `self` to receive assets. + /// Returns public receiving keys according to the `request`. #[inline] - pub fn receiving_key(&mut self) -> Result<ReceivingKey<C>, S::Error> { - self.signer.receiving_key() + pub fn receiving_keys( + &mut self, + request: ReceivingKeyRequest, + ) -> Result<Vec<ReceivingKey<C>>, S::Error> { + self.signer.receiving_keys(request) } } diff --git a/manta-accounting/src/wallet/test/mod.rs b/manta-accounting/src/wallet/test/mod.rs index e7967c970..9891c2ee9 100644 --- a/manta-accounting/src/wallet/test/mod.rs +++ b/manta-accounting/src/wallet/test/mod.rs @@ -21,7 +21,11 @@ use crate::{ asset::{Asset, AssetList}, transfer::{canonical::Transaction, Configuration, PublicKey, ReceivingKey}, - wallet::{self, ledger, signer, Wallet}, + wallet::{ + self, ledger, + signer::{self, ReceivingKeyRequest}, + Wallet, + }, }; use alloc::sync::Arc; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; @@ -398,9 +402,14 @@ where } Action::GeneratePublicKey => Event { action: ActionType::GeneratePublicKey, - result: match actor.wallet.receiving_key() { - Ok(key) => { - self.public_keys.write().insert(key); + result: match actor + .wallet + .receiving_keys(ReceivingKeyRequest::New { count: 1 }) + { + Ok(keys) => { + for key in keys { + self.public_keys.write().insert(key); + } Ok(true) } Err(err) => Err(wallet::Error::SignerConnectionError(err)), diff --git a/manta-pay/src/signer/client/http.rs b/manta-pay/src/signer/client/http.rs index cf0aa3e7b..adbe91b3a 100644 --- a/manta-pay/src/signer/client/http.rs +++ b/manta-pay/src/signer/client/http.rs @@ -19,7 +19,9 @@ use crate::config::Config; use manta_accounting::{ transfer::{canonical::Transaction, ReceivingKey}, - wallet::signer::{self, SignError, SignResponse, SyncError, SyncRequest, SyncResponse}, + wallet::signer::{ + self, ReceivingKeyRequest, SignError, SignResponse, SyncError, SyncRequest, SyncResponse, + }, }; use manta_util::serde::Serialize; use reqwest::{ @@ -109,9 +111,12 @@ impl signer::Connection<Config> for Client { } #[inline] - fn receiving_key(&mut self) -> Result<ReceivingKey<Config>, Self::Error> { + fn receiving_keys( + &mut self, + request: ReceivingKeyRequest, + ) -> Result<Vec<ReceivingKey<Config>>, Self::Error> { // NOTE: The receiving key command modifies the signer so it must be a POST command to match // the HTTP semantics. - self.post("receivingKey", ())?.json() + self.post("receivingKeys", request)?.json() } } diff --git a/manta-pay/src/signer/client/websocket.rs b/manta-pay/src/signer/client/websocket.rs index da04b491e..d197be663 100644 --- a/manta-pay/src/signer/client/websocket.rs +++ b/manta-pay/src/signer/client/websocket.rs @@ -19,7 +19,9 @@ use crate::config::Config; use manta_accounting::{ transfer::{canonical::Transaction, ReceivingKey}, - wallet::signer::{self, SignError, SignResponse, SyncError, SyncRequest, SyncResponse}, + wallet::signer::{ + self, ReceivingKeyRequest, SignError, SignResponse, SyncError, SyncRequest, SyncResponse, + }, }; use manta_util::serde::{de::DeserializeOwned, Serialize}; use std::net::TcpStream; @@ -89,7 +91,10 @@ impl signer::Connection<Config> for Client { } #[inline] - fn receiving_key(&mut self) -> Result<ReceivingKey<Config>, Self::Error> { - self.send("receivingKey", ()) + fn receiving_keys( + &mut self, + request: ReceivingKeyRequest, + ) -> Result<Vec<ReceivingKey<Config>>, Self::Error> { + self.send("receivingKeys", request) } } From c41925d4884ee3f3ffb61766f23e4dc98ab0919a Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 8 Feb 2022 17:42:31 -0500 Subject: [PATCH 231/275] fix: add missing Vec import --- manta-accounting/src/wallet/state.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 72ce3d05d..5f8f9aaa9 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -30,7 +30,10 @@ use crate::{ }, }, }; -use alloc::collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}; +use alloc::{ + collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}, + vec::Vec, +}; use core::{fmt::Debug, marker::PhantomData}; #[cfg(feature = "serde")] From 191082a759338c267179e4c100e7a81a5da3c280 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 8 Feb 2022 19:30:18 -0500 Subject: [PATCH 232/275] wip: start building encrypted serde infrastructure --- manta-accounting/Cargo.toml | 5 + manta-accounting/src/{fs.rs => fs/mod.rs} | 235 ++++--- manta-accounting/src/fs/serde.rs | 776 ++++++++++++++++++++++ manta-accounting/src/lib.rs | 8 +- manta-accounting/src/wallet/signer.rs | 24 +- 5 files changed, 943 insertions(+), 105 deletions(-) rename manta-accounting/src/{fs.rs => fs/mod.rs} (52%) create mode 100644 manta-accounting/src/fs/serde.rs diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 988544a5b..942824b44 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -25,9 +25,13 @@ is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } [features] +# Encrypted Filesystem +fs = ["bitflags"] + # Cocoon Filesystem Adapter cocoon-fs = [ "cocoon/std", + "fs", "manta-crypto/getrandom", "rand_chacha", "std", @@ -46,6 +50,7 @@ std = ["manta-crypto/std", "manta-util/std"] test = ["indexmap", "parking_lot", "rand/alloc", "statrs"] [dependencies] +bitflags = { version = "1.3.2", optional = true, default-features = false } cocoon = { version = "0.3.0", optional = true, default-features = false } crossbeam = { version = "0.8.1", optional = true, default-features = false } derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } diff --git a/manta-accounting/src/fs.rs b/manta-accounting/src/fs/mod.rs similarity index 52% rename from manta-accounting/src/fs.rs rename to manta-accounting/src/fs/mod.rs index a4390b80b..1a446714a 100644 --- a/manta-accounting/src/fs.rs +++ b/manta-accounting/src/fs/mod.rs @@ -19,25 +19,56 @@ use alloc::{boxed::Box, vec::Vec}; use core::{fmt::Debug, hash::Hash}; +#[cfg(feature = "serde")] +pub mod serde; + +bitflags::bitflags! { + /// File Access Mode + pub struct AccessMode: u8 { + /// Read Access Mode + const READ = 0b0001; + + /// Write Access Mode + const WRITE = 0b0010; + + /// Append Mode + const APPEND = 0b0100; + } + + /// File Creation Mode + #[derive(Default)] + pub struct CreationMode: u8 { + /// Create Mode + const CREATE = 0b0001; + + /// Truncate Mode + const TRUNCATE = 0b0010; + + /// Exclusive Creation Mode + const EXCLUSIVE = 0b0100; + } +} + /// Open Options #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +#[must_use] pub struct OpenOptions { - /// + /// Read Access read: bool, - /// + /// Write Access write: bool, - /// + /// Append Mode append: bool, - /// + /// Truncate Mode truncate: bool, - /// + /// Create Mode create: bool, - /// + /// Create New Mode create_new: bool, } @@ -48,49 +79,88 @@ impl OpenOptions { Self::default() } - /// + /// Sets the `read` flag in `self`. #[inline] pub fn read(mut self, read: bool) -> Self { self.read = read; self } - /// + /// Sets the `write` flag in `self`. #[inline] pub fn write(mut self, write: bool) -> Self { self.write = write; self } - /// + /// Sets the `append` flag in `self`. #[inline] pub fn append(mut self, append: bool) -> Self { self.append = append; self } - /// + /// Sets the `truncate` flag in `self`. #[inline] pub fn truncate(mut self, truncate: bool) -> Self { self.truncate = truncate; self } - /// + /// Sets the `create` flag in `self`. #[inline] pub fn create(mut self, create: bool) -> Self { self.create = create; self } - /// + /// Sets the `create_new` flag in `self`. #[inline] pub fn create_new(mut self, create_new: bool) -> Self { self.create_new = create_new; self } - /// + /// Returns the [`AccessMode`] for the combination of options stored in `self`. + #[inline] + pub fn access_mode(&self) -> Option<AccessMode> { + match (self.read, self.write, self.append) { + (true, false, false) => Some(AccessMode::READ), + (false, true, false) => Some(AccessMode::WRITE), + (true, true, false) => Some(AccessMode::READ | AccessMode::WRITE), + (false, _, true) => Some(AccessMode::WRITE | AccessMode::APPEND), + (true, _, true) => Some(AccessMode::READ | AccessMode::WRITE | AccessMode::APPEND), + (false, false, false) => None, + } + } + + /// Returns the [`CreationMode`] for the combination of options stored in `self`. + #[inline] + pub fn creation_mode(&self) -> Option<CreationMode> { + match (self.write, self.append) { + (true, false) => {} + (false, false) => { + if self.truncate || self.create || self.create_new { + return None; + } + } + (_, true) => { + if self.truncate && !self.create_new { + return None; + } + } + } + Some(match (self.create, self.truncate, self.create_new) { + (false, false, false) => CreationMode::empty(), + (true, false, false) => CreationMode::CREATE, + (false, true, false) => CreationMode::TRUNCATE, + (true, true, false) => CreationMode::CREATE | CreationMode::TRUNCATE, + (_, _, true) => CreationMode::CREATE | CreationMode::EXCLUSIVE, + }) + } + + /// Opens a file of type `F` at the given `path` using `self` for opening options and `password` + /// for encryption. #[inline] pub fn open<F, P>(&self, path: P, password: &[u8]) -> Result<F, F::Error> where @@ -101,22 +171,62 @@ impl OpenOptions { } } +#[cfg(feature = "std")] +impl From<OpenOptions> for std::fs::OpenOptions { + #[inline] + fn from(options: OpenOptions) -> Self { + let mut result = Self::new(); + result + .read(options.read) + .write(options.write) + .append(options.append) + .truncate(options.truncate) + .create(options.create) + .create_new(options.create_new); + result + } +} + /// Data Block pub struct Block { /// Block Data - data: Box<[u8; 8192]>, + data: Box<[u8; Self::SIZE]>, } impl Block { - /// Builds a new [`Block`] from an owned collection of bytes. + /// Block Size + pub const SIZE: usize = 8192; + + /// Builds a new [`Block`] from an owned collection of bytes. If the `data` vector is too short + /// it's padded to fit the block and if it's too long, `None` is returned. #[inline] - pub fn new(data: Vec<u8>) -> Option<Self> { + pub fn new(mut data: Vec<u8>) -> Option<Self> { + if data.len() > Self::SIZE { + return None; + } + data.resize(Self::SIZE, 0); Some(Self { - data: data.into_boxed_slice().try_into().ok()?, + data: data.into_boxed_slice().try_into().expect(""), }) } } +impl Default for Block { + #[inline] + fn default() -> Self { + Self { + data: Box::new([0; Self::SIZE]), + } + } +} + +impl From<Block> for Vec<u8> { + #[inline] + fn from(block: Block) -> Self { + block.data.to_vec() + } +} + /// Encrypted File pub trait File: Sized { /// Path Type @@ -136,7 +246,17 @@ pub trait File: Sized { where P: AsRef<Self::Path>, { - OpenOptions::new().create(true).open(path, password) + Self::options() + .write(true) + .create(true) + .truncate(true) + .open(path, password) + } + + /// Returns a new [`OpenOptions`] object. + #[inline] + fn options() -> OpenOptions { + OpenOptions::new() } /// Writes `block` to `self` after encrypting it. @@ -146,65 +266,17 @@ pub trait File: Sized { fn read(&mut self) -> Result<Block, Self::Error>; } -/// Encrypting Serializer -pub struct Serializer<'f, F> -where - F: File, -{ - /// Encrypted File - file: &'f mut F, -} - -impl<'f, F> Serializer<'f, F> -where - F: File, -{ - /// - #[inline] - pub fn new(file: &'f mut F) -> Self { - Self { file } - } -} - -// TODO: impl<'f, F> serde::Serializer for Serializer<'f, F> where F: File {} - -/// Decrypting Deserializer -pub struct Deserializer<'f, F> -where - F: File, -{ - /// Encrypted File - file: &'f mut F, -} - -impl<'f, F> Deserializer<'f, F> -where - F: File, -{ - /// - #[inline] - pub fn new(file: &'f mut F) -> Self { - Self { file } - } -} - -// TODO: impl<'de, 'f, F> serde::Deserializer<'de> for Deserializer<'f, F> where F: File + Read {} - /// Cocoon Encrypted File System Adapter #[cfg(feature = "cocoon-fs")] #[cfg_attr(doc_cfg, doc(cfg(feature = "cocoon-fs")))] pub mod cocoon { - use super::*; - use cocoon_crate::{Error as CocoonError, MiniCocoon}; + use super::{Block, OpenOptions}; + use cocoon::{Error as CocoonError, MiniCocoon}; use core::fmt; use manta_crypto::rand::{Rand, SeedableRng}; use manta_util::from_variant_impl; use rand_chacha::ChaCha20Rng; - use std::{ - fs::{self, OpenOptions}, - io::Error as IoError, - path::Path, - }; + use std::{fs, io::Error as IoError, path::Path}; /// Cocoon Loading/Saving Error #[derive(Debug)] @@ -247,20 +319,9 @@ pub mod cocoon { impl File { /// Builds a new [`File`] for encrypted data storage with `password`. #[inline] - fn new( - path: &Path, - password: &[u8], - options: &super::OpenOptions, - ) -> Result<Self, IoError> { + fn new(path: &Path, password: &[u8], options: OpenOptions) -> Result<Self, IoError> { Ok(Self { - file: OpenOptions::new() - .read(options.read) - .write(options.write) - .append(options.append) - .truncate(options.truncate) - .create(options.create) - .create_new(options.create_new) - .open(path)?, + file: fs::OpenOptions::from(options).open(path)?, cocoon: MiniCocoon::from_password( password, &ChaCha20Rng::from_entropy().gen::<_, [u8; 32]>(), @@ -274,20 +335,16 @@ pub mod cocoon { type Error = Error; #[inline] - fn open<P>( - path: P, - password: &[u8], - options: &super::OpenOptions, - ) -> Result<Self, Self::Error> + fn open<P>(path: P, password: &[u8], options: &OpenOptions) -> Result<Self, Self::Error> where P: AsRef<Path>, { - Ok(Self::new(path.as_ref(), password, options)?) + Ok(Self::new(path.as_ref(), password, *options)?) } #[inline] fn write(&mut self, block: Block) -> Result<(), Self::Error> { - Ok(self.cocoon.dump(block.data.to_vec(), &mut self.file)?) + Ok(self.cocoon.dump(block.into(), &mut self.file)?) } #[inline] diff --git a/manta-accounting/src/fs/serde.rs b/manta-accounting/src/fs/serde.rs new file mode 100644 index 000000000..621f78f53 --- /dev/null +++ b/manta-accounting/src/fs/serde.rs @@ -0,0 +1,776 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Serde-Compatible Encrypted Filesystem + +use crate::fs::{Block, File}; +use alloc::vec::Vec; +use core::marker::PhantomData; +use manta_util::serde::{ + self, + de::Visitor, + ser::{ + SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple, + SerializeTupleStruct, SerializeTupleVariant, + }, + Serialize, +}; + +/// Serialization Module +pub mod ser { + use super::*; + use core::fmt::Display; + use derive_more::Display; + + /// Serialization Error + #[derive(Debug, Display)] + pub enum Error {} + + #[cfg(feature = "std")] + impl std::error::Error for Error {} + + impl serde::ser::Error for Error { + #[inline] + fn custom<T>(msg: T) -> Self + where + T: Display, + { + todo!() + } + } + + /// Compound Data Structure + pub struct Compound<'f, F>(PhantomData<&'f mut F>); +} + +/// Encrypting Serializer +pub struct Serializer<'f, F> +where + F: File, +{ + /// Encrypted File + file: &'f mut F, + + /// Current Block Data + block_data: Vec<u8>, +} + +impl<'f, F> Serializer<'f, F> +where + F: File, +{ + /// + #[inline] + pub fn new(file: &'f mut F) -> Self { + Self { + file, + block_data: Vec::with_capacity(Block::SIZE), + } + } +} + +impl<'f, F> serde::Serializer for &mut Serializer<'f, F> +where + F: File, +{ + type Ok = (); + type Error = ser::Error; + type SerializeSeq = ser::Compound<'f, F>; + type SerializeTuple = ser::Compound<'f, F>; + type SerializeTupleStruct = ser::Compound<'f, F>; + type SerializeTupleVariant = ser::Compound<'f, F>; + type SerializeMap = ser::Compound<'f, F>; + type SerializeStruct = ser::Compound<'f, F>; + type SerializeStructVariant = ser::Compound<'f, F>; + + #[inline] + fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> { + todo!() + } + + #[inline] + fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> { + todo!() + } + + #[inline] + fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> { + todo!() + } + + #[inline] + fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> { + todo!() + } + + #[inline] + fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> { + todo!() + } + + #[inline] + fn serialize_i128(self, v: i128) -> Result<Self::Ok, Self::Error> { + todo!() + } + + #[inline] + fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> { + todo!() + } + + #[inline] + fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> { + todo!() + } + + #[inline] + fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> { + todo!() + } + + #[inline] + fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> { + todo!() + } + + #[inline] + fn serialize_u128(self, v: u128) -> Result<Self::Ok, Self::Error> { + todo!() + } + + #[inline] + fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> { + todo!() + } + + #[inline] + fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> { + todo!() + } + + #[inline] + fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> { + todo!() + } + + #[inline] + fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> { + todo!() + } + + #[inline] + fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> { + todo!() + } + + #[inline] + fn serialize_none(self) -> Result<Self::Ok, Self::Error> { + todo!() + } + + #[inline] + fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error> + where + T: Serialize + ?Sized, + { + todo!() + } + + #[inline] + fn serialize_unit(self) -> Result<Self::Ok, Self::Error> { + todo!() + } + + #[inline] + fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> { + todo!() + } + + #[inline] + fn serialize_unit_variant( + self, + name: &'static str, + variant_index: u32, + variant: &'static str, + ) -> Result<Self::Ok, Self::Error> { + todo!() + } + + #[inline] + fn serialize_newtype_struct<T>( + self, + name: &'static str, + value: &T, + ) -> Result<Self::Ok, Self::Error> + where + T: Serialize + ?Sized, + { + todo!() + } + + #[inline] + fn serialize_newtype_variant<T>( + self, + name: &'static str, + variant_index: u32, + variant: &'static str, + value: &T, + ) -> Result<Self::Ok, Self::Error> + where + T: Serialize + ?Sized, + { + todo!() + } + + #[inline] + fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> { + todo!() + } + + #[inline] + fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> { + todo!() + } + + #[inline] + fn serialize_tuple_struct( + self, + name: &'static str, + len: usize, + ) -> Result<Self::SerializeTupleStruct, Self::Error> { + todo!() + } + + #[inline] + fn serialize_tuple_variant( + self, + name: &'static str, + variant_index: u32, + variant: &'static str, + len: usize, + ) -> Result<Self::SerializeTupleVariant, Self::Error> { + todo!() + } + + #[inline] + fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> { + todo!() + } + + #[inline] + fn serialize_struct( + self, + name: &'static str, + len: usize, + ) -> Result<Self::SerializeStruct, Self::Error> { + todo!() + } + + #[inline] + fn serialize_struct_variant( + self, + name: &'static str, + variant_index: u32, + variant: &'static str, + len: usize, + ) -> Result<Self::SerializeStructVariant, Self::Error> { + todo!() + } + + #[inline] + fn is_human_readable(&self) -> bool { + false + } +} + +impl<'f, F> SerializeSeq for ser::Compound<'f, F> { + type Ok = (); + type Error = ser::Error; + + #[inline] + fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + todo!() + } + + #[inline] + fn end(self) -> Result<Self::Ok, Self::Error> { + todo!() + } +} + +impl<'f, F> SerializeTuple for ser::Compound<'f, F> { + type Ok = (); + type Error = ser::Error; + + #[inline] + fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + todo!() + } + #[inline] + fn end(self) -> Result<Self::Ok, Self::Error> { + todo!() + } +} + +impl<'f, F> SerializeTupleStruct for ser::Compound<'f, F> { + type Ok = (); + type Error = ser::Error; + + #[inline] + fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + todo!() + } + + #[inline] + fn end(self) -> Result<Self::Ok, Self::Error> { + todo!() + } +} + +impl<'f, F> SerializeTupleVariant for ser::Compound<'f, F> { + type Ok = (); + type Error = ser::Error; + + #[inline] + fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + todo!() + } + + #[inline] + fn end(self) -> Result<Self::Ok, Self::Error> { + todo!() + } +} + +impl<'f, F> SerializeMap for ser::Compound<'f, F> { + type Ok = (); + type Error = ser::Error; + + #[inline] + fn serialize_key<T>(&mut self, key: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + todo!() + } + + #[inline] + fn serialize_value<T>(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + todo!() + } + + #[inline] + fn serialize_entry<K, V>(&mut self, key: &K, value: &V) -> Result<(), Self::Error> + where + K: Serialize + ?Sized, + V: Serialize + ?Sized, + { + todo!() + } + + #[inline] + fn end(self) -> Result<Self::Ok, Self::Error> { + todo!() + } +} + +impl<'f, F> SerializeStruct for ser::Compound<'f, F> { + type Ok = (); + type Error = ser::Error; + + #[inline] + fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + todo!() + } + + #[inline] + fn skip_field(&mut self, key: &'static str) -> Result<(), Self::Error> { + todo!() + } + + #[inline] + fn end(self) -> Result<Self::Ok, Self::Error> { + todo!() + } +} + +impl<'f, F> SerializeStructVariant for ser::Compound<'f, F> { + type Ok = (); + type Error = ser::Error; + + #[inline] + fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + todo!() + } + + #[inline] + fn skip_field(&mut self, key: &'static str) -> Result<(), Self::Error> { + todo!() + } + + #[inline] + fn end(self) -> Result<Self::Ok, Self::Error> { + todo!() + } +} + +/// Deserialization Module +pub mod de { + use super::*; + use core::fmt::Display; + use derive_more::Display; + + /// Deserialization Error + #[derive(Debug, Display)] + pub enum Error {} + + #[cfg(feature = "std")] + impl std::error::Error for Error {} + + impl serde::de::Error for Error { + #[inline] + fn custom<T>(msg: T) -> Self + where + T: Display, + { + todo!() + } + } +} + +/// Decrypting Deserializer +pub struct Deserializer<'f, F> +where + F: File, +{ + /// Encrypted File + file: &'f mut F, +} + +impl<'f, F> Deserializer<'f, F> +where + F: File, +{ + /// + #[inline] + pub fn new(file: &'f mut F) -> Self { + Self { file } + } +} + +impl<'de, 'f, F> manta_util::serde::Deserializer<'de> for &mut Deserializer<'f, F> +where + F: File, +{ + type Error = de::Error; + + #[inline] + fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_i128<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_u128<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_unit_struct<V>( + self, + name: &'static str, + visitor: V, + ) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_newtype_struct<V>( + self, + name: &'static str, + visitor: V, + ) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_tuple_struct<V>( + self, + name: &'static str, + len: usize, + visitor: V, + ) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_struct<V>( + self, + name: &'static str, + fields: &'static [&'static str], + visitor: V, + ) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_enum<V>( + self, + name: &'static str, + variants: &'static [&'static str], + visitor: V, + ) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + todo!() + } + + #[inline] + fn is_human_readable(&self) -> bool { + false + } +} diff --git a/manta-accounting/src/lib.rs b/manta-accounting/src/lib.rs index 72ac5e1b7..a034b03f7 100644 --- a/manta-accounting/src/lib.rs +++ b/manta-accounting/src/lib.rs @@ -24,11 +24,11 @@ extern crate alloc; extern crate derive_more; -#[cfg(feature = "cocoon")] -extern crate cocoon as cocoon_crate; - pub mod asset; -pub mod fs; pub mod key; pub mod transfer; pub mod wallet; + +#[cfg(feature = "fs")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "fs")))] +pub mod fs; diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index eef83a338..8955238fb 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -59,10 +59,10 @@ use manta_util::{ }; #[cfg(feature = "serde")] -use { - crate::fs::{self, File}, - manta_util::serde::{Deserialize, Serialize}, -}; +use manta_util::serde::{Deserialize, Serialize}; + +#[cfg(all(feature = "fs", feature = "serde"))] +use crate::fs::{self, File}; /// Signer Connection pub trait Connection<C> @@ -424,27 +424,27 @@ where C: Configuration, { /// Saves the state of `self` to the encrypted `file`. - #[cfg(feature = "serde")] + #[cfg(all(feature = "fs", feature = "serde"))] #[inline] fn save<F>(&self, file: &mut F) -> Result<(), F::Error> where Self: Serialize, F: File, { - let _ = fs::Serializer::new(file); + let _ = fs::serde::Serializer::new(file); // TODO: let _ = self.serialize(serializer); todo!() } /// Loads an encrypted [`SignerState`] from the encrypted `file`. - #[cfg(feature = "serde")] + #[cfg(all(feature = "fs", feature = "serde"))] #[inline] fn load<'de, F>(file: &mut F) -> Result<Self, F::Error> where Self: Deserialize<'de>, F: File, { - let _ = fs::Deserializer::new(file); + let _ = fs::serde::Deserializer::new(file); // TODO: let _ = Self::deserialize(deserializer); todo!() } @@ -914,8 +914,8 @@ where } /// Saves the state of `self` to the encrypted `file`. - #[cfg(feature = "serde")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] + #[cfg(all(feature = "fs", feature = "serde"))] + #[cfg_attr(doc_cfg, doc(cfg(all(feature = "fs", feature = "serde"))))] #[inline] pub fn save<F>(&self, file: &mut F) -> Result<(), F::Error> where @@ -926,8 +926,8 @@ where } /// Loads an encrypted [`Signer`] state from the encrypted `file`. - #[cfg(feature = "serde")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] + #[cfg(all(feature = "fs", feature = "serde"))] + #[cfg_attr(doc_cfg, doc(cfg(all(feature = "fs", feature = "serde"))))] #[inline] pub fn load<'de, F>(parameters: SignerParameters<C>, file: &mut F) -> Result<Self, F::Error> where From 0d26590628d167c7611245388fb65e8a51efa72d Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 9 Feb 2022 13:03:18 -0500 Subject: [PATCH 233/275] fix: correct the test feature selection --- manta-accounting/src/fs/mod.rs | 1 + manta-pay/Cargo.toml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/manta-accounting/src/fs/mod.rs b/manta-accounting/src/fs/mod.rs index 1a446714a..46598c9ba 100644 --- a/manta-accounting/src/fs/mod.rs +++ b/manta-accounting/src/fs/mod.rs @@ -20,6 +20,7 @@ use alloc::{boxed::Box, vec::Vec}; use core::{fmt::Debug, hash::Hash}; #[cfg(feature = "serde")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] pub mod serde; bitflags::bitflags! { diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 4c7eb9c20..67549f19c 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -82,7 +82,7 @@ ark-ff = { version = "0.3.0", optional = true, default-features = false } ark-groth16 = { version = "0.3.0", optional = true, default-features = false } ark-r1cs-std = { version = "0.3.1", optional = true, default-features = false } ark-relations = { version = "0.3.0", optional = true, default-features = false } -ark-serialize = { version = "0.3.0", optional = true, default-features = false } +ark-serialize = { version = "0.3.0", optional = true, default-features = false, features = ["derive"] } ark-snark = { version = "0.3.0", optional = true, default-features = false } ark-std = { version = "0.3.0", optional = true, default-features = false } bip32 = { version = "0.3.0", optional = true, default-features = false, features = ["bip39", "secp256k1"] } @@ -104,5 +104,5 @@ tungstenite = { version = "0.16.0", optional = true, default-features = false, f [dev-dependencies] criterion = "0.3.5" -manta-pay = { path = ".", features = ["wallet", "test"] } +manta-pay = { path = ".", features = ["groth16", "rand", "test", "wallet"] } tempfile = "3.2.0" From 4769934fcb7936ba8a7da22d7905c3cea3c2d0dd Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 9 Feb 2022 19:29:33 -0500 Subject: [PATCH 234/275] feat: add serialization for encrypted file system --- manta-accounting/src/fs/mod.rs | 55 +++- manta-accounting/src/fs/serde.rs | 512 ++++++++++++++++++++++++------- 2 files changed, 436 insertions(+), 131 deletions(-) diff --git a/manta-accounting/src/fs/mod.rs b/manta-accounting/src/fs/mod.rs index 46598c9ba..ff3f22857 100644 --- a/manta-accounting/src/fs/mod.rs +++ b/manta-accounting/src/fs/mod.rs @@ -17,7 +17,7 @@ //! Encrypted Filesystem Primitives use alloc::{boxed::Box, vec::Vec}; -use core::{fmt::Debug, hash::Hash}; +use core::{cmp, fmt::Debug, hash::Hash}; #[cfg(feature = "serde")] #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] @@ -201,14 +201,35 @@ impl Block { /// Builds a new [`Block`] from an owned collection of bytes. If the `data` vector is too short /// it's padded to fit the block and if it's too long, `None` is returned. #[inline] - pub fn new(mut data: Vec<u8>) -> Option<Self> { - if data.len() > Self::SIZE { - return None; - } + pub fn new(data: Vec<u8>) -> Option<Self> { + (data.len() <= Self::SIZE).then(|| Self::new_unchecked(data)) + } + + /// Builds a new [`Block`] from an owned collection of bytes without checking if the data vector + /// is too long to fit into a block. + #[inline] + pub fn new_unchecked(mut data: Vec<u8>) -> Self { data.resize(Self::SIZE, 0); - Some(Self { - data: data.into_boxed_slice().try_into().expect(""), - }) + Self { + data: data + .into_boxed_slice() + .try_into() + .expect("Input data is guaranteed to be no greater than the block size."), + } + } + + /// Parses a [`Block`] from an owned collection of bytes, leaving the remaining bytes in `data` + /// that don't fit into a single [`Block`] and padding otherwise. + #[inline] + pub fn parse(data: &mut Vec<u8>) -> Self { + Self::new_unchecked(data.drain(..cmp::min(data.len(), Self::SIZE)).collect()) + } + + /// Parses a [`Block`] from an owned collection of bytes, if the bytes in `data` fill at least + /// one [`Block`] otherwise, return `None`. + #[inline] + pub fn parse_full(data: &mut Vec<u8>) -> Option<Self> { + (data.len() >= Self::SIZE).then(|| Self::parse(data)) } } @@ -263,8 +284,9 @@ pub trait File: Sized { /// Writes `block` to `self` after encrypting it. fn write(&mut self, block: Block) -> Result<(), Self::Error>; - /// Reads a [`Block`] from `self` after decrypting it. - fn read(&mut self) -> Result<Block, Self::Error>; + /// Reads a [`Block`] from `self` after decrypting it, returning `None` if there are no more + /// blocks in the file. + fn read(&mut self) -> Result<Option<Block>, Self::Error>; } /// Cocoon Encrypted File System Adapter @@ -287,9 +309,6 @@ pub mod cocoon { /// Cocoon Error Cocoon(CocoonError), - - /// Invalid Block Size - InvalidBlockSize, } from_variant_impl!(Error, IoError, IoError); @@ -301,7 +320,6 @@ pub mod cocoon { match self { Self::IoError(err) => write!(f, "File I/O Error: {}", err), Self::Cocoon(err) => write!(f, "Cocoon Error: {:?}", err), - Self::InvalidBlockSize => write!(f, "Invalid Block Size"), } } } @@ -349,8 +367,13 @@ pub mod cocoon { } #[inline] - fn read(&mut self) -> Result<Block, Self::Error> { - Block::new(self.cocoon.parse(&mut self.file)?).ok_or(Error::InvalidBlockSize) + fn read(&mut self) -> Result<Option<Block>, Self::Error> { + let data = self.cocoon.parse(&mut self.file)?; + if !data.is_empty() { + Ok(Block::new(data)) + } else { + Ok(None) + } } } } diff --git a/manta-accounting/src/fs/serde.rs b/manta-accounting/src/fs/serde.rs index 621f78f53..89f5fe478 100644 --- a/manta-accounting/src/fs/serde.rs +++ b/manta-accounting/src/fs/serde.rs @@ -17,43 +17,77 @@ //! Serde-Compatible Encrypted Filesystem use crate::fs::{Block, File}; -use alloc::vec::Vec; -use core::marker::PhantomData; -use manta_util::serde::{ - self, - de::Visitor, - ser::{ - SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple, - SerializeTupleStruct, SerializeTupleVariant, +use alloc::{format, string::String, vec::Vec}; +use core::{ + fmt::{self, Debug, Display}, + write, +}; +use manta_util::{ + into_array_unchecked, + serde::{ + self, + de::{Error, Visitor}, + ser::{ + SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple, + SerializeTupleStruct, SerializeTupleVariant, + }, + Serialize, }, - Serialize, }; /// Serialization Module pub mod ser { use super::*; - use core::fmt::Display; - use derive_more::Display; /// Serialization Error - #[derive(Debug, Display)] - pub enum Error {} + #[derive(derivative::Derivative)] + #[derivative(Debug(bound = "F::Error: Debug"))] + pub enum Error<F> + where + F: File, + { + /// Serialization Error + Serialization(String), + + /// Encrypted File I/O Error + Io(F::Error), + } + + impl<F> Display for Error<F> + where + F: File, + F::Error: Display, + { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Serialization(msg) => write!(f, "Serialization Error: {}", msg), + Self::Io(err) => write!(f, "File I/O Error: {}", err), + } + } + } #[cfg(feature = "std")] - impl std::error::Error for Error {} + impl<F> std::error::Error for Error<F> + where + F: File, + F::Error: Debug + Display, + { + } - impl serde::ser::Error for Error { + impl<F> serde::ser::Error for Error<F> + where + F: File, + F::Error: Debug + Display, + { #[inline] fn custom<T>(msg: T) -> Self where T: Display, { - todo!() + Self::Serialization(format!("{}", msg)) } } - - /// Compound Data Structure - pub struct Compound<'f, F>(PhantomData<&'f mut F>); } /// Encrypting Serializer @@ -66,119 +100,228 @@ where /// Current Block Data block_data: Vec<u8>, + + /// Recursion Depth + recursion_depth: usize, } impl<'f, F> Serializer<'f, F> where F: File, { - /// + /// Builds a new [`Serializer`] for `file`. #[inline] pub fn new(file: &'f mut F) -> Self { Self { file, block_data: Vec::with_capacity(Block::SIZE), + recursion_depth: 0, } } + + /// Pushes a single byte to the block data. + #[inline] + fn push(&mut self, byte: u8) { + self.block_data.push(byte); + } + + /// Extends the block data by appending `bytes`. + #[inline] + fn extend(&mut self, bytes: &[u8]) { + self.block_data.extend_from_slice(bytes); + } + + /// Flushes the currently full blocks to the encrypted file system. + #[inline] + fn flush_intermediate(&mut self) -> Result<(), F::Error> { + // TODO: Design a block iterator for `self.block_data` to make this more efficient. + while let Some(block) = Block::parse_full(&mut self.block_data) { + self.file.write(block)?; + } + Ok(()) + } + + /// Flushes all the remaining blocks to the encrypted file system. + #[inline] + fn flush_end(&mut self) -> Result<(), F::Error> { + while !self.block_data.is_empty() { + self.file.write(Block::parse(&mut self.block_data))?; + } + Ok(()) + } + + /// Flushes bytes to the encrypted file system using either full blocks or the last, + /// partially-full block, depending on the recursion depth. + #[inline] + fn flush(&mut self) -> Result<(), ser::Error<F>> { + if self.recursion_depth == 0 { + self.flush_end() + } else { + self.flush_intermediate() + } + .map_err(ser::Error::Io) + } + + /// Starts a new sequence, increasing the recursion depth. + #[inline] + fn start_sequence(&mut self, len: Option<usize>) -> Result<&mut Self, ser::Error<F>> { + self.recursion_depth += 1; + if let Some(len) = len { + self.extend(&(len as u64).to_le_bytes()); + } + Ok(self) + } + + /// Starts a new `struct` or `tuple`, increasing the recursion depth. + #[inline] + fn start_struct(&mut self) -> Result<&mut Self, ser::Error<F>> { + self.recursion_depth += 1; + Ok(self) + } + + /// Starts a new `struct` or `tuple` relative to the `variant_index`, increasing the recursion + /// depth. + #[inline] + fn start_struct_with_variant( + &mut self, + variant_index: u32, + len: usize, + ) -> Result<&mut Self, ser::Error<F>> { + self.recursion_depth += 1; + + // TODO: Consider compression of the variant tag. Something like the following: + // + // ```rust + // let leading_zeros = ((len - 1) as u32).leading_zeros(); + // self.extend(&variant_index.to_le_bytes()[leading_zeros as usize..]); + // ``` + // + let _ = len; + self.extend(&variant_index.to_le_bytes()); + Ok(self) + } + + /// Ends the compound structure, decreasing the recursion depth and flushing to the encrypted + /// file system. + #[inline] + fn end_compound(&mut self) -> Result<(), ser::Error<F>> { + self.recursion_depth -= 1; + self.flush() + } } -impl<'f, F> serde::Serializer for &mut Serializer<'f, F> +impl<'s, 'f, F> serde::Serializer for &'s mut Serializer<'f, F> where F: File, + F::Error: Debug + Display, { type Ok = (); - type Error = ser::Error; - type SerializeSeq = ser::Compound<'f, F>; - type SerializeTuple = ser::Compound<'f, F>; - type SerializeTupleStruct = ser::Compound<'f, F>; - type SerializeTupleVariant = ser::Compound<'f, F>; - type SerializeMap = ser::Compound<'f, F>; - type SerializeStruct = ser::Compound<'f, F>; - type SerializeStructVariant = ser::Compound<'f, F>; + type Error = ser::Error<F>; + type SerializeSeq = Self; + type SerializeTuple = Self; + type SerializeTupleStruct = Self; + type SerializeTupleVariant = Self; + type SerializeMap = Self; + type SerializeStruct = Self; + type SerializeStructVariant = Self; #[inline] fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> { - todo!() + self.push(v as u8); + self.flush() } #[inline] fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> { - todo!() + self.extend(&v.to_le_bytes()); + self.flush() } #[inline] fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> { - todo!() + self.extend(&v.to_le_bytes()); + self.flush() } #[inline] fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> { - todo!() + self.extend(&v.to_le_bytes()); + self.flush() } #[inline] fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> { - todo!() + self.extend(&v.to_le_bytes()); + self.flush() } #[inline] fn serialize_i128(self, v: i128) -> Result<Self::Ok, Self::Error> { - todo!() + self.extend(&v.to_le_bytes()); + self.flush() } #[inline] fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> { - todo!() + self.extend(&v.to_le_bytes()); + self.flush() } #[inline] fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> { - todo!() + self.extend(&v.to_le_bytes()); + self.flush() } #[inline] fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> { - todo!() + self.extend(&v.to_le_bytes()); + self.flush() } #[inline] fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> { - todo!() + self.extend(&v.to_le_bytes()); + self.flush() } #[inline] fn serialize_u128(self, v: u128) -> Result<Self::Ok, Self::Error> { - todo!() + self.extend(&v.to_le_bytes()); + self.flush() } #[inline] fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> { - todo!() + self.extend(&v.to_le_bytes()); + self.flush() } #[inline] fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> { - todo!() + self.extend(&v.to_le_bytes()); + self.flush() } #[inline] fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> { - todo!() + (v as u32).serialize(self) } #[inline] fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> { - todo!() + v.as_bytes().serialize(self) } #[inline] fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> { - todo!() + self.extend(v); + self.flush() } #[inline] fn serialize_none(self) -> Result<Self::Ok, Self::Error> { - todo!() + 0u8.serialize(self) } #[inline] @@ -186,17 +329,19 @@ where where T: Serialize + ?Sized, { - todo!() + self.push(1u8); + value.serialize(self) } #[inline] fn serialize_unit(self) -> Result<Self::Ok, Self::Error> { - todo!() + Ok(()) } #[inline] fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> { - todo!() + let _ = name; + Ok(()) } #[inline] @@ -206,7 +351,8 @@ where variant_index: u32, variant: &'static str, ) -> Result<Self::Ok, Self::Error> { - todo!() + let _ = (name, variant); + variant_index.serialize(self) } #[inline] @@ -218,7 +364,8 @@ where where T: Serialize + ?Sized, { - todo!() + let _ = name; + value.serialize(self) } #[inline] @@ -232,17 +379,19 @@ where where T: Serialize + ?Sized, { - todo!() + let _ = (name, variant); + self.extend(&variant_index.to_le_bytes()); + value.serialize(self) } #[inline] fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> { - todo!() + self.start_sequence(len) } #[inline] fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> { - todo!() + self.start_struct() } #[inline] @@ -251,7 +400,8 @@ where name: &'static str, len: usize, ) -> Result<Self::SerializeTupleStruct, Self::Error> { - todo!() + let _ = (name, len); + self.start_struct() } #[inline] @@ -262,12 +412,13 @@ where variant: &'static str, len: usize, ) -> Result<Self::SerializeTupleVariant, Self::Error> { - todo!() + let _ = (name, variant); + self.start_struct_with_variant(variant_index, len) } #[inline] fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> { - todo!() + self.start_sequence(len) } #[inline] @@ -276,7 +427,8 @@ where name: &'static str, len: usize, ) -> Result<Self::SerializeStruct, Self::Error> { - todo!() + let _ = (name, len); + self.start_struct() } #[inline] @@ -287,7 +439,8 @@ where variant: &'static str, len: usize, ) -> Result<Self::SerializeStructVariant, Self::Error> { - todo!() + let _ = (name, variant); + self.start_struct_with_variant(variant_index, len) } #[inline] @@ -296,87 +449,108 @@ where } } -impl<'f, F> SerializeSeq for ser::Compound<'f, F> { +impl<'s, 'f, F> SerializeSeq for &'s mut Serializer<'f, F> +where + F: File, + F::Error: Debug + Display, +{ type Ok = (); - type Error = ser::Error; + type Error = ser::Error<F>; #[inline] fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { - todo!() + value.serialize(&mut **self) } #[inline] fn end(self) -> Result<Self::Ok, Self::Error> { - todo!() + self.end_compound() } } -impl<'f, F> SerializeTuple for ser::Compound<'f, F> { +impl<'s, 'f, F> SerializeTuple for &'s mut Serializer<'f, F> +where + F: File, + F::Error: Debug + Display, +{ type Ok = (); - type Error = ser::Error; + type Error = ser::Error<F>; #[inline] fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { - todo!() + value.serialize(&mut **self) } + #[inline] fn end(self) -> Result<Self::Ok, Self::Error> { - todo!() + self.end_compound() } } -impl<'f, F> SerializeTupleStruct for ser::Compound<'f, F> { +impl<'s, 'f, F> SerializeTupleStruct for &'s mut Serializer<'f, F> +where + F: File, + F::Error: Debug + Display, +{ type Ok = (); - type Error = ser::Error; + type Error = ser::Error<F>; #[inline] fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { - todo!() + value.serialize(&mut **self) } #[inline] fn end(self) -> Result<Self::Ok, Self::Error> { - todo!() + self.end_compound() } } -impl<'f, F> SerializeTupleVariant for ser::Compound<'f, F> { +impl<'s, 'f, F> SerializeTupleVariant for &'s mut Serializer<'f, F> +where + F: File, + F::Error: Debug + Display, +{ type Ok = (); - type Error = ser::Error; + type Error = ser::Error<F>; #[inline] fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { - todo!() + value.serialize(&mut **self) } #[inline] fn end(self) -> Result<Self::Ok, Self::Error> { - todo!() + self.end_compound() } } -impl<'f, F> SerializeMap for ser::Compound<'f, F> { +impl<'s, 'f, F> SerializeMap for &'s mut Serializer<'f, F> +where + F: File, + F::Error: Debug + Display, +{ type Ok = (); - type Error = ser::Error; + type Error = ser::Error<F>; #[inline] fn serialize_key<T>(&mut self, key: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { - todo!() + key.serialize(&mut **self) } #[inline] @@ -384,90 +558,124 @@ impl<'f, F> SerializeMap for ser::Compound<'f, F> { where T: Serialize + ?Sized, { - todo!() - } - - #[inline] - fn serialize_entry<K, V>(&mut self, key: &K, value: &V) -> Result<(), Self::Error> - where - K: Serialize + ?Sized, - V: Serialize + ?Sized, - { - todo!() + value.serialize(&mut **self) } #[inline] fn end(self) -> Result<Self::Ok, Self::Error> { - todo!() + self.end_compound() } } -impl<'f, F> SerializeStruct for ser::Compound<'f, F> { +impl<'s, 'f, F> SerializeStruct for &'s mut Serializer<'f, F> +where + F: File, + F::Error: Debug + Display, +{ type Ok = (); - type Error = ser::Error; + type Error = ser::Error<F>; #[inline] fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { - todo!() + let _ = key; + value.serialize(&mut **self) } #[inline] fn skip_field(&mut self, key: &'static str) -> Result<(), Self::Error> { - todo!() + let _ = key; + Ok(()) } #[inline] fn end(self) -> Result<Self::Ok, Self::Error> { - todo!() + self.end_compound() } } -impl<'f, F> SerializeStructVariant for ser::Compound<'f, F> { +impl<'s, 'f, F> SerializeStructVariant for &'s mut Serializer<'f, F> +where + F: File, + F::Error: Debug + Display, +{ type Ok = (); - type Error = ser::Error; + type Error = ser::Error<F>; #[inline] fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { - todo!() + let _ = key; + value.serialize(&mut **self) } #[inline] fn skip_field(&mut self, key: &'static str) -> Result<(), Self::Error> { - todo!() + let _ = key; + Ok(()) } #[inline] fn end(self) -> Result<Self::Ok, Self::Error> { - todo!() + self.end_compound() } } /// Deserialization Module pub mod de { use super::*; - use core::fmt::Display; - use derive_more::Display; /// Deserialization Error - #[derive(Debug, Display)] - pub enum Error {} + #[derive(derivative::Derivative)] + #[derivative(Debug(bound = "F::Error: Debug"))] + pub enum Error<F> + where + F: File, + { + /// Deserialization Error + Deserialization(String), + + /// Encrypted File I/O Error + Io(F::Error), + } + + impl<F> Display for Error<F> + where + F: File, + F::Error: Display, + { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Deserialization(msg) => write!(f, "Deserialization Error: {}", msg), + Self::Io(err) => write!(f, "File I/O Error: {}", err), + } + } + } #[cfg(feature = "std")] - impl std::error::Error for Error {} + impl<F> std::error::Error for Error<F> + where + F: File, + F::Error: Debug + Display, + { + } - impl serde::de::Error for Error { + impl<F> serde::de::Error for Error<F> + where + F: File, + F::Error: Debug + Display, + { #[inline] fn custom<T>(msg: T) -> Self where T: Display, { - todo!() + Self::Deserialization(format!("{}", msg)) } } } @@ -479,31 +687,94 @@ where { /// Encrypted File file: &'f mut F, + + /// Accumulated Block Data + block_data: Vec<u8>, } impl<'f, F> Deserializer<'f, F> where F: File, { - /// + /// Builds a new [`Deserializer`] for `file`. #[inline] pub fn new(file: &'f mut F) -> Self { - Self { file } + Self { + file, + block_data: Vec::with_capacity(Block::SIZE), + } + } + + /// Loads a block from the encrypted file system and appends it to the block data, returning + /// `true` if the block was loaded and `false` otherwise. + #[inline] + fn load(&mut self) -> Result<bool, F::Error> { + match self.file.read()? { + Some(block) => { + self.block_data.append(&mut block.into()); + Ok(true) + } + _ => Ok(false), + } + } + + /// Loads at least `n` bytes from the encrypted file system. If there are enough bytes in the + /// block data then, it does not perform any reads, and if there are not enough, it continually + /// reads until it meets the requirement `n`, returning `true` if the requirement was met. + #[inline] + fn load_bytes(&mut self, n: usize) -> Result<bool, F::Error> { + while self.block_data.len() < n { + if self.load()? { + continue; + } else { + return Ok(self.block_data.len() >= n); + } + } + Ok(true) + } + + /// Reads an array of bytes of size `N` from the encrypted file system, returning `None` if + /// there weren't enough bytes loaded. + #[inline] + fn read_bytes<const N: usize>(&mut self) -> Result<Option<[u8; N]>, F::Error> { + if self.load_bytes(N)? { + Ok(Some(into_array_unchecked( + self.block_data.drain(..N).collect::<Vec<_>>(), + ))) + } else { + Ok(None) + } + } + + /// Reads one `u8` from the encrypted file system, returning `None` if the `u8` was missing. + #[inline] + fn read_u8(&mut self) -> Result<Option<u8>, F::Error> { + Ok(self.read_bytes()?.map(u8::from_le_bytes)) + } + + /// Reads one `u32` from the encrypted file system, returning `None` if the `u32` was missing. + #[inline] + fn read_u32(&mut self) -> Result<Option<u32>, F::Error> { + Ok(self.read_bytes()?.map(u32::from_le_bytes)) } } -impl<'de, 'f, F> manta_util::serde::Deserializer<'de> for &mut Deserializer<'f, F> +impl<'de, 'f, F> serde::Deserializer<'de> for &mut Deserializer<'f, F> where F: File, + F::Error: Debug + Display, { - type Error = de::Error; + type Error = de::Error<F>; #[inline] fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de>, { - todo!() + let _ = visitor; + Err(Self::Error::custom( + "the Deserializer::deserialize_any method is not supported", + )) } #[inline] @@ -663,7 +934,7 @@ where where V: Visitor<'de>, { - todo!() + visitor.visit_unit() } #[inline] @@ -675,7 +946,8 @@ where where V: Visitor<'de>, { - todo!() + let _ = name; + visitor.visit_unit() } #[inline] @@ -687,6 +959,7 @@ where where V: Visitor<'de>, { + let _ = name; todo!() } @@ -716,6 +989,7 @@ where where V: Visitor<'de>, { + let _ = name; todo!() } @@ -737,6 +1011,7 @@ where where V: Visitor<'de>, { + let _ = name; todo!() } @@ -750,6 +1025,7 @@ where where V: Visitor<'de>, { + let _ = name; todo!() } @@ -758,7 +1034,10 @@ where where V: Visitor<'de>, { - todo!() + let _ = visitor; + Err(Self::Error::custom( + "the Deserializer::deserialize_identifier method is not supported", + )) } #[inline] @@ -766,7 +1045,10 @@ where where V: Visitor<'de>, { - todo!() + let _ = visitor; + Err(Self::Error::custom( + "the Deserializer::deserialize_ignored_any method is not supported", + )) } #[inline] From 453be1d5e8078f790d09c538b604b7fa07a0470f Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 10 Feb 2022 00:44:11 -0500 Subject: [PATCH 235/275] feat: add encrypted serde-deserialization --- manta-accounting/src/fs/serde.rs | 435 +++++++++++++++++++++++-------- manta-util/src/bytes.rs | 82 ++++++ manta-util/src/lib.rs | 4 + manta-util/src/num.rs | 27 ++ 4 files changed, 443 insertions(+), 105 deletions(-) create mode 100644 manta-util/src/bytes.rs create mode 100644 manta-util/src/num.rs diff --git a/manta-accounting/src/fs/serde.rs b/manta-accounting/src/fs/serde.rs index 89f5fe478..cf156e1b4 100644 --- a/manta-accounting/src/fs/serde.rs +++ b/manta-accounting/src/fs/serde.rs @@ -22,23 +22,35 @@ use core::{ fmt::{self, Debug, Display}, write, }; +use derive_more::Display; use manta_util::{ - into_array_unchecked, + into_array_unchecked, num, serde::{ self, - de::{Error, Visitor}, + de::{ + DeserializeSeed, Deserializer as _, EnumAccess, IntoDeserializer, MapAccess, SeqAccess, + VariantAccess, Visitor, + }, ser::{ SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple, SerializeTupleStruct, SerializeTupleVariant, }, Serialize, }, + FromBytes, IntoBytes, }; /// Serialization Module pub mod ser { use super::*; + /// Unsupported Feature + #[derive(Clone, Copy, Debug, Display, Eq, Hash, PartialEq)] + pub enum UnsupportedFeature { + /// Unknown Length Iterators + UnknownLengthIterators, + } + /// Serialization Error #[derive(derivative::Derivative)] #[derivative(Debug(bound = "F::Error: Debug"))] @@ -46,6 +58,9 @@ pub mod ser { where F: File, { + /// Unsupported Features + UnsupportedFeature(UnsupportedFeature), + /// Serialization Error Serialization(String), @@ -61,6 +76,7 @@ pub mod ser { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { + Self::UnsupportedFeature(feature) => write!(f, "Unsupported Feature: {}", feature), Self::Serialization(msg) => write!(f, "Serialization Error: {}", msg), Self::Io(err) => write!(f, "File I/O Error: {}", err), } @@ -119,18 +135,21 @@ where } } - /// Pushes a single byte to the block data. - #[inline] - fn push(&mut self, byte: u8) { - self.block_data.push(byte); - } - /// Extends the block data by appending `bytes`. #[inline] fn extend(&mut self, bytes: &[u8]) { self.block_data.extend_from_slice(bytes); } + /// Extends the block data by appending the bytes extracted from `t`. + #[inline] + fn extend_bytes<T, const N: usize>(&mut self, value: T) + where + T: IntoBytes<N>, + { + self.extend(&value.into_bytes()); + } + /// Flushes the currently full blocks to the encrypted file system. #[inline] fn flush_intermediate(&mut self) -> Result<(), F::Error> { @@ -162,14 +181,29 @@ where .map_err(ser::Error::Io) } + /// Writes the bytes of `t` after conversion into `self` and then flushes them to the encrypted + /// file system. + #[inline] + fn write_bytes<T, const N: usize>(&mut self, value: T) -> Result<(), ser::Error<F>> + where + T: IntoBytes<N>, + { + self.extend_bytes(value); + self.flush() + } + /// Starts a new sequence, increasing the recursion depth. #[inline] fn start_sequence(&mut self, len: Option<usize>) -> Result<&mut Self, ser::Error<F>> { - self.recursion_depth += 1; if let Some(len) = len { - self.extend(&(len as u64).to_le_bytes()); + self.recursion_depth += 1; + self.extend_bytes(len as u64); + Ok(self) + } else { + Err(ser::Error::UnsupportedFeature( + ser::UnsupportedFeature::UnknownLengthIterators, + )) } - Ok(self) } /// Starts a new `struct` or `tuple`, increasing the recursion depth. @@ -193,11 +227,11 @@ where // // ```rust // let leading_zeros = ((len - 1) as u32).leading_zeros(); - // self.extend(&variant_index.to_le_bytes()[leading_zeros as usize..]); + // self.extend(&variant_index.into_bytes()[leading_zeros as usize..]); // ``` // let _ = len; - self.extend(&variant_index.to_le_bytes()); + self.extend_bytes(variant_index); Ok(self) } @@ -210,7 +244,7 @@ where } } -impl<'s, 'f, F> serde::Serializer for &'s mut Serializer<'f, F> +impl<'f, F> serde::Serializer for &mut Serializer<'f, F> where F: File, F::Error: Debug + Display, @@ -227,85 +261,72 @@ where #[inline] fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> { - self.push(v as u8); - self.flush() + self.write_bytes(v as u8) } #[inline] fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> { - self.extend(&v.to_le_bytes()); - self.flush() + self.write_bytes(v) } #[inline] fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> { - self.extend(&v.to_le_bytes()); - self.flush() + self.write_bytes(v) } #[inline] fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> { - self.extend(&v.to_le_bytes()); - self.flush() + self.write_bytes(v) } #[inline] fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> { - self.extend(&v.to_le_bytes()); - self.flush() + self.write_bytes(v) } #[inline] fn serialize_i128(self, v: i128) -> Result<Self::Ok, Self::Error> { - self.extend(&v.to_le_bytes()); - self.flush() + self.write_bytes(v) } #[inline] fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> { - self.extend(&v.to_le_bytes()); - self.flush() + self.write_bytes(v) } #[inline] fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> { - self.extend(&v.to_le_bytes()); - self.flush() + self.write_bytes(v) } #[inline] fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> { - self.extend(&v.to_le_bytes()); - self.flush() + self.write_bytes(v) } #[inline] fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> { - self.extend(&v.to_le_bytes()); - self.flush() + self.write_bytes(v) } #[inline] fn serialize_u128(self, v: u128) -> Result<Self::Ok, Self::Error> { - self.extend(&v.to_le_bytes()); - self.flush() + self.write_bytes(v) } #[inline] fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> { - self.extend(&v.to_le_bytes()); - self.flush() + self.write_bytes(v) } #[inline] fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> { - self.extend(&v.to_le_bytes()); - self.flush() + self.write_bytes(v) } #[inline] fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> { - (v as u32).serialize(self) + self.write_bytes(v) } #[inline] @@ -315,13 +336,14 @@ where #[inline] fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> { + self.extend_bytes(v.len() as u64); self.extend(v); self.flush() } #[inline] fn serialize_none(self) -> Result<Self::Ok, Self::Error> { - 0u8.serialize(self) + self.write_bytes(0u8) } #[inline] @@ -329,7 +351,7 @@ where where T: Serialize + ?Sized, { - self.push(1u8); + self.extend_bytes(1u8); value.serialize(self) } @@ -391,6 +413,7 @@ where #[inline] fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> { + let _ = len; self.start_struct() } @@ -449,7 +472,7 @@ where } } -impl<'s, 'f, F> SerializeSeq for &'s mut Serializer<'f, F> +impl<'f, F> SerializeSeq for &mut Serializer<'f, F> where F: File, F::Error: Debug + Display, @@ -471,7 +494,7 @@ where } } -impl<'s, 'f, F> SerializeTuple for &'s mut Serializer<'f, F> +impl<'f, F> SerializeTuple for &mut Serializer<'f, F> where F: File, F::Error: Debug + Display, @@ -493,7 +516,7 @@ where } } -impl<'s, 'f, F> SerializeTupleStruct for &'s mut Serializer<'f, F> +impl<'f, F> SerializeTupleStruct for &mut Serializer<'f, F> where F: File, F::Error: Debug + Display, @@ -515,7 +538,7 @@ where } } -impl<'s, 'f, F> SerializeTupleVariant for &'s mut Serializer<'f, F> +impl<'f, F> SerializeTupleVariant for &mut Serializer<'f, F> where F: File, F::Error: Debug + Display, @@ -537,7 +560,7 @@ where } } -impl<'s, 'f, F> SerializeMap for &'s mut Serializer<'f, F> +impl<'f, F> SerializeMap for &mut Serializer<'f, F> where F: File, F::Error: Debug + Display, @@ -567,7 +590,7 @@ where } } -impl<'s, 'f, F> SerializeStruct for &'s mut Serializer<'f, F> +impl<'f, F> SerializeStruct for &mut Serializer<'f, F> where F: File, F::Error: Debug + Display, @@ -596,7 +619,7 @@ where } } -impl<'s, 'f, F> SerializeStructVariant for &'s mut Serializer<'f, F> +impl<'f, F> SerializeStructVariant for &mut Serializer<'f, F> where F: File, F::Error: Debug + Display, @@ -628,6 +651,20 @@ where /// Deserialization Module pub mod de { use super::*; + use alloc::string::FromUtf8Error; + + /// Unsupported Feature + #[derive(Clone, Copy, Debug, Display, Eq, Hash, PartialEq)] + pub enum UnsupportedFeature { + /// Deserialize Any + Any, + + /// Deserialize Identifier + Identifier, + + /// Deserialize Ignored Any + IgnoredAny, + } /// Deserialization Error #[derive(derivative::Derivative)] @@ -636,11 +673,32 @@ pub mod de { where F: File, { - /// Deserialization Error - Deserialization(String), - /// Encrypted File I/O Error Io(F::Error), + + /// Missing Bytes + MissingBytes, + + /// Unsupported Feature + UnsupportedFeature(UnsupportedFeature), + + /// Invalid Boolean Tag + InvalidBoolTag(u8), + + /// Invalid Option Tag + InvalidOptionTag(u8), + + /// Invalid UTF-8 Character + InvalidCharacter(u32), + + /// Large Length Tag + LargeLengthTag(u64), + + /// From UTF-8 Error + FromUtf8Error(FromUtf8Error), + + /// Deserialization Error + Deserialization(String), } impl<F> Display for Error<F> @@ -651,8 +709,15 @@ pub mod de { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - Self::Deserialization(msg) => write!(f, "Deserialization Error: {}", msg), Self::Io(err) => write!(f, "File I/O Error: {}", err), + Self::MissingBytes => write!(f, "Missing Bytes"), + Self::UnsupportedFeature(feature) => write!(f, "Unsupported Feature: {}", feature), + Self::InvalidBoolTag(tag) => write!(f, "Invalid Bool Tag: {}", tag), + Self::InvalidOptionTag(tag) => write!(f, "Invalid Option Tag: {}", tag), + Self::InvalidCharacter(integer) => write!(f, "Invalid Character: {}", integer), + Self::LargeLengthTag(len) => write!(f, "Large Length Tag: {}", len), + Self::FromUtf8Error(err) => write!(f, "From UTF-8 Error: {}", err), + Self::Deserialization(msg) => write!(f, "Deserialization Error: {}", msg), } } } @@ -678,6 +743,18 @@ pub mod de { Self::Deserialization(format!("{}", msg)) } } + + /// Length Access + pub struct LengthAccess<'d, 'f, F> + where + F: File, + { + /// Known Length + pub(super) len: usize, + + /// Deserializer + pub(super) deserializer: &'d mut Deserializer<'f, F>, + } } /// Decrypting Deserializer @@ -724,38 +801,55 @@ where #[inline] fn load_bytes(&mut self, n: usize) -> Result<bool, F::Error> { while self.block_data.len() < n { - if self.load()? { - continue; - } else { + if !self.load()? { return Ok(self.block_data.len() >= n); } } Ok(true) } - /// Reads an array of bytes of size `N` from the encrypted file system, returning `None` if - /// there weren't enough bytes loaded. + /// Reads `len` bytes from the encrypted file system and loads them into an owned vector. #[inline] - fn read_bytes<const N: usize>(&mut self) -> Result<Option<[u8; N]>, F::Error> { - if self.load_bytes(N)? { - Ok(Some(into_array_unchecked( - self.block_data.drain(..N).collect::<Vec<_>>(), - ))) + fn read_exact(&mut self, len: usize) -> Result<Vec<u8>, de::Error<F>> { + if self.load_bytes(len).map_err(de::Error::Io)? { + Ok(self.block_data.drain(..len).collect::<Vec<_>>()) } else { - Ok(None) + Err(de::Error::MissingBytes) } } - /// Reads one `u8` from the encrypted file system, returning `None` if the `u8` was missing. + /// Reads an array of bytes of size `N` from the encrypted file system. + #[inline] + fn read_array<const N: usize>(&mut self) -> Result<[u8; N], de::Error<F>> { + Ok(into_array_unchecked(self.read_exact(N)?)) + } + + /// Reads one element of the type `T` from the encrypted file system. + #[inline] + fn read_bytes<T, const N: usize>(&mut self) -> Result<T, de::Error<F>> + where + T: FromBytes<N>, + { + Ok(T::from_bytes(self.read_array()?)) + } + + /// Reads a length tag from `self`, trying to fit it into a `usize`. #[inline] - fn read_u8(&mut self) -> Result<Option<u8>, F::Error> { - Ok(self.read_bytes()?.map(u8::from_le_bytes)) + fn read_len(&mut self) -> Result<usize, de::Error<F>> { + num::u64_as_usize(self.read_bytes()?).map_err(de::Error::LargeLengthTag) } - /// Reads one `u32` from the encrypted file system, returning `None` if the `u32` was missing. + /// Reads a byte vector from `self`. #[inline] - fn read_u32(&mut self) -> Result<Option<u32>, F::Error> { - Ok(self.read_bytes()?.map(u32::from_le_bytes)) + fn read_byte_buf(&mut self) -> Result<Vec<u8>, de::Error<F>> { + let len = self.read_len()?; + self.read_exact(len) + } + + /// Reads a string from `self`. + #[inline] + fn read_string(&mut self) -> Result<String, de::Error<F>> { + String::from_utf8(self.read_byte_buf()?).map_err(de::Error::FromUtf8Error) } } @@ -772,9 +866,7 @@ where V: Visitor<'de>, { let _ = visitor; - Err(Self::Error::custom( - "the Deserializer::deserialize_any method is not supported", - )) + Err(Self::Error::UnsupportedFeature(de::UnsupportedFeature::Any)) } #[inline] @@ -782,7 +874,11 @@ where where V: Visitor<'de>, { - todo!() + match self.read_bytes()? { + 0u8 => visitor.visit_bool(false), + 1u8 => visitor.visit_bool(true), + tag => Err(Self::Error::InvalidBoolTag(tag)), + } } #[inline] @@ -790,7 +886,7 @@ where where V: Visitor<'de>, { - todo!() + visitor.visit_i8(self.read_bytes()?) } #[inline] @@ -798,7 +894,7 @@ where where V: Visitor<'de>, { - todo!() + visitor.visit_i16(self.read_bytes()?) } #[inline] @@ -806,7 +902,7 @@ where where V: Visitor<'de>, { - todo!() + visitor.visit_i32(self.read_bytes()?) } #[inline] @@ -814,7 +910,7 @@ where where V: Visitor<'de>, { - todo!() + visitor.visit_i64(self.read_bytes()?) } #[inline] @@ -822,7 +918,7 @@ where where V: Visitor<'de>, { - todo!() + visitor.visit_i128(self.read_bytes()?) } #[inline] @@ -830,7 +926,7 @@ where where V: Visitor<'de>, { - todo!() + visitor.visit_u8(self.read_bytes()?) } #[inline] @@ -838,7 +934,7 @@ where where V: Visitor<'de>, { - todo!() + visitor.visit_u16(self.read_bytes()?) } #[inline] @@ -846,7 +942,7 @@ where where V: Visitor<'de>, { - todo!() + visitor.visit_u32(self.read_bytes()?) } #[inline] @@ -854,7 +950,7 @@ where where V: Visitor<'de>, { - todo!() + visitor.visit_u64(self.read_bytes()?) } #[inline] @@ -862,7 +958,7 @@ where where V: Visitor<'de>, { - todo!() + visitor.visit_u128(self.read_bytes()?) } #[inline] @@ -870,7 +966,7 @@ where where V: Visitor<'de>, { - todo!() + visitor.visit_f32(self.read_bytes()?) } #[inline] @@ -878,7 +974,7 @@ where where V: Visitor<'de>, { - todo!() + visitor.visit_f64(self.read_bytes()?) } #[inline] @@ -886,7 +982,11 @@ where where V: Visitor<'de>, { - todo!() + let integer = self.read_bytes()?; + match char::from_u32(integer) { + Some(c) => visitor.visit_char(c), + _ => Err(Self::Error::InvalidCharacter(integer)), + } } #[inline] @@ -894,7 +994,7 @@ where where V: Visitor<'de>, { - todo!() + visitor.visit_str(&self.read_string()?) } #[inline] @@ -902,7 +1002,7 @@ where where V: Visitor<'de>, { - todo!() + visitor.visit_string(self.read_string()?) } #[inline] @@ -910,7 +1010,7 @@ where where V: Visitor<'de>, { - todo!() + visitor.visit_bytes(&self.read_byte_buf()?) } #[inline] @@ -918,7 +1018,7 @@ where where V: Visitor<'de>, { - todo!() + visitor.visit_byte_buf(self.read_byte_buf()?) } #[inline] @@ -926,7 +1026,11 @@ where where V: Visitor<'de>, { - todo!() + match self.read_bytes()? { + 0u8 => visitor.visit_none(), + 1u8 => visitor.visit_some(&mut *self), + tag => Err(Self::Error::InvalidOptionTag(tag)), + } } #[inline] @@ -960,7 +1064,7 @@ where V: Visitor<'de>, { let _ = name; - todo!() + visitor.visit_newtype_struct(self) } #[inline] @@ -968,7 +1072,8 @@ where where V: Visitor<'de>, { - todo!() + let len = self.read_len()?; + self.deserialize_tuple(len, visitor) } #[inline] @@ -976,7 +1081,10 @@ where where V: Visitor<'de>, { - todo!() + visitor.visit_seq(de::LengthAccess { + len, + deserializer: self, + }) } #[inline] @@ -990,7 +1098,7 @@ where V: Visitor<'de>, { let _ = name; - todo!() + self.deserialize_tuple(len, visitor) } #[inline] @@ -998,7 +1106,10 @@ where where V: Visitor<'de>, { - todo!() + visitor.visit_map(de::LengthAccess { + len: self.read_len()?, + deserializer: self, + }) } #[inline] @@ -1012,7 +1123,7 @@ where V: Visitor<'de>, { let _ = name; - todo!() + self.deserialize_tuple(fields.len(), visitor) } #[inline] @@ -1025,8 +1136,8 @@ where where V: Visitor<'de>, { - let _ = name; - todo!() + let _ = (name, variants); + visitor.visit_enum(self) } #[inline] @@ -1035,8 +1146,8 @@ where V: Visitor<'de>, { let _ = visitor; - Err(Self::Error::custom( - "the Deserializer::deserialize_identifier method is not supported", + Err(Self::Error::UnsupportedFeature( + de::UnsupportedFeature::Identifier, )) } @@ -1046,8 +1157,8 @@ where V: Visitor<'de>, { let _ = visitor; - Err(Self::Error::custom( - "the Deserializer::deserialize_ignored_any method is not supported", + Err(Self::Error::UnsupportedFeature( + de::UnsupportedFeature::IgnoredAny, )) } @@ -1056,3 +1167,117 @@ where false } } + +impl<'d, 'de, 'f, F> SeqAccess<'de> for de::LengthAccess<'d, 'f, F> +where + F: File, + F::Error: Debug + Display, +{ + type Error = de::Error<F>; + + #[inline] + fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error> + where + T: DeserializeSeed<'de>, + { + if self.len > 0 { + self.len -= 1; + Ok(Some(seed.deserialize(&mut *self.deserializer)?)) + } else { + Ok(None) + } + } + + #[inline] + fn size_hint(&self) -> Option<usize> { + Some(self.len) + } +} + +impl<'d, 'de, 'f, F> MapAccess<'de> for de::LengthAccess<'d, 'f, F> +where + F: File, + F::Error: Debug + Display, +{ + type Error = de::Error<F>; + + #[inline] + fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error> + where + K: DeserializeSeed<'de>, + { + self.next_element_seed(seed) + } + + #[inline] + fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error> + where + V: DeserializeSeed<'de>, + { + seed.deserialize(&mut *self.deserializer) + } + + #[inline] + fn size_hint(&self) -> Option<usize> { + Some(self.len) + } +} + +impl<'de, 'f, F> EnumAccess<'de> for &mut Deserializer<'f, F> +where + F: File, + F::Error: Debug + Display, +{ + type Error = de::Error<F>; + type Variant = Self; + + #[inline] + fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error> + where + V: DeserializeSeed<'de>, + { + let variant_index: u32 = self.read_bytes()?; + Ok((seed.deserialize(variant_index.into_deserializer())?, self)) + } +} + +impl<'de, 'f, F> VariantAccess<'de> for &mut Deserializer<'f, F> +where + F: File, + F::Error: Debug + Display, +{ + type Error = de::Error<F>; + + #[inline] + fn unit_variant(self) -> Result<(), Self::Error> { + Ok(()) + } + + #[inline] + fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value, Self::Error> + where + T: DeserializeSeed<'de>, + { + seed.deserialize(self) + } + + #[inline] + fn tuple_variant<V>(self, len: usize, visitor: V) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + self.deserialize_tuple(len, visitor) + } + + #[inline] + fn struct_variant<V>( + self, + fields: &'static [&'static str], + visitor: V, + ) -> Result<V::Value, Self::Error> + where + V: Visitor<'de>, + { + self.deserialize_tuple(fields.len(), visitor) + } +} diff --git a/manta-util/src/bytes.rs b/manta-util/src/bytes.rs new file mode 100644 index 000000000..9042de58c --- /dev/null +++ b/manta-util/src/bytes.rs @@ -0,0 +1,82 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Utilities for Manipulating Bytes + +/// Exact From Bytes Conversion +pub trait FromBytes<const SIZE: usize> { + /// Converts an array of `bytes` into an element of type [`Self`]. + fn from_bytes(bytes: [u8; SIZE]) -> Self; +} + +/// Exact Into Bytes Conversion +pub trait IntoBytes<const SIZE: usize> { + /// Converts `self` into its byte array representation of the given `SIZE`. + fn into_bytes(self) -> [u8; SIZE]; +} + +/// Exact Bytes Conversion +pub trait Bytes<const SIZE: usize>: FromBytes<SIZE> + IntoBytes<SIZE> {} + +impl<B, const SIZE: usize> Bytes<SIZE> for B where B: FromBytes<SIZE> + IntoBytes<SIZE> {} + +/// Implements [`Bytes`] 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 { + #[inline] + fn from_bytes(bytes: [u8; $size]) -> Self { + Self::from_le_bytes(bytes) + } + } + + impl IntoBytes<$size> for $type { + #[inline] + fn into_bytes(self) -> [u8; $size] { + self.to_le_bytes() + } + } + }; + ($($type:tt),* $(,)?) => { + $(impl_bytes_primitive!($type, { ($type::BITS / 8) as usize });)* + }; +} + +impl_bytes_primitive!(i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize); +impl_bytes_primitive!(f32, 4); +impl_bytes_primitive!(f64, 8); + +impl IntoBytes<4> for char { + #[inline] + fn into_bytes(self) -> [u8; 4] { + (self as u32).into_bytes() + } +} + +impl<const N: usize> FromBytes<N> for [u8; N] { + #[inline] + fn from_bytes(bytes: [u8; N]) -> Self { + bytes + } +} + +impl<const N: usize> IntoBytes<N> for [u8; N] { + #[inline] + fn into_bytes(self) -> [u8; N] { + self + } +} diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index a3bfcc1d6..c0300e1d0 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -25,19 +25,23 @@ extern crate alloc; mod array; +mod bytes; mod sealed; pub mod cache; pub mod codec; pub mod convert; pub mod iter; +pub mod num; pub mod persistance; pub mod pointer; pub use array::*; +pub use bytes::*; pub use sealed::*; #[cfg(feature = "serde")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] #[doc(inline)] pub use serde; diff --git a/manta-util/src/num.rs b/manta-util/src/num.rs new file mode 100644 index 000000000..7438faf34 --- /dev/null +++ b/manta-util/src/num.rs @@ -0,0 +1,27 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Numeric Utilities + +/// Tries to convert `n` into a `usize` depending on how big the `usize` type is. +#[inline] +pub const fn u64_as_usize(n: u64) -> Result<usize, u64> { + if n <= usize::MAX as u64 { + Ok(n as usize) + } else { + Err(n) + } +} From 93de189bcec21bf4acf0817cbed842e0f375f735 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 10 Feb 2022 01:07:56 -0500 Subject: [PATCH 236/275] fix(doc): correct spelling mistake for persistence --- manta-accounting/src/wallet/signer.rs | 2 +- manta-crypto/src/merkle_tree/forest.rs | 2 +- manta-crypto/src/merkle_tree/tree.rs | 2 +- manta-util/src/lib.rs | 2 +- manta-util/src/{persistance.rs => persistence.rs} | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename manta-util/src/{persistance.rs => persistence.rs} (97%) diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 8955238fb..c43ebfd72 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -55,7 +55,7 @@ use manta_util::{ cache::{CachedResource, CachedResourceError}, into_array_unchecked, iter::IteratorExt, - persistance::Rollback, + persistence::Rollback, }; #[cfg(feature = "serde")] diff --git a/manta-crypto/src/merkle_tree/forest.rs b/manta-crypto/src/merkle_tree/forest.rs index 78590dcfe..e5f6cf6ff 100644 --- a/manta-crypto/src/merkle_tree/forest.rs +++ b/manta-crypto/src/merkle_tree/forest.rs @@ -36,7 +36,7 @@ use crate::{ }; use alloc::{boxed::Box, vec::Vec}; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; -use manta_util::{persistance::Rollback, pointer::PointerFamily, BoxArray}; +use manta_util::{persistence::Rollback, pointer::PointerFamily, BoxArray}; #[cfg(feature = "serde")] use manta_util::serde::{Deserialize, Serialize}; diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index 9f30d953c..8534e86e6 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -39,7 +39,7 @@ use crate::{ use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use manta_util::{ codec::{Decode, DecodeError, Encode, Read, Write}, - persistance::Rollback, + persistence::Rollback, pointer::PointerFamily, }; diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index c0300e1d0..07a528deb 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -33,7 +33,7 @@ pub mod codec; pub mod convert; pub mod iter; pub mod num; -pub mod persistance; +pub mod persistence; pub mod pointer; pub use array::*; diff --git a/manta-util/src/persistance.rs b/manta-util/src/persistence.rs similarity index 97% rename from manta-util/src/persistance.rs rename to manta-util/src/persistence.rs index 75c543d99..bcebde45d 100644 --- a/manta-util/src/persistance.rs +++ b/manta-util/src/persistence.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Persistance and Backups Utilities +//! Persistence and Backups Utilities /// Rollback Trait /// From 512ca7bdf08616d9eccc15f26da3ad7a1bb0ba76 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 10 Feb 2022 14:30:12 -0500 Subject: [PATCH 237/275] feat: move simulation out of Rust testing into a binary --- manta-accounting/src/asset.rs | 2 +- manta-accounting/src/transfer/canonical.rs | 2 +- manta-crypto/src/ecc.rs | 48 +++- manta-crypto/src/rand.rs | 6 +- manta-pay/Cargo.toml | 37 ++- manta-pay/src/bin/generate_parameters.rs | 6 +- manta-pay/src/bin/simulation.rs | 29 ++ manta-pay/src/crypto/encryption.rs | 10 +- manta-pay/src/lib.rs | 12 +- manta-pay/src/signer/client/mod.rs | 8 +- manta-pay/src/signer/client/websocket.rs | 79 ++++-- .../src/{test/ledger.rs => simulation/mod.rs} | 268 +++++++++--------- manta-pay/src/test/mod.rs | 2 - manta-pay/src/test/transfer.rs | 16 +- manta-util/src/lib.rs | 2 +- manta-util/src/pointer.rs | 2 +- 16 files changed, 325 insertions(+), 204 deletions(-) create mode 100644 manta-pay/src/bin/simulation.rs rename manta-pay/src/{test/ledger.rs => simulation/mod.rs} (73%) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 2ed6fdf72..fa93e5dc1 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -784,7 +784,7 @@ pub trait AssetMap: Default { /// Implements [`AssetMap`] for map types. macro_rules! impl_asset_map_for_maps_body { - ($k:tt, $entry:tt) => { + ($k:ty, $entry:tt) => { type Key = $k; #[inline] diff --git a/manta-accounting/src/transfer/canonical.rs b/manta-accounting/src/transfer/canonical.rs index b1034ef04..bf60e1b73 100644 --- a/manta-accounting/src/transfer/canonical.rs +++ b/manta-accounting/src/transfer/canonical.rs @@ -57,7 +57,7 @@ pub trait Shape: sealed::Sealed { /// Implements [`Shape`] for a given shape type. macro_rules! impl_shape { - ($shape:tt, $sources:expr, $senders:expr, $receivers:expr, $sinks:expr) => { + ($shape:ty, $sources:expr, $senders:expr, $receivers:expr, $sinks:expr) => { seal!($shape); impl Shape for $shape { const SOURCES: usize = $sources; diff --git a/manta-crypto/src/ecc.rs b/manta-crypto/src/ecc.rs index 2684f79cb..b4d26027d 100644 --- a/manta-crypto/src/ecc.rs +++ b/manta-crypto/src/ecc.rs @@ -38,7 +38,31 @@ pub trait PointDouble<COM = ()> { type Output; /// Performs a point doubling of `self` in `compiler`. + #[must_use] fn double(&self, compiler: &mut COM) -> Self::Output; + + /// Performs a point doubling of `self` in `compiler` using an owned value. + /// + /// # Implementation Note + /// + /// This method is an optimization path for [`double`](Self::double) whenever operating with + /// owned values is more efficient than with shared references. + #[inline] + fn double_owned(self, compiler: &mut COM) -> Self::Output + where + Self: Sized, + { + self.double(compiler) + } + + /// Performs a point doubling of `self` in `compiler`, modifying `self` in-place. + #[inline] + fn double_assign(&mut self, compiler: &mut COM) + where + Self: PointDouble<COM, Output = Self> + Sized, + { + *self = self.double(compiler); + } } /// Elliptic Curve Point Addition Operation @@ -57,7 +81,6 @@ pub trait PointAdd<COM = ()> { /// This method is an optimization path for [`add`](Self::add) whenever operating with owned /// values is more efficient than with shared references. #[inline] - #[must_use] fn add_owned(self, rhs: Self, compiler: &mut COM) -> Self::Output where Self: Sized, @@ -86,6 +109,29 @@ pub trait ScalarMul<COM = ()> { /// Multiplies `self` by `scalar` in `compiler`, returning a new group element. #[must_use] fn scalar_mul(&self, scalar: &Self::Scalar, compiler: &mut COM) -> Self::Output; + + /// Multiplies an owned `scalar` value to `self` in `compiler`, returning a new group element. + /// + /// # Implementation Note + /// + /// This method is an optimization path for [`scalar_mul`](Self::scalar_mul) whenever operating + /// with owned values is more efficient than with shared references. + #[inline] + fn scalar_mul_owned(self, scalar: Self::Scalar, compiler: &mut COM) -> Self::Output + where + Self: Sized, + { + self.scalar_mul(&scalar, compiler) + } + + /// Multiplies `self` by `scalar` in `compiler`, modifying `self` in-place. + #[inline] + fn scalar_mul_assign(&mut self, scalar: &Self::Scalar, compiler: &mut COM) + where + Self: ScalarMul<COM, Output = Self> + Sized, + { + *self = self.scalar_mul(scalar, compiler); + } } /// Elliptic Curve Pre-processed Scalar Multiplication Operation diff --git a/manta-crypto/src/rand.rs b/manta-crypto/src/rand.rs index 633ddb6e7..d7e27667a 100644 --- a/manta-crypto/src/rand.rs +++ b/manta-crypto/src/rand.rs @@ -169,7 +169,7 @@ where /// /// This `trait` is automatically implemented for all [`SeedableRng`] whenever the `getrandom` crate /// is in scope. This `trait` is used to capture the behavior of seeding from an entropy source even -/// if the crate is not imported. +/// if the `getrandom` crate is not imported. pub trait FromEntropy { /// Creates a new instance of `Self` seeded via some entropy source. fn from_entropy() -> Self; @@ -213,7 +213,7 @@ pub trait Sample<D = Standard>: Sized { /// Generates [`Sample`] implementation for `$type` using conversion from `u32`. macro_rules! impl_sample_from_u32 { - ($($type:tt),+) => { + ($($type:ty),+) => { $( impl Sample for $type { #[inline] @@ -229,7 +229,7 @@ macro_rules! impl_sample_from_u32 { }; } -impl_sample_from_u32! { u8, u16, u32 } +impl_sample_from_u32!(u8, u16, u32); impl Sample for u64 { #[inline] diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 67549f19c..116616328 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -32,6 +32,10 @@ harness = false name = "generate_parameters" required-features = ["groth16", "manta-util/std", "test"] +[[bin]] +name = "simulation" +required-features = ["groth16", "simulation", "tempfile"] + [features] # Enable Arkworks Backend arkworks = [ @@ -48,6 +52,9 @@ arkworks = [ # Enable Groth16 ZKP System groth16 = ["ark-groth16", "ark-snark", "arkworks"] +# Enable HTTP Signer Client +http = ["reqwest"] + # TODO: Enable PLONK ZKP System # TODO: plonk = ["zk-garage-plonk"] @@ -55,24 +62,27 @@ groth16 = ["ark-groth16", "ark-snark", "arkworks"] serde = ["manta-accounting/serde", "manta-crypto/serde"] # Simulation Framework -simulation = ["rand", "statrs"] - -# Standard Library -std = ["manta-accounting/std", "manta-util/std"] - -# Testing Frameworks -test = [ +simulation = [ "indexmap", "manta-accounting/parallel", - "manta-accounting/test", - "manta-crypto/test", "parking_lot", "rayon", + "std", + "test", ] +# Standard Library +std = ["manta-accounting/std", "manta-util/std"] + +# Testing Frameworks +test = ["manta-accounting/test", "manta-crypto/test"] + # Wallet wallet = ["bip32", "manta-crypto/getrandom", "std"] +# Enable WebSocket Signer Client +websocket = ["serde_json", "tungstenite"] + [dependencies] aes-gcm = { version = "0.9.4", default-features = false, features = ["aes", "alloc"] } ark-bls12-381 = { version = "0.3.0", optional = true, default-features = false, features = ["curve"] } @@ -93,16 +103,17 @@ manta-accounting = { path = "../manta-accounting", default-features = false } manta-crypto = { path = "../manta-crypto", default-features = false } manta-util = { path = "../manta-util", default-features = false } parking_lot = { version = "0.12.0", optional = true, default-features = false } -rand = { version = "0.8.4", optional = true } +# TODO: rand = { version = "0.8.4", optional = true } rand_chacha = { version = "0.3.1", default-features = false } rayon = { version = "1.5.1", optional = true, default-features = false } reqwest = { version = "0.11.9", optional = true, default-features = false, features = ["blocking", "json"] } scale-codec = { package = "parity-scale-codec", version = "2.3.1", default-features = false, features = ["derive"] } -statrs = { version = "0.15.0", optional = true, default-features = false } +serde_json = { version = "1.0.78", optional = true, default-features = false } +# TODO: statrs = { version = "0.15.0", optional = true, default-features = false } +tempfile = { version = "3.2.0", optional = true, default-features = false } tungstenite = { version = "0.16.0", optional = true, default-features = false, features = ["native-tls"] } # TODO: zk-garage-plonk = { package = "plonk", git = "https://github.com/zk-garage/plonk", optional = true, default-features = false } [dev-dependencies] criterion = "0.3.5" -manta-pay = { path = ".", features = ["groth16", "rand", "test", "wallet"] } -tempfile = "3.2.0" +manta-pay = { path = ".", features = ["groth16", "test"] } diff --git a/manta-pay/src/bin/generate_parameters.rs b/manta-pay/src/bin/generate_parameters.rs index 6feeaa9c3..dde069fe4 100644 --- a/manta-pay/src/bin/generate_parameters.rs +++ b/manta-pay/src/bin/generate_parameters.rs @@ -16,13 +16,12 @@ //! Generate Parameters -// TODO: Specify target directory in `main`. // TODO: Deduplicate the per-circuit proving context and verifying context serialization code. // TODO: Print some statistics about the parameters and circuits and into a stats file as well. use manta_accounting::transfer; use manta_crypto::{ - constraint::ProofSystem as _, + constraint::{measure::Measure, ProofSystem as _}, rand::{Rand, SeedableRng}, }; use manta_pay::config::{ @@ -125,6 +124,7 @@ pub fn main() -> io::Result<()> { fs::create_dir_all(&verifying_context_dir)?; let cs = Mint::unknown_constraints(full_parameters); + println!("MINT: {:#?}", cs.measure()); let (proving_context, verifying_context) = ProofSystem::generate_context(&(), cs, &mut rng).unwrap(); proving_context @@ -145,6 +145,7 @@ pub fn main() -> io::Result<()> { .unwrap(); let cs = PrivateTransfer::unknown_constraints(full_parameters); + println!("PRIVATE-TRANSFER: {:#?}", cs.measure()); let (proving_context, verifying_context) = ProofSystem::generate_context(&(), cs, &mut rng).unwrap(); proving_context @@ -165,6 +166,7 @@ pub fn main() -> io::Result<()> { .unwrap(); let cs = Reclaim::unknown_constraints(full_parameters); + println!("RECLAIM: {:#?}", cs.measure()); let (proving_context, verifying_context) = ProofSystem::generate_context(&(), cs, &mut rng).unwrap(); proving_context diff --git a/manta-pay/src/bin/simulation.rs b/manta-pay/src/bin/simulation.rs new file mode 100644 index 000000000..b9bcf66ab --- /dev/null +++ b/manta-pay/src/bin/simulation.rs @@ -0,0 +1,29 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Manta Pay Simulation + +use std::io; + +/// Runs the Manta Pay simulation. +#[inline] +pub fn main() -> io::Result<()> { + let directory = tempfile::tempdir()?; + println!("[INFO] Temporary Directory: {:?}", directory); + manta_pay::simulation::simulate(10, 10, directory.path()); + directory.close()?; + Ok(()) +} diff --git a/manta-pay/src/crypto/encryption.rs b/manta-pay/src/crypto/encryption.rs index fe03107da..658fb8d9a 100644 --- a/manta-pay/src/crypto/encryption.rs +++ b/manta-pay/src/crypto/encryption.rs @@ -39,6 +39,7 @@ pub mod aes { } /// AES Galois Counter Mode + #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct AesGcm<const P: usize, const C: usize>; impl<const P: usize, const C: usize> AesGcm<P, C> { @@ -78,13 +79,16 @@ pub mod aes { mod test { use super::*; use manta_accounting::asset::Asset; - use manta_crypto::{encryption, rand::RngCore}; - use rand::thread_rng; + use manta_crypto::{ + encryption, + rand::{FromEntropy, RngCore}, + }; + use rand_chacha::ChaCha20Rng; /// Tests if symmetric encryption of [`Asset`] decrypts properly. #[test] fn asset_encryption() { - let mut rng = thread_rng(); + let mut rng = ChaCha20Rng::from_entropy(); let mut key = [0; 32]; rng.fill_bytes(&mut key); let mut plaintext = [0; Asset::SIZE]; diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index 05b6f2b97..d58f9925e 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -28,14 +28,18 @@ mod test; pub mod crypto; -#[cfg(feature = "bip32")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "bip32")))] -pub mod key; - #[cfg(feature = "groth16")] #[cfg_attr(doc_cfg, doc(cfg(feature = "groth16")))] pub mod config; +#[cfg(feature = "bip32")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "bip32")))] +pub mod key; + #[cfg(feature = "groth16")] #[cfg_attr(doc_cfg, doc(cfg(feature = "groth16")))] pub mod signer; + +#[cfg(all(feature = "groth16", feature = "simulation"))] +#[cfg_attr(doc_cfg, doc(cfg(all(feature = "groth16", feature = "simulation"))))] +pub mod simulation; diff --git a/manta-pay/src/signer/client/mod.rs b/manta-pay/src/signer/client/mod.rs index d2b0f1f29..69687d6d5 100644 --- a/manta-pay/src/signer/client/mod.rs +++ b/manta-pay/src/signer/client/mod.rs @@ -16,10 +16,10 @@ //! Signer Client Implementations -#[cfg(feature = "reqwest")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "reqwest")))] +#[cfg(feature = "http")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "http")))] pub mod http; -#[cfg(feature = "tungstenite")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "tungstenite")))] +#[cfg(feature = "websocket")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "websocket")))] pub mod websocket; diff --git a/manta-pay/src/signer/client/websocket.rs b/manta-pay/src/signer/client/websocket.rs index d197be663..a96f554c6 100644 --- a/manta-pay/src/signer/client/websocket.rs +++ b/manta-pay/src/signer/client/websocket.rs @@ -23,51 +23,78 @@ use manta_accounting::{ self, ReceivingKeyRequest, SignError, SignResponse, SyncError, SyncRequest, SyncResponse, }, }; -use manta_util::serde::{de::DeserializeOwned, Serialize}; -use std::net::TcpStream; -use tungstenite::{ - client::IntoClientRequest, - handshake::server::{NoCallback, ServerHandshake}, - stream::MaybeTlsStream, - Message, +use manta_util::{ + from_variant_impl, + serde::{de::DeserializeOwned, Deserialize, Serialize}, }; +use std::net::TcpStream; +use tungstenite::{client::IntoClientRequest, stream::MaybeTlsStream, Message}; + +/// Web Socket Error +pub type WebSocketError = tungstenite::error::Error; -/// Stream Type -pub type Stream = MaybeTlsStream<TcpStream>; +/// Client Error +#[derive(Debug)] +pub enum Error { + /// Invalid Message Format + /// + /// The message received from the WebSocket connection was not a [`Message::Text`]. + InvalidMessageFormat, -/// Error Type -pub type Error = tungstenite::error::Error; + /// Serialization Error + SerializationError(serde_json::Error), + + /// WebSocket Error + WebSocket(WebSocketError), +} -/// Handshake Error Type -pub type HandshakeError = - tungstenite::handshake::HandshakeError<ServerHandshake<Stream, NoCallback>>; +from_variant_impl!(Error, SerializationError, serde_json::Error); +from_variant_impl!(Error, WebSocket, WebSocketError); + +/// Request +#[derive(derivative::Derivative, Deserialize, Serialize)] +#[serde(crate = "manta_util::serde")] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +pub struct Request<R> { + /// Request Command + /// + /// This command is used by the server to decide which command to execute the request on, and to + /// parse the request correctly from the serialized data. + pub command: &'static str, + + /// Request Body + pub request: R, +} /// WebSocket Client -pub struct Client(tungstenite::WebSocket<Stream>); +pub struct Client(tungstenite::WebSocket<MaybeTlsStream<TcpStream>>); impl Client { /// Builds a new [`Client`] from `url`. #[inline] - pub fn new<U>(url: U) -> Result<Self, Error> + pub fn new<U>(url: U) -> Result<Self, WebSocketError> where U: IntoClientRequest, { Ok(Self(tungstenite::connect(url)?.0)) } - /// Sends a `request` for the given `command` along the websockets and waits for the response. + /// Sends a `request` for the given `command` along the channel and waits for the response. #[inline] - pub fn send<Request, Response>( - &mut self, - command: &str, - request: Request, - ) -> Result<Response, Error> + fn send<S, D>(&mut self, command: &'static str, request: S) -> Result<D, Error> where - Request: Serialize, - Response: DeserializeOwned, + S: Serialize, + D: DeserializeOwned, { - // TODO: self.0.read_message(self.0.write_message(request)?) - todo!() + self.0 + .write_message(Message::Text(serde_json::to_string(&Request { + command, + request, + })?))?; + match self.0.read_message()? { + Message::Text(message) => Ok(serde_json::from_str(&message)?), + _ => Err(Error::InvalidMessageFormat), + } } } diff --git a/manta-pay/src/test/ledger.rs b/manta-pay/src/simulation/mod.rs similarity index 73% rename from manta-pay/src/test/ledger.rs rename to manta-pay/src/simulation/mod.rs index e0d841129..4c1edc65b 100644 --- a/manta-pay/src/test/ledger.rs +++ b/manta-pay/src/simulation/mod.rs @@ -14,27 +14,40 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Test Ledger Implementation +//! Ledger Simulation -// FIXME: How to model existential deposits and fee payments? -// FIXME: Add in some concurrency (and measure how much we need it). +// TODO: Move as much of this code into `manta-accounting` simulation as possible. +// TODO: How to model existential deposits and fee payments? +// TODO: Add in some concurrency (and measure how much we need it). -use crate::config::{ - Config, EncryptedNote, MerkleTreeConfiguration, MultiVerifyingContext, ProofSystem, - TransferPost, Utxo, UtxoSetModel, VoidNumber, +use crate::{ + config::{ + Config, EncryptedNote, FullParameters, MerkleTreeConfiguration, MultiVerifyingContext, + ProofSystem, TransferPost, Utxo, UtxoSetModel, VoidNumber, + }, + signer::base::{cache::OnDiskMultiProvingContext, Signer, UtxoSet}, }; use alloc::{sync::Arc, vec::Vec}; use core::convert::Infallible; use indexmap::IndexSet; use manta_accounting::{ - asset::{AssetId, AssetValue}, + asset::{Asset, AssetId, AssetList, AssetValue}, + key::AccountTable, + transfer, transfer::{ canonical::TransferShape, AccountBalance, InvalidSinkAccount, InvalidSourceAccount, Proof, ReceiverLedger, ReceiverPostingKey, SenderLedger, SenderPostingKey, SinkPostingKey, SourcePostingKey, TransferLedger, TransferLedgerSuperPostingKey, TransferPostingKey, UtxoSetOutput, }, - wallet::ledger::{self, PullResponse, PullResult, PushResponse, PushResult}, + wallet::{ + ledger::{self, PullResponse, PullResult, PushResponse, PushResult}, + test::{ + sim::{ActionSim, Simulator}, + ActionType, Actor, PublicBalanceOracle, Simulation, + }, + Wallet, + }, }; use manta_crypto::{ constraint::ProofSystem as _, @@ -43,10 +56,15 @@ use manta_crypto::{ forest::{Configuration, FixedIndex}, Tree, }, + rand::{CryptoRng, Rand, RngCore, SeedableRng}, }; use manta_util::into_array_unchecked; use parking_lot::RwLock; -use std::collections::{HashMap, HashSet}; +use rand_chacha::ChaCha20Rng; +use std::{ + collections::{HashMap, HashSet}, + path::Path, +}; /// Merkle Forest Index pub type MerkleForestIndex = <MerkleTreeConfiguration as Configuration>::Index; @@ -450,142 +468,124 @@ impl ledger::Connection<Config> for LedgerConnection { } } -/// Testing Suite -#[cfg(test)] -mod test { - use super::*; - use crate::{ - config::FullParameters, - signer::base::{cache::OnDiskMultiProvingContext, Signer, UtxoSet}, - }; - use manta_accounting::{ - asset::{Asset, AssetList}, - key::AccountTable, - transfer, - wallet::{ - test::{ - sim::{ActionSim, Simulator}, - ActionType, Actor, PublicBalanceOracle, Simulation, - }, - Wallet, - }, - }; - use manta_crypto::rand::{CryptoRng, Rand, RngCore, SeedableRng}; - use rand_chacha::ChaCha20Rng; - - impl PublicBalanceOracle for LedgerConnection { - #[inline] - fn public_balances(&self) -> Option<AssetList> { - Some( - self.ledger - .read() - .accounts - .get(&self.account)? - .iter() - .map(|(id, value)| Asset::new(*id, *value)) - .collect(), - ) - } - } - - /// Samples an empty wallet for `account` on `ledger`. +impl PublicBalanceOracle for LedgerConnection { #[inline] - fn sample_wallet<R>( - account: AccountId, - ledger: &SharedLedger, - cache: &OnDiskMultiProvingContext, - parameters: &transfer::Parameters<Config>, - utxo_set_model: &UtxoSetModel, - rng: &mut R, - ) -> Wallet<Config, LedgerConnection, Signer> - where - R: CryptoRng + RngCore + ?Sized, - { - Wallet::new( - LedgerConnection::new(account, ledger.clone()), - Signer::new( - AccountTable::new(rng.gen()), - cache.clone(), - parameters.clone(), - UtxoSet::new(utxo_set_model.clone()), - rng.seed_rng().expect("Failed to sample PRNG for signer."), - ), + fn public_balances(&self) -> Option<AssetList> { + Some( + self.ledger + .read() + .accounts + .get(&self.account)? + .iter() + .map(|(id, value)| Asset::new(*id, *value)) + .collect(), ) } +} - /// Runs a simple simulation to test that the signer-wallet-ledger connection works. - #[test] - fn test_simulation() { - let directory = tempfile::tempdir().expect("Unable to generate temporary test directory."); - println!("[INFO] Temporary Directory: {:?}", directory); - - let mut rng = ChaCha20Rng::from_entropy(); - let parameters = rng.gen(); - let utxo_set_model = rng.gen(); - - let (proving_context, verifying_context) = transfer::canonical::generate_context( - &(), - FullParameters::new(&parameters, &utxo_set_model), - &mut rng, - ) - .expect("Failed to generate contexts."); - - let cache = OnDiskMultiProvingContext::new(directory.path()); - cache - .save(proving_context) - .expect("Unable to save proving context to disk."); +/// Samples an empty wallet for `account` on `ledger`. +#[inline] +pub fn sample_wallet<R>( + account: AccountId, + ledger: &SharedLedger, + cache: &OnDiskMultiProvingContext, + parameters: &transfer::Parameters<Config>, + utxo_set_model: &UtxoSetModel, + rng: &mut R, +) -> Wallet<Config, LedgerConnection, Signer> +where + R: CryptoRng + RngCore + ?Sized, +{ + Wallet::new( + LedgerConnection::new(account, ledger.clone()), + Signer::new( + AccountTable::new(rng.gen()), + cache.clone(), + parameters.clone(), + UtxoSet::new(utxo_set_model.clone()), + rng.seed_rng().expect("Failed to sample PRNG for signer."), + ), + ) +} - const ACTOR_COUNT: usize = 10; - const ACTOR_LIFETIME: usize = 300; +/// Runs a simple simulation to test that the signer-wallet-ledger connection works. +#[inline] +pub fn simulate<P>(actor_count: usize, actor_lifetime: usize, directory: P) +where + P: AsRef<Path>, +{ + let mut rng = ChaCha20Rng::from_entropy(); + let parameters = rng.gen(); + let utxo_set_model = rng.gen(); + + let (proving_context, verifying_context) = transfer::canonical::generate_context( + &(), + FullParameters::new(&parameters, &utxo_set_model), + &mut rng, + ) + .expect("Failed to generate contexts."); + + let cache = OnDiskMultiProvingContext::new(directory); + cache + .save(proving_context) + .expect("Unable to save proving context to disk."); + + let mut ledger = Ledger::new(utxo_set_model.clone(), verifying_context); + + for i in 0..actor_count { + ledger.set_public_balance(AccountId(i as u64), AssetId(0), AssetValue(1000000)); + ledger.set_public_balance(AccountId(i as u64), AssetId(1), AssetValue(1000000)); + ledger.set_public_balance(AccountId(i as u64), AssetId(2), AssetValue(1000000)); + } - let mut ledger = Ledger::new(utxo_set_model.clone(), verifying_context); + let ledger = Arc::new(RwLock::new(ledger)); - for i in 0..ACTOR_COUNT { - ledger.set_public_balance(AccountId(i as u64), AssetId(0), AssetValue(1000000)); - ledger.set_public_balance(AccountId(i as u64), AssetId(1), AssetValue(1000000)); - ledger.set_public_balance(AccountId(i as u64), AssetId(2), AssetValue(1000000)); - } + println!("[INFO] Building {:?} Wallets", actor_count); - let ledger = Arc::new(RwLock::new(ledger)); - - println!("[INFO] Building {:?} Wallets", ACTOR_COUNT); - - let actors = (0..ACTOR_COUNT) - .map(|i| { - Actor::new( - sample_wallet( - AccountId(i as u64), - &ledger, - &cache, - &parameters, - &utxo_set_model, - &mut rng, - ), - Default::default(), - ACTOR_LIFETIME, - ) - }) - .collect::<Vec<_>>(); + let actors = (0..actor_count) + .map(|i| { + Actor::new( + sample_wallet( + AccountId(i as u64), + &ledger, + &cache, + &parameters, + &utxo_set_model, + &mut rng, + ), + Default::default(), + actor_lifetime, + ) + }) + .collect::<Vec<_>>(); - let mut simulator = Simulator::new(ActionSim(Simulation::default()), actors); + let mut simulator = Simulator::new(ActionSim(Simulation::default()), actors); - println!("[INFO] Starting Simulation\n"); + println!("[INFO] Starting Simulation\n"); - rayon::in_place_scope(|scope| { - for event in simulator.run(move || ChaCha20Rng::from_rng(&mut rng).unwrap(), scope) { - match event.event.action { - ActionType::Skip | ActionType::GeneratePublicKey => {} - _ => println!("{:?}", event), - } - if let Err(err) = event.event.result { - println!("\n[ERROR] Simulation Error: {:?}\n", err); - break; - } + rayon::in_place_scope(|scope| { + for event in simulator.run(move || ChaCha20Rng::from_rng(&mut rng).unwrap(), scope) { + match event.event.action { + ActionType::Skip | ActionType::GeneratePublicKey => {} + _ => println!("{:?}", event), + } + if let Err(err) = event.event.result { + println!("\n[ERROR] Simulation Error: {:?}\n", err); + break; } - }); + } + }); + + let balances = simulator + .actors + .iter() + .map(|actor| { + ( + actor.wallet.ledger().public_balances(), + actor.wallet.assets(), + ) + }) + .collect::<Vec<_>>(); - directory - .close() - .expect("Unable to delete temporary test directory."); - } + println!("BALANCES: {:#?}", balances); } diff --git a/manta-pay/src/test/mod.rs b/manta-pay/src/test/mod.rs index 6a5da2d47..625bef8d6 100644 --- a/manta-pay/src/test/mod.rs +++ b/manta-pay/src/test/mod.rs @@ -16,8 +16,6 @@ //! Manta Pay Testing -pub mod ledger; - // TODO: This is the old simulation. We need to integrate its features into the new asynchronous // simulation. // diff --git a/manta-pay/src/test/transfer.rs b/manta-pay/src/test/transfer.rs index df35b673c..0e6592274 100644 --- a/manta-pay/src/test/transfer.rs +++ b/manta-pay/src/test/transfer.rs @@ -22,16 +22,16 @@ use crate::config::{ use manta_crypto::{ constraint::{measure::Measure, ProofSystem as _}, merkle_tree, - rand::Rand, + rand::{FromEntropy, Rand}, }; -use rand::thread_rng; +use rand_chacha::ChaCha20Rng; type UtxoSet = merkle_tree::full::FullMerkleTree<MerkleTreeConfiguration>; /// Tests the generation of proving/verifying contexts for [`Mint`]. #[test] fn sample_mint_context() { - let mut rng = thread_rng(); + let mut rng = ChaCha20Rng::from_entropy(); let cs = Mint::unknown_constraints(FullParameters::new(&rng.gen(), &rng.gen())); println!("Mint: {:?}", cs.measure()); ProofSystem::generate_context(&(), cs, &mut rng).unwrap(); @@ -40,7 +40,7 @@ fn sample_mint_context() { /// Tests the generation of proving/verifying contexts for [`PrivateTransfer`]. #[test] fn sample_private_transfer_context() { - let mut rng = thread_rng(); + let mut rng = ChaCha20Rng::from_entropy(); let cs = PrivateTransfer::unknown_constraints(FullParameters::new(&rng.gen(), &rng.gen())); println!("PrivateTransfer: {:?}", cs.measure()); ProofSystem::generate_context(&(), cs, &mut rng).unwrap(); @@ -49,7 +49,7 @@ fn sample_private_transfer_context() { /// Tests the generation of proving/verifying contexts for [`Reclaim`]. #[test] fn sample_reclaim_context() { - let mut rng = thread_rng(); + let mut rng = ChaCha20Rng::from_entropy(); let cs = Reclaim::unknown_constraints(FullParameters::new(&rng.gen(), &rng.gen())); println!("Reclaim: {:?}", cs.measure()); ProofSystem::generate_context(&(), cs, &mut rng).unwrap(); @@ -58,7 +58,7 @@ fn sample_reclaim_context() { /// Tests the generation of a [`Mint`]. #[test] fn mint() { - let mut rng = thread_rng(); + let mut rng = ChaCha20Rng::from_entropy(); assert!( Mint::sample_and_check_proof(&(), &rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng) .expect("Random Mint should have successfully produced a proof."), @@ -69,7 +69,7 @@ fn mint() { /// Tests the generation of a [`PrivateTransfer`]. #[test] fn private_transfer() { - let mut rng = thread_rng(); + let mut rng = ChaCha20Rng::from_entropy(); assert!( PrivateTransfer::sample_and_check_proof( &(), @@ -85,7 +85,7 @@ fn private_transfer() { /// Tests the generation of a [`Reclaim`]. #[test] fn reclaim() { - let mut rng = thread_rng(); + let mut rng = ChaCha20Rng::from_entropy(); assert!( Reclaim::sample_and_check_proof(&(), &rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng) .expect("Random Reclaim should have successfully produced a proof."), diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index 07a528deb..640e65dd9 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -49,7 +49,7 @@ pub use serde; // TODO: add `where` clauses #[macro_export] macro_rules! from_variant_impl { - ($to:tt, $kind:ident, $from:tt) => { + ($to:ty, $kind:ident, $from:ty) => { impl From<$from> for $to { #[inline] fn from(t: $from) -> Self { diff --git a/manta-util/src/pointer.rs b/manta-util/src/pointer.rs index 5870fcbbd..b88d5cb0d 100644 --- a/manta-util/src/pointer.rs +++ b/manta-util/src/pointer.rs @@ -59,7 +59,7 @@ pub trait PointerFamily<T> { /// Implements [`PointerFamily`] for `$type` with `$strong` and `$weak` pointers. macro_rules! impl_pointer_family { - ($type:tt, $strong:ident, $weak:ident) => { + ($type:ty, $strong:ident, $weak:ident) => { #[cfg(feature = "alloc")] #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] impl<T> PointerFamily<T> for $type { From e1cdb00540d7909f16728bb7841cdfe7ea5c4397 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 10 Feb 2022 16:39:42 -0500 Subject: [PATCH 238/275] feat: add scale-info implementations for crypto types --- manta-pay/Cargo.toml | 8 +++++--- .../src/crypto/constraint/arkworks/codec.rs | 11 ++++++---- .../src/crypto/constraint/arkworks/groth16.rs | 20 +++++++++++++++++++ .../src/crypto/constraint/arkworks/mod.rs | 20 +++++++++++++++++++ manta-pay/src/crypto/ecc/arkworks.rs | 20 +++++++++++++++++++ manta-util/src/array.rs | 4 ++-- 6 files changed, 74 insertions(+), 9 deletions(-) diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 116616328..2d6fb4192 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -58,6 +58,9 @@ http = ["reqwest"] # TODO: Enable PLONK ZKP System # TODO: plonk = ["zk-garage-plonk"] +# SCALE Codec and Type Info +scale = ["scale-codec", "scale-info"] + # Serde serde = ["manta-accounting/serde", "manta-crypto/serde"] @@ -103,13 +106,12 @@ manta-accounting = { path = "../manta-accounting", default-features = false } manta-crypto = { path = "../manta-crypto", default-features = false } manta-util = { path = "../manta-util", default-features = false } parking_lot = { version = "0.12.0", optional = true, default-features = false } -# TODO: rand = { version = "0.8.4", optional = true } rand_chacha = { version = "0.3.1", default-features = false } rayon = { version = "1.5.1", optional = true, default-features = false } reqwest = { version = "0.11.9", optional = true, default-features = false, features = ["blocking", "json"] } -scale-codec = { package = "parity-scale-codec", version = "2.3.1", default-features = false, features = ["derive"] } +scale-codec = { package = "parity-scale-codec", version = "2.3.1", optional = true, default-features = false, features = ["derive"] } +scale-info = { version = "1.0.0", optional = true, default-features = false, features = ["derive"] } serde_json = { version = "1.0.78", optional = true, default-features = false } -# TODO: statrs = { version = "0.15.0", optional = true, default-features = false } tempfile = { version = "3.2.0", optional = true, default-features = false } tungstenite = { version = "0.16.0", optional = true, default-features = false, features = ["native-tls"] } # TODO: zk-garage-plonk = { package = "plonk", git = "https://github.com/zk-garage/plonk", optional = true, default-features = false } diff --git a/manta-pay/src/crypto/constraint/arkworks/codec.rs b/manta-pay/src/crypto/constraint/arkworks/codec.rs index 7ae02456a..6f1668490 100644 --- a/manta-pay/src/crypto/constraint/arkworks/codec.rs +++ b/manta-pay/src/crypto/constraint/arkworks/codec.rs @@ -18,19 +18,22 @@ use ark_std::io::{self, Error, ErrorKind}; use manta_util::codec::{Read, ReadExactError, Write}; -use scale_codec::Input; pub use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; /// Scale-Codec Input as Reader Wrapper +#[cfg(feature = "scale")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] #[derive(Debug, Eq, Hash, PartialEq)] pub struct ScaleCodecReader<'i, I>(pub &'i mut I) where - I: Input; + I: scale_codec::Input; +#[cfg(feature = "scale")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] impl<I> io::Read for ScaleCodecReader<'_, I> where - I: Input, + I: scale_codec::Input, { #[inline] fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> { @@ -40,7 +43,7 @@ where #[inline] fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Error> { - Input::read(self.0, buf).map_err(|_| ErrorKind::Other.into()) + scale_codec::Input::read(self.0, buf).map_err(|_| ErrorKind::Other.into()) } } diff --git a/manta-pay/src/crypto/constraint/arkworks/groth16.rs b/manta-pay/src/crypto/constraint/arkworks/groth16.rs index 301c7dcf1..dba9a5c4c 100644 --- a/manta-pay/src/crypto/constraint/arkworks/groth16.rs +++ b/manta-pay/src/crypto/constraint/arkworks/groth16.rs @@ -69,6 +69,8 @@ pub struct Proof<E>( where E: PairingEngine; +#[cfg(feature = "scale")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] impl<E> scale_codec::Decode for Proof<E> where E: PairingEngine, @@ -85,6 +87,8 @@ where } } +#[cfg(feature = "scale")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] impl<E> scale_codec::Encode for Proof<E> where E: PairingEngine, @@ -98,8 +102,24 @@ where } } +#[cfg(feature = "scale")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] impl<E> scale_codec::EncodeLike for Proof<E> where E: PairingEngine {} +#[cfg(feature = "scale")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] +impl<E> scale_info::TypeInfo for Proof<E> +where + E: PairingEngine, +{ + type Identity = [u8]; + + #[inline] + fn type_info() -> scale_info::Type { + Self::Identity::type_info() + } +} + impl<E> TryFrom<Vec<u8>> for Proof<E> where E: PairingEngine, diff --git a/manta-pay/src/crypto/constraint/arkworks/mod.rs b/manta-pay/src/crypto/constraint/arkworks/mod.rs index 7e340467d..92ba6e12e 100644 --- a/manta-pay/src/crypto/constraint/arkworks/mod.rs +++ b/manta-pay/src/crypto/constraint/arkworks/mod.rs @@ -105,6 +105,8 @@ where } } +#[cfg(feature = "scale")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] impl<F> scale_codec::Decode for Fp<F> where F: PrimeField, @@ -121,6 +123,8 @@ where } } +#[cfg(feature = "scale")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] impl<F> scale_codec::Encode for Fp<F> where F: PrimeField, @@ -134,8 +138,24 @@ where } } +#[cfg(feature = "scale")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] impl<F> scale_codec::EncodeLike for Fp<F> where F: PrimeField {} +#[cfg(feature = "scale")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] +impl<F> scale_info::TypeInfo for Fp<F> +where + F: PrimeField, +{ + type Identity = [u8]; + + #[inline] + fn type_info() -> scale_info::Type { + Self::Identity::type_info() + } +} + impl<F> Sample for Fp<F> where F: PrimeField, diff --git a/manta-pay/src/crypto/ecc/arkworks.rs b/manta-pay/src/crypto/ecc/arkworks.rs index 205c0335a..bbe6eacc7 100644 --- a/manta-pay/src/crypto/ecc/arkworks.rs +++ b/manta-pay/src/crypto/ecc/arkworks.rs @@ -155,6 +155,8 @@ where } } +#[cfg(feature = "scale")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] impl<C> scale_codec::Decode for Group<C> where C: ProjectiveCurve, @@ -171,6 +173,8 @@ where } } +#[cfg(feature = "scale")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] impl<C> scale_codec::Encode for Group<C> where C: ProjectiveCurve, @@ -184,8 +188,24 @@ where } } +#[cfg(feature = "scale")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] impl<C> scale_codec::EncodeLike for Group<C> where C: ProjectiveCurve {} +#[cfg(feature = "scale")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] +impl<C> scale_info::TypeInfo for Group<C> +where + C: ProjectiveCurve, +{ + type Identity = [u8]; + + #[inline] + fn type_info() -> scale_info::Type { + Self::Identity::type_info() + } +} + impl<C> kdf::AsBytes for Group<C> where C: ProjectiveCurve, diff --git a/manta-util/src/array.rs b/manta-util/src/array.rs index 899bfae10..d30633bf7 100644 --- a/manta-util/src/array.rs +++ b/manta-util/src/array.rs @@ -198,7 +198,7 @@ macro_rules! impl_array_traits { /// Array /// /// This type wraps a standard Rust array but provides some additional methods and optional -/// compatibility with [`serde`](crate::serde). The type `Array<T, N>` is mostly a drop-in +/// compatibility with [`serde`](https://docs.rs/serde). The type `Array<T, N>` is mostly a drop-in /// replacement for `[T; N]`. #[cfg_attr( feature = "serde-array", @@ -300,7 +300,7 @@ impl<T, const N: usize> From<Array<T, N>> for [T; N] { /// Boxed Array /// /// This type wraps a boxed standard Rust array but provides some additional methods and optional -/// compatibility with [`serde`](crate::serde). The type `BoxArray<T, N>` is mostly a drop-in +/// compatibility with [`serde`](https://docs.rs/serde). The type `BoxArray<T, N>` is mostly a drop-in /// replacement for `Box<[T; N]>`. #[cfg(feature = "alloc")] #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] From 3ab3c6419beb5b94412c548842327a1324a0e361 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sat, 12 Feb 2022 15:33:05 -0700 Subject: [PATCH 239/275] fix: bounds check for RunIter when all channels are disconnected --- manta-accounting/src/wallet/test/sim.rs | 92 ++++++++++++++++++------- manta-pay/Cargo.toml | 2 +- manta-pay/src/bin/simulation.rs | 2 +- manta-pay/src/simulation/mod.rs | 44 ++++++++---- 4 files changed, 103 insertions(+), 37 deletions(-) diff --git a/manta-accounting/src/wallet/test/sim.rs b/manta-accounting/src/wallet/test/sim.rs index a518f0e85..3e5b91abf 100644 --- a/manta-accounting/src/wallet/test/sim.rs +++ b/manta-accounting/src/wallet/test/sim.rs @@ -22,7 +22,7 @@ use manta_crypto::rand::{CryptoRng, RngCore}; #[cfg(feature = "parallel")] use { - crossbeam::channel::{self, Receiver, Select}, + crossbeam::channel::{self, Receiver, Select, Sender}, rayon::Scope, }; @@ -213,6 +213,53 @@ where } } +/// Run Iterator Task +#[cfg(feature = "parallel")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "parallel")))] +struct RunTask<'s, S, R> +where + S: Simulation, + R: 's + CryptoRng + RngCore, +{ + /// Underlying Actor Iterator + iter: ActorIter<'s, S, R>, + + /// Event Sender + sender: Sender<Event<S>>, + + /// Task Queue Sender + queue: Sender<Self>, +} + +#[cfg(feature = "parallel")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "parallel")))] +impl<'s, S, R> RunTask<'s, S, R> +where + S: Simulation, + R: 's + CryptoRng + RngCore, +{ + /// Builds a new [`RunTask`] from `iter`, `sender`, and `queue`. + #[inline] + pub fn new(iter: ActorIter<'s, S, R>, sender: Sender<Event<S>>, queue: &Sender<Self>) -> Self { + Self { + iter, + sender, + queue: queue.clone(), + } + } + + /// Sends the next element in the iterator to its receiver, and enqueue `self` onto the task + /// queue if sending was successful. + #[inline] + pub fn send_next(mut self) { + if let Some(next) = self.iter.next() { + if self.sender.send(next).is_ok() { + let _ = self.queue.clone().send(self); + } + } + } +} + /// Simulation Run Iterator #[cfg(feature = "parallel")] #[cfg_attr(doc_cfg, doc(cfg(feature = "parallel")))] @@ -240,30 +287,18 @@ where R: 's + CryptoRng + RngCore + Send, { let len = iterators.len(); - let mut senders = Vec::with_capacity(len); + let (queue, listener) = channel::bounded(len); let mut receivers = Vec::with_capacity(len); - for _ in 0..len { + for iter in iterators { let (sender, receiver) = channel::unbounded(); - senders.push(sender); + queue + .send(RunTask::new(iter, sender, &queue)) + .expect("This send is guaranteed because we have access to the receiver."); receivers.push(receiver); } - let (task_sender, task_receiver) = channel::unbounded(); - let mut tasks = iterators.into_iter().zip(senders).collect::<Vec<_>>(); - scope.spawn(move |scope| loop { - for (mut iter, sender) in tasks.drain(..) { - let sender = sender.clone(); - let task_sender = task_sender.clone(); - scope.spawn(move |_| { - if let Some(next) = iter.next() { - let _ = sender.send(next); - let _ = task_sender.send((iter, sender)); - } - }); - } - if let Ok(task) = task_receiver.recv() { - tasks.push(task); - } else { - break; + scope.spawn(move |scope| { + while let Ok(task) = listener.recv() { + scope.spawn(|_| task.send_next()); } }); Self { receivers } @@ -280,7 +315,11 @@ where #[inline] fn next(&mut self) -> Option<Self::Item> { - let mut drop_indices = Vec::<usize>::with_capacity(self.receivers.len()); + let len = self.receivers.len(); + if len == 0 { + return None; + } + let mut drop_indices = Vec::<usize>::with_capacity(len); let mut select = Select::new(); for receiver in &self.receivers { select.recv(receiver); @@ -296,7 +335,14 @@ where } return Some(event); } - Err(e) if e.is_disconnected() => drop_indices.push(index), + Err(e) if e.is_disconnected() => { + drop_indices.push(index); + select.remove(index); + if drop_indices.len() == len { + self.receivers.clear(); + return None; + } + } _ => {} } } diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 2d6fb4192..c0a5e7782 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -111,7 +111,7 @@ rayon = { version = "1.5.1", optional = true, default-features = false } reqwest = { version = "0.11.9", optional = true, default-features = false, features = ["blocking", "json"] } scale-codec = { package = "parity-scale-codec", version = "2.3.1", optional = true, default-features = false, features = ["derive"] } scale-info = { version = "1.0.0", optional = true, default-features = false, features = ["derive"] } -serde_json = { version = "1.0.78", optional = true, default-features = false } +serde_json = { version = "1.0.79", optional = true, default-features = false } tempfile = { version = "3.2.0", optional = true, default-features = false } tungstenite = { version = "0.16.0", optional = true, default-features = false, features = ["native-tls"] } # TODO: zk-garage-plonk = { package = "plonk", git = "https://github.com/zk-garage/plonk", optional = true, default-features = false } diff --git a/manta-pay/src/bin/simulation.rs b/manta-pay/src/bin/simulation.rs index b9bcf66ab..5b10dc343 100644 --- a/manta-pay/src/bin/simulation.rs +++ b/manta-pay/src/bin/simulation.rs @@ -23,7 +23,7 @@ use std::io; pub fn main() -> io::Result<()> { let directory = tempfile::tempdir()?; println!("[INFO] Temporary Directory: {:?}", directory); - manta_pay::simulation::simulate(10, 10, directory.path()); + manta_pay::simulation::simulate(10, 100, directory.path()); directory.close()?; Ok(()) } diff --git a/manta-pay/src/simulation/mod.rs b/manta-pay/src/simulation/mod.rs index 4c1edc65b..ee07acad9 100644 --- a/manta-pay/src/simulation/mod.rs +++ b/manta-pay/src/simulation/mod.rs @@ -46,7 +46,7 @@ use manta_accounting::{ sim::{ActionSim, Simulator}, ActionType, Actor, PublicBalanceOracle, Simulation, }, - Wallet, + BalanceState, Wallet, }, }; use manta_crypto::{ @@ -508,6 +508,26 @@ where ) } +/// Measures the public and secret balances for each wallet, summing them all together. +#[inline] +fn measure_balances<'w, I>(wallets: I) -> AssetList +where + I: IntoIterator<Item = &'w mut Wallet<Config, LedgerConnection, Signer>>, +{ + let mut balances = AssetList::new(); + for wallet in wallets { + wallet.sync().expect("Failed to synchronize wallet."); + balances.deposit_all(wallet.ledger().public_balances().unwrap()); + balances.deposit_all( + wallet + .assets() + .iter() + .map(|(id, value)| Asset::new(*id, *value)), + ); + } + balances +} + /// Runs a simple simulation to test that the signer-wallet-ledger connection works. #[inline] pub fn simulate<P>(actor_count: usize, actor_lifetime: usize, directory: P) @@ -561,6 +581,9 @@ where let mut simulator = Simulator::new(ActionSim(Simulation::default()), actors); + let initial_balances = + measure_balances(simulator.actors.iter_mut().map(|actor| &mut actor.wallet)); + println!("[INFO] Starting Simulation\n"); rayon::in_place_scope(|scope| { @@ -576,16 +599,13 @@ where } }); - let balances = simulator - .actors - .iter() - .map(|actor| { - ( - actor.wallet.ledger().public_balances(), - actor.wallet.assets(), - ) - }) - .collect::<Vec<_>>(); + println!("\n[INFO] Simulation Ended"); + + let final_balances = + measure_balances(simulator.actors.iter_mut().map(|actor| &mut actor.wallet)); - println!("BALANCES: {:#?}", balances); + assert_eq!( + initial_balances, final_balances, + "Simulation balance mismatch." + ); } From 39e9cc4cbb9fedbd103e8d8aa884b528f0fd2ebc Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sat, 12 Feb 2022 17:27:21 -0700 Subject: [PATCH 240/275] fix: remove proving context caching for signer --- manta-pay/src/bin/simulation.rs | 11 ++++------- manta-pay/src/signer/base.rs | 7 ++++--- manta-pay/src/simulation/mod.rs | 25 +++++++------------------ manta-util/src/cache.rs | 8 ++++---- 4 files changed, 19 insertions(+), 32 deletions(-) diff --git a/manta-pay/src/bin/simulation.rs b/manta-pay/src/bin/simulation.rs index 5b10dc343..b7f92e85d 100644 --- a/manta-pay/src/bin/simulation.rs +++ b/manta-pay/src/bin/simulation.rs @@ -16,14 +16,11 @@ //! Manta Pay Simulation -use std::io; +// TODO: Add CLI interface and configuration for simulation parameters. See the old simulation code +// `test/simulation/mod.rs` for more information. /// Runs the Manta Pay simulation. #[inline] -pub fn main() -> io::Result<()> { - let directory = tempfile::tempdir()?; - println!("[INFO] Temporary Directory: {:?}", directory); - manta_pay::simulation::simulate(10, 100, directory.path()); - directory.close()?; - Ok(()) +pub fn main() { + manta_pay::simulation::simulate(10, 100); } diff --git a/manta-pay/src/signer/base.rs b/manta-pay/src/signer/base.rs index ef2751210..51ce1f346 100644 --- a/manta-pay/src/signer/base.rs +++ b/manta-pay/src/signer/base.rs @@ -17,7 +17,7 @@ //! Manta Pay Signer Configuration use crate::{ - config::{Bls12_381_Edwards, Config, MerkleTreeConfiguration, SecretKey}, + config::{Bls12_381_Edwards, Config, MerkleTreeConfiguration, MultiProvingContext, SecretKey}, crypto::constraint::arkworks::Fp, key::TestnetKeySecret, }; @@ -63,7 +63,8 @@ pub type UtxoSet = merkle_tree::forest::TreeArrayMerkleForest< /// Proving Context Cache pub mod cache { - use crate::config::{MultiProvingContext, ProvingContext}; + use super::*; + use crate::config::ProvingContext; use core::marker::PhantomData; use manta_util::{ cache::CachedResource, @@ -215,7 +216,7 @@ impl manta_accounting::wallet::signer::Configuration for Config { key::Map<TestnetKeySecret, HierarchicalKeyDerivationFunction>; type UtxoSet = UtxoSet; type AssetMap = HashAssetMap<AssetMapKey<Self>>; - type ProvingContextCache = cache::OnDiskMultiProvingContext; + type ProvingContextCache = MultiProvingContext; type Rng = rand_chacha::ChaCha20Rng; } diff --git a/manta-pay/src/simulation/mod.rs b/manta-pay/src/simulation/mod.rs index ee07acad9..d447b0d61 100644 --- a/manta-pay/src/simulation/mod.rs +++ b/manta-pay/src/simulation/mod.rs @@ -22,10 +22,10 @@ use crate::{ config::{ - Config, EncryptedNote, FullParameters, MerkleTreeConfiguration, MultiVerifyingContext, - ProofSystem, TransferPost, Utxo, UtxoSetModel, VoidNumber, + Config, EncryptedNote, FullParameters, MerkleTreeConfiguration, MultiProvingContext, + MultiVerifyingContext, ProofSystem, TransferPost, Utxo, UtxoSetModel, VoidNumber, }, - signer::base::{cache::OnDiskMultiProvingContext, Signer, UtxoSet}, + signer::base::{Signer, UtxoSet}, }; use alloc::{sync::Arc, vec::Vec}; use core::convert::Infallible; @@ -61,10 +61,7 @@ use manta_crypto::{ use manta_util::into_array_unchecked; use parking_lot::RwLock; use rand_chacha::ChaCha20Rng; -use std::{ - collections::{HashMap, HashSet}, - path::Path, -}; +use std::collections::{HashMap, HashSet}; /// Merkle Forest Index pub type MerkleForestIndex = <MerkleTreeConfiguration as Configuration>::Index; @@ -488,7 +485,7 @@ impl PublicBalanceOracle for LedgerConnection { pub fn sample_wallet<R>( account: AccountId, ledger: &SharedLedger, - cache: &OnDiskMultiProvingContext, + cache: &MultiProvingContext, parameters: &transfer::Parameters<Config>, utxo_set_model: &UtxoSetModel, rng: &mut R, @@ -530,10 +527,7 @@ where /// Runs a simple simulation to test that the signer-wallet-ledger connection works. #[inline] -pub fn simulate<P>(actor_count: usize, actor_lifetime: usize, directory: P) -where - P: AsRef<Path>, -{ +pub fn simulate(actor_count: usize, actor_lifetime: usize) { let mut rng = ChaCha20Rng::from_entropy(); let parameters = rng.gen(); let utxo_set_model = rng.gen(); @@ -545,11 +539,6 @@ where ) .expect("Failed to generate contexts."); - let cache = OnDiskMultiProvingContext::new(directory); - cache - .save(proving_context) - .expect("Unable to save proving context to disk."); - let mut ledger = Ledger::new(utxo_set_model.clone(), verifying_context); for i in 0..actor_count { @@ -568,7 +557,7 @@ where sample_wallet( AccountId(i as u64), &ledger, - &cache, + &proving_context, &parameters, &utxo_set_model, &mut rng, diff --git a/manta-util/src/cache.rs b/manta-util/src/cache.rs index 093979b2e..a724b2a36 100644 --- a/manta-util/src/cache.rs +++ b/manta-util/src/cache.rs @@ -16,7 +16,7 @@ //! Caching Utilities -use core::{convert::Infallible, ops::Deref}; +use core::{borrow::Borrow, convert::Infallible}; /// Cached Resource pub trait CachedResource<T> { @@ -52,9 +52,9 @@ pub trait CachedResource<T> { /// Cached Resource Error Type pub type CachedResourceError<T, R> = <R as CachedResource<T>>::Error; -impl<T, D> CachedResource<T> for D +impl<T, B> CachedResource<T> for B where - D: Deref<Target = T>, + B: Borrow<T>, { type ReadingKey = (); type Error = Infallible; @@ -67,7 +67,7 @@ where #[inline] fn read(&self, reading_key: Self::ReadingKey) -> &T { let _ = reading_key; - self + self.borrow() } #[inline] From 194431a6ad470ed6430cdaaef1579890334f5ed2 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 13 Feb 2022 13:28:13 -0700 Subject: [PATCH 241/275] feat: add some rudimentary measurement infrastructure --- manta-crypto/src/constraint.rs | 172 ++++++++++++++++++++++++++++++++- manta-pay/Cargo.toml | 4 + manta-pay/src/bin/measure.rs | 60 ++++++++++++ manta-pay/src/config.rs | 1 - 4 files changed, 231 insertions(+), 6 deletions(-) create mode 100644 manta-pay/src/bin/measure.rs diff --git a/manta-crypto/src/constraint.rs b/manta-crypto/src/constraint.rs index 5d32240ce..f92f4928c 100644 --- a/manta-crypto/src/constraint.rs +++ b/manta-crypto/src/constraint.rs @@ -485,6 +485,11 @@ where /// Constraint System Measurement pub mod measure { use super::*; + use alloc::{format, string::String, vec::Vec}; + use core::{ + fmt::Display, + ops::{Add, AddAssign, Deref, DerefMut}, + }; #[cfg(feature = "serde")] use manta_util::serde::{Deserialize, Serialize}; @@ -512,26 +517,50 @@ pub mod measure { None } - /// Returns a [`SizeReport`] with the number of constraints and variables of each kind. + /// Returns a [`Size`] with the number of constraints and variables of each kind. #[inline] - fn measure(&self) -> SizeReport { - SizeReport { + fn measure(&self) -> Size { + Size { constraint_count: self.constraint_count(), constant_count: self.constant_count(), public_variable_count: self.public_variable_count(), secret_variable_count: self.secret_variable_count(), } } + + /// Performs a measurement after running `f` on `self`, adding the result to `measurement`. + #[inline] + fn after<T, F>(&mut self, measurement: &mut Size, f: F) -> T + where + F: FnOnce(&mut Self) -> T, + { + let value = f(self); + *measurement += self.measure(); + value + } + + /// Performs a measurement after running `f` on `self`, ignoring the resulting value, + /// returning the measurement only. + #[inline] + fn after_ignore<T, F>(&mut self, f: F) -> Size + where + F: FnOnce(&mut Self) -> T, + { + let mut measurement = Default::default(); + self.after(&mut measurement, f); + measurement + } } - /// Constraint System Size Measurement Report + /// Constraint System Size Measurement #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] - pub struct SizeReport { + #[must_use] + pub struct Size { /// Number of Constraints pub constraint_count: usize, @@ -544,4 +573,137 @@ pub mod measure { /// Number of Secret Variables pub secret_variable_count: Option<usize>, } + + impl Size { + /// Computes the difference between `self` and `rhs`. If any of the measurements in `rhs` + /// are greater than those in `self`, this method returns `None`. + #[inline] + pub fn checked_sub(&self, rhs: Self) -> Option<Self> { + Some(Self { + constraint_count: self.constraint_count.checked_sub(rhs.constraint_count)?, + constant_count: match (self.constant_count, rhs.constant_count) { + (Some(lhs), Some(rhs)) => Some(lhs.checked_sub(rhs)?), + (Some(lhs), None) => Some(lhs), + _ => None, + }, + public_variable_count: match (self.public_variable_count, rhs.public_variable_count) + { + (Some(lhs), Some(rhs)) => Some(lhs.checked_sub(rhs)?), + (Some(lhs), None) => Some(lhs), + _ => None, + }, + secret_variable_count: match (self.secret_variable_count, rhs.secret_variable_count) + { + (Some(lhs), Some(rhs)) => Some(lhs.checked_sub(rhs)?), + (Some(lhs), None) => Some(lhs), + _ => None, + }, + }) + } + } + + impl Add for Size { + type Output = Self; + + #[inline] + fn add(mut self, rhs: Self) -> Self::Output { + self += rhs; + self + } + } + + impl AddAssign for Size { + #[inline] + fn add_assign(&mut self, rhs: Self) { + self.constraint_count += rhs.constraint_count; + match (self.constant_count.as_mut(), rhs.constant_count) { + (Some(lhs), Some(rhs)) => *lhs += rhs, + (Some(_), None) => {} + (None, rhs) => self.constant_count = rhs, + } + match ( + self.public_variable_count.as_mut(), + rhs.public_variable_count, + ) { + (Some(lhs), Some(rhs)) => *lhs += rhs, + (Some(_), None) => {} + (None, rhs) => self.public_variable_count = rhs, + } + match ( + self.secret_variable_count.as_mut(), + rhs.secret_variable_count, + ) { + (Some(lhs), Some(rhs)) => *lhs += rhs, + (Some(_), None) => {} + (None, rhs) => self.secret_variable_count = rhs, + } + } + } + + /// Measurement Instrument + pub struct Instrument<'c, COM> + where + COM: Measure, + { + /// Base Compiler + pub base: &'c mut COM, + + /// Measurements + pub measurements: Vec<(String, Size)>, + } + + impl<'c, COM> Instrument<'c, COM> + where + COM: Measure, + { + /// Builds a new [`Instrument`] for `base`. + #[inline] + pub fn new(base: &'c mut COM) -> Self { + Self { + base, + measurements: Default::default(), + } + } + + /// Measures the size of `f` in the base compiler, attaching `label` to the measurement. + #[inline] + pub fn measure<D, T, F>(&mut self, label: D, f: F) -> T + where + D: Display, + F: FnOnce(&mut COM) -> T, + { + let before = self.base.measure(); + let value = f(self.base); + self.measurements.push(( + format!("{}", label), + self.base + .measure() + .checked_sub(before) + .expect("Measurements should increase when adding more constraints."), + )); + value + } + } + + impl<'c, COM> Deref for Instrument<'c, COM> + where + COM: Measure, + { + type Target = COM; + + #[inline] + fn deref(&self) -> &Self::Target { + self.base + } + } + + impl<'c, COM> DerefMut for Instrument<'c, COM> + where + COM: Measure, + { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + self.base + } + } } diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index c0a5e7782..ed5d49c1e 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -32,6 +32,10 @@ harness = false name = "generate_parameters" required-features = ["groth16", "manta-util/std", "test"] +[[bin]] +name = "measure" +required-features = ["groth16"] + [[bin]] name = "simulation" required-features = ["groth16", "simulation", "tempfile"] diff --git a/manta-pay/src/bin/measure.rs b/manta-pay/src/bin/measure.rs new file mode 100644 index 000000000..646ffe420 --- /dev/null +++ b/manta-pay/src/bin/measure.rs @@ -0,0 +1,60 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Manta Pay Circuit Measurements + +use manta_crypto::{ + constraint::{measure::Instrument, Allocator, Secret, ValueSource}, + hash::HashFunction, + key::KeyAgreementScheme as _, + rand::{Sample, SeedableRng}, +}; +use manta_pay::config::{ + Compiler, KeyAgreementScheme, KeyAgreementSchemeVar, Poseidon2, Poseidon2Var, +}; +use rand_chacha::ChaCha20Rng; + +/// Runs some basic measurements of the circuit component sizes. +#[inline] +pub fn main() { + let mut rng = ChaCha20Rng::from_entropy(); + let mut compiler = Compiler::for_unknown(); + + let mut instrument = Instrument::new(&mut compiler); + + let hasher = Poseidon2::gen(&mut rng).as_constant::<Poseidon2Var>(&mut instrument); + let poseidon_lhs = instrument.base.allocate_unknown::<Secret, _>(); + let poseidon_rhs = instrument.base.allocate_unknown::<Secret, _>(); + + let _ = instrument.measure("Poseidon ARITY-2", |compiler| { + hasher.hash_in([&poseidon_lhs, &poseidon_rhs], compiler) + }); + + let key_agreement = + KeyAgreementScheme::gen(&mut rng).as_constant::<KeyAgreementSchemeVar>(&mut instrument); + let secret_key_0 = instrument.base.allocate_unknown::<Secret, _>(); + let secret_key_1 = instrument.base.allocate_unknown::<Secret, _>(); + + let public_key_0 = instrument.measure("DHKE `derive`", |compiler| { + key_agreement.derive_in(&secret_key_0, compiler) + }); + + let _ = instrument.measure("DHKE `agree`", |compiler| { + key_agreement.agree_in(&secret_key_1, &public_key_0, compiler) + }); + + println!("{:#?}", instrument.measurements); +} diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 1052e3c69..880dadea8 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -547,7 +547,6 @@ impl ProofSystemInput<Fp<ConstraintField>> for ProofSystem { impl ProofSystemInput<Group> for ProofSystem { #[inline] fn extend(input: &mut Self::Input, next: &Group) { - // FIXME: Make sure we can type check the coordinate system here. input.append(&mut next.0.to_field_elements().unwrap()); } } From 4c81259a05147e0f117d6c6c28666942141a02d1 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 13 Feb 2022 14:33:55 -0700 Subject: [PATCH 242/275] feat: add testing framework for fs/serde --- manta-accounting/Cargo.toml | 1 + manta-accounting/src/fs/mod.rs | 52 ++++++++++++++++++++------------ manta-accounting/src/fs/serde.rs | 35 +++++++++++++++++++++ 3 files changed, 69 insertions(+), 19 deletions(-) diff --git a/manta-accounting/Cargo.toml b/manta-accounting/Cargo.toml index 942824b44..f8e338090 100644 --- a/manta-accounting/Cargo.toml +++ b/manta-accounting/Cargo.toml @@ -66,3 +66,4 @@ statrs = { version = "0.15.0", optional = true, default-features = false } [dev-dependencies] rand = "0.8.4" + diff --git a/manta-accounting/src/fs/mod.rs b/manta-accounting/src/fs/mod.rs index ff3f22857..3df2a27d6 100644 --- a/manta-accounting/src/fs/mod.rs +++ b/manta-accounting/src/fs/mod.rs @@ -17,7 +17,7 @@ //! Encrypted Filesystem Primitives use alloc::{boxed::Box, vec::Vec}; -use core::{cmp, fmt::Debug, hash::Hash}; +use core::{cmp, hash::Hash, marker::PhantomData}; #[cfg(feature = "serde")] #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] @@ -51,9 +51,13 @@ bitflags::bitflags! { } /// Open Options -#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] #[must_use] -pub struct OpenOptions { +pub struct OpenOptions<F> +where + F: File, +{ /// Read Access read: bool, @@ -71,9 +75,15 @@ pub struct OpenOptions { /// Create New Mode create_new: bool, + + /// Type Parameter Marker + __: PhantomData<F>, } -impl OpenOptions { +impl<F> OpenOptions<F> +where + F: File, +{ /// Builds a new default [`OpenOptions`]. #[inline] pub fn new() -> Self { @@ -163,9 +173,8 @@ impl OpenOptions { /// Opens a file of type `F` at the given `path` using `self` for opening options and `password` /// for encryption. #[inline] - pub fn open<F, P>(&self, path: P, password: &[u8]) -> Result<F, F::Error> + pub fn open<P>(&self, path: P, password: &[u8]) -> Result<F, F::Error> where - F: File, P: AsRef<F::Path>, { F::open(path, password, self) @@ -173,9 +182,12 @@ impl OpenOptions { } #[cfg(feature = "std")] -impl From<OpenOptions> for std::fs::OpenOptions { +impl<F> From<OpenOptions<F>> for std::fs::OpenOptions +where + F: File, +{ #[inline] - fn from(options: OpenOptions) -> Self { + fn from(options: OpenOptions<F>) -> Self { let mut result = Self::new(); result .read(options.read) @@ -252,13 +264,13 @@ impl From<Block> for Vec<u8> { /// Encrypted File pub trait File: Sized { /// Path Type - type Path: ?Sized; + type Path: AsRef<Self::Path> + ?Sized; /// Error Type type Error; /// Opens a new file at `path` with `password` and `options`. - fn open<P>(path: P, password: &[u8], options: &OpenOptions) -> Result<Self, Self::Error> + fn open<P>(path: P, password: &[u8], options: &OpenOptions<Self>) -> Result<Self, Self::Error> where P: AsRef<Self::Path>; @@ -277,7 +289,7 @@ pub trait File: Sized { /// Returns a new [`OpenOptions`] object. #[inline] - fn options() -> OpenOptions { + fn options() -> OpenOptions<Self> { OpenOptions::new() } @@ -296,9 +308,7 @@ pub mod cocoon { use super::{Block, OpenOptions}; use cocoon::{Error as CocoonError, MiniCocoon}; use core::fmt; - use manta_crypto::rand::{Rand, SeedableRng}; use manta_util::from_variant_impl; - use rand_chacha::ChaCha20Rng; use std::{fs, io::Error as IoError, path::Path}; /// Cocoon Loading/Saving Error @@ -338,13 +348,13 @@ pub mod cocoon { impl File { /// Builds a new [`File`] for encrypted data storage with `password`. #[inline] - fn new(path: &Path, password: &[u8], options: OpenOptions) -> Result<Self, IoError> { + fn new(path: &Path, password: &[u8], options: OpenOptions<Self>) -> Result<Self, IoError> { Ok(Self { file: fs::OpenOptions::from(options).open(path)?, - cocoon: MiniCocoon::from_password( - password, - &ChaCha20Rng::from_entropy().gen::<_, [u8; 32]>(), - ), + cocoon: { + // FIXME: Choose a better seed. Should we hash the password for this? + MiniCocoon::from_password(password, &[0; 32]) + }, }) } } @@ -354,7 +364,11 @@ pub mod cocoon { type Error = Error; #[inline] - fn open<P>(path: P, password: &[u8], options: &OpenOptions) -> Result<Self, Self::Error> + fn open<P>( + path: P, + password: &[u8], + options: &OpenOptions<Self>, + ) -> Result<Self, Self::Error> where P: AsRef<Path>, { diff --git a/manta-accounting/src/fs/serde.rs b/manta-accounting/src/fs/serde.rs index cf156e1b4..f0ca2a036 100644 --- a/manta-accounting/src/fs/serde.rs +++ b/manta-accounting/src/fs/serde.rs @@ -1281,3 +1281,38 @@ where self.deserialize_tuple(fields.len(), visitor) } } + +/// Testing Framework +pub mod test { + use super::*; + use manta_util::serde::de::DeserializeOwned; + + /// Asserts that the encryption and decryption of `data` at a new file `path` with `password` + /// succeed without error, and that the decrypted value matches the initial data. + #[inline] + pub fn assert_decryption<F, P, T>(path: P, password: &[u8], data: T) + where + F: File, + F::Error: Debug + Display, + P: AsRef<F::Path>, + T: Debug + DeserializeOwned + PartialEq + Serialize, + { + let path = path.as_ref(); + data.serialize(&mut Serializer::new( + &mut F::options() + .create_new(true) + .write(true) + .open(&path, password) + .expect("Unable to create file for writing."), + )) + .expect("Unable to serialize and encrypt the data."); + let decrypted_data = T::deserialize(&mut Deserializer::new( + &mut F::options() + .read(true) + .open(&path, password) + .expect("Unable to open file for reading."), + )) + .expect("Unable to decrypt and deserialize the data."); + assert_eq!(data, decrypted_data, "Data and decrypted data don't match."); + } +} From b56d0b48c809b9da71cbc04712f36cb83ff2e884 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 13 Feb 2022 14:58:33 -0700 Subject: [PATCH 243/275] feat: hook fs save/load into the signer --- manta-accounting/src/wallet/signer.rs | 74 +++++++++++++++++++-------- 1 file changed, 52 insertions(+), 22 deletions(-) diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index c43ebfd72..31fd821e1 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -21,10 +21,8 @@ // TODO: Should have a mode on the signer where we return a generic error which reveals no detail // about what went wrong during signing. The kind of error returned from a signing could // reveal information about the internal state (privacy leak, not a secrecy leak). -// TODO: Review which subset of the errors are actually recoverable or not. // TODO: Setup multi-account wallets using `crate::key::AccountTable`. // TODO: Move `sync` to a streaming algorithm. -// TODO: Save/Load `SignerState` to/from disk. // TODO: Add self-destruct feature for clearing all secret and private data. // TODO: Compress the `SyncResponse` data before sending (improves privacy and bandwidth). @@ -59,10 +57,21 @@ use manta_util::{ }; #[cfg(feature = "serde")] -use manta_util::serde::{Deserialize, Serialize}; +use manta_util::serde::{de::DeserializeOwned, Deserialize, Serialize}; #[cfg(all(feature = "fs", feature = "serde"))] -use crate::fs::{self, File}; +use { + crate::fs::{ + serde::{Deserializer, Serializer}, + File, + }, + core::fmt::Display, +}; + +#[cfg(all(feature = "fs", feature = "serde"))] +#[cfg_attr(doc_cfg, doc(cfg(all(feature = "fs", feature = "serde"))))] +#[doc(inline)] +pub use crate::fs::serde::{de::Error as LoadError, ser::Error as SaveError}; /// Signer Connection pub trait Connection<C> @@ -423,30 +432,40 @@ impl<C> SignerState<C> where C: Configuration, { - /// Saves the state of `self` to the encrypted `file`. + /// Saves the state of `self` to an encrypted file at `path`. #[cfg(all(feature = "fs", feature = "serde"))] #[inline] - fn save<F>(&self, file: &mut F) -> Result<(), F::Error> + fn save<F, P>(&self, path: P, password: &[u8]) -> Result<(), SaveError<F>> where Self: Serialize, F: File, + F::Error: Debug + Display, + P: AsRef<F::Path>, { - let _ = fs::serde::Serializer::new(file); - // TODO: let _ = self.serialize(serializer); - todo!() + let mut file = F::options() + .create(true) + .truncate(true) + .write(true) + .open(path, password) + .map_err(SaveError::Io)?; + self.serialize(&mut Serializer::new(&mut file)) } - /// Loads an encrypted [`SignerState`] from the encrypted `file`. + /// Loads an encrypted [`SignerState`] from the encrypted file at `path`. #[cfg(all(feature = "fs", feature = "serde"))] #[inline] - fn load<'de, F>(file: &mut F) -> Result<Self, F::Error> + fn load<F, P>(path: P, password: &[u8]) -> Result<Self, LoadError<F>> where - Self: Deserialize<'de>, + Self: DeserializeOwned, F: File, + F::Error: Debug + Display, + P: AsRef<F::Path>, { - let _ = fs::serde::Deserializer::new(file); - // TODO: let _ = Self::deserialize(deserializer); - todo!() + let mut file = F::options() + .read(true) + .open(path, password) + .map_err(LoadError::Io)?; + Self::deserialize(&mut Deserializer::new(&mut file)) } /// Inserts the new `utxo`-`encrypted_note` pair if a known key can decrypt the note and @@ -913,28 +932,39 @@ where ) } - /// Saves the state of `self` to the encrypted `file`. + /// Saves the state of `self` to an encrypted file at `path`. #[cfg(all(feature = "fs", feature = "serde"))] #[cfg_attr(doc_cfg, doc(cfg(all(feature = "fs", feature = "serde"))))] #[inline] - pub fn save<F>(&self, file: &mut F) -> Result<(), F::Error> + pub fn save<F, P>(&self, path: P, password: &[u8]) -> Result<(), SaveError<F>> where SignerState<C>: Serialize, F: File, + F::Error: Debug + Display, + P: AsRef<F::Path>, { - self.state.save(file) + self.state.save(path, password) } - /// Loads an encrypted [`Signer`] state from the encrypted `file`. + /// Loads an encrypted [`Signer`] state from the encrypted file at `path`. #[cfg(all(feature = "fs", feature = "serde"))] #[cfg_attr(doc_cfg, doc(cfg(all(feature = "fs", feature = "serde"))))] #[inline] - pub fn load<'de, F>(parameters: SignerParameters<C>, file: &mut F) -> Result<Self, F::Error> + pub fn load<F, P>( + parameters: SignerParameters<C>, + path: P, + password: &[u8], + ) -> Result<Self, LoadError<F>> where - SignerState<C>: Deserialize<'de>, + SignerState<C>: DeserializeOwned, F: File, + F::Error: Debug + Display, + P: AsRef<F::Path>, { - Ok(Self::from_parts(parameters, SignerState::load(file)?)) + Ok(Self::from_parts( + parameters, + SignerState::load(path, password)?, + )) } /// Updates the internal ledger state, returning the new asset distribution. From 55ab6dc16c1bb4caf0a6206499f43a875bf0c6be Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 13 Feb 2022 16:28:50 -0700 Subject: [PATCH 244/275] feat: add basic recovery mechanism --- manta-accounting/src/key.rs | 42 ++++++++- manta-accounting/src/wallet/ledger.rs | 16 +++- manta-accounting/src/wallet/signer.rs | 29 ++++-- manta-accounting/src/wallet/state.rs | 45 ++++++++-- manta-pay/src/simulation/mod.rs | 1 + manta-util/src/lib.rs | 1 + manta-util/src/ops.rs | 124 ++++++++++++++++++++++++++ 7 files changed, 242 insertions(+), 16 deletions(-) create mode 100644 manta-util/src/ops.rs diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index 7643d8ae5..15ba68c66 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -20,6 +20,7 @@ use alloc::vec::Vec; use core::{ + cmp, fmt::{self, Debug}, hash::Hash, iter, @@ -40,7 +41,7 @@ pub type IndexType = u32; #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde(crate = "manta_util::serde", deny_unknown_fields) + serde(crate = "manta_util::serde", deny_unknown_fields, transparent) )] #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -55,7 +56,7 @@ pub struct HierarchicalKeyDerivationParameter<M> { impl<M> HierarchicalKeyDerivationParameter<M> { /// Builds a new [`HierarchicalKeyDerivationParameter`] from `index`. #[inline] - fn new(index: IndexType) -> Self { + pub const fn new(index: IndexType) -> Self { Self { index, __: PhantomData, @@ -70,7 +71,7 @@ impl<M> HierarchicalKeyDerivationParameter<M> { /// Returns the index of `self`. #[inline] - pub fn index(&self) -> IndexType { + pub const fn index(&self) -> IndexType { self.index } } @@ -390,7 +391,8 @@ where } /// Applies `f` to the view keys generated by `self` returning the first non-`None` result with - /// it's key index and key attached, or returns an error if the key derivation failed. + /// it's key index and key attached, or returns `None` if every application of `f` returned + /// `None`. #[inline] pub fn find_index<T, F>(&self, mut f: F) -> Option<ViewKeySelection<H, T>> where @@ -409,6 +411,38 @@ where index.increment(); } } + + /// Applies `f` to the view keys generated by `self` returning the first non-`None` result with + /// it's key index and key attached, or returns `None` if every application pf `f` returned + /// `None`. + /// + /// # Gap Limit + /// + /// This method, extends the current maximum index by `gap`-many indices while searching + /// and then sets the new maximum to the previous maximum or the located index, whichever is + /// larger. + #[inline] + pub fn find_index_with_gap<T, F>( + &mut self, + gap: KeyIndex, + f: F, + ) -> Option<ViewKeySelection<H, T>> + where + F: FnMut(&H::SecretKey) -> Option<T>, + { + let previous_maximum = self.max_index; + self.max_index.index += gap.index; + match self.find_index(f) { + Some(result) => { + self.max_index = cmp::max(previous_maximum, result.index); + Some(result) + } + _ => { + self.max_index = previous_maximum; + None + } + } + } } /// View Key Selection diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs index a32e01444..def3a99b4 100644 --- a/manta-accounting/src/wallet/ledger.rs +++ b/manta-accounting/src/wallet/ledger.rs @@ -18,7 +18,6 @@ // TODO: Report a more meaningful error on `push` failure. In some way, it must match the // `TransferPostError` variants. -// TODO: Move to a streaming protocol for `Connection::pull`. use crate::transfer::{Configuration, EncryptedNote, TransferPost, Utxo, VoidNumber}; use alloc::vec::Vec; @@ -90,7 +89,17 @@ where C: Configuration, L: Connection<C> + ?Sized, { - /// Current Ledger Checkpoint + /// Pull Continuation Flag + /// + /// The `should_continue` flag is set to `true` if the client should request more data from the + /// ledger to finish the pull. + pub should_continue: bool, + + /// Ledger Checkpoint + /// + /// If the `should_continue` flag is set to `true` then `checkpoint` is the next + /// [`Checkpoint`](Connection::Checkpoint) to request data from the ledger. Otherwise, it + /// represents the current ledger state. pub checkpoint: L::Checkpoint, /// Ledger Receiver Chunk @@ -107,5 +116,8 @@ where #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct PushResponse { /// Transaction Success Flag + /// + /// The `success` flag is set to `true` if the ledger accepted the vector of [`TransferPost`] + /// and the ledger has been updated to the new state. pub success: bool, } diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 31fd821e1..94dfafd51 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -16,8 +16,6 @@ //! Wallet Signer -// FIXME: Add wallet recovery i.e. remove the assumption that a new signer represents a completely -// new derived secret key generator. // TODO: Should have a mode on the signer where we return a generic error which reveals no detail // about what went wrong during signing. The kind of error returned from a signing could // reveal information about the internal state (privacy leak, not a secrecy leak). @@ -57,7 +55,7 @@ use manta_util::{ }; #[cfg(feature = "serde")] -use manta_util::serde::{de::DeserializeOwned, Deserialize, Serialize}; +use manta_util::serde::{Deserialize, Serialize}; #[cfg(all(feature = "fs", feature = "serde"))] use { @@ -66,6 +64,7 @@ use { File, }, core::fmt::Display, + manta_util::serde::de::DeserializeOwned, }; #[cfg(all(feature = "fs", feature = "serde"))] @@ -73,6 +72,9 @@ use { #[doc(inline)] pub use crate::fs::serde::{de::Error as LoadError, ser::Error as SaveError}; +/// Signer Recovery Gap Limit +pub const GAP_LIMIT: KeyIndex = KeyIndex::new(20); + /// Signer Connection pub trait Connection<C> where @@ -137,6 +139,12 @@ pub struct SyncRequest<C> where C: transfer::Configuration, { + /// Recovery Flag + /// + /// If `with_recovery` is set to `true`, the [`GAP_LIMIT`] is used during sync to perform a full + /// recovery. + pub with_recovery: bool, + /// Starting Index pub starting_index: usize, @@ -474,6 +482,7 @@ where fn insert_next_item( &mut self, parameters: &Parameters<C>, + gap_limit: KeyIndex, utxo: Utxo<C>, encrypted_note: EncryptedNote<C>, void_numbers: &mut Vec<VoidNumber<C>>, @@ -491,7 +500,7 @@ where }) = self .accounts .get_default() - .find_index(|k| finder.decrypt(&parameters.key_agreement, k)) + .find_index_with_gap(gap_limit, |k| finder.decrypt(&parameters.key_agreement, k)) { if let Some(void_number) = C::check_full_asset( parameters, @@ -553,6 +562,7 @@ where fn sync_with<I>( &mut self, parameters: &Parameters<C>, + gap_limit: KeyIndex, inserts: I, mut void_numbers: Vec<VoidNumber<C>>, is_partial: bool, @@ -565,6 +575,7 @@ where for (utxo, encrypted_note) in inserts { self.insert_next_item( parameters, + gap_limit, utxo, encrypted_note, &mut void_numbers, @@ -971,6 +982,7 @@ where #[inline] pub fn sync<I, R>( &mut self, + with_recovery: bool, starting_index: usize, inserts: I, removes: R, @@ -990,6 +1002,7 @@ where Some(diff) => { let result = self.state.sync_with( &self.parameters.parameters, + with_recovery.then(|| GAP_LIMIT).unwrap_or_default(), inserts.into_iter().skip(diff), removes.into_iter().collect(), diff == 0, @@ -1087,6 +1100,7 @@ where /// Returns public receiving keys according to the `request`. #[inline] pub fn receiving_keys(&mut self, request: ReceivingKeyRequest) -> Vec<ReceivingKey<C>> { + // FIXME: Enforce that more than `GAP_LIMIT`-many keys cannot be generated. match request { ReceivingKeyRequest::Get { index } => self .state @@ -1125,7 +1139,12 @@ where &mut self, request: SyncRequest<C>, ) -> Result<Result<SyncResponse, SyncError>, Self::Error> { - Ok(self.sync(request.starting_index, request.inserts, request.removes)) + Ok(self.sync( + request.with_recovery, + request.starting_index, + request.inserts, + request.removes, + )) } #[inline] diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index 5f8f9aaa9..be8a17f2f 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -35,6 +35,7 @@ use alloc::{ vec::Vec, }; use core::{fmt::Debug, marker::PhantomData}; +use manta_util::ops::ControlFlow; #[cfg(feature = "serde")] use manta_util::serde::{Deserialize, Serialize}; @@ -291,13 +292,23 @@ where } /// Performs full wallet recovery. + /// + /// # Failure Conditions + /// + /// This method returns an element of type [`Error`] on failure, which can result from any + /// number of synchronization issues between the wallet, the ledger, and the signer. See the + /// [`InconsistencyError`] type for more information on the kinds of errors that can occur and + /// how to resolve them. #[inline] - pub fn recover(&mut self) { - // TODO: Can we just call `sync` with some extra flag? - todo!() + pub fn recover(&mut self) -> Result<(), Error<C, L, S>> { + self.reset(); + while self.sync_with(true)?.is_continue() {} + Ok(()) } - /// Pulls data from the `ledger`, synchronizing the wallet and balance state. + /// Pulls data from the ledger, synchronizing the wallet and balance state. This method loops + /// continuously calling [`sync_partial`](Self::sync_partial) until all the ledger data has + /// arrived at and has been synchronized with the wallet. /// /// # Failure Conditions /// @@ -307,7 +318,30 @@ where /// how to resolve them. #[inline] pub fn sync(&mut self) -> Result<(), Error<C, L, S>> { + while self.sync_partial()?.is_continue() {} + Ok(()) + } + + /// Pulls data from the ledger, synchronizing the wallet and balance state. This method returns + /// a [`ControlFlow`] for matching against to determine if the wallet requires more + /// synchronization. + /// + /// # Failure Conditions + /// + /// This method returns an element of type [`Error`] on failure, which can result from any + /// number of synchronization issues between the wallet, the ledger, and the signer. See the + /// [`InconsistencyError`] type for more information on the kinds of errors that can occur and + /// how to resolve them. + #[inline] + pub fn sync_partial(&mut self) -> Result<ControlFlow, Error<C, L, S>> { + self.sync_with(false) + } + + /// Pulls data from the ledger, synchronizing the wallet and balance state. + #[inline] + fn sync_with(&mut self, with_recovery: bool) -> Result<ControlFlow, Error<C, L, S>> { let PullResponse { + should_continue, checkpoint, receivers, senders, @@ -321,6 +355,7 @@ where match self .signer .sync(SyncRequest { + with_recovery, starting_index: self.checkpoint.receiver_index(), inserts: receivers.into_iter().collect(), removes: senders.into_iter().collect(), @@ -355,7 +390,7 @@ where } } self.checkpoint = checkpoint; - Ok(()) + Ok(ControlFlow::should_continue(should_continue)) } /// Checks if `transaction` can be executed on the balance state of `self`, returning the diff --git a/manta-pay/src/simulation/mod.rs b/manta-pay/src/simulation/mod.rs index d447b0d61..0db77356e 100644 --- a/manta-pay/src/simulation/mod.rs +++ b/manta-pay/src/simulation/mod.rs @@ -424,6 +424,7 @@ impl ledger::Connection<Config> for LedgerConnection { .copied() .collect(); Ok(PullResponse { + should_continue: false, checkpoint: Checkpoint::new( into_array_unchecked( ledger diff --git a/manta-util/src/lib.rs b/manta-util/src/lib.rs index 640e65dd9..9f31253c4 100644 --- a/manta-util/src/lib.rs +++ b/manta-util/src/lib.rs @@ -33,6 +33,7 @@ pub mod codec; pub mod convert; pub mod iter; pub mod num; +pub mod ops; pub mod persistence; pub mod pointer; diff --git a/manta-util/src/ops.rs b/manta-util/src/ops.rs new file mode 100644 index 000000000..4efad41ce --- /dev/null +++ b/manta-util/src/ops.rs @@ -0,0 +1,124 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Operations Utilities + +use core::ops; + +/// Used to tell an operation whether it should exit early or go on as usual. +/// +/// This is an alternative definition and mostly drop-in replacement for [`core::ops::ControlFlow`] +/// but may diverge from the standard library interface over time. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[must_use] +pub enum ControlFlow<B = (), C = ()> { + /// Move on to the next phase of the operation as normal. + Continue(C), + + /// Exit the operation without running subsequent phases. + Break(B), +} + +impl<B, C> ControlFlow<B, C> { + /// Returns `true` if this is a [`Break`](Self::Break) variant. + #[inline] + pub const fn is_break(&self) -> bool { + matches!(self, Self::Break(_)) + } + + /// Returns `true` if this is a [`Continue`](Self::Continue) variant. + #[inline] + pub const fn is_continue(&self) -> bool { + matches!(self, Self::Continue(_)) + } + + /// Converts the [`ControlFlow`] into an [`Option`] which is [`Some`] if `self` is + /// [`Break`](Self::Break) and [`None`] otherwise. + #[inline] + pub fn break_value(self) -> Option<B> { + match self { + Self::Break(b) => Some(b), + _ => None, + } + } + + /// Maps [`ControlFlow<B, C>`] to [`ControlFlow<T, C>`] by applying `f` to the break value when + /// it exists. + #[inline] + pub fn map_break<T, F>(self, f: F) -> ControlFlow<T, C> + where + F: FnOnce(B) -> T, + { + match self { + Self::Continue(c) => ControlFlow::Continue(c), + Self::Break(b) => ControlFlow::Break(f(b)), + } + } +} + +impl<B> ControlFlow<B, ()> { + /// Continue Constant + pub const CONTINUE: Self = Self::Continue(()); +} + +impl<C> ControlFlow<(), C> { + /// Break Constant + pub const BREAK: Self = Self::Break(()); +} + +impl ControlFlow { + /// Returns a [`ControlFlow`] with [`BREAK`](Self::BREAK) if `should_break` is `true` and + /// [`CONTINUE`](Self::CONTINUE) otherwise. + #[inline] + pub fn should_break(should_break: bool) -> Self { + if should_break { + Self::BREAK + } else { + Self::CONTINUE + } + } + + /// Returns a [`ControlFlow`] with [`CONTINUE`](Self::CONTINUE) if `should_continue` is `true` + /// and [`BREAK`](Self::BREAK) otherwise. + #[inline] + pub fn should_continue(should_continue: bool) -> Self { + if should_continue { + Self::CONTINUE + } else { + Self::BREAK + } + } +} + +impl<B, C> From<ops::ControlFlow<B, C>> for ControlFlow<B, C> { + #[inline] + fn from(flow: ops::ControlFlow<B, C>) -> Self { + match flow { + ops::ControlFlow::Continue(c) => Self::Continue(c), + ops::ControlFlow::Break(b) => Self::Break(b), + } + } +} + +impl<B, C> From<ControlFlow<B, C>> for ops::ControlFlow<B, C> { + #[inline] + fn from(flow: ControlFlow<B, C>) -> Self { + match flow { + ControlFlow::Continue(c) => Self::Continue(c), + ControlFlow::Break(b) => Self::Break(b), + } + } +} From 0c2ae73472c9ecde356270f7494705b44dea7ab4 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 13 Feb 2022 17:49:10 -0700 Subject: [PATCH 245/275] feat: add gap-limit enforcement at the key-generation level --- manta-accounting/src/key.rs | 266 +++++++++++++++++++++----- manta-accounting/src/wallet/signer.rs | 23 +-- manta-pay/src/bin/simulation.rs | 2 +- manta-pay/src/key.rs | 6 +- 4 files changed, 238 insertions(+), 59 deletions(-) diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index 15ba68c66..fa3db5ab2 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -122,6 +122,9 @@ pub enum Kind { /// Hierarchical Key Derivation Scheme pub trait HierarchicalKeyDerivationScheme { + /// [`KeyIndex`] Gap Limit + const GAP_LIMIT: IndexType; + /// Secret Key Type type SecretKey; @@ -164,6 +167,8 @@ impl<H> HierarchicalKeyDerivationScheme for &H where H: HierarchicalKeyDerivationScheme + ?Sized, { + const GAP_LIMIT: IndexType = H::GAP_LIMIT; + type SecretKey = H::SecretKey; #[inline] @@ -222,6 +227,8 @@ where H: HierarchicalKeyDerivationScheme, K: KeyDerivationFunction<Key = H::SecretKey>, { + const GAP_LIMIT: IndexType = H::GAP_LIMIT; + type SecretKey = K::Output; #[inline] @@ -295,23 +302,135 @@ where keys: &'h H, /// Account Index - account: AccountIndex, + index: AccountIndex, - /// Maximum Index - max_index: KeyIndex, + /// Account Information + account: Account, } impl<'h, H> AccountKeys<'h, H> where H: HierarchicalKeyDerivationScheme + ?Sized, { - /// Builds a new [`AccountKeys`] from `keys`, `account`, and `max_index`. + /// Builds a new [`AccountKeys`] from `keys`, `index`, and `account`. + #[inline] + fn new(keys: &'h H, index: AccountIndex, account: Account) -> Self { + Self { + keys, + index, + account, + } + } + + /// Performs the bounds check on `index` and then runs `f`. + #[inline] + fn with_bounds_check<T, F>(&self, index: KeyIndex, f: F) -> Option<T> + where + F: FnOnce(&Self, KeyIndex) -> T, + { + (index <= self.account.maximum_index).then(|| f(self, index)) + } + + /// Derives the spend key for this account at `index` without performing bounds checks. + #[inline] + fn derive_spend(&self, index: KeyIndex) -> H::SecretKey { + self.keys.derive_spend(self.index, index) + } + + /// Returns the default spend key for this account. + #[inline] + pub fn default_spend_key(&self) -> H::SecretKey { + self.derive_spend(Default::default()) + } + + /// Returns the spend key for this account at `index`, if it does not exceed the maximum index. + #[inline] + pub fn spend_key(&self, index: KeyIndex) -> Option<H::SecretKey> { + self.with_bounds_check(index, Self::derive_spend) + } + + /// Derives the view key for this account at `index` without performing bounds checks. + #[inline] + fn derive_view(&self, index: KeyIndex) -> H::SecretKey { + self.keys.derive_view(self.index, index) + } + + /// Returns the default view key for this account. + #[inline] + pub fn default_view_key(&self) -> H::SecretKey { + self.derive_view(Default::default()) + } + + /// Returns the view key for this account at `index`, if it does not exceed the maximum index. + #[inline] + pub fn view_key(&self, index: KeyIndex) -> Option<H::SecretKey> { + self.with_bounds_check(index, Self::derive_view) + } + + /// Derives the secret key pair for this account at `index` without performing bounds checks. + #[inline] + fn derive_pair(&self, index: KeyIndex) -> SecretKeyPair<H> { + self.keys.derive_pair(self.index, index) + } + + /// Returns the default secret key pair for this account. + #[inline] + pub fn default_keypair(&self) -> SecretKeyPair<H> { + self.derive_pair(Default::default()) + } + + /// Returns the key pair for this account at the `spend` and `view` indices, if those indices + /// do not exceed the maximum indices. + #[inline] + pub fn keypair(&self, index: KeyIndex) -> Option<SecretKeyPair<H>> { + self.with_bounds_check(index, Self::derive_pair) + } + + /// Returns an iterator over all the key pairs associated to `self`. + #[inline] + pub fn keypairs(&self) -> impl '_ + Iterator<Item = SecretKeyPair<H>> { + let mut index = KeyIndex::default(); + iter::from_fn(move || { + let next = self.keypair(index); + index.increment(); + next + }) + } +} + +/// Account Keys with Mutable Access to the Account Table +#[derive(derivative::Derivative)] +#[derivative( + Debug(bound = "H: Debug"), + Eq(bound = "H: Eq"), + Hash(bound = "H: Hash"), + PartialEq(bound = "H: PartialEq") +)] +pub struct AccountKeysMut<'h, H> +where + H: HierarchicalKeyDerivationScheme + ?Sized, +{ + /// Hierarchical Key Derivation Scheme + keys: &'h H, + + /// Account Index + index: AccountIndex, + + /// Account Information + account: &'h mut Account, +} + +impl<'h, H> AccountKeysMut<'h, H> +where + H: HierarchicalKeyDerivationScheme + ?Sized, +{ + /// Builds a new [`AccountKeysMut`] from `keys`, `index`, and `account`. #[inline] - fn new(keys: &'h H, account: AccountIndex, max_index: KeyIndex) -> Self { + fn new(keys: &'h H, index: AccountIndex, account: &'h mut Account) -> Self { Self { keys, + index, account, - max_index, } } @@ -321,13 +440,13 @@ where where F: FnOnce(&Self, KeyIndex) -> T, { - (index <= self.max_index).then(|| f(self, index)) + (index <= self.account.maximum_index).then(|| f(self, index)) } /// Derives the spend key for this account at `index` without performing bounds checks. #[inline] fn derive_spend(&self, index: KeyIndex) -> H::SecretKey { - self.keys.derive_spend(self.account, index) + self.keys.derive_spend(self.index, index) } /// Returns the default spend key for this account. @@ -345,7 +464,7 @@ where /// Derives the view key for this account at `index` without performing bounds checks. #[inline] fn derive_view(&self, index: KeyIndex) -> H::SecretKey { - self.keys.derive_view(self.account, index) + self.keys.derive_view(self.index, index) } /// Returns the default view key for this account. @@ -363,7 +482,7 @@ where /// Derives the secret key pair for this account at `index` without performing bounds checks. #[inline] fn derive_pair(&self, index: KeyIndex) -> SecretKeyPair<H> { - self.keys.derive_pair(self.account, index) + self.keys.derive_pair(self.index, index) } /// Returns the default secret key pair for this account. @@ -394,7 +513,7 @@ where /// it's key index and key attached, or returns `None` if every application of `f` returned /// `None`. #[inline] - pub fn find_index<T, F>(&self, mut f: F) -> Option<ViewKeySelection<H, T>> + pub fn find_index<T, F>(&mut self, mut f: F) -> Option<ViewKeySelection<H, T>> where F: FnMut(&H::SecretKey) -> Option<T>, { @@ -402,6 +521,7 @@ where loop { let view_key = self.view_key(index)?; if let Some(item) = f(&view_key) { + self.account.last_used_index = cmp::max(self.account.last_used_index, index); return Some(ViewKeySelection { index, keypair: SecretKeyPair::new(self.derive_spend(index), view_key), @@ -418,31 +538,48 @@ where /// /// # Gap Limit /// - /// This method, extends the current maximum index by `gap`-many indices while searching + /// This method extends the current maximum index by [`GAP_LIMIT`]-many indices while searching /// and then sets the new maximum to the previous maximum or the located index, whichever is /// larger. + /// + /// [`GAP_LIMIT`]: HierarchicalKeyDerivationScheme::GAP_LIMIT #[inline] - pub fn find_index_with_gap<T, F>( - &mut self, - gap: KeyIndex, - f: F, - ) -> Option<ViewKeySelection<H, T>> + pub fn find_index_with_gap<T, F>(&mut self, f: F) -> Option<ViewKeySelection<H, T>> where F: FnMut(&H::SecretKey) -> Option<T>, { - let previous_maximum = self.max_index; - self.max_index.index += gap.index; + let previous_maximum = self.account.maximum_index; + self.account.maximum_index.index += H::GAP_LIMIT; match self.find_index(f) { Some(result) => { - self.max_index = cmp::max(previous_maximum, result.index); + self.account.maximum_index = cmp::max(previous_maximum, result.index); Some(result) } _ => { - self.max_index = previous_maximum; + self.account.maximum_index = previous_maximum; None } } } + + /// Runs one of the index search algorithms depending on the value of `use_gap_limit`, where + /// [`find_index_with_gap`](Self::find_index_with_gap) is used in the case that `use_gap_limit` + /// is `true`, and [`find_index`](Self::find_index) is used otherwise. + #[inline] + pub fn find_index_with_maybe_gap<T, F>( + &mut self, + use_gap_limit: bool, + f: F, + ) -> Option<ViewKeySelection<H, T>> + where + F: FnMut(&H::SecretKey) -> Option<T>, + { + if use_gap_limit { + self.find_index_with_gap(f) + } else { + self.find_index(f) + } + } } /// View Key Selection @@ -460,6 +597,18 @@ where pub item: T, } +/// Account +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +pub struct Account { + /// Last Used Index + /// + /// This index is used to enforce limits when generating new keys beyond the `maximum_index`. + pub last_used_index: KeyIndex, + + /// Maximum Index + pub maximum_index: KeyIndex, +} + /// Account Map Trait pub trait AccountMap { /// Builds a new [`AccountMap`] with a starting account with the default maximum index. @@ -468,19 +617,18 @@ pub trait AccountMap { /// Returns the last account index stored in the map. fn last_account(&self) -> AccountIndex; - /// Returns the maximum key index for `account`, if it exists. - fn max_index(&self, account: AccountIndex) -> Option<KeyIndex>; - /// Adds a new account to the map, returning the new account index. fn create_account(&mut self) -> AccountIndex; - /// Increments the maximum key index for `account`, if it exists, returning the new maximum - /// index. - fn increment_index(&mut self, account: AccountIndex) -> Option<KeyIndex>; + /// Returns the [`Account`] associated to `account`. + fn get(&self, account: AccountIndex) -> Option<Account>; + + /// Returns the [`Account`] associated to `account`. + fn get_mut(&mut self, account: AccountIndex) -> Option<&mut Account>; } /// [`Vec`] Account Map Type -pub type VecAccountMap = Vec<KeyIndex>; +pub type VecAccountMap = Vec<Account>; impl AccountMap for VecAccountMap { #[inline] @@ -499,11 +647,6 @@ impl AccountMap for VecAccountMap { ) } - #[inline] - fn max_index(&self, account: AccountIndex) -> Option<KeyIndex> { - self.get(account.index() as usize).copied() - } - #[inline] fn create_account(&mut self) -> AccountIndex { let index = AccountIndex::new( @@ -516,11 +659,13 @@ impl AccountMap for VecAccountMap { } #[inline] - fn increment_index(&mut self, account: AccountIndex) -> Option<KeyIndex> { - self.get_mut(account.index() as usize).map(|m| { - m.increment(); - *m - }) + fn get(&self, account: AccountIndex) -> Option<Account> { + self.as_slice().get(account.index() as usize).copied() + } + + #[inline] + fn get_mut(&mut self, account: AccountIndex) -> Option<&mut Account> { + self.as_mut_slice().get_mut(account.index() as usize) } } @@ -585,7 +730,17 @@ where Some(AccountKeys::new( &self.keys, account, - self.accounts.max_index(account)?, + self.accounts.get(account)?, + )) + } + + /// Returns the account keys for `account` if it exists. + #[inline] + pub fn get_mut(&mut self, account: AccountIndex) -> Option<AccountKeysMut<H>> { + Some(AccountKeysMut::new( + &self.keys, + account, + self.accounts.get_mut(account)?, )) } @@ -595,10 +750,18 @@ where self.get(Default::default()).unwrap() } + /// Returns the account keys for the default account. + #[inline] + pub fn get_mut_default(&mut self) -> AccountKeysMut<H> { + self.get_mut(Default::default()).unwrap() + } + /// Returns the maximum key index for `account`, if it exists. #[inline] - pub fn max_index(&self, account: AccountIndex) -> Option<KeyIndex> { - self.accounts.max_index(account) + pub fn maximum_index(&self, account: AccountIndex) -> Option<KeyIndex> { + self.accounts + .get(account) + .map(|account| account.maximum_index) } /// Adds a new account to the map, returning the new account parameter. @@ -608,16 +771,27 @@ where } /// Increments the maximum key index for `account`, if it exists, returning the current - /// maximum index. - #[inline] - pub fn increment_index(&mut self, account: AccountIndex) -> Option<KeyIndex> { - self.accounts.increment_index(account) + /// maximum index. This method also returns `None` in the case that the + /// [`GAP_LIMIT`](HierarchicalKeyDerivationScheme::GAP_LIMIT) would be exceeded. + #[inline] + pub fn increment_maximum_index(&mut self, account: AccountIndex) -> Option<KeyIndex> { + self.accounts.get_mut(account).and_then(|account| { + match H::GAP_LIMIT + .checked_sub(account.maximum_index.index - account.last_used_index.index) + { + Some(diff) if diff > 0 => { + account.maximum_index.increment(); + Some(account.maximum_index) + } + _ => None, + } + }) } /// Increments the spend index and returns the [`KeyIndex`] for the new index. #[inline] pub fn next_index(&mut self, account: AccountIndex) -> Option<KeyIndex> { - let max_index = self.increment_index(account)?; + let max_index = self.increment_maximum_index(account)?; Some(max_index) } diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 94dfafd51..10ee995fd 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -72,9 +72,6 @@ use { #[doc(inline)] pub use crate::fs::serde::{de::Error as LoadError, ser::Error as SaveError}; -/// Signer Recovery Gap Limit -pub const GAP_LIMIT: KeyIndex = KeyIndex::new(20); - /// Signer Connection pub trait Connection<C> where @@ -143,6 +140,8 @@ where /// /// If `with_recovery` is set to `true`, the [`GAP_LIMIT`] is used during sync to perform a full /// recovery. + /// + /// [`GAP_LIMIT`]: HierarchicalKeyDerivationScheme::GAP_LIMIT pub with_recovery: bool, /// Starting Index @@ -315,7 +314,8 @@ pub enum ReceivingKeyRequest { /// New Keys /// /// Requests `count`-many new keys from the hierarchical key derivation scheme. The signer - /// should always respond with exactly `count`-many keys. + /// should always respond with at most `count`-many keys. If there are fewer, this is because, + /// adding such keys would exceed the [`GAP_LIMIT`](HierarchicalKeyDerivationScheme::GAP_LIMIT). New { /// Number of New Keys to Generate count: usize, @@ -482,7 +482,7 @@ where fn insert_next_item( &mut self, parameters: &Parameters<C>, - gap_limit: KeyIndex, + with_recovery: bool, utxo: Utxo<C>, encrypted_note: EncryptedNote<C>, void_numbers: &mut Vec<VoidNumber<C>>, @@ -499,8 +499,10 @@ where }, }) = self .accounts - .get_default() - .find_index_with_gap(gap_limit, |k| finder.decrypt(&parameters.key_agreement, k)) + .get_mut_default() + .find_index_with_maybe_gap(with_recovery, |k| { + finder.decrypt(&parameters.key_agreement, k) + }) { if let Some(void_number) = C::check_full_asset( parameters, @@ -562,7 +564,7 @@ where fn sync_with<I>( &mut self, parameters: &Parameters<C>, - gap_limit: KeyIndex, + with_recovery: bool, inserts: I, mut void_numbers: Vec<VoidNumber<C>>, is_partial: bool, @@ -575,7 +577,7 @@ where for (utxo, encrypted_note) in inserts { self.insert_next_item( parameters, - gap_limit, + with_recovery, utxo, encrypted_note, &mut void_numbers, @@ -1002,7 +1004,7 @@ where Some(diff) => { let result = self.state.sync_with( &self.parameters.parameters, - with_recovery.then(|| GAP_LIMIT).unwrap_or_default(), + with_recovery, inserts.into_iter().skip(diff), removes.into_iter().collect(), diff == 0, @@ -1100,7 +1102,6 @@ where /// Returns public receiving keys according to the `request`. #[inline] pub fn receiving_keys(&mut self, request: ReceivingKeyRequest) -> Vec<ReceivingKey<C>> { - // FIXME: Enforce that more than `GAP_LIMIT`-many keys cannot be generated. match request { ReceivingKeyRequest::Get { index } => self .state diff --git a/manta-pay/src/bin/simulation.rs b/manta-pay/src/bin/simulation.rs index b7f92e85d..afbae791b 100644 --- a/manta-pay/src/bin/simulation.rs +++ b/manta-pay/src/bin/simulation.rs @@ -22,5 +22,5 @@ /// Runs the Manta Pay simulation. #[inline] pub fn main() { - manta_pay::simulation::simulate(10, 100); + manta_pay::simulation::simulate(10, 10); } diff --git a/manta-pay/src/key.rs b/manta-pay/src/key.rs index 99cf5bfd1..a1a3be445 100644 --- a/manta-pay/src/key.rs +++ b/manta-pay/src/key.rs @@ -27,7 +27,9 @@ use alloc::{format, string::String}; use bip32::{Seed, XPrv}; use core::marker::PhantomData; -use manta_accounting::key::{self, AccountIndex, HierarchicalKeyDerivationScheme, KeyIndex, Kind}; +use manta_accounting::key::{ + self, AccountIndex, HierarchicalKeyDerivationScheme, IndexType, KeyIndex, Kind, +}; use manta_crypto::rand::{CryptoRng, RngCore, Sample, Standard}; use manta_util::{create_seal, seal}; @@ -196,6 +198,8 @@ impl<C> HierarchicalKeyDerivationScheme for KeySecret<C> where C: CoinType, { + const GAP_LIMIT: IndexType = 20; + type SecretKey = XPrv; #[inline] From 392d5e95d6f7fefeb86fd7a8d0e1ec2d88fa143e Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 13 Feb 2022 18:04:03 -0700 Subject: [PATCH 246/275] feat: add (de)serialization for ledger::Connection --- manta-accounting/src/wallet/ledger.rs | 28 ++++++++++++++++++++++++ manta-pay/src/signer/client/http.rs | 11 ++++++++-- manta-pay/src/signer/client/websocket.rs | 11 ++++++++-- 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/manta-accounting/src/wallet/ledger.rs b/manta-accounting/src/wallet/ledger.rs index def3a99b4..14c35acc5 100644 --- a/manta-accounting/src/wallet/ledger.rs +++ b/manta-accounting/src/wallet/ledger.rs @@ -23,6 +23,9 @@ use crate::transfer::{Configuration, EncryptedNote, TransferPost, Utxo, VoidNumb use alloc::vec::Vec; use core::{fmt::Debug, hash::Hash}; +#[cfg(feature = "serde")] +use manta_util::serde::{Deserialize, Serialize}; + /// Ledger Checkpoint pub trait Checkpoint: Default + PartialOrd { /// Returns the index into the receiver set for the ledger. @@ -72,6 +75,26 @@ pub type PushResult<C, L> = Result<PushResponse, <L as Connection<C>>::Error>; /// /// This `struct` is created by the [`pull`](Connection::pull) method on [`Connection`]. /// See its documentation for more. +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = r" + L::Checkpoint: Deserialize<'de>, + L::ReceiverChunk: Deserialize<'de>, + L::SenderChunk: Deserialize<'de> + ", + serialize = r" + L::Checkpoint: Serialize, + L::ReceiverChunk: Serialize, + L::SenderChunk: Serialize + ", + ), + crate = "manta_util::serde", + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "L::Checkpoint: Clone, L::ReceiverChunk: Clone, L::SenderChunk: Clone"), @@ -113,6 +136,11 @@ where /// /// This `struct` is created by the [`push`](Connection::push) method on [`Connection`]. /// See its documentation for more. +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct PushResponse { /// Transaction Success Flag diff --git a/manta-pay/src/signer/client/http.rs b/manta-pay/src/signer/client/http.rs index adbe91b3a..d1ed094eb 100644 --- a/manta-pay/src/signer/client/http.rs +++ b/manta-pay/src/signer/client/http.rs @@ -19,8 +19,12 @@ use crate::config::Config; use manta_accounting::{ transfer::{canonical::Transaction, ReceivingKey}, - wallet::signer::{ - self, ReceivingKeyRequest, SignError, SignResponse, SyncError, SyncRequest, SyncResponse, + wallet::{ + self, + signer::{ + self, ReceivingKeyRequest, SignError, SignResponse, SyncError, SyncRequest, + SyncResponse, + }, }, }; use manta_util::serde::Serialize; @@ -29,6 +33,9 @@ use reqwest::{ Error, IntoUrl, Method, Url, }; +/// Wallet Associated to [`Client`] +pub type Wallet<L> = wallet::Wallet<Config, L, Client>; + /// HTTP Client pub struct Client { /// Server URL diff --git a/manta-pay/src/signer/client/websocket.rs b/manta-pay/src/signer/client/websocket.rs index a96f554c6..4e1451a3e 100644 --- a/manta-pay/src/signer/client/websocket.rs +++ b/manta-pay/src/signer/client/websocket.rs @@ -19,8 +19,12 @@ use crate::config::Config; use manta_accounting::{ transfer::{canonical::Transaction, ReceivingKey}, - wallet::signer::{ - self, ReceivingKeyRequest, SignError, SignResponse, SyncError, SyncRequest, SyncResponse, + wallet::{ + self, + signer::{ + self, ReceivingKeyRequest, SignError, SignResponse, SyncError, SyncRequest, + SyncResponse, + }, }, }; use manta_util::{ @@ -66,6 +70,9 @@ pub struct Request<R> { pub request: R, } +/// Wallet Associated to [`Client`] +pub type Wallet<L> = wallet::Wallet<Config, L, Client>; + /// WebSocket Client pub struct Client(tungstenite::WebSocket<MaybeTlsStream<TcpStream>>); From 5e3cd42e6ab60df3368ae4329f80e49674b06043 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 14 Feb 2022 17:30:33 -0700 Subject: [PATCH 247/275] fix: update for manta-signer --- .rustfmt.toml | 2 +- manta-accounting/src/key.rs | 5 ++ manta-accounting/src/wallet/signer.rs | 42 ++++----- manta-crypto/src/rand.rs | 5 ++ manta-pay/src/config.rs | 85 ++++++++++--------- .../src/crypto/constraint/arkworks/groth16.rs | 12 +-- .../src/crypto/constraint/arkworks/mod.rs | 2 +- manta-pay/src/crypto/ecc/arkworks.rs | 11 +-- manta-pay/src/crypto/hash/poseidon.rs | 15 ++++ manta-pay/src/key.rs | 29 +++++-- manta-pay/src/signer/base.rs | 12 ++- manta-pay/src/signer/client/http.rs | 22 ++--- manta-pay/src/signer/mod.rs | 21 +++++ manta-util/src/convert.rs | 9 ++ 14 files changed, 172 insertions(+), 100 deletions(-) diff --git a/.rustfmt.toml b/.rustfmt.toml index 4aefbd7c0..b150d56bf 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -1,2 +1,2 @@ -imports_granularity="Crate" +imports_granularity = "Crate" license_template_path = "FILE_TEMPLATE" diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index fa3db5ab2..1a463402f 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -598,6 +598,11 @@ where } /// Account +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct Account { /// Last Used Index diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 10ee995fd..2a9710421 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -371,6 +371,15 @@ impl<C> SignerParameters<C> where C: Configuration, { + /// Builds a new [`SignerParameters`] from `parameters` and `proving_context`. + #[inline] + pub fn new(parameters: Parameters<C>, proving_context: C::ProvingContextCache) -> Self { + Self { + parameters, + proving_context, + } + } + /// Returns the public parameters by reading from the proving context cache. #[inline] pub fn get( @@ -443,7 +452,7 @@ where /// Saves the state of `self` to an encrypted file at `path`. #[cfg(all(feature = "fs", feature = "serde"))] #[inline] - fn save<F, P>(&self, path: P, password: &[u8]) -> Result<(), SaveError<F>> + pub fn save<F, P>(&self, path: P, password: &[u8]) -> Result<(), SaveError<F>> where Self: Serialize, F: File, @@ -462,7 +471,7 @@ where /// Loads an encrypted [`SignerState`] from the encrypted file at `path`. #[cfg(all(feature = "fs", feature = "serde"))] #[inline] - fn load<F, P>(path: P, password: &[u8]) -> Result<Self, LoadError<F>> + pub fn load<F, P>(path: P, password: &[u8]) -> Result<Self, LoadError<F>> where Self: DeserializeOwned, F: File, @@ -893,7 +902,7 @@ where { /// Builds a new [`Signer`] from `parameters` and `state`. #[inline] - fn from_parts(parameters: SignerParameters<C>, state: SignerState<C>) -> Self { + pub fn from_parts(parameters: SignerParameters<C>, state: SignerState<C>) -> Self { Self { parameters, state } } @@ -982,17 +991,7 @@ where /// Updates the internal ledger state, returning the new asset distribution. #[inline] - pub fn sync<I, R>( - &mut self, - with_recovery: bool, - starting_index: usize, - inserts: I, - removes: R, - ) -> Result<SyncResponse, SyncError> - where - I: IntoIterator<Item = (Utxo<C>, EncryptedNote<C>)>, - R: IntoIterator<Item = VoidNumber<C>>, - { + pub fn sync(&mut self, request: SyncRequest<C>) -> Result<SyncResponse, SyncError> { // TODO: Do a capacity check on the current UTXO set? // // if self.utxo_set.capacity() < starting_index { @@ -1000,13 +999,13 @@ where // } // let utxo_set_len = self.state.utxo_set.len(); - match utxo_set_len.checked_sub(starting_index) { + match utxo_set_len.checked_sub(request.starting_index) { Some(diff) => { let result = self.state.sync_with( &self.parameters.parameters, - with_recovery, - inserts.into_iter().skip(diff), - removes.into_iter().collect(), + request.with_recovery, + request.inserts.into_iter().skip(diff), + request.removes, diff == 0, ); self.state.utxo_set.commit(); @@ -1140,12 +1139,7 @@ where &mut self, request: SyncRequest<C>, ) -> Result<Result<SyncResponse, SyncError>, Self::Error> { - Ok(self.sync( - request.with_recovery, - request.starting_index, - request.inserts, - request.removes, - )) + Ok(self.sync(request)) } #[inline] diff --git a/manta-crypto/src/rand.rs b/manta-crypto/src/rand.rs index d7e27667a..5e9ec9fe6 100644 --- a/manta-crypto/src/rand.rs +++ b/manta-crypto/src/rand.rs @@ -24,6 +24,11 @@ use manta_util::into_array_unchecked; pub use rand_core::{block, CryptoRng, Error, RngCore, SeedableRng}; +#[cfg(feature = "getrandom")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "getrandom")))] +#[doc(inline)] +pub use rand_core::OsRng; + /// Random Number Generator Sized Wrapper #[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct SizedRng<'r, R>( diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 880dadea8..a1ccd55ae 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -473,6 +473,47 @@ impl merkle_tree::Configuration for MerkleTreeConfiguration { const HEIGHT: usize = 20; } +impl MerkleTreeConfiguration { + /// Width of the Merkle Forest + pub const FOREST_WIDTH: usize = 256; +} + +impl merkle_tree::forest::Configuration for MerkleTreeConfiguration { + type Index = u8; + + #[inline] + fn tree_index(leaf: &merkle_tree::Leaf<Self>) -> Self::Index { + let mut hasher = Blake2sVar::new(1).unwrap(); + let mut buffer = Vec::new(); + leaf.0 + .serialize_unchecked(&mut buffer) + .expect("Serializing is not allowed to fail."); + hasher.update(&buffer); + let mut result = [0]; + hasher + .finalize_variable(&mut result) + .expect("Hashing is not allowed to fail."); + result[0] + } +} + +/* NOTE: Configuration for testing single-tree forest. +impl MerkleTreeConfiguration { + /// Width of the Merkle Forest + pub const FOREST_WIDTH: usize = 1; +} + +impl merkle_tree::forest::Configuration for MerkleTreeConfiguration { + type Index = merkle_tree::forest::SingleTreeIndex; + + #[inline] + fn tree_index(leaf: &merkle_tree::Leaf<Self>) -> Self::Index { + let _ = leaf; + Default::default() + } +} +*/ + #[cfg(any(feature = "test", test))] impl merkle_tree::test::HashParameterSampling for MerkleTreeConfiguration { type LeafHashParameterDistribution = Standard; @@ -645,43 +686,11 @@ pub type MultiProvingContext = transfer::canonical::MultiProvingContext<Config>; /// Multi-Verifying Context Type pub type MultiVerifyingContext = transfer::canonical::MultiVerifyingContext<Config>; -impl MerkleTreeConfiguration { - /// Width of the Merkle Forest - pub const FOREST_WIDTH: usize = 256; -} +/// Transaction Type +pub type Transaction = transfer::canonical::Transaction<Config>; -impl merkle_tree::forest::Configuration for MerkleTreeConfiguration { - type Index = u8; +/// Spending Key Type +pub type SpendingKey = transfer::SpendingKey<Config>; - #[inline] - fn tree_index(leaf: &merkle_tree::Leaf<Self>) -> Self::Index { - let mut hasher = Blake2sVar::new(1).unwrap(); - let mut buffer = Vec::new(); - leaf.0 - .serialize_unchecked(&mut buffer) - .expect("Serializing is not allowed to fail."); - hasher.update(&buffer); - let mut result = [0]; - hasher - .finalize_variable(&mut result) - .expect("Hashing is not allowed to fail."); - result[0] - } -} - -/* NOTE: Configuration for testing single-tree forest. -impl MerkleTreeConfiguration { - /// Width of the Merkle Forest - pub const FOREST_WIDTH: usize = 1; -} - -impl merkle_tree::forest::Configuration for MerkleTreeConfiguration { - type Index = merkle_tree::forest::SingleTreeIndex; - - #[inline] - fn tree_index(leaf: &merkle_tree::Leaf<Self>) -> Self::Index { - let _ = leaf; - Default::default() - } -} -*/ +/// Receiving Key Type +pub type ReceivingKey = transfer::ReceivingKey<Config>; diff --git a/manta-pay/src/crypto/constraint/arkworks/groth16.rs b/manta-pay/src/crypto/constraint/arkworks/groth16.rs index dba9a5c4c..2602daeca 100644 --- a/manta-pay/src/crypto/constraint/arkworks/groth16.rs +++ b/manta-pay/src/crypto/constraint/arkworks/groth16.rs @@ -18,7 +18,7 @@ use crate::crypto::constraint::arkworks::{ self, - codec::{ArkReader, ArkWriter, HasDeserialization, HasSerialization, SerializationError}, + codec::{HasDeserialization, HasSerialization, SerializationError}, R1CS, }; use alloc::vec::Vec; @@ -134,7 +134,7 @@ where /// Converts `proof` into its canonical byte-representation. #[inline] -fn proof_as_bytes<E>(proof: &ark_groth16::Proof<E>) -> Vec<u8> +pub fn proof_as_bytes<E>(proof: &ark_groth16::Proof<E>) -> Vec<u8> where E: PairingEngine, { @@ -185,7 +185,7 @@ where where R: codec::Read, { - let mut reader = ArkReader::new(reader); + let mut reader = arkworks::codec::ArkReader::new(reader); match CanonicalDeserialize::deserialize_unchecked(&mut reader) { Ok(value) => reader .finish() @@ -205,7 +205,7 @@ where where W: codec::Write, { - let mut writer = ArkWriter::new(writer); + let mut writer = arkworks::codec::ArkWriter::new(writer); let _ = self.0.serialize_unchecked(&mut writer); writer.finish().map(move |_| ()) } @@ -395,7 +395,7 @@ where where R: codec::Read, { - let mut reader = ArkReader::new(reader); + let mut reader = arkworks::codec::ArkReader::new(reader); match CanonicalDeserialize::deserialize(&mut reader) { Ok(value) => reader .finish() @@ -416,7 +416,7 @@ where where W: codec::Write, { - let mut writer = ArkWriter::new(writer); + let mut writer = arkworks::codec::ArkWriter::new(writer); let _ = self.serialize(&mut writer); writer.finish().map(move |_| ()) } diff --git a/manta-pay/src/crypto/constraint/arkworks/mod.rs b/manta-pay/src/crypto/constraint/arkworks/mod.rs index 92ba6e12e..f174d62d6 100644 --- a/manta-pay/src/crypto/constraint/arkworks/mod.rs +++ b/manta-pay/src/crypto/constraint/arkworks/mod.rs @@ -184,7 +184,7 @@ where /// Converts `element` into its canonical byte-representation. #[inline] -fn field_element_as_bytes<F>(element: &F) -> Vec<u8> +pub fn field_element_as_bytes<F>(element: &F) -> Vec<u8> where F: PrimeField, { diff --git a/manta-pay/src/crypto/ecc/arkworks.rs b/manta-pay/src/crypto/ecc/arkworks.rs index bbe6eacc7..fad85a5de 100644 --- a/manta-pay/src/crypto/ecc/arkworks.rs +++ b/manta-pay/src/crypto/ecc/arkworks.rs @@ -16,10 +16,7 @@ //! Arkworks Elliptic Curve Primitives -use crate::crypto::constraint::arkworks::{ - codec::{ArkReader, ArkWriter, ScaleCodecReader}, - empty, full, Boolean, Fp, FpVar, R1CS, -}; +use crate::crypto::constraint::arkworks::{self, empty, full, Boolean, Fp, FpVar, R1CS}; use alloc::vec::Vec; use ark_ff::{BigInteger, Field, FpParameters, PrimeField}; use ark_r1cs_std::ToBitsGadget; @@ -129,7 +126,7 @@ where where R: codec::Read, { - let mut reader = ArkReader::new(reader); + let mut reader = arkworks::codec::ArkReader::new(reader); match CanonicalDeserialize::deserialize(&mut reader) { Ok(value) => reader .finish() @@ -149,7 +146,7 @@ where where W: codec::Write, { - let mut writer = ArkWriter::new(writer); + let mut writer = arkworks::codec::ArkWriter::new(writer); let _ = self.0.serialize(&mut writer); writer.finish().map(|_| ()) } @@ -167,7 +164,7 @@ where I: scale_codec::Input, { Ok(Self( - CanonicalDeserialize::deserialize(ScaleCodecReader(input)) + CanonicalDeserialize::deserialize(arkworks::codec::ScaleCodecReader(input)) .map_err(|_| "Deserialization Error")?, )) } diff --git a/manta-pay/src/crypto/hash/poseidon.rs b/manta-pay/src/crypto/hash/poseidon.rs index eb810d35b..aed316135 100644 --- a/manta-pay/src/crypto/hash/poseidon.rs +++ b/manta-pay/src/crypto/hash/poseidon.rs @@ -24,6 +24,9 @@ use core::{fmt::Debug, iter, mem}; use manta_crypto::hash::HashFunction; use manta_util::codec::{Decode, DecodeError, Encode, Read, Write}; +#[cfg(feature = "serde")] +use manta_util::serde::{Deserialize, Serialize}; + #[cfg(any(feature = "test", test))] use { core::iter::repeat, @@ -73,6 +76,18 @@ where } /// Poseidon Hash +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "S::Field: Deserialize<'de>", + serialize = "S::Field: Serialize" + ), + crate = "manta_util::serde", + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "S::Field: Clone"), diff --git a/manta-pay/src/key.rs b/manta-pay/src/key.rs index a1a3be445..fab134045 100644 --- a/manta-pay/src/key.rs +++ b/manta-pay/src/key.rs @@ -31,7 +31,10 @@ use manta_accounting::key::{ self, AccountIndex, HierarchicalKeyDerivationScheme, IndexType, KeyIndex, Kind, }; use manta_crypto::rand::{CryptoRng, RngCore, Sample, Standard}; -use manta_util::{create_seal, seal}; +use manta_util::{create_seal, seal, Array}; + +#[cfg(feature = "serde")] +use manta_util::serde::{Deserialize, Serialize}; pub use bip32::{Language, Mnemonic}; @@ -125,13 +128,21 @@ impl_coin_type!( /// Account Table Type pub type AccountTable<C> = key::AccountTable<KeySecret<C>>; +/// Seed Byte Array Type +type SeedBytes = Array<u8, { Seed::SIZE }>; + /// Key Secret +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields, transparent) +)] pub struct KeySecret<C> where C: CoinType, { /// Key Seed - seed: Seed, + seed: SeedBytes, /// Type Parameter Marker __: PhantomData<C>, @@ -141,15 +152,21 @@ impl<C> KeySecret<C> where C: CoinType, { - /// Builds a [`KeySecret`] from a raw `seed`. + /// Builds a [`KeySecret`] from raw bytes. #[inline] - fn from_seed(seed: Seed) -> Self { + fn build(seed: [u8; Seed::SIZE]) -> Self { Self { - seed, + seed: seed.into(), __: PhantomData, } } + /// Builds a [`KeySecret`] from a `seed`. + #[inline] + fn from_seed(seed: Seed) -> Self { + Self::build(*seed.as_bytes()) + } + /// Converts a `mnemonic` phrase into a [`KeySecret`], locking it with `password`. #[inline] #[must_use] @@ -170,7 +187,7 @@ where let _ = distribution; let mut seed = [0; Seed::SIZE]; rng.fill_bytes(&mut seed); - Self::from_seed(Seed::new(seed)) + Self::build(seed) } } diff --git a/manta-pay/src/signer/base.rs b/manta-pay/src/signer/base.rs index 51ce1f346..bd499c881 100644 --- a/manta-pay/src/signer/base.rs +++ b/manta-pay/src/signer/base.rs @@ -26,7 +26,7 @@ use ark_ff::PrimeField; use manta_accounting::{ asset::HashAssetMap, key::{self, HierarchicalKeyDerivationScheme}, - wallet::signer::AssetMapKey, + wallet::{self, signer::AssetMapKey}, }; use manta_crypto::{key::KeyDerivationFunction, merkle_tree}; use manta_util::pointer::ThreadSafe; @@ -211,7 +211,7 @@ pub mod cache { } } -impl manta_accounting::wallet::signer::Configuration for Config { +impl wallet::signer::Configuration for Config { type HierarchicalKeyDerivationScheme = key::Map<TestnetKeySecret, HierarchicalKeyDerivationFunction>; type UtxoSet = UtxoSet; @@ -220,5 +220,11 @@ impl manta_accounting::wallet::signer::Configuration for Config { type Rng = rand_chacha::ChaCha20Rng; } +/// Signer Parameters Type +pub type SignerParameters = wallet::signer::SignerParameters<Config>; + +/// Signer State Type +pub type SignerState = wallet::signer::SignerState<Config>; + /// Signer Base Type -pub type Signer = manta_accounting::wallet::signer::Signer<Config>; +pub type Signer = wallet::signer::Signer<Config>; diff --git a/manta-pay/src/signer/client/http.rs b/manta-pay/src/signer/client/http.rs index d1ed094eb..a7df23892 100644 --- a/manta-pay/src/signer/client/http.rs +++ b/manta-pay/src/signer/client/http.rs @@ -16,17 +16,11 @@ //! Signer HTTP Client Implementation -use crate::config::Config; -use manta_accounting::{ - transfer::{canonical::Transaction, ReceivingKey}, - wallet::{ - self, - signer::{ - self, ReceivingKeyRequest, SignError, SignResponse, SyncError, SyncRequest, - SyncResponse, - }, - }, +use crate::{ + config::{Config, ReceivingKey, Transaction}, + signer::{ReceivingKeyRequest, SignError, SignResponse, SyncError, SyncRequest, SyncResponse}, }; +use manta_accounting::wallet::{self, signer}; use manta_util::serde::Serialize; use reqwest::{ blocking::{Client as BaseClient, Response}, @@ -100,7 +94,7 @@ impl signer::Connection<Config> for Client { #[inline] fn sync( &mut self, - request: SyncRequest<Config>, + request: SyncRequest, ) -> Result<Result<SyncResponse, SyncError>, Self::Error> { // NOTE: The synchronization command modifies the signer so it must be a POST command // to match the HTTP semantics. @@ -110,8 +104,8 @@ impl signer::Connection<Config> for Client { #[inline] fn sign( &mut self, - transaction: Transaction<Config>, - ) -> Result<Result<SignResponse<Config>, SignError<Config>>, Self::Error> { + transaction: Transaction, + ) -> Result<Result<SignResponse, SignError>, Self::Error> { // NOTE: The signing command does not modify the signer so it must be a GET command to match // the HTTP semantics. self.get("sign", transaction)?.json() @@ -121,7 +115,7 @@ impl signer::Connection<Config> for Client { fn receiving_keys( &mut self, request: ReceivingKeyRequest, - ) -> Result<Vec<ReceivingKey<Config>>, Self::Error> { + ) -> Result<Vec<ReceivingKey>, Self::Error> { // NOTE: The receiving key command modifies the signer so it must be a POST command to match // the HTTP semantics. self.post("receivingKeys", request)?.json() diff --git a/manta-pay/src/signer/mod.rs b/manta-pay/src/signer/mod.rs index dff4470cb..c04a664f3 100644 --- a/manta-pay/src/signer/mod.rs +++ b/manta-pay/src/signer/mod.rs @@ -16,8 +16,29 @@ //! Manta Pay Signer Tools +use crate::config::Config; +use manta_accounting::wallet::signer; + pub mod client; #[cfg(feature = "wallet")] #[cfg_attr(doc_cfg, doc(cfg(feature = "wallet")))] pub mod base; + +/// Synchronization Request +pub type SyncRequest = signer::SyncRequest<Config>; + +/// Synchronization Response +pub type SyncResponse = signer::SyncResponse; + +/// Synchronization Error +pub type SyncError = signer::SyncError; + +/// Sign Response +pub type SignResponse = signer::SignResponse<Config>; + +/// Sign Error +pub type SignError = signer::SignError<Config>; + +/// Receiving Key Request +pub type ReceivingKeyRequest = signer::ReceivingKeyRequest; diff --git a/manta-util/src/convert.rs b/manta-util/src/convert.rs index 47d5d708e..9bf1e7559 100644 --- a/manta-util/src/convert.rs +++ b/manta-util/src/convert.rs @@ -29,3 +29,12 @@ pub type Never = Infallible; pub fn never<T>(_: Never) -> T { unreachable!("This type never has any values, so this promotion is safe.") } + +/// Promotes a [`Never`] error value to the `Ok` variant. +#[inline] +pub fn never_err<T>(result: Result<T, Never>) -> T { + match result { + Ok(value) => value, + Err(err) => never(err), + } +} From 6138113e0a3b4c0d61541d28175d8a7c444ac2f9 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 15 Feb 2022 11:15:50 -0700 Subject: [PATCH 248/275] feat: remove reference-counting from ForkedTree implementation --- manta-crypto/src/merkle_tree/forest.rs | 6 +- manta-crypto/src/merkle_tree/fork.rs | 604 +++++++++++++++++-------- manta-crypto/src/merkle_tree/tree.rs | 3 +- manta-pay/src/signer/base.rs | 2 - manta-util/src/pointer.rs | 41 +- 5 files changed, 455 insertions(+), 201 deletions(-) diff --git a/manta-crypto/src/merkle_tree/forest.rs b/manta-crypto/src/merkle_tree/forest.rs index e5f6cf6ff..da72286a9 100644 --- a/manta-crypto/src/merkle_tree/forest.rs +++ b/manta-crypto/src/merkle_tree/forest.rs @@ -36,7 +36,7 @@ use crate::{ }; use alloc::{boxed::Box, vec::Vec}; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; -use manta_util::{persistence::Rollback, pointer::PointerFamily, BoxArray}; +use manta_util::{persistence::Rollback, BoxArray}; #[cfg(feature = "serde")] use manta_util::serde::{Deserialize, Serialize}; @@ -620,13 +620,11 @@ where } } -impl<C, T, P, M, const N: usize> Rollback - for MerkleForest<C, TreeArray<C, ForkedTree<C, T, P, M>, N>> +impl<C, T, M, const N: usize> Rollback for MerkleForest<C, TreeArray<C, ForkedTree<C, T, M>, N>> where C: Configuration + ?Sized, C::Index: FixedIndex<N>, T: Tree<C>, - P: PointerFamily<T>, M: Default + InnerMap<C>, LeafDigest<C>: Clone + Default, InnerDigest<C>: Clone + Default + PartialEq, diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs index 93e6621f7..e6971be9d 100644 --- a/manta-crypto/src/merkle_tree/fork.rs +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -16,9 +16,6 @@ //! Merkle Tree Forks -// TODO: Implement derive-able traits for these types. -// TODO: See if we can get rid of the smart pointer logic (we can for `ForkedTree` at least). - use crate::merkle_tree::{ capacity, inner_tree::{BTreeMap, InnerMap, PartialInnerTree}, @@ -31,6 +28,9 @@ use alloc::{vec, vec::Vec}; use core::{borrow::Borrow, fmt::Debug, hash::Hash, marker::PhantomData, mem, ops::Deref}; use manta_util::pointer::{self, PointerFamily}; +#[cfg(feature = "serde")] +use manta_util::serde::{Deserialize, Serialize}; + /// Fork-able Merkle Tree #[derive(derivative::Derivative)] #[derivative(Debug(bound = "P::Strong: Debug"))] @@ -126,7 +126,7 @@ where { match fork.get_attached_base(self) { Some(base) => { - self.merge_branch(parameters, base, fork.base_contribution, fork.branch); + self.merge_branch(parameters, base, fork.branch); Ok(()) } _ => Err(fork), @@ -140,29 +140,29 @@ where &mut self, parameters: &Parameters<C>, fork_base: P::Strong, - base_contribution: BaseContribution, - branch: Partial<C, M>, + branch: Branch<C, M>, ) where M: InnerMap<C> + Default, LeafDigest<C>: Default, { self.base = Some(fork_base); let mut base = P::claim(mem::take(&mut self.base).unwrap()); - assert!(base - .extend_digests( - parameters, - Fork::<C, T, P, M>::extract_leaves(base_contribution, branch) - ) - .is_ok()); + branch.merge(parameters, &mut base); self.base = Some(P::new(base)); } - /// Borrows the underlying merkle tree pointer. + /// Borrows the underlying merkle tree strong pointer. #[inline] fn borrow_base(&self) -> &P::Strong { self.base.as_ref().unwrap() } + /// Borrows the underlying merkle tree. + #[inline] + fn get(&self) -> &T { + self.borrow_base().borrow() + } + /// Returns a new weak pointer to the base tree. #[inline] fn downgrade(&self) -> P::Weak { @@ -184,7 +184,7 @@ where { #[inline] fn as_ref(&self) -> &T { - self.borrow_base().as_ref() + self.get() } } @@ -196,7 +196,7 @@ where { #[inline] fn borrow(&self) -> &T { - self.borrow_base().borrow() + self.get() } } @@ -210,11 +210,16 @@ where #[inline] fn deref(&self) -> &Self::Target { - self.borrow_base().deref() + self.get() } } /// Base Tree Leaf Contribution +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] enum BaseContribution { /// No Leaves Contributed @@ -234,93 +239,77 @@ impl Default for BaseContribution { } } -/// Merkle Tree Fork +/// Merkle Tree Branch +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = r" + LeafDigest<C>: Deserialize<'de>, + InnerDigest<C>: Deserialize<'de>, + M: Deserialize<'de>, + ", + serialize = r" + LeafDigest<C>: Serialize, + InnerDigest<C>: Serialize, + M: Serialize, + ", + ), + crate = "manta_util::serde", + deny_unknown_fields, + ) +)] #[derive(derivative::Derivative)] #[derivative( - Debug(bound = "P::Weak: Debug, LeafDigest<C>: Debug, InnerDigest<C>: Debug, M: Debug"), + Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug, M: Debug"), Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default") )] -pub struct Fork<C, T, P = pointer::SingleThreaded, M = BTreeMap<C>> +struct Branch<C, M = BTreeMap<C>> where C: Configuration + ?Sized, - T: Tree<C>, - P: PointerFamily<T>, M: Default + InnerMap<C>, { - /// Base Merkle Tree - base: P::Weak, - /// Base Tree Contribution base_contribution: BaseContribution, /// Branch Data - branch: Partial<C, M>, + data: Partial<C, M>, } -impl<C, T, P, M> Fork<C, T, P, M> +impl<C, M> Branch<C, M> where C: Configuration + ?Sized, - T: Tree<C>, - P: PointerFamily<T>, M: Default + InnerMap<C>, { - /// Builds a new [`Fork`] from `trunk`. - #[inline] - pub fn new(parameters: &Parameters<C>, trunk: &Trunk<C, T, P>) -> Self - where - LeafDigest<C>: Clone + Default, - InnerDigest<C>: Default, - { - Self::with_leaves(parameters, trunk, Default::default()).unwrap() - } - - /// Builds a new [`Fork`] from `trunk` extended by `leaf_digests`, returning `None` if - /// appending `leaf_digests` would exceed the capacity of the `trunk`. - #[inline] - pub fn with_leaves( - parameters: &Parameters<C>, - trunk: &Trunk<C, T, P>, - leaf_digests: Vec<LeafDigest<C>>, - ) -> Option<Self> - where - LeafDigest<C>: Clone + Default, - InnerDigest<C>: Default, - { - let (base_contribution, branch) = - Self::new_branch(parameters, trunk.borrow_base().as_ref(), leaf_digests)?; - Some(Self { - base: trunk.downgrade(), - base_contribution, - branch, - }) - } - /// Builds a new branch off of `base`, extending by `leaf_digests`. #[inline] - fn new_branch( + fn new<T>( parameters: &Parameters<C>, base: &T, leaf_digests: Vec<LeafDigest<C>>, - ) -> Option<(BaseContribution, Partial<C, M>)> + ) -> Option<Self> where + T: Tree<C>, LeafDigest<C>: Clone + Default, InnerDigest<C>: Default, { if leaf_digests.len() + base.len() >= capacity::<C>() { return None; } - Some(Self::new_branch_unchecked(parameters, base, leaf_digests)) + Some(Self::new_unchecked(parameters, base, leaf_digests)) } /// Builds a new branch off of `base`, extending by `leaf_digests` without checking that /// `base` can accept new leaves. #[inline] - fn new_branch_unchecked( + fn new_unchecked<T>( parameters: &Parameters<C>, base: &T, leaf_digests: Vec<LeafDigest<C>>, - ) -> (BaseContribution, Partial<C, M>) + ) -> Self where + T: Tree<C>, LeafDigest<C>: Clone + Default, InnerDigest<C>: Default, { @@ -334,12 +323,15 @@ where for (i, digest) in leaf_digests.into_iter().enumerate() { partial.push_leaf_digest(parameters, Node(partial_tree_len + i), digest); } - (base_contribution, partial) + Self { + base_contribution, + data: partial, + } } - /// Generates the setup data to compute [`new_branch_unchecked`](Self::new_branch_unchecked). + /// Generates the setup data to compute [`new_unchecked`](Self::new_unchecked). #[inline] - fn generate_branch_setup( + fn generate_branch_setup<T>( parameters: &Parameters<C>, base: &T, ) -> ( @@ -349,6 +341,7 @@ where CurrentInnerPath<C>, ) where + T: Tree<C>, LeafDigest<C>: Clone + Default, InnerDigest<C>: Default, { @@ -379,45 +372,248 @@ where } } - /// Extracts the non-base leaves from `branch`. + /// Extracts the non-base leaves from `base_contribution` and `data`. #[inline] fn extract_leaves( base_contribution: BaseContribution, - branch: Partial<C, M>, + data: Partial<C, M>, ) -> Vec<LeafDigest<C>> where LeafDigest<C>: Default, { - let mut leaf_digests = branch.into_leaves(); + let mut leaf_digests = data.into_leaves(); mem::drop(leaf_digests.drain(0..base_contribution as usize)); leaf_digests } - /// Tries to rebase `branch` at `base`. + /// Tries to rebase `self` at `base`. #[inline] - fn try_rebase( - parameters: &Parameters<C>, - base: &T, - base_contribution: &mut BaseContribution, - branch: &mut Partial<C, M>, - ) -> bool + fn try_rebase<T>(&mut self, parameters: &Parameters<C>, base: &T) -> bool where + T: Tree<C>, LeafDigest<C>: Clone + Default, InnerDigest<C>: Default, { - if branch.len() + base.len() - (*base_contribution as usize) >= capacity::<C>() { + if self.data.len() + base.len() - (self.base_contribution as usize) >= capacity::<C>() { return false; } - let (new_base_contribution, new_branch) = Self::new_branch_unchecked( + let new_branch = Self::new_unchecked( parameters, base, - Self::extract_leaves(*base_contribution, mem::take(branch)), + Self::extract_leaves(self.base_contribution, mem::take(&mut self.data)), ); - *base_contribution = new_base_contribution; - *branch = new_branch; + *self = new_branch; true } + /// Computes the length of this branch of the tree. + #[inline] + pub fn len(&self) -> usize { + self.data.len() + } + + /// Returns `true` if this branch is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } + + /// Returns the current root of this branch. + #[inline] + pub fn root(&self) -> &InnerDigest<C> { + self.data.root() + } + + /// Returns the leaf digest at the given `index` in the tree. + #[inline] + pub fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>> { + self.data.leaf_digest(index) + } + + /// Returns the current (right-most) leaf of the branch. + #[inline] + pub fn current_leaf(&self) -> Option<&LeafDigest<C>> { + self.data.current_leaf() + } + + /// Returns the current (right-most) path of the branch. + #[inline] + pub fn current_path(&self) -> CurrentPath<C> + where + LeafDigest<C>: Clone + Default, + InnerDigest<C>: Clone + Default + PartialEq, + { + self.data.current_path() + } + + /// Computes the modified path for leaves in the main trunk. + #[inline] + fn modified_path_unchecked<T>( + &self, + parameters: &Parameters<C>, + index: usize, + base: &T, + ) -> Result<Path<C>, PathError> + where + T: WithProofs<C>, + LeafDigest<C>: Clone + Default, + InnerDigest<C>: Clone, + { + let base_index = Node(index); + let base_path = base.path(parameters, base_index.0)?; + let fork_index = self.data.starting_leaf_node(); + let mut fork_path = self.data.path_unchecked(fork_index.0); + if !Node::are_siblings(&base_index, &fork_index) { + let matching_index = base_index + .parents() + .zip(fork_index.parents()) + .position(|(b, f)| Node::are_siblings(&b, &f)) + .unwrap(); + fork_path.inner_path.path[matching_index] = InnerPath::fold( + parameters, + fork_index, + fork_index.join_leaves( + parameters, + self.leaf_digest(fork_index.0) + .unwrap_or(&Default::default()), + &fork_path.sibling_digest, + ), + &fork_path.inner_path.path[..matching_index], + ); + fork_path.inner_path.path[..matching_index] + .clone_from_slice(&base_path.inner_path.path[..matching_index]); + } + fork_path.inner_path.leaf_index = base_path.inner_path.leaf_index; + fork_path.sibling_digest = base_path.sibling_digest; + Ok(fork_path) + } + + /// Computes the path of any leaf in the forked tree, assuming that `modified_path` returns the + /// outcome of [`modified_path_unchecked`](Self::modified_path_unchecked) on some base tree. + #[inline] + fn path<F>(&self, index: usize, modified_path: F) -> Result<Path<C>, PathError> + where + F: FnOnce(&Self) -> Result<Path<C>, PathError>, + LeafDigest<C>: Clone + Default, + InnerDigest<C>: Clone, + { + let length = self.len(); + if index > 0 && index >= length { + return Err(PathError::IndexTooLarge { length }); + } + if index < self.data.starting_leaf_index() { + modified_path(self) + } else { + Ok(self.data.path_unchecked(index)) + } + } + + /// Appends a new `leaf` onto this branch. + #[inline] + pub fn push(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> bool + where + LeafDigest<C>: Default, + { + self.data.push(parameters, leaf) + } + + /// Appends a new `leaf_digest` onto this branch. + #[inline] + fn maybe_push_digest<F>(&mut self, parameters: &Parameters<C>, leaf_digest: F) -> Option<bool> + where + F: FnOnce() -> Option<LeafDigest<C>>, + LeafDigest<C>: Default, + { + self.data.maybe_push_digest(parameters, leaf_digest) + } + + /// Merges `self` into `base`. + /// + /// # Panics + /// + /// This method panics if the [`Tree::extend_digests`] method returns an `Err` variant because + /// the capacity invariant should have prevented the addition of leaves to this branch if they + /// would have exceeded the capacity limit of `base`. + #[inline] + fn merge<T>(self, parameters: &Parameters<C>, base: &mut T) + where + T: Tree<C>, + LeafDigest<C>: Default, + { + assert!( + base.extend_digests( + parameters, + Self::extract_leaves(self.base_contribution, self.data) + ) + .is_ok(), + "Should have been able to extend extracted leaves." + ); + } +} + +/// Merkle Tree Fork +#[derive(derivative::Derivative)] +#[derivative( + Debug(bound = "P::Weak: Debug, LeafDigest<C>: Debug, InnerDigest<C>: Debug, M: Debug"), + Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default") +)] +pub struct Fork<C, T, P = pointer::SingleThreaded, M = BTreeMap<C>> +where + C: Configuration + ?Sized, + T: Tree<C>, + P: PointerFamily<T>, + M: Default + InnerMap<C>, +{ + /// Base Merkle Tree + base: P::Weak, + + /// Branch Data + branch: Branch<C, M>, +} + +impl<C, T, P, M> Fork<C, T, P, M> +where + C: Configuration + ?Sized, + T: Tree<C>, + P: PointerFamily<T>, + M: Default + InnerMap<C>, +{ + /// Builds a new [`Fork`] off of `trunk` with the given `base_contribution` and `branch`. + #[inline] + fn build(trunk: &Trunk<C, T, P>, branch: Branch<C, M>) -> Self { + Self { + base: trunk.downgrade(), + branch, + } + } + + /// Builds a new [`Fork`] from `trunk`. + #[inline] + pub fn new(parameters: &Parameters<C>, trunk: &Trunk<C, T, P>) -> Self + where + LeafDigest<C>: Clone + Default, + InnerDigest<C>: Default, + { + let branch = Branch::new_unchecked(parameters, trunk.get(), Default::default()); + Self::build(trunk, branch) + } + + /// Builds a new [`Fork`] from `trunk` extended by `leaf_digests`, returning `None` if + /// appending `leaf_digests` would exceed the capacity of the `trunk`. + #[inline] + pub fn with_leaves( + parameters: &Parameters<C>, + trunk: &Trunk<C, T, P>, + leaf_digests: Vec<LeafDigest<C>>, + ) -> Option<Self> + where + LeafDigest<C>: Clone + Default, + InnerDigest<C>: Default, + { + let branch = Branch::new(parameters, trunk.get(), leaf_digests)?; + Some(Self::build(trunk, branch)) + } + /// Tries to attach this fork to a new `trunk`, returning `false` if `self` has too many leaves /// to fit in `trunk`. #[inline] @@ -426,12 +622,7 @@ where LeafDigest<C>: Clone + Default, InnerDigest<C>: Default, { - if !Self::try_rebase( - parameters, - trunk.borrow_base().as_ref(), - &mut self.base_contribution, - &mut self.branch, - ) { + if !self.branch.try_rebase(parameters, trunk.get()) { return false; } self.base = trunk.downgrade(); @@ -497,9 +688,9 @@ where T: WithProofs<C>, LeafDigest<C>: PartialEq, { - self.branch - .position(leaf_digest) - .or_else(move || P::upgrade(&self.base).and_then(move |b| b.position(leaf_digest))) + self.branch.data.position(leaf_digest).or_else(move || { + P::upgrade(&self.base).and_then(move |b| b.borrow().position(leaf_digest)) + }) } /// Returns the current (right-most) leaf of the tree. @@ -526,47 +717,11 @@ where LeafDigest<C>: Clone + Default, InnerDigest<C>: Clone, { - // TODO: Move this algorithm to `crate::merkle_tree::path`. - let length = self.len(); - if index > 0 && index >= length { - return Err(PathError::IndexTooLarge { length }); - } - if index < self.branch.starting_leaf_index() { - match P::upgrade(&self.base) { - Some(base) => { - let base_index = Node(index); - let base_path = base.path(parameters, base_index.0)?; - let fork_index = self.branch.starting_leaf_node(); - let mut fork_path = self.branch.path_unchecked(fork_index.0); - if !Node::are_siblings(&base_index, &fork_index) { - let matching_index = base_index - .parents() - .zip(fork_index.parents()) - .position(|(b, f)| Node::are_siblings(&b, &f)) - .unwrap(); - fork_path.inner_path.path[matching_index] = InnerPath::fold( - parameters, - fork_index, - fork_index.join_leaves( - parameters, - self.leaf_digest(fork_index.0) - .unwrap_or(&Default::default()), - &fork_path.sibling_digest, - ), - &fork_path.inner_path.path[..matching_index], - ); - fork_path.inner_path.path[..matching_index] - .clone_from_slice(&base_path.inner_path.path[..matching_index]); - } - fork_path.inner_path.leaf_index = base_path.inner_path.leaf_index; - fork_path.sibling_digest = base_path.sibling_digest; - Ok(fork_path) - } + self.branch + .path(index, move |branch| match P::upgrade(&self.base) { + Some(base) => branch.modified_path_unchecked(parameters, index, base.borrow()), _ => Err(PathError::MissingPath), - } - } else { - Ok(self.branch.path_unchecked(index)) - } + }) } /// Appends a new `leaf` onto this fork. @@ -587,7 +742,11 @@ where /// Returns `None` if this fork has been detached from its trunk. Use [`attach`](Self::attach) /// to re-associate a trunk to this fork. #[inline] - fn maybe_push_digest<F>(&mut self, parameters: &Parameters<C>, leaf_digest: F) -> Option<bool> + pub fn maybe_push_digest<F>( + &mut self, + parameters: &Parameters<C>, + leaf_digest: F, + ) -> Option<bool> where F: FnOnce() -> Option<LeafDigest<C>>, LeafDigest<C>: Default, @@ -598,29 +757,45 @@ where } /// Forked Tree -#[derive(derivative::Derivative)] -#[derivative(Debug( - bound = "P::Strong: Debug, P::Weak: Debug, LeafDigest<C>: Debug, InnerDigest<C>: Debug, M: Debug" -))] -pub struct ForkedTree<C, T, P = pointer::SingleThreaded, M = BTreeMap<C>> +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = r" + T: Deserialize<'de>, + LeafDigest<C>: Deserialize<'de>, + InnerDigest<C>: Deserialize<'de>, + M: Deserialize<'de>, + ", + serialize = r" + T: Serialize, + LeafDigest<C>: Serialize, + InnerDigest<C>: Serialize, + M: Serialize, + ", + ), + crate = "manta_util::serde", + deny_unknown_fields, + ) +)] +pub struct ForkedTree<C, T, M = BTreeMap<C>> where C: Configuration + ?Sized, T: Tree<C>, - P: PointerFamily<T>, M: Default + InnerMap<C>, { - /// Base Trunk - trunk: Trunk<C, T, P>, + /// Base Tree + base: T, - /// Fork - fork: Fork<C, T, P, M>, + /// Branch Data + branch: Branch<C, M>, } -impl<C, T, P, M> ForkedTree<C, T, P, M> +impl<C, T, M> ForkedTree<C, T, M> where C: Configuration + ?Sized, T: Tree<C>, - P: PointerFamily<T>, M: Default + InnerMap<C>, { /// Builds a new [`ForkedTree`] for `tree`. @@ -630,73 +805,121 @@ where LeafDigest<C>: Clone + Default, InnerDigest<C>: Default, { - let trunk = Trunk::new(tree); - Self { - fork: trunk.fork(parameters), - trunk, - } + let branch = Branch::new_unchecked(parameters, &tree, Default::default()); + Self { base: tree, branch } } - /// Returns a shared reference to the trunk of this tree. + /// Computes the length of this forked tree. #[inline] - pub fn trunk(&self) -> &Trunk<C, T, P> { - &self.trunk + pub fn len(&self) -> usize { + self.branch.len() } - /// Returns a shared reference to the fork of this tree. + /// Returns `true` if this forked tree is empty. #[inline] - pub fn fork(&self) -> &Fork<C, T, P, M> { - &self.fork + pub fn is_empty(&self) -> bool { + self.branch.is_empty() } - /// Returns a mutable reference to the trunk of this tree. + /// Returns the current root of this forked tree. #[inline] - pub fn fork_mut(&mut self) -> &mut Fork<C, T, P, M> { - &mut self.fork + pub fn root(&self) -> &InnerDigest<C> { + self.branch.root() } - /// Resets the fork of this tree back to the trunk. + /// Returns the leaf digest at the given `index` in the forked tree. #[inline] - pub fn reset_fork(&mut self, parameters: &Parameters<C>) + pub fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>> { + self.branch.leaf_digest(index) + } + + /// Returns the position of `leaf_digest` in the forked tree. + #[inline] + pub fn position(&self, leaf_digest: &LeafDigest<C>) -> Option<usize> + where + T: WithProofs<C>, + LeafDigest<C>: PartialEq, + { + self.branch + .data + .position(leaf_digest) + .or_else(move || self.base.position(leaf_digest)) + } + + /// Returns the current (right-most) leaf of the forked tree. + #[inline] + pub fn current_leaf(&self) -> Option<&LeafDigest<C>> { + self.branch.current_leaf() + } + + /// Returns the current (right-most) path of the forked tree. + #[inline] + pub fn current_path(&self) -> CurrentPath<C> where LeafDigest<C>: Clone + Default, - InnerDigest<C>: Default, + InnerDigest<C>: Clone + Default + PartialEq, + { + self.branch.current_path() + } + + /// Returns the path at the given `index` in the forked tree. + #[inline] + pub fn path(&self, parameters: &Parameters<C>, index: usize) -> Result<Path<C>, PathError> + where + T: WithProofs<C>, + LeafDigest<C>: Clone + Default, + InnerDigest<C>: Clone, + { + self.branch.path(index, |branch| { + branch.modified_path_unchecked(parameters, index, &self.base) + }) + } + + /// Appends a new `leaf` onto this forked tree. + #[inline] + pub fn push(&mut self, parameters: &Parameters<C>, leaf: &Leaf<C>) -> bool + where + LeafDigest<C>: Default, + { + self.branch.push(parameters, leaf) + } + + /// Appends a new `leaf_digest` onto this forked tree. + #[inline] + fn maybe_push_digest<F>(&mut self, parameters: &Parameters<C>, leaf_digest: F) -> Option<bool> + where + F: FnOnce() -> Option<LeafDigest<C>>, + LeafDigest<C>: Default, { - self.fork = self.trunk.fork(parameters); + self.branch.maybe_push_digest(parameters, leaf_digest) } - /// Merges the fork of this tree back into the trunk returning `true` if it was successful. + /// Resets the fork of the base tree back to the trunk. #[inline] - pub fn merge_fork(&mut self, parameters: &Parameters<C>) -> bool + pub fn reset_fork(&mut self, parameters: &Parameters<C>) where LeafDigest<C>: Clone + Default, InnerDigest<C>: Default, { - if let Err(fork) = self.trunk.merge(parameters, mem::take(&mut self.fork)) { - self.fork = fork; - false - } else { - self.reset_fork(parameters); - true - } + self.branch = Branch::new_unchecked(parameters, &self.base, Default::default()); } - /// Converts `self` back into its inner [`Tree`]. - /// - /// # Safety - /// - /// This method automatically detaches all of the forks associated to the trunk of this tree. + /// Merges the fork of the base tree back into the trunk. #[inline] - pub fn into_tree(self) -> T { - self.trunk.into_tree() + pub fn merge_fork(&mut self, parameters: &Parameters<C>) + where + LeafDigest<C>: Clone + Default, + InnerDigest<C>: Default, + { + mem::take(&mut self.branch).merge(parameters, &mut self.base); + self.reset_fork(parameters) } } -impl<C, T, P, M> Tree<C> for ForkedTree<C, T, P, M> +impl<C, T, M> Tree<C> for ForkedTree<C, T, M> where C: Configuration + ?Sized, T: Tree<C>, - P: PointerFamily<T>, M: Default + InnerMap<C>, LeafDigest<C>: Clone + Default, InnerDigest<C>: Clone + Default + PartialEq, @@ -708,23 +931,23 @@ where #[inline] fn len(&self) -> usize { - self.fork.len() + self.len() } #[inline] fn current_leaf(&self) -> Option<&LeafDigest<C>> { - self.fork.current_leaf() + self.current_leaf() } #[inline] fn root(&self) -> &Root<C> { - self.fork.root() + self.root() } #[inline] fn current_path(&self, parameters: &Parameters<C>) -> CurrentPath<C> { let _ = parameters; - self.fork.current_path() + self.current_path() } #[inline] @@ -732,27 +955,26 @@ where where F: FnOnce() -> Option<LeafDigest<C>>, { - self.fork.maybe_push_digest(parameters, leaf_digest) + self.maybe_push_digest(parameters, leaf_digest) } } -impl<C, T, P, M> WithProofs<C> for ForkedTree<C, T, P, M> +impl<C, T, M> WithProofs<C> for ForkedTree<C, T, M> where C: Configuration + ?Sized, T: Tree<C> + WithProofs<C>, - P: PointerFamily<T>, M: Default + InnerMap<C>, LeafDigest<C>: Clone + Default + PartialEq, InnerDigest<C>: Clone + Default, { #[inline] fn leaf_digest(&self, index: usize) -> Option<&LeafDigest<C>> { - self.fork.leaf_digest(index) + self.leaf_digest(index) } #[inline] fn position(&self, leaf_digest: &LeafDigest<C>) -> Option<usize> { - self.fork.position(leaf_digest) + self.position(leaf_digest) } #[inline] @@ -764,11 +986,11 @@ where where F: FnOnce() -> Option<LeafDigest<C>>, { - self.fork.maybe_push_digest(parameters, leaf_digest) + self.maybe_push_digest(parameters, leaf_digest) } #[inline] fn path(&self, parameters: &Parameters<C>, index: usize) -> Result<Path<C>, PathError> { - self.fork.path(parameters, index) + self.path(parameters, index) } } diff --git a/manta-crypto/src/merkle_tree/tree.rs b/manta-crypto/src/merkle_tree/tree.rs index 8534e86e6..cc0958b8a 100644 --- a/manta-crypto/src/merkle_tree/tree.rs +++ b/manta-crypto/src/merkle_tree/tree.rs @@ -1152,11 +1152,10 @@ where } } -impl<C, T, P, M> Rollback for MerkleTree<C, ForkedTree<C, T, P, M>> +impl<C, T, M> Rollback for MerkleTree<C, ForkedTree<C, T, M>> where C: Configuration + ?Sized, T: Tree<C>, - P: PointerFamily<T>, M: Default + InnerMap<C>, LeafDigest<C>: Clone + Default, InnerDigest<C>: Clone + Default + PartialEq, diff --git a/manta-pay/src/signer/base.rs b/manta-pay/src/signer/base.rs index bd499c881..73368ce7d 100644 --- a/manta-pay/src/signer/base.rs +++ b/manta-pay/src/signer/base.rs @@ -29,7 +29,6 @@ use manta_accounting::{ wallet::{self, signer::AssetMapKey}, }; use manta_crypto::{key::KeyDerivationFunction, merkle_tree}; -use manta_util::pointer::ThreadSafe; /// Hierarchical Key Derivation Function pub struct HierarchicalKeyDerivationFunction; @@ -56,7 +55,6 @@ pub type UtxoSet = merkle_tree::forest::TreeArrayMerkleForest< merkle_tree::fork::ForkedTree< MerkleTreeConfiguration, merkle_tree::full::Full<MerkleTreeConfiguration>, - ThreadSafe, >, { MerkleTreeConfiguration::FOREST_WIDTH }, >; diff --git a/manta-util/src/pointer.rs b/manta-util/src/pointer.rs index b88d5cb0d..950e3caf6 100644 --- a/manta-util/src/pointer.rs +++ b/manta-util/src/pointer.rs @@ -16,7 +16,7 @@ //! Pointer Utilities -use core::{borrow::Borrow, ops::Deref}; +use core::borrow::Borrow; #[cfg(feature = "alloc")] #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] @@ -29,7 +29,7 @@ pub use alloc::{rc::Rc, sync::Arc}; /// Pointer Family pub trait PointerFamily<T> { /// Strong Pointer - type Strong: AsRef<T> + Borrow<T> + Deref<Target = T>; + type Strong: Borrow<T>; /// Weak Pointer type Weak: Default; @@ -109,3 +109,40 @@ impl_pointer_family!(SingleThreaded, Rc, WeakRc); pub struct ThreadSafe; impl_pointer_family!(ThreadSafe, Arc, WeakArc); + +/// No-Pointer Pointer Family +/// +/// This is the pointer family for a raw value, and does not do any reference counting. +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct NoPointer; + +impl<T> PointerFamily<T> for NoPointer { + type Strong = T; + type Weak = (); + + #[inline] + fn new(base: T) -> Self::Strong { + base + } + + #[inline] + fn claim(strong: Self::Strong) -> T { + strong + } + + #[inline] + fn downgrade(strong: &Self::Strong) -> Self::Weak { + let _ = strong; + } + + #[inline] + fn upgrade(weak: &Self::Weak) -> Option<Self::Strong> { + let _ = weak; + None + } + + #[inline] + fn strong_ptr_eq(lhs: &Self::Strong, rhs: &Self::Strong) -> bool { + core::ptr::eq(lhs, rhs) + } +} From 6a3f42bafeb391e906eaa73651c0bf11aa1eac8f Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 15 Feb 2022 12:46:12 -0700 Subject: [PATCH 249/275] feat: add password hashing, refactor encryption for aead support --- manta-accounting/src/transfer/mod.rs | 2 +- manta-accounting/src/wallet/signer.rs | 2 +- manta-crypto/src/encryption/authenticated.rs | 20 +++ .../{encryption.rs => encryption/hybrid.rs} | 94 +------------ manta-crypto/src/encryption/mod.rs | 21 +++ manta-crypto/src/encryption/symmetric.rs | 110 ++++++++++++++++ manta-crypto/src/lib.rs | 1 + manta-crypto/src/password.rs | 124 ++++++++++++++++++ manta-pay/src/config.rs | 4 +- manta-pay/src/crypto/encryption.rs | 4 +- 10 files changed, 288 insertions(+), 94 deletions(-) create mode 100644 manta-crypto/src/encryption/authenticated.rs rename manta-crypto/src/{encryption.rs => encryption/hybrid.rs} (79%) create mode 100644 manta-crypto/src/encryption/mod.rs create mode 100644 manta-crypto/src/encryption/symmetric.rs create mode 100644 manta-crypto/src/password.rs diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index fe5a06476..05afb83ce 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -26,7 +26,7 @@ use manta_crypto::{ Add, Allocator, Constant, ConstraintSystem, Derived, Equal, ProofSystem, ProofSystemInput, Public, Secret, ValueSource, Variable, }, - encryption::{DecryptedMessage, EncryptedMessage, HybridPublicKeyEncryptionScheme}, + encryption::hybrid::{DecryptedMessage, EncryptedMessage, HybridPublicKeyEncryptionScheme}, hash::BinaryHashFunction, key::KeyAgreementScheme, rand::{CryptoRng, RngCore, Sample}, diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 2a9710421..3c81792fe 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -43,7 +43,7 @@ use alloc::{vec, vec::Vec}; use core::{convert::Infallible, fmt::Debug, hash::Hash}; use manta_crypto::{ accumulator::{Accumulator, ExactSizeAccumulator, OptimizedAccumulator}, - encryption::DecryptedMessage, + encryption::hybrid::DecryptedMessage, rand::{CryptoRng, FromEntropy, Rand, RngCore}, }; use manta_util::{ diff --git a/manta-crypto/src/encryption/authenticated.rs b/manta-crypto/src/encryption/authenticated.rs new file mode 100644 index 000000000..bbf7c9f71 --- /dev/null +++ b/manta-crypto/src/encryption/authenticated.rs @@ -0,0 +1,20 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Authenticated Encryption + +// TODO: Write authenticated encryption scheme. +// TODO: Write `online` authenticated encryption scheme. diff --git a/manta-crypto/src/encryption.rs b/manta-crypto/src/encryption/hybrid.rs similarity index 79% rename from manta-crypto/src/encryption.rs rename to manta-crypto/src/encryption/hybrid.rs index ba7623399..8f004d9da 100644 --- a/manta-crypto/src/encryption.rs +++ b/manta-crypto/src/encryption/hybrid.rs @@ -14,82 +14,17 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Encryption Primitives +//! Hybrid Encryption -use crate::key::{KeyAgreementScheme, KeyDerivationFunction}; +use crate::{ + encryption::symmetric::SymmetricKeyEncryptionScheme, + key::{KeyAgreementScheme, KeyDerivationFunction}, +}; use core::{fmt::Debug, hash::Hash, marker::PhantomData}; #[cfg(feature = "serde")] use manta_util::serde::{Deserialize, Serialize}; -/// Symmetric Key Encryption Scheme -/// -/// # Specification -/// -/// All implementations of this trait must adhere to the following properties: -/// -/// 1. **Invertibility**: For all possible inputs, the following function returns `true`: -/// -/// ```text -/// fn invertibility(key: Key, plaintext: Plaintext) -> bool { -/// matches!(decrypt(key, &encrypt(key, plaintext.clone())), Some(p) if p == plaintext) -/// } -/// ``` -pub trait SymmetricKeyEncryptionScheme { - /// Encryption/Decryption Key Type - type Key; - - /// Plaintext Type - type Plaintext; - - /// Ciphertext Type - type Ciphertext; - - /// Encrypts `plaintext` using `key`. - fn encrypt(key: Self::Key, plaintext: Self::Plaintext) -> Self::Ciphertext; - - /// Tries to decrypt `ciphertext` using `key`. - fn decrypt(key: Self::Key, ciphertext: &Self::Ciphertext) -> Option<Self::Plaintext>; -} - -/// Symmetric Encryption -pub mod symmetric { - use super::*; - - /// Mapped Symmetric Encryption Scheme - #[cfg_attr( - feature = "serde", - derive(Deserialize, Serialize), - serde(crate = "manta_util::serde") - )] - #[derive(derivative::Derivative)] - #[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct Map<S, P = <S as SymmetricKeyEncryptionScheme>::Plaintext>(PhantomData<(S, P)>) - where - S: SymmetricKeyEncryptionScheme, - P: Into<S::Plaintext> + TryFrom<S::Plaintext>; - - impl<S, P> SymmetricKeyEncryptionScheme for Map<S, P> - where - S: SymmetricKeyEncryptionScheme, - P: Into<S::Plaintext> + TryFrom<S::Plaintext>, - { - type Key = S::Key; - type Plaintext = P; - type Ciphertext = S::Ciphertext; - - #[inline] - fn encrypt(key: Self::Key, plaintext: Self::Plaintext) -> Self::Ciphertext { - S::encrypt(key, plaintext.into()) - } - - #[inline] - fn decrypt(key: Self::Key, ciphertext: &Self::Ciphertext) -> Option<Self::Plaintext> { - S::decrypt(key, ciphertext).and_then(move |p| p.try_into().ok()) - } - } -} - /// Hybrid Public Key Encryption Scheme pub trait HybridPublicKeyEncryptionScheme: SymmetricKeyEncryptionScheme { /// Key Agreement Scheme Type @@ -369,27 +304,10 @@ where pub mod test { use super::*; - /// Tests if symmetric encryption of `plaintext` using `key` returns the same plaintext on - /// decryption. - #[inline] - pub fn symmetric_encryption<S>(key: S::Key, plaintext: S::Plaintext) - where - S: SymmetricKeyEncryptionScheme, - S::Key: Clone, - S::Plaintext: Clone + Debug + PartialEq, - { - assert_eq!( - S::decrypt(key.clone(), &S::encrypt(key, plaintext.clone())) - .expect("Decryption of encrypted message should have succeeded."), - plaintext, - "Plaintext should have matched decrypted-encrypted plaintext." - ) - } - /// Tests if hybrid encryption of `plaintext` using `public_key` returns the same plaintext on /// decryption. #[inline] - pub fn hybrid_encryption<H>( + pub fn encryption<H>( parameters: &H::KeyAgreementScheme, secret_key: &SecretKey<H>, ephemeral_secret_key: &SecretKey<H>, diff --git a/manta-crypto/src/encryption/mod.rs b/manta-crypto/src/encryption/mod.rs new file mode 100644 index 000000000..20fef51d4 --- /dev/null +++ b/manta-crypto/src/encryption/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 <http://www.gnu.org/licenses/>. + +//! Encryption Primitives + +// TODO: pub mod authenticated; +pub mod hybrid; +pub mod symmetric; diff --git a/manta-crypto/src/encryption/symmetric.rs b/manta-crypto/src/encryption/symmetric.rs new file mode 100644 index 000000000..7f46c57af --- /dev/null +++ b/manta-crypto/src/encryption/symmetric.rs @@ -0,0 +1,110 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Symmetric Encryption + +use core::marker::PhantomData; + +#[cfg(feature = "serde")] +use manta_util::serde::{Deserialize, Serialize}; + +/// Symmetric Key Encryption Scheme +/// +/// # Specification +/// +/// All implementations of this trait must adhere to the following properties: +/// +/// 1. **Invertibility**: For all possible inputs, the following function returns `true`: +/// +/// ```text +/// fn invertibility(key: Key, plaintext: Plaintext) -> bool { +/// matches!(decrypt(key, &encrypt(key, plaintext.clone())), Some(p) if p == plaintext) +/// } +/// ``` +pub trait SymmetricKeyEncryptionScheme { + /// Encryption/Decryption Key Type + type Key; + + /// Plaintext Type + type Plaintext; + + /// Ciphertext Type + type Ciphertext; + + /// Encrypts `plaintext` using `key`. + fn encrypt(key: Self::Key, plaintext: Self::Plaintext) -> Self::Ciphertext; + + /// Tries to decrypt `ciphertext` using `key`. + fn decrypt(key: Self::Key, ciphertext: &Self::Ciphertext) -> Option<Self::Plaintext>; +} + +/// Mapped Symmetric Encryption Scheme +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde") +)] +#[derive(derivative::Derivative)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Map<S, P = <S as SymmetricKeyEncryptionScheme>::Plaintext>(PhantomData<(S, P)>) +where + S: SymmetricKeyEncryptionScheme, + P: Into<S::Plaintext> + TryFrom<S::Plaintext>; + +impl<S, P> SymmetricKeyEncryptionScheme for Map<S, P> +where + S: SymmetricKeyEncryptionScheme, + P: Into<S::Plaintext> + TryFrom<S::Plaintext>, +{ + type Key = S::Key; + type Plaintext = P; + type Ciphertext = S::Ciphertext; + + #[inline] + fn encrypt(key: Self::Key, plaintext: Self::Plaintext) -> Self::Ciphertext { + S::encrypt(key, plaintext.into()) + } + + #[inline] + fn decrypt(key: Self::Key, ciphertext: &Self::Ciphertext) -> Option<Self::Plaintext> { + S::decrypt(key, ciphertext).and_then(move |p| p.try_into().ok()) + } +} + +/// Testing Framework +#[cfg(feature = "test")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] +pub mod test { + use super::*; + use core::fmt::Debug; + + /// Tests if symmetric encryption of `plaintext` using `key` returns the same plaintext on + /// decryption. + #[inline] + pub fn encryption<S>(key: S::Key, plaintext: S::Plaintext) + where + S: SymmetricKeyEncryptionScheme, + S::Key: Clone, + S::Plaintext: Clone + Debug + PartialEq, + { + assert_eq!( + S::decrypt(key.clone(), &S::encrypt(key, plaintext.clone())) + .expect("Decryption of encrypted message should have succeeded."), + plaintext, + "Plaintext should have matched decrypted-encrypted plaintext." + ) + } +} diff --git a/manta-crypto/src/lib.rs b/manta-crypto/src/lib.rs index 13c00ab2f..46f0a812a 100644 --- a/manta-crypto/src/lib.rs +++ b/manta-crypto/src/lib.rs @@ -31,4 +31,5 @@ pub mod encryption; pub mod hash; pub mod key; pub mod merkle_tree; +pub mod password; pub mod rand; diff --git a/manta-crypto/src/password.rs b/manta-crypto/src/password.rs new file mode 100644 index 000000000..d4d5cadf1 --- /dev/null +++ b/manta-crypto/src/password.rs @@ -0,0 +1,124 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Password Hashing Primitives + +use core::{fmt::Debug, hash::Hash}; + +#[cfg(feature = "serde")] +use manta_util::serde::{Deserialize, Serialize}; + +/// Password Hasher +pub trait Hasher { + /// Password Type + /// + /// In general, this type can be `[u8]` but if the password must be a certain length or must + /// have a particular format, this should be a custom type which parses or validates the input + /// password. + type Password: ?Sized; + + /// Salt Type + type Salt; + + /// Hash Type + type Hash: PartialEq; + + /// Hashes `password` with the given `salt`. + fn hash(&self, salt: &Self::Salt, password: &Self::Password) -> Self::Hash; + + /// Hashes `password` with the given `salt`, and checks that its output hash is equal to `hash`. + #[inline] + fn verify(&self, salt: &Self::Salt, hash: &Self::Hash, password: &Self::Password) -> bool { + &self.hash(salt, password) == hash + } +} + +/// Password Hash +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "H: Clone, H::Salt: Clone, H::Hash: Clone"), + Copy(bound = "H: Copy, H::Salt: Copy, H::Hash: Copy"), + Debug(bound = "H: Debug, H::Salt: Debug, H::Hash: Debug"), + Eq(bound = "H: Eq, H::Salt: Eq, H::Hash: Eq"), + Hash(bound = "H: Hash, H::Salt: Hash, H::Hash: Hash"), + PartialEq(bound = "H: PartialEq, H::Salt: PartialEq, H::Hash: PartialEq") +)] +pub struct PasswordHash<H> +where + H: Hasher, +{ + /// Hasher + hasher: H, + + /// Hash Salt + salt: H::Salt, + + /// Output Hash + hash: H::Hash, +} + +impl<H> PasswordHash<H> +where + H: Hasher, +{ + /// Hashes `password` with the given `hasher` and `salt`. + #[inline] + pub fn new(hasher: H, salt: H::Salt, password: &H::Password) -> Self { + Self { + hash: hasher.hash(&salt, password), + hasher, + salt, + } + } + + /// Hashes `password` with the given `salt` using the default [`Hasher`]. + #[inline] + pub fn from_default(salt: H::Salt, password: &H::Password) -> Self + where + H: Default, + { + Self::new(H::default(), salt, password) + } + + /// Verifies that `password` hashes to the same value as the hash stored in `self`. + #[inline] + pub fn verify(&self, password: &H::Password) -> bool { + self.hasher.verify(&self.salt, &self.hash, password) + } + + /// Returns a shared reference to the [`Hasher`] used to generate `self`. + #[inline] + pub fn hasher(&self) -> &H { + &self.hasher + } + + /// Returns a shared reference to the salt used to generate `self`. + #[inline] + pub fn salt(&self) -> &H::Salt { + &self.salt + } + + /// Returns a shared reference to the hash stored in `self`. + #[inline] + pub fn hash(&self) -> &H::Hash { + &self.hash + } +} diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index a1ccd55ae..8cc6898ca 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -593,7 +593,7 @@ impl ProofSystemInput<Group> for ProofSystem { } /// Note Encryption Scheme -pub type NoteEncryptionScheme = encryption::Hybrid< +pub type NoteEncryptionScheme = encryption::hybrid::Hybrid< KeyAgreementScheme, encryption::symmetric::Map< AesGcm<{ Asset::SIZE }, { aes::ciphertext_size(Asset::SIZE) }>, @@ -607,7 +607,7 @@ pub type NoteEncryptionScheme = encryption::Hybrid< /// Asset Ciphertext pub type Ciphertext = - <NoteEncryptionScheme as encryption::SymmetricKeyEncryptionScheme>::Ciphertext; + <NoteEncryptionScheme as encryption::symmetric::SymmetricKeyEncryptionScheme>::Ciphertext; /// Base Configuration pub struct Config; diff --git a/manta-pay/src/crypto/encryption.rs b/manta-pay/src/crypto/encryption.rs index 658fb8d9a..7fc610fa0 100644 --- a/manta-pay/src/crypto/encryption.rs +++ b/manta-pay/src/crypto/encryption.rs @@ -24,7 +24,7 @@ pub mod aes { aead::{Aead, NewAead}, Aes256Gcm, Nonce, }; - use manta_crypto::encryption::SymmetricKeyEncryptionScheme; + use manta_crypto::encryption::symmetric::SymmetricKeyEncryptionScheme; use manta_util::Array; /// AES-GCM Authentication Tag Size @@ -93,7 +93,7 @@ pub mod aes { rng.fill_bytes(&mut key); let mut plaintext = [0; Asset::SIZE]; rng.fill_bytes(&mut plaintext); - encryption::test::symmetric_encryption::< + encryption::symmetric::test::encryption::< AesGcm<{ Asset::SIZE }, { ciphertext_size(Asset::SIZE) }>, >(key, Array(plaintext)); } From fc1e203e10c841e0e53b71c3b34146ad7f18cf53 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 15 Feb 2022 13:24:55 -0700 Subject: [PATCH 250/275] feat: add SIV for some extra nonce protection in AES --- manta-pay/Cargo.toml | 4 ++-- manta-pay/src/config.rs | 4 ++-- manta-pay/src/crypto/encryption.rs | 32 +++++++++++++++++------------- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index ed5d49c1e..c2c8100b9 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -91,7 +91,7 @@ wallet = ["bip32", "manta-crypto/getrandom", "std"] websocket = ["serde_json", "tungstenite"] [dependencies] -aes-gcm = { version = "0.9.4", default-features = false, features = ["aes", "alloc"] } +aes-gcm-siv = { version = "0.10.3", default-features = false, features = ["aes", "alloc"] } ark-bls12-381 = { version = "0.3.0", optional = true, default-features = false, features = ["curve"] } ark-ec = { version = "0.3.0", optional = true, default-features = false } ark-ed-on-bls12-381 = { version = "0.3.0", optional = true, default-features = false, features = ["r1cs"] } @@ -117,7 +117,7 @@ scale-codec = { package = "parity-scale-codec", version = "2.3.1", optional = tr scale-info = { version = "1.0.0", optional = true, default-features = false, features = ["derive"] } serde_json = { version = "1.0.79", optional = true, default-features = false } tempfile = { version = "3.2.0", optional = true, default-features = false } -tungstenite = { version = "0.16.0", optional = true, default-features = false, features = ["native-tls"] } +tungstenite = { version = "0.17.0", optional = true, default-features = false, features = ["native-tls"] } # TODO: zk-garage-plonk = { package = "plonk", git = "https://github.com/zk-garage/plonk", optional = true, default-features = false } [dev-dependencies] diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 8cc6898ca..3e0ec1da1 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -19,7 +19,7 @@ use crate::crypto::{ constraint::arkworks::{groth16, Boolean, Fp, FpVar, R1CS}, ecc, - encryption::aes::{self, AesGcm}, + encryption::aes::{self, AesGcmSiv}, hash::poseidon, key::Blake2sKdf, }; @@ -596,7 +596,7 @@ impl ProofSystemInput<Group> for ProofSystem { pub type NoteEncryptionScheme = encryption::hybrid::Hybrid< KeyAgreementScheme, encryption::symmetric::Map< - AesGcm<{ Asset::SIZE }, { aes::ciphertext_size(Asset::SIZE) }>, + AesGcmSiv<{ Asset::SIZE }, { aes::ciphertext_size(Asset::SIZE) }>, Asset, >, key::kdf::FromByteVector< diff --git a/manta-pay/src/crypto/encryption.rs b/manta-pay/src/crypto/encryption.rs index 7fc610fa0..49dfdcf59 100644 --- a/manta-pay/src/crypto/encryption.rs +++ b/manta-pay/src/crypto/encryption.rs @@ -20,16 +20,16 @@ /// AES Encryption Implementation pub mod aes { - use aes_gcm::{ + use aes_gcm_siv::{ aead::{Aead, NewAead}, - Aes256Gcm, Nonce, + Aes256GcmSiv, Nonce, }; use manta_crypto::encryption::symmetric::SymmetricKeyEncryptionScheme; use manta_util::Array; /// AES-GCM Authentication Tag Size #[allow(clippy::cast_possible_truncation)] // NOTE: GCM Tag Size should be smaller than `2^32`. - const TAG_SIZE: usize = (aes_gcm::C_MAX - aes_gcm::P_MAX) as usize; + const TAG_SIZE: usize = (aes_gcm_siv::C_MAX - aes_gcm_siv::P_MAX) as usize; /// Computes the size of the ciphertext corresponding to a plaintext of the given /// `plaintext_size`. @@ -38,25 +38,30 @@ pub mod aes { plaintext_size + TAG_SIZE } - /// AES Galois Counter Mode + /// AES Galois Counter Mode with Synthetic Initialization Vectors #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct AesGcm<const P: usize, const C: usize>; + pub struct AesGcmSiv<const P: usize, const C: usize>; - impl<const P: usize, const C: usize> AesGcm<P, C> { - /// Encryption/Decryption Nonce - const NONCE: &'static [u8] = b"manta rocks!"; + impl<const P: usize, const C: usize> AesGcmSiv<P, C> { + /// Fixed Random Nonce + /// + /// # Safety + /// + /// Using a fixed nonce is safe under the assumption that the encryption keys are only used + /// once. We add SIV for some extra safety which makes the effective nonce dependent on the + /// plaintext. + const NONCE: &'static [u8] = b"random nonce"; } - impl<const P: usize, const C: usize> SymmetricKeyEncryptionScheme for AesGcm<P, C> { + impl<const P: usize, const C: usize> SymmetricKeyEncryptionScheme for AesGcmSiv<P, C> { type Key = [u8; 32]; type Plaintext = Array<u8, P>; type Ciphertext = Array<u8, C>; #[inline] fn encrypt(key: Self::Key, plaintext: Self::Plaintext) -> Self::Ciphertext { - // SAFETY: Using a deterministic nonce is ok since we never reuse keys. Array::from_unchecked( - Aes256Gcm::new_from_slice(&key) + Aes256GcmSiv::new_from_slice(&key) .expect("The key has the correct size.") .encrypt(Nonce::from_slice(Self::NONCE), plaintext.as_ref()) .expect("Symmetric encryption is not allowed to fail."), @@ -65,8 +70,7 @@ pub mod aes { #[inline] fn decrypt(key: Self::Key, ciphertext: &Self::Ciphertext) -> Option<Self::Plaintext> { - // SAFETY: Using a deterministic nonce is ok since we never reuse keys. - Aes256Gcm::new_from_slice(&key) + Aes256GcmSiv::new_from_slice(&key) .expect("The key has the correct size.") .decrypt(Nonce::from_slice(Self::NONCE), ciphertext.as_ref()) .ok() @@ -94,7 +98,7 @@ pub mod aes { let mut plaintext = [0; Asset::SIZE]; rng.fill_bytes(&mut plaintext); encryption::symmetric::test::encryption::< - AesGcm<{ Asset::SIZE }, { ciphertext_size(Asset::SIZE) }>, + AesGcmSiv<{ Asset::SIZE }, { ciphertext_size(Asset::SIZE) }>, >(key, Array(plaintext)); } } From 6b66d6e61e68e7eedeb0cf3faebe5fe3e770dac7 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 15 Feb 2022 14:00:06 -0700 Subject: [PATCH 251/275] feat: move save/load API to fs --- manta-accounting/src/fs/mod.rs | 48 ++++++++++++++- manta-accounting/src/fs/serde.rs | 4 +- manta-accounting/src/wallet/signer.rs | 86 ++------------------------- 3 files changed, 56 insertions(+), 82 deletions(-) diff --git a/manta-accounting/src/fs/mod.rs b/manta-accounting/src/fs/mod.rs index 3df2a27d6..ff6a934a8 100644 --- a/manta-accounting/src/fs/mod.rs +++ b/manta-accounting/src/fs/mod.rs @@ -19,6 +19,14 @@ use alloc::{boxed::Box, vec::Vec}; use core::{cmp, hash::Hash, marker::PhantomData}; +#[cfg(feature = "serde")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] +use core::fmt::{Debug, Display}; + +#[cfg(feature = "serde")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] +pub use serde::{de::Error as LoadError, ser::Error as SaveError}; + #[cfg(feature = "serde")] #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] pub mod serde; @@ -299,6 +307,42 @@ pub trait File: Sized { /// Reads a [`Block`] from `self` after decrypting it, returning `None` if there are no more /// blocks in the file. fn read(&mut self) -> Result<Option<Block>, Self::Error>; + + /// Saves `value` to `path` by serializing and encrypting with `password`. + #[cfg(feature = "serde")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] + #[inline] + fn save<P, T>(path: P, password: &[u8], value: T) -> Result<(), SaveError<Self>> + where + Self::Error: Debug + Display, + P: AsRef<Self::Path>, + T: serde::Serialize, + { + let mut file = Self::options() + .create(true) + .truncate(true) + .write(true) + .open(path, password) + .map_err(SaveError::Io)?; + value.serialize(&mut serde::Serializer::new(&mut file)) + } + + /// Loads a value of type `T` from `path` by deserializing and decrypting with `password`. + #[cfg(feature = "serde")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] + #[inline] + fn load<P, T>(path: P, password: &[u8]) -> Result<T, LoadError<Self>> + where + Self::Error: Debug + Display, + P: AsRef<Self::Path>, + T: serde::DeserializeOwned, + { + let mut file = Self::options() + .read(true) + .open(path, password) + .map_err(LoadError::Io)?; + T::deserialize(&mut serde::Deserializer::new(&mut file)) + } } /// Cocoon Encrypted File System Adapter @@ -352,7 +396,9 @@ pub mod cocoon { Ok(Self { file: fs::OpenOptions::from(options).open(path)?, cocoon: { - // FIXME: Choose a better seed. Should we hash the password for this? + // FIXME: Use a random seed here. Ideally, we want to rewrite `cocoon` for + // better security and flexibility and move to a streaming encryption + // protocol. MiniCocoon::from_password(password, &[0; 32]) }, }) diff --git a/manta-accounting/src/fs/serde.rs b/manta-accounting/src/fs/serde.rs index f0ca2a036..c405b4ae6 100644 --- a/manta-accounting/src/fs/serde.rs +++ b/manta-accounting/src/fs/serde.rs @@ -35,11 +35,13 @@ use manta_util::{ SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple, SerializeTupleStruct, SerializeTupleVariant, }, - Serialize, }, FromBytes, IntoBytes, }; +#[doc(inline)] +pub use manta_util::serde::{de::DeserializeOwned, Deserialize, Serialize}; + /// Serialization Module pub mod ser { use super::*; diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 3c81792fe..fd14d516d 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -57,21 +57,6 @@ use manta_util::{ #[cfg(feature = "serde")] use manta_util::serde::{Deserialize, Serialize}; -#[cfg(all(feature = "fs", feature = "serde"))] -use { - crate::fs::{ - serde::{Deserializer, Serializer}, - File, - }, - core::fmt::Display, - manta_util::serde::de::DeserializeOwned, -}; - -#[cfg(all(feature = "fs", feature = "serde"))] -#[cfg_attr(doc_cfg, doc(cfg(all(feature = "fs", feature = "serde"))))] -#[doc(inline)] -pub use crate::fs::serde::{de::Error as LoadError, ser::Error as SaveError}; - /// Signer Connection pub trait Connection<C> where @@ -449,42 +434,6 @@ impl<C> SignerState<C> where C: Configuration, { - /// Saves the state of `self` to an encrypted file at `path`. - #[cfg(all(feature = "fs", feature = "serde"))] - #[inline] - pub fn save<F, P>(&self, path: P, password: &[u8]) -> Result<(), SaveError<F>> - where - Self: Serialize, - F: File, - F::Error: Debug + Display, - P: AsRef<F::Path>, - { - let mut file = F::options() - .create(true) - .truncate(true) - .write(true) - .open(path, password) - .map_err(SaveError::Io)?; - self.serialize(&mut Serializer::new(&mut file)) - } - - /// Loads an encrypted [`SignerState`] from the encrypted file at `path`. - #[cfg(all(feature = "fs", feature = "serde"))] - #[inline] - pub fn load<F, P>(path: P, password: &[u8]) -> Result<Self, LoadError<F>> - where - Self: DeserializeOwned, - F: File, - F::Error: Debug + Display, - P: AsRef<F::Path>, - { - let mut file = F::options() - .read(true) - .open(path, password) - .map_err(LoadError::Io)?; - Self::deserialize(&mut Deserializer::new(&mut file)) - } - /// Inserts the new `utxo`-`encrypted_note` pair if a known key can decrypt the note and /// validate the utxo. #[inline] @@ -954,39 +903,16 @@ where ) } - /// Saves the state of `self` to an encrypted file at `path`. - #[cfg(all(feature = "fs", feature = "serde"))] - #[cfg_attr(doc_cfg, doc(cfg(all(feature = "fs", feature = "serde"))))] + /// Returns a shared reference to the signer parameters. #[inline] - pub fn save<F, P>(&self, path: P, password: &[u8]) -> Result<(), SaveError<F>> - where - SignerState<C>: Serialize, - F: File, - F::Error: Debug + Display, - P: AsRef<F::Path>, - { - self.state.save(path, password) + pub fn parameters(&self) -> &SignerParameters<C> { + &self.parameters } - /// Loads an encrypted [`Signer`] state from the encrypted file at `path`. - #[cfg(all(feature = "fs", feature = "serde"))] - #[cfg_attr(doc_cfg, doc(cfg(all(feature = "fs", feature = "serde"))))] + /// Returns a shared reference to the signer state. #[inline] - pub fn load<F, P>( - parameters: SignerParameters<C>, - path: P, - password: &[u8], - ) -> Result<Self, LoadError<F>> - where - SignerState<C>: DeserializeOwned, - F: File, - F::Error: Debug + Display, - P: AsRef<F::Path>, - { - Ok(Self::from_parts( - parameters, - SignerState::load(path, password)?, - )) + pub fn state(&self) -> &SignerState<C> { + &self.state } /// Updates the internal ledger state, returning the new asset distribution. From 993ac2191adb01a075421d21e1cf79d038546f2e Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Wed, 16 Feb 2022 13:30:13 -0700 Subject: [PATCH 252/275] wip: start upgrading for polkadot-v0.9.16 compatibility --- manta-pay/Cargo.toml | 6 ++--- manta-pay/src/config.rs | 4 ++-- .../src/crypto/constraint/arkworks/groth16.rs | 12 ++++++++++ .../src/crypto/constraint/arkworks/mod.rs | 12 ++++++++++ manta-pay/src/crypto/ecc/arkworks.rs | 12 ++++++++++ manta-pay/src/crypto/encryption.rs | 23 +++++++++---------- 6 files changed, 52 insertions(+), 17 deletions(-) diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index c2c8100b9..0631324a5 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -91,7 +91,7 @@ wallet = ["bip32", "manta-crypto/getrandom", "std"] websocket = ["serde_json", "tungstenite"] [dependencies] -aes-gcm-siv = { version = "0.10.3", default-features = false, features = ["aes", "alloc"] } +aes-gcm = { version = "0.9.4", default-features = false, features = ["aes", "alloc"] } ark-bls12-381 = { version = "0.3.0", optional = true, default-features = false, features = ["curve"] } ark-ec = { version = "0.3.0", optional = true, default-features = false } ark-ed-on-bls12-381 = { version = "0.3.0", optional = true, default-features = false, features = ["r1cs"] } @@ -113,11 +113,11 @@ parking_lot = { version = "0.12.0", optional = true, default-features = false } rand_chacha = { version = "0.3.1", default-features = false } rayon = { version = "1.5.1", optional = true, default-features = false } reqwest = { version = "0.11.9", optional = true, default-features = false, features = ["blocking", "json"] } -scale-codec = { package = "parity-scale-codec", version = "2.3.1", optional = true, default-features = false, features = ["derive"] } +scale-codec = { package = "parity-scale-codec", version = "2.3.1", optional = true, default-features = false, features = ["derive", "max-encoded-len"] } scale-info = { version = "1.0.0", optional = true, default-features = false, features = ["derive"] } serde_json = { version = "1.0.79", optional = true, default-features = false } tempfile = { version = "3.2.0", optional = true, default-features = false } -tungstenite = { version = "0.17.0", optional = true, default-features = false, features = ["native-tls"] } +tungstenite = { version = "0.17.1", optional = true, default-features = false, features = ["native-tls"] } # TODO: zk-garage-plonk = { package = "plonk", git = "https://github.com/zk-garage/plonk", optional = true, default-features = false } [dev-dependencies] diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 3e0ec1da1..c2d34f380 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -19,7 +19,7 @@ use crate::crypto::{ constraint::arkworks::{groth16, Boolean, Fp, FpVar, R1CS}, ecc, - encryption::aes::{self, AesGcmSiv}, + encryption::aes::{self, FixedNonceAesGcm}, hash::poseidon, key::Blake2sKdf, }; @@ -596,7 +596,7 @@ impl ProofSystemInput<Group> for ProofSystem { pub type NoteEncryptionScheme = encryption::hybrid::Hybrid< KeyAgreementScheme, encryption::symmetric::Map< - AesGcmSiv<{ Asset::SIZE }, { aes::ciphertext_size(Asset::SIZE) }>, + FixedNonceAesGcm<{ Asset::SIZE }, { aes::ciphertext_size(Asset::SIZE) }>, Asset, >, key::kdf::FromByteVector< diff --git a/manta-pay/src/crypto/constraint/arkworks/groth16.rs b/manta-pay/src/crypto/constraint/arkworks/groth16.rs index 2602daeca..f1fc13e9d 100644 --- a/manta-pay/src/crypto/constraint/arkworks/groth16.rs +++ b/manta-pay/src/crypto/constraint/arkworks/groth16.rs @@ -106,6 +106,18 @@ where #[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] impl<E> scale_codec::EncodeLike for Proof<E> where E: PairingEngine {} +#[cfg(feature = "scale")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] +impl<E> scale_codec::MaxEncodedLen for Proof<E> +where + E: PairingEngine, +{ + #[inline] + fn max_encoded_len() -> usize { + todo!() + } +} + #[cfg(feature = "scale")] #[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] impl<E> scale_info::TypeInfo for Proof<E> diff --git a/manta-pay/src/crypto/constraint/arkworks/mod.rs b/manta-pay/src/crypto/constraint/arkworks/mod.rs index f174d62d6..78cfc3708 100644 --- a/manta-pay/src/crypto/constraint/arkworks/mod.rs +++ b/manta-pay/src/crypto/constraint/arkworks/mod.rs @@ -142,6 +142,18 @@ where #[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] impl<F> scale_codec::EncodeLike for Fp<F> where F: PrimeField {} +#[cfg(feature = "scale")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] +impl<F> scale_codec::MaxEncodedLen for Fp<F> +where + F: PrimeField, +{ + #[inline] + fn max_encoded_len() -> usize { + todo!() + } +} + #[cfg(feature = "scale")] #[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] impl<F> scale_info::TypeInfo for Fp<F> diff --git a/manta-pay/src/crypto/ecc/arkworks.rs b/manta-pay/src/crypto/ecc/arkworks.rs index fad85a5de..b938cf420 100644 --- a/manta-pay/src/crypto/ecc/arkworks.rs +++ b/manta-pay/src/crypto/ecc/arkworks.rs @@ -189,6 +189,18 @@ where #[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] impl<C> scale_codec::EncodeLike for Group<C> where C: ProjectiveCurve {} +#[cfg(feature = "scale")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] +impl<C> scale_codec::MaxEncodedLen for Group<C> +where + C: ProjectiveCurve, +{ + #[inline] + fn max_encoded_len() -> usize { + todo!() + } +} + #[cfg(feature = "scale")] #[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] impl<C> scale_info::TypeInfo for Group<C> diff --git a/manta-pay/src/crypto/encryption.rs b/manta-pay/src/crypto/encryption.rs index 49dfdcf59..7faee7525 100644 --- a/manta-pay/src/crypto/encryption.rs +++ b/manta-pay/src/crypto/encryption.rs @@ -20,16 +20,16 @@ /// AES Encryption Implementation pub mod aes { - use aes_gcm_siv::{ + use aes_gcm::{ aead::{Aead, NewAead}, - Aes256GcmSiv, Nonce, + Aes256Gcm, Nonce, }; use manta_crypto::encryption::symmetric::SymmetricKeyEncryptionScheme; use manta_util::Array; /// AES-GCM Authentication Tag Size #[allow(clippy::cast_possible_truncation)] // NOTE: GCM Tag Size should be smaller than `2^32`. - const TAG_SIZE: usize = (aes_gcm_siv::C_MAX - aes_gcm_siv::P_MAX) as usize; + const TAG_SIZE: usize = (aes_gcm::C_MAX - aes_gcm::P_MAX) as usize; /// Computes the size of the ciphertext corresponding to a plaintext of the given /// `plaintext_size`. @@ -38,22 +38,21 @@ pub mod aes { plaintext_size + TAG_SIZE } - /// AES Galois Counter Mode with Synthetic Initialization Vectors + /// Fixed-Nonce AES Galois Counter Mode #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct AesGcmSiv<const P: usize, const C: usize>; + pub struct FixedNonceAesGcm<const P: usize, const C: usize>; - impl<const P: usize, const C: usize> AesGcmSiv<P, C> { + impl<const P: usize, const C: usize> FixedNonceAesGcm<P, C> { /// Fixed Random Nonce /// /// # Safety /// /// Using a fixed nonce is safe under the assumption that the encryption keys are only used - /// once. We add SIV for some extra safety which makes the effective nonce dependent on the - /// plaintext. + /// once. const NONCE: &'static [u8] = b"random nonce"; } - impl<const P: usize, const C: usize> SymmetricKeyEncryptionScheme for AesGcmSiv<P, C> { + impl<const P: usize, const C: usize> SymmetricKeyEncryptionScheme for FixedNonceAesGcm<P, C> { type Key = [u8; 32]; type Plaintext = Array<u8, P>; type Ciphertext = Array<u8, C>; @@ -61,7 +60,7 @@ pub mod aes { #[inline] fn encrypt(key: Self::Key, plaintext: Self::Plaintext) -> Self::Ciphertext { Array::from_unchecked( - Aes256GcmSiv::new_from_slice(&key) + Aes256Gcm::new_from_slice(&key) .expect("The key has the correct size.") .encrypt(Nonce::from_slice(Self::NONCE), plaintext.as_ref()) .expect("Symmetric encryption is not allowed to fail."), @@ -70,7 +69,7 @@ pub mod aes { #[inline] fn decrypt(key: Self::Key, ciphertext: &Self::Ciphertext) -> Option<Self::Plaintext> { - Aes256GcmSiv::new_from_slice(&key) + Aes256Gcm::new_from_slice(&key) .expect("The key has the correct size.") .decrypt(Nonce::from_slice(Self::NONCE), ciphertext.as_ref()) .ok() @@ -98,7 +97,7 @@ pub mod aes { let mut plaintext = [0; Asset::SIZE]; rng.fill_bytes(&mut plaintext); encryption::symmetric::test::encryption::< - AesGcmSiv<{ Asset::SIZE }, { ciphertext_size(Asset::SIZE) }>, + FixedNonceAesGcm<{ Asset::SIZE }, { ciphertext_size(Asset::SIZE) }>, >(key, Array(plaintext)); } } From 02012b4e21c3f4d02f68a3113ec397bd99c22270 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 17 Feb 2022 13:45:51 -0700 Subject: [PATCH 253/275] fix: include implementations of MaxEncodedLen --- .../src/crypto/constraint/arkworks/groth16.rs | 4 +- .../src/crypto/constraint/arkworks/mod.rs | 40 +++++++++++-------- manta-pay/src/crypto/ecc/arkworks.rs | 4 +- manta-util/src/bytes.rs | 6 +++ 4 files changed, 35 insertions(+), 19 deletions(-) diff --git a/manta-pay/src/crypto/constraint/arkworks/groth16.rs b/manta-pay/src/crypto/constraint/arkworks/groth16.rs index f1fc13e9d..bd43f682b 100644 --- a/manta-pay/src/crypto/constraint/arkworks/groth16.rs +++ b/manta-pay/src/crypto/constraint/arkworks/groth16.rs @@ -114,7 +114,9 @@ where { #[inline] fn max_encoded_len() -> usize { - todo!() + use crate::crypto::ecc::arkworks::Group; + 2 * Group::<E::G1Projective>::max_encoded_len() + + Group::<E::G2Projective>::max_encoded_len() } } diff --git a/manta-pay/src/crypto/constraint/arkworks/mod.rs b/manta-pay/src/crypto/constraint/arkworks/mod.rs index 78cfc3708..4ea9987d7 100644 --- a/manta-pay/src/crypto/constraint/arkworks/mod.rs +++ b/manta-pay/src/crypto/constraint/arkworks/mod.rs @@ -17,7 +17,7 @@ //! Arkworks Constraint System and Proof System Implementations use alloc::vec::Vec; -use ark_ff::PrimeField; +use ark_ff::{Field, FpParameters, PrimeField}; use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget, select::CondSelectGadget}; use ark_relations::{ ns, r1cs as ark_r1cs, @@ -30,7 +30,10 @@ use manta_crypto::{ }, rand::{CryptoRng, RngCore, Sample, Standard}, }; -use manta_util::codec::{Decode, DecodeError, Encode, Read, Write}; +use manta_util::{ + byte_count, + codec::{Decode, DecodeError, Encode, Read, Write}, +}; #[cfg(feature = "serde")] use manta_util::serde::{Deserialize, Serialize, Serializer}; @@ -45,7 +48,7 @@ pub mod pairing; #[cfg_attr(doc_cfg, doc(cfg(feature = "groth16")))] pub mod groth16; -/// Prime Field Element +/// Field Element #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), @@ -58,7 +61,7 @@ pub mod groth16; )] #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Fp<F>( - /// Field Point Element + /// Field Element #[cfg_attr( feature = "serde", serde(serialize_with = "serialize_field_element::<F, _>") @@ -66,11 +69,11 @@ pub struct Fp<F>( pub F, ) where - F: PrimeField; + F: Field; impl<F> Decode for Fp<F> where - F: PrimeField, + F: Field, { type Error = codec::SerializationError; @@ -92,7 +95,7 @@ where impl<F> Encode for Fp<F> where - F: PrimeField, + F: Field, { #[inline] fn encode<W>(&self, writer: W) -> Result<(), W::Error> @@ -109,7 +112,7 @@ where #[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] impl<F> scale_codec::Decode for Fp<F> where - F: PrimeField, + F: Field, { #[inline] fn decode<I>(input: &mut I) -> Result<Self, scale_codec::Error> @@ -127,7 +130,7 @@ where #[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] impl<F> scale_codec::Encode for Fp<F> where - F: PrimeField, + F: Field, { #[inline] fn using_encoded<R, Encoder>(&self, f: Encoder) -> R @@ -140,17 +143,20 @@ where #[cfg(feature = "scale")] #[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] -impl<F> scale_codec::EncodeLike for Fp<F> where F: PrimeField {} +impl<F> scale_codec::EncodeLike for Fp<F> where F: Field {} #[cfg(feature = "scale")] #[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] impl<F> scale_codec::MaxEncodedLen for Fp<F> where - F: PrimeField, + F: Field, { #[inline] fn max_encoded_len() -> usize { - todo!() + byte_count( + <<F::BasePrimeField as PrimeField>::Params as FpParameters>::MODULUS_BITS + * (F::extension_degree() as u32), + ) as usize } } @@ -158,7 +164,7 @@ where #[cfg_attr(doc_cfg, doc(cfg(feature = "scale")))] impl<F> scale_info::TypeInfo for Fp<F> where - F: PrimeField, + F: Field, { type Identity = [u8]; @@ -170,7 +176,7 @@ where impl<F> Sample for Fp<F> where - F: PrimeField, + F: Field, { #[inline] fn sample<R>(distribution: Standard, rng: &mut R) -> Self @@ -184,7 +190,7 @@ where impl<F> TryFrom<Vec<u8>> for Fp<F> where - F: PrimeField, + F: Field, { type Error = codec::SerializationError; @@ -198,7 +204,7 @@ where #[inline] pub fn field_element_as_bytes<F>(element: &F) -> Vec<u8> where - F: PrimeField, + F: Field, { let mut buffer = Vec::new(); element @@ -212,7 +218,7 @@ where #[inline] fn serialize_field_element<F, S>(element: &F, serializer: S) -> Result<S::Ok, S::Error> where - F: PrimeField, + F: Field, S: Serializer, { serializer.serialize_bytes(&field_element_as_bytes(element)) diff --git a/manta-pay/src/crypto/ecc/arkworks.rs b/manta-pay/src/crypto/ecc/arkworks.rs index b938cf420..7e075dac0 100644 --- a/manta-pay/src/crypto/ecc/arkworks.rs +++ b/manta-pay/src/crypto/ecc/arkworks.rs @@ -197,7 +197,9 @@ where { #[inline] fn max_encoded_len() -> usize { - todo!() + // NOTE: In affine form, we have two base field elements to represent the point. We add + // space for an extra byte flag in case we need to keep track of "infinity". + 2 * Fp::<C::BaseField>::max_encoded_len() + 1 } } diff --git a/manta-util/src/bytes.rs b/manta-util/src/bytes.rs index 9042de58c..b97e4fde1 100644 --- a/manta-util/src/bytes.rs +++ b/manta-util/src/bytes.rs @@ -80,3 +80,9 @@ impl<const N: usize> IntoBytes<N> for [u8; N] { self } } + +/// Counts the number of bytes required to encode a number with the given number of `bits`. +#[inline] +pub const fn byte_count(bits: u32) -> u32 { + (bits / 8) + (((bits % 8) != 0) as u32) +} From f4d94f01b7fb75c08c5578dfd3555563de2df1f9 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 17 Feb 2022 18:08:15 -0700 Subject: [PATCH 254/275] fix: change name of randomness in commitment schemes from trapdoor --- manta-accounting/src/transfer/mod.rs | 4 ++-- manta-crypto/src/commitment.rs | 14 +++++++------- manta-pay/src/config.rs | 19 ++++++++++++------- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index 05afb83ce..be9c0cce7 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -85,7 +85,7 @@ pub trait Configuration { /// UTXO Commitment Scheme Type type UtxoCommitmentScheme: CommitmentScheme< - Trapdoor = Trapdoor<Self>, + Randomness = Trapdoor<Self>, Input = Asset, Output = Self::Utxo, >; @@ -98,7 +98,7 @@ pub trait Configuration { /// UTXO Commitment Scheme Variable Type type UtxoCommitmentSchemeVar: CommitmentScheme< Self::Compiler, - Trapdoor = TrapdoorVar<Self>, + Randomness = TrapdoorVar<Self>, Input = AssetVar<Self>, Output = Self::UtxoVar, > + Constant<Self::Compiler, Type = Self::UtxoCommitmentScheme>; diff --git a/manta-crypto/src/commitment.rs b/manta-crypto/src/commitment.rs index 43d8a339e..8bfdba944 100644 --- a/manta-crypto/src/commitment.rs +++ b/manta-crypto/src/commitment.rs @@ -20,8 +20,8 @@ use crate::constraint::Native; /// Commitment Scheme pub trait CommitmentScheme<COM = ()> { - /// Trapdoor Type - type Trapdoor; + /// Randomness Type + type Randomness; /// Input Type type Input; @@ -29,20 +29,20 @@ pub trait CommitmentScheme<COM = ()> { /// Output Type type Output; - /// Commits to the `input` value using the randomness `trapdoor` inside the `compiler`. + /// Commits to the `input` value using `randomness` inside the `compiler`. fn commit_in( &self, - trapdoor: &Self::Trapdoor, + randomness: &Self::Randomness, input: &Self::Input, compiler: &mut COM, ) -> Self::Output; - /// Commits to the `input` value using the randomness `trapdoor`. + /// Commits to the `input` value using `randomness` #[inline] - fn commit(&self, trapdoor: &Self::Trapdoor, input: &Self::Input) -> Self::Output + fn commit(&self, randomness: &Self::Randomness, input: &Self::Input) -> Self::Output where COM: Native, { - self.commit_in(trapdoor, input, &mut COM::compiler()) + self.commit_in(randomness, input, &mut COM::compiler()) } } diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index c2d34f380..e51cb8f8b 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -132,21 +132,21 @@ pub type Utxo = Fp<ConstraintField>; pub struct UtxoCommitmentScheme(pub Poseidon4); impl CommitmentScheme for UtxoCommitmentScheme { - type Trapdoor = Group; + type Randomness = Group; type Input = Asset; type Output = Utxo; #[inline] fn commit_in( &self, - trapdoor: &Self::Trapdoor, + randomness: &Self::Randomness, input: &Self::Input, _: &mut (), ) -> Self::Output { // NOTE: The group is already in affine form, so we can extract `x` and `y`. self.0.hash([ - &Fp(trapdoor.0.x), - &Fp(trapdoor.0.y), + &Fp(randomness.0.x), + &Fp(randomness.0.y), &Fp(input.id.0.into()), &Fp(input.value.0.into()), ]) @@ -193,20 +193,25 @@ pub type UtxoVar = ConstraintFieldVar; pub struct UtxoCommitmentSchemeVar(pub Poseidon4Var); impl CommitmentScheme<Compiler> for UtxoCommitmentSchemeVar { - type Trapdoor = GroupVar; + type Randomness = GroupVar; type Input = Asset<AssetIdVar, AssetValueVar>; type Output = UtxoVar; #[inline] fn commit_in( &self, - trapdoor: &Self::Trapdoor, + randomness: &Self::Randomness, input: &Self::Input, compiler: &mut Compiler, ) -> Self::Output { // NOTE: The group is already in affine form, so we can extract `x` and `y`. self.0.hash_in( - [&trapdoor.0.x, &trapdoor.0.y, &input.id.0, &input.value.0], + [ + &randomness.0.x, + &randomness.0.y, + &input.id.0, + &input.value.0, + ], compiler, ) } From 89c3b14522bd7b98be54682d8ed0167570f33430 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 17 Feb 2022 19:29:16 -0700 Subject: [PATCH 255/275] fix: correct feature selection --- manta-pay/src/crypto/constraint/arkworks/groth16.rs | 4 +++- manta-pay/src/crypto/constraint/arkworks/mod.rs | 10 +++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/manta-pay/src/crypto/constraint/arkworks/groth16.rs b/manta-pay/src/crypto/constraint/arkworks/groth16.rs index bd43f682b..416aaac97 100644 --- a/manta-pay/src/crypto/constraint/arkworks/groth16.rs +++ b/manta-pay/src/crypto/constraint/arkworks/groth16.rs @@ -33,6 +33,9 @@ use manta_crypto::{ }; use manta_util::codec::{self, DecodeError}; +#[cfg(feature = "scale")] +use crate::crypto::ecc::arkworks::Group; + #[cfg(feature = "serde")] use manta_util::serde::{Deserialize, Serialize, Serializer}; @@ -114,7 +117,6 @@ where { #[inline] fn max_encoded_len() -> usize { - use crate::crypto::ecc::arkworks::Group; 2 * Group::<E::G1Projective>::max_encoded_len() + Group::<E::G2Projective>::max_encoded_len() } diff --git a/manta-pay/src/crypto/constraint/arkworks/mod.rs b/manta-pay/src/crypto/constraint/arkworks/mod.rs index 4ea9987d7..e00b16b94 100644 --- a/manta-pay/src/crypto/constraint/arkworks/mod.rs +++ b/manta-pay/src/crypto/constraint/arkworks/mod.rs @@ -17,7 +17,7 @@ //! Arkworks Constraint System and Proof System Implementations use alloc::vec::Vec; -use ark_ff::{Field, FpParameters, PrimeField}; +use ark_ff::{Field, PrimeField}; use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget, select::CondSelectGadget}; use ark_relations::{ ns, r1cs as ark_r1cs, @@ -30,10 +30,10 @@ use manta_crypto::{ }, rand::{CryptoRng, RngCore, Sample, Standard}, }; -use manta_util::{ - byte_count, - codec::{Decode, DecodeError, Encode, Read, Write}, -}; +use manta_util::codec::{Decode, DecodeError, Encode, Read, Write}; + +#[cfg(feature = "scale")] +use {ark_ff::FpParameters, manta_util::byte_count}; #[cfg(feature = "serde")] use manta_util::serde::{Deserialize, Serialize, Serializer}; From ac77eb9d7e2f6e5a7b01114ea029dccbb84a4127 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Thu, 17 Feb 2022 20:06:28 -0700 Subject: [PATCH 256/275] feat: add shape command for Transaction --- manta-accounting/src/transfer/canonical.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/manta-accounting/src/transfer/canonical.rs b/manta-accounting/src/transfer/canonical.rs index bf60e1b73..c6b743f4c 100644 --- a/manta-accounting/src/transfer/canonical.rs +++ b/manta-accounting/src/transfer/canonical.rs @@ -335,6 +335,16 @@ where } } } + + /// Returns the associated [`TransferShape`] for this [`Transaction`]. + #[inline] + pub fn shape(&self) -> TransferShape { + match self { + Self::Mint(_) => TransferShape::Mint, + Self::PrivateTransfer(_, _) => TransferShape::PrivateTransfer, + Self::Reclaim(_) => TransferShape::Reclaim, + } + } } /// Transaction Kind From 389f229a733a09b19d8930e2087954f128df498f Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 21 Feb 2022 13:19:15 -0500 Subject: [PATCH 257/275] feat: move Mnemonic into manta-pay::key to improve featureset --- manta-accounting/src/wallet/signer.rs | 19 ++++++ manta-crypto/src/rand.rs | 2 +- manta-pay/Cargo.toml | 4 +- manta-pay/src/key.rs | 85 +++++++++++++++++++++++++-- 4 files changed, 102 insertions(+), 8 deletions(-) diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index fd14d516d..bddb8f9d5 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -341,6 +341,14 @@ pub type ProvingContextCacheError<C> = CachedResourceError<MultiProvingContext<C>, <C as Configuration>::ProvingContextCache>; /// Signer Parameters +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "Parameters<C>: Clone, C::ProvingContextCache: Clone"), + Debug(bound = "Parameters<C>: Debug, C::ProvingContextCache: Debug"), + Eq(bound = "Parameters<C>: Eq, C::ProvingContextCache: Eq"), + Hash(bound = "Parameters<C>: Hash, C::ProvingContextCache: Hash"), + PartialEq(bound = "Parameters<C>: PartialEq, C::ProvingContextCache: PartialEq") +)] pub struct SignerParameters<C> where C: Configuration, @@ -434,6 +442,17 @@ impl<C> SignerState<C> where C: Configuration, { + /// Builds a new [`SignerState`] from `keys` and `utxo_set`. + #[inline] + pub fn new(keys: C::HierarchicalKeyDerivationScheme, utxo_set: C::UtxoSet) -> Self { + Self { + accounts: AccountTable::<C>::new(keys), + utxo_set, + assets: Default::default(), + rng: C::Rng::from_entropy(), + } + } + /// Inserts the new `utxo`-`encrypted_note` pair if a known key can decrypt the note and /// validate the utxo. #[inline] diff --git a/manta-crypto/src/rand.rs b/manta-crypto/src/rand.rs index 5e9ec9fe6..1e76c8af4 100644 --- a/manta-crypto/src/rand.rs +++ b/manta-crypto/src/rand.rs @@ -234,7 +234,7 @@ macro_rules! impl_sample_from_u32 { }; } -impl_sample_from_u32!(u8, u16, u32); +impl_sample_from_u32!(i8, i16, i32, u8, u16, u32); impl Sample for u64 { #[inline] diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 0631324a5..5f178a8ae 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -103,7 +103,7 @@ ark-serialize = { version = "0.3.0", optional = true, default-features = false, ark-snark = { version = "0.3.0", optional = true, default-features = false } 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.0", default-features = false } +blake2 = { version = "0.10.4", default-features = false } derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } indexmap = { version = "1.8.0", optional = true, default-features = false } manta-accounting = { path = "../manta-accounting", default-features = false } @@ -117,7 +117,7 @@ scale-codec = { package = "parity-scale-codec", version = "2.3.1", optional = tr scale-info = { version = "1.0.0", optional = true, default-features = false, features = ["derive"] } serde_json = { version = "1.0.79", optional = true, default-features = false } tempfile = { version = "3.2.0", optional = true, default-features = false } -tungstenite = { version = "0.17.1", optional = true, default-features = false, features = ["native-tls"] } +tungstenite = { version = "0.17.2", optional = true, default-features = false, features = ["native-tls"] } # TODO: zk-garage-plonk = { package = "plonk", git = "https://github.com/zk-garage/plonk", optional = true, default-features = false } [dev-dependencies] diff --git a/manta-pay/src/key.rs b/manta-pay/src/key.rs index fab134045..76c0c9def 100644 --- a/manta-pay/src/key.rs +++ b/manta-pay/src/key.rs @@ -25,7 +25,6 @@ //! [`BIP-0044`]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki use alloc::{format, string::String}; -use bip32::{Seed, XPrv}; use core::marker::PhantomData; use manta_accounting::key::{ self, AccountIndex, HierarchicalKeyDerivationScheme, IndexType, KeyIndex, Kind, @@ -34,9 +33,9 @@ use manta_crypto::rand::{CryptoRng, RngCore, Sample, Standard}; use manta_util::{create_seal, seal, Array}; #[cfg(feature = "serde")] -use manta_util::serde::{Deserialize, Serialize}; +use manta_util::serde::{Deserialize, Serialize, Serializer}; -pub use bip32::{Language, Mnemonic}; +pub use bip32::{Error, Seed, XPrv as SecretKey}; create_seal! {} @@ -217,11 +216,11 @@ where { const GAP_LIMIT: IndexType = 20; - type SecretKey = XPrv; + type SecretKey = SecretKey; #[inline] fn derive(&self, account: AccountIndex, kind: Kind, index: KeyIndex) -> Self::SecretKey { - XPrv::derive_from_path( + SecretKey::derive_from_path( &self.seed, &path_string::<C>(account, kind, index) .parse() @@ -230,3 +229,79 @@ where .expect("Unable to generate secret key for valid seed and path string.") } } + +/// Mnemonic +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields, try_from = "String") +)] +#[derive(Clone)] +pub struct Mnemonic( + /// Underlying BIP39 Mnemonic + #[serde(serialize_with = "Mnemonic::serialize")] + bip32::Mnemonic, +); + +impl Mnemonic { + /// Create a new BIP39 mnemonic phrase from the given string. + #[inline] + pub fn new<S>(phrase: S) -> Result<Self, Error> + where + S: AsRef<str>, + { + bip32::Mnemonic::new(phrase, Default::default()).map(Self) + } + + /// Convert this mnemonic phrase into the BIP39 seed value. + #[inline] + pub fn to_seed(&self, password: &str) -> Seed { + self.0.to_seed(password) + } + + /// Serializes the underlying `mnemonic` phrase. + #[cfg(feature = "serde")] + #[inline] + fn serialize<S>(mnemonic: &bip32::Mnemonic, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + mnemonic.phrase().serialize(serializer) + } +} + +impl AsRef<str> for Mnemonic { + #[inline] + fn as_ref(&self) -> &str { + self.0.phrase() + } +} + +impl Eq for Mnemonic {} + +impl PartialEq for Mnemonic { + #[inline] + fn eq(&self, rhs: &Self) -> bool { + self.as_ref().eq(rhs.as_ref()) + } +} + +impl Sample for Mnemonic { + #[inline] + fn sample<R>(distribution: Standard, rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + let _ = distribution; + Self(bip32::Mnemonic::random(rng, Default::default())) + } +} + +impl TryFrom<String> for Mnemonic { + type Error = Error; + + #[inline] + fn try_from(string: String) -> Result<Self, Self::Error> { + Self::new(string) + } +} From 874f6701d78f407f9cb2435e30ce5b116b82c2e4 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 21 Feb 2022 14:38:47 -0500 Subject: [PATCH 258/275] feat: add cloning methods for signer state --- manta-accounting/src/wallet/signer.rs | 33 +++++++++++++++++++++++---- manta-crypto/src/merkle_tree/fork.rs | 6 +++++ manta-pay/src/key.rs | 2 ++ 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index bddb8f9d5..8a7eb2747 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -442,17 +442,23 @@ impl<C> SignerState<C> where C: Configuration, { - /// Builds a new [`SignerState`] from `keys` and `utxo_set`. + /// Builds a new [`SignerState`] from `keys`, `utxo_set`, and `assets`. #[inline] - pub fn new(keys: C::HierarchicalKeyDerivationScheme, utxo_set: C::UtxoSet) -> Self { + fn build(accounts: AccountTable<C>, utxo_set: C::UtxoSet, assets: C::AssetMap) -> Self { Self { - accounts: AccountTable::<C>::new(keys), + accounts, utxo_set, - assets: Default::default(), - rng: C::Rng::from_entropy(), + assets, + rng: FromEntropy::from_entropy(), } } + /// Builds a new [`SignerState`] from `keys` and `utxo_set`. + #[inline] + pub fn new(keys: C::HierarchicalKeyDerivationScheme, utxo_set: C::UtxoSet) -> Self { + Self::build(AccountTable::<C>::new(keys), utxo_set, Default::default()) + } + /// Inserts the new `utxo`-`encrypted_note` pair if a known key can decrypt the note and /// validate the utxo. #[inline] @@ -852,6 +858,23 @@ where } } +impl<C> Clone for SignerState<C> +where + C: Configuration, + C::HierarchicalKeyDerivationScheme: Clone, + C::UtxoSet: Clone, + C::AssetMap: Clone, +{ + #[inline] + fn clone(&self) -> Self { + Self::build( + self.accounts.clone(), + self.utxo_set.clone(), + self.assets.clone(), + ) + } +} + /// Signer pub struct Signer<C> where diff --git a/manta-crypto/src/merkle_tree/fork.rs b/manta-crypto/src/merkle_tree/fork.rs index e6971be9d..cbed72161 100644 --- a/manta-crypto/src/merkle_tree/fork.rs +++ b/manta-crypto/src/merkle_tree/fork.rs @@ -262,6 +262,7 @@ impl Default for BaseContribution { )] #[derive(derivative::Derivative)] #[derivative( + Clone(bound = "LeafDigest<C>: Clone, InnerDigest<C>: Clone, M: Clone"), Debug(bound = "LeafDigest<C>: Debug, InnerDigest<C>: Debug, M: Debug"), Default(bound = "LeafDigest<C>: Default, InnerDigest<C>: Default") )] @@ -779,6 +780,11 @@ where deny_unknown_fields, ) )] +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "T: Clone, LeafDigest<C>: Clone, InnerDigest<C>: Clone, M: Clone"), + Debug(bound = "T: Debug, LeafDigest<C>: Debug, InnerDigest<C>: Debug, M: Debug") +)] pub struct ForkedTree<C, T, M = BTreeMap<C>> where C: Configuration + ?Sized, diff --git a/manta-pay/src/key.rs b/manta-pay/src/key.rs index 76c0c9def..c8edb225e 100644 --- a/manta-pay/src/key.rs +++ b/manta-pay/src/key.rs @@ -136,6 +136,8 @@ type SeedBytes = Array<u8, { Seed::SIZE }>; derive(Deserialize, Serialize), serde(crate = "manta_util::serde", deny_unknown_fields, transparent) )] +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""))] pub struct KeySecret<C> where C: CoinType, From f99b8c06d10cd52df39ccf74bf60d19222fc57d9 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 25 Feb 2022 16:51:53 -0500 Subject: [PATCH 259/275] wip: start working on ledger simulation service --- manta-pay/src/lib.rs | 1 + manta-pay/src/signer/client/http.rs | 62 +-- manta-pay/src/simulation/ledger/client.rs | 17 + manta-pay/src/simulation/ledger/mod.rs | 470 ++++++++++++++++++++++ manta-pay/src/simulation/ledger/server.rs | 17 + manta-pay/src/simulation/mod.rs | 467 +-------------------- manta-pay/src/util/http.rs | 82 ++++ manta-pay/src/util/mod.rs | 21 + 8 files changed, 630 insertions(+), 507 deletions(-) create mode 100644 manta-pay/src/simulation/ledger/client.rs create mode 100644 manta-pay/src/simulation/ledger/mod.rs create mode 100644 manta-pay/src/simulation/ledger/server.rs create mode 100644 manta-pay/src/util/http.rs create mode 100644 manta-pay/src/util/mod.rs diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index d58f9925e..18752413d 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -27,6 +27,7 @@ extern crate alloc; mod test; pub mod crypto; +pub mod util; #[cfg(feature = "groth16")] #[cfg_attr(doc_cfg, doc(cfg(feature = "groth16")))] diff --git a/manta-pay/src/signer/client/http.rs b/manta-pay/src/signer/client/http.rs index a7df23892..18b2ecbb0 100644 --- a/manta-pay/src/signer/client/http.rs +++ b/manta-pay/src/signer/client/http.rs @@ -19,25 +19,15 @@ use crate::{ config::{Config, ReceivingKey, Transaction}, signer::{ReceivingKeyRequest, SignError, SignResponse, SyncError, SyncRequest, SyncResponse}, + util::http::{self, Error, IntoUrl}, }; use manta_accounting::wallet::{self, signer}; -use manta_util::serde::Serialize; -use reqwest::{ - blocking::{Client as BaseClient, Response}, - Error, IntoUrl, Method, Url, -}; /// Wallet Associated to [`Client`] pub type Wallet<L> = wallet::Wallet<Config, L, Client>; -/// HTTP Client -pub struct Client { - /// Server URL - server_url: Url, - - /// Base HTTP Client - client: BaseClient, -} +/// HTTP Signer Client +pub struct Client(http::Client); impl Client { /// Builds a new HTTP [`Client`] that connects to `server_url`. @@ -46,45 +36,7 @@ impl Client { where U: IntoUrl, { - Ok(Self { - client: BaseClient::builder().build()?, - server_url: server_url.into_url()?, - }) - } - - /// Sends a new request of type `command` with body `request`. - #[inline] - fn request<T>(&self, method: Method, command: &str, request: T) -> Result<Response, Error> - where - T: Serialize, - { - self.client - .request( - method, - self.server_url - .join(command) - .expect("This error branch is not allowed to happen."), - ) - .json(&request) - .send() - } - - /// Sends a GET request of type `command` with body `request`. - #[inline] - fn get<T>(&self, command: &str, request: T) -> Result<Response, Error> - where - T: Serialize, - { - self.request(Method::GET, command, request) - } - - /// Sends a POST request of type `command` with body `request`. - #[inline] - fn post<T>(&self, command: &str, request: T) -> Result<Response, Error> - where - T: Serialize, - { - self.request(Method::POST, command, request) + Ok(Self(http::Client::new(server_url)?)) } } @@ -98,7 +50,7 @@ impl signer::Connection<Config> for Client { ) -> Result<Result<SyncResponse, SyncError>, Self::Error> { // NOTE: The synchronization command modifies the signer so it must be a POST command // to match the HTTP semantics. - self.post("sync", request)?.json() + self.0.post("sync", request)?.json() } #[inline] @@ -108,7 +60,7 @@ impl signer::Connection<Config> for Client { ) -> Result<Result<SignResponse, SignError>, Self::Error> { // NOTE: The signing command does not modify the signer so it must be a GET command to match // the HTTP semantics. - self.get("sign", transaction)?.json() + self.0.get("sign", transaction)?.json() } #[inline] @@ -118,6 +70,6 @@ impl signer::Connection<Config> for Client { ) -> Result<Vec<ReceivingKey>, Self::Error> { // NOTE: The receiving key command modifies the signer so it must be a POST command to match // the HTTP semantics. - self.post("receivingKeys", request)?.json() + self.0.post("receivingKeys", request)?.json() } } diff --git a/manta-pay/src/simulation/ledger/client.rs b/manta-pay/src/simulation/ledger/client.rs new file mode 100644 index 000000000..12e86edc1 --- /dev/null +++ b/manta-pay/src/simulation/ledger/client.rs @@ -0,0 +1,17 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Ledger Simulation Client diff --git a/manta-pay/src/simulation/ledger/mod.rs b/manta-pay/src/simulation/ledger/mod.rs new file mode 100644 index 000000000..895daf5d2 --- /dev/null +++ b/manta-pay/src/simulation/ledger/mod.rs @@ -0,0 +1,470 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Ledger Simulation + +use crate::config::{ + Config, EncryptedNote, MerkleTreeConfiguration, MultiVerifyingContext, ProofSystem, + TransferPost, Utxo, UtxoSetModel, VoidNumber, +}; +use alloc::{sync::Arc, vec::Vec}; +use core::convert::Infallible; +use indexmap::IndexSet; +use manta_accounting::{ + asset::{Asset, AssetId, AssetList, AssetValue}, + transfer::{ + canonical::TransferShape, AccountBalance, InvalidSinkAccount, InvalidSourceAccount, Proof, + ReceiverLedger, ReceiverPostingKey, SenderLedger, SenderPostingKey, SinkPostingKey, + SourcePostingKey, TransferLedger, TransferLedgerSuperPostingKey, TransferPostingKey, + UtxoSetOutput, + }, + wallet::{ + ledger::{self, PullResponse, PullResult, PushResponse, PushResult}, + test::PublicBalanceOracle, + }, +}; +use manta_crypto::{ + constraint::ProofSystem as _, + merkle_tree::{ + self, + forest::{Configuration, FixedIndex}, + Tree, + }, +}; +use manta_util::into_array_unchecked; +use parking_lot::RwLock; +use std::collections::{HashMap, HashSet}; + +pub mod client; +pub mod server; + +/// Merkle Forest Index +pub type MerkleForestIndex = <MerkleTreeConfiguration as Configuration>::Index; + +/// UTXO Merkle Forest Type +pub type UtxoMerkleForest = merkle_tree::forest::TreeArrayMerkleForest< + MerkleTreeConfiguration, + merkle_tree::single_path::SinglePath<MerkleTreeConfiguration>, + { MerkleTreeConfiguration::FOREST_WIDTH }, +>; + +/// Wrap Type +#[derive(Clone, Copy)] +pub struct Wrap<T>(T); + +impl<T> AsRef<T> for Wrap<T> { + #[inline] + fn as_ref(&self) -> &T { + &self.0 + } +} + +/// Wrap Pair Type +#[derive(Clone, Copy)] +pub struct WrapPair<L, R>(L, R); + +impl<L, R> AsRef<R> for WrapPair<L, R> { + #[inline] + fn as_ref(&self) -> &R { + &self.1 + } +} + +/// Account Id +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct AccountId(pub u64); + +/// Ledger +#[derive(Debug)] +pub struct Ledger { + /// Void Numbers + void_numbers: IndexSet<VoidNumber>, + + /// UTXOs + utxos: HashSet<Utxo>, + + /// Shards + shards: HashMap<MerkleForestIndex, IndexSet<(Utxo, EncryptedNote)>>, + + /// UTXO Forest + utxo_forest: UtxoMerkleForest, + + /// Account Table + accounts: HashMap<AccountId, HashMap<AssetId, AssetValue>>, + + /// Verifying Contexts + verifying_context: MultiVerifyingContext, +} + +impl Ledger { + /// Builds an empty [`Ledger`]. + #[inline] + pub fn new( + utxo_forest_parameters: UtxoSetModel, + verifying_context: MultiVerifyingContext, + ) -> Self { + Self { + void_numbers: Default::default(), + utxos: Default::default(), + shards: (0..MerkleTreeConfiguration::FOREST_WIDTH) + .map(move |i| (MerkleForestIndex::from_index(i), Default::default())) + .collect(), + utxo_forest: UtxoMerkleForest::new(utxo_forest_parameters), + accounts: Default::default(), + verifying_context, + } + } + + /// Sets the public balance of `account` in assets with `id` to `value`. + #[inline] + pub fn set_public_balance(&mut self, account: AccountId, id: AssetId, value: AssetValue) { + self.accounts.entry(account).or_default().insert(id, value); + } +} + +impl SenderLedger<Config> for Ledger { + type ValidVoidNumber = Wrap<VoidNumber>; + type ValidUtxoSetOutput = Wrap<UtxoSetOutput<Config>>; + type SuperPostingKey = (Wrap<()>, ()); + + #[inline] + fn is_unspent(&self, void_number: VoidNumber) -> Option<Self::ValidVoidNumber> { + if self.void_numbers.contains(&void_number) { + None + } else { + Some(Wrap(void_number)) + } + } + + #[inline] + fn has_matching_utxo_set_output( + &self, + output: UtxoSetOutput<Config>, + ) -> Option<Self::ValidUtxoSetOutput> { + for tree in self.utxo_forest.forest.as_ref() { + if tree.root() == &output { + return Some(Wrap(output)); + } + } + None + } + + #[inline] + fn spend( + &mut self, + utxo_set_output: Self::ValidUtxoSetOutput, + void_number: Self::ValidVoidNumber, + super_key: &Self::SuperPostingKey, + ) { + let _ = (utxo_set_output, super_key); + self.void_numbers.insert(void_number.0); + } +} + +impl ReceiverLedger<Config> for Ledger { + type ValidUtxo = Wrap<Utxo>; + type SuperPostingKey = (Wrap<()>, ()); + + #[inline] + fn is_not_registered(&self, utxo: Utxo) -> Option<Self::ValidUtxo> { + if self.utxos.contains(&utxo) { + None + } else { + Some(Wrap(utxo)) + } + } + + #[inline] + fn register( + &mut self, + utxo: Self::ValidUtxo, + note: EncryptedNote, + super_key: &Self::SuperPostingKey, + ) { + let _ = super_key; + let shard = self + .shards + .get_mut(&MerkleTreeConfiguration::tree_index(&utxo.0)) + .expect("All shards exist when the ledger is constructed."); + shard.insert((utxo.0, note)); + self.utxos.insert(utxo.0); + self.utxo_forest.push(&utxo.0); + } +} + +impl TransferLedger<Config> for Ledger { + type AccountId = AccountId; + type Event = (); + type ValidSourceAccount = WrapPair<Self::AccountId, AssetValue>; + type ValidSinkAccount = WrapPair<Self::AccountId, AssetValue>; + type ValidProof = Wrap<()>; + type SuperPostingKey = (); + + #[inline] + fn check_source_accounts<I>( + &self, + asset_id: AssetId, + sources: I, + ) -> Result<Vec<Self::ValidSourceAccount>, InvalidSourceAccount<Self::AccountId>> + where + I: Iterator<Item = (Self::AccountId, AssetValue)>, + { + sources + .map(|(account_id, withdraw)| { + match self.accounts.get(&account_id) { + Some(map) => match map.get(&asset_id) { + Some(balance) => { + if balance >= &withdraw { + Ok(WrapPair(account_id, withdraw)) + } else { + Err(InvalidSourceAccount { + account_id, + balance: AccountBalance::Known(*balance), + withdraw, + }) + } + } + _ => { + // FIXME: What about zero values in `sources`? + Err(InvalidSourceAccount { + account_id, + balance: AccountBalance::Known(AssetValue(0)), + withdraw, + }) + } + }, + _ => Err(InvalidSourceAccount { + account_id, + balance: AccountBalance::UnknownAccount, + withdraw, + }), + } + }) + .collect() + } + + #[inline] + fn check_sink_accounts<I>( + &self, + sinks: I, + ) -> Result<Vec<Self::ValidSinkAccount>, InvalidSinkAccount<Self::AccountId>> + where + I: Iterator<Item = (Self::AccountId, AssetValue)>, + { + sinks + .map(move |(account_id, deposit)| { + if self.accounts.contains_key(&account_id) { + Ok(WrapPair(account_id, deposit)) + } else { + Err(InvalidSinkAccount { account_id }) + } + }) + .collect() + } + + #[inline] + fn is_valid( + &self, + asset_id: Option<AssetId>, + sources: &[SourcePostingKey<Config, Self>], + senders: &[SenderPostingKey<Config, Self>], + receivers: &[ReceiverPostingKey<Config, Self>], + sinks: &[SinkPostingKey<Config, Self>], + proof: Proof<Config>, + ) -> Option<(Self::ValidProof, Self::Event)> { + let verifying_context = self.verifying_context.select(TransferShape::select( + asset_id.is_some(), + sources.len(), + senders.len(), + receivers.len(), + sinks.len(), + )?); + ProofSystem::verify( + verifying_context, + &TransferPostingKey::generate_proof_input(asset_id, sources, senders, receivers, sinks), + &proof, + ) + .ok()? + .then(move || (Wrap(()), ())) + } + + #[inline] + fn update_public_balances( + &mut self, + asset_id: AssetId, + sources: Vec<SourcePostingKey<Config, Self>>, + sinks: Vec<SinkPostingKey<Config, Self>>, + proof: Self::ValidProof, + super_key: &TransferLedgerSuperPostingKey<Config, Self>, + ) { + let _ = (proof, super_key); + for WrapPair(account_id, withdraw) in sources { + *self + .accounts + .get_mut(&account_id) + .expect("We checked that this account exists.") + .get_mut(&asset_id) + .expect("We checked that this account has enough balance to withdraw.") -= withdraw; + } + for WrapPair(account_id, deposit) in sinks { + *self + .accounts + .get_mut(&account_id) + .expect("We checked that this account exists.") + .entry(asset_id) + .or_default() += deposit; + } + } +} + +/// Checkpoint +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Checkpoint { + /// Receiver Index + pub receiver_index: [usize; MerkleTreeConfiguration::FOREST_WIDTH], + + /// Sender Index + pub sender_index: usize, +} + +impl Checkpoint { + /// Builds a new [`Checkpoint`] from `receiver_index` and `sender_index`. + #[inline] + pub fn new( + receiver_index: [usize; MerkleTreeConfiguration::FOREST_WIDTH], + sender_index: usize, + ) -> Self { + Self { + receiver_index, + sender_index, + } + } +} + +impl Default for Checkpoint { + #[inline] + fn default() -> Self { + Self::new([0; MerkleTreeConfiguration::FOREST_WIDTH], 0) + } +} + +impl ledger::Checkpoint for Checkpoint { + #[inline] + fn receiver_index(&self) -> usize { + self.receiver_index.iter().sum() + } +} + +/// Shared Ledger +pub type SharedLedger = Arc<RwLock<Ledger>>; + +/// Ledger Connection +pub struct LedgerConnection { + /// Ledger Account + account: AccountId, + + /// Ledger Accessor + ledger: SharedLedger, +} + +impl LedgerConnection { + /// Builds a new [`LedgerConnection`] for `account` and `ledger`. + #[inline] + pub fn new(account: AccountId, ledger: SharedLedger) -> Self { + Self { account, ledger } + } +} + +impl ledger::Connection<Config> for LedgerConnection { + type Checkpoint = Checkpoint; + type ReceiverChunk = Vec<(Utxo, EncryptedNote)>; + type SenderChunk = Vec<VoidNumber>; + type Error = Infallible; + + #[inline] + fn pull(&mut self, checkpoint: &Self::Checkpoint) -> PullResult<Config, Self> { + let ledger = self.ledger.read(); + let mut receivers = Vec::new(); + for (i, mut index) in checkpoint.receiver_index.iter().copied().enumerate() { + let shard = &ledger.shards[&MerkleForestIndex::from_index(i)]; + while let Some(entry) = shard.get_index(index) { + receivers.push(*entry); + index += 1; + } + } + let senders = ledger + .void_numbers + .iter() + .skip(checkpoint.sender_index) + .copied() + .collect(); + Ok(PullResponse { + should_continue: false, + checkpoint: Checkpoint::new( + into_array_unchecked( + ledger + .utxo_forest + .forest + .as_ref() + .iter() + .map(move |t| t.len()) + .collect::<Vec<_>>(), + ), + ledger.void_numbers.len(), + ), + receivers, + senders, + }) + } + + #[inline] + fn push(&mut self, posts: Vec<TransferPost>) -> PushResult<Config, Self> { + let mut ledger = self.ledger.write(); + for post in posts { + let (sources, sinks) = match TransferShape::from_post(&post) { + Some(TransferShape::Mint) => (vec![self.account], vec![]), + Some(TransferShape::PrivateTransfer) => (vec![], vec![]), + Some(TransferShape::Reclaim) => (vec![], vec![self.account]), + _ => return Ok(PushResponse { success: false }), + }; + match post.validate(sources, sinks, &*ledger) { + Ok(posting_key) => { + posting_key.post(&(), &mut *ledger); + } + Err(err) => { + println!("ERROR: {:?}", err); + return Ok(PushResponse { success: false }); + } + } + } + Ok(PushResponse { success: true }) + } +} + +impl PublicBalanceOracle for LedgerConnection { + #[inline] + fn public_balances(&self) -> Option<AssetList> { + Some( + self.ledger + .read() + .accounts + .get(&self.account)? + .iter() + .map(|(id, value)| Asset::new(*id, *value)) + .collect(), + ) + } +} diff --git a/manta-pay/src/simulation/ledger/server.rs b/manta-pay/src/simulation/ledger/server.rs new file mode 100644 index 000000000..ebdbcb2f6 --- /dev/null +++ b/manta-pay/src/simulation/ledger/server.rs @@ -0,0 +1,17 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Ledger Simulation Server diff --git a/manta-pay/src/simulation/mod.rs b/manta-pay/src/simulation/mod.rs index 0db77356e..5f6b83005 100644 --- a/manta-pay/src/simulation/mod.rs +++ b/manta-pay/src/simulation/mod.rs @@ -14,34 +14,23 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. -//! Ledger Simulation +//! Manta Pay Protocol Simulation // TODO: Move as much of this code into `manta-accounting` simulation as possible. // TODO: How to model existential deposits and fee payments? // TODO: Add in some concurrency (and measure how much we need it). use crate::{ - config::{ - Config, EncryptedNote, FullParameters, MerkleTreeConfiguration, MultiProvingContext, - MultiVerifyingContext, ProofSystem, TransferPost, Utxo, UtxoSetModel, VoidNumber, - }, + config::{Config, FullParameters, MultiProvingContext, UtxoSetModel}, signer::base::{Signer, UtxoSet}, }; use alloc::{sync::Arc, vec::Vec}; -use core::convert::Infallible; -use indexmap::IndexSet; use manta_accounting::{ + self, asset::{Asset, AssetId, AssetList, AssetValue}, key::AccountTable, transfer, - transfer::{ - canonical::TransferShape, AccountBalance, InvalidSinkAccount, InvalidSourceAccount, Proof, - ReceiverLedger, ReceiverPostingKey, SenderLedger, SenderPostingKey, SinkPostingKey, - SourcePostingKey, TransferLedger, TransferLedgerSuperPostingKey, TransferPostingKey, - UtxoSetOutput, - }, wallet::{ - ledger::{self, PullResponse, PullResult, PushResponse, PushResult}, test::{ sim::{ActionSim, Simulator}, ActionType, Actor, PublicBalanceOracle, Simulation, @@ -49,453 +38,27 @@ use manta_accounting::{ BalanceState, Wallet, }, }; -use manta_crypto::{ - constraint::ProofSystem as _, - merkle_tree::{ - self, - forest::{Configuration, FixedIndex}, - Tree, - }, - rand::{CryptoRng, Rand, RngCore, SeedableRng}, -}; -use manta_util::into_array_unchecked; +use manta_crypto::rand::{CryptoRng, Rand, RngCore, SeedableRng}; use parking_lot::RwLock; use rand_chacha::ChaCha20Rng; -use std::collections::{HashMap, HashSet}; - -/// Merkle Forest Index -pub type MerkleForestIndex = <MerkleTreeConfiguration as Configuration>::Index; - -/// UTXO Merkle Forest Type -pub type UtxoMerkleForest = merkle_tree::forest::TreeArrayMerkleForest< - MerkleTreeConfiguration, - merkle_tree::single_path::SinglePath<MerkleTreeConfiguration>, - { MerkleTreeConfiguration::FOREST_WIDTH }, ->; - -/// Wrap Type -#[derive(Clone, Copy)] -pub struct Wrap<T>(T); - -impl<T> AsRef<T> for Wrap<T> { - #[inline] - fn as_ref(&self) -> &T { - &self.0 - } -} - -/// Wrap Pair Type -#[derive(Clone, Copy)] -pub struct WrapPair<L, R>(L, R); - -impl<L, R> AsRef<R> for WrapPair<L, R> { - #[inline] - fn as_ref(&self) -> &R { - &self.1 - } -} - -/// Account Id -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct AccountId(pub u64); - -/// Ledger -#[derive(Debug)] -pub struct Ledger { - /// Void Numbers - void_numbers: IndexSet<VoidNumber>, - - /// UTXOs - utxos: HashSet<Utxo>, - - /// Shards - shards: HashMap<MerkleForestIndex, IndexSet<(Utxo, EncryptedNote)>>, - - /// UTXO Forest - utxo_forest: UtxoMerkleForest, - - /// Account Table - accounts: HashMap<AccountId, HashMap<AssetId, AssetValue>>, - - /// Verifying Contexts - verifying_context: MultiVerifyingContext, -} - -impl Ledger { - /// Builds an empty [`Ledger`]. - #[inline] - pub fn new( - utxo_forest_parameters: UtxoSetModel, - verifying_context: MultiVerifyingContext, - ) -> Self { - Self { - void_numbers: Default::default(), - utxos: Default::default(), - shards: (0..MerkleTreeConfiguration::FOREST_WIDTH) - .map(move |i| (MerkleForestIndex::from_index(i), Default::default())) - .collect(), - utxo_forest: UtxoMerkleForest::new(utxo_forest_parameters), - accounts: Default::default(), - verifying_context, - } - } - - /// Sets the public balance of `account` in assets with `id` to `value`. - #[inline] - pub fn set_public_balance(&mut self, account: AccountId, id: AssetId, value: AssetValue) { - self.accounts.entry(account).or_default().insert(id, value); - } -} - -impl SenderLedger<Config> for Ledger { - type ValidVoidNumber = Wrap<VoidNumber>; - type ValidUtxoSetOutput = Wrap<UtxoSetOutput<Config>>; - type SuperPostingKey = (Wrap<()>, ()); - - #[inline] - fn is_unspent(&self, void_number: VoidNumber) -> Option<Self::ValidVoidNumber> { - if self.void_numbers.contains(&void_number) { - None - } else { - Some(Wrap(void_number)) - } - } - - #[inline] - fn has_matching_utxo_set_output( - &self, - output: UtxoSetOutput<Config>, - ) -> Option<Self::ValidUtxoSetOutput> { - for tree in self.utxo_forest.forest.as_ref() { - if tree.root() == &output { - return Some(Wrap(output)); - } - } - None - } - - #[inline] - fn spend( - &mut self, - utxo_set_output: Self::ValidUtxoSetOutput, - void_number: Self::ValidVoidNumber, - super_key: &Self::SuperPostingKey, - ) { - let _ = (utxo_set_output, super_key); - self.void_numbers.insert(void_number.0); - } -} - -impl ReceiverLedger<Config> for Ledger { - type ValidUtxo = Wrap<Utxo>; - type SuperPostingKey = (Wrap<()>, ()); - - #[inline] - fn is_not_registered(&self, utxo: Utxo) -> Option<Self::ValidUtxo> { - if self.utxos.contains(&utxo) { - None - } else { - Some(Wrap(utxo)) - } - } - - #[inline] - fn register( - &mut self, - utxo: Self::ValidUtxo, - note: EncryptedNote, - super_key: &Self::SuperPostingKey, - ) { - let _ = super_key; - let shard = self - .shards - .get_mut(&MerkleTreeConfiguration::tree_index(&utxo.0)) - .expect("All shards exist when the ledger is constructed."); - shard.insert((utxo.0, note)); - self.utxos.insert(utxo.0); - self.utxo_forest.push(&utxo.0); - } -} - -impl TransferLedger<Config> for Ledger { - type AccountId = AccountId; - type Event = (); - type ValidSourceAccount = WrapPair<Self::AccountId, AssetValue>; - type ValidSinkAccount = WrapPair<Self::AccountId, AssetValue>; - type ValidProof = Wrap<()>; - type SuperPostingKey = (); - #[inline] - fn check_source_accounts<I>( - &self, - asset_id: AssetId, - sources: I, - ) -> Result<Vec<Self::ValidSourceAccount>, InvalidSourceAccount<Self::AccountId>> - where - I: Iterator<Item = (Self::AccountId, AssetValue)>, - { - sources - .map(|(account_id, withdraw)| { - match self.accounts.get(&account_id) { - Some(map) => match map.get(&asset_id) { - Some(balance) => { - if balance >= &withdraw { - Ok(WrapPair(account_id, withdraw)) - } else { - Err(InvalidSourceAccount { - account_id, - balance: AccountBalance::Known(*balance), - withdraw, - }) - } - } - _ => { - // FIXME: What about zero values in `sources`? - Err(InvalidSourceAccount { - account_id, - balance: AccountBalance::Known(AssetValue(0)), - withdraw, - }) - } - }, - _ => Err(InvalidSourceAccount { - account_id, - balance: AccountBalance::UnknownAccount, - withdraw, - }), - } - }) - .collect() - } - - #[inline] - fn check_sink_accounts<I>( - &self, - sinks: I, - ) -> Result<Vec<Self::ValidSinkAccount>, InvalidSinkAccount<Self::AccountId>> - where - I: Iterator<Item = (Self::AccountId, AssetValue)>, - { - sinks - .map(move |(account_id, deposit)| { - if self.accounts.contains_key(&account_id) { - Ok(WrapPair(account_id, deposit)) - } else { - Err(InvalidSinkAccount { account_id }) - } - }) - .collect() - } - - #[inline] - fn is_valid( - &self, - asset_id: Option<AssetId>, - sources: &[SourcePostingKey<Config, Self>], - senders: &[SenderPostingKey<Config, Self>], - receivers: &[ReceiverPostingKey<Config, Self>], - sinks: &[SinkPostingKey<Config, Self>], - proof: Proof<Config>, - ) -> Option<(Self::ValidProof, Self::Event)> { - let verifying_context = self.verifying_context.select(TransferShape::select( - asset_id.is_some(), - sources.len(), - senders.len(), - receivers.len(), - sinks.len(), - )?); - ProofSystem::verify( - verifying_context, - &TransferPostingKey::generate_proof_input(asset_id, sources, senders, receivers, sinks), - &proof, - ) - .ok()? - .then(move || (Wrap(()), ())) - } - - #[inline] - fn update_public_balances( - &mut self, - asset_id: AssetId, - sources: Vec<SourcePostingKey<Config, Self>>, - sinks: Vec<SinkPostingKey<Config, Self>>, - proof: Self::ValidProof, - super_key: &TransferLedgerSuperPostingKey<Config, Self>, - ) { - let _ = (proof, super_key); - for WrapPair(account_id, withdraw) in sources { - *self - .accounts - .get_mut(&account_id) - .expect("We checked that this account exists.") - .get_mut(&asset_id) - .expect("We checked that this account has enough balance to withdraw.") -= withdraw; - } - for WrapPair(account_id, deposit) in sinks { - *self - .accounts - .get_mut(&account_id) - .expect("We checked that this account exists.") - .entry(asset_id) - .or_default() += deposit; - } - } -} - -/// Checkpoint -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Checkpoint { - /// Receiver Index - pub receiver_index: [usize; MerkleTreeConfiguration::FOREST_WIDTH], - - /// Sender Index - pub sender_index: usize, -} - -impl Checkpoint { - /// Builds a new [`Checkpoint`] from `receiver_index` and `sender_index`. - #[inline] - pub fn new( - receiver_index: [usize; MerkleTreeConfiguration::FOREST_WIDTH], - sender_index: usize, - ) -> Self { - Self { - receiver_index, - sender_index, - } - } -} - -impl Default for Checkpoint { - #[inline] - fn default() -> Self { - Self::new([0; MerkleTreeConfiguration::FOREST_WIDTH], 0) - } -} - -impl ledger::Checkpoint for Checkpoint { - #[inline] - fn receiver_index(&self) -> usize { - self.receiver_index.iter().sum() - } -} - -/// Shared Ledger -pub type SharedLedger = Arc<RwLock<Ledger>>; - -/// Ledger Connection -pub struct LedgerConnection { - /// Ledger Account - account: AccountId, - - /// Ledger Accessor - ledger: SharedLedger, -} - -impl LedgerConnection { - /// Builds a new [`LedgerConnection`] for `account` and `ledger`. - #[inline] - pub fn new(account: AccountId, ledger: SharedLedger) -> Self { - Self { account, ledger } - } -} - -impl ledger::Connection<Config> for LedgerConnection { - type Checkpoint = Checkpoint; - type ReceiverChunk = Vec<(Utxo, EncryptedNote)>; - type SenderChunk = Vec<VoidNumber>; - type Error = Infallible; - - #[inline] - fn pull(&mut self, checkpoint: &Self::Checkpoint) -> PullResult<Config, Self> { - let ledger = self.ledger.read(); - let mut receivers = Vec::new(); - for (i, mut index) in checkpoint.receiver_index.iter().copied().enumerate() { - let shard = &ledger.shards[&MerkleForestIndex::from_index(i)]; - while let Some(entry) = shard.get_index(index) { - receivers.push(*entry); - index += 1; - } - } - let senders = ledger - .void_numbers - .iter() - .skip(checkpoint.sender_index) - .copied() - .collect(); - Ok(PullResponse { - should_continue: false, - checkpoint: Checkpoint::new( - into_array_unchecked( - ledger - .utxo_forest - .forest - .as_ref() - .iter() - .map(move |t| t.len()) - .collect::<Vec<_>>(), - ), - ledger.void_numbers.len(), - ), - receivers, - senders, - }) - } - - #[inline] - fn push(&mut self, posts: Vec<TransferPost>) -> PushResult<Config, Self> { - let mut ledger = self.ledger.write(); - for post in posts { - let (sources, sinks) = match TransferShape::from_post(&post) { - Some(TransferShape::Mint) => (vec![self.account], vec![]), - Some(TransferShape::PrivateTransfer) => (vec![], vec![]), - Some(TransferShape::Reclaim) => (vec![], vec![self.account]), - _ => return Ok(PushResponse { success: false }), - }; - match post.validate(sources, sinks, &*ledger) { - Ok(posting_key) => { - posting_key.post(&(), &mut *ledger); - } - Err(err) => { - println!("ERROR: {:?}", err); - return Ok(PushResponse { success: false }); - } - } - } - Ok(PushResponse { success: true }) - } -} - -impl PublicBalanceOracle for LedgerConnection { - #[inline] - fn public_balances(&self) -> Option<AssetList> { - Some( - self.ledger - .read() - .accounts - .get(&self.account)? - .iter() - .map(|(id, value)| Asset::new(*id, *value)) - .collect(), - ) - } -} +pub mod ledger; /// Samples an empty wallet for `account` on `ledger`. #[inline] pub fn sample_wallet<R>( - account: AccountId, - ledger: &SharedLedger, + account: ledger::AccountId, + ledger: &ledger::SharedLedger, cache: &MultiProvingContext, parameters: &transfer::Parameters<Config>, utxo_set_model: &UtxoSetModel, rng: &mut R, -) -> Wallet<Config, LedgerConnection, Signer> +) -> Wallet<Config, ledger::LedgerConnection, Signer> where R: CryptoRng + RngCore + ?Sized, { Wallet::new( - LedgerConnection::new(account, ledger.clone()), + ledger::LedgerConnection::new(account, ledger.clone()), Signer::new( AccountTable::new(rng.gen()), cache.clone(), @@ -510,7 +73,7 @@ where #[inline] fn measure_balances<'w, I>(wallets: I) -> AssetList where - I: IntoIterator<Item = &'w mut Wallet<Config, LedgerConnection, Signer>>, + I: IntoIterator<Item = &'w mut Wallet<Config, ledger::LedgerConnection, Signer>>, { let mut balances = AssetList::new(); for wallet in wallets { @@ -540,12 +103,12 @@ pub fn simulate(actor_count: usize, actor_lifetime: usize) { ) .expect("Failed to generate contexts."); - let mut ledger = Ledger::new(utxo_set_model.clone(), verifying_context); + let mut ledger = ledger::Ledger::new(utxo_set_model.clone(), verifying_context); for i in 0..actor_count { - ledger.set_public_balance(AccountId(i as u64), AssetId(0), AssetValue(1000000)); - ledger.set_public_balance(AccountId(i as u64), AssetId(1), AssetValue(1000000)); - ledger.set_public_balance(AccountId(i as u64), AssetId(2), AssetValue(1000000)); + ledger.set_public_balance(ledger::AccountId(i as u64), AssetId(0), AssetValue(1000000)); + ledger.set_public_balance(ledger::AccountId(i as u64), AssetId(1), AssetValue(1000000)); + ledger.set_public_balance(ledger::AccountId(i as u64), AssetId(2), AssetValue(1000000)); } let ledger = Arc::new(RwLock::new(ledger)); @@ -556,7 +119,7 @@ pub fn simulate(actor_count: usize, actor_lifetime: usize) { .map(|i| { Actor::new( sample_wallet( - AccountId(i as u64), + ledger::AccountId(i as u64), &ledger, &proving_context, &parameters, diff --git a/manta-pay/src/util/http.rs b/manta-pay/src/util/http.rs new file mode 100644 index 000000000..c53abe22b --- /dev/null +++ b/manta-pay/src/util/http.rs @@ -0,0 +1,82 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! HTTP Utilities + +use manta_util::serde::Serialize; +use reqwest::blocking; + +pub use reqwest::{blocking::Response, Error, IntoUrl, Method, Url}; + +/// Blocking HTTP Client +/// +/// This client is a wrapper around [`reqwest::blocking::Client`] which has a known server URL. +pub struct Client { + /// Server URL + pub server_url: Url, + + /// Base HTTP Client + pub client: blocking::Client, +} + +impl Client { + /// Builds a new HTTP [`Client`] that connects to `server_url`. + #[inline] + pub fn new<U>(server_url: U) -> Result<Self, Error> + where + U: IntoUrl, + { + Ok(Self { + client: blocking::Client::builder().build()?, + server_url: server_url.into_url()?, + }) + } + + /// Sends a new request of type `command` with body `request`. + #[inline] + pub fn request<T>(&self, method: Method, command: &str, request: T) -> Result<Response, Error> + where + T: Serialize, + { + self.client + .request( + method, + self.server_url + .join(command) + .expect("This error branch is not allowed to happen."), + ) + .json(&request) + .send() + } + + /// Sends a GET request of type `command` with body `request`. + #[inline] + pub fn get<T>(&self, command: &str, request: T) -> Result<Response, Error> + where + T: Serialize, + { + self.request(Method::GET, command, request) + } + + /// Sends a POST request of type `command` with body `request`. + #[inline] + pub fn post<T>(&self, command: &str, request: T) -> Result<Response, Error> + where + T: Serialize, + { + self.request(Method::POST, command, request) + } +} diff --git a/manta-pay/src/util/mod.rs b/manta-pay/src/util/mod.rs new file mode 100644 index 000000000..f210fbcb8 --- /dev/null +++ b/manta-pay/src/util/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 <http://www.gnu.org/licenses/>. + +//! Manta Pay Utilities + +#[cfg(feature = "http")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "http")))] +pub mod http; From cba8d5a4426744a65d3603af76cf889a70d7ac40 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Fri, 25 Feb 2022 19:16:59 -0500 Subject: [PATCH 260/275] feat: add ledger simulation http client --- manta-pay/src/signer/client/http.rs | 6 +- manta-pay/src/simulation/ledger/client.rs | 75 +++++++++++++++++++++++ manta-pay/src/simulation/ledger/mod.rs | 28 +++++++-- manta-pay/src/util/http.rs | 14 +++-- 4 files changed, 110 insertions(+), 13 deletions(-) diff --git a/manta-pay/src/signer/client/http.rs b/manta-pay/src/signer/client/http.rs index 18b2ecbb0..19f12f904 100644 --- a/manta-pay/src/signer/client/http.rs +++ b/manta-pay/src/signer/client/http.rs @@ -50,7 +50,7 @@ impl signer::Connection<Config> for Client { ) -> Result<Result<SyncResponse, SyncError>, Self::Error> { // NOTE: The synchronization command modifies the signer so it must be a POST command // to match the HTTP semantics. - self.0.post("sync", request)?.json() + self.0.post("sync", request) } #[inline] @@ -60,7 +60,7 @@ impl signer::Connection<Config> for Client { ) -> Result<Result<SignResponse, SignError>, Self::Error> { // NOTE: The signing command does not modify the signer so it must be a GET command to match // the HTTP semantics. - self.0.get("sign", transaction)?.json() + self.0.get("sign", transaction) } #[inline] @@ -70,6 +70,6 @@ impl signer::Connection<Config> for Client { ) -> Result<Vec<ReceivingKey>, Self::Error> { // NOTE: The receiving key command modifies the signer so it must be a POST command to match // the HTTP semantics. - self.0.post("receivingKeys", request)?.json() + self.0.post("receivingKeys", request) } } diff --git a/manta-pay/src/simulation/ledger/client.rs b/manta-pay/src/simulation/ledger/client.rs index 12e86edc1..671727763 100644 --- a/manta-pay/src/simulation/ledger/client.rs +++ b/manta-pay/src/simulation/ledger/client.rs @@ -15,3 +15,78 @@ // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. //! Ledger Simulation Client + +use crate::{ + config::{Config, EncryptedNote, TransferPost, Utxo, VoidNumber}, + simulation::ledger::{AccountId, Checkpoint}, + util::http::{self, Error, IntoUrl}, +}; +use manta_accounting::wallet::ledger::{self, PullResult, PushResult}; +use manta_util::serde::{Deserialize, Serialize}; + +/// HTTP Ledger Client +pub struct Client { + /// Account Id + account: AccountId, + + /// Client Connection + client: http::Client, +} + +impl Client { + /// Builds a new HTTP [`Client`] that connects to `server_url`. + #[inline] + pub fn new<U>(account: AccountId, server_url: U) -> Result<Self, Error> + where + U: IntoUrl, + { + Ok(Self { + account, + client: http::Client::new(server_url)?, + }) + } +} + +/// HTTP Client Request +#[derive(Deserialize, Serialize)] +#[serde(crate = "manta_util::serde", deny_unknown_fields)] +struct Request<T> { + /// Account Id + account: AccountId, + + /// Request Payload + request: T, +} + +impl ledger::Connection<Config> for Client { + type Checkpoint = Checkpoint; + type ReceiverChunk = Vec<(Utxo, EncryptedNote)>; + type SenderChunk = Vec<VoidNumber>; + type Error = Error; + + #[inline] + fn pull(&mut self, checkpoint: &Self::Checkpoint) -> PullResult<Config, Self> { + // NOTE: The pull command does not modify the ledger so it must be a GET command to match + // the HTTP semantics. + self.client.get( + "pull", + Request { + account: self.account, + request: checkpoint, + }, + ) + } + + #[inline] + fn push(&mut self, posts: Vec<TransferPost>) -> PushResult<Config, Self> { + // NOTE: The push command modifies the ledger so it must be a POST command to match the + // HTTP semantics. + self.client.post( + "push", + Request { + account: self.account, + request: posts, + }, + ) + } +} diff --git a/manta-pay/src/simulation/ledger/mod.rs b/manta-pay/src/simulation/ledger/mod.rs index 895daf5d2..b053082fe 100644 --- a/manta-pay/src/simulation/ledger/mod.rs +++ b/manta-pay/src/simulation/ledger/mod.rs @@ -44,11 +44,19 @@ use manta_crypto::{ Tree, }, }; -use manta_util::into_array_unchecked; +use manta_util::Array; use parking_lot::RwLock; use std::collections::{HashMap, HashSet}; +#[cfg(feature = "serde")] +use manta_util::serde::{Deserialize, Serialize}; + +#[cfg(feature = "serde")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] pub mod client; + +#[cfg(feature = "serde")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] pub mod server; /// Merkle Forest Index @@ -84,6 +92,11 @@ impl<L, R> AsRef<R> for WrapPair<L, R> { } /// Account Id +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields, transparent) +)] #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct AccountId(pub u64); @@ -331,10 +344,15 @@ impl TransferLedger<Config> for Ledger { } /// Checkpoint +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Checkpoint { /// Receiver Index - pub receiver_index: [usize; MerkleTreeConfiguration::FOREST_WIDTH], + pub receiver_index: Array<usize, { MerkleTreeConfiguration::FOREST_WIDTH }>, /// Sender Index pub sender_index: usize, @@ -344,7 +362,7 @@ impl Checkpoint { /// Builds a new [`Checkpoint`] from `receiver_index` and `sender_index`. #[inline] pub fn new( - receiver_index: [usize; MerkleTreeConfiguration::FOREST_WIDTH], + receiver_index: Array<usize, { MerkleTreeConfiguration::FOREST_WIDTH }>, sender_index: usize, ) -> Self { Self { @@ -357,7 +375,7 @@ impl Checkpoint { impl Default for Checkpoint { #[inline] fn default() -> Self { - Self::new([0; MerkleTreeConfiguration::FOREST_WIDTH], 0) + Self::new([0; MerkleTreeConfiguration::FOREST_WIDTH].into(), 0) } } @@ -414,7 +432,7 @@ impl ledger::Connection<Config> for LedgerConnection { Ok(PullResponse { should_continue: false, checkpoint: Checkpoint::new( - into_array_unchecked( + Array::from_unchecked( ledger .utxo_forest .forest diff --git a/manta-pay/src/util/http.rs b/manta-pay/src/util/http.rs index c53abe22b..300607894 100644 --- a/manta-pay/src/util/http.rs +++ b/manta-pay/src/util/http.rs @@ -16,7 +16,7 @@ //! HTTP Utilities -use manta_util::serde::Serialize; +use manta_util::serde::{de::DeserializeOwned, Serialize}; use reqwest::blocking; pub use reqwest::{blocking::Response, Error, IntoUrl, Method, Url}; @@ -47,9 +47,10 @@ impl Client { /// Sends a new request of type `command` with body `request`. #[inline] - pub fn request<T>(&self, method: Method, command: &str, request: T) -> Result<Response, Error> + pub fn request<T, R>(&self, method: Method, command: &str, request: T) -> Result<R, Error> where T: Serialize, + R: DeserializeOwned, { self.client .request( @@ -59,23 +60,26 @@ impl Client { .expect("This error branch is not allowed to happen."), ) .json(&request) - .send() + .send()? + .json() } /// Sends a GET request of type `command` with body `request`. #[inline] - pub fn get<T>(&self, command: &str, request: T) -> Result<Response, Error> + pub fn get<T, R>(&self, command: &str, request: T) -> Result<R, Error> where T: Serialize, + R: DeserializeOwned, { self.request(Method::GET, command, request) } /// Sends a POST request of type `command` with body `request`. #[inline] - pub fn post<T>(&self, command: &str, request: T) -> Result<Response, Error> + pub fn post<T, R>(&self, command: &str, request: T) -> Result<R, Error> where T: Serialize, + R: DeserializeOwned, { self.request(Method::POST, command, request) } From b03aa7f5596df467b8341bdc68d3df1e4a2402bd Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 27 Feb 2022 14:08:05 -0500 Subject: [PATCH 261/275] feat: add back workflows --- .github/dependabot.yml | 10 ++++++ .github/workflows/audit.yml | 14 ++++++++ .github/workflows/ci.yml | 72 +++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/audit.yml create mode 100644 .github/workflows/ci.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..13b4ef51e --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + - package-ecosystem: "cargo" + directory: "/" + schedule: + interval: "daily" diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml new file mode 100644 index 000000000..03ff19641 --- /dev/null +++ b/.github/workflows/audit.yml @@ -0,0 +1,14 @@ +name: Audit +on: + pull_request: + push: + schedule: + - cron: '0 */12 * * *' +jobs: + audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2.4.0 + - uses: actions-rs/audit-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..5206e4d0d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,72 @@ +name: CI +on: + pull_request: + push: + schedule: + - cron: '0 0 * * */2' +env: + CARGO_TERM_COLOR: always + RUSTFLAGS: -D warnings + RUST_BACKTRACE: full +jobs: + test: + name: Test (${{ matrix.os }}) + strategy: + fail-fast: false + matrix: + os: + - macos-latest + - ubuntu-latest + - wasm32-unknown-unknown + - windows-latest + channel: + - stable + - nightly + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v2.4.0 + - run: rustup update ${{ matrix.channel }} --no-self-update && rustup default ${{ matrix.channel }} + - run: cargo install cargo-hack + - run: cargo hack test --feature-powerset --workspace + - run: cargo hack test --feature-powerset --workspace --release + lint: + name: Lint (${{ matrix.os }}) + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + channel: + - stable + - nightly + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v2.4.0 + - run: rustup update ${{ matrix.channel }} && rustup default ${{ matrix.channel }} && rustup component add clippy + - run: cargo install cargo-hack + - run: cargo hack clippy --feature-powerset --tests --workspace + format: + name: Format + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@2.4.0 + - run: rustup update nightly && rustup default nightly && rustup component add rustfmt + - run: cargo fmt --all -- --check + docs: + name: Docs + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2.4.0 + - run: rustup update nightly && rustup default nightly + - run: RUSTDOCFLAGS="-D warnings --cfg doc_cfg" cargo doc --workspace --all-features --no-deps + bench: + name: Bench (${{ matrix.os }}) + strategy: + matrix: + os: + - ubuntu-latest + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v2.4.0 + - run: rustup update nightly && rustup default nightly + - run: cargo bench --workspace --all-features From b731f79e6f417c2fbba33827951e2e92afdfc8e5 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 27 Feb 2022 14:23:58 -0500 Subject: [PATCH 262/275] fix: derive correct error trait for serde --- .github/workflows/ci.yml | 4 ++-- manta-accounting/src/fs/serde.rs | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5206e4d0d..7d3d60b7c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,7 +49,7 @@ jobs: name: Format runs-on: ubuntu-latest steps: - - uses: actions/checkout@2.4.0 + - uses: actions/checkout@v2.4.0 - run: rustup update nightly && rustup default nightly && rustup component add rustfmt - run: cargo fmt --all -- --check docs: @@ -69,4 +69,4 @@ jobs: steps: - uses: actions/checkout@v2.4.0 - run: rustup update nightly && rustup default nightly - - run: cargo bench --workspace --all-features + - run: cargo bench --no-run --workspace --all-features diff --git a/manta-accounting/src/fs/serde.rs b/manta-accounting/src/fs/serde.rs index c405b4ae6..23768019c 100644 --- a/manta-accounting/src/fs/serde.rs +++ b/manta-accounting/src/fs/serde.rs @@ -85,8 +85,7 @@ pub mod ser { } } - #[cfg(feature = "std")] - impl<F> std::error::Error for Error<F> + impl<F> serde::ser::StdError for Error<F> where F: File, F::Error: Debug + Display, @@ -724,8 +723,7 @@ pub mod de { } } - #[cfg(feature = "std")] - impl<F> std::error::Error for Error<F> + impl<F> serde::de::StdError for Error<F> where F: File, F::Error: Debug + Display, From de0e956e292a89280faa8916d3c1e0910567ca26 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 27 Feb 2022 14:45:57 -0500 Subject: [PATCH 263/275] feat: remove empty benchmarks and unused global library --- Cargo.toml | 42 --------------------------------------- manta-pay/Cargo.toml | 5 ----- manta-pay/benches/main.rs | 17 ---------------- src/lib.rs | 34 ------------------------------- 4 files changed, 98 deletions(-) delete mode 100644 manta-pay/benches/main.rs delete mode 100644 src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 13c735d91..edd5578d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,29 +1,3 @@ -[package] -name = "manta" -edition = "2021" -version = "0.4.0" -authors = ["Manta Network <contact@manta.network>"] -readme = "README.md" -license-file = "LICENSE" -repository = "https://github.com/Manta-Network/manta-rs" -homepage = "https://github.com/Manta-Network" -documentation = "https://github.com/Manta-Network/manta-rs" -categories = [""] -keywords = [""] -description = "Rust Crates for the Manta Network Ecosystem" -publish = false - -[package.metadata.docs.rs] -# To build locally: -# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --open -all-features = true -rustdoc-args = ["--cfg", "doc_cfg"] - -[badges] -is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } -is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } -maintenance = { status = "actively-developed" } - [workspace] resolver = "2" members = [ @@ -33,19 +7,3 @@ members = [ "manta-util", ] -[features] -# Default Features -default = [] - -# Standard Library -std = [] - -# Test Frameworks -test = ["manta-accounting/test", "manta-crypto/test"] - -[dependencies] -manta-accounting = { path = "manta-accounting" } -manta-crypto = { path = "manta-crypto" } -manta-pay = { path = "manta-pay" } -manta-util = { path = "manta-util" } - diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 5f178a8ae..1d155541b 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -24,10 +24,6 @@ is-it-maintained-issue-resolution = { repository = "Manta-Network/manta-rs" } is-it-maintained-open-issues = { repository = "Manta-Network/manta-rs" } maintenance = { status = "actively-developed" } -[[bench]] -name = "main" -harness = false - [[bin]] name = "generate_parameters" required-features = ["groth16", "manta-util/std", "test"] @@ -121,5 +117,4 @@ tungstenite = { version = "0.17.2", optional = true, default-features = false, f # TODO: zk-garage-plonk = { package = "plonk", git = "https://github.com/zk-garage/plonk", optional = true, default-features = false } [dev-dependencies] -criterion = "0.3.5" manta-pay = { path = ".", features = ["groth16", "test"] } diff --git a/manta-pay/benches/main.rs b/manta-pay/benches/main.rs deleted file mode 100644 index e27e450db..000000000 --- a/manta-pay/benches/main.rs +++ /dev/null @@ -1,17 +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 <http://www.gnu.org/licenses/>. - -//! Manta-Pay Benchmarks diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 2aa16e4fd..000000000 --- a/src/lib.rs +++ /dev/null @@ -1,34 +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 <http://www.gnu.org/licenses/>. - -//! Manta Network Crates - -#![cfg_attr(not(any(feature = "std", test)), no_std)] -#![cfg_attr(doc_cfg, feature(doc_cfg))] -#![forbid(rustdoc::broken_intra_doc_links)] -#![forbid(missing_docs)] - -#[doc(inline)] -pub use manta_accounting as accounting; - -#[doc(inline)] -pub use manta_crypto as crypto; - -#[doc(inline)] -pub use manta_pay as pay; - -#[doc(inline)] -pub use manta_util as util; From 71a7027bbd18e221252f3e25f68a1e9c41436063 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 27 Feb 2022 14:58:53 -0500 Subject: [PATCH 264/275] fix: simplify testing/checking pipeline --- .github/workflows/ci.yml | 6 ++---- manta-pay/src/crypto/encryption.rs | 2 +- manta-pay/src/test/transfer.rs | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7d3d60b7c..2469a120e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,9 +26,7 @@ jobs: steps: - uses: actions/checkout@v2.4.0 - run: rustup update ${{ matrix.channel }} --no-self-update && rustup default ${{ matrix.channel }} - - run: cargo install cargo-hack - - run: cargo hack test --feature-powerset --workspace - - run: cargo hack test --feature-powerset --workspace --release + - run: cargo test --all-features --workspace --release lint: name: Lint (${{ matrix.os }}) strategy: @@ -44,7 +42,7 @@ jobs: - uses: actions/checkout@v2.4.0 - run: rustup update ${{ matrix.channel }} && rustup default ${{ matrix.channel }} && rustup component add clippy - run: cargo install cargo-hack - - run: cargo hack clippy --feature-powerset --tests --workspace + - run: cargo hack clippy --feature-powerset --bins --examples --tests --workspace format: name: Format runs-on: ubuntu-latest diff --git a/manta-pay/src/crypto/encryption.rs b/manta-pay/src/crypto/encryption.rs index 7faee7525..4b932adce 100644 --- a/manta-pay/src/crypto/encryption.rs +++ b/manta-pay/src/crypto/encryption.rs @@ -84,7 +84,7 @@ pub mod aes { use manta_accounting::asset::Asset; use manta_crypto::{ encryption, - rand::{FromEntropy, RngCore}, + rand::{RngCore, SeedableRng}, }; use rand_chacha::ChaCha20Rng; diff --git a/manta-pay/src/test/transfer.rs b/manta-pay/src/test/transfer.rs index 0e6592274..7f15042ae 100644 --- a/manta-pay/src/test/transfer.rs +++ b/manta-pay/src/test/transfer.rs @@ -22,7 +22,7 @@ use crate::config::{ use manta_crypto::{ constraint::{measure::Measure, ProofSystem as _}, merkle_tree, - rand::{FromEntropy, Rand}, + rand::{Rand, SeedableRng}, }; use rand_chacha::ChaCha20Rng; From dcb84ad41a326dac3721fc5af0f355f6e9f806cc Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 27 Feb 2022 15:19:17 -0500 Subject: [PATCH 265/275] fix: include serde feature for http --- manta-pay/Cargo.toml | 2 +- manta-pay/src/signer/client/http.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 1d155541b..9a0d53da6 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -53,7 +53,7 @@ arkworks = [ groth16 = ["ark-groth16", "ark-snark", "arkworks"] # Enable HTTP Signer Client -http = ["reqwest"] +http = ["reqwest", "serde"] # TODO: Enable PLONK ZKP System # TODO: plonk = ["zk-garage-plonk"] diff --git a/manta-pay/src/signer/client/http.rs b/manta-pay/src/signer/client/http.rs index 19f12f904..5c2a1463e 100644 --- a/manta-pay/src/signer/client/http.rs +++ b/manta-pay/src/signer/client/http.rs @@ -21,6 +21,7 @@ use crate::{ signer::{ReceivingKeyRequest, SignError, SignResponse, SyncError, SyncRequest, SyncResponse}, util::http::{self, Error, IntoUrl}, }; +use alloc::vec::Vec; use manta_accounting::wallet::{self, signer}; /// Wallet Associated to [`Client`] From 51930a1bae4abedc7b1594d3602d96cd8ffc6d48 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 27 Feb 2022 16:30:28 -0500 Subject: [PATCH 266/275] fix: match features --- manta-pay/Cargo.toml | 6 +++--- manta-pay/src/key.rs | 2 +- manta-pay/src/signer/client/websocket.rs | 1 + manta-pay/src/simulation/ledger/mod.rs | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 9a0d53da6..b22c6933f 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -70,8 +70,8 @@ simulation = [ "manta-accounting/parallel", "parking_lot", "rayon", - "std", "test", + "wallet", ] # Standard Library @@ -84,7 +84,7 @@ test = ["manta-accounting/test", "manta-crypto/test"] wallet = ["bip32", "manta-crypto/getrandom", "std"] # Enable WebSocket Signer Client -websocket = ["serde_json", "tungstenite"] +websocket = ["serde", "serde_json", "std", "tungstenite"] [dependencies] aes-gcm = { version = "0.9.4", default-features = false, features = ["aes", "alloc"] } @@ -111,7 +111,7 @@ rayon = { version = "1.5.1", optional = true, default-features = false } reqwest = { version = "0.11.9", optional = true, default-features = false, features = ["blocking", "json"] } scale-codec = { package = "parity-scale-codec", version = "2.3.1", optional = true, default-features = false, features = ["derive", "max-encoded-len"] } scale-info = { version = "1.0.0", optional = true, default-features = false, features = ["derive"] } -serde_json = { version = "1.0.79", optional = true, default-features = false } +serde_json = { version = "1.0.79", optional = true, default-features = false, features = ["alloc"] } tempfile = { version = "3.2.0", optional = true, default-features = false } tungstenite = { version = "0.17.2", optional = true, default-features = false, features = ["native-tls"] } # TODO: zk-garage-plonk = { package = "plonk", git = "https://github.com/zk-garage/plonk", optional = true, default-features = false } diff --git a/manta-pay/src/key.rs b/manta-pay/src/key.rs index c8edb225e..0f2a39766 100644 --- a/manta-pay/src/key.rs +++ b/manta-pay/src/key.rs @@ -241,7 +241,7 @@ where #[derive(Clone)] pub struct Mnemonic( /// Underlying BIP39 Mnemonic - #[serde(serialize_with = "Mnemonic::serialize")] + #[cfg_attr(feature = "serde", serde(serialize_with = "Mnemonic::serialize"))] bip32::Mnemonic, ); diff --git a/manta-pay/src/signer/client/websocket.rs b/manta-pay/src/signer/client/websocket.rs index 4e1451a3e..29dbcb236 100644 --- a/manta-pay/src/signer/client/websocket.rs +++ b/manta-pay/src/signer/client/websocket.rs @@ -17,6 +17,7 @@ //! Signer WebSocket Client Implementation use crate::config::Config; +use alloc::vec::Vec; use manta_accounting::{ transfer::{canonical::Transaction, ReceivingKey}, wallet::{ diff --git a/manta-pay/src/simulation/ledger/mod.rs b/manta-pay/src/simulation/ledger/mod.rs index b053082fe..5f4d10a95 100644 --- a/manta-pay/src/simulation/ledger/mod.rs +++ b/manta-pay/src/simulation/ledger/mod.rs @@ -51,8 +51,8 @@ use std::collections::{HashMap, HashSet}; #[cfg(feature = "serde")] use manta_util::serde::{Deserialize, Serialize}; -#[cfg(feature = "serde")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] +#[cfg(feature = "http")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "http")))] pub mod client; #[cfg(feature = "serde")] From 5b09e9281660e7e7165b24e763f762d0b74e9eae Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 27 Feb 2022 17:07:11 -0500 Subject: [PATCH 267/275] fix: use better naming --- manta-accounting/src/wallet/signer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 8a7eb2747..75d8f1c30 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -852,9 +852,9 @@ where &mut self, parameters: &Parameters<C>, asset: Asset, - receiver: ReceivingKey<C>, + receiving_key: ReceivingKey<C>, ) -> Receiver<C> { - receiver.into_receiver(parameters, self.rng.gen(), asset) + receiving_key.into_receiver(parameters, self.rng.gen(), asset) } } From 05994e210f493314d6bec1659de97a98f4c2d874 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 27 Feb 2022 23:22:48 -0500 Subject: [PATCH 268/275] fix: move Checkpoint to signer module out of simulation --- .github/workflows/ci.yml | 1 - manta-pay/src/signer/mod.rs | 51 ++++++++++++++++++++++++- manta-pay/src/simulation/ledger/mod.rs | 52 +++----------------------- 3 files changed, 55 insertions(+), 49 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2469a120e..2ac24db6f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,6 @@ jobs: os: - macos-latest - ubuntu-latest - - wasm32-unknown-unknown - windows-latest channel: - stable diff --git a/manta-pay/src/signer/mod.rs b/manta-pay/src/signer/mod.rs index c04a664f3..ae2d59797 100644 --- a/manta-pay/src/signer/mod.rs +++ b/manta-pay/src/signer/mod.rs @@ -16,8 +16,12 @@ //! Manta Pay Signer Tools -use crate::config::Config; -use manta_accounting::wallet::signer; +use crate::config::{Config, MerkleTreeConfiguration}; +use manta_accounting::wallet::{ledger, signer}; +use manta_util::Array; + +#[cfg(feature = "serde")] +use manta_util::serde::{Deserialize, Serialize}; pub mod client; @@ -42,3 +46,46 @@ pub type SignError = signer::SignError<Config>; /// Receiving Key Request pub type ReceivingKeyRequest = signer::ReceivingKeyRequest; + +/// Checkpoint +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Checkpoint { + /// Receiver Index + pub receiver_index: Array<usize, { MerkleTreeConfiguration::FOREST_WIDTH }>, + + /// Sender Index + pub sender_index: usize, +} + +impl Checkpoint { + /// Builds a new [`Checkpoint`] from `receiver_index` and `sender_index`. + #[inline] + pub fn new( + receiver_index: Array<usize, { MerkleTreeConfiguration::FOREST_WIDTH }>, + sender_index: usize, + ) -> Self { + Self { + receiver_index, + sender_index, + } + } +} + +impl Default for Checkpoint { + #[inline] + fn default() -> Self { + Self::new([0; MerkleTreeConfiguration::FOREST_WIDTH].into(), 0) + } +} + +impl ledger::Checkpoint for Checkpoint { + #[inline] + fn receiver_index(&self) -> usize { + self.receiver_index.iter().sum() + } +} diff --git a/manta-pay/src/simulation/ledger/mod.rs b/manta-pay/src/simulation/ledger/mod.rs index 5f4d10a95..0fc5d5d3e 100644 --- a/manta-pay/src/simulation/ledger/mod.rs +++ b/manta-pay/src/simulation/ledger/mod.rs @@ -16,9 +16,12 @@ //! Ledger Simulation -use crate::config::{ - Config, EncryptedNote, MerkleTreeConfiguration, MultiVerifyingContext, ProofSystem, - TransferPost, Utxo, UtxoSetModel, VoidNumber, +use crate::{ + config::{ + Config, EncryptedNote, MerkleTreeConfiguration, MultiVerifyingContext, ProofSystem, + TransferPost, Utxo, UtxoSetModel, VoidNumber, + }, + signer::Checkpoint, }; use alloc::{sync::Arc, vec::Vec}; use core::convert::Infallible; @@ -343,49 +346,6 @@ impl TransferLedger<Config> for Ledger { } } -/// Checkpoint -#[cfg_attr( - feature = "serde", - derive(Deserialize, Serialize), - serde(crate = "manta_util::serde", deny_unknown_fields) -)] -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Checkpoint { - /// Receiver Index - pub receiver_index: Array<usize, { MerkleTreeConfiguration::FOREST_WIDTH }>, - - /// Sender Index - pub sender_index: usize, -} - -impl Checkpoint { - /// Builds a new [`Checkpoint`] from `receiver_index` and `sender_index`. - #[inline] - pub fn new( - receiver_index: Array<usize, { MerkleTreeConfiguration::FOREST_WIDTH }>, - sender_index: usize, - ) -> Self { - Self { - receiver_index, - sender_index, - } - } -} - -impl Default for Checkpoint { - #[inline] - fn default() -> Self { - Self::new([0; MerkleTreeConfiguration::FOREST_WIDTH].into(), 0) - } -} - -impl ledger::Checkpoint for Checkpoint { - #[inline] - fn receiver_index(&self) -> usize { - self.receiver_index.iter().sum() - } -} - /// Shared Ledger pub type SharedLedger = Arc<RwLock<Ledger>>; From 165f741ec0eabcdd362a230bd1a614b97df15d5f Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Sun, 27 Feb 2022 23:59:47 -0500 Subject: [PATCH 269/275] fix: replace reqwest::blocking with futures::executor::block_on --- manta-pay/Cargo.toml | 5 +++-- manta-pay/src/util/http.rs | 30 +++++++++++++++++++++++------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index b22c6933f..27d1dec2a 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -53,7 +53,7 @@ arkworks = [ groth16 = ["ark-groth16", "ark-snark", "arkworks"] # Enable HTTP Signer Client -http = ["reqwest", "serde"] +http = ["futures", "reqwest", "serde"] # TODO: Enable PLONK ZKP System # TODO: plonk = ["zk-garage-plonk"] @@ -101,6 +101,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 } derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } +futures = { version = "0.3.21", optional = true, default-features = false, features = ["executor"] } indexmap = { version = "1.8.0", optional = true, default-features = false } manta-accounting = { path = "../manta-accounting", default-features = false } manta-crypto = { path = "../manta-crypto", default-features = false } @@ -108,7 +109,7 @@ manta-util = { path = "../manta-util", default-features = false } parking_lot = { version = "0.12.0", optional = true, default-features = false } rand_chacha = { version = "0.3.1", default-features = false } rayon = { version = "1.5.1", optional = true, default-features = false } -reqwest = { version = "0.11.9", optional = true, default-features = false, features = ["blocking", "json"] } +reqwest = { version = "0.11.9", optional = true, default-features = false, features = ["json"] } scale-codec = { package = "parity-scale-codec", version = "2.3.1", optional = true, default-features = false, features = ["derive", "max-encoded-len"] } scale-info = { version = "1.0.0", optional = true, default-features = false, features = ["derive"] } serde_json = { version = "1.0.79", optional = true, default-features = false, features = ["alloc"] } diff --git a/manta-pay/src/util/http.rs b/manta-pay/src/util/http.rs index 300607894..49417365a 100644 --- a/manta-pay/src/util/http.rs +++ b/manta-pay/src/util/http.rs @@ -17,9 +17,8 @@ //! HTTP Utilities use manta_util::serde::{de::DeserializeOwned, Serialize}; -use reqwest::blocking; -pub use reqwest::{blocking::Response, Error, IntoUrl, Method, Url}; +pub use reqwest::{Error, IntoUrl, Method, Response, Url}; /// Blocking HTTP Client /// @@ -29,7 +28,7 @@ pub struct Client { pub server_url: Url, /// Base HTTP Client - pub client: blocking::Client, + pub client: reqwest::Client, } impl Client { @@ -40,14 +39,19 @@ impl Client { U: IntoUrl, { Ok(Self { - client: blocking::Client::builder().build()?, + client: reqwest::Client::builder().build()?, server_url: server_url.into_url()?, }) } - /// Sends a new request of type `command` with body `request`. + /// Sends a new request asynchronously of type `command` with body `request`. #[inline] - pub fn request<T, R>(&self, method: Method, command: &str, request: T) -> Result<R, Error> + async fn send_request<T, R>( + &self, + method: Method, + command: &str, + request: T, + ) -> Result<R, Error> where T: Serialize, R: DeserializeOwned, @@ -60,8 +64,20 @@ impl Client { .expect("This error branch is not allowed to happen."), ) .json(&request) - .send()? + .send() + .await? .json() + .await + } + + /// Sends a new request of type `command` with body `request`. + #[inline] + pub fn request<T, R>(&self, method: Method, command: &str, request: T) -> Result<R, Error> + where + T: Serialize, + R: DeserializeOwned, + { + futures::executor::block_on(async { self.send_request(method, command, request).await }) } /// Sends a GET request of type `command` with body `request`. From 7db8b7842bc082ea758f0df9057b0c14a27032b1 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 28 Feb 2022 00:33:38 -0500 Subject: [PATCH 270/275] fix: update documentation --- manta-pay/src/util/http.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/manta-pay/src/util/http.rs b/manta-pay/src/util/http.rs index 49417365a..5a3ce30a4 100644 --- a/manta-pay/src/util/http.rs +++ b/manta-pay/src/util/http.rs @@ -22,7 +22,9 @@ pub use reqwest::{Error, IntoUrl, Method, Response, Url}; /// Blocking HTTP Client /// -/// This client is a wrapper around [`reqwest::blocking::Client`] which has a known server URL. +/// This client is a wrapper around [`reqwest::Client`] with a known server URL. The +/// [`request`](Self::request) method on this `struct` uses [`futures::executor::block_on`] to +/// convert the asynchronous client into a blocking on. pub struct Client { /// Server URL pub server_url: Url, From d88c099d97f389b6c46b3ad46e10a2060819b508 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 28 Feb 2022 15:24:45 -0500 Subject: [PATCH 271/275] feat: add asset metadata for transaction summaries --- manta-accounting/src/asset.rs | 73 ++++++++++++++++++++++ manta-accounting/src/transfer/canonical.rs | 21 ++++++- manta-accounting/src/wallet/signer.rs | 45 +++++++++++-- manta-accounting/src/wallet/state.rs | 17 +++-- manta-accounting/src/wallet/test/mod.rs | 2 +- manta-pay/Cargo.toml | 5 +- manta-pay/src/config.rs | 27 ++++++++ manta-pay/src/signer/client/http.rs | 11 ++-- manta-pay/src/signer/client/websocket.rs | 27 ++++---- manta-pay/src/signer/mod.rs | 3 + 10 files changed, 196 insertions(+), 35 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index fa93e5dc1..680206a79 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -18,6 +18,8 @@ use alloc::{ collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}, + format, + string::String, vec, vec::Vec, }; @@ -1050,6 +1052,77 @@ where impl<'s, M> FusedIterator for SelectionKeys<'s, M> where M: AssetMap + ?Sized {} +/// Asset Metadata +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde") +)] +#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] +pub struct AssetMetadata { + /// Number of Decimals + pub decimals: u32, + + /// Asset Ticker + pub ticker: String, +} + +impl AssetMetadata { + /// Returns a string formatting of only the `value` interpreted using `self` as the metadata. + #[inline] + pub fn display_value(&self, value: AssetValue) -> String { + const FRACTIONAL_DIGITS: u32 = 3; + let value_base_units = value.0 / (10u128.pow(self.decimals)); + let fractional_digits = value.0 / (10u128.pow(self.decimals - FRACTIONAL_DIGITS)) + % (10u128.pow(FRACTIONAL_DIGITS)); + format!("{}.{:0>3}", value_base_units, fractional_digits) + } + + /// Returns a string formatting of `value` interpreted using `self` as the metadata including + /// the ticker. + #[inline] + pub fn display(&self, value: AssetValue) -> String { + format!("{} {}", self.display_value(value), self.ticker) + } +} + +/// Asset Manager +pub trait AssetManager { + /// Returns the metadata associated to `id`. + fn metadata(&self, id: AssetId) -> Option<&AssetMetadata>; +} + +/// Implements [`AssetManager`] for map types. +macro_rules! impl_asset_manager_for_maps_body { + () => { + #[inline] + fn metadata(&self, id: AssetId) -> Option<&AssetMetadata> { + self.get(&id) + } + }; +} + +/// B-Tree Map [`AssetManager`] Implementation +pub type BTreeAssetManager = BTreeMap<AssetId, AssetMetadata>; + +impl AssetManager for BTreeAssetManager { + impl_asset_manager_for_maps_body! {} +} + +/// Hash Map [`AssetManager`] Implementation +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +pub type HashAssetManager<S = RandomState> = HashMap<AssetId, AssetMetadata, S>; + +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +impl<S> AssetManager for HashAssetManager<S> +where + S: BuildHasher + Default, +{ + impl_asset_manager_for_maps_body! {} +} + /// Testing Suite #[cfg(test)] mod test { diff --git a/manta-accounting/src/transfer/canonical.rs b/manta-accounting/src/transfer/canonical.rs index c6b743f4c..5ecf3b11f 100644 --- a/manta-accounting/src/transfer/canonical.rs +++ b/manta-accounting/src/transfer/canonical.rs @@ -19,14 +19,14 @@ // TODO: Add typing for `ProvingContext` and `VerifyingContext` against the canonical shapes. use crate::{ - asset::{self, Asset, AssetValue}, + asset::{self, Asset, AssetMap, AssetMetadata, AssetValue}, transfer::{ has_public_participants, Configuration, FullParameters, Parameters, PreSender, ProofSystemError, ProofSystemPublicParameters, ProvingContext, Receiver, ReceivingKey, Sender, SpendingKey, Transfer, TransferPost, VerifyingContext, }, }; -use alloc::vec::Vec; +use alloc::{format, string::String, vec::Vec}; use core::{fmt::Debug, hash::Hash}; use manta_crypto::rand::{CryptoRng, Rand, RngCore}; use manta_util::{create_seal, seal}; @@ -345,6 +345,21 @@ where Self::Reclaim(_) => TransferShape::Reclaim, } } + + /// Returns a transaction summary given the asset `metadata`. + #[inline] + pub fn display<F>(&self, metadata: &AssetMetadata, f: F) -> String + where + F: FnOnce(&ReceivingKey<C>) -> String, + { + match self { + Self::Mint(Asset { value, .. }) => format!("Deposit {}", metadata.display(*value)), + Self::PrivateTransfer(Asset { value, .. }, receiving_key) => { + format!("Send {} to {}", metadata.display(*value), f(receiving_key)) + } + Self::Reclaim(Asset { value, .. }) => format!("Withdraw {}", metadata.display(*value)), + } + } } /// Transaction Kind @@ -390,7 +405,7 @@ where #[inline] pub fn new<M, E, F>(selection: asset::Selection<M>, mut builder: F) -> Result<Self, E> where - M: asset::AssetMap, + M: AssetMap, F: FnMut(M::Key, AssetValue) -> Result<PreSender<C>, E>, { Ok(Self::build( diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 75d8f1c30..b071d4d0d 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -25,7 +25,7 @@ // TODO: Compress the `SyncResponse` data before sending (improves privacy and bandwidth). use crate::{ - asset::{Asset, AssetId, AssetMap, AssetValue}, + asset::{Asset, AssetId, AssetMap, AssetMetadata, AssetValue}, key::{self, HierarchicalKeyDerivationScheme, KeyIndex, SecretKeyPair, ViewKeySelection}, transfer::{ self, @@ -75,10 +75,10 @@ where request: SyncRequest<C>, ) -> Result<Result<SyncResponse, SyncError>, Self::Error>; - /// Signs a `transaction` and returns the ledger transfer posts if successful. + /// Signs a transaction and returns the ledger transfer posts if successful. fn sign( &mut self, - transaction: Transaction<C>, + request: SignRequest<C>, ) -> Result<Result<SignResponse<C>, SignError<C>>, Self::Error>; /// Returns public receiving keys according to the `request`. @@ -191,6 +191,41 @@ pub enum SyncError { }, } +/// Signer Signing Request +/// +/// This `struct` is used by the [`sign`](Connection::sign) method on [`Connection`]. +/// See its documentation for more. +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = "Transaction<C>: Deserialize<'de>", + serialize = "Transaction<C>: Serialize" + ), + crate = "manta_util::serde", + deny_unknown_fields + ) +)] +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "Transaction<C>: Clone"), + Debug(bound = "Transaction<C>: Debug"), + Eq(bound = "Transaction<C>: Eq"), + Hash(bound = "Transaction<C>: Hash"), + PartialEq(bound = "Transaction<C>: PartialEq") +)] +pub struct SignRequest<C> +where + C: transfer::Configuration, +{ + /// Transaction Data + pub transaction: Transaction<C>, + + /// Asset Metadata + pub metadata: Option<AssetMetadata>, +} + /// Signer Signing Response /// /// This `struct` is created by the [`sign`](Connection::sign) method on [`Connection`]. @@ -1113,9 +1148,9 @@ where #[inline] fn sign( &mut self, - transaction: Transaction<C>, + request: SignRequest<C>, ) -> Result<Result<SignResponse<C>, SignError<C>>, Self::Error> { - Ok(self.sign(transaction)) + Ok(self.sign(request.transaction)) } #[inline] diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index be8a17f2f..cdfabc20a 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -17,7 +17,7 @@ //! Full Wallet Implementation use crate::{ - asset::{Asset, AssetId, AssetList, AssetValue}, + asset::{Asset, AssetId, AssetList, AssetMetadata, AssetValue}, transfer::{ canonical::{Transaction, TransactionKind}, Configuration, ReceivingKey, @@ -25,8 +25,8 @@ use crate::{ wallet::{ ledger::{self, Checkpoint, PullResponse, PushResponse}, signer::{ - self, ReceivingKeyRequest, SignError, SignResponse, SyncError, SyncRequest, - SyncResponse, + self, ReceivingKeyRequest, SignError, SignRequest, SignResponse, SyncError, + SyncRequest, SyncResponse, }, }, }; @@ -423,13 +423,20 @@ where /// This method returns an error in any other case. The internal state of the wallet is kept /// consistent between calls and recoverable errors are returned for the caller to handle. #[inline] - pub fn post(&mut self, transaction: Transaction<C>) -> Result<bool, Error<C, L, S>> { + pub fn post( + &mut self, + transaction: Transaction<C>, + metadata: Option<AssetMetadata>, + ) -> Result<bool, Error<C, L, S>> { self.sync()?; self.check(&transaction) .map_err(Error::InsufficientBalance)?; let SignResponse { posts } = self .signer - .sign(transaction) + .sign(SignRequest { + transaction, + metadata, + }) .map_err(Error::SignerConnectionError)? .map_err(Error::SignError)?; let PushResponse { success } = self diff --git a/manta-accounting/src/wallet/test/mod.rs b/manta-accounting/src/wallet/test/mod.rs index 9891c2ee9..5795d0cd2 100644 --- a/manta-accounting/src/wallet/test/mod.rs +++ b/manta-accounting/src/wallet/test/mod.rs @@ -387,7 +387,7 @@ where }; let mut retries = 5; // TODO: Make this parameter tunable based on concurrency. loop { - let result = actor.wallet.post(transaction.clone()); + let result = actor.wallet.post(transaction.clone(), None); if let Ok(false) = result { if retries == 0 { break Event { action, result }; diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index 27d1dec2a..0809d6d16 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -62,7 +62,7 @@ http = ["futures", "reqwest", "serde"] scale = ["scale-codec", "scale-info"] # Serde -serde = ["manta-accounting/serde", "manta-crypto/serde"] +serde = ["bs58", "manta-accounting/serde", "manta-crypto/serde", "serde_json"] # Simulation Framework simulation = [ @@ -84,7 +84,7 @@ test = ["manta-accounting/test", "manta-crypto/test"] wallet = ["bip32", "manta-crypto/getrandom", "std"] # Enable WebSocket Signer Client -websocket = ["serde", "serde_json", "std", "tungstenite"] +websocket = ["serde", "std", "tungstenite"] [dependencies] aes-gcm = { version = "0.9.4", default-features = false, features = ["aes", "alloc"] } @@ -100,6 +100,7 @@ ark-snark = { version = "0.3.0", optional = true, default-features = false } 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"] } derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } futures = { version = "0.3.21", optional = true, default-features = false, features = ["executor"] } indexmap = { version = "1.8.0", optional = true, default-features = false } diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index e51cb8f8b..75cecef3e 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -49,6 +49,9 @@ use manta_crypto::{ }; use manta_util::codec::{Decode, DecodeError, Encode, Read, Write}; +#[cfg(feature = "serde")] +use alloc::string::String; + #[cfg(any(feature = "test", test))] use manta_crypto::rand::{CryptoRng, Rand, RngCore, Sample, Standard}; @@ -699,3 +702,27 @@ pub type SpendingKey = transfer::SpendingKey<Config>; /// Receiving Key Type pub type ReceivingKey = transfer::ReceivingKey<Config>; + +/// Converts a [`ReceivingKey`] into a base58-encoded string. +#[cfg(feature = "serde")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] +#[inline] +pub fn receiving_key_to_base58(receiving_key: &ReceivingKey) -> String { + bs58::encode( + serde_json::to_string(receiving_key) + .expect("Can always serialize to JSON.") + .as_bytes(), + ) + .into_string() +} + +/// Converts a base58-encoded string into a [`ReceivingKey`]. +#[cfg(feature = "serde")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] +#[inline] +pub fn receiving_key_from_base58(string: &str) -> Option<ReceivingKey> { + serde_json::from_str( + core::str::from_utf8(&bs58::decode(string.as_bytes()).into_vec().ok()?).ok()?, + ) + .ok() +} diff --git a/manta-pay/src/signer/client/http.rs b/manta-pay/src/signer/client/http.rs index 5c2a1463e..3b012edb6 100644 --- a/manta-pay/src/signer/client/http.rs +++ b/manta-pay/src/signer/client/http.rs @@ -17,8 +17,11 @@ //! Signer HTTP Client Implementation use crate::{ - config::{Config, ReceivingKey, Transaction}, - signer::{ReceivingKeyRequest, SignError, SignResponse, SyncError, SyncRequest, SyncResponse}, + config::{Config, ReceivingKey}, + signer::{ + ReceivingKeyRequest, SignError, SignRequest, SignResponse, SyncError, SyncRequest, + SyncResponse, + }, util::http::{self, Error, IntoUrl}, }; use alloc::vec::Vec; @@ -57,11 +60,11 @@ impl signer::Connection<Config> for Client { #[inline] fn sign( &mut self, - transaction: Transaction, + request: SignRequest, ) -> Result<Result<SignResponse, SignError>, Self::Error> { // NOTE: The signing command does not modify the signer so it must be a GET command to match // the HTTP semantics. - self.0.get("sign", transaction) + self.0.get("sign", request) } #[inline] diff --git a/manta-pay/src/signer/client/websocket.rs b/manta-pay/src/signer/client/websocket.rs index 29dbcb236..f1a8982eb 100644 --- a/manta-pay/src/signer/client/websocket.rs +++ b/manta-pay/src/signer/client/websocket.rs @@ -16,18 +16,15 @@ //! Signer WebSocket Client Implementation -use crate::config::Config; -use alloc::vec::Vec; -use manta_accounting::{ - transfer::{canonical::Transaction, ReceivingKey}, - wallet::{ - self, - signer::{ - self, ReceivingKeyRequest, SignError, SignResponse, SyncError, SyncRequest, - SyncResponse, - }, +use crate::{ + config::{Config, ReceivingKey}, + signer::{ + ReceivingKeyRequest, SignError, SignRequest, SignResponse, SyncError, SyncRequest, + SyncResponse, }, }; +use alloc::vec::Vec; +use manta_accounting::wallet::{self, signer}; use manta_util::{ from_variant_impl, serde::{de::DeserializeOwned, Deserialize, Serialize}, @@ -112,7 +109,7 @@ impl signer::Connection<Config> for Client { #[inline] fn sync( &mut self, - request: SyncRequest<Config>, + request: SyncRequest, ) -> Result<Result<SyncResponse, SyncError>, Self::Error> { self.send("sync", request) } @@ -120,16 +117,16 @@ impl signer::Connection<Config> for Client { #[inline] fn sign( &mut self, - transaction: Transaction<Config>, - ) -> Result<Result<SignResponse<Config>, SignError<Config>>, Self::Error> { - self.send("sign", transaction) + request: SignRequest, + ) -> Result<Result<SignResponse, SignError>, Self::Error> { + self.send("sign", request) } #[inline] fn receiving_keys( &mut self, request: ReceivingKeyRequest, - ) -> Result<Vec<ReceivingKey<Config>>, Self::Error> { + ) -> Result<Vec<ReceivingKey>, Self::Error> { self.send("receivingKeys", request) } } diff --git a/manta-pay/src/signer/mod.rs b/manta-pay/src/signer/mod.rs index ae2d59797..df58e9876 100644 --- a/manta-pay/src/signer/mod.rs +++ b/manta-pay/src/signer/mod.rs @@ -38,6 +38,9 @@ pub type SyncResponse = signer::SyncResponse; /// Synchronization Error pub type SyncError = signer::SyncError; +/// Sign Request +pub type SignRequest = signer::SignRequest<Config>; + /// Sign Response pub type SignResponse = signer::SignResponse<Config>; From 257e0d7ce7d1d9a13e3371abb6c5dfbe6106b927 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 28 Feb 2022 16:08:17 -0500 Subject: [PATCH 272/275] feat: add more derive implementations --- manta-accounting/src/wallet/state.rs | 31 ++++++++++++++++++++++++++-- manta-pay/src/signer/client/http.rs | 5 ++++- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs index cdfabc20a..08914d421 100644 --- a/manta-accounting/src/wallet/state.rs +++ b/manta-accounting/src/wallet/state.rs @@ -34,7 +34,7 @@ use alloc::{ collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}, vec::Vec, }; -use core::{fmt::Debug, marker::PhantomData}; +use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use manta_util::ops::ControlFlow; #[cfg(feature = "serde")] @@ -504,8 +504,35 @@ pub enum InconsistencyError { /// /// This `enum` is the error state for [`Wallet`] methods. See [`sync`](Wallet::sync) and /// [`post`](Wallet::post) for more. +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = r" + SignError<C>: Deserialize<'de>, + L::Error: Deserialize<'de>, + S::Error: Deserialize<'de> + ", + serialize = r" + SignError<C>: Serialize, + L::Error: Serialize, + S::Error: Serialize + " + ), + crate = "manta_util::serde", + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] -#[derivative(Debug(bound = "SignError<C>: Debug, S::Error: Debug, L::Error: Debug"))] +#[derivative( + Clone(bound = "SignError<C>: Clone, L::Error: Clone, S::Error: Clone"), + Copy(bound = "SignError<C>: Copy, L::Error: Copy, S::Error: Copy"), + Debug(bound = "SignError<C>: Debug, L::Error: Debug, S::Error: Debug"), + Eq(bound = "SignError<C>: Eq, L::Error: Eq, S::Error: Eq"), + Hash(bound = "SignError<C>: Hash, L::Error: Hash, S::Error: Hash"), + PartialEq(bound = "SignError<C>: PartialEq, L::Error: PartialEq, S::Error: PartialEq") +)] pub enum Error<C, L, S> where C: Configuration, diff --git a/manta-pay/src/signer/client/http.rs b/manta-pay/src/signer/client/http.rs index 3b012edb6..db39f012c 100644 --- a/manta-pay/src/signer/client/http.rs +++ b/manta-pay/src/signer/client/http.rs @@ -22,11 +22,14 @@ use crate::{ ReceivingKeyRequest, SignError, SignRequest, SignResponse, SyncError, SyncRequest, SyncResponse, }, - util::http::{self, Error, IntoUrl}, + util::http::{self, IntoUrl}, }; use alloc::vec::Vec; use manta_accounting::wallet::{self, signer}; +#[doc(inline)] +pub use http::Error; + /// Wallet Associated to [`Client`] pub type Wallet<L> = wallet::Wallet<Config, L, Client>; From 31881fed956981941e56bb1f507e54fa3dc79b84 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 28 Feb 2022 16:43:44 -0500 Subject: [PATCH 273/275] feat: add more serde implementations --- manta-accounting/src/transfer/canonical.rs | 5 +++++ manta-util/src/ops.rs | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/manta-accounting/src/transfer/canonical.rs b/manta-accounting/src/transfer/canonical.rs index 5ecf3b11f..5f66a5b29 100644 --- a/manta-accounting/src/transfer/canonical.rs +++ b/manta-accounting/src/transfer/canonical.rs @@ -363,6 +363,11 @@ where } /// Transaction Kind +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum TransactionKind { /// Deposit Transaction diff --git a/manta-util/src/ops.rs b/manta-util/src/ops.rs index 4efad41ce..791d6535d 100644 --- a/manta-util/src/ops.rs +++ b/manta-util/src/ops.rs @@ -18,10 +18,18 @@ use core::ops; +#[cfg(feature = "serde")] +use crate::serde::{Deserialize, Serialize}; + /// Used to tell an operation whether it should exit early or go on as usual. /// /// This is an alternative definition and mostly drop-in replacement for [`core::ops::ControlFlow`] /// but may diverge from the standard library interface over time. +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "crate::serde", deny_unknown_fields) +)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[must_use] pub enum ControlFlow<B = (), C = ()> { From 0668ba8e1ee7c943d52b4dcb98b0d96cf6fcf37a Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Mon, 28 Feb 2022 23:24:21 -0500 Subject: [PATCH 274/275] fix: address documentation and naming comments --- manta-accounting/src/asset.rs | 7 + manta-accounting/src/fs/mod.rs | 13 + manta-accounting/src/fs/serde.rs | 7 + manta-accounting/src/key.rs | 9 + manta-accounting/src/lib.rs | 8 + manta-accounting/src/transfer/batch.rs | 10 +- manta-accounting/src/transfer/mod.rs | 249 ++++++---- manta-accounting/src/transfer/test.rs | 52 +-- manta-accounting/src/wallet/mod.rs | 553 +++++++++++++++++++++- manta-accounting/src/wallet/signer.rs | 101 ++-- manta-accounting/src/wallet/state.rs | 558 ----------------------- manta-pay/src/bin/generate_parameters.rs | 11 +- manta-pay/src/config.rs | 18 +- manta-pay/src/signer/base.rs | 6 +- manta-pay/src/simulation/ledger/mod.rs | 20 +- manta-pay/src/simulation/mod.rs | 16 +- manta-pay/src/test/transfer.rs | 22 +- 17 files changed, 889 insertions(+), 771 deletions(-) delete mode 100644 manta-accounting/src/wallet/state.rs diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 680206a79..3351cd544 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -15,6 +15,11 @@ // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. //! Assets +//! +//! This module defines the data structures and canonical encodings of the notion of an "asset". +//! Assets are defined by an [`AssetId`] field and an [`AssetValue`] field. For describing an +//! [`Asset`] with a particular [`AssetId`] we use [`AssetMetadata`] to assign a ticker and decimal +//! for human-readable display purposes. use alloc::{ collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}, @@ -1071,6 +1076,8 @@ impl AssetMetadata { /// Returns a string formatting of only the `value` interpreted using `self` as the metadata. #[inline] pub fn display_value(&self, value: AssetValue) -> String { + // TODO: What if we want more than three `FRACTIONAL_DIGITS`? How do we make this method + // more general? const FRACTIONAL_DIGITS: u32 = 3; let value_base_units = value.0 / (10u128.pow(self.decimals)); let fractional_digits = value.0 / (10u128.pow(self.decimals - FRACTIONAL_DIGITS)) diff --git a/manta-accounting/src/fs/mod.rs b/manta-accounting/src/fs/mod.rs index ff6a934a8..b0f22cfe6 100644 --- a/manta-accounting/src/fs/mod.rs +++ b/manta-accounting/src/fs/mod.rs @@ -15,6 +15,19 @@ // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. //! Encrypted Filesystem Primitives +//! +//! This module defines an abstraction over the standard file system which enforces that data is +//! encrypted before being written and decrypted after being read. See [`File`] for the main +//! abstraction which allows for encrypted file systems and [`cocoon`] for a concrete implementation +//! of this file system. +//! +//! # Serialization +//! +//! For these file system abstractions we use the [`Block`] type for buffering raw binary data into +//! an encryption scheme, so to facilitate the encryption of structured data, we have the [`serde`] +//! module which defines serializers and deserializers which encrypt and decrypt data on the fly +//! using the [`Block`] as the underlying serialization and deserialization target. See the +//! [`serde`] module for more. use alloc::{boxed::Box, vec::Vec}; use core::{cmp, hash::Hash, marker::PhantomData}; diff --git a/manta-accounting/src/fs/serde.rs b/manta-accounting/src/fs/serde.rs index 23768019c..79fd9f435 100644 --- a/manta-accounting/src/fs/serde.rs +++ b/manta-accounting/src/fs/serde.rs @@ -15,6 +15,13 @@ // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. //! Serde-Compatible Encrypted Filesystem +//! +//! To facilitate the encryption and file I/O of structured data, we define the [`Serializer`] and +//! [`Deserializer`] which use a [`File`] to encrypt and decrypt [`Block`] data during reading and +//! writing from the file system. The encoding scheme is a binary-only concatenative format which +//! stores no type or name metadata for the smallest data sizes: it is not a self-describing nor +//! human-readable format. See the [`test`](mod@test) module for testing of encrypting serialization +//! and decrypting deserialization. use crate::fs::{Block, File}; use alloc::{format, string::String, vec::Vec}; diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index 1a463402f..92042742d 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -15,6 +15,11 @@ // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. //! Hierarchical Key Derivation Schemes +//! +//! This module defines a Hierarchical Key Derivation Scheme similar to the one defined in the +//! [`BIP-0044`] specification. +//! +//! [`BIP-0044`]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki // TODO: Build custom iterator types for [`keypairs`] and [`generate_keys`]. @@ -675,6 +680,10 @@ impl AccountMap for VecAccountMap { } /// Account Table +/// +/// The account table stores an underlying [`HierarchicalKeyDerivationScheme`] for key generation +/// and a set of accounts defined by an [`AccountMap`] which can be queried to get the set of +/// existing keys and for generating new keys. #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), diff --git a/manta-accounting/src/lib.rs b/manta-accounting/src/lib.rs index a034b03f7..9a8fe02ee 100644 --- a/manta-accounting/src/lib.rs +++ b/manta-accounting/src/lib.rs @@ -15,6 +15,14 @@ // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. //! Accounting Primitives +//! +//! This crate defines the abstractions required for the private transfer of assets and the keeping +//! of accounts related to private assets, including the definitions of an [asset], a [private +//! transfer protocol], and a [wallet protocol]. +//! +//! [asset]: asset +//! [private transfer protocol]: transfer +//! [wallet protocol]: wallet #![cfg_attr(not(any(feature = "std", test)), no_std)] #![cfg_attr(doc_cfg, feature(doc_cfg))] diff --git a/manta-accounting/src/transfer/batch.rs b/manta-accounting/src/transfer/batch.rs index d1fe77723..fdaed77e8 100644 --- a/manta-accounting/src/transfer/batch.rs +++ b/manta-accounting/src/transfer/batch.rs @@ -69,15 +69,15 @@ where (into_array_unchecked(receivers), Self { zeroes, pre_sender }) } - /// Inserts UTXOs for each sender in `self` into the `utxo_set` for future proof selection. + /// Inserts UTXOs for each sender in `self` into the `utxo_accumulator` for future proof selection. #[inline] - pub fn insert_utxos<A>(&self, utxo_set: &mut A) + pub fn insert_utxos<A>(&self, utxo_accumulator: &mut A) where - A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + A: Accumulator<Item = Utxo<C>, Model = C::UtxoAccumulatorModel>, { - self.pre_sender.insert_utxo(utxo_set); + self.pre_sender.insert_utxo(utxo_accumulator); for zero in &self.zeroes { - zero.insert_utxo(utxo_set); + zero.insert_utxo(utxo_accumulator); } } } diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index be9c0cce7..a4e45b0ba 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -15,6 +15,19 @@ // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. //! Transfer Protocol +//! +//! This module defines a protocol for the zero-knowledge transfer of private assets. We define the +//! following structures: +//! +//! - Global Configuration: [`Configuration`] +//! - Sender Abstraction: [`Sender`], [`SenderVar`], [`SenderPost`], [`SenderLedger`] +//! - Receiver Abstraction: [`Receiver`], [`ReceiverVar`], [`ReceiverPost`], [`ReceiverLedger`] +//! - Transfer Abstraction: [`Transfer`], [`TransferPost`], [`TransferLedger`] +//! - Canonical Transactions: [`canonical`] +//! - Batched Transactions: [`batch`] +//! +//! See the [`crate::wallet`] module for more on how this transfer protocol is used in a wallet +//! protocol for the keeping of accounts for private assets. use crate::asset::{Asset, AssetId, AssetValue}; use alloc::vec::Vec; @@ -125,23 +138,31 @@ pub trait Configuration { Output = Self::VoidNumberVar, > + Constant<Self::Compiler, Type = Self::VoidNumberHashFunction>; - /// UTXO Set Model Type - type UtxoSetModel: Model<Item = Self::Utxo, Verification = bool>; + /// UTXO Accumulator Model Type + type UtxoAccumulatorModel: Model<Item = Self::Utxo, Verification = bool>; - /// UTXO Set Witness Variable Type - type UtxoSetWitnessVar: Variable<Secret, Self::Compiler, Type = UtxoSetWitness<Self>>; + /// UTXO Accumulator Witness Variable Type + type UtxoAccumulatorWitnessVar: Variable< + Secret, + Self::Compiler, + Type = UtxoAccumulatorWitness<Self>, + >; - /// UTXO Set Output Variable Type - type UtxoSetOutputVar: Variable<Public, Self::Compiler, Type = UtxoSetOutput<Self>>; + /// UTXO Accumulator Output Variable Type + type UtxoAccumulatorOutputVar: Variable< + Public, + Self::Compiler, + Type = UtxoAccumulatorOutput<Self>, + >; - /// UTXO Set Model Variable Type - type UtxoSetModelVar: Model< + /// UTXO Accumulator Model Variable Type + type UtxoAccumulatorModelVar: Model< Self::Compiler, Item = Self::UtxoVar, - Witness = Self::UtxoSetWitnessVar, - Output = Self::UtxoSetOutputVar, + Witness = Self::UtxoAccumulatorWitnessVar, + Output = Self::UtxoAccumulatorOutputVar, Verification = <Self::Compiler as ConstraintSystem>::Bool, - > + Constant<Self::Compiler, Type = Self::UtxoSetModel>; + > + Constant<Self::Compiler, Type = Self::UtxoAccumulatorModel>; /// Asset Id Variable Type type AssetIdVar: Variable<Public, Self::Compiler, Type = AssetId> @@ -161,7 +182,7 @@ pub trait Configuration { type ProofSystem: ProofSystem<ConstraintSystem = Self::Compiler> + ProofSystemInput<AssetId> + ProofSystemInput<AssetValue> - + ProofSystemInput<UtxoSetOutput<Self>> + + ProofSystemInput<UtxoAccumulatorOutput<Self>> + ProofSystemInput<Utxo<Self>> + ProofSystemInput<VoidNumber<Self>> + ProofSystemInput<PublicKey<Self>>; @@ -314,18 +335,18 @@ pub type VoidNumber<C> = pub type VoidNumberVar<C> = <<C as Configuration>::VoidNumberHashFunctionVar as BinaryHashFunction<Compiler<C>>>::Output; -/// UTXO Set Witness Type -pub type UtxoSetWitness<C> = <<C as Configuration>::UtxoSetModel as Model>::Witness; +/// UTXO Accumulator Witness Type +pub type UtxoAccumulatorWitness<C> = <<C as Configuration>::UtxoAccumulatorModel as Model>::Witness; -/// UTXO Set Output Type -pub type UtxoSetOutput<C> = <<C as Configuration>::UtxoSetModel as Model>::Output; +/// UTXO Accumulator Output Type +pub type UtxoAccumulatorOutput<C> = <<C as Configuration>::UtxoAccumulatorModel as Model>::Output; /// UTXO Membership Proof Type -pub type UtxoMembershipProof<C> = MembershipProof<<C as Configuration>::UtxoSetModel>; +pub type UtxoMembershipProof<C> = MembershipProof<<C as Configuration>::UtxoAccumulatorModel>; /// UTXO Membership Proof Variable Type pub type UtxoMembershipProofVar<C> = - MembershipProof<<C as Configuration>::UtxoSetModelVar, Compiler<C>>; + MembershipProof<<C as Configuration>::UtxoAccumulatorModelVar, Compiler<C>>; /// Encrypted Note Type pub type EncryptedNote<C> = EncryptedMessage<<C as Configuration>::NoteEncryptionScheme>; @@ -439,20 +460,23 @@ where /// Base Parameters pub base: &'p Parameters<C>, - /// UTXO Set Model - pub utxo_set_model: &'p C::UtxoSetModel, + /// UTXO Accumulator Model + pub utxo_accumulator_model: &'p C::UtxoAccumulatorModel, } impl<'p, C> FullParameters<'p, C> where C: Configuration, { - /// Builds a new [`FullParameters`] from `base` and `utxo_set_model`. + /// Builds a new [`FullParameters`] from `base` and `utxo_accumulator_model`. #[inline] - pub fn new(base: &'p Parameters<C>, utxo_set_model: &'p C::UtxoSetModel) -> Self { + pub fn new( + base: &'p Parameters<C>, + utxo_accumulator_model: &'p C::UtxoAccumulatorModel, + ) -> Self { Self { base, - utxo_set_model, + utxo_accumulator_model, } } } @@ -471,8 +495,8 @@ where /// Void Number Hash Function void_number_hash: C::VoidNumberHashFunctionVar, - /// UTXO Set Model - utxo_set_model: C::UtxoSetModelVar, + /// UTXO Accumulator Model + utxo_accumulator_model: C::UtxoAccumulatorModelVar, /// Type Parameter Marker __: PhantomData<&'p ()>, @@ -491,7 +515,7 @@ where key_agreement: this.base.key_agreement.as_constant(compiler), utxo_commitment: this.base.utxo_commitment.as_constant(compiler), void_number_hash: this.base.void_number_hash.as_constant(compiler), - utxo_set_model: this.utxo_set_model.as_constant(compiler), + utxo_accumulator_model: this.utxo_accumulator_model.as_constant(compiler), __: PhantomData, } } @@ -707,25 +731,25 @@ where } } - /// Inserts the [`Utxo`] corresponding to `self` into the `utxo_set` with the intention of - /// returning a proof later by a call to [`get_proof`](Self::get_proof). + /// Inserts the [`Utxo`] corresponding to `self` into the `utxo_accumulator` with the intention + /// of returning a proof later by a call to [`get_proof`](Self::get_proof). #[inline] - pub fn insert_utxo<S>(&self, utxo_set: &mut S) -> bool + pub fn insert_utxo<A>(&self, utxo_accumulator: &mut A) -> bool where - S: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + A: Accumulator<Item = Utxo<C>, Model = C::UtxoAccumulatorModel>, { - utxo_set.insert(&self.utxo) + utxo_accumulator.insert(&self.utxo) } - /// Requests the membership proof of the [`Utxo`] corresponding to `self` from `utxo_set` to - /// prepare the conversion from `self` into a [`Sender`]. + /// Requests the membership proof of the [`Utxo`] corresponding to `self` from + /// `utxo_accumulator` to prepare the conversion from `self` into a [`Sender`]. #[inline] - pub fn get_proof<S>(&self, utxo_set: &S) -> Option<SenderProof<C>> + pub fn get_proof<A>(&self, utxo_accumulator: &A) -> Option<SenderProof<C>> where - S: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + A: Accumulator<Item = Utxo<C>, Model = C::UtxoAccumulatorModel>, { Some(SenderProof { - utxo_membership_proof: utxo_set.prove(&self.utxo)?, + utxo_membership_proof: utxo_accumulator.prove(&self.utxo)?, }) } @@ -742,37 +766,37 @@ where } } - /// Tries to convert `self` into a [`Sender`] by getting a proof from `utxo_set`. + /// Tries to convert `self` into a [`Sender`] by getting a proof from `utxo_accumulator`. #[inline] - pub fn try_upgrade<S>(self, utxo_set: &S) -> Option<Sender<C>> + pub fn try_upgrade<A>(self, utxo_accumulator: &A) -> Option<Sender<C>> where - S: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + A: Accumulator<Item = Utxo<C>, Model = C::UtxoAccumulatorModel>, { - Some(self.get_proof(utxo_set)?.upgrade(self)) + Some(self.get_proof(utxo_accumulator)?.upgrade(self)) } - /// Inserts the [`Utxo`] corresponding to `self` into the `utxo_set` and upgrades to a full - /// [`Sender`] if the insertion succeeded. + /// Inserts the [`Utxo`] corresponding to `self` into the `utxo_accumulator` and upgrades to a + /// full [`Sender`] if the insertion succeeded. #[inline] - pub fn insert_and_upgrade<S>(self, utxo_set: &mut S) -> Option<Sender<C>> + pub fn insert_and_upgrade<A>(self, utxo_accumulator: &mut A) -> Option<Sender<C>> where - S: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + A: Accumulator<Item = Utxo<C>, Model = C::UtxoAccumulatorModel>, { - if self.insert_utxo(utxo_set) { - self.try_upgrade(utxo_set) + if self.insert_utxo(utxo_accumulator) { + self.try_upgrade(utxo_accumulator) } else { None } } /// Returns `true` whenever `self.utxo` and `rhs.utxo` can be inserted in any order into the - /// `utxo_set`. + /// `utxo_accumulator`. #[inline] - pub fn is_independent_from<A>(&self, rhs: &Self, utxo_set: &A) -> bool + pub fn is_independent_from<A>(&self, rhs: &Self, utxo_accumulator: &A) -> bool where - A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + A: Accumulator<Item = Utxo<C>, Model = C::UtxoAccumulatorModel>, { - utxo_set.are_independent(&self.utxo, &rhs.utxo) + utxo_accumulator.are_independent(&self.utxo, &rhs.utxo) } } @@ -834,13 +858,13 @@ where } /// Returns `true` whenever `self.utxo` and `rhs.utxo` can be inserted in any order into the - /// `utxo_set`. + /// `utxo_accumulator`. #[inline] - pub fn is_independent_from<A>(&self, rhs: &Self, utxo_set: &A) -> bool + pub fn is_independent_from<A>(&self, rhs: &Self, utxo_accumulator: &A) -> bool where - A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + A: Accumulator<Item = Utxo<C>, Model = C::UtxoAccumulatorModel>, { - utxo_set.are_independent(&self.utxo, &rhs.utxo) + utxo_accumulator.are_independent(&self.utxo, &rhs.utxo) } /// Reverts `self` back into a [`PreSender`]. @@ -862,7 +886,7 @@ where #[inline] pub fn into_post(self) -> SenderPost<C> { SenderPost { - utxo_set_output: self.utxo_membership_proof.into_output(), + utxo_accumulator_output: self.utxo_membership_proof.into_output(), void_number: self.void_number, } } @@ -909,9 +933,11 @@ where &self.asset, compiler, ); - let is_valid_proof = - self.utxo_membership_proof - .verify_in(&parameters.utxo_set_model, &utxo, compiler); + let is_valid_proof = self.utxo_membership_proof.verify_in( + &parameters.utxo_accumulator_model, + &utxo, + compiler, + ); compiler.assert(is_valid_proof); let void_number = C::void_number_var(&parameters.void_number_hash, &utxo, &self.spend, compiler); @@ -950,6 +976,10 @@ where } /// Sender Ledger +/// +/// This is the validation trait for ensuring that a particular instance of [`Sender`] is valid +/// according to the ledger state. These methods are the minimum required for a ledger which accepts +/// the [`Sender`] abstraction. pub trait SenderLedger<C> where C: Configuration, @@ -961,20 +991,20 @@ where /// This type must be some wrapper around [`VoidNumber`] which can only be constructed by this /// implementation of [`SenderLedger`]. This is to prevent that [`spend`](Self::spend) is /// called before [`is_unspent`](Self::is_unspent) and - /// [`has_matching_utxo_set_output`](Self::has_matching_utxo_set_output). + /// [`has_matching_utxo_accumulator_output`](Self::has_matching_utxo_accumulator_output). type ValidVoidNumber: AsRef<VoidNumber<C>>; - /// Valid UTXO Set Output Posting Key + /// Valid UTXO Accumulator Output Posting Key /// /// # Safety /// /// This type must be some wrapper around [`S::Output`] which can only be constructed by this /// implementation of [`SenderLedger`]. This is to prevent that [`spend`](Self::spend) is /// called before [`is_unspent`](Self::is_unspent) and - /// [`has_matching_utxo_set_output`](Self::has_matching_utxo_set_output). + /// [`has_matching_utxo_accumulator_output`](Self::has_matching_utxo_accumulator_output). /// /// [`S::Output`]: Model::Output - type ValidUtxoSetOutput: AsRef<UtxoSetOutput<C>>; + type ValidUtxoAccumulatorOutput: AsRef<UtxoAccumulatorOutput<C>>; /// Super Posting Key /// @@ -986,15 +1016,15 @@ where /// Existence of such a void number could indicate a possible double-spend. fn is_unspent(&self, void_number: VoidNumber<C>) -> Option<Self::ValidVoidNumber>; - /// Checks if `output` matches the current accumulated value of the UTXO set that is stored on - /// the ledger. + /// Checks if `output` matches the current accumulated value of the UTXO accumulator that is + /// stored on the ledger. /// /// Failure to match the ledger state means that the sender was constructed under an invalid or /// older state of the ledger. - fn has_matching_utxo_set_output( + fn has_matching_utxo_accumulator_output( &self, - output: UtxoSetOutput<C>, - ) -> Option<Self::ValidUtxoSetOutput>; + output: UtxoAccumulatorOutput<C>, + ) -> Option<Self::ValidUtxoAccumulatorOutput>; /// Posts the `void_number` to the ledger, spending the asset. /// @@ -1014,11 +1044,14 @@ where #[inline] fn spend( &mut self, - utxo_set_output: Self::ValidUtxoSetOutput, + utxo_accumulator_output: Self::ValidUtxoAccumulatorOutput, void_number: Self::ValidVoidNumber, super_key: &Self::SuperPostingKey, ) { - self.spend_all(iter::once((utxo_set_output, void_number)), super_key) + self.spend_all( + iter::once((utxo_accumulator_output, void_number)), + super_key, + ) } /// Posts all of the [`VoidNumber`] to the ledger, spending the assets. @@ -1039,10 +1072,10 @@ where #[inline] fn spend_all<I>(&mut self, iter: I, super_key: &Self::SuperPostingKey) where - I: IntoIterator<Item = (Self::ValidUtxoSetOutput, Self::ValidVoidNumber)>, + I: IntoIterator<Item = (Self::ValidUtxoAccumulatorOutput, Self::ValidVoidNumber)>, { - for (utxo_set_output, void_number) in iter { - self.spend(utxo_set_output, void_number, super_key) + for (utxo_accumulator_output, void_number) in iter { + self.spend(utxo_accumulator_output, void_number, super_key) } } } @@ -1060,20 +1093,25 @@ pub enum SenderPostError { /// The asset has already been spent. AssetSpent, - /// Invalid UTXO Set Output Error + /// Invalid UTXO Accumulator Output Error /// - /// The sender was not constructed under the current state of the UTXO set. - InvalidUtxoSetOutput, + /// The sender was not constructed under the current state of the UTXO accumulator. + InvalidUtxoAccumulatorOutput, } /// Sender Post +/// +/// This `struct` represents the public data required to verify that a particular instance of a +/// [`Sender`] should be valid according to the [`SenderLedger`]. The rest of the information +/// required to verify a [`Transfer`] is stored in the [`TransferPost`] which includes the [`Proof`] +/// of validity. #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), serde( bound( - deserialize = "UtxoSetOutput<C>: Deserialize<'de>, VoidNumber<C>: Deserialize<'de>", - serialize = "UtxoSetOutput<C>: Serialize, VoidNumber<C>: Serialize", + deserialize = "UtxoAccumulatorOutput<C>: Deserialize<'de>, VoidNumber<C>: Deserialize<'de>", + serialize = "UtxoAccumulatorOutput<C>: Serialize, VoidNumber<C>: Serialize", ), crate = "manta_util::serde", deny_unknown_fields @@ -1081,19 +1119,19 @@ pub enum SenderPostError { )] #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "UtxoSetOutput<C>: Clone, VoidNumber<C>: Clone"), - Copy(bound = "UtxoSetOutput<C>: Copy, VoidNumber<C>: Copy"), - Debug(bound = "UtxoSetOutput<C>: Debug, VoidNumber<C>: Debug"), - Eq(bound = "UtxoSetOutput<C>: Eq, VoidNumber<C>: Eq"), - Hash(bound = "UtxoSetOutput<C>: Hash, VoidNumber<C>: Hash"), - PartialEq(bound = "UtxoSetOutput<C>: PartialEq, VoidNumber<C>: PartialEq") + Clone(bound = "UtxoAccumulatorOutput<C>: Clone, VoidNumber<C>: Clone"), + Copy(bound = "UtxoAccumulatorOutput<C>: Copy, VoidNumber<C>: Copy"), + Debug(bound = "UtxoAccumulatorOutput<C>: Debug, VoidNumber<C>: Debug"), + Eq(bound = "UtxoAccumulatorOutput<C>: Eq, VoidNumber<C>: Eq"), + Hash(bound = "UtxoAccumulatorOutput<C>: Hash, VoidNumber<C>: Hash"), + PartialEq(bound = "UtxoAccumulatorOutput<C>: PartialEq, VoidNumber<C>: PartialEq") )] pub struct SenderPost<C> where C: Configuration, { - /// UTXO Set Output - pub utxo_set_output: UtxoSetOutput<C>, + /// UTXO Accumulator Output + pub utxo_accumulator_output: UtxoAccumulatorOutput<C>, /// Void Number pub void_number: VoidNumber<C>, @@ -1108,7 +1146,7 @@ where pub fn extend_input(&self, input: &mut ProofInput<C>) { // TODO: Add a "public part" trait that extracts the public part of `Sender` (using // `SenderVar` to determine the types), then generate this method automatically. - C::ProofSystem::extend(input, &self.utxo_set_output); + C::ProofSystem::extend(input, &self.utxo_accumulator_output); C::ProofSystem::extend(input, &self.void_number); } @@ -1119,9 +1157,9 @@ where L: SenderLedger<C>, { Ok(SenderPostingKey { - utxo_set_output: ledger - .has_matching_utxo_set_output(self.utxo_set_output) - .ok_or(SenderPostError::InvalidUtxoSetOutput)?, + utxo_accumulator_output: ledger + .has_matching_utxo_accumulator_output(self.utxo_accumulator_output) + .ok_or(SenderPostError::InvalidUtxoAccumulatorOutput)?, void_number: ledger .is_unspent(self.void_number) .ok_or(SenderPostError::AssetSpent)?, @@ -1135,8 +1173,8 @@ where C: Configuration, L: SenderLedger<C> + ?Sized, { - /// UTXO Set Output Posting Key - utxo_set_output: L::ValidUtxoSetOutput, + /// UTXO Accumulator Output Posting Key + utxo_accumulator_output: L::ValidUtxoAccumulatorOutput, /// Void Number Posting Key void_number: L::ValidVoidNumber, @@ -1150,14 +1188,14 @@ where /// Extends proof public input with `self`. #[inline] pub fn extend_input(&self, input: &mut ProofInput<C>) { - C::ProofSystem::extend(input, self.utxo_set_output.as_ref()); + C::ProofSystem::extend(input, self.utxo_accumulator_output.as_ref()); C::ProofSystem::extend(input, self.void_number.as_ref()); } /// Posts `self` to the sender `ledger`. #[inline] pub fn post(self, super_key: &L::SuperPostingKey, ledger: &mut L) { - ledger.spend(self.utxo_set_output, self.void_number, super_key); + ledger.spend(self.utxo_accumulator_output, self.void_number, super_key); } /// Posts all of the [`SenderPostingKey`] in `iter` to the sender `ledger`. @@ -1168,7 +1206,7 @@ where { ledger.spend_all( iter.into_iter() - .map(move |k| (k.utxo_set_output, k.void_number)), + .map(move |k| (k.utxo_accumulator_output, k.void_number)), super_key, ) } @@ -1235,13 +1273,13 @@ where } /// Returns `true` whenever `self.utxo` and `rhs.utxo` can be inserted in any order into the - /// `utxo_set`. + /// `utxo_accumulator`. #[inline] - pub fn is_independent_from<A>(&self, rhs: &Self, utxo_set: &A) -> bool + pub fn is_independent_from<A>(&self, rhs: &Self, utxo_accumulator: &A) -> bool where - A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + A: Accumulator<Item = Utxo<C>, Model = C::UtxoAccumulatorModel>, { - utxo_set.are_independent(&self.utxo, &rhs.utxo) + utxo_accumulator.are_independent(&self.utxo, &rhs.utxo) } /// Extracts the ledger posting data from `self`. @@ -1336,6 +1374,10 @@ where } /// Receiver Ledger +/// +/// This is the validation trait for ensuring that a particular instance of [`Receiver`] is valid +/// according to the ledger state. These methods are the minimum required for a ledger which accepts +/// the [`Receiver`] abstraction. pub trait ReceiverLedger<C> where C: Configuration, @@ -1426,6 +1468,11 @@ pub enum ReceiverPostError { } /// Receiver Post +/// +/// This `struct` represents the public data required to verify that a particular instance of a +/// [`Receiver`] should be valid according to the [`ReceiverLedger`]. The rest of the information +/// required to verify a [`Transfer`] is stored in the [`TransferPost`] which includes the [`Proof`] +/// of validity. #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), @@ -1856,6 +1903,12 @@ where } /// Transfer Ledger +/// +/// This is the validation trait for ensuring that a particular instance of [`Transfer`] is valid +/// according to the ledger state. These methods are the minimum required for a ledger which accepts +/// the [`Transfer`] abstraction. This `trait` inherits from [`SenderLedger`] and [`ReceiverLedger`] +/// which validate the [`Sender`] and [`Receiver`] parts of any [`Transfer`]. See their +/// documentation for more. pub trait TransferLedger<C>: SenderLedger<C, SuperPostingKey = (Self::ValidProof, TransferLedgerSuperPostingKey<C, Self>)> + ReceiverLedger<C, SuperPostingKey = (Self::ValidProof, TransferLedgerSuperPostingKey<C, Self>)> where diff --git a/manta-accounting/src/transfer/test.rs b/manta-accounting/src/transfer/test.rs index 6aa510118..45c751782 100644 --- a/manta-accounting/src/transfer/test.rs +++ b/manta-accounting/src/transfer/test.rs @@ -113,19 +113,19 @@ where pub struct TransferDistribution<'p, C, A> where C: Configuration, - A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + A: Accumulator<Item = Utxo<C>, Model = C::UtxoAccumulatorModel>, { /// Parameters pub parameters: &'p Parameters<C>, - /// UTXO Set - pub utxo_set: &'p mut A, + /// UTXO Accumulator + pub utxo_accumulator: &'p mut A, } impl<'p, C, A> From<FixedTransferDistribution<'p, C, A>> for TransferDistribution<'p, C, A> where C: Configuration, - A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + A: Accumulator<Item = Utxo<C>, Model = C::UtxoAccumulatorModel>, { #[inline] fn from(distribution: FixedTransferDistribution<'p, C, A>) -> Self { @@ -142,7 +142,7 @@ where pub struct FixedTransferDistribution<'p, C, A> where C: Configuration, - A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + A: Accumulator<Item = Utxo<C>, Model = C::UtxoAccumulatorModel>, { /// Base Transfer Distribution pub base: TransferDistribution<'p, C, A>, @@ -168,28 +168,28 @@ impl<C, const SOURCES: usize, const SENDERS: usize, const RECEIVERS: usize, cons where C: Configuration, { - /// Samples a [`TransferPost`] from `parameters` and `utxo_set` using `proving_context` and - /// `rng`. + /// Samples a [`TransferPost`] from `parameters` and `utxo_accumulator` using `proving_context` + /// and `rng`. #[inline] pub fn sample_post<A, R>( proving_context: &ProvingContext<C>, parameters: &Parameters<C>, - utxo_set: &mut A, + utxo_accumulator: &mut A, rng: &mut R, ) -> Result<TransferPost<C>, ProofSystemError<C>> where - A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + A: Accumulator<Item = Utxo<C>, Model = C::UtxoAccumulatorModel>, R: CryptoRng + RngCore + ?Sized, { Self::sample( TransferDistribution { parameters, - utxo_set, + utxo_accumulator, }, rng, ) .into_post( - FullParameters::new(parameters, utxo_set.model()), + FullParameters::new(parameters, utxo_accumulator.model()), proving_context, rng, ) @@ -201,23 +201,23 @@ where pub fn sample_and_check_proof<A, R>( public_parameters: &ProofSystemPublicParameters<C>, parameters: &Parameters<C>, - utxo_set: &mut A, + utxo_accumulator: &mut A, rng: &mut R, ) -> Result<bool, ProofSystemError<C>> where - A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + A: Accumulator<Item = Utxo<C>, Model = C::UtxoAccumulatorModel>, R: CryptoRng + RngCore + ?Sized, { let (proving_context, verifying_context) = Self::generate_context( public_parameters, - FullParameters::new(parameters, utxo_set.model()), + FullParameters::new(parameters, utxo_accumulator.model()), rng, )?; Self::sample_and_check_proof_with_context( &proving_context, &verifying_context, parameters, - utxo_set, + utxo_accumulator, rng, ) } @@ -229,14 +229,14 @@ where proving_context: &ProvingContext<C>, verifying_context: &VerifyingContext<C>, parameters: &Parameters<C>, - utxo_set: &mut A, + utxo_accumulator: &mut A, rng: &mut R, ) -> Result<bool, ProofSystemError<C>> where - A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + A: Accumulator<Item = Utxo<C>, Model = C::UtxoAccumulatorModel>, R: CryptoRng + RngCore + ?Sized, { - let post = Self::sample_post(proving_context, parameters, utxo_set, rng)?; + let post = Self::sample_post(proving_context, parameters, utxo_accumulator, rng)?; C::ProofSystem::verify( verifying_context, &post.generate_proof_input(), @@ -252,12 +252,12 @@ fn sample_senders_and_receivers<C, A, R>( asset_id: AssetId, senders: &[AssetValue], receivers: &[AssetValue], - utxo_set: &mut A, + utxo_accumulator: &mut A, rng: &mut R, ) -> (Vec<Sender<C>>, Vec<Receiver<C>>) where C: Configuration, - A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + A: Accumulator<Item = Utxo<C>, Model = C::UtxoAccumulatorModel>, R: CryptoRng + RngCore + ?Sized, { ( @@ -270,8 +270,8 @@ where parameters.key_agreement.derive_owned(rng.gen()), asset_id.with(*v), ); - sender.insert_utxo(utxo_set); - sender.try_upgrade(utxo_set).unwrap() + sender.insert_utxo(utxo_accumulator); + sender.try_upgrade(utxo_accumulator).unwrap() }) .collect(), receivers @@ -299,7 +299,7 @@ impl< > Sample<TransferDistribution<'_, C, A>> for Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS> where C: Configuration, - A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + A: Accumulator<Item = Utxo<C>, Model = C::UtxoAccumulatorModel>, { #[inline] fn sample<R>(distribution: TransferDistribution<'_, C, A>, rng: &mut R) -> Self @@ -316,7 +316,7 @@ where asset.id, &secret_input, &output, - distribution.utxo_set, + distribution.utxo_accumulator, rng, ); Self::new( @@ -340,7 +340,7 @@ impl< for Transfer<C, SOURCES, SENDERS, RECEIVERS, SINKS> where C: Configuration, - A: Accumulator<Item = Utxo<C>, Model = C::UtxoSetModel>, + A: Accumulator<Item = Utxo<C>, Model = C::UtxoAccumulatorModel>, { #[inline] fn sample<R>(distribution: FixedTransferDistribution<'_, C, A>, rng: &mut R) -> Self @@ -352,7 +352,7 @@ where distribution.asset_id, &value_distribution(SENDERS, distribution.sender_sum, rng), &value_distribution(RECEIVERS, distribution.receiver_sum, rng), - distribution.base.utxo_set, + distribution.base.utxo_accumulator, rng, ); Self::new( diff --git a/manta-accounting/src/wallet/mod.rs b/manta-accounting/src/wallet/mod.rs index 6f8b6b66f..123ef9698 100644 --- a/manta-accounting/src/wallet/mod.rs +++ b/manta-accounting/src/wallet/mod.rs @@ -15,8 +15,48 @@ // along with manta-rs. If not, see <http://www.gnu.org/licenses/>. //! Wallet Abstractions +//! +//! This module defines the notion of a "wallet" which can store and manage accounts that control +//! private assets, those defined in [`crate::asset`] and [`crate::transfer`]. The [`Wallet`] +//! abstraction implements the main interface to an account and requires two asynchronous +//! connections, one to a zero-knowledge signing source and secret manager called the [`Signer`] and +//! another connection to the [`Ledger`] itself. The wallet itself only stores the information +//! related to the current balances of any particular account and queries the [`Signer`] and the +//! [`Ledger`] to get the newest balances from incoming transactions and to send out transactions of +//! its own. +//! +//! [`Signer`]: signer::Connection +//! [`Ledger`]: ledger::Connection -mod state; +use crate::{ + asset::{Asset, AssetId, AssetList, AssetMetadata, AssetValue}, + transfer::{ + canonical::{Transaction, TransactionKind}, + Configuration, ReceivingKey, + }, + wallet::{ + ledger::{Checkpoint, PullResponse, PushResponse}, + signer::{ + ReceivingKeyRequest, SignError, SignRequest, SignResponse, SyncError, SyncRequest, + SyncResponse, + }, + }, +}; +use alloc::{ + collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}, + vec::Vec, +}; +use core::{fmt::Debug, hash::Hash, marker::PhantomData}; +use manta_util::ops::ControlFlow; + +#[cfg(feature = "serde")] +use manta_util::serde::{Deserialize, Serialize}; + +#[cfg(feature = "std")] +use std::{ + collections::hash_map::{Entry as HashMapEntry, HashMap, RandomState}, + hash::BuildHasher, +}; pub mod ledger; pub mod signer; @@ -25,4 +65,513 @@ pub mod signer; #[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] pub mod test; -pub use state::*; +/// Balance State +pub trait BalanceState: Default { + /// Returns the current balance associated with this `id`. + fn balance(&self, id: AssetId) -> AssetValue; + + /// Returns true if `self` contains at least `asset.value` of the asset of kind `asset.id`. + #[inline] + fn contains(&self, asset: Asset) -> bool { + self.balance(asset.id) >= asset.value + } + + /// Deposits `asset` into the balance state, increasing the balance of the asset stored at + /// `asset.id` by an amount equal to `asset.value`. + fn deposit(&mut self, asset: Asset); + + /// Deposits every asset in `assets` into the balance state. + #[inline] + fn deposit_all<I>(&mut self, assets: I) + where + I: IntoIterator<Item = Asset>, + { + assets.into_iter().for_each(move |a| self.deposit(a)); + } + + /// Withdraws `asset` from the balance state returning `false` if it would overdraw the balance. + fn withdraw(&mut self, asset: Asset) -> bool; + + /// Withdraws every asset in `assets` from the balance state, returning `false` if it would + /// overdraw the balance. + #[inline] + fn withdraw_all<I>(&mut self, assets: I) -> bool + where + I: IntoIterator<Item = Asset>, + { + for asset in AssetList::from_iter(assets) { + if !self.withdraw(asset) { + return false; + } + } + true + } + + /// Clears the entire balance state. + fn clear(&mut self); +} + +impl BalanceState for AssetList { + #[inline] + fn balance(&self, id: AssetId) -> AssetValue { + self.value(id) + } + + #[inline] + fn deposit(&mut self, asset: Asset) { + self.deposit(asset) + } + + #[inline] + fn withdraw(&mut self, asset: Asset) -> bool { + self.withdraw(asset) + } + + #[inline] + fn clear(&mut self) { + self.clear(); + } +} + +/// Performs a withdraw on `balance` returning `false` if it would overflow. +#[inline] +fn withdraw(balance: Option<&mut AssetValue>, withdraw: AssetValue) -> bool { + match balance { + Some(balance) => { + *balance = match balance.checked_sub(withdraw) { + Some(balance) => balance, + _ => return false, + }; + true + } + _ => false, + } +} + +/// Adds implementation of [`BalanceState`] for a map type with the given `$entry` type. +macro_rules! impl_balance_state_map_body { + ($entry:tt) => { + #[inline] + fn balance(&self, id: AssetId) -> AssetValue { + self.get(&id).copied().unwrap_or_default() + } + + #[inline] + fn deposit(&mut self, asset: Asset) { + if asset.is_zero() { + return; + } + match self.entry(asset.id) { + $entry::Vacant(entry) => { + entry.insert(asset.value); + } + $entry::Occupied(entry) => *entry.into_mut() += asset.value, + } + } + + #[inline] + fn withdraw(&mut self, asset: Asset) -> bool { + if !asset.is_zero() { + withdraw(self.get_mut(&asset.id), asset.value) + } else { + true + } + } + + #[inline] + fn clear(&mut self) { + self.clear(); + } + }; +} + +/// B-Tree Map [`BalanceState`] Implementation +pub type BTreeMapBalanceState = BTreeMap<AssetId, AssetValue>; + +impl BalanceState for BTreeMapBalanceState { + impl_balance_state_map_body! { BTreeMapEntry } +} + +/// Hash Map [`BalanceState`] Implementation +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +pub type HashMapBalanceState<S = RandomState> = HashMap<AssetId, AssetValue, S>; + +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +impl<S> BalanceState for HashMapBalanceState<S> +where + S: BuildHasher + Default, +{ + impl_balance_state_map_body! { HashMapEntry } +} + +/// Wallet +pub struct Wallet<C, L, S = signer::Signer<C>, B = BTreeMapBalanceState> +where + C: Configuration, + L: ledger::Connection<C>, + S: signer::Connection<C>, + B: BalanceState, +{ + /// Ledger Connection + ledger: L, + + /// Ledger Checkpoint + checkpoint: L::Checkpoint, + + /// Signer Connection + signer: S, + + /// Balance State + assets: B, + + /// Type Parameter Marker + __: PhantomData<C>, +} + +impl<C, L, S, B> Wallet<C, L, S, B> +where + C: Configuration, + L: ledger::Connection<C>, + S: signer::Connection<C>, + B: BalanceState, +{ + /// Builds a new [`Wallet`] without checking if `ledger`, `checkpoint`, `signer`, and `assets` + /// are properly synchronized. + #[inline] + fn new_unchecked(ledger: L, checkpoint: L::Checkpoint, signer: S, assets: B) -> Self { + Self { + ledger, + checkpoint, + signer, + assets, + __: PhantomData, + } + } + + /// Starts a new [`Wallet`] from existing `signer` and `ledger` connections. + /// + /// # Setting Up the Wallet + /// + /// Creating a [`Wallet`] using this method should be followed with a call to [`sync`] or + /// [`recover`] to retrieve the current checkpoint and balance for this [`Wallet`]. If the + /// backing `signer` is known to be already initialized, a call to [`sync`] is enough, + /// otherwise, a call to [`recover`] is necessary to retrieve the full balance state. + /// + /// [`sync`]: Self::sync + /// [`recover`]: Self::recover + #[inline] + pub fn new(ledger: L, signer: S) -> Self { + Self::new_unchecked(ledger, Default::default(), signer, Default::default()) + } + + /// Returns the current balance associated with this `id`. + #[inline] + pub fn balance(&self, id: AssetId) -> AssetValue { + self.assets.balance(id) + } + + /// Returns true if `self` contains at least `asset.value` of the asset of kind `asset.id`. + #[inline] + pub fn contains(&self, asset: Asset) -> bool { + self.assets.contains(asset) + } + + /// Returns a shared reference to the balance state associated to `self`. + #[inline] + pub fn assets(&self) -> &B { + &self.assets + } + + /// Returns a shared reference to the ledger connection associated to `self`. + #[inline] + pub fn ledger(&self) -> &L { + &self.ledger + } + + /// Returns the [`Checkpoint`](ledger::Connection::Checkpoint) representing the current state + /// of this wallet. + #[inline] + pub fn checkpoint(&self) -> &L::Checkpoint { + &self.checkpoint + } + + /// Resets `self` to the default checkpoint and no balance. A call to this method should be + /// followed by a call to [`sync`](Self::sync) to retrieve the correct checkpoint and balance. + /// + /// # Note + /// + /// This is not a "full wallet recovery" which would involve resetting the signer as well as + /// this wallet state. See the [`recover`](Self::recover) method for more. + #[inline] + pub fn reset(&mut self) { + self.checkpoint = Default::default(); + self.assets = Default::default(); + } + + /// Performs full wallet recovery. + /// + /// # Failure Conditions + /// + /// This method returns an element of type [`Error`] on failure, which can result from any + /// number of synchronization issues between the wallet, the ledger, and the signer. See the + /// [`InconsistencyError`] type for more information on the kinds of errors that can occur and + /// how to resolve them. + #[inline] + pub fn recover(&mut self) -> Result<(), Error<C, L, S>> { + self.reset(); + while self.sync_with(true)?.is_continue() {} + Ok(()) + } + + /// Pulls data from the ledger, synchronizing the wallet and balance state. This method loops + /// continuously calling [`sync_partial`](Self::sync_partial) until all the ledger data has + /// arrived at and has been synchronized with the wallet. + /// + /// # Failure Conditions + /// + /// This method returns an element of type [`Error`] on failure, which can result from any + /// number of synchronization issues between the wallet, the ledger, and the signer. See the + /// [`InconsistencyError`] type for more information on the kinds of errors that can occur and + /// how to resolve them. + #[inline] + pub fn sync(&mut self) -> Result<(), Error<C, L, S>> { + while self.sync_partial()?.is_continue() {} + Ok(()) + } + + /// Pulls data from the ledger, synchronizing the wallet and balance state. This method returns + /// a [`ControlFlow`] for matching against to determine if the wallet requires more + /// synchronization. + /// + /// # Failure Conditions + /// + /// This method returns an element of type [`Error`] on failure, which can result from any + /// number of synchronization issues between the wallet, the ledger, and the signer. See the + /// [`InconsistencyError`] type for more information on the kinds of errors that can occur and + /// how to resolve them. + #[inline] + pub fn sync_partial(&mut self) -> Result<ControlFlow, Error<C, L, S>> { + self.sync_with(false) + } + + /// Pulls data from the ledger, synchronizing the wallet and balance state. + #[inline] + fn sync_with(&mut self, with_recovery: bool) -> Result<ControlFlow, Error<C, L, S>> { + let PullResponse { + should_continue, + checkpoint, + receivers, + senders, + } = self + .ledger + .pull(&self.checkpoint) + .map_err(Error::LedgerConnectionError)?; + if checkpoint < self.checkpoint { + return Err(Error::Inconsistency(InconsistencyError::LedgerCheckpoint)); + } + match self + .signer + .sync(SyncRequest { + with_recovery, + starting_index: self.checkpoint.receiver_index(), + inserts: receivers.into_iter().collect(), + removes: senders.into_iter().collect(), + }) + .map_err(Error::SignerConnectionError)? + { + Ok(SyncResponse::Partial { deposit, withdraw }) => { + self.assets.deposit_all(deposit); + if !self.assets.withdraw_all(withdraw) { + return Err(Error::Inconsistency(InconsistencyError::WalletBalance)); + } + } + Ok(SyncResponse::Full { assets }) => { + self.assets.clear(); + self.assets.deposit_all(assets); + } + Err(SyncError::InconsistentSynchronization { starting_index }) => { + // FIXME: What should be done when we receive an `InconsistentSynchronization` error + // from the signer? + // - One option is to do some sort of (exponential) backoff algorithm to + // find the point at which the signer and the wallet are able to + // synchronize again. The correct algorithm may be simply to exchange + // some checkpoints between the signer and the wallet until they can + // agree on a minimal one. + // - In the worst case we would have to recover the wallet (not necessarily + // the signer), which is what the docs currently recommend. + // + let _ = starting_index; + return Err(Error::Inconsistency( + InconsistencyError::SignerSynchronization, + )); + } + } + self.checkpoint = checkpoint; + Ok(ControlFlow::should_continue(should_continue)) + } + + /// Checks if `transaction` can be executed on the balance state of `self`, returning the + /// kind of update that should be performed on the balance state if the transaction is + /// successfully posted to the ledger. + /// + /// # Safety + /// + /// This method is already called by [`post`](Self::post), but can be used by custom + /// implementations to perform checks elsewhere. + #[inline] + pub fn check(&self, transaction: &Transaction<C>) -> Result<TransactionKind, Asset> { + transaction.check(move |a| self.contains(a)) + } + + /// Posts a transaction to the ledger, returning `true` if the `transaction` was successfully + /// saved onto the ledger. This method automatically synchronizes with the ledger before + /// posting, _but not after_. To amortize the cost of future calls to [`post`](Self::post), the + /// [`sync`](Self::sync) method can be used to synchronize with the ledger. + /// + /// # Failure Conditions + /// + /// This method returns `false` when there were no errors in producing transfer data and + /// sending and receiving from the ledger, but instead the ledger just did not accept the + /// transaction as is. This could be caused by an external update to the ledger while the + /// signer was building the transaction that caused the wallet and the ledger to get out of + /// sync. In this case, [`post`](Self::post) can safely be called again, to retry the + /// transaction. + /// + /// This method returns an error in any other case. The internal state of the wallet is kept + /// consistent between calls and recoverable errors are returned for the caller to handle. + #[inline] + pub fn post( + &mut self, + transaction: Transaction<C>, + metadata: Option<AssetMetadata>, + ) -> Result<bool, Error<C, L, S>> { + self.sync()?; + self.check(&transaction) + .map_err(Error::InsufficientBalance)?; + let SignResponse { posts } = self + .signer + .sign(SignRequest { + transaction, + metadata, + }) + .map_err(Error::SignerConnectionError)? + .map_err(Error::SignError)?; + let PushResponse { success } = self + .ledger + .push(posts) + .map_err(Error::LedgerConnectionError)?; + Ok(success) + } + + /// Returns public receiving keys according to the `request`. + #[inline] + pub fn receiving_keys( + &mut self, + request: ReceivingKeyRequest, + ) -> Result<Vec<ReceivingKey<C>>, S::Error> { + self.signer.receiving_keys(request) + } +} + +/// Inconsistency Error +/// +/// This `enum` is the error state for the [`sync`](Wallet::sync) method on [`Wallet`]. See its +/// documentation for more. The variants below describe their error conditions and how to solve the +/// issue whenever they arise. +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum InconsistencyError { + /// Ledger Checkpoint Inconsistency + /// + /// This error state arises when the ledger checkpoint is behind the checkpoint of the wallet. + /// To resolve this error, ensure that the ledger connection is correct and try again. This + /// error does not result in a bad wallet state. + LedgerCheckpoint, + + /// Wallet Balance Inconsistency + /// + /// ⚠️ This error causes the wallet system to enter an inconsistent state. ⚠️ + /// + /// This error state arises whenever the signer requests a withdraw from the wallet that would + /// overdraw the balance. To resolve this error, ensure that the signer connection is correct + /// and perform a wallet reset by resetting the checkpoint and balance state with a call to + /// [`reset`](Wallet::reset). If other errors continue or if there is reason to suspect that the + /// signer or ledger connections (or their true state) are corrupted, a full recovery is + /// required. See the [`recover`](Wallet::recover) method for more. + WalletBalance, + + /// Signer Synchronization Inconsistency + /// + /// ⚠️ This error causes the wallet system to enter an inconsistent state. ⚠️ + /// + /// This error state arises whenever the signer gets behind the wallet checkpoint. To resolve + /// this error, ensure that the signer connection is correct and perform a wallet reset by + /// resetting the checkpoint and balance state with a call to [`reset`](Wallet::reset). If other + /// errors continue or if there is reason to suspect that the signer or ledger connections (or + /// their true state) are corrupted, a full recovery is required. See the + /// [`recover`](Wallet::recover) method for more. + SignerSynchronization, +} + +/// Wallet Error +/// +/// This `enum` is the error state for [`Wallet`] methods. See [`sync`](Wallet::sync) and +/// [`post`](Wallet::post) for more. +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound( + deserialize = r" + SignError<C>: Deserialize<'de>, + L::Error: Deserialize<'de>, + S::Error: Deserialize<'de> + ", + serialize = r" + SignError<C>: Serialize, + L::Error: Serialize, + S::Error: Serialize + " + ), + crate = "manta_util::serde", + deny_unknown_fields + ) +)] +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "SignError<C>: Clone, L::Error: Clone, S::Error: Clone"), + Copy(bound = "SignError<C>: Copy, L::Error: Copy, S::Error: Copy"), + Debug(bound = "SignError<C>: Debug, L::Error: Debug, S::Error: Debug"), + Eq(bound = "SignError<C>: Eq, L::Error: Eq, S::Error: Eq"), + Hash(bound = "SignError<C>: Hash, L::Error: Hash, S::Error: Hash"), + PartialEq(bound = "SignError<C>: PartialEq, L::Error: PartialEq, S::Error: PartialEq") +)] +pub enum Error<C, L, S> +where + C: Configuration, + L: ledger::Connection<C>, + S: signer::Connection<C>, +{ + /// Insufficient Balance + InsufficientBalance(Asset), + + /// Inconsistency Error + /// + /// See the documentation of [`InconsistencyError`] for more. + Inconsistency(InconsistencyError), + + /// Signing Error + SignError(SignError<C>), + + /// Signer Connection Error + SignerConnectionError(S::Error), + + /// Ledger Connection Error + LedgerConnectionError(L::Error), +} diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index b071d4d0d..999fbb590 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -124,12 +124,20 @@ where /// Recovery Flag /// /// If `with_recovery` is set to `true`, the [`GAP_LIMIT`] is used during sync to perform a full - /// recovery. + /// recovery. See [`Configuration::HierarchicalKeyDerivationScheme`] for the scheme where the + /// [`GAP_LIMIT`] is configured. /// /// [`GAP_LIMIT`]: HierarchicalKeyDerivationScheme::GAP_LIMIT pub with_recovery: bool, /// Starting Index + /// + /// This index is the starting point for insertions and indicates how far into the + /// [`UtxoAccumulator`] the insertions received are starting from. The signer may be ahead of + /// this index and so can skip those UTXOs which are in between the `starting_index` and its own + /// internal index. + /// + /// [`UtxoAccumulator`]: Configuration::UtxoAccumulator pub starting_index: usize, /// Balance Insertions @@ -350,7 +358,7 @@ pub trait Configuration: transfer::Configuration { >; /// [`Utxo`] Accumulator Type - type UtxoSet: Accumulator<Item = Self::Utxo, Model = Self::UtxoSetModel> + type UtxoAccumulator: Accumulator<Item = Self::Utxo, Model = Self::UtxoAccumulatorModel> + ExactSizeAccumulator + OptimizedAccumulator + Rollback; @@ -436,12 +444,12 @@ where bound( deserialize = r" AccountTable<C>: Deserialize<'de>, - C::UtxoSet: Deserialize<'de>, + C::UtxoAccumulator: Deserialize<'de>, C::AssetMap: Deserialize<'de> ", serialize = r" AccountTable<C>: Serialize, - C::UtxoSet: Serialize, + C::UtxoAccumulator: Serialize, C::AssetMap: Serialize ", ), @@ -458,12 +466,12 @@ where /// # Note /// /// For now, we only use the default account, and the rest of the storage data is related to - /// this account. Eventually, we want to have a global `utxo_set` for all accounts and a local - /// `assets` map for each account. + /// this account. Eventually, we want to have a global `utxo_accumulator` for all accounts and + /// a local `assets` map for each account. accounts: AccountTable<C>, - /// UTXO Set - utxo_set: C::UtxoSet, + /// UTXO Accumulator + utxo_accumulator: C::UtxoAccumulator, /// Asset Distribution assets: C::AssetMap, @@ -477,21 +485,32 @@ impl<C> SignerState<C> where C: Configuration, { - /// Builds a new [`SignerState`] from `keys`, `utxo_set`, and `assets`. + /// Builds a new [`SignerState`] from `keys`, `utxo_accumulator`, and `assets`. #[inline] - fn build(accounts: AccountTable<C>, utxo_set: C::UtxoSet, assets: C::AssetMap) -> Self { + fn build( + accounts: AccountTable<C>, + utxo_accumulator: C::UtxoAccumulator, + assets: C::AssetMap, + ) -> Self { Self { accounts, - utxo_set, + utxo_accumulator, assets, rng: FromEntropy::from_entropy(), } } - /// Builds a new [`SignerState`] from `keys` and `utxo_set`. + /// Builds a new [`SignerState`] from `keys` and `utxo_accumulator`. #[inline] - pub fn new(keys: C::HierarchicalKeyDerivationScheme, utxo_set: C::UtxoSet) -> Self { - Self::build(AccountTable::<C>::new(keys), utxo_set, Default::default()) + pub fn new( + keys: C::HierarchicalKeyDerivationScheme, + utxo_accumulator: C::UtxoAccumulator, + ) -> Self { + Self::build( + AccountTable::<C>::new(keys), + utxo_accumulator, + Default::default(), + ) } /// Inserts the new `utxo`-`encrypted_note` pair if a known key can decrypt the note and @@ -532,7 +551,7 @@ where if let Some(index) = void_numbers.iter().position(move |v| v == &void_number) { void_numbers.remove(index); } else { - self.utxo_set.insert(&utxo); + self.utxo_accumulator.insert(&utxo); self.assets.insert((index, ephemeral_public_key), asset); if !asset.is_zero() { deposit.push(asset); @@ -541,12 +560,12 @@ where } } } - self.utxo_set.insert_nonprovable(&utxo); + self.utxo_accumulator.insert_nonprovable(&utxo); Ok(()) } - /// Checks if `asset` matches with `void_number`, removing it from the `utxo_set` and inserting - /// it into the `withdraw` set if this is the case. + /// Checks if `asset` matches with `void_number`, removing it from the `utxo_accumulator` and + /// inserting it into the `withdraw` set if this is the case. #[inline] fn is_asset_unspent( parameters: &Parameters<C>, @@ -554,7 +573,7 @@ where ephemeral_public_key: &PublicKey<C>, asset: Asset, void_numbers: &mut Vec<VoidNumber<C>>, - utxo_set: &mut C::UtxoSet, + utxo_accumulator: &mut C::UtxoAccumulator, withdraw: &mut Vec<Asset>, ) -> bool { let utxo = C::utxo( @@ -567,7 +586,7 @@ where let void_number = C::void_number(&parameters.void_number_hash, &utxo, secret_key); if let Some(index) = void_numbers.iter().position(move |v| v == &void_number) { void_numbers.remove(index); - utxo_set.remove_proof(&utxo); + utxo_accumulator.remove_proof(&utxo); if !asset.is_zero() { withdraw.push(asset); } @@ -611,7 +630,7 @@ where ephemeral_public_key, *asset, &mut void_numbers, - &mut self.utxo_set, + &mut self.utxo_accumulator, &mut withdraw, ), _ => true, @@ -726,7 +745,7 @@ where mint: Mint<C>, ) -> Result<TransferPost<C>, SignError<C>> { Self::build_post( - FullParameters::new(parameters, self.utxo_set.model()), + FullParameters::new(parameters, self.utxo_accumulator.model()), proving_context, mint, &mut self.rng, @@ -742,7 +761,7 @@ where private_transfer: PrivateTransfer<C>, ) -> Result<TransferPost<C>, SignError<C>> { Self::build_post( - FullParameters::new(parameters, self.utxo_set.model()), + FullParameters::new(parameters, self.utxo_accumulator.model()), proving_context, private_transfer, &mut self.rng, @@ -758,7 +777,7 @@ where reclaim: Reclaim<C>, ) -> Result<TransferPost<C>, SignError<C>> { Self::build_post( - FullParameters::new(parameters, self.utxo_set.model()), + FullParameters::new(parameters, self.utxo_accumulator.model()), proving_context, reclaim, &mut self.rng, @@ -820,7 +839,7 @@ where for _ in 0..needed_mints { let (mint, pre_sender) = self.mint_zero(parameters, asset_id)?; posts.push(self.mint_post(parameters, &proving_context.mint, mint)?); - pre_sender.insert_utxo(&mut self.utxo_set); + pre_sender.insert_utxo(&mut self.utxo_accumulator); pre_senders.push(pre_sender); } Ok(()) @@ -844,7 +863,7 @@ where .chunk_by::<{ PrivateTransferShape::SENDERS }>(); for chunk in &mut iter { let senders = array_map(chunk, |s| { - s.try_upgrade(&self.utxo_set) + s.try_upgrade(&self.utxo_accumulator) .expect("Unable to upgrade expected UTXO.") }); let (receivers, mut join) = self.next_join( @@ -857,7 +876,7 @@ where &proving_context.private_transfer, PrivateTransfer::build(senders, receivers), )?); - join.insert_utxos(&mut self.utxo_set); + join.insert_utxos(&mut self.utxo_accumulator); joins.push(join.pre_sender); new_zeroes.append(&mut join.zeroes); } @@ -875,7 +894,7 @@ where Ok(into_array_unchecked( pre_senders .into_iter() - .map(move |s| s.try_upgrade(&self.utxo_set)) + .map(move |s| s.try_upgrade(&self.utxo_accumulator)) .collect::<Option<Vec<_>>>() .expect("Unable to upgrade expected UTXOs."), )) @@ -897,14 +916,14 @@ impl<C> Clone for SignerState<C> where C: Configuration, C::HierarchicalKeyDerivationScheme: Clone, - C::UtxoSet: Clone, + C::UtxoAccumulator: Clone, C::AssetMap: Clone, { #[inline] fn clone(&self) -> Self { Self::build( self.accounts.clone(), - self.utxo_set.clone(), + self.utxo_accumulator.clone(), self.assets.clone(), ) } @@ -938,7 +957,7 @@ where accounts: AccountTable<C>, proving_context: C::ProvingContextCache, parameters: Parameters<C>, - utxo_set: C::UtxoSet, + utxo_accumulator: C::UtxoAccumulator, assets: C::AssetMap, rng: C::Rng, ) -> Self { @@ -949,7 +968,7 @@ where }, SignerState { accounts, - utxo_set, + utxo_accumulator, assets, rng, }, @@ -967,14 +986,14 @@ where accounts: AccountTable<C>, proving_context: C::ProvingContextCache, parameters: Parameters<C>, - utxo_set: C::UtxoSet, + utxo_accumulator: C::UtxoAccumulator, rng: C::Rng, ) -> Self { Self::new_inner( accounts, proving_context, parameters, - utxo_set, + utxo_accumulator, Default::default(), rng, ) @@ -995,14 +1014,14 @@ where /// Updates the internal ledger state, returning the new asset distribution. #[inline] pub fn sync(&mut self, request: SyncRequest<C>) -> Result<SyncResponse, SyncError> { - // TODO: Do a capacity check on the current UTXO set? + // TODO: Do a capacity check on the current UTXO accumulator? // - // if self.utxo_set.capacity() < starting_index { + // if self.utxo_accumulator.capacity() < starting_index { // panic!("full capacity") // } // - let utxo_set_len = self.state.utxo_set.len(); - match utxo_set_len.checked_sub(request.starting_index) { + let utxo_accumulator_len = self.state.utxo_accumulator.len(); + match utxo_accumulator_len.checked_sub(request.starting_index) { Some(diff) => { let result = self.state.sync_with( &self.parameters.parameters, @@ -1011,11 +1030,11 @@ where request.removes, diff == 0, ); - self.state.utxo_set.commit(); + self.state.utxo_accumulator.commit(); result } _ => Err(SyncError::InconsistentSynchronization { - starting_index: utxo_set_len, + starting_index: utxo_accumulator_len, }), } } @@ -1096,7 +1115,7 @@ where // TODO: Should we do a time-based release mechanism to amortize the cost of reading // from the proving context cache? let result = self.sign_internal(transaction); - self.state.utxo_set.rollback(); + self.state.utxo_accumulator.rollback(); self.parameters.proving_context.release(); result } diff --git a/manta-accounting/src/wallet/state.rs b/manta-accounting/src/wallet/state.rs deleted file mode 100644 index 08914d421..000000000 --- a/manta-accounting/src/wallet/state.rs +++ /dev/null @@ -1,558 +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 <http://www.gnu.org/licenses/>. - -//! Full Wallet Implementation - -use crate::{ - asset::{Asset, AssetId, AssetList, AssetMetadata, AssetValue}, - transfer::{ - canonical::{Transaction, TransactionKind}, - Configuration, ReceivingKey, - }, - wallet::{ - ledger::{self, Checkpoint, PullResponse, PushResponse}, - signer::{ - self, ReceivingKeyRequest, SignError, SignRequest, SignResponse, SyncError, - SyncRequest, SyncResponse, - }, - }, -}; -use alloc::{ - collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}, - vec::Vec, -}; -use core::{fmt::Debug, hash::Hash, marker::PhantomData}; -use manta_util::ops::ControlFlow; - -#[cfg(feature = "serde")] -use manta_util::serde::{Deserialize, Serialize}; - -#[cfg(feature = "std")] -use std::{ - collections::hash_map::{Entry as HashMapEntry, HashMap, RandomState}, - hash::BuildHasher, -}; - -/// Balance State -pub trait BalanceState: Default { - /// Returns the current balance associated with this `id`. - fn balance(&self, id: AssetId) -> AssetValue; - - /// Returns true if `self` contains at least `asset.value` of the asset of kind `asset.id`. - #[inline] - fn contains(&self, asset: Asset) -> bool { - self.balance(asset.id) >= asset.value - } - - /// Deposits `asset` into the balance state, increasing the balance of the asset stored at - /// `asset.id` by an amount equal to `asset.value`. - fn deposit(&mut self, asset: Asset); - - /// Deposits every asset in `assets` into the balance state. - #[inline] - fn deposit_all<I>(&mut self, assets: I) - where - I: IntoIterator<Item = Asset>, - { - assets.into_iter().for_each(move |a| self.deposit(a)); - } - - /// Withdraws `asset` from the balance state returning `false` if it would overdraw the balance. - fn withdraw(&mut self, asset: Asset) -> bool; - - /// Withdraws every asset in `assets` from the balance state, returning `false` if it would - /// overdraw the balance. - #[inline] - fn withdraw_all<I>(&mut self, assets: I) -> bool - where - I: IntoIterator<Item = Asset>, - { - for asset in AssetList::from_iter(assets) { - if !self.withdraw(asset) { - return false; - } - } - true - } - - /// Clears the entire balance state. - fn clear(&mut self); -} - -impl BalanceState for AssetList { - #[inline] - fn balance(&self, id: AssetId) -> AssetValue { - self.value(id) - } - - #[inline] - fn deposit(&mut self, asset: Asset) { - self.deposit(asset) - } - - #[inline] - fn withdraw(&mut self, asset: Asset) -> bool { - self.withdraw(asset) - } - - #[inline] - fn clear(&mut self) { - self.clear(); - } -} - -/// Performs a withdraw on `balance` returning `false` if it would overflow. -#[inline] -fn withdraw(balance: Option<&mut AssetValue>, withdraw: AssetValue) -> bool { - match balance { - Some(balance) => { - *balance = match balance.checked_sub(withdraw) { - Some(balance) => balance, - _ => return false, - }; - true - } - _ => false, - } -} - -/// Adds implementation of [`BalanceState`] for a map type with the given `$entry` type. -macro_rules! impl_balance_state_map_body { - ($entry:tt) => { - #[inline] - fn balance(&self, id: AssetId) -> AssetValue { - self.get(&id).copied().unwrap_or_default() - } - - #[inline] - fn deposit(&mut self, asset: Asset) { - if asset.is_zero() { - return; - } - match self.entry(asset.id) { - $entry::Vacant(entry) => { - entry.insert(asset.value); - } - $entry::Occupied(entry) => *entry.into_mut() += asset.value, - } - } - - #[inline] - fn withdraw(&mut self, asset: Asset) -> bool { - if !asset.is_zero() { - withdraw(self.get_mut(&asset.id), asset.value) - } else { - true - } - } - - #[inline] - fn clear(&mut self) { - self.clear(); - } - }; -} - -/// B-Tree Map [`BalanceState`] Implementation -pub type BTreeMapBalanceState = BTreeMap<AssetId, AssetValue>; - -impl BalanceState for BTreeMapBalanceState { - impl_balance_state_map_body! { BTreeMapEntry } -} - -/// Hash Map [`BalanceState`] Implementation -#[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] -pub type HashMapBalanceState<S = RandomState> = HashMap<AssetId, AssetValue, S>; - -#[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] -impl<S> BalanceState for HashMapBalanceState<S> -where - S: BuildHasher + Default, -{ - impl_balance_state_map_body! { HashMapEntry } -} - -/// Wallet -pub struct Wallet<C, L, S = signer::Signer<C>, B = BTreeMapBalanceState> -where - C: Configuration, - L: ledger::Connection<C>, - S: signer::Connection<C>, - B: BalanceState, -{ - /// Ledger Connection - ledger: L, - - /// Ledger Checkpoint - checkpoint: L::Checkpoint, - - /// Signer Connection - signer: S, - - /// Balance State - assets: B, - - /// Type Parameter Marker - __: PhantomData<C>, -} - -impl<C, L, S, B> Wallet<C, L, S, B> -where - C: Configuration, - L: ledger::Connection<C>, - S: signer::Connection<C>, - B: BalanceState, -{ - /// Builds a new [`Wallet`] without checking if `ledger`, `checkpoint`, `signer`, and `assets` - /// are properly synchronized. - #[inline] - fn new_unchecked(ledger: L, checkpoint: L::Checkpoint, signer: S, assets: B) -> Self { - Self { - ledger, - checkpoint, - signer, - assets, - __: PhantomData, - } - } - - /// Starts a new [`Wallet`] from existing `signer` and `ledger` connections. - /// - /// # Setting Up the Wallet - /// - /// Creating a [`Wallet`] using this method should be followed with a call to [`sync`] or - /// [`recover`] to retrieve the current checkpoint and balance for this [`Wallet`]. If the - /// backing `signer` is known to be already initialized, a call to [`sync`] is enough, - /// otherwise, a call to [`recover`] is necessary to retrieve the full balance state. - /// - /// [`sync`]: Self::sync - /// [`recover`]: Self::recover - #[inline] - pub fn new(ledger: L, signer: S) -> Self { - Self::new_unchecked(ledger, Default::default(), signer, Default::default()) - } - - /// Returns the current balance associated with this `id`. - #[inline] - pub fn balance(&self, id: AssetId) -> AssetValue { - self.assets.balance(id) - } - - /// Returns true if `self` contains at least `asset.value` of the asset of kind `asset.id`. - #[inline] - pub fn contains(&self, asset: Asset) -> bool { - self.assets.contains(asset) - } - - /// Returns a shared reference to the balance state associated to `self`. - #[inline] - pub fn assets(&self) -> &B { - &self.assets - } - - /// Returns a shared reference to the ledger connection associated to `self`. - #[inline] - pub fn ledger(&self) -> &L { - &self.ledger - } - - /// Returns the [`Checkpoint`](ledger::Connection::Checkpoint) representing the current state - /// of this wallet. - #[inline] - pub fn checkpoint(&self) -> &L::Checkpoint { - &self.checkpoint - } - - /// Resets `self` to the default checkpoint and no balance. A call to this method should be - /// followed by a call to [`sync`](Self::sync) to retrieve the correct checkpoint and balance. - /// - /// # Note - /// - /// This is not a "full wallet recovery" which would involve resetting the signer as well as - /// this wallet state. See the [`recover`](Self::recover) method for more. - #[inline] - pub fn reset(&mut self) { - self.checkpoint = Default::default(); - self.assets = Default::default(); - } - - /// Performs full wallet recovery. - /// - /// # Failure Conditions - /// - /// This method returns an element of type [`Error`] on failure, which can result from any - /// number of synchronization issues between the wallet, the ledger, and the signer. See the - /// [`InconsistencyError`] type for more information on the kinds of errors that can occur and - /// how to resolve them. - #[inline] - pub fn recover(&mut self) -> Result<(), Error<C, L, S>> { - self.reset(); - while self.sync_with(true)?.is_continue() {} - Ok(()) - } - - /// Pulls data from the ledger, synchronizing the wallet and balance state. This method loops - /// continuously calling [`sync_partial`](Self::sync_partial) until all the ledger data has - /// arrived at and has been synchronized with the wallet. - /// - /// # Failure Conditions - /// - /// This method returns an element of type [`Error`] on failure, which can result from any - /// number of synchronization issues between the wallet, the ledger, and the signer. See the - /// [`InconsistencyError`] type for more information on the kinds of errors that can occur and - /// how to resolve them. - #[inline] - pub fn sync(&mut self) -> Result<(), Error<C, L, S>> { - while self.sync_partial()?.is_continue() {} - Ok(()) - } - - /// Pulls data from the ledger, synchronizing the wallet and balance state. This method returns - /// a [`ControlFlow`] for matching against to determine if the wallet requires more - /// synchronization. - /// - /// # Failure Conditions - /// - /// This method returns an element of type [`Error`] on failure, which can result from any - /// number of synchronization issues between the wallet, the ledger, and the signer. See the - /// [`InconsistencyError`] type for more information on the kinds of errors that can occur and - /// how to resolve them. - #[inline] - pub fn sync_partial(&mut self) -> Result<ControlFlow, Error<C, L, S>> { - self.sync_with(false) - } - - /// Pulls data from the ledger, synchronizing the wallet and balance state. - #[inline] - fn sync_with(&mut self, with_recovery: bool) -> Result<ControlFlow, Error<C, L, S>> { - let PullResponse { - should_continue, - checkpoint, - receivers, - senders, - } = self - .ledger - .pull(&self.checkpoint) - .map_err(Error::LedgerConnectionError)?; - if checkpoint < self.checkpoint { - return Err(Error::Inconsistency(InconsistencyError::LedgerCheckpoint)); - } - match self - .signer - .sync(SyncRequest { - with_recovery, - starting_index: self.checkpoint.receiver_index(), - inserts: receivers.into_iter().collect(), - removes: senders.into_iter().collect(), - }) - .map_err(Error::SignerConnectionError)? - { - Ok(SyncResponse::Partial { deposit, withdraw }) => { - self.assets.deposit_all(deposit); - if !self.assets.withdraw_all(withdraw) { - return Err(Error::Inconsistency(InconsistencyError::WalletBalance)); - } - } - Ok(SyncResponse::Full { assets }) => { - self.assets.clear(); - self.assets.deposit_all(assets); - } - Err(SyncError::InconsistentSynchronization { starting_index }) => { - // FIXME: What should be done when we receive an `InconsistentSynchronization` error - // from the signer? - // - One option is to do some sort of (exponential) backoff algorithm to - // find the point at which the signer and the wallet are able to - // synchronize again. The correct algorithm may be simply to exchange - // some checkpoints between the signer and the wallet until they can - // agree on a minimal one. - // - In the worst case we would have to recover the wallet (not necessarily - // the signer), which is what the docs currently recommend. - // - let _ = starting_index; - return Err(Error::Inconsistency( - InconsistencyError::SignerSynchronization, - )); - } - } - self.checkpoint = checkpoint; - Ok(ControlFlow::should_continue(should_continue)) - } - - /// Checks if `transaction` can be executed on the balance state of `self`, returning the - /// kind of update that should be performed on the balance state if the transaction is - /// successfully posted to the ledger. - /// - /// # Safety - /// - /// This method is already called by [`post`](Self::post), but can be used by custom - /// implementations to perform checks elsewhere. - #[inline] - pub fn check(&self, transaction: &Transaction<C>) -> Result<TransactionKind, Asset> { - transaction.check(move |a| self.contains(a)) - } - - /// Posts a transaction to the ledger, returning `true` if the `transaction` was successfully - /// saved onto the ledger. This method automatically synchronizes with the ledger before - /// posting, _but not after_. To amortize the cost of future calls to [`post`](Self::post), the - /// [`sync`](Self::sync) method can be used to synchronize with the ledger. - /// - /// # Failure Conditions - /// - /// This method returns `false` when there were no errors in producing transfer data and - /// sending and receiving from the ledger, but instead the ledger just did not accept the - /// transaction as is. This could be caused by an external update to the ledger while the - /// signer was building the transaction that caused the wallet and the ledger to get out of - /// sync. In this case, [`post`](Self::post) can safely be called again, to retry the - /// transaction. - /// - /// This method returns an error in any other case. The internal state of the wallet is kept - /// consistent between calls and recoverable errors are returned for the caller to handle. - #[inline] - pub fn post( - &mut self, - transaction: Transaction<C>, - metadata: Option<AssetMetadata>, - ) -> Result<bool, Error<C, L, S>> { - self.sync()?; - self.check(&transaction) - .map_err(Error::InsufficientBalance)?; - let SignResponse { posts } = self - .signer - .sign(SignRequest { - transaction, - metadata, - }) - .map_err(Error::SignerConnectionError)? - .map_err(Error::SignError)?; - let PushResponse { success } = self - .ledger - .push(posts) - .map_err(Error::LedgerConnectionError)?; - Ok(success) - } - - /// Returns public receiving keys according to the `request`. - #[inline] - pub fn receiving_keys( - &mut self, - request: ReceivingKeyRequest, - ) -> Result<Vec<ReceivingKey<C>>, S::Error> { - self.signer.receiving_keys(request) - } -} - -/// Inconsistency Error -/// -/// This `enum` is the error state for the [`sync`](Wallet::sync) method on [`Wallet`]. See its -/// documentation for more. The variants below describe their error conditions and how to solve the -/// issue whenever they arise. -#[cfg_attr( - feature = "serde", - derive(Deserialize, Serialize), - serde(crate = "manta_util::serde", deny_unknown_fields) -)] -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum InconsistencyError { - /// Ledger Checkpoint Inconsistency - /// - /// This error state arises when the ledger checkpoint is behind the checkpoint of the wallet. - /// To resolve this error, ensure that the ledger connection is correct and try again. This - /// error does not result in a bad wallet state. - LedgerCheckpoint, - - /// Wallet Balance Inconsistency - /// - /// ⚠️ This error causes the wallet system to enter an inconsistent state. ⚠️ - /// - /// This error state arises whenever the signer requests a withdraw from the wallet that would - /// overdraw the balance. To resolve this error, ensure that the signer connection is correct - /// and perform a wallet reset by resetting the checkpoint and balance state with a call to - /// [`reset`](Wallet::reset). If other errors continue or if there is reason to suspect that the - /// signer or ledger connections (or their true state) are corrupted, a full recovery is - /// required. See the [`recover`](Wallet::recover) method for more. - WalletBalance, - - /// Signer Synchronization Inconsistency - /// - /// ⚠️ This error causes the wallet system to enter an inconsistent state. ⚠️ - /// - /// This error state arises whenever the signer gets behind the wallet checkpoint. To resolve - /// this error, ensure that the signer connection is correct and perform a wallet reset by - /// resetting the checkpoint and balance state with a call to [`reset`](Wallet::reset). If other - /// errors continue or if there is reason to suspect that the signer or ledger connections (or - /// their true state) are corrupted, a full recovery is required. See the - /// [`recover`](Wallet::recover) method for more. - SignerSynchronization, -} - -/// Wallet Error -/// -/// This `enum` is the error state for [`Wallet`] methods. See [`sync`](Wallet::sync) and -/// [`post`](Wallet::post) for more. -#[cfg_attr( - feature = "serde", - derive(Deserialize, Serialize), - serde( - bound( - deserialize = r" - SignError<C>: Deserialize<'de>, - L::Error: Deserialize<'de>, - S::Error: Deserialize<'de> - ", - serialize = r" - SignError<C>: Serialize, - L::Error: Serialize, - S::Error: Serialize - " - ), - crate = "manta_util::serde", - deny_unknown_fields - ) -)] -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = "SignError<C>: Clone, L::Error: Clone, S::Error: Clone"), - Copy(bound = "SignError<C>: Copy, L::Error: Copy, S::Error: Copy"), - Debug(bound = "SignError<C>: Debug, L::Error: Debug, S::Error: Debug"), - Eq(bound = "SignError<C>: Eq, L::Error: Eq, S::Error: Eq"), - Hash(bound = "SignError<C>: Hash, L::Error: Hash, S::Error: Hash"), - PartialEq(bound = "SignError<C>: PartialEq, L::Error: PartialEq, S::Error: PartialEq") -)] -pub enum Error<C, L, S> -where - C: Configuration, - L: ledger::Connection<C>, - S: signer::Connection<C>, -{ - /// Insufficient Balance - InsufficientBalance(Asset), - - /// Inconsistency Error - /// - /// See the documentation of [`InconsistencyError`] for more. - Inconsistency(InconsistencyError), - - /// Signing Error - SignError(SignError<C>), - - /// Signer Connection Error - SignerConnectionError(S::Error), - - /// Ledger Connection Error - LedgerConnectionError(L::Error), -} diff --git a/manta-pay/src/bin/generate_parameters.rs b/manta-pay/src/bin/generate_parameters.rs index dde069fe4..4f629a69c 100644 --- a/manta-pay/src/bin/generate_parameters.rs +++ b/manta-pay/src/bin/generate_parameters.rs @@ -19,13 +19,12 @@ // TODO: Deduplicate the per-circuit proving context and verifying context serialization code. // TODO: Print some statistics about the parameters and circuits and into a stats file as well. -use manta_accounting::transfer; use manta_crypto::{ constraint::{measure::Measure, ProofSystem as _}, rand::{Rand, SeedableRng}, }; use manta_pay::config::{ - Config, FullParameters, Mint, Parameters, PrivateTransfer, ProofSystem, Reclaim, + FullParameters, Mint, Parameters, PrivateTransfer, ProofSystem, Reclaim, UtxoAccumulatorModel, }; use manta_util::codec::{Encode, IoWriter}; use rand_chacha::ChaCha20Rng; @@ -68,7 +67,7 @@ pub fn main() -> io::Result<()> { let mut rng = ChaCha20Rng::from_seed(SEED); let parameters = rng.gen(); - let utxo_set_parameters: <Config as transfer::Configuration>::UtxoSetModel = rng.gen(); + let utxo_accumulator_model: UtxoAccumulatorModel = rng.gen(); let Parameters { key_agreement, @@ -106,16 +105,16 @@ pub fn main() -> io::Result<()> { )) .unwrap(); - utxo_set_parameters + utxo_accumulator_model .encode(IoWriter( OpenOptions::new() .create(true) .write(true) - .open(parameters_dir.join("utxo-set-parameters.dat"))?, + .open(parameters_dir.join("utxo-accumulator-model.dat"))?, )) .unwrap(); - let full_parameters = FullParameters::new(&parameters, &utxo_set_parameters); + let full_parameters = FullParameters::new(&parameters, &utxo_accumulator_model); let proving_context_dir = target_dir.join("proving"); fs::create_dir_all(&proving_context_dir)?; diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 75cecef3e..8cad84112 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -463,11 +463,11 @@ impl merkle_tree::InnerHash<Compiler> for InnerHashVar { } } -/// UTXO Set Model -pub type UtxoSetModel = merkle_tree::Parameters<MerkleTreeConfiguration>; +/// UTXO Accumulator Model +pub type UtxoAccumulatorModel = merkle_tree::Parameters<MerkleTreeConfiguration>; -/// UTXO Set Output -pub type UtxoSetOutput = merkle_tree::Root<MerkleTreeConfiguration>; +/// UTXO Accumulator Output +pub type UtxoAccumulatorOutput = merkle_tree::Root<MerkleTreeConfiguration>; /// Merkle Tree Configuration pub struct MerkleTreeConfiguration; @@ -638,10 +638,12 @@ impl transfer::Configuration for Config { type VoidNumberVar = <Self::VoidNumberHashFunctionVar as BinaryHashFunction<Self::Compiler>>::Output; type VoidNumberHashFunctionVar = VoidNumberHashFunctionVar; - type UtxoSetModel = UtxoSetModel; - type UtxoSetWitnessVar = <Self::UtxoSetModelVar as accumulator::Model<Self::Compiler>>::Witness; - type UtxoSetOutputVar = <Self::UtxoSetModelVar as accumulator::Model<Self::Compiler>>::Output; - type UtxoSetModelVar = merkle_tree::Parameters<MerkleTreeConfigurationVar, Compiler>; + type UtxoAccumulatorModel = UtxoAccumulatorModel; + type UtxoAccumulatorWitnessVar = + <Self::UtxoAccumulatorModelVar as accumulator::Model<Self::Compiler>>::Witness; + type UtxoAccumulatorOutputVar = + <Self::UtxoAccumulatorModelVar as accumulator::Model<Self::Compiler>>::Output; + type UtxoAccumulatorModelVar = merkle_tree::Parameters<MerkleTreeConfigurationVar, Compiler>; type AssetIdVar = AssetIdVar; type AssetValueVar = AssetValueVar; type Compiler = Compiler; diff --git a/manta-pay/src/signer/base.rs b/manta-pay/src/signer/base.rs index 73368ce7d..cf164197c 100644 --- a/manta-pay/src/signer/base.rs +++ b/manta-pay/src/signer/base.rs @@ -49,8 +49,8 @@ impl KeyDerivationFunction for HierarchicalKeyDerivationFunction { } } -/// Signer UTXO Set -pub type UtxoSet = merkle_tree::forest::TreeArrayMerkleForest< +/// Signer UTXO Accumulator +pub type UtxoAccumulator = merkle_tree::forest::TreeArrayMerkleForest< MerkleTreeConfiguration, merkle_tree::fork::ForkedTree< MerkleTreeConfiguration, @@ -212,7 +212,7 @@ pub mod cache { impl wallet::signer::Configuration for Config { type HierarchicalKeyDerivationScheme = key::Map<TestnetKeySecret, HierarchicalKeyDerivationFunction>; - type UtxoSet = UtxoSet; + type UtxoAccumulator = UtxoAccumulator; type AssetMap = HashAssetMap<AssetMapKey<Self>>; type ProvingContextCache = MultiProvingContext; type Rng = rand_chacha::ChaCha20Rng; diff --git a/manta-pay/src/simulation/ledger/mod.rs b/manta-pay/src/simulation/ledger/mod.rs index 0fc5d5d3e..39ca495ce 100644 --- a/manta-pay/src/simulation/ledger/mod.rs +++ b/manta-pay/src/simulation/ledger/mod.rs @@ -19,7 +19,7 @@ use crate::{ config::{ Config, EncryptedNote, MerkleTreeConfiguration, MultiVerifyingContext, ProofSystem, - TransferPost, Utxo, UtxoSetModel, VoidNumber, + TransferPost, Utxo, UtxoAccumulatorModel, VoidNumber, }, signer::Checkpoint, }; @@ -32,7 +32,7 @@ use manta_accounting::{ canonical::TransferShape, AccountBalance, InvalidSinkAccount, InvalidSourceAccount, Proof, ReceiverLedger, ReceiverPostingKey, SenderLedger, SenderPostingKey, SinkPostingKey, SourcePostingKey, TransferLedger, TransferLedgerSuperPostingKey, TransferPostingKey, - UtxoSetOutput, + UtxoAccumulatorOutput, }, wallet::{ ledger::{self, PullResponse, PullResult, PushResponse, PushResult}, @@ -129,7 +129,7 @@ impl Ledger { /// Builds an empty [`Ledger`]. #[inline] pub fn new( - utxo_forest_parameters: UtxoSetModel, + utxo_accumulator_model: UtxoAccumulatorModel, verifying_context: MultiVerifyingContext, ) -> Self { Self { @@ -138,7 +138,7 @@ impl Ledger { shards: (0..MerkleTreeConfiguration::FOREST_WIDTH) .map(move |i| (MerkleForestIndex::from_index(i), Default::default())) .collect(), - utxo_forest: UtxoMerkleForest::new(utxo_forest_parameters), + utxo_forest: UtxoMerkleForest::new(utxo_accumulator_model), accounts: Default::default(), verifying_context, } @@ -153,7 +153,7 @@ impl Ledger { impl SenderLedger<Config> for Ledger { type ValidVoidNumber = Wrap<VoidNumber>; - type ValidUtxoSetOutput = Wrap<UtxoSetOutput<Config>>; + type ValidUtxoAccumulatorOutput = Wrap<UtxoAccumulatorOutput<Config>>; type SuperPostingKey = (Wrap<()>, ()); #[inline] @@ -166,10 +166,10 @@ impl SenderLedger<Config> for Ledger { } #[inline] - fn has_matching_utxo_set_output( + fn has_matching_utxo_accumulator_output( &self, - output: UtxoSetOutput<Config>, - ) -> Option<Self::ValidUtxoSetOutput> { + output: UtxoAccumulatorOutput<Config>, + ) -> Option<Self::ValidUtxoAccumulatorOutput> { for tree in self.utxo_forest.forest.as_ref() { if tree.root() == &output { return Some(Wrap(output)); @@ -181,11 +181,11 @@ impl SenderLedger<Config> for Ledger { #[inline] fn spend( &mut self, - utxo_set_output: Self::ValidUtxoSetOutput, + utxo_accumulator_output: Self::ValidUtxoAccumulatorOutput, void_number: Self::ValidVoidNumber, super_key: &Self::SuperPostingKey, ) { - let _ = (utxo_set_output, super_key); + let _ = (utxo_accumulator_output, super_key); self.void_numbers.insert(void_number.0); } } diff --git a/manta-pay/src/simulation/mod.rs b/manta-pay/src/simulation/mod.rs index 5f6b83005..fda5cf1b8 100644 --- a/manta-pay/src/simulation/mod.rs +++ b/manta-pay/src/simulation/mod.rs @@ -21,8 +21,8 @@ // TODO: Add in some concurrency (and measure how much we need it). use crate::{ - config::{Config, FullParameters, MultiProvingContext, UtxoSetModel}, - signer::base::{Signer, UtxoSet}, + config::{Config, FullParameters, MultiProvingContext, UtxoAccumulatorModel}, + signer::base::{Signer, UtxoAccumulator}, }; use alloc::{sync::Arc, vec::Vec}; use manta_accounting::{ @@ -51,7 +51,7 @@ pub fn sample_wallet<R>( ledger: &ledger::SharedLedger, cache: &MultiProvingContext, parameters: &transfer::Parameters<Config>, - utxo_set_model: &UtxoSetModel, + utxo_accumulator_model: &UtxoAccumulatorModel, rng: &mut R, ) -> Wallet<Config, ledger::LedgerConnection, Signer> where @@ -63,7 +63,7 @@ where AccountTable::new(rng.gen()), cache.clone(), parameters.clone(), - UtxoSet::new(utxo_set_model.clone()), + UtxoAccumulator::new(utxo_accumulator_model.clone()), rng.seed_rng().expect("Failed to sample PRNG for signer."), ), ) @@ -94,16 +94,16 @@ where pub fn simulate(actor_count: usize, actor_lifetime: usize) { let mut rng = ChaCha20Rng::from_entropy(); let parameters = rng.gen(); - let utxo_set_model = rng.gen(); + let utxo_accumulator_model = rng.gen(); let (proving_context, verifying_context) = transfer::canonical::generate_context( &(), - FullParameters::new(&parameters, &utxo_set_model), + FullParameters::new(&parameters, &utxo_accumulator_model), &mut rng, ) .expect("Failed to generate contexts."); - let mut ledger = ledger::Ledger::new(utxo_set_model.clone(), verifying_context); + let mut ledger = ledger::Ledger::new(utxo_accumulator_model.clone(), verifying_context); for i in 0..actor_count { ledger.set_public_balance(ledger::AccountId(i as u64), AssetId(0), AssetValue(1000000)); @@ -123,7 +123,7 @@ pub fn simulate(actor_count: usize, actor_lifetime: usize) { &ledger, &proving_context, &parameters, - &utxo_set_model, + &utxo_accumulator_model, &mut rng, ), Default::default(), diff --git a/manta-pay/src/test/transfer.rs b/manta-pay/src/test/transfer.rs index 7f15042ae..8d07d67b5 100644 --- a/manta-pay/src/test/transfer.rs +++ b/manta-pay/src/test/transfer.rs @@ -26,7 +26,7 @@ use manta_crypto::{ }; use rand_chacha::ChaCha20Rng; -type UtxoSet = merkle_tree::full::FullMerkleTree<MerkleTreeConfiguration>; +type UtxoAccumulator = merkle_tree::full::FullMerkleTree<MerkleTreeConfiguration>; /// Tests the generation of proving/verifying contexts for [`Mint`]. #[test] @@ -60,8 +60,13 @@ fn sample_reclaim_context() { fn mint() { let mut rng = ChaCha20Rng::from_entropy(); assert!( - Mint::sample_and_check_proof(&(), &rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng) - .expect("Random Mint should have successfully produced a proof."), + Mint::sample_and_check_proof( + &(), + &rng.gen(), + &mut UtxoAccumulator::new(rng.gen()), + &mut rng + ) + .expect("Random Mint should have successfully produced a proof."), "The Mint proof should have been valid." ); } @@ -74,7 +79,7 @@ fn private_transfer() { PrivateTransfer::sample_and_check_proof( &(), &rng.gen(), - &mut UtxoSet::new(rng.gen()), + &mut UtxoAccumulator::new(rng.gen()), &mut rng ) .expect("Random PrivateTransfer should have successfully produced a proof."), @@ -87,8 +92,13 @@ fn private_transfer() { fn reclaim() { let mut rng = ChaCha20Rng::from_entropy(); assert!( - Reclaim::sample_and_check_proof(&(), &rng.gen(), &mut UtxoSet::new(rng.gen()), &mut rng) - .expect("Random Reclaim should have successfully produced a proof."), + Reclaim::sample_and_check_proof( + &(), + &rng.gen(), + &mut UtxoAccumulator::new(rng.gen()), + &mut rng + ) + .expect("Random Reclaim should have successfully produced a proof."), "The Reclaim proof should have been valid." ); } From cbe41aa933cd73768d7777eb4c8ac81ffbb98987 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" <bhgomes@pm.me> Date: Tue, 1 Mar 2022 14:44:14 -0500 Subject: [PATCH 275/275] fix: rename ticker to symbol to match other conventions --- manta-accounting/src/asset.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 3351cd544..4857ea4ac 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -18,7 +18,7 @@ //! //! This module defines the data structures and canonical encodings of the notion of an "asset". //! Assets are defined by an [`AssetId`] field and an [`AssetValue`] field. For describing an -//! [`Asset`] with a particular [`AssetId`] we use [`AssetMetadata`] to assign a ticker and decimal +//! [`Asset`] with a particular [`AssetId`] we use [`AssetMetadata`] to assign a symbol and decimals //! for human-readable display purposes. use alloc::{ @@ -1068,8 +1068,8 @@ pub struct AssetMetadata { /// Number of Decimals pub decimals: u32, - /// Asset Ticker - pub ticker: String, + /// Asset Symbol + pub symbol: String, } impl AssetMetadata { @@ -1086,10 +1086,10 @@ impl AssetMetadata { } /// Returns a string formatting of `value` interpreted using `self` as the metadata including - /// the ticker. + /// the symbol. #[inline] pub fn display(&self, value: AssetValue) -> String { - format!("{} {}", self.display_value(value), self.ticker) + format!("{} {}", self.display_value(value), self.symbol) } }