From 8ba38d20d9a11051702d19b3b7436a4a4513fa21 Mon Sep 17 00:00:00 2001 From: Francisco Hernandez Iglesias Date: Tue, 15 Nov 2022 13:27:53 +0100 Subject: [PATCH 01/20] key system upgraded in manta-accounting Signed-off-by: Francisco Hernandez Iglesias --- manta-accounting/src/key.rs | 852 ++++-------------------- manta-accounting/src/wallet/mod.rs | 13 +- manta-accounting/src/wallet/signer.rs | 217 ++---- manta-accounting/src/wallet/test/mod.rs | 30 +- 4 files changed, 215 insertions(+), 897 deletions(-) diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index 858dbf3d8..3621d31e6 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -14,34 +14,22 @@ // You should have received a copy of the GNU General Public License // along with manta-rs. If not, see . -//! 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 +//! Key Accounting use alloc::vec::Vec; use core::{ - cmp, fmt::{self, Debug}, hash::Hash, - iter, marker::PhantomData, }; -use manta_crypto::{ - key::kdf::KeyDerivationFunction, - rand::{RngCore, Sample}, -}; -use manta_util::collections::btree_map::{self, BTreeMap}; #[cfg(feature = "serde")] use manta_util::serde::{Deserialize, Serialize}; -/// Hierarchical Key Derivation Parameter Type +/// Base Index Type pub type IndexType = u32; -/// Hierarchical Key Derivation Parameter +/// Index #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), @@ -49,7 +37,7 @@ pub type IndexType = u32; )] #[derive(derivative::Derivative)] #[derivative(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct HierarchicalKeyDerivationParameter { +pub struct Index { /// Index index: IndexType, @@ -57,8 +45,8 @@ pub struct HierarchicalKeyDerivationParameter { __: PhantomData, } -impl HierarchicalKeyDerivationParameter { - /// Builds a new [`HierarchicalKeyDerivationParameter`] from `index`. +impl Index { + /// Builds a new [`Index`] from `index`. #[inline] pub const fn new(index: IndexType) -> Self { Self { @@ -69,7 +57,7 @@ impl HierarchicalKeyDerivationParameter { /// Increments the index of `self` by one unit. #[inline] - fn increment(&mut self) { + pub fn increment(&mut self) { self.index += 1; } @@ -80,7 +68,7 @@ impl HierarchicalKeyDerivationParameter { } } -/// Implements the [`HierarchicalKeyDerivationParameter`] subtype for `$type` and `$index`. +/// Implements the [`Index`] subtype for `$type` and `$index`. macro_rules! impl_index_type { ($doc:expr, $fmt:expr, $type:ident, $index:ident) => { #[doc = $doc] @@ -89,7 +77,7 @@ macro_rules! impl_index_type { pub struct $type; #[doc = $doc] - pub type $index = HierarchicalKeyDerivationParameter<$type>; + pub type $index = Index<$type>; impl Debug for $index { #[inline] @@ -97,6 +85,20 @@ macro_rules! impl_index_type { f.debug_tuple($fmt).field(&self.index).finish() } } + + impl From for $index { + #[inline] + fn from(index: IndexType) -> Self { + Self::new(index) + } + } + + impl From<$index> for IndexType { + #[inline] + fn from(index: $index) -> Self { + index.index() + } + } }; } @@ -107,679 +109,180 @@ impl_index_type!( AccountIndex ); -impl_index_type!("Key Index", "KeyIndex", KeyIndexType, KeyIndex); - -/// Key Kind -#[cfg_attr( - feature = "serde", - derive(Deserialize, Serialize), - serde(crate = "manta_util::serde", deny_unknown_fields) -)] -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum Kind { - /// Spend Key - Spend, +/// Account Collection +pub trait AccountCollection { + /// Spending Key Type + type SpendingKey; - /// View Key - View, + /// Returns the spending key associated to this account. + fn spending_key(&self, index: &AccountIndex) -> Self::SpendingKey; } -/// Hierarchical Key Derivation Scheme -pub trait HierarchicalKeyDerivationScheme { - /// [`KeyIndex`] Gap Limit - const GAP_LIMIT: IndexType; - - /// Secret Key Type - type SecretKey; - - /// 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`. - #[inline] - 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 view `index`. - #[inline] - 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` and `index`. - #[inline] - fn derive_pair(&self, account: AccountIndex, index: KeyIndex) -> SecretKeyPair { - SecretKeyPair::new( - self.derive_spend(account, index), - self.derive_view(account, index), - ) - } - - /// Borrows `self` rather than consuming it, returning an implementation of - /// [`HierarchicalKeyDerivationScheme`]. - #[inline] - fn by_ref(&self) -> &Self { - self - } - - /// Maps `self` along a key derivation function. - #[inline] - fn map(self, key_derivation_function: F) -> Map - where - Self: Sized, - F: KeyDerivationFunction, - { - Map::new(self, key_derivation_function) - } -} - -impl HierarchicalKeyDerivationScheme for &H +impl AccountCollection for &A where - H: HierarchicalKeyDerivationScheme + ?Sized, + A: AccountCollection, { - const GAP_LIMIT: IndexType = H::GAP_LIMIT; - - type SecretKey = H::SecretKey; + type SpendingKey = A::SpendingKey; #[inline] - fn derive(&self, account: AccountIndex, kind: Kind, index: KeyIndex) -> Self::SecretKey { - (*self).derive(account, kind, index) - } - - #[inline] - fn derive_spend(&self, account: AccountIndex, index: KeyIndex) -> Self::SecretKey { - (*self).derive_spend(account, index) - } - - #[inline] - fn derive_view(&self, account: AccountIndex, index: KeyIndex) -> Self::SecretKey { - (*self).derive_view(account, index) + fn spending_key(&self, index: &AccountIndex) -> Self::SpendingKey { + (**self).spending_key(index) } } -/// Mapping Hierarchical Key Derivation Scheme -#[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 Map -where - H: HierarchicalKeyDerivationScheme, - F: KeyDerivationFunction, -{ - /// Base Derivation Scheme - base: H, - - /// Key Derivation Function - key_derivation_function: F, -} - -impl Map -where - H: HierarchicalKeyDerivationScheme, - F: KeyDerivationFunction, -{ - /// Builds a new [`Map`] from `base` and `key_derivation_function`. - #[inline] - pub fn new(base: H, key_derivation_function: F) -> Self { - Self { - base, - key_derivation_function, - } - } - - /// Returns the base [`HierarchicalKeyDerivationScheme`] for `self`. - #[inline] - pub fn base(&self) -> &H { - &self.base - } -} +/// Spending Key Type +pub type SpendingKey = ::SpendingKey; -impl HierarchicalKeyDerivationScheme for Map -where - H: HierarchicalKeyDerivationScheme, - F: KeyDerivationFunction, -{ - const GAP_LIMIT: IndexType = H::GAP_LIMIT; +/// Account Map +pub trait AccountMap { + /// Account Type + type Account; - type SecretKey = F::Output; + /// Builds a new [`AccountMap`] with a starting account. + fn new() -> Self; - #[inline] - fn derive(&self, account: AccountIndex, kind: Kind, index: KeyIndex) -> Self::SecretKey { - self.key_derivation_function - .derive(&self.base.derive(account, kind, index), &mut ()) - } + /// Returns the last account index stored in the map. + fn last_account(&self) -> AccountIndex; - #[inline] - fn derive_spend(&self, account: AccountIndex, index: KeyIndex) -> Self::SecretKey { - self.key_derivation_function - .derive(&self.base.derive_spend(account, index), &mut ()) - } + /// Adds a new account to the map, returning the new account index. + fn create_account(&mut self) -> AccountIndex; - #[inline] - fn derive_view(&self, account: AccountIndex, index: KeyIndex) -> Self::SecretKey { - self.key_derivation_function - .derive(&self.base.derive_view(account, index), &mut ()) - } -} + /// Returns a shared reference to the [`Account`](Self::Account) associated to `account`. + fn get(&self, account: AccountIndex) -> Option<&Self::Account>; -impl Sample<(D, F)> for Map -where - H: HierarchicalKeyDerivationScheme + Sample, - F: KeyDerivationFunction, -{ + /// Returns a shared referece to the default [`Account`](Self::Account) for this map. #[inline] - fn sample(distribution: (D, F), rng: &mut R) -> Self - where - R: RngCore + ?Sized, - { - Self::new(H::sample(distribution.0, rng), distribution.1) + fn get_default(&self) -> &Self::Account { + self.get(Default::default()) + .expect("The default account must always exist.") } -} -/// Hierarchical Key Derivation Secret Key Pair -pub struct SecretKeyPair -where - H: HierarchicalKeyDerivationScheme + ?Sized, -{ - /// Spend Part of the Key Pair - pub spend: H::SecretKey, - - /// View Part of the Key Pair - pub view: H::SecretKey, -} + /// Returns a mutable reference to the [`Account`](Self::Account) associated to `account`. + fn get_mut(&mut self, account: AccountIndex) -> Option<&mut Self::Account>; -impl SecretKeyPair -where - H: HierarchicalKeyDerivationScheme + ?Sized, -{ - /// Builds a new [`SecretKeyPair`] from `spend` and `view`. + /// Returns a mutable reference to the default [`Account`](Self::Account) for this map. #[inline] - pub fn new(spend: H::SecretKey, view: H::SecretKey) -> Self { - Self { spend, view } + fn get_mut_default(&mut self) -> &mut Self::Account { + self.get_mut(Default::default()) + .expect("The default account must always exist.") } } -/// Account Keys -#[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - Debug(bound = "H: Debug"), - Eq(bound = "H: Eq"), - Hash(bound = "H: Hash"), - PartialEq(bound = "H: PartialEq") -)] -pub struct AccountKeys<'h, H> -where - H: HierarchicalKeyDerivationScheme + ?Sized, -{ - /// Hierarchical Key Derivation Scheme - keys: &'h H, - - /// Account Index - index: AccountIndex, - - /// Account Information - account: Account, -} +/// [`Vec`] Account Map Type +pub type VecAccountMap = Vec; -impl<'h, H> AccountKeys<'h, H> -where - H: HierarchicalKeyDerivationScheme + ?Sized, -{ - /// Builds a new [`AccountKeys`] from `keys`, `index`, and `account`. - #[inline] - fn new(keys: &'h H, index: AccountIndex, account: Account) -> Self { - Self { - keys, - index, - account, - } - } +impl AccountMap for VecAccountMap { + type Account = AccountIndex; - /// Performs the bounds check on `index` and then runs `f`. #[inline] - fn with_bounds_check(&self, index: KeyIndex, f: F) -> Option - where - F: FnOnce(&Self, KeyIndex) -> T, - { - (index <= self.account.maximum_index).then(|| f(self, index)) + fn new() -> Self { + let mut this = Self::new(); + this.create_account(); + this } - /// 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) + fn last_account(&self) -> AccountIndex { + AccountIndex::new( + (self.len() - 1) + .try_into() + .expect("AccountIndex is not allowed to exceed IndexType::MAX."), + ) } - /// Returns the default spend key for this account. #[inline] - pub fn default_spend_key(&self) -> H::SecretKey { - self.derive_spend(Default::default()) + 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()); + index } - /// 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 { - self.with_bounds_check(index, Self::derive_spend) + fn get(&self, account: AccountIndex) -> Option<&Self::Account> { + self.as_slice().get(account.index() as usize) } - /// 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) + fn get_mut(&mut self, account: AccountIndex) -> Option<&mut Self::Account> { + self.as_mut_slice().get_mut(account.index() as usize) } +} - /// Returns the default view key for this account. - #[inline] - pub fn default_view_key(&self) -> H::SecretKey { - self.derive_view(Default::default()) - } +/// Derive Address Trait +pub trait DeriveAddress { + /// Address Generation Parameters + type Parameters; - /// 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 { - self.with_bounds_check(index, Self::derive_view) - } + /// Address Type + type Address; - /// Derives the secret key pair for this account at `index` without performing bounds checks. - #[inline] - fn derive_pair(&self, index: KeyIndex) -> SecretKeyPair { - self.keys.derive_pair(self.index, index) - } + /// Returns the spending key associated to this account. + fn address(&self, parameters: &Self::Parameters) -> Self::Address; +} - /// Returns the default secret key pair for this account. - #[inline] - pub fn default_keypair(&self) -> SecretKeyPair { - self.derive_pair(Default::default()) - } +/// Derive Addresses Trait +pub trait DeriveAddresses { + /// Address Generation Parameters + type Parameters; - /// 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> { - self.with_bounds_check(index, Self::derive_pair) - } + /// Address Type + type Address; - /// Returns an iterator over all the key pairs associated to `self`. - #[inline] - pub fn keypairs(&self) -> impl '_ + Iterator> { - let mut index = KeyIndex::default(); - iter::from_fn(move || { - let next = self.keypair(index); - index.increment(); - next - }) - } + /// Returns the spending key associated to this account. + fn address(&self, parameters: &Self::Parameters, index: AccountIndex) -> Self::Address; } -/// Account Keys with Mutable Access to the Account Table +/// Account Struct #[derive(derivative::Derivative)] #[derivative( + Clone(bound = "H: Clone"), + Copy(bound = "H: Copy"), Debug(bound = "H: Debug"), Eq(bound = "H: Eq"), Hash(bound = "H: Hash"), PartialEq(bound = "H: PartialEq") )] -pub struct AccountKeysMut<'h, H> +pub struct Account where - H: HierarchicalKeyDerivationScheme + ?Sized, + H: AccountCollection, { - /// Hierarchical Key Derivation Scheme - keys: &'h H, - - /// Account Index + key: H, index: AccountIndex, - - /// Account Information - account: &'h mut Account, } -impl<'h, H> AccountKeysMut<'h, H> +impl Account where - H: HierarchicalKeyDerivationScheme + ?Sized, + H: AccountCollection, { - /// Builds a new [`AccountKeysMut`] from `keys`, `index`, and `account`. - #[inline] - fn new(keys: &'h H, index: AccountIndex, account: &'h mut Account) -> Self { - Self { - keys, - index, - account, - } - } - - /// Performs the bounds check on `index` and then runs `f`. - #[inline] - fn with_bounds_check(&self, index: KeyIndex, f: F) -> Option - 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 { - 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 { - 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 { - self.keys.derive_pair(self.index, index) - } - - /// Returns the default secret key pair for this account. - #[inline] - pub fn default_keypair(&self) -> SecretKeyPair { - 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> { - 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> { - let mut index = KeyIndex::default(); - iter::from_fn(move || { - let next = self.keypair(index); - index.increment(); - next - }) - } - - /// Returns a new [`ViewKeyTable`] for `self`. - #[inline] - pub fn view_key_table(self) -> ViewKeyTable<'h, H> { - ViewKeyTable::new(self) - } -} - -/// View Key Table -pub struct ViewKeyTable<'h, H> -where - H: HierarchicalKeyDerivationScheme + ?Sized, -{ - /// Account Keys - keys: AccountKeysMut<'h, H>, - - /// Pre-computed View Keys - view_keys: BTreeMap, -} - -impl<'h, H> ViewKeyTable<'h, H> -where - H: HierarchicalKeyDerivationScheme + ?Sized, -{ - /// View Key Buffer Maximum Size Limit - pub const VIEW_KEY_BUFFER_LIMIT: usize = 16 * (H::GAP_LIMIT as usize); - - /// Builds a new [`ViewKeyTable`] over the account `keys`. - #[inline] - pub fn new(keys: AccountKeysMut<'h, H>) -> Self { - Self { - keys, - view_keys: Default::default(), - } - } - - /// Returns the account keys associated to `self`. - #[inline] - pub fn into_keys(self) -> AccountKeysMut<'h, H> { - self.keys - } - - /// Returns the view key for this account at `index`, if it does not exceed the maximum index. - /// - /// # Limits - /// - /// This function uses a view key buffer that stores the computed keys to reduce the number of - /// times a re-compute of the view keys is needed while searching. The buffer only grows past - /// the current key bounds with a call to [`find_index_with_gap`](Self::find_index_with_gap) - /// which extends the buffer by at most [`GAP_LIMIT`]-many keys per round. To prevent allocating - /// too much memory, the internal buffer is capped at [`VIEW_KEY_BUFFER_LIMIT`]-many elements. /// - /// [`GAP_LIMIT`]: HierarchicalKeyDerivationScheme::GAP_LIMIT - /// [`VIEW_KEY_BUFFER_LIMIT`]: Self::VIEW_KEY_BUFFER_LIMIT - #[inline] - pub fn view_key(&mut self, index: KeyIndex) -> Option<&H::SecretKey> { - btree_map::get_or_mutate(&mut self.view_keys, &index, |map| { - let next_key = self.keys.view_key(index)?; - if map.len() == Self::VIEW_KEY_BUFFER_LIMIT { - btree_map::pop_last(map); - } - Some(btree_map::insert_then_get(map, index, next_key)) - }) + pub fn new(key: H, index: AccountIndex) -> Self { + Self { key, index } } - - /// 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 of `f` returned - /// `None`. - #[inline] - pub fn find_index(&mut self, mut f: F) -> Option> - where - F: FnMut(&H::SecretKey) -> Option, - { - let mut index = KeyIndex::default(); - loop { - if let Some(item) = f(self.view_key(index)?) { - self.keys.account.last_used_index = - cmp::max(self.keys.account.last_used_index, index); - return Some(ViewKeySelection { - index, - keypair: self.keys.derive_pair(index), - item, - }); - } - 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_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(&mut self, f: F) -> Option> - where - F: FnMut(&H::SecretKey) -> Option, - { - let previous_maximum = self.keys.account.maximum_index; - self.keys.account.maximum_index.index += H::GAP_LIMIT; - match self.find_index(f) { - Some(result) => { - self.keys.account.maximum_index = cmp::max(previous_maximum, result.index); - Some(result) - } - _ => { - self.keys.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( - &mut self, - use_gap_limit: bool, - f: F, - ) -> Option> - where - F: FnMut(&H::SecretKey) -> Option, - { - if use_gap_limit { - self.find_index_with_gap(f) - } else { - self.find_index(f) - } + pub fn spending_key(&self) -> SpendingKey { + self.key.spending_key(&self.index) } } -/// View Key Selection -pub struct ViewKeySelection +impl DeriveAddress for Account where - H: HierarchicalKeyDerivationScheme + ?Sized, + H: AccountCollection + DeriveAddresses, { - /// Selection Index - pub index: KeyIndex, - - /// Selection Key Pair - pub keypair: SecretKeyPair, - - /// Selection Item - pub item: T, -} + type Address = H::Address; + type Parameters = H::Parameters; -impl ViewKeySelection -where - H: HierarchicalKeyDerivationScheme + ?Sized, -{ - /// Computes `f` on `self.item` returning a new [`ViewKeySelection`] with the same `index` and - /// `keypair`. - #[inline] - pub fn map(self, f: F) -> ViewKeySelection - where - F: FnOnce(T) -> U, - { - ViewKeySelection { - index: self.index, - keypair: self.keypair, - item: f(self.item), - } - } -} - -/// 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 - /// - /// 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. - fn new() -> Self; - - /// Returns the last account index stored in the map. - fn last_account(&self) -> AccountIndex; - - /// Adds a new account to the map, returning the new account index. - fn create_account(&mut self) -> AccountIndex; - - /// Returns the [`Account`] associated to `account`. - fn get(&self, account: AccountIndex) -> Option; - - /// Returns the [`Account`] associated to `account`. - fn get_mut(&mut self, account: AccountIndex) -> Option<&mut Account>; -} - -/// [`Vec`] Account Map Type -pub type VecAccountMap = Vec; - -impl AccountMap for VecAccountMap { - #[inline] - fn new() -> Self { - let mut this = Self::new(); - this.create_account(); - 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 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()); - index - } - - #[inline] - fn get(&self, account: AccountIndex) -> Option { - 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) + fn address(&self, parameters: &Self::Parameters) -> Self::Address { + self.key.address(parameters, self.index) } } /// Account Table /// -/// The account table stores an underlying [`HierarchicalKeyDerivationScheme`] for key generation +/// The account table stores an underlying [`AccountCollection`] 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( @@ -798,10 +301,10 @@ impl AccountMap for VecAccountMap { )] pub struct AccountTable where - H: HierarchicalKeyDerivationScheme, - M: AccountMap, + H: AccountCollection, + M: AccountMap, { - /// Hierarchical Key Derivation Scheme + /// AccountCollection keys: H, /// Account Map @@ -810,8 +313,8 @@ where impl AccountTable where - H: HierarchicalKeyDerivationScheme, - M: AccountMap, + H: AccountCollection, + M: AccountMap, { /// Builds a new [`AccountTable`] using `keys` and the default account map. #[inline] @@ -825,115 +328,36 @@ where Self { keys, accounts } } - /// Returns the underlying [`HierarchicalKeyDerivationScheme`] of `self`. - #[inline] - pub fn keys(&self) -> &H { - &self.keys - } - - /// 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: KeyIndex, - ) -> Option>> { - self.get(account).map(move |k| k.keypair(index)) - } - - /// Returns the account keys for `account` if it exists. - #[inline] - pub fn get(&self, account: AccountIndex) -> Option> { - Some(AccountKeys::new( - &self.keys, - account, - self.accounts.get(account)?, - )) - } - /// Returns the account keys for `account` if it exists. #[inline] - pub fn get_mut(&mut self, account: AccountIndex) -> Option> { - Some(AccountKeysMut::new( - &self.keys, - account, - self.accounts.get_mut(account)?, - )) + pub fn get(&self, account: AccountIndex) -> Option> + where + H: Clone, + { + let index = self.accounts.get(account).copied()?; + Some(Account::new(self.keys.clone(), index)) } /// Returns the account keys for the default account. #[inline] - pub fn get_default(&self) -> AccountKeys { + pub fn get_default(&self) -> Account + where + H: Clone, + { self.get(Default::default()).unwrap() } - /// Returns the account keys for the default account. - #[inline] - pub fn get_mut_default(&mut self) -> AccountKeysMut { - self.get_mut(Default::default()).unwrap() - } - - /// Returns the maximum key index for `account`, if it exists. - #[inline] - pub fn maximum_index(&self, account: AccountIndex) -> Option { - self.accounts - .get(account) - .map(|account| account.maximum_index) - } - /// Adds a new account to the map, returning the new account parameter. #[inline] pub fn create_account(&mut self) -> AccountIndex { self.accounts.create_account() } - - /// Increments the maximum key index for `account`, if it exists, returning the current - /// 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 { - 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 { - let max_index = self.increment_maximum_index(account)?; - Some(max_index) - } - - /// Increments the spend index and returns the [`SecretKeyPair`] for the new index. - #[inline] - pub fn next(&mut self, account: AccountIndex) -> Option> { - 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> { - iter::from_fn(move || self.next(account)) - } } impl Default for AccountTable where - H: Default + HierarchicalKeyDerivationScheme, - M: AccountMap, + H: Default + AccountCollection, + M: AccountMap, { #[inline] fn default() -> Self { diff --git a/manta-accounting/src/wallet/mod.rs b/manta-accounting/src/wallet/mod.rs index 8bc7cba8e..9ce4a6d6d 100644 --- a/manta-accounting/src/wallet/mod.rs +++ b/manta-accounting/src/wallet/mod.rs @@ -31,14 +31,14 @@ use crate::{ asset::{Asset, AssetId, AssetList, AssetMetadata, AssetValue}, transfer::{ canonical::{Transaction, TransactionKind}, - Configuration, ReceivingKey, TransferPost, + Configuration, PublicKey, TransferPost, }, wallet::{ balance::{BTreeMapBalanceState, BalanceState}, ledger::ReadResponse, signer::{ - BalanceUpdate, ReceivingKeyRequest, SignError, SignRequest, SignResponse, SyncData, - SyncError, SyncRequest, SyncResponse, + BalanceUpdate, SignError, SignRequest, SignResponse, SyncData, SyncError, SyncRequest, + SyncResponse, }, }, }; @@ -381,11 +381,8 @@ where /// Returns public receiving keys according to the `request`. #[inline] - pub async fn receiving_keys( - &mut self, - request: ReceivingKeyRequest, - ) -> Result>, S::Error> { - self.signer.receiving_keys(request).await + pub async fn receiving_keys(&mut self) -> Result, S::Error> { + self.signer.receiving_keys().await } } diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index c96446aa3..1940d9488 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -28,10 +28,7 @@ use crate::{ asset::{Asset, AssetId, AssetMap, AssetMetadata, AssetValue}, - key::{ - self, HierarchicalKeyDerivationScheme, KeyIndex, SecretKeyPair, ViewKeySelection, - ViewKeyTable, - }, + key::{self, AccountCollection, DeriveAddress, DeriveAddresses}, transfer::{ self, batch::Join, @@ -40,13 +37,13 @@ use crate::{ Shape, Transaction, }, EncryptedNote, FullParameters, Note, Parameters, PreSender, ProofSystemError, - ProvingContext, Receiver, ReceivingKey, SecretKey, Sender, SpendingKey, Transfer, - TransferPost, Utxo, VoidNumber, + ProvingContext, PublicKey, Receiver, ReceivingKey, SecretKey, Sender, SpendingKey, + Transfer, TransferPost, Utxo, VoidNumber, }, wallet::ledger::{self, Data}, }; use alloc::{boxed::Box, vec, vec::Vec}; -use core::{convert::Infallible, fmt::Debug, hash::Hash}; +use core::{convert::Infallible, default, fmt::Debug, hash::Hash}; use manta_crypto::{ accumulator::{Accumulator, ExactSizeAccumulator, OptimizedAccumulator}, rand::{CryptoRng, FromEntropy, Rand, RngCore}, @@ -92,10 +89,7 @@ where ) -> LocalBoxFutureResult, SignError>, Self::Error>; /// Returns public receiving keys according to the `request`. - fn receiving_keys( - &mut self, - request: ReceivingKeyRequest, - ) -> LocalBoxFutureResult>, Self::Error>; + fn receiving_keys(&mut self) -> LocalBoxFutureResult, Self::Error>; } /// Signer Synchronization Data @@ -412,40 +406,6 @@ where /// Signing Result pub type SignResult = Result, SignError>; -/// 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 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, - }, -} - /// Signer Checkpoint pub trait Checkpoint: ledger::Checkpoint where @@ -478,10 +438,10 @@ pub trait Configuration: transfer::Configuration { /// Checkpoint Type type Checkpoint: Checkpoint; - /// Hierarchical Key Derivation Scheme - type HierarchicalKeyDerivationScheme: HierarchicalKeyDerivationScheme< - SecretKey = SecretKey, - >; + /// Account Type + type Account: AccountCollection> + + Clone + + DeriveAddresses, Address = PublicKey>; /// [`Utxo`] Accumulator Type type UtxoAccumulator: Accumulator @@ -490,17 +450,14 @@ pub trait Configuration: transfer::Configuration { + Rollback; /// Asset Map Type - type AssetMap: AssetMap>; + type AssetMap: AssetMap>; /// Random Number Generator Type type Rng: CryptoRng + FromEntropy + RngCore; } /// Account Table Type -pub type AccountTable = key::AccountTable<::HierarchicalKeyDerivationScheme>; - -/// Asset Map Key Type -pub type AssetMapKey = (KeyIndex, SecretKey); +pub type AccountTable = key::AccountTable<::Account>; /// Signer Parameters #[derive(derivative::Derivative)] @@ -540,9 +497,10 @@ where #[inline] fn receiving_key( &self, - keypair: SecretKeyPair, + key: SecretKey, ) -> ReceivingKey { - SpendingKey::new(keypair.spend, keypair.view).derive(self.parameters.key_agreement_scheme()) + let spending_key = SpendingKey::new(key.clone(), key); + spending_key.derive(self.parameters.key_agreement_scheme()) } } @@ -623,10 +581,7 @@ where /// Builds a new [`SignerState`] from `keys` and `utxo_accumulator`. #[inline] - pub fn new( - keys: C::HierarchicalKeyDerivationScheme, - utxo_accumulator: C::UtxoAccumulator, - ) -> Self { + pub fn new(keys: C::Account, utxo_accumulator: C::UtxoAccumulator) -> Self { Self::build( AccountTable::::new(keys), utxo_accumulator, @@ -641,20 +596,6 @@ where &self.accounts } - /// Finds the next viewing key that can decrypt the `encrypted_note` from the `view_key_table`. - #[inline] - fn find_next_key<'h>( - view_key_table: &mut ViewKeyTable<'h, C::HierarchicalKeyDerivationScheme>, - parameters: &Parameters, - with_recovery: bool, - encrypted_note: EncryptedNote, - ) -> Option>> { - let mut finder = Finder::new(encrypted_note); - view_key_table.find_index_with_maybe_gap(with_recovery, move |k| { - finder.next(|note| note.decrypt(¶meters.note_encryption_scheme, k, &mut ())) - }) - } - /// Inserts the new `utxo`-`note` pair into the `utxo_accumulator` adding the spendable amount /// to `assets` if there is no void number to match it. #[inline] @@ -663,28 +604,21 @@ where assets: &mut C::AssetMap, parameters: &Parameters, utxo: Utxo, - selection: ViewKeySelection>, + note: Note, + key: &SecretKey, void_numbers: &mut Vec>, deposit: &mut Vec, ) { - let ViewKeySelection { - index, - keypair, - item: Note { - ephemeral_secret_key, - asset, - }, - } = selection; if let Some(void_number) = - parameters.check_full_asset(&keypair.spend, &ephemeral_secret_key, &asset, &utxo) + parameters.check_full_asset(key, ¬e.ephemeral_secret_key, ¬e.asset, &utxo) { if let Some(index) = void_numbers.iter().position(move |v| v == &void_number) { void_numbers.remove(index); } else { utxo_accumulator.insert(&utxo); - assets.insert((index, ephemeral_secret_key), asset); - if !asset.is_zero() { - deposit.push(asset); + assets.insert(note.ephemeral_secret_key, note.asset); + if !note.asset.is_zero() { + deposit.push(note.asset); } return; } @@ -738,20 +672,18 @@ where let void_number_count = void_numbers.len(); let mut deposit = Vec::new(); let mut withdraw = Vec::new(); - let mut view_key_table = self.accounts.get_mut_default().view_key_table(); + let mut default_key = self.accounts.get_default().spending_key(); for (utxo, encrypted_note) in inserts { - if let Some(selection) = Self::find_next_key( - &mut view_key_table, - parameters, - with_recovery, - encrypted_note, - ) { + if let Some(note) = + encrypted_note.decrypt(¶meters.note_encryption_scheme, &default_key, &mut ()) + { Self::insert_next_item( &mut self.utxo_accumulator, &mut self.assets, parameters, utxo, - selection, + note, + &default_key, &mut void_numbers, &mut deposit, ); @@ -759,21 +691,18 @@ where self.utxo_accumulator.insert_nonprovable(&utxo); } } - self.assets.retain(|(index, ephemeral_secret_key), assets| { - assets.retain( - |asset| match self.accounts.get_default().spend_key(*index) { - Some(secret_spend_key) => Self::is_asset_unspent( - &mut self.utxo_accumulator, - parameters, - &secret_spend_key, - ephemeral_secret_key, - *asset, - &mut void_numbers, - &mut withdraw, - ), - _ => true, - }, - ); + self.assets.retain(|ephemeral_secret_key, assets| { + assets.retain(|asset| { + Self::is_asset_unspent( + &mut self.utxo_accumulator, + parameters, + &self.accounts.get_default().spending_key(), + ephemeral_secret_key, + *asset, + &mut void_numbers, + &mut withdraw, + ) + }); !assets.is_empty() }); self.checkpoint.update_from_void_numbers(void_number_count); @@ -798,17 +727,13 @@ where fn build_pre_sender( &self, parameters: &Parameters, - key: AssetMapKey, + key: SecretKey, asset: Asset, ) -> Result, SignError> { - let (spend_index, ephemeral_secret_key) = key; Ok(PreSender::new( parameters, - self.accounts - .get_default() - .spend_key(spend_index) - .expect("Index is guaranteed to be within bounds."), - ephemeral_secret_key, + self.accounts.get_default().spending_key(), + key, asset, )) } @@ -820,12 +745,11 @@ where parameters: &Parameters, asset: Asset, ) -> Result, SignError> { - let keypair = self.accounts.get_default().default_keypair(); - Ok(SpendingKey::new(keypair.spend, keypair.view).receiver( - parameters, - self.rng.gen(), - asset, - )) + let receiving_key = ReceivingKey:: { + spend: self.accounts.get_default().address(parameters).clone(), + view: self.accounts.get_default().address(parameters), + }; + Ok(receiving_key.into_receiver(parameters, self.rng.gen(), asset)) } /// Builds a new internal [`Mint`] for zero assets. @@ -836,10 +760,10 @@ where asset_id: AssetId, ) -> Result<(Mint, PreSender), SignError> { let asset = Asset::zero(asset_id); - let keypair = self.accounts.get_default().default_keypair(); + let key = self.accounts.get_default().spending_key(); Ok(Mint::internal_pair( parameters, - &SpendingKey::new(keypair.spend, keypair.view), + &SpendingKey::new(key.clone(), key), asset, &mut self.rng, )) @@ -937,11 +861,11 @@ where asset_id: AssetId, total: AssetValue, ) -> Result<([Receiver; PrivateTransferShape::RECEIVERS], Join), SignError> { - let keypair = self.accounts.get_default().default_keypair(); + let secret_key = self.accounts.get_default().spending_key(); Ok(Join::new( parameters, asset_id.with(total), - &SpendingKey::new(keypair.spend, keypair.view), + &SpendingKey::new(secret_key.clone(), secret_key), &mut self.rng, )) } @@ -1059,7 +983,7 @@ where impl Clone for SignerState where C: Configuration, - C::HierarchicalKeyDerivationScheme: Clone, + C::Account: Clone, C::UtxoAccumulator: Clone, C::AssetMap: Clone, { @@ -1256,31 +1180,11 @@ where /// Returns public receiving keys according to the `request`. #[inline] - pub fn receiving_keys(&mut self, request: ReceivingKeyRequest) -> Vec> { - 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(), - } + pub fn receiving_keys(&mut self) -> PublicKey { + self.state + .accounts + .get_default() + .address(&self.parameters.parameters) } } @@ -1311,10 +1215,7 @@ where } #[inline] - fn receiving_keys( - &mut self, - request: ReceivingKeyRequest, - ) -> LocalBoxFutureResult>, Self::Error> { - Box::pin(async move { Ok(self.receiving_keys(request)) }) + fn receiving_keys(&mut self) -> LocalBoxFutureResult, Self::Error> { + Box::pin(async move { Ok(self.receiving_keys()) }) } } diff --git a/manta-accounting/src/wallet/test/mod.rs b/manta-accounting/src/wallet/test/mod.rs index 9a0091459..a27a86356 100644 --- a/manta-accounting/src/wallet/test/mod.rs +++ b/manta-accounting/src/wallet/test/mod.rs @@ -24,7 +24,7 @@ use crate::{ transfer::{self, canonical::Transaction, PublicKey, ReceivingKey, TransferPost}, wallet::{ ledger, - signer::{self, ReceivingKeyRequest, SyncData}, + signer::{self, SyncData}, BalanceState, Error, Wallet, }, }; @@ -108,8 +108,9 @@ where /// Generates a [`Transaction::PrivateTransfer`] for `asset` to `key` self-pointed if `is_self` /// is `true`. #[inline] - pub fn private_transfer(is_self: bool, asset: Asset, key: ReceivingKey) -> Self { - Self::post(is_self, false, Transaction::PrivateTransfer(asset, key)) + pub fn private_transfer(is_self: bool, asset: Asset, key: PublicKey) -> Self { + let receiving_key = ReceivingKey::{spend: key.clone(), view: key}; + Self::post(is_self, false, Transaction::PrivateTransfer(asset, receiving_key)) } /// Generates a [`Transaction::Reclaim`] for `asset` which is maximal if `is_maximal` is `true`. @@ -427,14 +428,11 @@ where /// Returns the default receiving key for `self`. #[inline] - async fn default_receiving_key(&mut self) -> Result, Error> { + async fn default_receiving_key(&mut self) -> Result, Error> { self.wallet - .receiving_keys(ReceivingKeyRequest::Get { - index: Default::default(), - }) + .receiving_keys() .await .map_err(Error::SignerConnectionError) - .map(Vec::take_first) } /// Returns the latest public balances from the ledger. @@ -561,7 +559,7 @@ where where L: PublicBalanceOracle, R: RngCore + ?Sized, - K: FnOnce(&mut R) -> Result>, Error>, + K: FnOnce(&mut R) -> Result>, Error>, { let action = if is_self { ActionType::SelfTransfer @@ -596,7 +594,7 @@ where where L: PublicBalanceOracle, R: RngCore + ?Sized, - K: FnOnce(&mut R) -> Result>, Error>, + K: FnOnce(&mut R) -> Result>, Error>, { let action = if is_self { ActionType::SelfTransfer @@ -684,7 +682,7 @@ pub type Event = ActionLabelled>>>::Response, Error>>; /// Receiving Key Database -pub type ReceivingKeyDatabase = IndexSet>; +pub type ReceivingKeyDatabase = IndexSet>; /// Shared Receiving Key Database pub type SharedReceivingKeyDatabase = Arc>>; @@ -715,7 +713,7 @@ where { /// Builds a new [`Simulation`] with a starting set of public `keys`. #[inline] - pub fn new(keys: [ReceivingKey; N]) -> Self { + pub fn new(keys: [PublicKey; N]) -> Self { Self { receiving_keys: Arc::new(Mutex::new(keys.into_iter().collect())), __: PhantomData, @@ -724,7 +722,7 @@ where /// Samples a random receiving key from #[inline] - pub fn sample_receiving_key(&self, rng: &mut R) -> Option> + pub fn sample_receiving_key(&self, rng: &mut R) -> Option> where R: RngCore + ?Sized, { @@ -841,13 +839,11 @@ where action: ActionType::GenerateReceivingKeys, value: match actor .wallet - .receiving_keys(ReceivingKeyRequest::New { count }) + .receiving_keys() .await { - Ok(keys) => { - for key in keys { + Ok(key) => { self.receiving_keys.lock().insert(key); - } Ok(true) } Err(err) => Err(Error::SignerConnectionError(err)), From 82fa7a876cafb868ebe984945b205f339d83182e Mon Sep 17 00:00:00 2001 From: Francisco Hernandez Iglesias Date: Tue, 15 Nov 2022 15:00:09 +0100 Subject: [PATCH 02/20] it compiles Signed-off-by: Francisco Hernandez Iglesias --- manta-accounting/src/wallet/signer.rs | 5 +- manta-accounting/src/wallet/test/mod.rs | 19 ++-- manta-pay/src/key.rs | 132 +++++++++++------------ manta-pay/src/signer/base.rs | 72 ++++++------- manta-pay/src/signer/client/websocket.rs | 13 ++- manta-pay/src/signer/mod.rs | 15 ++- 6 files changed, 123 insertions(+), 133 deletions(-) diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 1940d9488..04db9c130 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -495,10 +495,7 @@ where /// Converts `keypair` into a [`ReceivingKey`] by using the key-agreement scheme to derive the /// public keys associated to `keypair`. #[inline] - fn receiving_key( - &self, - key: SecretKey, - ) -> ReceivingKey { + fn receiving_key(&self, key: SecretKey) -> ReceivingKey { let spending_key = SpendingKey::new(key.clone(), key); spending_key.derive(self.parameters.key_agreement_scheme()) } diff --git a/manta-accounting/src/wallet/test/mod.rs b/manta-accounting/src/wallet/test/mod.rs index a27a86356..95932e609 100644 --- a/manta-accounting/src/wallet/test/mod.rs +++ b/manta-accounting/src/wallet/test/mod.rs @@ -109,8 +109,15 @@ where /// is `true`. #[inline] pub fn private_transfer(is_self: bool, asset: Asset, key: PublicKey) -> Self { - let receiving_key = ReceivingKey::{spend: key.clone(), view: key}; - Self::post(is_self, false, Transaction::PrivateTransfer(asset, receiving_key)) + let receiving_key = ReceivingKey:: { + spend: key.clone(), + view: key, + }; + Self::post( + is_self, + false, + Transaction::PrivateTransfer(asset, receiving_key), + ) } /// Generates a [`Transaction::Reclaim`] for `asset` which is maximal if `is_maximal` is `true`. @@ -837,13 +844,9 @@ where } Action::GenerateReceivingKeys { count } => Event { action: ActionType::GenerateReceivingKeys, - value: match actor - .wallet - .receiving_keys() - .await - { + value: match actor.wallet.receiving_keys().await { Ok(key) => { - self.receiving_keys.lock().insert(key); + self.receiving_keys.lock().insert(key); Ok(true) } Err(err) => Err(Error::SignerConnectionError(err)), diff --git a/manta-pay/src/key.rs b/manta-pay/src/key.rs index 2f587e7ec..54d1e3027 100644 --- a/manta-pay/src/key.rs +++ b/manta-pay/src/key.rs @@ -26,17 +26,14 @@ use alloc::{format, string::String}; use core::marker::PhantomData; -use manta_accounting::key::{ - self, AccountIndex, HierarchicalKeyDerivationScheme, IndexType, KeyIndex, Kind, -}; -use manta_crypto::rand::{CryptoRng, RngCore}; +use manta_accounting::key::{self, AccountIndex}; +use manta_crypto::rand::{CryptoRng, RngCore, Sample}; use manta_util::{create_seal, seal, Array}; #[cfg(feature = "serde")] use manta_util::serde::{Deserialize, Serialize, Serializer}; -pub use bip0039; -pub use bip32::{self, Error, XPrv as SecretKey}; +pub use bip32::{Error, Seed, XPrv as SecretKey}; create_seal! {} @@ -67,7 +64,7 @@ macro_rules! impl_coin_type { $id:expr, $coin_type_id:ident, $key_secret:ident, - $account_table:ident + $account_map:ident ) => { #[doc = $doc] #[doc = "Network"] @@ -84,8 +81,8 @@ macro_rules! impl_coin_type { pub type $key_secret = KeySecret<$coin>; #[doc = stringify!($coin)] - #[doc = "[`AccountTable`] Type"] - pub type $account_table = AccountTable<$coin>; + #[doc = "[`VecAccountMap`] Type"] + pub type $account_map = VecAccountMap<$coin>; seal!($coin); @@ -102,7 +99,7 @@ impl_coin_type!( 1, TESTNET_COIN_TYPE_ID, TestnetKeySecret, - TestnetAccountTable + TestnetAccountMap ); impl_coin_type!( @@ -112,7 +109,7 @@ impl_coin_type!( 611, MANTA_COIN_TYPE_ID, MantaKeySecret, - MantaAccountTable + MantaAccountMap ); impl_coin_type!( @@ -122,23 +119,20 @@ impl_coin_type!( 612, CALAMARI_COIN_TYPE_ID, CalamariKeySecret, - CalamariAccountTable + CalamariAccountMap ); -/// Account Table Type -pub type AccountTable = key::AccountTable>; - -/// Seed Bytes -pub type SeedBytes = Array; +/// Seed Byte Array Type +type SeedBytes = Array; /// Key Secret #[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(bound = ""))] +#[derivative(Clone(bound = ""), Default(bound = ""))] pub struct KeySecret where C: CoinType, @@ -146,9 +140,6 @@ where /// Key Seed seed: SeedBytes, - /// Mnemonic - mnemonic: Mnemonic, - /// Type Parameter Marker __: PhantomData, } @@ -157,36 +148,59 @@ impl KeySecret where C: CoinType, { - /// Builds a [`KeySecret`] from `seed` and `mnemonic`. + /// Builds a [`KeySecret`] from raw bytes. #[inline] - fn new_unchecked(seed: [u8; bip32::Seed::SIZE], mnemonic: Mnemonic) -> Self { + fn build(seed: [u8; Seed::SIZE]) -> Self { Self { seed: seed.into(), - mnemonic, __: 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] pub fn new(mnemonic: Mnemonic, password: &str) -> Self { - Self::new_unchecked(mnemonic.to_seed(password), mnemonic) + Self::from_seed(mnemonic.to_seed(password)) } - /// Exposes a shared reference to the [`Mnemonic`] for `self`. + /// Returns the `xpr_secret_key`. NOTE: This function should be made private in the following PRs. #[inline] - pub fn expose_mnemonic(&self) -> &Mnemonic { - &self.mnemonic + pub fn xpr_secret_key(&self, index: &AccountIndex) -> SecretKey { + SecretKey::derive_from_path( + self.seed, + &path_string::(*index) + .parse() + .expect("Path string is valid by construction."), + ) + .expect("Unable to generate secret key for valid seed and path string.") } +} + +/// Account type +pub type Account = key::Account>; - /// Samples a random [`KeySecret`] from `rng` with no password. +/// Vec Account type +pub type VecAccountMap = Vec>; + +impl Sample for KeySecret +where + C: CoinType, +{ #[inline] - pub fn sample(rng: &mut R) -> Self + fn sample(_: (), rng: &mut R) -> Self where - R: CryptoRng + RngCore + ?Sized, + R: RngCore + ?Sized, { - Self::new(Mnemonic::sample(rng), "") + let mut seed = [0; Seed::SIZE]; + rng.fill_bytes(&mut seed); + Self::build(seed) } } @@ -195,41 +209,19 @@ where /// [`BIP-0044`]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki #[inline] #[must_use] -pub fn path_string(account: AccountIndex, kind: Kind, index: KeyIndex) -> String +pub fn path_string(account: AccountIndex) -> String where C: CoinType, { const BIP_44_PURPOSE_ID: u8 = 44; format!( - "m/{}'/{}'/{}'/{}'/{}'", + "m/{}'/{}'/{}'", BIP_44_PURPOSE_ID, C::COIN_TYPE_ID, - account.index(), - kind as u8, - index.index(), + account.index() ) } -impl HierarchicalKeyDerivationScheme for KeySecret -where - C: CoinType, -{ - const GAP_LIMIT: IndexType = 20; - - type SecretKey = SecretKey; - - #[inline] - fn derive(&self, account: AccountIndex, kind: Kind, index: KeyIndex) -> Self::SecretKey { - SecretKey::derive_from_path( - self.seed, - &path_string::(account, kind, index) - .parse() - .expect("Path string is valid by construction."), - ) - .expect("Unable to generate secret key for valid seed and path string.") - } -} - /// Mnemonic #[cfg_attr( feature = "serde", @@ -240,28 +232,26 @@ where pub struct Mnemonic( /// Underlying BIP39 Mnemonic #[cfg_attr(feature = "serde", serde(serialize_with = "Mnemonic::serialize"))] - bip0039::Mnemonic, + bip32::Mnemonic, ); -/// Seed Type -pub type Seed = [u8; 64]; - impl Mnemonic { - /// Create a new BIP39 mnemonic phrase from the given phrase. + /// Create a new BIP39 mnemonic phrase from the given string. #[inline] - pub fn new(phrase: &str) -> Result { - Ok(Self(bip0039::Mnemonic::from_phrase(phrase).unwrap())) + pub fn new(phrase: S) -> Result + where + S: AsRef, + { + bip32::Mnemonic::new(phrase, Default::default()).map(Self) } - /// Samples a random 12 word [`Mnemonic`] using the entropy returned from `rng`. + /// Samples a random [`Mnemonic`] using the entropy returned from `rng`. #[inline] pub fn sample(rng: &mut R) -> Self where R: CryptoRng + RngCore + ?Sized, { - let mut entropy: [u8; 16] = [0; 16]; - rng.fill_bytes(&mut entropy); - Self(bip0039::Mnemonic::from_entropy(entropy.to_vec()).unwrap()) + Self(bip32::Mnemonic::random(rng, Default::default())) } /// Convert this mnemonic phrase into the BIP39 seed value. @@ -273,7 +263,7 @@ impl Mnemonic { /// Serializes the underlying `mnemonic` phrase. #[cfg(feature = "serde")] #[inline] - fn serialize(mnemonic: &bip0039::Mnemonic, serializer: S) -> Result + fn serialize(mnemonic: &bip32::Mnemonic, serializer: S) -> Result where S: Serializer, { @@ -302,6 +292,6 @@ impl TryFrom for Mnemonic { #[inline] fn try_from(string: String) -> Result { - Self::new(string.as_str()) + Self::new(string) } } diff --git a/manta-pay/src/signer/base.rs b/manta-pay/src/signer/base.rs index 520b3d7b2..f7886b973 100644 --- a/manta-pay/src/signer/base.rs +++ b/manta-pay/src/signer/base.rs @@ -17,74 +17,66 @@ //! Manta Pay Signer Configuration use crate::{ - config::{Bls12_381_Edwards, Config, MerkleTreeConfiguration, SecretKey}, + config::{Config, MerkleTreeConfiguration, PublicKey, SecretKey, Parameters}, crypto::constraint::arkworks::Fp, key::{CoinType, KeySecret, Testnet}, signer::Checkpoint, }; use alloc::collections::BTreeMap; -use core::{cmp, marker::PhantomData, mem}; +use core::{cmp, mem}; use manta_accounting::{ asset::HashAssetMap, - key, + key::{self, AccountIndex, DeriveAddresses, AccountCollection}, wallet::{ self, - signer::{self, AssetMapKey, SyncData}, + signer::{self, SyncData}, }, }; use manta_crypto::{ - arkworks::{ec::ProjectiveCurve, ff::PrimeField}, - key::kdf::KeyDerivationFunction, + arkworks::{ + ed_on_bls12_381::FrParameters, + ff::{Fp256, PrimeField}, + }, + key::{agreement::Derive}, merkle_tree::{self, forest::Configuration}, - rand::{ChaCha20Rng, CryptoRng, RngCore}, + rand::{ChaCha20Rng, CryptoRng, RngCore, Rand}, }; -#[cfg(feature = "serde")] -use manta_util::serde::{Deserialize, Serialize}; - -/// Hierarchical Key Derivation Function -#[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, Ord, PartialEq, PartialOrd)] -pub struct HierarchicalKeyDerivationFunction(PhantomData) +impl DeriveAddresses for KeySecret where - C: CoinType; + C: CoinType, +{ + type Address = PublicKey; + type Parameters = Parameters; + #[inline] + fn address(&self, parameters: &Self::Parameters, index: AccountIndex) -> Self::Address { + let spending_key = self.spending_key(&index); + parameters.key_agreement_scheme().derive(&spending_key, & mut ()) + } +} -impl KeyDerivationFunction for HierarchicalKeyDerivationFunction +impl key::AccountCollection for KeySecret where C: CoinType, { - type Key = as key::HierarchicalKeyDerivationScheme>::SecretKey; - type Output = SecretKey; - + type SpendingKey = SecretKey; #[inline] - fn derive(&self, key: &Self::Key, _: &mut ()) -> Self::Output { - // FIXME: Check that this conversion is logical/safe. - let bytes: [u8; 32] = key - .private_key() - .to_bytes() - .try_into() - .expect("The secret key has 32 bytes."); - Fp(::ScalarField::from_le_bytes_mod_order(&bytes)) + fn spending_key(&self, index: &AccountIndex) -> Self::SpendingKey { + let xpr_secret_key = self.xpr_secret_key(index); + Fp(Fp256::::from_le_bytes_mod_order( + &xpr_secret_key.to_bytes(), + )) } } -/// Hierarchical Key Derivation Scheme -pub type HierarchicalKeyDerivationScheme = - key::Map, HierarchicalKeyDerivationFunction>; - /// Samples a [`HierarchicalKeyDerivationFunction`] from `rng`. #[inline] -pub fn sample_key_secret(rng: &mut R) -> HierarchicalKeyDerivationScheme +pub fn sample_key_secret(rng: &mut R) -> KeySecret where C: CoinType, R: CryptoRng + RngCore + ?Sized, { - HierarchicalKeyDerivationScheme::new(KeySecret::sample(rng), Default::default()) + rng.gen() } /// Signer UTXO Accumulator @@ -99,9 +91,9 @@ pub type UtxoAccumulator = merkle_tree::forest::TreeArrayMerkleForest< impl wallet::signer::Configuration for Config { type Checkpoint = Checkpoint; - type HierarchicalKeyDerivationScheme = HierarchicalKeyDerivationScheme; + type Account = KeySecret; type UtxoAccumulator = UtxoAccumulator; - type AssetMap = HashAssetMap>; + type AssetMap = HashAssetMap; type Rng = ChaCha20Rng; } diff --git a/manta-pay/src/signer/client/websocket.rs b/manta-pay/src/signer/client/websocket.rs index 0f2b21cc5..45be550b1 100644 --- a/manta-pay/src/signer/client/websocket.rs +++ b/manta-pay/src/signer/client/websocket.rs @@ -19,13 +19,13 @@ // TODO: Make this code work on WASM and non-WASM by choosing the correct dependency library. use crate::{ - config::{Config, ReceivingKey}, + config::{Config, PublicKey}, signer::{ - Checkpoint, ReceivingKeyRequest, SignError, SignRequest, SignResponse, SyncError, - SyncRequest, SyncResponse, + Checkpoint, SignError, SignRequest, SignResponse, SyncError, + SyncRequest, SyncResponse, GetRequest, }, }; -use alloc::{boxed::Box, vec::Vec}; +use alloc::{boxed::Box}; use core::marker::Unpin; use futures::{SinkExt, StreamExt}; use manta_accounting::wallet::{self, signer}; @@ -143,8 +143,7 @@ impl signer::Connection for Client { #[inline] fn receiving_keys( &mut self, - request: ReceivingKeyRequest, - ) -> LocalBoxFutureResult, Self::Error> { - Box::pin(async move { self.send("receivingKeys", request).await }) + ) -> LocalBoxFutureResult { + Box::pin(async move { self.send("receivingKeys", GetRequest::Get).await }) } } diff --git a/manta-pay/src/signer/mod.rs b/manta-pay/src/signer/mod.rs index 9feb02c3e..5e3c22f20 100644 --- a/manta-pay/src/signer/mod.rs +++ b/manta-pay/src/signer/mod.rs @@ -53,9 +53,6 @@ pub type SignError = signer::SignError; /// Signing Result pub type SignResult = signer::SignResult; -/// Receiving Key Request -pub type ReceivingKeyRequest = signer::ReceivingKeyRequest; - /// Checkpoint #[cfg_attr( feature = "serde", @@ -201,3 +198,15 @@ impl From for RawCheckpoint { ) } } + +/// Get Request +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(crate = "manta_util::serde", deny_unknown_fields) +)] +#[derive(Clone)] +pub enum GetRequest { + /// GET + Get, +} \ No newline at end of file From 076d18208ed28d78f5bb0ca6672d9c866c6b9e06 Mon Sep 17 00:00:00 2001 From: Francisco Hernandez Iglesias Date: Tue, 15 Nov 2022 15:01:30 +0100 Subject: [PATCH 03/20] fmt and sort Signed-off-by: Francisco Hernandez Iglesias --- manta-pay/src/signer/base.rs | 12 +++++++----- manta-pay/src/signer/client/websocket.rs | 10 ++++------ manta-pay/src/signer/mod.rs | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/manta-pay/src/signer/base.rs b/manta-pay/src/signer/base.rs index f7886b973..9332329af 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::{Config, MerkleTreeConfiguration, PublicKey, SecretKey, Parameters}, + config::{Config, MerkleTreeConfiguration, Parameters, PublicKey, SecretKey}, crypto::constraint::arkworks::Fp, key::{CoinType, KeySecret, Testnet}, signer::Checkpoint, @@ -26,7 +26,7 @@ use alloc::collections::BTreeMap; use core::{cmp, mem}; use manta_accounting::{ asset::HashAssetMap, - key::{self, AccountIndex, DeriveAddresses, AccountCollection}, + key::{self, AccountCollection, AccountIndex, DeriveAddresses}, wallet::{ self, signer::{self, SyncData}, @@ -37,9 +37,9 @@ use manta_crypto::{ ed_on_bls12_381::FrParameters, ff::{Fp256, PrimeField}, }, - key::{agreement::Derive}, + key::agreement::Derive, merkle_tree::{self, forest::Configuration}, - rand::{ChaCha20Rng, CryptoRng, RngCore, Rand}, + rand::{ChaCha20Rng, CryptoRng, Rand, RngCore}, }; impl DeriveAddresses for KeySecret @@ -51,7 +51,9 @@ where #[inline] fn address(&self, parameters: &Self::Parameters, index: AccountIndex) -> Self::Address { let spending_key = self.spending_key(&index); - parameters.key_agreement_scheme().derive(&spending_key, & mut ()) + parameters + .key_agreement_scheme() + .derive(&spending_key, &mut ()) } } diff --git a/manta-pay/src/signer/client/websocket.rs b/manta-pay/src/signer/client/websocket.rs index 45be550b1..49f94434f 100644 --- a/manta-pay/src/signer/client/websocket.rs +++ b/manta-pay/src/signer/client/websocket.rs @@ -21,11 +21,11 @@ use crate::{ config::{Config, PublicKey}, signer::{ - Checkpoint, SignError, SignRequest, SignResponse, SyncError, - SyncRequest, SyncResponse, GetRequest, + Checkpoint, GetRequest, SignError, SignRequest, SignResponse, SyncError, SyncRequest, + SyncResponse, }, }; -use alloc::{boxed::Box}; +use alloc::boxed::Box; use core::marker::Unpin; use futures::{SinkExt, StreamExt}; use manta_accounting::wallet::{self, signer}; @@ -141,9 +141,7 @@ impl signer::Connection for Client { } #[inline] - fn receiving_keys( - &mut self, - ) -> LocalBoxFutureResult { + fn receiving_keys(&mut self) -> LocalBoxFutureResult { Box::pin(async move { self.send("receivingKeys", GetRequest::Get).await }) } } diff --git a/manta-pay/src/signer/mod.rs b/manta-pay/src/signer/mod.rs index 5e3c22f20..2e16d0b0d 100644 --- a/manta-pay/src/signer/mod.rs +++ b/manta-pay/src/signer/mod.rs @@ -209,4 +209,4 @@ impl From for RawCheckpoint { pub enum GetRequest { /// GET Get, -} \ No newline at end of file +} From 8bb9a836afce6d07d8f2704bd4decbed76d505a2 Mon Sep 17 00:00:00 2001 From: Francisco Hernandez Iglesias Date: Tue, 15 Nov 2022 16:30:27 +0100 Subject: [PATCH 04/20] clippy issues Signed-off-by: Francisco Hernandez Iglesias --- manta-accounting/src/wallet/signer.rs | 21 ++++++--------------- manta-accounting/src/wallet/test/mod.rs | 4 ++-- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 04db9c130..cf6773ba2 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -43,16 +43,13 @@ use crate::{ wallet::ledger::{self, Data}, }; use alloc::{boxed::Box, vec, vec::Vec}; -use core::{convert::Infallible, default, fmt::Debug, hash::Hash}; +use core::{convert::Infallible, fmt::Debug, hash::Hash}; use manta_crypto::{ accumulator::{Accumulator, ExactSizeAccumulator, OptimizedAccumulator}, rand::{CryptoRng, FromEntropy, Rand, RngCore}, }; use manta_util::{ - array_map, - future::LocalBoxFutureResult, - into_array_unchecked, - iter::{Finder, IteratorExt}, + array_map, future::LocalBoxFutureResult, into_array_unchecked, iter::IteratorExt, persistence::Rollback, }; @@ -491,14 +488,6 @@ where proving_context, } } - - /// Converts `keypair` into a [`ReceivingKey`] by using the key-agreement scheme to derive the - /// public keys associated to `keypair`. - #[inline] - fn receiving_key(&self, key: SecretKey) -> ReceivingKey { - let spending_key = SpendingKey::new(key.clone(), key); - spending_key.derive(self.parameters.key_agreement_scheme()) - } } /// Signer State @@ -595,6 +584,7 @@ where /// Inserts the new `utxo`-`note` pair into the `utxo_accumulator` adding the spendable amount /// to `assets` if there is no void number to match it. + #[allow(clippy::too_many_arguments)] #[inline] fn insert_next_item( utxo_accumulator: &mut C::UtxoAccumulator, @@ -666,10 +656,11 @@ where where I: Iterator, EncryptedNote)>, { + let _ = with_recovery; let void_number_count = void_numbers.len(); let mut deposit = Vec::new(); let mut withdraw = Vec::new(); - let mut default_key = self.accounts.get_default().spending_key(); + let default_key = self.accounts.get_default().spending_key(); for (utxo, encrypted_note) in inserts { if let Some(note) = encrypted_note.decrypt(¶meters.note_encryption_scheme, &default_key, &mut ()) @@ -743,7 +734,7 @@ where asset: Asset, ) -> Result, SignError> { let receiving_key = ReceivingKey:: { - spend: self.accounts.get_default().address(parameters).clone(), + spend: self.accounts.get_default().address(parameters), view: self.accounts.get_default().address(parameters), }; Ok(receiving_key.into_receiver(parameters, self.rng.gen(), asset)) diff --git a/manta-accounting/src/wallet/test/mod.rs b/manta-accounting/src/wallet/test/mod.rs index 95932e609..e4ce2c217 100644 --- a/manta-accounting/src/wallet/test/mod.rs +++ b/manta-accounting/src/wallet/test/mod.rs @@ -33,7 +33,7 @@ use core::{fmt::Debug, future::Future, hash::Hash, marker::PhantomData}; use futures::StreamExt; use indexmap::IndexSet; use manta_crypto::rand::{CryptoRng, Distribution, Rand, RngCore, Sample}; -use manta_util::{future::LocalBoxFuture, vec::VecExt}; +use manta_util::future::LocalBoxFuture; use parking_lot::Mutex; use statrs::{ distribution::{Categorical, Poisson}, @@ -842,7 +842,7 @@ where } } } - Action::GenerateReceivingKeys { count } => Event { + Action::GenerateReceivingKeys { count: _ } => Event { action: ActionType::GenerateReceivingKeys, value: match actor.wallet.receiving_keys().await { Ok(key) => { From 8ec1b9fefdc2ebf706b588762ad5952eb8349517 Mon Sep 17 00:00:00 2001 From: Francisco Hernandez Iglesias Date: Tue, 15 Nov 2022 17:16:21 +0100 Subject: [PATCH 05/20] final touches Signed-off-by: Francisco Hernandez Iglesias --- manta-accounting/src/key.rs | 24 ++++++++++++++++++++---- manta-pay/src/key.rs | 3 ++- manta-pay/src/signer/base.rs | 2 +- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index 3621d31e6..e397d8e44 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -236,7 +236,16 @@ pub trait DeriveAddresses { fn address(&self, parameters: &Self::Parameters, index: AccountIndex) -> Self::Address; } -/// Account Struct +/// Account +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde( + bound(deserialize = "H: Deserialize<'de>", serialize = "H: Serialize",), + crate = "manta_util::serde", + deny_unknown_fields + ) +)] #[derive(derivative::Derivative)] #[derivative( Clone(bound = "H: Clone"), @@ -250,7 +259,10 @@ pub struct Account where H: AccountCollection, { + /// Account Collection key: H, + + /// Index index: AccountIndex, } @@ -258,11 +270,14 @@ impl Account where H: AccountCollection, { - /// + /// Builds a new [`Account`] from `key` and `index`. + #[inline] pub fn new(key: H, index: AccountIndex) -> Self { Self { key, index } } - /// + + /// Returns the [`SpendingKey`] corresponding to `self`. + #[inline] pub fn spending_key(&self) -> SpendingKey { self.key.spending_key(&self.index) } @@ -275,6 +290,7 @@ where type Address = H::Address; type Parameters = H::Parameters; + #[inline] fn address(&self, parameters: &Self::Parameters) -> Self::Address { self.key.address(parameters, self.index) } @@ -304,7 +320,7 @@ where H: AccountCollection, M: AccountMap, { - /// AccountCollection + /// Account Collection keys: H, /// Account Map diff --git a/manta-pay/src/key.rs b/manta-pay/src/key.rs index 54d1e3027..6f9d671fc 100644 --- a/manta-pay/src/key.rs +++ b/manta-pay/src/key.rs @@ -170,9 +170,10 @@ where Self::from_seed(mnemonic.to_seed(password)) } - /// Returns the `xpr_secret_key`. NOTE: This function should be made private in the following PRs. + /// Returns the [`SecretKey`]. #[inline] pub fn xpr_secret_key(&self, index: &AccountIndex) -> SecretKey { + // TODO: This function should be made private in the following PRs. SecretKey::derive_from_path( self.seed, &path_string::(*index) diff --git a/manta-pay/src/signer/base.rs b/manta-pay/src/signer/base.rs index 9332329af..db6bd07e9 100644 --- a/manta-pay/src/signer/base.rs +++ b/manta-pay/src/signer/base.rs @@ -71,7 +71,7 @@ where } } -/// Samples a [`HierarchicalKeyDerivationFunction`] from `rng`. +/// Samples a [`KeySecret`] from `rng`. #[inline] pub fn sample_key_secret(rng: &mut R) -> KeySecret where From 44d0394a6f75b17fa6320b8a8c080acdfadabf77 Mon Sep 17 00:00:00 2001 From: Francisco Hernandez Iglesias Date: Tue, 15 Nov 2022 17:18:43 +0100 Subject: [PATCH 06/20] fmt Signed-off-by: Francisco Hernandez Iglesias --- manta-pay/src/key.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manta-pay/src/key.rs b/manta-pay/src/key.rs index 6f9d671fc..6143c4d3a 100644 --- a/manta-pay/src/key.rs +++ b/manta-pay/src/key.rs @@ -170,7 +170,7 @@ where Self::from_seed(mnemonic.to_seed(password)) } - /// Returns the [`SecretKey`]. + /// Returns the [`SecretKey`]. #[inline] pub fn xpr_secret_key(&self, index: &AccountIndex) -> SecretKey { // TODO: This function should be made private in the following PRs. From 149f3c107b289b493384bae597b53df8f39fc008 Mon Sep 17 00:00:00 2001 From: Francisco Hernandez Iglesias Date: Tue, 15 Nov 2022 17:26:21 +0100 Subject: [PATCH 07/20] build documentation fix Signed-off-by: Francisco Hernandez Iglesias --- manta-accounting/src/wallet/signer.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index cf6773ba2..9175d9e8b 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -168,12 +168,6 @@ where T: ledger::Checkpoint, { /// Recovery Flag - /// - /// If `with_recovery` is set to `true`, the [`GAP_LIMIT`] is used during sync to perform a full - /// recovery. See [`Configuration::HierarchicalKeyDerivationScheme`] for the scheme where the - /// [`GAP_LIMIT`] is configured. - /// - /// [`GAP_LIMIT`]: HierarchicalKeyDerivationScheme::GAP_LIMIT pub with_recovery: bool, /// Origin Checkpoint From 15e9c4763127a994ff30a24aac4475fd379c9c10 Mon Sep 17 00:00:00 2001 From: Francisco Hernandez Iglesias Date: Tue, 15 Nov 2022 17:40:16 +0100 Subject: [PATCH 08/20] documentation Signed-off-by: Francisco Hernandez Iglesias --- manta-accounting/src/key.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index e397d8e44..206012b67 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -114,7 +114,7 @@ pub trait AccountCollection { /// Spending Key Type type SpendingKey; - /// Returns the spending key associated to this account. + /// Returns the [`SpendingKey`] corresponding to `index`. fn spending_key(&self, index: &AccountIndex) -> Self::SpendingKey; } @@ -220,7 +220,7 @@ pub trait DeriveAddress { /// Address Type type Address; - /// Returns the spending key associated to this account. + /// Returns the [`Address`](Self::Address) corresponding to `self`. fn address(&self, parameters: &Self::Parameters) -> Self::Address; } @@ -232,7 +232,7 @@ pub trait DeriveAddresses { /// Address Type type Address; - /// Returns the spending key associated to this account. + /// Returns the [`Address`](Self::Address) corresponding to `index`. fn address(&self, parameters: &Self::Parameters, index: AccountIndex) -> Self::Address; } From a9a1ca2bb978f84e1b0474d5945b59c6a9ed50ea Mon Sep 17 00:00:00 2001 From: Francisco Hernandez Iglesias Date: Tue, 15 Nov 2022 17:43:38 +0100 Subject: [PATCH 09/20] changelog Signed-off-by: Francisco Hernandez Iglesias --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7894709d..796964a3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Added ### Changed +- [\#282](https://github.com/Manta-Network/manta-rs/pull/282) Upgrade key system. ### Deprecated From b33ac61879018d7549a92ddb31ae3f6f69c5fc38 Mon Sep 17 00:00:00 2001 From: Francisco Hernandez Iglesias Date: Tue, 15 Nov 2022 21:24:26 +0100 Subject: [PATCH 10/20] asset is compatible with transfer now Signed-off-by: Francisco Hernandez Iglesias --- manta-accounting/src/asset.rs | 980 +++++++++------------ manta-accounting/src/lib.rs | 2 +- manta-accounting/src/transfer/batch.rs | 12 +- manta-accounting/src/transfer/canonical.rs | 95 +- manta-accounting/src/transfer/mod.rs | 273 ++++-- manta-accounting/src/transfer/receiver.rs | 21 +- manta-accounting/src/transfer/sender.rs | 30 +- manta-accounting/src/transfer/test.rs | 71 +- 8 files changed, 727 insertions(+), 757 deletions(-) diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 9a7f2df03..686059253 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -16,10 +16,10 @@ //! 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 symbol and decimals -//! for human-readable display purposes. +//! This module defines the data structures and canonical encodings of a standard notion of "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 symbol and decimals for +//! human-readable display purposes. #![allow(clippy::uninlined_format_args)] // NOTE: Clippy false positive https://github.com/rust-lang/rust-clippy/issues/9715 on Display implementation on Asset below @@ -34,16 +34,30 @@ use core::{ borrow::Borrow, fmt::Debug, hash::Hash, - iter::{self, FusedIterator, Sum}, - ops::{Add, AddAssign, Deref, Sub, SubAssign}, + iter::{self, FusedIterator}, + ops::{Add, AddAssign, Deref, Div, Sub, SubAssign}, slice, }; -use derive_more::{Add, AddAssign, Display, From, Sub, SubAssign, Sum}; +use derive_more::{Display, From}; use manta_crypto::{ - eclair::alloc::{mode::Secret, Allocate, Allocator, Variable}, + constraint::{HasInput, Input}, + eclair::{ + self, + alloc::{ + mode::{Public, Secret}, + Allocate, Allocator, Variable, + }, + bool::{Assert, AssertEq, Bool, ConditionalSelect}, + num::Zero, + ops::BitAnd, + Has, + }, rand::{Rand, RngCore, Sample}, }; -use manta_util::{into_array_unchecked, Array, SizeLimit}; +use manta_util::{ + codec::{Encode, Write}, + num::CheckedSub, +}; #[cfg(feature = "serde")] use manta_util::serde::{Deserialize, Serialize}; @@ -54,263 +68,16 @@ use std::{ hash::BuildHasher, }; -/// [`AssetId`] Base Type -pub type AssetIdType = u32; - -/// Asset Id Type -#[cfg_attr( - feature = "serde", - derive(Deserialize, Serialize), - serde(crate = "manta_util::serde", deny_unknown_fields) -)] -#[derive(Clone, Copy, Debug, Default, Display, Eq, From, Hash, Ord, PartialEq, PartialOrd)] -#[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; - - /// 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 { - 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() - } - - /// Samples an [`Asset`] by uniformly choosing between zero and `maximum` when selecting coins. - #[cfg(feature = "test")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] - #[inline] - pub fn sample_up_to(self, maximum: AssetValue, rng: &mut R) -> Asset - where - R: RngCore + ?Sized, - { - self.value(rng.gen_range(0..maximum.0)) - } -} - -impl From for [u8; AssetId::SIZE] { - #[inline] - fn from(id: AssetId) -> Self { - id.into_bytes() - } -} - -impl PartialEq for AssetId { - #[inline] - fn eq(&self, rhs: &AssetIdType) -> bool { - self.0 == *rhs - } -} - -impl Sample for AssetId -where - AssetIdType: Sample, -{ - #[inline] - fn sample(distribution: D, rng: &mut R) -> Self - where - R: RngCore + ?Sized, - { - Self(rng.sample(distribution)) - } -} - -impl SizeLimit for AssetId { - const SIZE: usize = Self::SIZE; -} - -/// [`AssetValue`] Base Type -pub type AssetValueType = u128; - -/// Asset Value Type -#[cfg_attr( - feature = "serde", - derive(Deserialize, Serialize), - serde(crate = "manta_util::serde", deny_unknown_fields) -)] -#[derive( - Add, - AddAssign, - Clone, - Copy, - Debug, - Default, - Display, - Eq, - From, - Hash, - Ord, - PartialEq, - PartialOrd, - Sub, - SubAssign, - Sum, -)] -#[from(forward)] -pub struct AssetValue( - /// [`Asset`] Value - pub AssetValueType, -); - -impl AssetValue { - /// The size of this type in 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 [`AssetValue`] and `id` as the [`AssetId`]. - #[inline] - pub const fn with(self, id: AssetId) -> Asset { - 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 { - Self(AssetValueType::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 { - 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 { - match self.0.checked_sub(rhs.0) { - Some(result) => Some(Self(result)), - _ => None, - } - } -} - -impl Add for AssetValue { - type Output = Self; - - #[inline] - fn add(mut self, rhs: AssetValueType) -> Self { - self.add_assign(rhs); - self - } -} - -impl AddAssign for AssetValue { - #[inline] - fn add_assign(&mut self, rhs: AssetValueType) { - self.0 += rhs; - } -} - -impl From for [u8; AssetValue::SIZE] { - #[inline] - fn from(value: AssetValue) -> Self { - value.into_bytes() - } -} - -impl PartialEq for AssetValue { - #[inline] - fn eq(&self, rhs: &AssetValueType) -> bool { - self.0 == *rhs - } -} - -impl Sample for AssetValue -where - AssetValueType: Sample, -{ - #[inline] - fn sample(distribution: D, rng: &mut R) -> Self - where - R: RngCore + ?Sized, - { - Self(rng.sample(distribution)) - } -} - -impl SizeLimit for AssetValue { - const SIZE: usize = Self::SIZE; -} - -impl Sub for AssetValue { - type Output = Self; - - #[inline] - fn sub(mut self, rhs: AssetValueType) -> Self { - self.sub_assign(rhs); - self - } -} - -impl SubAssign for AssetValue { - #[inline] - fn sub_assign(&mut self, rhs: AssetValueType) { - self.0 -= rhs; - } -} - -impl<'a> Sum<&'a AssetValue> for AssetValue { - #[inline] - fn sum(iter: I) -> Self - where - I: Iterator, - { - iter.copied().sum() - } -} - /// Asset #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), serde(crate = "manta_util::serde", deny_unknown_fields) )] -#[derive(Clone, Copy, Debug, Default, Display, Eq, From, Hash, Ord, PartialEq, PartialOrd)] +#[derive(derivative::Derivative, Display, From)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] #[display(fmt = "{{id: {}, value: {}}}", id, value)] -pub struct Asset { +pub struct Asset { /// Asset Id pub id: I, @@ -324,92 +91,37 @@ impl Asset { pub const fn new(id: I, value: V) -> Self { Self { id, value } } -} - -impl Asset { - /// The size of the data in this type in 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 zero [`Asset`] with the given `id`. #[inline] - pub const fn zero(id: AssetId) -> Self { - Self::new(id, AssetValue(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 { - self.id.0 == rhs.id.0 - } - - /// Converts a byte array into `self`. - #[inline] - pub fn from_bytes(bytes: [u8; Self::SIZE]) -> Self { - Self::new( - 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] { - 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). - #[inline] - pub const fn value_of(&self, id: AssetId) -> Option { - 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 AssetValue> { - if self.id.0 == id.0 { - Some(&mut self.value) - } else { - None - } + pub fn zero(id: I) -> Self + where + V: Default, + { + Self::new(id, Default::default()) } - /// Adds the value of `asset` to `self` if it has the same [`AssetId`]. + /// Returns `true` if `self` is a zero [`Asset`] of some asset id. #[inline] - pub fn try_add_assign(&mut self, asset: Asset) -> bool { - if self.id == asset.id { - self.value += asset.value; - true - } else { - false - } + pub fn is_zero(&self) -> bool + where + V: Default + PartialEq, + { + self.value == Default::default() } - /// Subtracts the value of `asset` from `self` if it has the same [`AssetId`]. + /// Returns `true` if `self` is an empty [`Asset`], i.e. both the `id` and `value` are zero. #[inline] - pub fn try_sub_assign(&mut self, asset: Asset) -> bool { - if self.id == asset.id { - self.value -= asset.value; - true - } else { - false - } + pub fn is_empty(&self, compiler: &mut COM) -> Bool + where + COM: Has, + I: Zero>, + V: Zero>, + Bool: BitAnd, COM, Output = Bool>, + { + self.id + .is_zero(compiler) + .bitand(self.value.is_zero(compiler), compiler) } } @@ -436,42 +148,25 @@ where } } -impl From<[u8; Self::SIZE]> for Asset { - #[inline] - fn from(array: [u8; Self::SIZE]) -> Self { - Self::from_bytes(array) - } -} - -impl From> for Asset { - #[inline] - fn from(array: Array) -> Self { - array.0.into() - } -} - -impl From for [u8; Asset::SIZE] { - #[inline] - fn from(asset: Asset) -> Self { - asset.into_bytes() - } -} - -impl From for Array { +impl From> for (I, V) { #[inline] - fn from(asset: Asset) -> Self { - Self(asset.into_bytes()) + fn from(asset: Asset) -> Self { + (asset.id, asset.value) } } -impl From> for (I, V) { +impl<'a, I, V> From<&'a Asset> for (&'a I, &'a V) { #[inline] - fn from(asset: Asset) -> Self { - (asset.id, asset.value) + fn from(asset: &'a Asset) -> Self { + (&asset.id, &asset.value) } } -impl Sample for Asset { +impl Sample for Asset +where + I: Sample, + V: Sample, +{ #[inline] fn sample(_: (), rng: &mut R) -> Self where @@ -481,10 +176,6 @@ impl Sample for Asset { } } -impl SizeLimit for Asset { - const SIZE: usize = Self::SIZE; -} - impl Sub for Asset where V: SubAssign, @@ -508,43 +199,128 @@ where } } +impl ConditionalSelect for Asset +where + COM: Has, + I: ConditionalSelect, + V: ConditionalSelect, +{ + #[inline] + fn select(bit: &Bool, true_value: &Self, false_value: &Self, compiler: &mut COM) -> Self { + Self::new( + I::select(bit, &true_value.id, &false_value.id, compiler), + V::select(bit, &true_value.value, &false_value.value, compiler), + ) + } +} + +impl eclair::cmp::PartialEq for Asset +where + COM: Has, + Bool: BitAnd, COM, Output = Bool>, + I: eclair::cmp::PartialEq, + V: eclair::cmp::PartialEq, +{ + #[inline] + fn eq(&self, rhs: &Self, compiler: &mut COM) -> Bool { + self.id + .eq(&rhs.id, compiler) + .bitand(self.value.eq(&rhs.value, compiler), compiler) + } + + #[inline] + fn assert_equal(&self, rhs: &Self, compiler: &mut COM) + where + COM: Assert, + { + compiler.assert_eq(&self.id, &rhs.id); + compiler.assert_eq(&self.value, &rhs.value); + } +} + impl Variable for Asset where - I: Variable, - V: Variable, + I: Variable, + V: Variable, { - type Type = Asset; + type Type = Asset; + + #[inline] + fn new_unknown(compiler: &mut COM) -> Self { + Self::new(compiler.allocate_unknown(), compiler.allocate_unknown()) + } #[inline] fn new_known(this: &Self::Type, compiler: &mut COM) -> Self { Self::new(this.id.as_known(compiler), this.value.as_known(compiler)) } +} + +impl Variable for Asset +where + I: Variable, + V: Variable, +{ + type Type = Asset; #[inline] fn new_unknown(compiler: &mut COM) -> Self { Self::new(compiler.allocate_unknown(), compiler.allocate_unknown()) } + + #[inline] + fn new_known(this: &Self::Type, compiler: &mut COM) -> Self { + Self::new(this.id.as_known(compiler), this.value.as_known(compiler)) + } +} + +impl Encode for Asset +where + I: Encode, + V: Encode, +{ + #[inline] + fn encode(&self, mut writer: W) -> Result<(), W::Error> + where + W: Write, + { + self.id.encode(&mut writer)?; + self.value.encode(&mut writer)?; + Ok(()) + } +} + +impl Input

for Asset where P: HasInput + HasInput + ?Sized, @@ -742,7 +802,7 @@ macro_rules! impl_asset_map_for_maps_body { } else if value == &asset.value { return Selection::new(Default::default(), vec![(key.clone(), value.clone())]); } else { - sum.value.add_assign(value); + sum.value.add_assign(value.clone()); values.push((key.clone(), value.clone())); } } @@ -819,8 +879,9 @@ impl AssetMap for BTreeAssetMap where K: Clone + Ord, I: Clone + Ord, - V: AddAssign + Clone + Default + Ord + Sub + for<'v> AddAssign<&'v V>, + V: AddAssign + Clone + Default + Ord + Sub, for<'v> &'v V: Sub, + for<'v> &'v V: AddAssign<&'v V>, { impl_asset_map_for_maps_body! { K, I, V, BTreeMapEntry } } @@ -836,8 +897,9 @@ impl AssetMap for HashAssetMap where K: Clone + Hash + Eq, I: Clone + Ord, - V: AddAssign + Clone + Default + Ord + Sub + for<'v> AddAssign<&'v V>, + V: AddAssign + Clone + Default + Ord + Sub, for<'v> &'v V: Sub, + for<'v> &'v V: AddAssign<&'v V>, S: BuildHasher + Default, { impl_asset_map_for_maps_body! { K, I, V, HashMapEntry } diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index 87fa4dc90..6781d265c 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -36,7 +36,7 @@ use core::{ hash::Hash, iter::Sum, marker::PhantomData, - ops::{AddAssign, Deref}, + ops::{AddAssign, Deref, Sub}, }; use manta_crypto::{ accumulator::{self, AssertValidVerification, MembershipProof, Model}, @@ -129,7 +129,7 @@ pub trait Configuration { type AssetId: Clone + Hash + Ord; // Hash /// Asset Value Type - type AssetValue: AddAssign + Clone + Copy + Default + PartialOrd + Sum; // Copy + type AssetValue: AddAssign + Clone + Copy + Default + PartialOrd + Sub + Sum; // Sub, Copy /// Secret Key Type type SecretKey: Clone + Sample + SizeLimit; @@ -835,12 +835,13 @@ where } } -// impl SizeLimit for Note -// where -// C: Configuration, -// { -// const SIZE: usize = SecretKey::::SIZE + Asset::SIZE; -// } +impl SizeLimit for Note +where + C: Configuration, + Asset: SizeLimit, +{ + const SIZE: usize = SecretKey::::SIZE + Asset::::SIZE; +} /// Transfer pub struct Transfer< diff --git a/manta-accounting/src/transfer/test.rs b/manta-accounting/src/transfer/test.rs index 7e4a891a6..73d0a8305 100644 --- a/manta-accounting/src/transfer/test.rs +++ b/manta-accounting/src/transfer/test.rs @@ -25,7 +25,10 @@ use crate::{ }, }; use alloc::vec::Vec; -use core::{fmt::Debug, ops::Sub}; +use core::{ + fmt::Debug, + ops::{Rem, Sub}, +}; use manta_crypto::{ accumulator::Accumulator, constraint::ProofSystem, @@ -35,39 +38,29 @@ use manta_util::into_array_unchecked; use super::ProofInput; -/// Asset Value Type -type AssetValueType = u128; - -/// Asset Value -#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)] -pub struct AssetValue(pub AssetValueType); - -impl Sub for AssetValue { - type Output = Self; - - #[inline] - fn sub(self, rhs: Self) -> Self::Output { - Self(self.0 - rhs.0) - } -} - /// 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(count: usize, total: AssetValue, rng: &mut R) -> Vec +pub fn value_distribution( + count: usize, + total: C::AssetValue, + rng: &mut R, +) -> Vec where + C: Configuration, + C::AssetValue: Ord + Rem + Sample + Sub, R: RngCore + ?Sized, { if count == 0 { return Vec::default(); } let mut result = Vec::with_capacity(count + 1); - result.push(AssetValue(0)); + result.push(C::AssetValue::default()); for _ in 1..count { - result.push(AssetValue(AssetValueType::gen(rng) % total.0)); + result.push(::gen(rng) % total); } result.push(total); result.sort_unstable(); @@ -86,11 +79,16 @@ where /// /// This is a naive algorithm and should only be used for testing purposes. #[inline] -pub fn sample_asset_values(total: AssetValue, rng: &mut R) -> [AssetValue; N] +pub fn sample_asset_values( + total: C::AssetValue, + rng: &mut R, +) -> [C::AssetValue; N] where + C: Configuration, + C::AssetValue: Ord + Rem + Sample + Sub, R: RngCore + ?Sized, { - into_array_unchecked(value_distribution(N, total, rng)) + into_array_unchecked(value_distribution::(N, total, rng)) } /// Parameters Distribution @@ -189,9 +187,9 @@ where impl Transfer where - C: Configuration, + C: Configuration, C::AssetId: Sample, - C::AssetValue: Sample, + C::AssetValue: Rem + Sample + Sub, { /// Samples a [`TransferPost`] from `parameters` and `utxo_accumulator` using `proving_context` /// and `rng`. @@ -205,6 +203,7 @@ where where A: Accumulator, Model = C::UtxoAccumulatorModel>, R: CryptoRng + RngCore + ?Sized, + C::AssetValue: Ord + Rem + Sample + Sub, { Self::sample( TransferDistribution { @@ -232,6 +231,7 @@ where where A: Accumulator, Model = C::UtxoAccumulatorModel>, R: CryptoRng + RngCore + ?Sized, + C::AssetValue: Ord + Rem + Sample + Sub, { let (proving_context, verifying_context) = Self::generate_context( public_parameters, @@ -260,6 +260,7 @@ where where A: Accumulator, Model = C::UtxoAccumulatorModel>, R: CryptoRng + RngCore + ?Sized, + C::AssetValue: Ord + Rem + Sample + Sub, { let post = Self::sample_post(proving_context, parameters, utxo_accumulator, rng)?; C::ProofSystem::verify( @@ -282,6 +283,7 @@ where R: CryptoRng + RngCore + ?Sized, ProofInput: PartialEq, ProofSystemError: Debug, + C::AssetValue: Ord + Rem + Sample + Sub, { let transfer = Self::sample( TransferDistribution { @@ -378,9 +380,9 @@ impl< const SINKS: usize, > Sample> for Transfer where - C: Configuration, + C: Configuration, C::AssetId: Sample, - C::AssetValue: Sample, + C::AssetValue: Ord + Rem + Sample + Sub, A: Accumulator, Model = C::UtxoAccumulatorModel>, { #[inline] @@ -389,8 +391,8 @@ where R: RngCore + ?Sized, { 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 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( @@ -421,7 +423,8 @@ impl< > Sample> for Transfer where - C: Configuration, + C: Configuration, + C::AssetValue: Ord + Rem + Sample + Sub, A: Accumulator, Model = C::UtxoAccumulatorModel>, { #[inline] @@ -432,17 +435,17 @@ where let (senders, receivers) = sample_senders_and_receivers( distribution.base.parameters, distribution.asset_id.clone(), - &value_distribution(SENDERS, distribution.sender_sum, rng), - &value_distribution(RECEIVERS, distribution.receiver_sum, rng), + &value_distribution::(SENDERS, distribution.sender_sum, rng), + &value_distribution::(RECEIVERS, distribution.receiver_sum, rng), distribution.base.utxo_accumulator, rng, ); Self::new( has_public_participants(SOURCES, SINKS).then_some(distribution.asset_id), - sample_asset_values(distribution.source_sum, rng), + sample_asset_values::(distribution.source_sum, rng), into_array_unchecked(senders), into_array_unchecked(receivers), - sample_asset_values(distribution.sink_sum, rng), + sample_asset_values::(distribution.sink_sum, rng), ) } } diff --git a/manta-accounting/src/wallet/balance.rs b/manta-accounting/src/wallet/balance.rs index b4242eda7..999d41236 100644 --- a/manta-accounting/src/wallet/balance.rs +++ b/manta-accounting/src/wallet/balance.rs @@ -21,11 +21,10 @@ //! abstractions. use crate::{ - asset::{self, AssetList}, + asset::AssetList, transfer::{Asset, Configuration}, }; use alloc::collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}; -use core::fmt::Debug; use manta_util::num::CheckedSub; #[cfg(feature = "std")] @@ -190,14 +189,12 @@ where #[cfg(any(feature = "test", test))] #[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] pub mod test { - use core::ops::Add; use super::*; + use crate::asset; + use core::{fmt::Debug, ops::Add}; use manta_crypto::rand::{CryptoRng, RngCore, Sample}; - #[cfg(test)] - use manta_crypto::rand::OsRng; - /// Asserts that a random deposit and withdraw is always valid. #[inline] pub fn assert_valid_withdraw(state: &mut S, rng: &mut R) @@ -263,6 +260,10 @@ pub mod test { ); } + /* + + Note: the following tests cannot be defined until we specify a transfer configuration. + /// Defines the tests across multiple different [`BalanceState`] types. macro_rules! define_tests { ($(( @@ -296,34 +297,34 @@ pub mod test { } } - // Note: the following tests cannot be defined until we specify a transfer configuration. - - // define_tests!( - // ( - // AssetList, - // "[`AssetList`]", - // asset_list_valid_withdraw, - // asset_list_full_withdraw, - // ), - // ( - // BTreeMapBalanceState, - // "[`BTreeMapBalanceState`]", - // btree_map_valid_withdraw, - // btree_map_full_withdraw, - // ), - // ); - - // /// Tests valid withdrawals for a [`HashMapBalanceState`] balance state. - // #[cfg(feature = "std")] - // #[test] - // fn hash_map_valid_withdraw() { - // assert_valid_withdraw(&mut HashMapBalanceState::new(), &mut OsRng); - // } - - // /// - // #[cfg(feature = "std")] - // #[test] - // fn hash_map_full_withdraw() { - // assert_full_withdraw_should_remove_entry::(&mut OsRng); - // } + define_tests!( + ( + AssetList, + "[`AssetList`]", + asset_list_valid_withdraw, + asset_list_full_withdraw, + ), + ( + BTreeMapBalanceState, + "[`BTreeMapBalanceState`]", + btree_map_valid_withdraw, + btree_map_full_withdraw, + ), + ); + + /// Tests valid withdrawals for a [`HashMapBalanceState`] balance state. + #[cfg(feature = "std")] + #[test] + fn hash_map_valid_withdraw() { + assert_valid_withdraw(&mut HashMapBalanceState::new(), &mut OsRng); + } + + /// + #[cfg(feature = "std")] + #[test] + fn hash_map_full_withdraw() { + assert_full_withdraw_should_remove_entry::(&mut OsRng); + } + + */ } diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 3a16c8e15..6a5e21646 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -16,6 +16,11 @@ //! Manta-Pay Configuration +use core::{ + iter::Sum, + ops::{AddAssign, Rem, Sub, SubAssign}, +}; + use crate::crypto::{ constraint::arkworks::{field_element_as_bytes, groth16, Boolean, Fp, FpVar, R1CS}, ecc, @@ -29,8 +34,8 @@ use blake2::{ Blake2sVar, }; use manta_accounting::{ - asset::{Asset, AssetId, AssetValue}, - transfer, + asset, + transfer::{self, Asset}, }; use manta_crypto::{ accumulator, @@ -57,7 +62,9 @@ use manta_crypto::{ }; use manta_util::{ codec::{Decode, DecodeError, Encode, Read, Write}, - into_array_unchecked, Array, AsBytes, SizeLimit, + into_array_unchecked, + num::CheckedSub, + Array, AsBytes, SizeLimit, }; #[cfg(feature = "bs58")] @@ -66,8 +73,199 @@ use alloc::string::String; #[cfg(any(feature = "test", test))] use manta_crypto::rand::{Rand, RngCore, Sample}; +#[cfg(feature = "serde")] +use manta_util::serde::{Deserialize, Serialize}; + pub(crate) use ed_on_bls12_381::EdwardsProjective as Bls12_381_Edwards; +/// Asset Id Type +pub type AssetIdType = u32; + +/// Asset Id +#[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 AssetId(pub AssetIdType); + +impl SizeLimit for AssetId { + const SIZE: usize = (AssetIdType::BITS / 8) as usize; +} + +impl Encode for AssetId { + #[inline] + fn encode(&self, writer: W) -> Result<(), W::Error> + where + W: Write, + { + self.0.encode(writer) + } +} + +impl Decode for AssetId { + type Error = ::Error; + + #[inline] + fn decode(reader: R) -> Result> + where + R: Read, + { + Ok(Self(AssetIdType::decode(reader)?)) + } +} + +impl Sample for AssetId { + #[inline] + fn gen(rng: &mut R) -> Self + where + (): Default, + R: RngCore + ?Sized, + { + Self(AssetIdType::gen(rng)) + } + + #[inline] + fn sample(distribution: (), rng: &mut R) -> Self + where + R: RngCore + ?Sized, + { + Self(AssetIdType::sample(distribution, rng)) + } +} + +/// Asset Value Type +pub type AssetValueType = u128; + +/// Asset Value +#[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 AssetValue(pub AssetValueType); + +impl SizeLimit for AssetValue { + const SIZE: usize = (AssetValueType::BITS / 8) as usize; +} + +impl Sub for AssetValue { + type Output = Self; + + #[inline] + fn sub(self, rhs: Self) -> Self::Output { + Self(self.0 - rhs.0) + } +} + +impl SubAssign for AssetValue { + #[inline] + fn sub_assign(&mut self, rhs: Self) { + self.0.sub_assign(rhs.0); + } +} + +impl CheckedSub for AssetValue { + type Output = Self; + + #[inline] + fn checked_sub(self, rhs: Self) -> Option { + Some(Self(self.0.checked_sub(rhs.0)?)) + } +} + +impl CheckedSub for &AssetValue { + type Output = ::Output; + + #[inline] + fn checked_sub(self, rhs: Self) -> Option { + (*self).checked_sub(*rhs) + } +} + +impl Sub for &AssetValue { + type Output = ::Output; + + #[inline] + fn sub(self, rhs: Self) -> Self::Output { + (*self).sub(*rhs) + } +} + +impl Sum for AssetValue { + #[inline] + fn sum>(iter: I) -> Self { + Self(iter.map(|x| x.0).sum()) + } +} + +impl Rem for AssetValue { + type Output = Self; + + #[inline] + fn rem(self, rhs: Self) -> Self::Output { + Self(self.0 % rhs.0) + } +} + +impl AddAssign for AssetValue { + #[inline] + fn add_assign(&mut self, rhs: Self) { + self.0 += rhs.0; + } +} + +impl AddAssign for &AssetValue { + #[inline] + fn add_assign(&mut self, rhs: Self) { + let mut asset_value = **self; + asset_value.add_assign(*rhs); + } +} + +impl Encode for AssetValue { + #[inline] + fn encode(&self, writer: W) -> Result<(), W::Error> + where + W: Write, + { + self.0.encode(writer) + } +} + +impl Decode for AssetValue { + type Error = ::Error; + + #[inline] + fn decode(reader: R) -> Result> + where + R: Read, + { + Ok(Self(AssetValueType::decode(reader)?)) + } +} + +impl Sample for AssetValue { + #[inline] + fn gen(rng: &mut R) -> Self + where + (): Default, + R: RngCore + ?Sized, + { + Self(AssetValueType::gen(rng)) + } + + #[inline] + fn sample(distribution: (), rng: &mut R) -> Self + where + R: RngCore + ?Sized, + { + Self(AssetValueType::sample(distribution, rng)) + } +} + /// Pairing Curve Type pub type PairingCurve = Bls12_381; @@ -174,7 +372,7 @@ pub struct UtxoCommitmentScheme(pub Poseidon4); impl transfer::UtxoCommitmentScheme for UtxoCommitmentScheme { type EphemeralSecretKey = EmbeddedScalar; type PublicSpendKey = Group; - type Asset = Asset; + type Asset = asset::Asset; type Utxo = Utxo; #[inline] @@ -241,7 +439,7 @@ pub struct UtxoCommitmentSchemeVar(pub Poseidon4Var); impl transfer::UtxoCommitmentScheme for UtxoCommitmentSchemeVar { type EphemeralSecretKey = EmbeddedScalarVar; type PublicSpendKey = GroupVar; - type Asset = Asset; + type Asset = asset::Asset; type Utxo = UtxoVar; #[inline] @@ -661,7 +859,7 @@ impl encryption::convert::plaintext::Forward for NotePlaintextMapping { let mut bytes = Vec::new(); bytes.append(&mut field_element_as_bytes(&source.ephemeral_secret_key.0)); bytes - .write(&mut source.asset.into_bytes().as_slice()) + .write(&mut source.asset.to_vec().as_slice()) .expect("This can never fail."); Array::from_unchecked(bytes) } @@ -678,10 +876,13 @@ impl encryption::convert::plaintext::Reverse for NotePlaintextMapping { fn into_source(target: Self::TargetDecryptedPlaintext, _: &mut ()) -> Self::DecryptedPlaintext { // TODO: Use a deserialization method to do this. let target = target?; - let mut slice = target.as_ref(); + let mut slice: &[u8] = target.as_ref(); Some(Note { ephemeral_secret_key: Fp(EmbeddedScalarField::deserialize(&mut slice).ok()?), - asset: Asset::from_bytes(into_array_unchecked(slice)), + asset: Asset::::from_vec( + into_array_unchecked::(slice).to_vec(), + ) + .expect("Decoding Asset is not allowed to fail."), }) } } @@ -732,6 +933,8 @@ pub type NoteEncryptionScheme = pub struct Config; impl transfer::Configuration for Config { + type AssetId = AssetId; + type AssetValue = AssetValue; type SecretKey = SecretKey; type PublicKey = PublicKey; type KeyAgreementScheme = KeyAgreementScheme; @@ -848,3 +1051,56 @@ pub fn receiving_key_from_base58(string: &str) -> Option { view: view.to_owned().try_into().ok()?, }) } + +/// Asset Value Sample +#[cfg(feature = "test")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] +pub mod asset_value_sample { + use super::*; + use ark_std::rand::{ + distributions::uniform::{SampleBorrow, UniformInt, UniformSampler}, + Rng, + }; + use manta_crypto::rand::SampleUniform; + + /// Asset Value Sampler + #[derive(Clone, Copy, Debug)] + pub struct AssetValueSampler(UniformInt); + + impl UniformSampler for AssetValueSampler { + type X = AssetValue; + + #[inline] + fn new(low: B1, high: B2) -> Self + where + B1: SampleBorrow, + B2: SampleBorrow, + { + AssetValueSampler(UniformInt::::new(low.borrow().0, high.borrow().0)) + } + + #[inline] + fn new_inclusive(low: B1, high: B2) -> Self + where + B1: SampleBorrow, + B2: SampleBorrow, + { + AssetValueSampler(UniformInt::::new_inclusive( + low.borrow().0, + high.borrow().0, + )) + } + + #[inline] + fn sample(&self, rng: &mut R) -> Self::X + where + R: Rng + ?Sized, + { + AssetValue(self.0.sample(rng)) + } + } + + impl SampleUniform for AssetValue { + type Sampler = AssetValueSampler; + } +} diff --git a/manta-pay/src/signer/base.rs b/manta-pay/src/signer/base.rs index db6bd07e9..e7aaa9b45 100644 --- a/manta-pay/src/signer/base.rs +++ b/manta-pay/src/signer/base.rs @@ -27,6 +27,7 @@ use core::{cmp, mem}; use manta_accounting::{ asset::HashAssetMap, key::{self, AccountCollection, AccountIndex, DeriveAddresses}, + transfer, wallet::{ self, signer::{self, SyncData}, @@ -95,7 +96,11 @@ impl wallet::signer::Configuration for Config { type Checkpoint = Checkpoint; type Account = KeySecret; type UtxoAccumulator = UtxoAccumulator; - type AssetMap = HashAssetMap; + type AssetMap = HashAssetMap< + SecretKey, + ::AssetId, + ::AssetValue, + >; type Rng = ChaCha20Rng; } diff --git a/manta-pay/src/signer/mod.rs b/manta-pay/src/signer/mod.rs index 2e16d0b0d..91204d9aa 100644 --- a/manta-pay/src/signer/mod.rs +++ b/manta-pay/src/signer/mod.rs @@ -33,13 +33,13 @@ pub mod base; pub type SyncRequest = signer::SyncRequest; /// Synchronization Response -pub type SyncResponse = signer::SyncResponse; +pub type SyncResponse = signer::SyncResponse; /// Synchronization Error pub type SyncError = signer::SyncError; /// Synchronization Result -pub type SyncResult = signer::SyncResult; +pub type SyncResult = signer::SyncResult; /// Signing Request pub type SignRequest = signer::SignRequest; diff --git a/manta-pay/src/simulation/ledger/mod.rs b/manta-pay/src/simulation/ledger/mod.rs index 0d6c19836..68ddf0283 100644 --- a/manta-pay/src/simulation/ledger/mod.rs +++ b/manta-pay/src/simulation/ledger/mod.rs @@ -30,11 +30,12 @@ use alloc::{sync::Arc, vec::Vec}; use core::convert::Infallible; use indexmap::IndexSet; use manta_accounting::{ - asset::{Asset, AssetId, AssetList, AssetValue}, + asset::AssetList, transfer::{ - canonical::TransferShape, InvalidSinkAccount, InvalidSourceAccount, Proof, ReceiverLedger, - ReceiverPostingKey, SenderLedger, SenderPostingKey, SinkPostingKey, SourcePostingKey, - TransferLedger, TransferLedgerSuperPostingKey, TransferPostingKey, UtxoAccumulatorOutput, + self, canonical::TransferShape, Asset, InvalidSinkAccount, InvalidSourceAccount, Proof, + ReceiverLedger, ReceiverPostingKey, SenderLedger, SenderPostingKey, SinkPostingKey, + SourcePostingKey, TransferLedger, TransferLedgerSuperPostingKey, TransferPostingKey, + UtxoAccumulatorOutput, }, wallet::{ ledger::{self, ReadResponse}, @@ -117,7 +118,13 @@ pub struct Ledger { utxo_forest: UtxoMerkleForest, /// Account Table - accounts: HashMap>, + accounts: HashMap< + AccountId, + HashMap< + ::AssetId, + ::AssetValue, + >, + >, /// Verifying Contexts verifying_context: MultiVerifyingContext, @@ -144,19 +151,32 @@ impl Ledger { /// Returns the public balances of `account` if it exists. #[inline] - pub fn public_balances(&self, account: AccountId) -> Option { + pub fn public_balances( + &self, + account: AccountId, + ) -> Option< + AssetList< + ::AssetId, + ::AssetValue, + >, + > { Some( self.accounts .get(&account)? .iter() - .map(|(id, value)| Asset::new(*id, *value)) + .map(|(id, value)| Asset::::new(*id, *value)) .collect(), ) } /// 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) { + pub fn set_public_balance( + &mut self, + account: AccountId, + id: ::AssetId, + value: ::AssetValue, + ) { self.accounts.entry(account).or_default().insert(id, value); } @@ -275,8 +295,10 @@ impl ReceiverLedger for Ledger { impl TransferLedger for Ledger { type AccountId = AccountId; type Event = (); - type ValidSourceAccount = WrapPair; - type ValidSinkAccount = WrapPair; + type ValidSourceAccount = + WrapPair::AssetValue>; + type ValidSinkAccount = + WrapPair::AssetValue>; type ValidProof = Wrap<()>; type SuperPostingKey = (); type UpdateError = Infallible; @@ -284,11 +306,16 @@ impl TransferLedger for Ledger { #[inline] fn check_source_accounts( &self, - asset_id: AssetId, + asset_id: ::AssetId, sources: I, - ) -> Result, InvalidSourceAccount> + ) -> Result, InvalidSourceAccount> where - I: Iterator, + I: Iterator< + Item = ( + Self::AccountId, + ::AssetValue, + ), + >, { sources .map(|(account_id, withdraw)| { @@ -327,11 +354,16 @@ impl TransferLedger for Ledger { #[inline] fn check_sink_accounts( &self, - asset_id: AssetId, + asset_id: ::AssetId, sinks: I, - ) -> Result, InvalidSinkAccount> + ) -> Result, InvalidSinkAccount> where - I: Iterator, + I: Iterator< + Item = ( + Self::AccountId, + ::AssetValue, + ), + >, { sinks .map(move |(account_id, deposit)| { @@ -351,7 +383,7 @@ impl TransferLedger for Ledger { #[inline] fn is_valid( &self, - asset_id: Option, + asset_id: Option<::AssetId>, sources: &[SourcePostingKey], senders: &[SenderPostingKey], receivers: &[ReceiverPostingKey], @@ -377,7 +409,7 @@ impl TransferLedger for Ledger { #[inline] fn update_public_balances( &mut self, - asset_id: AssetId, + asset_id: ::AssetId, sources: Vec>, sinks: Vec>, proof: Self::ValidProof, @@ -452,9 +484,18 @@ impl ledger::Write> for LedgerConnection { } } -impl PublicBalanceOracle for LedgerConnection { +impl PublicBalanceOracle for LedgerConnection { #[inline] - fn public_balances(&self) -> LocalBoxFuture> { + fn public_balances( + &self, + ) -> LocalBoxFuture< + Option< + AssetList< + ::AssetId, + ::AssetValue, + >, + >, + > { Box::pin(async move { self.ledger.read().await.public_balances(self.account) }) } } diff --git a/manta-pay/src/simulation/mod.rs b/manta-pay/src/simulation/mod.rs index ea37a390a..8537a8905 100644 --- a/manta-pay/src/simulation/mod.rs +++ b/manta-pay/src/simulation/mod.rs @@ -18,7 +18,8 @@ use crate::{ config::{ - Config, MultiProvingContext, MultiVerifyingContext, Parameters, UtxoAccumulatorModel, + AssetId, AssetValue, AssetValueType, Config, MultiProvingContext, MultiVerifyingContext, + Parameters, UtxoAccumulatorModel, }, signer::base::{sample_key_secret, Signer, UtxoAccumulator}, simulation::ledger::{AccountId, Ledger, LedgerConnection}, @@ -27,7 +28,6 @@ use alloc::{format, sync::Arc}; use core::fmt::Debug; use manta_accounting::{ self, - asset::{AssetId, AssetValue, AssetValueType}, key::AccountTable, wallet::{ self, @@ -134,7 +134,7 @@ impl Simulation { #[inline] pub async fn run_with(&self, ledger: GL, signer: GS) where - L: wallet::test::Ledger + PublicBalanceOracle, + L: wallet::test::Ledger + PublicBalanceOracle, S: wallet::signer::Connection, GL: FnMut(usize) -> L, GS: FnMut(usize) -> S, diff --git a/manta-pay/src/test/payment.rs b/manta-pay/src/test/payment.rs index e15ec7b59..58a5654f9 100644 --- a/manta-pay/src/test/payment.rs +++ b/manta-pay/src/test/payment.rs @@ -17,12 +17,13 @@ //! Prove and Verify Functions for Benchmark and Test Purposes use crate::config::{ - self, FullParameters, MerkleTreeConfiguration, Mint, MultiProvingContext, Parameters, - PrivateTransfer, ProvingContext, Reclaim, UtxoAccumulatorModel, + self, AssetId, AssetValue, Config, FullParameters, MerkleTreeConfiguration, Mint, + MultiProvingContext, Parameters, PrivateTransfer, ProvingContext, Reclaim, + UtxoAccumulatorModel, }; use manta_accounting::{ - asset::{Asset, AssetId}, - transfer::SpendingKey, + asset, + transfer::{Asset, SpendingKey}, }; use manta_crypto::{ accumulator::Accumulator, @@ -40,7 +41,7 @@ pub fn prove_mint( proving_context: &ProvingContext, parameters: &Parameters, utxo_accumulator_model: &UtxoAccumulatorModel, - asset: Asset, + asset: Asset, rng: &mut R, ) -> config::TransferPost where @@ -64,7 +65,7 @@ where pub fn sample_mint_spender( parameters: &Parameters, utxo_accumulator: &mut UtxoAccumulator, - asset: Asset, + asset: Asset, rng: &mut R, ) -> (config::SpendingKey, config::Sender) where @@ -90,8 +91,14 @@ where R: CryptoRng + RngCore + ?Sized, { let asset_id = AssetId(rng.gen()); - let asset_0 = asset_id.value(10_000); - let asset_1 = asset_id.value(20_000); + let asset_0 = asset::Asset { + id: asset_id, + value: AssetValue(10_000), + }; + let asset_1 = asset::Asset { + id: asset_id, + value: AssetValue(20_000), + }; let mut utxo_accumulator = UtxoAccumulator::new(utxo_accumulator_model.clone()); let (spending_key_0, sender_0) = sample_mint_spender(parameters, &mut utxo_accumulator, asset_0, rng); @@ -124,8 +131,14 @@ where R: CryptoRng + RngCore + ?Sized, { let asset_id = AssetId(rng.gen()); - let asset_0 = asset_id.value(10_000); - let asset_1 = asset_id.value(20_000); + let asset_0 = asset::Asset { + id: asset_id, + value: AssetValue(10_000), + }; + let asset_1 = asset::Asset { + id: asset_id, + value: AssetValue(20_000), + }; let mut utxo_accumulator = UtxoAccumulator::new(utxo_accumulator_model.clone()); let (spending_key_0, sender_0) = sample_mint_spender(parameters, &mut utxo_accumulator, asset_0, rng); diff --git a/manta-util/src/codec.rs b/manta-util/src/codec.rs index 9750ab278..378a6ceaa 100644 --- a/manta-util/src/codec.rs +++ b/manta-util/src/codec.rs @@ -845,6 +845,23 @@ impl Decode for u64 { } } +impl Decode for u128 { + type Error = (); + + #[inline] + fn decode(mut reader: R) -> Result> + where + R: Read, + { + let mut bytes = [0; 16]; + match reader.read_exact(&mut bytes) { + Ok(()) => Ok(Self::from_le_bytes(bytes)), + Err(ReadExactError::Read(err)) => Err(DecodeError::Read(err)), + _ => Err(DecodeError::Decode(())), + } + } +} + #[cfg(feature = "alloc")] #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] impl Decode for [T; N] From 07a23b3d50d14a83aeb85166127ac9749c93843a Mon Sep 17 00:00:00 2001 From: Francisco Hernandez Iglesias Date: Wed, 16 Nov 2022 18:43:52 +0100 Subject: [PATCH 13/20] documentation fixed Signed-off-by: Francisco Hernandez Iglesias --- manta-accounting/src/transfer/mod.rs | 4 ++-- manta-accounting/src/wallet/mod.rs | 2 +- manta-accounting/src/wallet/test/mod.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index 6781d265c..c251b78d2 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -1209,7 +1209,7 @@ where /// track this condition. type UpdateError; - /// Valid [`AssetValue`] for [`TransferPost`] Source + /// Valid [`AssetValue`](Configuration::AssetValue) for [`TransferPost`] Source /// /// # Safety /// @@ -1217,7 +1217,7 @@ where /// [`TransferLedger`]. type ValidSourceAccount: AsRef; - /// Valid [`AssetValue`] for [`TransferPost`] Sink + /// Valid [`AssetValue`](Configuration::AssetValue) for [`TransferPost`] Sink /// /// # Safety /// diff --git a/manta-accounting/src/wallet/mod.rs b/manta-accounting/src/wallet/mod.rs index bf6663299..4c2f826a5 100644 --- a/manta-accounting/src/wallet/mod.rs +++ b/manta-accounting/src/wallet/mod.rs @@ -148,7 +148,7 @@ where } /// Returns `true` if `self` contains at least every asset in `assets`. Assets are combined - /// first by [`AssetId`] before checking for membership. + /// first by [`AssetId`](Configuration::AssetValue) before checking for membership. #[inline] pub fn contains_all(&self, assets: I) -> bool where diff --git a/manta-accounting/src/wallet/test/mod.rs b/manta-accounting/src/wallet/test/mod.rs index e382b3fd6..88643ef6b 100644 --- a/manta-accounting/src/wallet/test/mod.rs +++ b/manta-accounting/src/wallet/test/mod.rs @@ -547,7 +547,7 @@ where } } - /// Samples a [`MintZero`] against `self` using `rng` to select the [`AssetId`], returning + /// Samples a [`MintZero`] against `self` using `rng` to select the [`AssetId`](transfer::Configuration::AssetId), returning /// a [`Skip`] if [`MintZero`] is impossible. /// /// [`MintZero`]: ActionType::MintZero @@ -677,7 +677,7 @@ where .unwrap_or(Action::Skip)) } - /// Reclaims all of the private balance of a random [`AssetId`] to public balance or [`Skip`] if + /// Reclaims all of the private balance of a random [`AssetId`](transfer::Configuration::AssetId) to public balance or [`Skip`] if /// the private balance is empty. /// /// [`AssetId`]: crate::asset::AssetId From f6a3177e95c02acd5a6c6de605cd8df602caae24 Mon Sep 17 00:00:00 2001 From: Francisco Hernandez Iglesias Date: Wed, 16 Nov 2022 19:15:39 +0100 Subject: [PATCH 14/20] key system upgraded to bip0039 Signed-off-by: Francisco Hernandez Iglesias --- manta-accounting/src/wallet/mod.rs | 2 +- manta-accounting/src/wallet/signer.rs | 4 +- manta-pay/Cargo.toml | 5 +- manta-pay/src/key.rs | 85 ++++++++++++++------------- manta-pay/src/lib.rs | 4 +- manta-pay/src/signer/base.rs | 4 +- 6 files changed, 54 insertions(+), 50 deletions(-) diff --git a/manta-accounting/src/wallet/mod.rs b/manta-accounting/src/wallet/mod.rs index 9ce4a6d6d..8aeef190d 100644 --- a/manta-accounting/src/wallet/mod.rs +++ b/manta-accounting/src/wallet/mod.rs @@ -379,7 +379,7 @@ where .map_err(Error::LedgerConnectionError) } - /// Returns public receiving keys according to the `request`. + /// Returns public receiving keys. #[inline] pub async fn receiving_keys(&mut self) -> Result, S::Error> { self.signer.receiving_keys().await diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 9175d9e8b..7d46cb633 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -85,7 +85,7 @@ where request: SignRequest, ) -> LocalBoxFutureResult, SignError>, Self::Error>; - /// Returns public receiving keys according to the `request`. + /// Returns public receiving keys. fn receiving_keys(&mut self) -> LocalBoxFutureResult, Self::Error>; } @@ -1160,7 +1160,7 @@ where result } - /// Returns public receiving keys according to the `request`. + /// Returns public receiving keys. #[inline] pub fn receiving_keys(&mut self) -> PublicKey { self.state diff --git a/manta-pay/Cargo.toml b/manta-pay/Cargo.toml index b7774bf05..5409c7fe7 100644 --- a/manta-pay/Cargo.toml +++ b/manta-pay/Cargo.toml @@ -48,6 +48,9 @@ arkworks = [ # Enable Download Parameters download = ["manta-parameters/download", "std"] +# Key Features +key = ["bip32", "bip0039"] + # Enable Groth16 ZKP System groth16 = ["ark-groth16", "ark-snark", "arkworks"] @@ -82,7 +85,7 @@ std = ["manta-accounting/std", "manta-util/std"] test = ["manta-accounting/test", "manta-crypto/test", "manta-parameters", "tempfile"] # Wallet -wallet = ["bip32", "bip0039", "manta-crypto/getrandom", "std"] +wallet = ["key", "manta-crypto/getrandom", "std"] # Enable WebSocket Signer Client websocket = [ diff --git a/manta-pay/src/key.rs b/manta-pay/src/key.rs index 6143c4d3a..cb68588e2 100644 --- a/manta-pay/src/key.rs +++ b/manta-pay/src/key.rs @@ -27,13 +27,14 @@ use alloc::{format, string::String}; use core::marker::PhantomData; use manta_accounting::key::{self, AccountIndex}; -use manta_crypto::rand::{CryptoRng, RngCore, Sample}; +use manta_crypto::rand::{CryptoRng, RngCore}; use manta_util::{create_seal, seal, Array}; #[cfg(feature = "serde")] use manta_util::serde::{Deserialize, Serialize, Serializer}; -pub use bip32::{Error, Seed, XPrv as SecretKey}; +pub use bip0039::{self, Error}; +pub use bip32::{self, XPrv as SecretKey}; create_seal! {} @@ -123,16 +124,16 @@ impl_coin_type!( ); /// Seed Byte Array Type -type SeedBytes = Array; +type SeedBytes = Array; /// Key Secret #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde(crate = "manta_util::serde", deny_unknown_fields, transparent) + serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(derivative::Derivative)] -#[derivative(Clone(bound = ""), Default(bound = ""))] +#[derivative(Clone(bound = ""))] pub struct KeySecret where C: CoinType, @@ -140,6 +141,9 @@ where /// Key Seed seed: SeedBytes, + /// Mnemonic + mnemonic: Mnemonic, + /// Type Parameter Marker __: PhantomData, } @@ -148,26 +152,36 @@ impl KeySecret where C: CoinType, { - /// Builds a [`KeySecret`] from raw bytes. + /// Builds a [`KeySecret`] from `seed` and `mnemonic`. #[inline] - fn build(seed: [u8; Seed::SIZE]) -> Self { + fn new_unchecked(seed: [u8; bip32::Seed::SIZE], mnemonic: Mnemonic) -> Self { Self { seed: seed.into(), + mnemonic, __: 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] pub fn new(mnemonic: Mnemonic, password: &str) -> Self { - Self::from_seed(mnemonic.to_seed(password)) + Self::new_unchecked(mnemonic.to_seed(password), mnemonic) + } + + /// Exposes a shared reference to the [`Mnemonic`] for `self`. + #[inline] + pub fn expose_mnemonic(&self) -> &Mnemonic { + &self.mnemonic + } + + /// Samples a random [`KeySecret`] from `rng` with no password. + #[inline] + pub fn sample(rng: &mut R) -> Self + where + R: CryptoRng + RngCore + ?Sized, + { + Self::new(Mnemonic::sample(rng), "") } /// Returns the [`SecretKey`]. @@ -190,21 +204,6 @@ pub type Account = key::Account>; /// Vec Account type pub type VecAccountMap = Vec>; -impl Sample for KeySecret -where - C: CoinType, -{ - #[inline] - fn sample(_: (), rng: &mut R) -> Self - where - R: RngCore + ?Sized, - { - let mut seed = [0; Seed::SIZE]; - rng.fill_bytes(&mut seed); - Self::build(seed) - } -} - /// Computes the [`BIP-0044`] path string for the given coin settings. /// /// [`BIP-0044`]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki @@ -233,38 +232,40 @@ where pub struct Mnemonic( /// Underlying BIP39 Mnemonic #[cfg_attr(feature = "serde", serde(serialize_with = "Mnemonic::serialize"))] - bip32::Mnemonic, + bip0039::Mnemonic, ); impl Mnemonic { - /// Create a new BIP39 mnemonic phrase from the given string. + /// Create a new BIP0039 mnemonic phrase from the given string. #[inline] - pub fn new(phrase: S) -> Result - where - S: AsRef, - { - bip32::Mnemonic::new(phrase, Default::default()).map(Self) + pub fn new(phrase: &str) -> Result { + bip0039::Mnemonic::from_phrase(phrase).map(Self) } - /// Samples a random [`Mnemonic`] using the entropy returned from `rng`. + /// Samples a random 12 word [`Mnemonic`] using the entropy returned from `rng`. #[inline] pub fn sample(rng: &mut R) -> Self where R: CryptoRng + RngCore + ?Sized, { - Self(bip32::Mnemonic::random(rng, Default::default())) + let mut entropy: [u8; 16] = [0; 16]; + rng.fill_bytes(&mut entropy); + Self( + bip0039::Mnemonic::from_entropy(entropy.to_vec()) + .expect("Creating a Mnemonic from 16 bytes of entropy is not allowed to fail."), + ) } - /// Convert this mnemonic phrase into the BIP39 seed value. + /// Convert this mnemonic phrase into the BIP32 seed value. #[inline] - pub fn to_seed(&self, password: &str) -> Seed { + pub fn to_seed(&self, password: &str) -> [u8; bip32::Seed::SIZE] { self.0.to_seed(password) } /// Serializes the underlying `mnemonic` phrase. #[cfg(feature = "serde")] #[inline] - fn serialize(mnemonic: &bip32::Mnemonic, serializer: S) -> Result + fn serialize(mnemonic: &bip0039::Mnemonic, serializer: S) -> Result where S: Serializer, { @@ -293,6 +294,6 @@ impl TryFrom for Mnemonic { #[inline] fn try_from(string: String) -> Result { - Self::new(string) + Self::new(string.as_str()) } } diff --git a/manta-pay/src/lib.rs b/manta-pay/src/lib.rs index 273691e3e..41776d4e5 100644 --- a/manta-pay/src/lib.rs +++ b/manta-pay/src/lib.rs @@ -30,8 +30,8 @@ pub mod util; #[cfg_attr(doc_cfg, doc(cfg(feature = "groth16")))] pub mod config; -#[cfg(feature = "bip32")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "bip32")))] +#[cfg(feature = "key")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "key")))] pub mod key; #[cfg(all(feature = "groth16", feature = "test"))] diff --git a/manta-pay/src/signer/base.rs b/manta-pay/src/signer/base.rs index db6bd07e9..99985d5bc 100644 --- a/manta-pay/src/signer/base.rs +++ b/manta-pay/src/signer/base.rs @@ -39,7 +39,7 @@ use manta_crypto::{ }, key::agreement::Derive, merkle_tree::{self, forest::Configuration}, - rand::{ChaCha20Rng, CryptoRng, Rand, RngCore}, + rand::{ChaCha20Rng, CryptoRng, RngCore}, }; impl DeriveAddresses for KeySecret @@ -78,7 +78,7 @@ where C: CoinType, R: CryptoRng + RngCore + ?Sized, { - rng.gen() + KeySecret::sample(rng) } /// Signer UTXO Accumulator From 1678fabc539eceb07cce85a1af3472ddce870db5 Mon Sep 17 00:00:00 2001 From: Francisco Hernandez Iglesias Date: Wed, 16 Nov 2022 19:37:51 +0100 Subject: [PATCH 15/20] clippy Signed-off-by: Francisco Hernandez Iglesias --- manta-pay/src/key.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manta-pay/src/key.rs b/manta-pay/src/key.rs index cb68588e2..90bf993d2 100644 --- a/manta-pay/src/key.rs +++ b/manta-pay/src/key.rs @@ -24,7 +24,7 @@ //! //! [`BIP-0044`]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki -use alloc::{format, string::String}; +use alloc::{format, string::String, vec::Vec}; use core::marker::PhantomData; use manta_accounting::key::{self, AccountIndex}; use manta_crypto::rand::{CryptoRng, RngCore}; From e3509b5fad28d07398bcde8f51cc681223f7ecf5 Mon Sep 17 00:00:00 2001 From: Francisco Hernandez Iglesias Date: Wed, 16 Nov 2022 20:13:23 +0100 Subject: [PATCH 16/20] clippy issue fixed Signed-off-by: Francisco Hernandez Iglesias --- manta-crypto/src/rand.rs | 9 ++++++--- manta-pay/src/config.rs | 11 ++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/manta-crypto/src/rand.rs b/manta-crypto/src/rand.rs index 2bde07a7b..a81708a76 100644 --- a/manta-crypto/src/rand.rs +++ b/manta-crypto/src/rand.rs @@ -39,9 +39,12 @@ pub use rand_core::OsRng; #[cfg(feature = "rand")] #[cfg_attr(doc_cfg, doc(cfg(feature = "rand")))] #[doc(inline)] -pub use rand::distributions::{ - uniform::{SampleRange, SampleUniform}, - Distribution, +pub use rand::{ + self, + distributions::{ + uniform::{SampleBorrow, SampleRange, SampleUniform, UniformInt, UniformSampler}, + Distribution, + }, }; /// Random Number Generator Sized Wrapper diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 6a5e21646..f898a134a 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -59,6 +59,7 @@ use manta_crypto::{ hash::ArrayHashFunction, key::{self, kdf::KeyDerivationFunction}, merkle_tree, + rand::{RngCore, Sample}, }; use manta_util::{ codec::{Decode, DecodeError, Encode, Read, Write}, @@ -70,8 +71,8 @@ use manta_util::{ #[cfg(feature = "bs58")] use alloc::string::String; -#[cfg(any(feature = "test", test))] -use manta_crypto::rand::{Rand, RngCore, Sample}; +#[cfg(feature = "test")] +use manta_crypto::rand::Rand; #[cfg(feature = "serde")] use manta_util::serde::{Deserialize, Serialize}; @@ -1057,11 +1058,7 @@ pub fn receiving_key_from_base58(string: &str) -> Option { #[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] pub mod asset_value_sample { use super::*; - use ark_std::rand::{ - distributions::uniform::{SampleBorrow, UniformInt, UniformSampler}, - Rng, - }; - use manta_crypto::rand::SampleUniform; + use manta_crypto::rand::{rand::Rng, SampleBorrow, SampleUniform, UniformInt, UniformSampler}; /// Asset Value Sampler #[derive(Clone, Copy, Debug)] From f3130c63bd79e3d217cb5a545fc6741db7d76eb6 Mon Sep 17 00:00:00 2001 From: Francisco Hernandez Iglesias Date: Thu, 17 Nov 2022 11:26:10 +0100 Subject: [PATCH 17/20] AccountCollection exposure from Account and Accounttable Signed-off-by: Francisco Hernandez Iglesias --- manta-accounting/src/key.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/manta-accounting/src/key.rs b/manta-accounting/src/key.rs index 206012b67..03f878fd5 100644 --- a/manta-accounting/src/key.rs +++ b/manta-accounting/src/key.rs @@ -281,6 +281,12 @@ where pub fn spending_key(&self) -> SpendingKey { self.key.spending_key(&self.index) } + + /// Returns the [`AccountCollection`] corresponding to `self`. + #[inline] + pub fn keys(&self) -> &H { + &self.key + } } impl DeriveAddress for Account @@ -344,6 +350,12 @@ where Self { keys, accounts } } + /// Returns the [`AccountCollection`] corresponding to `self`. + #[inline] + pub fn keys(&self) -> &H { + &self.keys + } + /// Returns the account keys for `account` if it exists. #[inline] pub fn get(&self, account: AccountIndex) -> Option> From 732c5da5dd6571904bc98861f3937f2e398c4ca8 Mon Sep 17 00:00:00 2001 From: Francisco Hernandez Iglesias Date: Thu, 17 Nov 2022 11:48:49 +0100 Subject: [PATCH 18/20] fixed bug note encryption Signed-off-by: Francisco Hernandez Iglesias --- manta-pay/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index f898a134a..d9e92d96d 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -881,7 +881,7 @@ impl encryption::convert::plaintext::Reverse for NotePlaintextMapping { Some(Note { ephemeral_secret_key: Fp(EmbeddedScalarField::deserialize(&mut slice).ok()?), asset: Asset::::from_vec( - into_array_unchecked::(slice).to_vec(), + into_array_unchecked::::SIZE }>(slice).to_vec(), ) .expect("Decoding Asset is not allowed to fail."), }) From 1de881a32223cf058f9da308cefdf54d48f1ce5d Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" Date: Tue, 22 Nov 2022 10:20:49 -0500 Subject: [PATCH 19/20] chore: fix input impl Signed-off-by: Brandon H. Gomes --- manta-pay/src/config.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index cdb8c6a3c..4024e61e4 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -16,11 +16,6 @@ //! Manta-Pay Configuration -use core::{ - iter::Sum, - ops::{AddAssign, Rem, Sub, SubAssign}, -}; - use crate::crypto::{ ecc, encryption::aes::{self, FixedNonceAesGcm}, @@ -32,6 +27,10 @@ use blake2::{ digest::{Update, VariableOutput}, Blake2sVar, }; +use core::{ + iter::Sum, + ops::{AddAssign, Rem, Sub, SubAssign}, +}; use manta_accounting::{ asset, transfer::{self, Asset}, @@ -140,6 +139,13 @@ impl Sample for AssetId { } } +impl Input for AssetId { + #[inline] + fn extend(&self, input: &mut Vec) { + input.push(self.0.into()); + } +} + /// Asset Value Type pub type AssetValueType = u128; @@ -271,6 +277,13 @@ impl Sample for AssetValue { } } +impl Input for AssetValue { + #[inline] + fn extend(&self, input: &mut Vec) { + input.push(self.0.into()); + } +} + /// Pairing Curve Type pub type PairingCurve = Bls12_381; From 6d70fff9914726cfad2d9a343dda630d1ad9aa55 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" Date: Tue, 22 Nov 2022 10:37:58 -0500 Subject: [PATCH 20/20] chore: simplify reading Signed-off-by: Brandon H. Gomes --- manta-pay/src/config.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/manta-pay/src/config.rs b/manta-pay/src/config.rs index 4024e61e4..366a125be 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -66,7 +66,6 @@ use manta_crypto::{ }; use manta_util::{ codec::{Decode, DecodeError, Encode, Read, Write}, - into_array_unchecked, num::CheckedSub, Array, AsBytes, SizeLimit, }; @@ -883,10 +882,8 @@ impl encryption::convert::plaintext::Reverse for NotePlaintextMapping { let mut slice: &[u8] = target.as_ref(); Some(Note { ephemeral_secret_key: Fp(EmbeddedScalarField::deserialize(&mut slice).ok()?), - asset: Asset::::from_vec( - into_array_unchecked::::SIZE }>(slice).to_vec(), - ) - .expect("Decoding Asset is not allowed to fail."), + asset: Asset::::from_vec(slice.to_vec()) + .expect("Decoding Asset is not allowed to fail."), }) } }

for Asset +where + P: HasInput + HasInput + ?Sized, +{ + #[inline] + fn extend(&self, input: &mut P::Input) { + P::extend(input, &self.id); + P::extend(input, &self.value); + } } /// 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. +/// Stores assets sorted by `I` 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(crate = "manta_util::serde", deny_unknown_fields) )] -#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] -pub struct AssetList { +#[derive(derivative::Derivative)] +#[derivative(Clone, Debug, Default(bound = ""), 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, + /// The elements of the vector are sorted by `I`. To insert/remove we perform a binary search + /// on `I` and update `V` at that location. + map: Vec>, } -impl AssetList { +impl AssetList { /// Builds a new empty [`AssetList`]. #[inline] pub const fn new() -> Self { @@ -563,7 +339,7 @@ impl AssetList { self.map.is_empty() } - /// Returns the number of [`AssetId`] that can be inserted into `self` before needing to + /// Returns the number of `AssetId`s that can be inserted into `self` before needing to /// reallocate. #[inline] pub fn capacity(&self) -> usize { @@ -573,37 +349,66 @@ impl AssetList { /// 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 { - self.map.binary_search_by_key(&id, move |a| a.id) + fn find(&self, id: &I) -> Result + where + I: Ord, + { + self.map.binary_search_by(move |a| a.id.cmp(id)) + } + + /// Returns the value of the [`Asset`] with the given `id`, returning `None` if `self` doesn't contain + /// any [`Asset`] with `id`. + #[inline] + fn get_value(&self, id: &I) -> Option<&V> + where + I: Ord, + { + self.find(id).ok().map(move |i| &self.map[i].value) } /// 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() + pub fn value(&self, id: &I) -> V + where + I: Ord, + V: Clone + Default, + { + self.get_value(id).cloned().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 + pub fn contains(&self, asset: &Asset) -> bool + where + I: Ord, + V: Default + PartialOrd, + { + if asset.value == Default::default() { + return true; + } + match self.get_value(&asset.id) { + Some(value) => value >= &asset.value, + _ => false, + } } /// Returns an iterator over the assets in `self`. #[inline] - pub fn iter(&self) -> slice::Iter { + pub fn iter(&self) -> slice::Iter> { self.map.iter() } - /// Inserts `asset` into `self` increasing the [`AssetValue`] at `asset.id`. + /// Inserts `asset` into `self` increasing the `AssetValue` at `asset.id`. #[inline] - pub fn deposit(&mut self, asset: Asset) { - if asset.is_zero() { + pub fn deposit(&mut self, asset: Asset) + where + I: Ord, + V: AddAssign + Default + PartialEq, + { + if asset.value == Default::default() { return; } - match self.find(asset.id) { + match self.find(&asset.id) { Ok(index) => self.map[index] += asset.value, Err(index) => self.map.insert(index, asset), } @@ -611,24 +416,32 @@ impl AssetList { /// 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 { + fn set_or_remove(&mut self, index: usize, value: V) + where + V: Default + PartialEq, + { + if value == Default::default() { self.map.remove(index); } else { self.map[index].value = value; } } - /// Tries to remove `asset` from `self` decreasing the [`AssetValue`] at `asset.id`, returning + /// 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() { + pub fn withdraw(&mut self, asset: &Asset) -> bool + where + I: Ord, + V: Default + PartialEq, + for<'v> &'v V: CheckedSub, + { + if asset.value == Default::default() { return true; } - if let Ok(index) = self.find(asset.id) { - if let Some(value) = self.map[index].value.checked_sub(asset.value) { + 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; } @@ -636,19 +449,24 @@ impl AssetList { false } - /// Removes `asset` from `self` decreasing the [`AssetValue`] at `asset.id`. + /// 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() { + pub fn withdraw_unchecked(&mut self, asset: &Asset) + where + I: Ord, + V: Default + PartialEq, + for<'v> &'v V: Sub, + { + if asset.value == Default::default() { return; } - match self.find(asset.id) { - Ok(index) => self.set_or_remove(index, self.map[index].value - asset.value), + 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."), } } @@ -657,9 +475,9 @@ impl AssetList { #[inline] pub fn retain(&mut self, mut f: F) where - F: FnMut(Asset) -> bool, + F: FnMut(&Asset) -> bool, { - self.map.retain(move |asset| f(*asset)) + self.map.retain(move |asset| f(asset)) } /// Removes all assets from `self`. @@ -671,53 +489,64 @@ impl AssetList { /// 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 { - self.find(id).ok().map(move |i| self.map.remove(i).value) + pub fn remove(&mut self, id: I) -> Option + where + I: Ord, + { + self.find(&id).ok().map(move |i| self.map.remove(i).value) } } -impl AsRef<[Asset]> for AssetList { +impl AsRef<[Asset]> for AssetList { #[inline] - fn as_ref(&self) -> &[Asset] { + fn as_ref(&self) -> &[Asset] { self.map.as_ref() } } -impl Borrow<[Asset]> for AssetList { +impl Borrow<[Asset]> for AssetList { #[inline] - fn borrow(&self) -> &[Asset] { + fn borrow(&self) -> &[Asset] { self.map.borrow() } } -impl Deref for AssetList { - type Target = [Asset]; +impl Deref for AssetList { + type Target = [Asset]; #[inline] - fn deref(&self) -> &[Asset] { + fn deref(&self) -> &[Asset] { self.map.deref() } } -impl From for Vec { +impl From> for Vec> { #[inline] - fn from(list: AssetList) -> Self { + fn from(list: AssetList) -> Self { list.map } } -impl From> for AssetList { +impl From>> for AssetList +where + I: Ord, + V: AddAssign + Default + PartialEq, +{ #[inline] - fn from(vector: Vec) -> Self { + fn from(vector: Vec>) -> Self { Self::from_iter(iter::once(vector)) } } -impl FromIterator<(AssetId, AssetValue)> for AssetList { +impl FromIterator<(I, V)> for AssetList +where + I: Ord, + V: AddAssign + Default + PartialEq, +{ #[inline] - fn from_iter(iter: I) -> Self + fn from_iter(iter: A) -> Self where - I: IntoIterator, + A: IntoIterator, { iter.into_iter() .map(move |(id, value)| Asset::new(id, value)) @@ -725,11 +554,15 @@ impl FromIterator<(AssetId, AssetValue)> for AssetList { } } -impl FromIterator for AssetList { +impl FromIterator> for AssetList +where + I: Ord, + V: AddAssign + Default + PartialEq, +{ #[inline] - fn from_iter(iter: I) -> Self + fn from_iter(iter: A) -> Self where - I: IntoIterator, + A: IntoIterator>, { let mut list = Self::new(); iter.into_iter().for_each(|a| list.deposit(a)); @@ -737,21 +570,29 @@ impl FromIterator for AssetList { } } -impl FromIterator for AssetList { +impl FromIterator> for AssetList +where + I: Ord, + V: AddAssign + Default + PartialEq, +{ #[inline] - fn from_iter(iter: I) -> Self + fn from_iter(iter: A) -> Self where - I: IntoIterator, + A: IntoIterator>, { iter.into_iter().map::, _>(Into::into).collect() } } -impl FromIterator> for AssetList { +impl FromIterator>> for AssetList +where + I: Ord, + V: AddAssign + Default + PartialEq, +{ #[inline] - fn from_iter(iter: I) -> Self + fn from_iter(iter: A) -> Self where - I: IntoIterator>, + A: IntoIterator>>, { let mut list = Self::new(); for item in iter { @@ -763,9 +604,9 @@ impl FromIterator> for AssetList { } } -impl IntoIterator for AssetList { - type Item = Asset; - type IntoIter = vec::IntoIter; +impl IntoIterator for AssetList { + type Item = Asset; + type IntoIter = vec::IntoIter>; #[inline] fn into_iter(self) -> Self::IntoIter { @@ -773,9 +614,9 @@ impl IntoIterator for AssetList { } } -impl<'a> IntoIterator for &'a AssetList { - type Item = &'a Asset; - type IntoIter = slice::Iter<'a, Asset>; +impl<'a, I, V> IntoIterator for &'a AssetList { + type Item = &'a Asset; + type IntoIter = slice::Iter<'a, Asset>; #[inline] fn into_iter(self) -> Self::IntoIter { @@ -790,29 +631,29 @@ impl<'a> IntoIterator for &'a AssetList { /// # Warning /// /// It is possible that keys are repeated, as long as the assets associated to them are different. -pub trait AssetMap: Default { +pub trait AssetMap: Default { /// Key Type /// /// Keys are used to access the underlying asset values. type Key; /// Returns the sum of all the assets in `self`. - fn assets(&self) -> AssetList; + fn assets(&self) -> AssetList; /// Selects asset keys which total up to at least `asset` in value. - fn select(&self, asset: Asset) -> Selection; + fn select(&self, asset: &Asset) -> Selection; /// Returns at most `n` zero assets with the given `id`. - fn zeroes(&self, n: usize, id: AssetId) -> Vec; + fn zeroes(&self, n: usize, id: &I) -> Vec; /// Inserts `asset` at the `key` in the map. - fn insert(&mut self, key: Self::Key, asset: Asset); + fn insert(&mut self, key: Self::Key, asset: Asset); /// Inserts all of the assets in `iter`. #[inline] - fn insert_all(&mut self, iter: I) + fn insert_all(&mut self, iter: A) where - I: IntoIterator, + A: IntoIterator)>, { iter.into_iter() .for_each(move |(key, asset)| self.insert(key, asset)) @@ -820,32 +661,35 @@ pub trait AssetMap: Default { /// Inserts all of the assets in `iter` using a fixed `id`. #[inline] - fn insert_all_same(&mut self, id: AssetId, iter: I) + fn insert_all_same(&mut self, id: I, iter: A) where - I: IntoIterator, + I: Clone, + A: IntoIterator, { iter.into_iter() - .for_each(move |(key, value)| self.insert(key, id.with(value))); + .for_each(move |(key, value)| self.insert(key, Asset::new(id.clone(), value))); } /// Inserts all of the assets in `iter` using a fixed `id` and zero value. #[inline] - fn insert_zeroes(&mut self, id: AssetId, iter: I) + fn insert_zeroes(&mut self, id: I, iter: A) where - I: IntoIterator, + I: Clone, + V: Default, + A: IntoIterator, { iter.into_iter() - .for_each(move |key| self.insert(key, Asset::zero(id))); + .for_each(move |key| self.insert(key, Asset::zero(id.clone()))); } /// 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; + fn remove(&mut self, key: Self::Key, asset: Asset) -> bool; /// Removes all the keys in `iter` from the map. - fn remove_all(&mut self, iter: I) + fn remove_all(&mut self, iter: A) where - I: IntoIterator, + A: IntoIterator)>, { for (key, asset) in iter { self.remove(key, asset); @@ -855,70 +699,73 @@ pub trait AssetMap: Default { /// Retains the elements from `self` that return `true` after applying `f`. fn retain(&mut self, f: F) where - F: FnMut(&Self::Key, &mut Vec) -> bool; + F: FnMut(&Self::Key, &mut Vec>) -> bool; } /// Implements [`AssetMap`] for map types. macro_rules! impl_asset_map_for_maps_body { - ($k:ty, $entry:tt) => { - type Key = $k; + ($K:ty, $I:ty, $V:ty, $entry:tt) => { + type Key = $K; #[inline] - fn assets(&self) -> AssetList { + fn assets(&self) -> AssetList<$I, $V> { self.iter() - .flat_map(move |(_, assets)| assets.iter().copied()) + .flat_map(move |(_, assets)| assets.iter().cloned()) .collect() } #[inline] - fn select(&self, asset: Asset) -> Selection { - if asset.is_zero() { + fn select(&self, asset: &Asset<$I, $V>) -> Selection<$I, $V, Self> { + if asset.value == Default::default() { return Selection::default(); } - let mut sum = Asset::zero(asset.id); + let mut sum = Asset::<$I, $V>::zero(asset.id.clone()); let mut values = Vec::new(); - let mut min_max_asset: Option<($k, AssetValue)> = None; + let mut min_max_asset = Option::<(&$K, &$V)>::None; let map = self .iter() .map(|(key, assets)| assets.iter().map(move |asset| (key, asset))) .flatten() .filter_map(|(key, item)| { - if !item.is_zero() && item.id == asset.id { - Some((key, item.value)) + if item.value != Default::default() && item.id == asset.id { + Some((key, &item.value)) } else { None } }); for (key, value) in map { - if value > asset.value { + if value > &asset.value { min_max_asset = Some(match min_max_asset.take() { - Some(best) if value >= best.1 => best, - _ => (key.clone(), value), + Some(best) if value >= &best.1 => best, + _ => (key, value), }); - } else if value == asset.value { - return Selection::new(Default::default(), vec![(key.clone(), value)]); + } else if value == &asset.value { + return Selection::new(Default::default(), vec![(key.clone(), value.clone())]); } else { - sum.add_assign(value); - values.push((key.clone(), value)); + sum.value.add_assign(value); + values.push((key.clone(), value.clone())); } } if let Some((best_key, best_value)) = min_max_asset { - return Selection::new(best_value - asset.value, vec![(best_key, best_value)]); + return Selection::new( + best_value - &asset.value, + vec![(best_key.clone(), best_value.clone())], + ); } if sum.value < asset.value { Selection::default() } else { - Selection::new(sum.value - asset.value, values) + Selection::new(&sum.value - &asset.value, values) } } #[inline] - fn zeroes(&self, n: usize, id: AssetId) -> Vec { + fn zeroes(&self, n: usize, id: &$I) -> Vec { self.iter() .filter_map(move |(key, assets)| { assets .iter() - .any(move |a| a.id == id && a.value == 0) + .any(move |a| &a.id == id && a.value == Default::default()) .then(move || key.clone()) }) .take(n) @@ -926,7 +773,7 @@ macro_rules! impl_asset_map_for_maps_body { } #[inline] - fn insert(&mut self, key: Self::Key, asset: Asset) { + fn insert(&mut self, key: Self::Key, asset: Asset<$I, $V>) { match self.entry(key) { $entry::Vacant(entry) => { entry.insert(vec![asset]); @@ -941,7 +788,7 @@ macro_rules! impl_asset_map_for_maps_body { } #[inline] - fn remove(&mut self, key: Self::Key, asset: Asset) -> bool { + fn remove(&mut self, key: Self::Key, asset: Asset<$I, $V>) -> bool { if let $entry::Occupied(mut entry) = self.entry(key) { let assets = entry.get_mut(); if let Ok(index) = assets.binary_search(&asset) { @@ -958,7 +805,7 @@ macro_rules! impl_asset_map_for_maps_body { #[inline] fn retain(&mut self, mut f: F) where - F: FnMut(&Self::Key, &mut Vec) -> bool, + F: FnMut(&Self::Key, &mut Vec>) -> bool, { self.retain(move |key, assets| f(key, assets)); } @@ -966,28 +813,34 @@ macro_rules! impl_asset_map_for_maps_body { } /// B-Tree Map [`AssetMap`] Implementation -pub type BTreeAssetMap = BTreeMap>; +pub type BTreeAssetMap = BTreeMap>>; -impl AssetMap for BTreeAssetMap +impl AssetMap for BTreeAssetMap where K: Clone + Ord, + I: Clone + Ord, + V: AddAssign + Clone + Default + Ord + Sub + for<'v> AddAssign<&'v V>, + for<'v> &'v V: Sub, { - impl_asset_map_for_maps_body! { K, BTreeMapEntry } + impl_asset_map_for_maps_body! { K, I, V, BTreeMapEntry } } /// Hash Map [`AssetMap`] Implementation #[cfg(feature = "std")] #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] -pub type HashAssetMap = HashMap, S>; +pub type HashAssetMap = HashMap>, S>; #[cfg(feature = "std")] #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] -impl AssetMap for HashAssetMap +impl AssetMap for HashAssetMap where K: Clone + Hash + Eq, + I: Clone + Ord, + V: AddAssign + Clone + Default + Ord + Sub + for<'v> AddAssign<&'v V>, + for<'v> &'v V: Sub, S: BuildHasher + Default, { - impl_asset_map_for_maps_body! { K, HashMapEntry } + impl_asset_map_for_maps_body! { K, I, V, HashMapEntry } } /// Asset Selection @@ -996,31 +849,31 @@ where /// 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") + Clone(bound = "V: Clone, M::Key: Clone"), + Debug(bound = "V: Debug, M::Key: Debug"), + Default(bound = "V: Default"), + Eq(bound = "V: Eq, M::Key: Eq"), + Hash(bound = "V: Hash, M::Key: Hash"), + PartialEq(bound = "V: PartialEq, M::Key: PartialEq") )] -pub struct Selection +pub struct Selection where - M: AssetMap + ?Sized, + M: AssetMap + ?Sized, { /// Change Amount - pub change: AssetValue, + pub change: V, /// Asset Value Distribution - pub values: Vec<(M::Key, AssetValue)>, + pub values: Vec<(M::Key, V)>, } -impl Selection +impl Selection where - M: AssetMap + ?Sized, + M: AssetMap + ?Sized, { /// Builds a new [`Selection`] from `change` and `values`. #[inline] - pub fn new(change: AssetValue, values: Vec<(M::Key, AssetValue)>) -> Self { + pub fn new(change: V, values: Vec<(M::Key, V)>) -> Self { Self { change, values } } @@ -1032,51 +885,51 @@ where /// Returns an iterator over [`self.values`](Self::values) by reference. #[inline] - pub fn iter(&self) -> SelectionIter { + pub fn iter(&self) -> SelectionIter { SelectionIter::new(self.values.iter()) } /// Returns an iterator over the keys in [`self.values`](Self::values) by reference. #[inline] - pub fn keys(&self) -> SelectionKeys { + pub fn keys(&self) -> SelectionKeys { SelectionKeys::new(self.values.iter().map(move |(key, _)| key)) } } /// [`SelectionIter`] Iterator Type -type SelectionIterType<'s, M> = slice::Iter<'s, (::Key, AssetValue)>; +type SelectionIterType<'s, I, V, M> = slice::Iter<'s, (>::Key, V)>; /// 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> +#[derivative(Clone(bound = ""), Debug(bound = "V: Debug, M::Key: Debug"))] +pub struct SelectionIter<'s, I, V, M> where - M: AssetMap + ?Sized, + M: AssetMap + ?Sized, { /// Base Iterator - iter: SelectionIterType<'s, M>, + iter: SelectionIterType<'s, I, V, M>, } -impl<'s, M> SelectionIter<'s, M> +impl<'s, I, V, M> SelectionIter<'s, I, V, M> where - M: AssetMap + ?Sized, + M: AssetMap + ?Sized, { /// Builds a new [`SelectionIter`] from `iter`. #[inline] - fn new(iter: SelectionIterType<'s, M>) -> Self { + fn new(iter: SelectionIterType<'s, I, V, M>) -> Self { Self { iter } } } // TODO: Implement all optimized methods/traits. -impl<'s, M> Iterator for SelectionIter<'s, M> +impl<'s, I, V, M> Iterator for SelectionIter<'s, I, V, M> where - M: AssetMap + ?Sized, + M: AssetMap + ?Sized, { - type Item = &'s (M::Key, AssetValue); + type Item = &'s (M::Key, V); #[inline] fn next(&mut self) -> Option { @@ -1089,44 +942,45 @@ where } } -impl<'s, M> FusedIterator for SelectionIter<'s, M> where M: AssetMap + ?Sized {} +impl<'s, I, V, M> FusedIterator for SelectionIter<'s, I, V, M> where M: AssetMap + ?Sized {} /// [`SelectionKeys`] Map Function Type -type SelectionKeysMapFnType<'s, M> = - fn(&'s (::Key, AssetValue)) -> &'s ::Key; +type SelectionKeysMapFnType<'s, I, V, M> = + fn(&'s (>::Key, V)) -> &'s >::Key; /// [`SelectionKeys`] Iterator Type -type SelectionKeysType<'s, M> = iter::Map, SelectionKeysMapFnType<'s, M>>; +type SelectionKeysType<'s, I, V, M> = + iter::Map, SelectionKeysMapFnType<'s, I, V, 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> +#[derivative(Clone(bound = ""), Debug(bound = "V: Debug, M::Key: Debug"))] +pub struct SelectionKeys<'s, I, V, M> where - M: AssetMap + ?Sized, + M: AssetMap + ?Sized, { /// Base Iterator - iter: SelectionKeysType<'s, M>, + iter: SelectionKeysType<'s, I, V, M>, } -impl<'s, M> SelectionKeys<'s, M> +impl<'s, I, V, M> SelectionKeys<'s, I, V, M> where - M: AssetMap + ?Sized, + M: AssetMap + ?Sized, { /// Builds a new [`SelectionKeys`] from `iter`. #[inline] - fn new(iter: SelectionKeysType<'s, M>) -> Self { + fn new(iter: SelectionKeysType<'s, I, V, M>) -> Self { Self { iter } } } // TODO: Implement all optimized methods/traits. -impl<'s, M> Iterator for SelectionKeys<'s, M> +impl<'s, I, V, M> Iterator for SelectionKeys<'s, I, V, M> where - M: AssetMap + ?Sized, + M: AssetMap + ?Sized, { type Item = &'s M::Key; @@ -1141,7 +995,7 @@ where } } -impl<'s, M> FusedIterator for SelectionKeys<'s, M> where M: AssetMap + ?Sized {} +impl<'s, I, V, M> FusedIterator for SelectionKeys<'s, I, V, M> where M: AssetMap + ?Sized {} /// Asset Metadata #[cfg_attr( @@ -1161,12 +1015,15 @@ pub struct AssetMetadata { 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 { + pub fn display_value(&self, value: V) -> String + where + for<'v> &'v V: Div, + { // 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)) + let value_base_units = &value / (10u128.pow(self.decimals)); + let fractional_digits = &value / (10u128.pow(self.decimals - FRACTIONAL_DIGITS)) % (10u128.pow(FRACTIONAL_DIGITS)); format!("{value_base_units}.{fractional_digits:0>3}") } @@ -1174,74 +1031,57 @@ impl AssetMetadata { /// Returns a string formatting of `value` interpreted using `self` as the metadata including /// the symbol. #[inline] - pub fn display(&self, value: AssetValue) -> String { + pub fn display(&self, value: V) -> String + where + for<'v> &'v V: Div, + { format!("{} {}", self.display_value(value), self.symbol) } } +/// Metadata Display +pub trait MetadataDisplay { + /// Returns a string representation of `self` given the asset `metadata`. + fn display(&self, metadata: &AssetMetadata) -> String; +} + /// Asset Manager -pub trait AssetManager { +pub trait AssetManager { /// Returns the metadata associated to `id`. - fn metadata(&self, id: AssetId) -> Option<&AssetMetadata>; + fn metadata(&self, id: &I) -> Option<&AssetMetadata>; } /// Implements [`AssetManager`] for map types. macro_rules! impl_asset_manager_for_maps_body { - () => { + ($I:ident) => { #[inline] - fn metadata(&self, id: AssetId) -> Option<&AssetMetadata> { - self.get(&id) + fn metadata(&self, id: &$I) -> Option<&AssetMetadata> { + self.get(id) } }; } /// B-Tree Map [`AssetManager`] Implementation -pub type BTreeAssetManager = BTreeMap; +pub type BTreeAssetManager = BTreeMap; -impl AssetManager for BTreeAssetManager { - impl_asset_manager_for_maps_body! {} +impl AssetManager for BTreeAssetManager +where + I: Ord, +{ + impl_asset_manager_for_maps_body! { I } } /// Hash Map [`AssetManager`] Implementation #[cfg(feature = "std")] #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] -pub type HashAssetManager = HashMap; +pub type HashAssetManager = HashMap; #[cfg(feature = "std")] #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] -impl AssetManager for HashAssetManager +impl AssetManager for HashAssetManager where + I: Eq + Hash, S: BuildHasher + Default, { - impl_asset_manager_for_maps_body! {} -} - -/// Testing Suite -#[cfg(test)] -mod test { - use super::*; - use manta_crypto::rand::OsRng; - - /// Tests asset conversion into and from bytes. - #[test] - fn asset_into_and_from_bytes() { - let mut rng = OsRng; - 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); - assert_eq!(asset_bytes, Asset::from_bytes(asset_bytes).into_bytes()); - } - - /// Tests asset arithmetic. - #[test] - fn asset_arithmetic() { - let mut rng = OsRng; - let mut asset = Asset::zero(rng.gen()); - let value = rng.gen(); - let _ = asset + value; - asset += value; - let _ = asset - value; - asset -= value; - } + impl_asset_manager_for_maps_body! { I } } diff --git a/manta-accounting/src/lib.rs b/manta-accounting/src/lib.rs index 9a8fe02ee..957658c26 100644 --- a/manta-accounting/src/lib.rs +++ b/manta-accounting/src/lib.rs @@ -35,7 +35,7 @@ extern crate derive_more; pub mod asset; pub mod key; pub mod transfer; -pub mod wallet; +//pub mod wallet; #[cfg(feature = "fs")] #[cfg_attr(doc_cfg, doc(cfg(feature = "fs")))] diff --git a/manta-accounting/src/transfer/batch.rs b/manta-accounting/src/transfer/batch.rs index fdaed77e8..949192c2a 100644 --- a/manta-accounting/src/transfer/batch.rs +++ b/manta-accounting/src/transfer/batch.rs @@ -18,10 +18,7 @@ // TODO: Move more of the batching algorithm here to improve library interfaces. -use crate::{ - asset::Asset, - transfer::{Configuration, Parameters, PreSender, Receiver, SpendingKey, Utxo}, -}; +use crate::transfer::{Asset, Configuration, Parameters, PreSender, Receiver, SpendingKey, Utxo}; use alloc::vec::Vec; use manta_crypto::{ accumulator::Accumulator, @@ -49,7 +46,7 @@ where #[inline] pub fn new( parameters: &Parameters, - asset: Asset, + asset: Asset, spending_key: &SpendingKey, rng: &mut R, ) -> ([Receiver; RECEIVERS], Self) @@ -58,11 +55,12 @@ where { 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); + let (receiver, pre_sender) = + spending_key.internal_pair(parameters, rng.gen(), asset.clone()); receivers.push(receiver); for _ in 1..RECEIVERS { let (receiver, pre_sender) = - spending_key.internal_zero_pair(parameters, rng.gen(), asset.id); + spending_key.internal_zero_pair(parameters, rng.gen(), asset.id.clone()); receivers.push(receiver); zeroes.push(pre_sender); } diff --git a/manta-accounting/src/transfer/canonical.rs b/manta-accounting/src/transfer/canonical.rs index 19f1922c1..b75928461 100644 --- a/manta-accounting/src/transfer/canonical.rs +++ b/manta-accounting/src/transfer/canonical.rs @@ -19,9 +19,9 @@ // TODO: Add typing for `ProvingContext` and `VerifyingContext` against the canonical shapes. use crate::{ - asset::{self, Asset, AssetMap, AssetValue}, + asset::{self, AssetMap}, transfer::{ - has_public_participants, Configuration, FullParameters, Parameters, PreSender, + has_public_participants, Asset, Configuration, FullParameters, Parameters, PreSender, ProofSystemError, ProofSystemPublicParameters, ProvingContext, Receiver, ReceivingKey, Sender, SpendingKey, Transfer, TransferPost, VerifyingContext, }, @@ -101,7 +101,7 @@ where { /// Builds a [`Mint`] from `asset` and `receiver`. #[inline] - pub fn build(asset: Asset, receiver: Receiver) -> Self { + pub fn build(asset: Asset, receiver: Receiver) -> Self { Self::new_unchecked(Some(asset.id), [asset.value], [], [receiver], []) } @@ -110,13 +110,16 @@ where pub fn from_spending_key( parameters: &Parameters, spending_key: &SpendingKey, - asset: Asset, + asset: Asset, rng: &mut R, ) -> Self where R: CryptoRng + RngCore + ?Sized, { - Self::build(asset, spending_key.receiver(parameters, rng.gen(), asset)) + Self::build( + asset.clone(), + spending_key.receiver(parameters, rng.gen(), asset), + ) } /// Builds a new [`Mint`] and [`PreSender`] pair from a [`SpendingKey`] using @@ -125,13 +128,14 @@ where pub fn internal_pair( parameters: &Parameters, spending_key: &SpendingKey, - asset: Asset, + asset: Asset, rng: &mut R, ) -> (Self, PreSender) where R: CryptoRng + RngCore + ?Sized, { - let (receiver, pre_sender) = spending_key.internal_pair(parameters, rng.gen(), asset); + let (receiver, pre_sender) = + spending_key.internal_pair(parameters, rng.gen(), asset.clone()); (Self::build(asset, receiver), pre_sender) } } @@ -194,7 +198,7 @@ where pub fn build( senders: [Sender; ReclaimShape::SENDERS], receivers: [Receiver; ReclaimShape::RECEIVERS], - asset: Asset, + asset: Asset, ) -> Self { Self::new_unchecked(Some(asset.id), [], senders, receivers, [asset.value]) } @@ -282,8 +286,8 @@ impl TransferShape { derive(Deserialize, Serialize), serde( bound( - deserialize = "ReceivingKey: Deserialize<'de>", - serialize = "ReceivingKey: Serialize" + deserialize = "Asset: Deserialize<'de>, ReceivingKey: Deserialize<'de>", + serialize = "Asset: Serialize, ReceivingKey: Serialize" ), crate = "manta_util::serde", deny_unknown_fields @@ -291,25 +295,25 @@ impl TransferShape { )] #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "ReceivingKey: Clone"), - Copy(bound = "ReceivingKey: Copy"), - Debug(bound = "ReceivingKey: Debug"), - Eq(bound = "ReceivingKey: Eq"), - Hash(bound = "ReceivingKey: Hash"), - PartialEq(bound = "ReceivingKey: PartialEq") + Clone(bound = "Asset: Clone, ReceivingKey: Clone"), + Copy(bound = "Asset: Copy, ReceivingKey: Copy"), + Debug(bound = "Asset: Debug, ReceivingKey: Debug"), + Eq(bound = "Asset: Eq, ReceivingKey: Eq"), + Hash(bound = "Asset: Hash, ReceivingKey: Hash"), + PartialEq(bound = "Asset: PartialEq, ReceivingKey: PartialEq") )] pub enum Transaction where C: Configuration, { /// Mint Private Asset - Mint(Asset), + Mint(Asset), /// Private Transfer Asset to Receiver - PrivateTransfer(Asset, ReceivingKey), + PrivateTransfer(Asset, ReceivingKey), /// Reclaim Private Asset - Reclaim(Asset), + Reclaim(Asset), } impl Transaction @@ -320,17 +324,17 @@ where /// transaction kind if successful, and returning the asset back if the balance was /// insufficient. #[inline] - pub fn check(&self, balance: F) -> Result + pub fn check(&self, balance: F) -> Result, Asset> where - F: FnOnce(Asset) -> bool, + F: FnOnce(Asset) -> bool, { match self { - Self::Mint(asset) => Ok(TransactionKind::Deposit(*asset)), + Self::Mint(asset) => Ok(TransactionKind::Deposit(asset.clone())), Self::PrivateTransfer(asset, _) | Self::Reclaim(asset) => { - if balance(*asset) { - Ok(TransactionKind::Withdraw(*asset)) + if balance(asset.clone()) { + Ok(TransactionKind::Withdraw(asset.clone())) } else { - Err(*asset) + Err(asset.clone()) } } } @@ -361,19 +365,37 @@ where #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde(crate = "manta_util::serde", deny_unknown_fields) + serde( + bound( + deserialize = "Asset: Deserialize<'de>", + serialize = "Asset: Serialize" + ), + crate = "manta_util::serde", + deny_unknown_fields + ) )] -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum TransactionKind { +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "Asset: Clone"), + Copy(bound = "Asset: Copy"), + Debug(bound = "Asset: Debug"), + Eq(bound = "Asset: Eq"), + Hash(bound = "Asset: Hash"), + PartialEq(bound = "Asset: PartialEq") +)] +pub enum TransactionKind +where + C: Configuration, +{ /// Deposit Transaction /// /// A transaction of this kind will result in a deposit of `asset`. - Deposit(Asset), + Deposit(Asset), /// Withdraw Transaction /// /// A transaction of this kind will result in a withdraw of `asset`. - Withdraw(Asset), + Withdraw(Asset), } /// Transfer Asset Selection @@ -382,7 +404,7 @@ where C: Configuration, { /// Change Value - pub change: AssetValue, + pub change: C::AssetValue, /// Pre-Senders pub pre_senders: Vec>, @@ -394,7 +416,7 @@ where { /// Builds a new [`Selection`] from `change` and `pre_senders`. #[inline] - fn build(change: AssetValue, pre_senders: Vec>) -> Self { + fn build(change: C::AssetValue, pre_senders: Vec>) -> Self { Self { change, pre_senders, @@ -403,10 +425,13 @@ where /// Builds a new [`Selection`] by mapping over an asset selection with `builder`. #[inline] - pub fn new(selection: asset::Selection, mut builder: F) -> Result + pub fn new( + selection: asset::Selection, + mut builder: F, + ) -> Result where - M: AssetMap, - F: FnMut(M::Key, AssetValue) -> Result, E>, + M: AssetMap, + F: FnMut(M::Key, C::AssetValue) -> Result, E>, { Ok(Self::build( selection.change, diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index 74217539b..4e622a284 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -29,9 +29,15 @@ //! 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 crate::asset; use alloc::vec::Vec; -use core::{fmt::Debug, hash::Hash, marker::PhantomData, ops::Deref}; +use core::{ + fmt::Debug, + hash::Hash, + iter::Sum, + marker::PhantomData, + ops::{AddAssign, Deref}, +}; use manta_crypto::{ accumulator::{self, AssertValidVerification, MembershipProof, Model}, constraint::{HasInput, ProofSystem}, @@ -119,6 +125,12 @@ pub trait VoidNumberCommitmentScheme { /// Transfer Configuration pub trait Configuration { + /// Asset Id Type + type AssetId: Clone + Ord; + + /// Asset Value Type + type AssetValue: AddAssign + Clone + Default + PartialOrd + Sum; + /// Secret Key Type type SecretKey: Clone + Sample + SizeLimit; @@ -157,7 +169,7 @@ pub trait Configuration { type UtxoCommitmentScheme: UtxoCommitmentScheme< EphemeralSecretKey = SecretKey, PublicSpendKey = PublicKey, - Asset = Asset, + Asset = Asset, Utxo = Utxo, >; @@ -228,13 +240,13 @@ pub trait Configuration { >; /// Asset Id Variable Type - type AssetIdVar: Variable - + Variable + type AssetIdVar: Variable + + Variable + eclair::cmp::PartialEq; /// Asset Value Variable Type - type AssetValueVar: Variable - + Variable + type AssetValueVar: Variable + + Variable + Add + eclair::cmp::PartialEq; @@ -243,8 +255,8 @@ pub trait Configuration { /// Proof System Type type ProofSystem: ProofSystem - + HasInput - + HasInput + + HasInput + + HasInput + HasInput> + HasInput> + HasInput> @@ -262,8 +274,12 @@ pub trait Configuration { >; } +/// Asset Type +pub type Asset = asset::Asset<::AssetId, ::AssetValue>; + /// Asset Variable Type -pub type AssetVar = Asset<::AssetIdVar, ::AssetValueVar>; +pub type AssetVar = + asset::Asset<::AssetIdVar, ::AssetValueVar>; /// Secret Key Type pub type SecretKey = ::SecretKey; @@ -438,7 +454,7 @@ where &self, ephemeral_secret_key: &SecretKey, public_spend_key: &PublicKey, - asset: &Asset, + asset: &Asset, ) -> Utxo { self.utxo_commitment .commit(ephemeral_secret_key, public_spend_key, asset, &mut ()) @@ -458,7 +474,7 @@ where &self, secret_spend_key: &SecretKey, ephemeral_secret_key: &SecretKey, - asset: &Asset, + asset: &Asset, utxo: &Utxo, ) -> Option> { (&self.utxo(ephemeral_secret_key, &self.derive(secret_spend_key), asset) == utxo) @@ -639,7 +655,7 @@ where &self, parameters: &Parameters, ephemeral_secret_key: &SecretKey, - asset: &Asset, + asset: &Asset, utxo: &Utxo, ) -> Option> { parameters.check_full_asset(&self.spend, ephemeral_secret_key, asset, utxo) @@ -651,7 +667,7 @@ where &self, parameters: &Parameters, ephemeral_secret_key: SecretKey, - asset: Asset, + asset: Asset, ) -> PreSender { PreSender::new(parameters, self.spend.clone(), ephemeral_secret_key, asset) } @@ -662,7 +678,7 @@ where &self, parameters: &Parameters, ephemeral_secret_key: SecretKey, - asset: Asset, + asset: Asset, ) -> Receiver { self.derive(parameters.key_agreement_scheme()) .into_receiver(parameters, ephemeral_secret_key, asset) @@ -674,9 +690,9 @@ where &self, parameters: &Parameters, ephemeral_secret_key: SecretKey, - asset: Asset, + asset: Asset, ) -> (Receiver, PreSender) { - let receiver = self.receiver(parameters, ephemeral_secret_key.clone(), asset); + let receiver = self.receiver(parameters, ephemeral_secret_key.clone(), asset.clone()); let sender = self.sender(parameters, ephemeral_secret_key, asset); (receiver, sender) } @@ -687,9 +703,9 @@ where &self, parameters: &Parameters, ephemeral_secret_key: SecretKey, - asset_id: AssetId, + asset_id: C::AssetId, ) -> (Receiver, PreSender) { - self.internal_pair(parameters, ephemeral_secret_key, Asset::zero(asset_id)) + self.internal_pair(parameters, ephemeral_secret_key, Asset::::zero(asset_id)) } } @@ -754,7 +770,7 @@ where self, parameters: &Parameters, ephemeral_secret_key: SecretKey, - asset: Asset, + asset: Asset, ) -> Receiver { Receiver::new( parameters, @@ -769,12 +785,12 @@ where /// Note #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "SecretKey: Clone"), - Copy(bound = "SecretKey: Copy"), - Debug(bound = "SecretKey: Debug"), - Eq(bound = "SecretKey: Eq"), - Hash(bound = "SecretKey: Hash"), - PartialEq(bound = "SecretKey: PartialEq") + Clone(bound = "Asset: Clone, SecretKey: Clone"), + Copy(bound = "Asset: Copy, SecretKey: Copy"), + Debug(bound = "Asset: Debug, SecretKey: Debug"), + Eq(bound = "Asset: Eq, SecretKey: Eq"), + Hash(bound = "Asset: Hash, SecretKey: Hash"), + PartialEq(bound = "Asset: PartialEq, SecretKey: PartialEq") )] pub struct Note where @@ -784,7 +800,7 @@ where pub ephemeral_secret_key: SecretKey, /// Asset - pub asset: Asset, + pub asset: Asset, } impl Note @@ -793,7 +809,7 @@ where { /// Builds a new plaintext [`Note`] from `ephemeral_secret_key` and `asset`. #[inline] - pub fn new(ephemeral_secret_key: SecretKey, asset: Asset) -> Self { + pub fn new(ephemeral_secret_key: SecretKey, asset: Asset) -> Self { Self { ephemeral_secret_key, asset, @@ -805,7 +821,7 @@ impl Sample<(SD, AD)> for Note where C: Configuration, SecretKey: Sample, - Asset: Sample, + Asset: Sample, { #[inline] fn sample(distribution: (SD, AD), rng: &mut R) -> Self @@ -819,12 +835,12 @@ where } } -impl SizeLimit for Note -where - C: Configuration, -{ - const SIZE: usize = SecretKey::::SIZE + Asset::SIZE; -} +// impl SizeLimit for Note +// where +// C: Configuration, +// { +// const SIZE: usize = SecretKey::::SIZE + Asset::SIZE; +// } /// Transfer pub struct Transfer< @@ -837,10 +853,10 @@ pub struct Transfer< C: Configuration, { /// Asset Id - asset_id: Option, + asset_id: Option, /// Sources - sources: [AssetValue; SOURCES], + sources: [C::AssetValue; SOURCES], /// Senders senders: [Sender; SENDERS], @@ -849,7 +865,7 @@ pub struct Transfer< receivers: [Receiver; RECEIVERS], /// Sinks - sinks: [AssetValue; SINKS], + sinks: [C::AssetValue; SINKS], } impl @@ -860,11 +876,11 @@ where /// Builds a new [`Transfer`]. #[inline] pub fn new( - asset_id: impl Into>, - sources: [AssetValue; SOURCES], + asset_id: impl Into>, + sources: [C::AssetValue; SOURCES], senders: [Sender; SENDERS], receivers: [Receiver; RECEIVERS], - sinks: [AssetValue; SINKS], + sinks: [C::AssetValue; SINKS], ) -> Self { let asset_id = asset_id.into(); Self::check_shape(asset_id.is_some()); @@ -875,8 +891,8 @@ where #[inline] pub fn generate_proof_input(&self) -> ProofInput { let mut input = Default::default(); - if let Some(asset_id) = self.asset_id { - C::ProofSystem::extend(&mut input, &asset_id); + if let Some(asset_id) = &self.asset_id { + C::ProofSystem::extend(&mut input, asset_id); } self.sources .iter() @@ -941,11 +957,11 @@ where /// output sides. #[inline] fn new_unchecked( - asset_id: Option, - sources: [AssetValue; SOURCES], + asset_id: Option, + sources: [C::AssetValue; SOURCES], senders: [Sender; SENDERS], receivers: [Receiver; RECEIVERS], - sinks: [AssetValue; SINKS], + sinks: [C::AssetValue; SINKS], ) -> Self { Self { asset_id, @@ -1115,7 +1131,10 @@ where #[inline] fn new_known(this: &Self::Type, compiler: &mut C::Compiler) -> Self { Self { - asset_id: this.asset_id.map(|id| id.as_known::(compiler)), + asset_id: this + .asset_id + .as_ref() + .map(|id| id.as_known::(compiler)), sources: this .sources .iter() @@ -1195,7 +1214,7 @@ where /// /// This type must be restricted so that it can only be constructed by this implementation of /// [`TransferLedger`]. - type ValidSourceAccount: AsRef; + type ValidSourceAccount: AsRef; /// Valid [`AssetValue`] for [`TransferPost`] Sink /// @@ -1203,7 +1222,7 @@ where /// /// This type must be restricted so that it can only be constructed by this implementation of /// [`TransferLedger`]. - type ValidSinkAccount: AsRef; + type ValidSinkAccount: AsRef; /// Valid [`Proof`] Posting Key /// @@ -1225,25 +1244,25 @@ where /// amount given in `sources`. fn check_source_accounts( &self, - asset_id: AssetId, + asset_id: C::AssetId, sources: I, - ) -> Result, InvalidSourceAccount> + ) -> Result, InvalidSourceAccount> where - I: Iterator; + I: Iterator; /// Checks that the sink accounts exist and balance can be increased by the specified amounts. fn check_sink_accounts( &self, - asset_id: AssetId, + asset_id: C::AssetId, sinks: I, - ) -> Result, InvalidSinkAccount> + ) -> Result, InvalidSinkAccount> where - I: Iterator; + I: Iterator; /// Checks that the transfer `proof` is valid. fn is_valid( &self, - asset_id: Option, + asset_id: Option, sources: &[SourcePostingKey], senders: &[SenderPostingKey], receivers: &[ReceiverPostingKey], @@ -1260,7 +1279,7 @@ where /// [`is_valid`](Self::is_valid) for more. fn update_public_balances( &mut self, - asset_id: AssetId, + asset_id: C::AssetId, sources: Vec>, sinks: Vec>, proof: Self::ValidProof, @@ -1287,15 +1306,18 @@ pub type TransferLedgerSuperPostingKey = >::SuperPo serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub struct InvalidSourceAccount { +pub struct InvalidSourceAccount +where + C: Configuration, +{ /// Account Id pub account_id: AccountId, /// Asset Id - pub asset_id: AssetId, + pub asset_id: C::AssetId, /// Amount Attempting to Withdraw - pub withdraw: AssetValue, + pub withdraw: C::AssetValue, } /// Invalid Sink Accounts @@ -1308,15 +1330,18 @@ pub struct InvalidSourceAccount { serde(crate = "manta_util::serde", deny_unknown_fields) )] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub struct InvalidSinkAccount { +pub struct InvalidSinkAccount +where + C: Configuration, +{ /// Account Id pub account_id: AccountId, /// Asset Id - pub asset_id: AssetId, + pub asset_id: C::AssetId, /// Amount Attempting to Deposit - pub deposit: AssetValue, + pub deposit: C::AssetValue, } /// Transfer Post Error @@ -1326,18 +1351,45 @@ pub struct InvalidSinkAccount { #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde(crate = "manta_util::serde", deny_unknown_fields) + serde( + bound( + deserialize = "InvalidSourceAccount: Deserialize<'de>, InvalidSinkAccount: Deserialize<'de>, UpdateError: Deserialize<'de>", + serialize = "InvalidSourceAccount: Serialize, InvalidSinkAccount: Serialize, UpdateError: Serialize" + ), + crate = "manta_util::serde", + deny_unknown_fields + ) )] -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum TransferPostError { +#[derive(derivative::Derivative)] +#[derivative( + Clone( + bound = "InvalidSourceAccount: Clone, InvalidSinkAccount: Clone, UpdateError: Clone" + ), + Debug( + bound = "InvalidSourceAccount: Debug, InvalidSinkAccount: Debug, UpdateError: Debug" + ), + Eq( + bound = "InvalidSourceAccount: Eq, InvalidSinkAccount: Eq, UpdateError: Eq" + ), + Hash( + bound = "InvalidSourceAccount: Hash, InvalidSinkAccount: Hash, UpdateError: Hash" + ), + PartialEq( + bound = "InvalidSourceAccount: PartialEq, InvalidSinkAccount: PartialEq, UpdateError: PartialEq" + ) +)] +pub enum TransferPostError +where + C: Configuration, +{ /// Invalid Transfer Post Shape InvalidShape, /// Invalid Source Accounts - InvalidSourceAccount(InvalidSourceAccount), + InvalidSourceAccount(InvalidSourceAccount), /// Invalid Sink Accounts - InvalidSinkAccount(InvalidSinkAccount), + InvalidSinkAccount(InvalidSinkAccount), /// Sender Post Error Sender(SenderPostError), @@ -1362,32 +1414,44 @@ pub enum TransferPostError { UpdateError(UpdateError), } -impl From> - for TransferPostError +impl From> + for TransferPostError +where + C: Configuration, { #[inline] - fn from(err: InvalidSourceAccount) -> Self { + fn from(err: InvalidSourceAccount) -> Self { Self::InvalidSourceAccount(err) } } -impl From> - for TransferPostError +impl From> + for TransferPostError +where + C: Configuration, { #[inline] - fn from(err: InvalidSinkAccount) -> Self { + fn from(err: InvalidSinkAccount) -> Self { Self::InvalidSinkAccount(err) } } -impl From for TransferPostError { +impl From + for TransferPostError +where + C: Configuration, +{ #[inline] fn from(err: SenderPostError) -> Self { Self::Sender(err) } } -impl From for TransferPostError { +impl From + for TransferPostError +where + C: Configuration, +{ #[inline] fn from(err: ReceiverPostError) -> Self { Self::Receiver(err) @@ -1401,11 +1465,15 @@ impl From for TransferPostError, + C::AssetValue: Deserialize<'de>, SenderPost: Deserialize<'de>, ReceiverPost: Deserialize<'de>, Proof: Deserialize<'de>, ", serialize = r" + C::AssetId: Serialize, + C::AssetValue: Serialize, SenderPost: Serialize, ReceiverPost: Serialize, Proof: Serialize, @@ -1417,21 +1485,31 @@ impl From for TransferPostError where C: Configuration, { /// Asset Id - pub asset_id: Option, + pub asset_id: Option, /// Sources - pub sources: Vec, + pub sources: Vec, /// Sender Posts pub sender_posts: Vec>, @@ -1440,7 +1518,7 @@ where pub receiver_posts: Vec>, /// Sinks - pub sinks: Vec, + pub sinks: Vec, /// Validity Proof pub validity_proof: Proof, @@ -1454,8 +1532,8 @@ where #[inline] pub fn generate_proof_input(&self) -> ProofInput { let mut input = Default::default(); - if let Some(asset_id) = self.asset_id { - C::ProofSystem::extend(&mut input, &asset_id); + if let Some(asset_id) = &self.asset_id { + C::ProofSystem::extend(&mut input, asset_id); } self.sources .iter() @@ -1490,15 +1568,15 @@ where #[allow(clippy::type_complexity)] // FIXME: Use a better abstraction for this. #[inline] fn check_public_participants( - asset_id: Option, + asset_id: Option, source_accounts: Vec, - source_values: Vec, + source_values: Vec, sink_accounts: Vec, - sink_values: Vec, + sink_values: Vec, ledger: &L, ) -> Result< (Vec, Vec), - TransferPostError, + TransferPostError, > where L: TransferLedger, @@ -1516,7 +1594,7 @@ where } let sources = if sources > 0 { ledger.check_source_accounts( - asset_id.unwrap(), + asset_id.clone().unwrap(), source_accounts.into_iter().zip(source_values), )? } else { @@ -1534,18 +1612,19 @@ where } /// Validates `self` on the transfer `ledger`. + #[allow(clippy::type_complexity)] #[inline] pub fn validate( self, source_accounts: Vec, sink_accounts: Vec, ledger: &L, - ) -> Result, TransferPostError> + ) -> Result, TransferPostError> where L: TransferLedger, { let (source_posting_keys, sink_posting_keys) = Self::check_public_participants( - self.asset_id, + self.asset_id.clone(), source_accounts, self.sources, sink_accounts, @@ -1583,7 +1662,7 @@ where .map(move |r| r.validate(ledger)) .collect::, _>>()?; let (validity_proof, event) = match ledger.is_valid( - self.asset_id, + self.asset_id.clone(), &source_posting_keys, &sender_posting_keys, &receiver_posting_keys, @@ -1613,7 +1692,7 @@ where sink_accounts: Vec, super_key: &TransferLedgerSuperPostingKey, ledger: &mut L, - ) -> Result> + ) -> Result> where L: TransferLedger, { @@ -1630,7 +1709,7 @@ where L: TransferLedger, { /// Asset Id - asset_id: Option, + asset_id: Option, /// Source Posting Keys source_posting_keys: Vec>, @@ -1659,7 +1738,7 @@ where /// Generates the public input for the [`Transfer`] validation proof. #[inline] pub fn generate_proof_input( - asset_id: Option, + asset_id: Option, sources: &[SourcePostingKey], senders: &[SenderPostingKey], receivers: &[ReceiverPostingKey], diff --git a/manta-accounting/src/transfer/receiver.rs b/manta-accounting/src/transfer/receiver.rs index c7b4a813a..875470043 100644 --- a/manta-accounting/src/transfer/receiver.rs +++ b/manta-accounting/src/transfer/receiver.rs @@ -16,19 +16,16 @@ //! Transfer Receiver -use crate::{ - asset::Asset, - transfer::{ - AssetVar, Configuration, EncryptedNote, FullParametersVar, Note, Parameters, ProofInput, - PublicKey, PublicKeyVar, SecretKey, SecretKeyVar, Utxo, UtxoVar, - }, +use crate::transfer::{ + Asset, AssetVar, Configuration, EncryptedNote, FullParametersVar, Note, Parameters, ProofInput, + PublicKey, PublicKeyVar, SecretKey, SecretKeyVar, Utxo, UtxoVar, }; use core::{fmt::Debug, hash::Hash, iter}; use manta_crypto::{ constraint::HasInput, eclair::{ alloc::{ - mode::{Derived, Public}, + mode::{Derived, Public, Secret}, Allocate, Allocator, Variable, }, bool::AssertEq, @@ -51,7 +48,7 @@ where ephemeral_secret_key: SecretKey, /// Asset - asset: Asset, + asset: Asset, /// Unspent Transaction Output utxo: Utxo, @@ -72,7 +69,7 @@ where public_spend_key: PublicKey, public_view_key: PublicKey, ephemeral_secret_key: SecretKey, - asset: Asset, + asset: Asset, ) -> Self { let randomness = hybrid::Randomness::from_key(ephemeral_secret_key); Self { @@ -81,7 +78,7 @@ where &public_view_key, &randomness, (), - &Note::new(randomness.ephemeral_secret_key.clone(), asset), + &Note::new(randomness.ephemeral_secret_key.clone(), asset.clone()), &mut (), ), ephemeral_secret_key: randomness.ephemeral_secret_key, @@ -163,7 +160,7 @@ where Self { ephemeral_secret_key: this.ephemeral_secret_key.as_known(compiler), public_spend_key: this.public_spend_key.as_known(compiler), - asset: this.asset.as_known(compiler), + asset: this.asset.as_known::>(compiler), utxo: this.utxo.as_known::(compiler), } } @@ -173,7 +170,7 @@ where Self { ephemeral_secret_key: compiler.allocate_unknown(), public_spend_key: compiler.allocate_unknown(), - asset: compiler.allocate_unknown(), + asset: compiler.allocate_unknown::>(), utxo: compiler.allocate_unknown::(), } } diff --git a/manta-accounting/src/transfer/sender.rs b/manta-accounting/src/transfer/sender.rs index 7a4ba0ffd..d7e10ecf6 100644 --- a/manta-accounting/src/transfer/sender.rs +++ b/manta-accounting/src/transfer/sender.rs @@ -16,20 +16,20 @@ //! Transfer Sender -use crate::{ - asset::{Asset, AssetValue}, - transfer::{ - AssetVar, Configuration, FullParametersVar, Parameters, ProofInput, SecretKey, - SecretKeyVar, Utxo, UtxoAccumulatorOutput, UtxoMembershipProof, UtxoMembershipProofVar, - VoidNumber, VoidNumberVar, - }, +use crate::transfer::{ + Asset, AssetVar, Configuration, FullParametersVar, Parameters, ProofInput, SecretKey, + SecretKeyVar, Utxo, UtxoAccumulatorOutput, UtxoMembershipProof, UtxoMembershipProofVar, + VoidNumber, VoidNumberVar, }; use core::{fmt::Debug, hash::Hash, iter}; use manta_crypto::{ accumulator::Accumulator, constraint::HasInput, eclair::{ - alloc::{mode::Derived, Allocate, Allocator, Variable}, + alloc::{ + mode::{Derived, Secret}, + Allocate, Allocator, Variable, + }, bool::AssertEq, }, }; @@ -49,7 +49,7 @@ where ephemeral_secret_key: SecretKey, /// Asset - asset: Asset, + asset: Asset, /// Unspent Transaction Output utxo: Utxo, @@ -69,7 +69,7 @@ where parameters: &Parameters, secret_spend_key: SecretKey, ephemeral_secret_key: SecretKey, - asset: Asset, + asset: Asset, ) -> Self { let utxo = parameters.utxo( &ephemeral_secret_key, @@ -179,7 +179,7 @@ where ephemeral_secret_key: SecretKey, /// Asset - asset: Asset, + asset: Asset, /// Unspent Transaction Output utxo: Utxo, @@ -197,8 +197,8 @@ where { /// Returns the asset value sent by `self` in the transaction. #[inline] - pub fn asset_value(&self) -> AssetValue { - self.asset.value + pub fn asset_value(&self) -> C::AssetValue { + self.asset.value.clone() } /// Reverts `self` back into a [`PreSender`]. @@ -294,7 +294,7 @@ where Self { secret_spend_key: this.secret_spend_key.as_known(compiler), ephemeral_secret_key: this.ephemeral_secret_key.as_known(compiler), - asset: this.asset.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), } @@ -305,7 +305,7 @@ where Self { secret_spend_key: compiler.allocate_unknown(), ephemeral_secret_key: compiler.allocate_unknown(), - asset: compiler.allocate_unknown(), + asset: compiler.allocate_unknown::>(), utxo_membership_proof: compiler.allocate_unknown(), void_number: compiler.allocate_unknown(), } diff --git a/manta-accounting/src/transfer/test.rs b/manta-accounting/src/transfer/test.rs index f1c373269..63144e8f0 100644 --- a/manta-accounting/src/transfer/test.rs +++ b/manta-accounting/src/transfer/test.rs @@ -17,15 +17,15 @@ //! Transfer Protocol Testing Framework use crate::{ - asset::{Asset, AssetId, AssetValue, AssetValueType}, + asset, transfer::{ - canonical::Mint, has_public_participants, Configuration, FullParameters, Parameters, + canonical::Mint, has_public_participants, Asset, Configuration, FullParameters, Parameters, PreSender, Proof, ProofSystemError, ProofSystemPublicParameters, ProvingContext, Receiver, Sender, SpendingKey, Transfer, TransferPost, Utxo, VerifyingContext, }, }; use alloc::vec::Vec; -use core::fmt::Debug; +use core::{fmt::Debug, ops::Sub}; use manta_crypto::{ accumulator::Accumulator, constraint::ProofSystem, @@ -35,6 +35,22 @@ use manta_util::into_array_unchecked; use super::ProofInput; +/// Asset Value Type +type AssetValueType = u128; + +/// Asset Value +#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)] +pub struct AssetValue(pub AssetValueType); + +impl Sub for AssetValue { + type Output = Self; + + #[inline] + fn sub(self, rhs: Self) -> Self::Output { + Self(self.0 - rhs.0) + } +} + /// Samples a distribution over `count`-many values summing to `total`. /// /// # Warning @@ -155,25 +171,27 @@ where pub base: TransferDistribution<'p, C, A>, /// Asset Id for this Transfer - pub asset_id: AssetId, + pub asset_id: C::AssetId, /// Source Asset Value Sum - pub source_sum: AssetValue, + pub source_sum: C::AssetValue, /// Sender Asset Value Sum - pub sender_sum: AssetValue, + pub sender_sum: C::AssetValue, /// Receiver Asset Value Sum - pub receiver_sum: AssetValue, + pub receiver_sum: C::AssetValue, /// Sink Asset Value Sum - pub sink_sum: AssetValue, + pub sink_sum: C::AssetValue, } impl Transfer where - C: Configuration, + C: Configuration, + C::AssetId: Sample, + C::AssetValue: Sample, { /// Samples a [`TransferPost`] from `parameters` and `utxo_accumulator` using `proving_context` /// and `rng`. @@ -305,9 +323,9 @@ where #[inline] fn sample_senders_and_receivers( parameters: &Parameters, - asset_id: AssetId, - senders: &[AssetValue], - receivers: &[AssetValue], + asset_id: C::AssetId, + senders: &[C::AssetValue], + receivers: &[C::AssetValue], utxo_accumulator: &mut A, rng: &mut R, ) -> (Vec>, Vec>) @@ -320,7 +338,15 @@ where senders .iter() .map(|v| { - let sender = PreSender::new(parameters, rng.gen(), rng.gen(), asset_id.with(*v)); + let sender = PreSender::new( + parameters, + rng.gen(), + rng.gen(), + asset::Asset { + id: asset_id.clone(), + value: v.clone(), + }, + ); sender.insert_utxo(utxo_accumulator); sender.try_upgrade(utxo_accumulator).unwrap() }) @@ -333,7 +359,10 @@ where parameters.derive(&rng.gen()), parameters.derive(&rng.gen()), rng.gen(), - asset_id.with(*v), + asset::Asset { + id: asset_id.clone(), + value: v.clone(), + }, ) }) .collect(), @@ -349,7 +378,9 @@ impl< const SINKS: usize, > Sample> for Transfer where - C: Configuration, + C: Configuration, + C::AssetId: Sample, + C::AssetValue: Sample, A: Accumulator, Model = C::UtxoAccumulatorModel>, { #[inline] @@ -357,14 +388,14 @@ where where R: RngCore + ?Sized, { - let asset = Asset::gen(rng); + 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( distribution.parameters, - asset.id, + asset.id.clone(), &secret_input, &output, distribution.utxo_accumulator, @@ -390,7 +421,7 @@ impl< > Sample> for Transfer where - C: Configuration, + C: Configuration, A: Accumulator, Model = C::UtxoAccumulatorModel>, { #[inline] @@ -400,7 +431,7 @@ where { let (senders, receivers) = sample_senders_and_receivers( distribution.base.parameters, - distribution.asset_id, + distribution.asset_id.clone(), &value_distribution(SENDERS, distribution.sender_sum, rng), &value_distribution(RECEIVERS, distribution.receiver_sum, rng), distribution.base.utxo_accumulator, @@ -423,7 +454,7 @@ pub fn sample_mint( proving_context: &ProvingContext, full_parameters: FullParameters, spending_key: &SpendingKey, - asset: Asset, + asset: Asset, rng: &mut R, ) -> Result<(TransferPost, PreSender), ProofSystemError> where From 20a84dade56cef7e800a6e928d5ad69d5c1987d9 Mon Sep 17 00:00:00 2001 From: Francisco Hernandez Iglesias Date: Wed, 16 Nov 2022 12:53:48 +0100 Subject: [PATCH 11/20] wallet compiles Signed-off-by: Francisco Hernandez Iglesias --- manta-accounting/src/lib.rs | 2 +- manta-accounting/src/transfer/mod.rs | 4 +- manta-accounting/src/transfer/sender.rs | 2 +- manta-accounting/src/transfer/test.rs | 4 +- manta-accounting/src/wallet/balance.rs | 170 ++++++++++++++---------- manta-accounting/src/wallet/mod.rs | 36 ++--- manta-accounting/src/wallet/signer.rs | 159 ++++++++++++++-------- manta-accounting/src/wallet/test/mod.rs | 100 +++++++++----- 8 files changed, 299 insertions(+), 178 deletions(-) diff --git a/manta-accounting/src/lib.rs b/manta-accounting/src/lib.rs index 957658c26..9a8fe02ee 100644 --- a/manta-accounting/src/lib.rs +++ b/manta-accounting/src/lib.rs @@ -35,7 +35,7 @@ extern crate derive_more; pub mod asset; pub mod key; pub mod transfer; -//pub mod wallet; +pub mod wallet; #[cfg(feature = "fs")] #[cfg_attr(doc_cfg, doc(cfg(feature = "fs")))] diff --git a/manta-accounting/src/transfer/mod.rs b/manta-accounting/src/transfer/mod.rs index 4e622a284..87fa4dc90 100644 --- a/manta-accounting/src/transfer/mod.rs +++ b/manta-accounting/src/transfer/mod.rs @@ -126,10 +126,10 @@ pub trait VoidNumberCommitmentScheme { /// Transfer Configuration pub trait Configuration { /// Asset Id Type - type AssetId: Clone + Ord; + type AssetId: Clone + Hash + Ord; // Hash /// Asset Value Type - type AssetValue: AddAssign + Clone + Default + PartialOrd + Sum; + type AssetValue: AddAssign + Clone + Copy + Default + PartialOrd + Sum; // Copy /// Secret Key Type type SecretKey: Clone + Sample + SizeLimit; diff --git a/manta-accounting/src/transfer/sender.rs b/manta-accounting/src/transfer/sender.rs index d7e10ecf6..1fc941fc7 100644 --- a/manta-accounting/src/transfer/sender.rs +++ b/manta-accounting/src/transfer/sender.rs @@ -198,7 +198,7 @@ where /// Returns the asset value sent by `self` in the transaction. #[inline] pub fn asset_value(&self) -> C::AssetValue { - self.asset.value.clone() + self.asset.value } /// Reverts `self` back into a [`PreSender`]. diff --git a/manta-accounting/src/transfer/test.rs b/manta-accounting/src/transfer/test.rs index 63144e8f0..7e4a891a6 100644 --- a/manta-accounting/src/transfer/test.rs +++ b/manta-accounting/src/transfer/test.rs @@ -344,7 +344,7 @@ where rng.gen(), asset::Asset { id: asset_id.clone(), - value: v.clone(), + value: *v, }, ); sender.insert_utxo(utxo_accumulator); @@ -361,7 +361,7 @@ where rng.gen(), asset::Asset { id: asset_id.clone(), - value: v.clone(), + value: *v, }, ) }) diff --git a/manta-accounting/src/wallet/balance.rs b/manta-accounting/src/wallet/balance.rs index a72f4f9db..b4242eda7 100644 --- a/manta-accounting/src/wallet/balance.rs +++ b/manta-accounting/src/wallet/balance.rs @@ -20,8 +20,13 @@ //! protocol. Applications which define balances beyond fungible assets should extend these //! abstractions. -use crate::asset::{Asset, AssetId, AssetList, AssetValue}; +use crate::{ + asset::{self, AssetList}, + transfer::{Asset, Configuration}, +}; use alloc::collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}; +use core::fmt::Debug; +use manta_util::num::CheckedSub; #[cfg(feature = "std")] use std::{ @@ -30,38 +35,41 @@ use std::{ }; /// Balance State -pub trait BalanceState: Default { +pub trait BalanceState: Default +where + C: Configuration, +{ /// Returns the current balance associated with this `id`. - fn balance(&self, id: AssetId) -> AssetValue; + fn balance(&self, id: C::AssetId) -> C::AssetValue; /// Returns true if `self` contains at least `asset.value` of the asset of kind `asset.id`. #[inline] - fn contains(&self, asset: Asset) -> bool { + 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); + fn deposit(&mut self, asset: Asset); /// Deposits every asset in `assets` into the balance state. #[inline] fn deposit_all(&mut self, assets: I) where - I: IntoIterator, + I: IntoIterator>, { 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; + 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(&mut self, assets: I) -> bool where - I: IntoIterator, + I: IntoIterator>, { for asset in AssetList::from_iter(assets) { if !self.withdraw(asset) { @@ -75,20 +83,24 @@ pub trait BalanceState: Default { fn clear(&mut self); } -impl BalanceState for AssetList { +impl BalanceState for AssetList +where + C: Configuration, + for<'v> &'v C::AssetValue: CheckedSub, +{ #[inline] - fn balance(&self, id: AssetId) -> AssetValue { - self.value(id) + fn balance(&self, id: C::AssetId) -> C::AssetValue { + self.value(&id) } #[inline] - fn deposit(&mut self, asset: Asset) { + fn deposit(&mut self, asset: Asset) { self.deposit(asset); } #[inline] - fn withdraw(&mut self, asset: Asset) -> bool { - self.withdraw(asset) + fn withdraw(&mut self, asset: Asset) -> bool { + self.withdraw(&asset) } #[inline] @@ -99,14 +111,14 @@ impl BalanceState for AssetList { /// Adds implementation of [`BalanceState`] for a map type with the given `$entry` type. macro_rules! impl_balance_state_map_body { - ($entry:tt) => { + ($entry:tt, $config: ty) => { #[inline] - fn balance(&self, id: AssetId) -> AssetValue { + fn balance(&self, id: <$config>::AssetId) -> <$config>::AssetValue { self.get(&id).copied().unwrap_or_default() } #[inline] - fn deposit(&mut self, asset: Asset) { + fn deposit(&mut self, asset: Asset<$config>) { if asset.is_zero() { return; } @@ -119,12 +131,12 @@ macro_rules! impl_balance_state_map_body { } #[inline] - fn withdraw(&mut self, asset: Asset) -> bool { + fn withdraw(&mut self, asset: Asset<$config>) -> bool { if !asset.is_zero() { if let $entry::Occupied(mut entry) = self.entry(asset.id) { let balance = entry.get_mut(); if let Some(next_balance) = balance.checked_sub(asset.value) { - if next_balance == 0 { + if next_balance == Default::default() { entry.remove(); } else { *balance = next_balance; @@ -146,30 +158,40 @@ macro_rules! impl_balance_state_map_body { } /// B-Tree Map [`BalanceState`] Implementation -pub type BTreeMapBalanceState = BTreeMap; +pub type BTreeMapBalanceState = + BTreeMap<::AssetId, ::AssetValue>; -impl BalanceState for BTreeMapBalanceState { - impl_balance_state_map_body! { BTreeMapEntry } +impl BalanceState for BTreeMapBalanceState +where + C: Configuration, + C::AssetValue: CheckedSub, +{ + impl_balance_state_map_body! { BTreeMapEntry, C } } /// Hash Map [`BalanceState`] Implementation #[cfg(feature = "std")] #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] -pub type HashMapBalanceState = HashMap; +pub type HashMapBalanceState = + HashMap<::AssetId, ::AssetValue, S>; #[cfg(feature = "std")] #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] -impl BalanceState for HashMapBalanceState +impl BalanceState for HashMapBalanceState where + C: Configuration, S: BuildHasher + Default, + C::AssetValue: CheckedSub, { - impl_balance_state_map_body! { HashMapEntry } + impl_balance_state_map_body! { HashMapEntry, C } } /// Testing Framework #[cfg(any(feature = "test", test))] #[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] pub mod test { + use core::ops::Add; + use super::*; use manta_crypto::rand::{CryptoRng, RngCore, Sample}; @@ -178,20 +200,23 @@ pub mod test { /// Asserts that a random deposit and withdraw is always valid. #[inline] - pub fn assert_valid_withdraw(state: &mut S, rng: &mut R) + pub fn assert_valid_withdraw(state: &mut S, rng: &mut R) where - S: BalanceState, + C: Configuration, + S: BalanceState, R: CryptoRng + RngCore + ?Sized, + C::AssetId: Sample, + C::AssetValue: Add + Debug + Sample, { - let asset = Asset::gen(rng); - let initial_balance = state.balance(asset.id); - state.deposit(asset); + let asset = Asset::::gen(rng); + let initial_balance = state.balance(asset.clone().id); + state.deposit(asset.clone()); assert_eq!( initial_balance + asset.value, - state.balance(asset.id), + state.balance(asset.clone().id), "Current balance and sum of initial balance and new deposit should have been equal." ); - state.withdraw(asset); + state.withdraw(asset.clone()); assert_eq!( initial_balance, state.balance(asset.id), @@ -202,27 +227,33 @@ pub mod test { /// Asserts that a maximal withdraw that leaves the state with no value should delete its memory /// for this process. #[inline] - pub fn assert_full_withdraw_should_remove_entry(rng: &mut R) + pub fn assert_full_withdraw_should_remove_entry(rng: &mut R) where - S: BalanceState, + C: Configuration, + C::AssetId: Sample, + C::AssetValue: Sample + Debug, + S: BalanceState, for<'s> &'s S: IntoIterator, for<'s> <&'s S as IntoIterator>::IntoIter: ExactSizeIterator, R: CryptoRng + RngCore + ?Sized, { let mut state = S::default(); - let asset = Asset::gen(rng); + let asset = Asset::::gen(rng); let initial_length = state.into_iter().len(); - state.deposit(asset); + state.deposit(asset.clone()); assert_eq!( initial_length + 1, state.into_iter().len(), "Length should have increased by one after depositing a new asset." ); - let balance = state.balance(asset.id); - state.withdraw(asset.id.with(balance)); + let balance = state.balance(asset.clone().id); + state.withdraw(asset::Asset { + id: asset.clone().id, + value: balance, + }); assert_eq!( state.balance(asset.id), - 0, + Default::default(), "Balance in the removed AssetId should be zero." ); assert_eq!( @@ -236,6 +267,7 @@ pub mod test { macro_rules! define_tests { ($(( $type:ty, + $config: ty, $doc:expr, $valid_withdraw:ident, $full_withdraw:ident @@ -249,7 +281,7 @@ pub mod test { let mut state = <$type>::default(); let mut rng = OsRng; for _ in 0..0xFFFF { - assert_valid_withdraw(&mut state, &mut rng); + assert_valid_withdraw::<$config, _, _>(&mut state, &mut rng); } } @@ -258,38 +290,40 @@ pub mod test { #[doc = "with no value stored in them."] #[test] fn $full_withdraw() { - assert_full_withdraw_should_remove_entry::<$type, _>(&mut OsRng); + assert_full_withdraw_should_remove_entry::<$config, $type, _>(&mut OsRng); } )* } } - define_tests!( - ( - AssetList, - "[`AssetList`]", - asset_list_valid_withdraw, - asset_list_full_withdraw, - ), - ( - BTreeMapBalanceState, - "[`BTreeMapBalanceState`]", - btree_map_valid_withdraw, - btree_map_full_withdraw, - ), - ); - - /// Tests valid withdrawals for a [`HashMapBalanceState`] balance state. - #[cfg(feature = "std")] - #[test] - fn hash_map_valid_withdraw() { - assert_valid_withdraw(&mut HashMapBalanceState::new(), &mut OsRng); - } + // Note: the following tests cannot be defined until we specify a transfer configuration. - /// - #[cfg(feature = "std")] - #[test] - fn hash_map_full_withdraw() { - assert_full_withdraw_should_remove_entry::(&mut OsRng); - } + // define_tests!( + // ( + // AssetList, + // "[`AssetList`]", + // asset_list_valid_withdraw, + // asset_list_full_withdraw, + // ), + // ( + // BTreeMapBalanceState, + // "[`BTreeMapBalanceState`]", + // btree_map_valid_withdraw, + // btree_map_full_withdraw, + // ), + // ); + + // /// Tests valid withdrawals for a [`HashMapBalanceState`] balance state. + // #[cfg(feature = "std")] + // #[test] + // fn hash_map_valid_withdraw() { + // assert_valid_withdraw(&mut HashMapBalanceState::new(), &mut OsRng); + // } + + // /// + // #[cfg(feature = "std")] + // #[test] + // fn hash_map_full_withdraw() { + // assert_full_withdraw_should_remove_entry::(&mut OsRng); + // } } diff --git a/manta-accounting/src/wallet/mod.rs b/manta-accounting/src/wallet/mod.rs index 9ce4a6d6d..bf6663299 100644 --- a/manta-accounting/src/wallet/mod.rs +++ b/manta-accounting/src/wallet/mod.rs @@ -28,10 +28,10 @@ //! [`Ledger`]: ledger::Connection use crate::{ - asset::{Asset, AssetId, AssetList, AssetMetadata, AssetValue}, + asset::{AssetList, AssetMetadata}, transfer::{ canonical::{Transaction, TransactionKind}, - Configuration, PublicKey, TransferPost, + Asset, Configuration, PublicKey, TransferPost, }, wallet::{ balance::{BTreeMapBalanceState, BalanceState}, @@ -58,12 +58,12 @@ pub mod signer; pub mod test; /// Wallet -pub struct Wallet, B = BTreeMapBalanceState> +pub struct Wallet, B = BTreeMapBalanceState> where C: Configuration, L: ledger::Connection, S: signer::Connection, - B: BalanceState, + B: BalanceState, { /// Ledger Connection ledger: L, @@ -86,7 +86,7 @@ where C: Configuration, L: ledger::Connection, S: signer::Connection, - B: BalanceState, + B: BalanceState, { /// Builds a new [`Wallet`] without checking if `ledger`, `checkpoint`, `signer`, and `assets` /// are properly synchronized. @@ -137,13 +137,13 @@ where /// Returns the current balance associated with this `id`. #[inline] - pub fn balance(&self, id: AssetId) -> AssetValue { + pub fn balance(&self, id: C::AssetId) -> C::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 { + pub fn contains(&self, asset: Asset) -> bool { self.assets.contains(asset) } @@ -152,7 +152,7 @@ where #[inline] pub fn contains_all(&self, assets: I) -> bool where - I: IntoIterator, + I: IntoIterator>, { AssetList::from_iter(assets) .into_iter() @@ -317,7 +317,7 @@ where /// 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) -> Result { + pub fn check(&self, transaction: &Transaction) -> Result, Asset> { transaction.check(move |a| self.contains(a)) } @@ -432,11 +432,13 @@ pub enum InconsistencyError { serde( bound( deserialize = r" + Asset: Deserialize<'de>, SignError: Deserialize<'de>, L::Error: Deserialize<'de>, S::Error: Deserialize<'de> ", serialize = r" + Asset: Serialize, SignError: Serialize, L::Error: Serialize, S::Error: Serialize @@ -448,12 +450,14 @@ pub enum InconsistencyError { )] #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "SignError: Clone, L::Error: Clone, S::Error: Clone"), - Copy(bound = "SignError: Copy, L::Error: Copy, S::Error: Copy"), - Debug(bound = "SignError: Debug, L::Error: Debug, S::Error: Debug"), - Eq(bound = "SignError: Eq, L::Error: Eq, S::Error: Eq"), - Hash(bound = "SignError: Hash, L::Error: Hash, S::Error: Hash"), - PartialEq(bound = "SignError: PartialEq, L::Error: PartialEq, S::Error: PartialEq") + Clone(bound = "Asset: Clone, SignError: Clone, L::Error: Clone, S::Error: Clone"), + Copy(bound = "Asset: Copy, SignError: Copy, L::Error: Copy, S::Error: Copy"), + Debug(bound = "Asset: Debug, SignError: Debug, L::Error: Debug, S::Error: Debug"), + Eq(bound = "Asset: Eq, SignError: Eq, L::Error: Eq, S::Error: Eq"), + Hash(bound = "Asset: Hash, SignError: Hash, L::Error: Hash, S::Error: Hash"), + PartialEq( + bound = "Asset: PartialEq, SignError: PartialEq, L::Error: PartialEq, S::Error: PartialEq" + ) )] pub enum Error where @@ -462,7 +466,7 @@ where S: signer::Connection, { /// Insufficient Balance - InsufficientBalance(Asset), + InsufficientBalance(Asset), /// Inconsistency Error /// diff --git a/manta-accounting/src/wallet/signer.rs b/manta-accounting/src/wallet/signer.rs index 9175d9e8b..6beac4167 100644 --- a/manta-accounting/src/wallet/signer.rs +++ b/manta-accounting/src/wallet/signer.rs @@ -27,7 +27,7 @@ // internally. use crate::{ - asset::{Asset, AssetId, AssetMap, AssetMetadata, AssetValue}, + asset::{self, AssetMap, AssetMetadata}, key::{self, AccountCollection, DeriveAddress, DeriveAddresses}, transfer::{ self, @@ -36,7 +36,7 @@ use crate::{ Mint, MultiProvingContext, PrivateTransfer, PrivateTransferShape, Reclaim, Selection, Shape, Transaction, }, - EncryptedNote, FullParameters, Note, Parameters, PreSender, ProofSystemError, + Asset, EncryptedNote, FullParameters, Note, Parameters, PreSender, ProofSystemError, ProvingContext, PublicKey, Receiver, ReceivingKey, SecretKey, Sender, SpendingKey, Transfer, TransferPost, Utxo, VoidNumber, }, @@ -77,7 +77,7 @@ where fn sync( &mut self, request: SyncRequest, - ) -> LocalBoxFutureResult, Self::Error>; + ) -> LocalBoxFutureResult, Self::Error>; /// Signs a transaction and returns the ledger transfer posts if successful. fn sign( @@ -206,38 +206,70 @@ where #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde(crate = "manta_util::serde", deny_unknown_fields) + serde( + bound( + deserialize = "BalanceUpdate: Deserialize<'de>, T: Deserialize<'de>", + serialize = "BalanceUpdate: Serialize, T: Serialize" + ), + crate = "manta_util::serde", + deny_unknown_fields + ) )] -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub struct SyncResponse +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "BalanceUpdate: Clone"), + Debug(bound = "BalanceUpdate: Debug, T: Debug"), + Eq(bound = "BalanceUpdate: Eq, T: Eq"), + Hash(bound = "BalanceUpdate: Hash, T: Hash"), + PartialEq(bound = "BalanceUpdate: PartialEq, T: PartialEq") +)] +pub struct SyncResponse where + C: transfer::Configuration, T: ledger::Checkpoint, { /// Checkpoint pub checkpoint: T, /// Balance Update - pub balance_update: BalanceUpdate, + pub balance_update: BalanceUpdate, } /// Balance Update #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), - serde(crate = "manta_util::serde", deny_unknown_fields) + serde( + bound( + deserialize = "Asset: Deserialize<'de>", + serialize = "Asset: Serialize" + ), + crate = "manta_util::serde", + deny_unknown_fields + ) )] -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub enum BalanceUpdate { +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "Asset: Clone"), + Debug(bound = "Asset: Debug"), + Eq(bound = "Asset: Eq"), + Hash(bound = "Asset: Hash"), + PartialEq(bound = "Asset: PartialEq") +)] +pub enum BalanceUpdate +where + C: transfer::Configuration, +{ /// Partial Update /// /// This is the typical response from the [`Signer`]. In rare de-synchronization cases, we may /// need to perform a [`Full`](Self::Full) update. Partial { /// Assets Deposited in the Last Update - deposit: Vec, + deposit: Vec>, /// Assets Withdrawn in the Last Update - withdraw: Vec, + withdraw: Vec>, }, /// Full Update @@ -247,7 +279,7 @@ pub enum BalanceUpdate { /// case, the entire balance state needs to be sent to catch up. Full { /// Full Balance State - assets: Vec, + assets: Vec>, }, } @@ -278,7 +310,7 @@ where } /// Synchronization Result -pub type SyncResult = Result, SyncError>; +pub type SyncResult = Result, SyncError>; /// Signer Signing Request /// @@ -367,8 +399,8 @@ where derive(Deserialize, Serialize), serde( bound( - deserialize = "ProofSystemError: Deserialize<'de>", - serialize = "ProofSystemError: Serialize" + deserialize = "Asset: Deserialize<'de>, ProofSystemError: Deserialize<'de>", + serialize = "Asset: Serialize, ProofSystemError: Serialize" ), crate = "manta_util::serde", deny_unknown_fields @@ -376,19 +408,19 @@ where )] #[derive(derivative::Derivative)] #[derivative( - Clone(bound = "ProofSystemError: Clone"), - Copy(bound = "ProofSystemError: Copy"), - Debug(bound = "ProofSystemError: Debug"), - Eq(bound = "ProofSystemError: Eq"), - Hash(bound = "ProofSystemError: Hash"), - PartialEq(bound = "ProofSystemError: PartialEq") + Clone(bound = "Asset: Clone, ProofSystemError: Clone"), + Copy(bound = "Asset: Copy, ProofSystemError: Copy"), + Debug(bound = "Asset: Debug, ProofSystemError: Debug"), + Eq(bound = "Asset: Eq, ProofSystemError: Eq"), + Hash(bound = "Asset: Hash, ProofSystemError: Hash"), + PartialEq(bound = "Asset: PartialEq, ProofSystemError: PartialEq") )] pub enum SignError where C: transfer::Configuration, { /// Insufficient Balance - InsufficientBalance(Asset), + InsufficientBalance(Asset), /// Proof System Error ProofSystemError(ProofSystemError), @@ -441,7 +473,7 @@ pub trait Configuration: transfer::Configuration { + Rollback; /// Asset Map Type - type AssetMap: AssetMap>; + type AssetMap: AssetMap>; /// Random Number Generator Type type Rng: CryptoRng + FromEntropy + RngCore; @@ -588,7 +620,7 @@ where note: Note, key: &SecretKey, void_numbers: &mut Vec>, - deposit: &mut Vec, + deposit: &mut Vec>, ) { if let Some(void_number) = parameters.check_full_asset(key, ¬e.ephemeral_secret_key, ¬e.asset, &utxo) @@ -597,7 +629,7 @@ where void_numbers.remove(index); } else { utxo_accumulator.insert(&utxo); - assets.insert(note.ephemeral_secret_key, note.asset); + assets.insert(note.ephemeral_secret_key, note.asset.clone()); if !note.asset.is_zero() { deposit.push(note.asset); } @@ -615,9 +647,9 @@ where parameters: &Parameters, secret_spend_key: &SecretKey, ephemeral_secret_key: &SecretKey, - asset: Asset, + asset: Asset, void_numbers: &mut Vec>, - withdraw: &mut Vec, + withdraw: &mut Vec>, ) -> bool { let utxo = parameters.utxo( ephemeral_secret_key, @@ -646,7 +678,7 @@ where inserts: I, mut void_numbers: Vec>, is_partial: bool, - ) -> SyncResponse + ) -> SyncResponse where I: Iterator, EncryptedNote)>, { @@ -680,7 +712,7 @@ where parameters, &self.accounts.get_default().spending_key(), ephemeral_secret_key, - *asset, + asset.clone(), &mut void_numbers, &mut withdraw, ) @@ -710,7 +742,7 @@ where &self, parameters: &Parameters, key: SecretKey, - asset: Asset, + asset: Asset, ) -> Result, SignError> { Ok(PreSender::new( parameters, @@ -725,7 +757,7 @@ where fn build_receiver( &mut self, parameters: &Parameters, - asset: Asset, + asset: Asset, ) -> Result, SignError> { let receiving_key = ReceivingKey:: { spend: self.accounts.get_default().address(parameters), @@ -739,9 +771,9 @@ where fn mint_zero( &mut self, parameters: &Parameters, - asset_id: AssetId, + asset_id: C::AssetId, ) -> Result<(Mint, PreSender), SignError> { - let asset = Asset::zero(asset_id); + let asset = Asset::::zero(asset_id); let key = self.accounts.get_default().spending_key(); Ok(Mint::internal_pair( parameters, @@ -756,14 +788,21 @@ where fn select( &mut self, parameters: &Parameters, - asset: Asset, + asset: Asset, ) -> Result, SignError> { - let selection = self.assets.select(asset); + let selection = self.assets.select(&asset); if !asset.is_zero() && selection.is_empty() { return Err(SignError::InsufficientBalance(asset)); } Selection::new(selection, move |k, v| { - self.build_pre_sender(parameters, k, asset.id.with(v)) + self.build_pre_sender( + parameters, + k, + asset::Asset { + id: asset.clone().id, + value: v, + }, + ) }) } @@ -840,13 +879,16 @@ where fn next_join( &mut self, parameters: &Parameters, - asset_id: AssetId, - total: AssetValue, + asset_id: C::AssetId, + total: C::AssetValue, ) -> Result<([Receiver; PrivateTransferShape::RECEIVERS], Join), SignError> { let secret_key = self.accounts.get_default().spending_key(); Ok(Join::new( parameters, - asset_id.with(total), + asset::Asset { + id: asset_id, + value: total, + }, &SpendingKey::new(secret_key.clone(), secret_key), &mut self.rng, )) @@ -858,7 +900,7 @@ where &mut self, parameters: &Parameters, proving_context: &MultiProvingContext, - asset_id: AssetId, + asset_id: C::AssetId, mut new_zeroes: Vec>, pre_senders: &mut Vec>, posts: &mut Vec>, @@ -867,10 +909,11 @@ where if needed_zeroes == 0 { return Ok(()); } - let zeroes = self.assets.zeroes(needed_zeroes, asset_id); + let zeroes = self.assets.zeroes(needed_zeroes, &asset_id); needed_zeroes -= zeroes.len(); for zero in zeroes { - let pre_sender = self.build_pre_sender(parameters, zero, Asset::zero(asset_id))?; + let pre_sender = + self.build_pre_sender(parameters, zero, Asset::::zero(asset_id.clone()))?; pre_senders.push(pre_sender); } if needed_zeroes == 0 { @@ -887,7 +930,7 @@ where return Ok(()); } for _ in 0..needed_mints { - let (mint, pre_sender) = self.mint_zero(parameters, asset_id)?; + let (mint, pre_sender) = self.mint_zero(parameters, asset_id.clone())?; posts.push(self.mint_post(parameters, &proving_context.mint, mint)?); pre_sender.insert_utxo(&mut self.utxo_accumulator); pre_senders.push(pre_sender); @@ -901,7 +944,7 @@ where &mut self, parameters: &Parameters, proving_context: &MultiProvingContext, - asset_id: AssetId, + asset_id: C::AssetId, mut pre_senders: Vec>, posts: &mut Vec>, ) -> Result<[Sender; PrivateTransferShape::SENDERS], SignError> { @@ -918,7 +961,7 @@ where }); let (receivers, mut join) = self.next_join( parameters, - asset_id, + asset_id.clone(), senders.iter().map(Sender::asset_value).sum(), )?; posts.push(self.private_transfer_post( @@ -955,7 +998,7 @@ where fn prepare_receiver( &mut self, parameters: &Parameters, - asset: Asset, + asset: Asset, receiving_key: ReceivingKey, ) -> Receiver { receiving_key.into_receiver(parameters, self.rng.gen(), asset) @@ -1062,7 +1105,7 @@ where pub fn sync( &mut self, mut request: SyncRequest, - ) -> Result, SyncError> { + ) -> Result, SyncError> { // TODO: Do a capacity check on the current UTXO accumulator? // // if self.utxo_accumulator.capacity() < starting_index { @@ -1092,18 +1135,24 @@ where #[inline] fn sign_withdraw( &mut self, - asset: Asset, + asset: Asset, receiver: Option>, ) -> Result, SignError> { - let selection = self.state.select(&self.parameters.parameters, asset)?; - let change = self + let selection = self .state - .build_receiver(&self.parameters.parameters, asset.id.with(selection.change))?; + .select(&self.parameters.parameters, asset.clone())?; + let change = self.state.build_receiver( + &self.parameters.parameters, + asset::Asset { + id: asset.clone().id, + value: selection.change, + }, + )?; let mut posts = Vec::new(); let senders = self.state.compute_batched_transactions( &self.parameters.parameters, &self.parameters.proving_context, - asset.id, + asset.clone().id, selection.pre_senders, &mut posts, )?; @@ -1138,7 +1187,7 @@ where Transaction::Mint(asset) => { let receiver = self .state - .build_receiver(&self.parameters.parameters, asset)?; + .build_receiver(&self.parameters.parameters, asset.clone())?; Ok(SignResponse::new(vec![self.state.mint_post( &self.parameters.parameters, &self.parameters.proving_context.mint, @@ -1182,7 +1231,7 @@ where &mut self, request: SyncRequest, ) -> LocalBoxFutureResult< - Result, SyncError>, + Result, SyncError>, Self::Error, > { Box::pin(async move { Ok(self.sync(request)) }) diff --git a/manta-accounting/src/wallet/test/mod.rs b/manta-accounting/src/wallet/test/mod.rs index e4ce2c217..e382b3fd6 100644 --- a/manta-accounting/src/wallet/test/mod.rs +++ b/manta-accounting/src/wallet/test/mod.rs @@ -20,8 +20,8 @@ // TODO: Generalize `PushResponse` so that we can test against more general wallet setups. use crate::{ - asset::{Asset, AssetList}, - transfer::{self, canonical::Transaction, PublicKey, ReceivingKey, TransferPost}, + asset::{self, AssetList}, + transfer::{self, canonical::Transaction, Asset, PublicKey, ReceivingKey, TransferPost}, wallet::{ ledger, signer::{self, SyncData}, @@ -32,8 +32,8 @@ use alloc::{boxed::Box, sync::Arc, vec::Vec}; use core::{fmt::Debug, future::Future, hash::Hash, marker::PhantomData}; use futures::StreamExt; use indexmap::IndexSet; -use manta_crypto::rand::{CryptoRng, Distribution, Rand, RngCore, Sample}; -use manta_util::future::LocalBoxFuture; +use manta_crypto::rand::{CryptoRng, Distribution, Rand, RngCore, Sample, SampleUniform}; +use manta_util::{future::LocalBoxFuture, num::CheckedSub}; use parking_lot::Mutex; use statrs::{ distribution::{Categorical, Poisson}, @@ -101,14 +101,14 @@ where /// Generates a [`Transaction::Mint`] for `asset`. #[inline] - pub fn mint(asset: Asset) -> Self { + pub fn mint(asset: Asset) -> Self { Self::self_post(false, Transaction::Mint(asset)) } /// Generates a [`Transaction::PrivateTransfer`] for `asset` to `key` self-pointed if `is_self` /// is `true`. #[inline] - pub fn private_transfer(is_self: bool, asset: Asset, key: PublicKey) -> Self { + pub fn private_transfer(is_self: bool, asset: Asset, key: PublicKey) -> Self { let receiving_key = ReceivingKey:: { spend: key.clone(), view: key, @@ -122,7 +122,7 @@ where /// Generates a [`Transaction::Reclaim`] for `asset` which is maximal if `is_maximal` is `true`. #[inline] - pub fn reclaim(is_maximal: bool, asset: Asset) -> Self { + pub fn reclaim(is_maximal: bool, asset: Asset) -> Self { Self::self_post(is_maximal, Transaction::Reclaim(asset)) } @@ -370,9 +370,12 @@ impl Sample for ActionType { } /// Public Balance Oracle -pub trait PublicBalanceOracle { +pub trait PublicBalanceOracle +where + C: transfer::Configuration, +{ /// Returns the public balances of `self`. - fn public_balances(&self) -> LocalBoxFuture>; + fn public_balances(&self) -> LocalBoxFuture>>; } /// Ledger Alias Trait @@ -399,6 +402,7 @@ where C: transfer::Configuration, L: Ledger, S: signer::Connection, + C::AssetValue: CheckedSub, { /// Wallet pub wallet: Wallet, @@ -415,6 +419,7 @@ where C: transfer::Configuration, L: Ledger, S: signer::Connection, + C::AssetValue: CheckedSub + SampleUniform, { /// Builds a new [`Actor`] with `wallet`, `distribution`, and `lifetime`. #[inline] @@ -444,9 +449,11 @@ where /// Returns the latest public balances from the ledger. #[inline] - async fn public_balances(&mut self) -> Result, Error> + async fn public_balances( + &mut self, + ) -> Result>, Error> where - L: PublicBalanceOracle, + L: PublicBalanceOracle, { self.wallet.sync().await?; Ok(self.wallet.ledger().public_balances().await) @@ -460,9 +467,9 @@ where /// Samples a deposit from `self` using `rng` returning `None` if no deposit is possible. #[inline] - async fn sample_deposit(&mut self, rng: &mut R) -> Result, Error> + async fn sample_deposit(&mut self, rng: &mut R) -> Result>, Error> where - L: PublicBalanceOracle, + L: PublicBalanceOracle, R: RngCore + ?Sized, { let assets = match self.public_balances().await? { @@ -470,7 +477,13 @@ where _ => return Ok(None), }; match rng.select_item(assets) { - Some(asset) => Ok(Some(asset.id.sample_up_to(asset.value, rng))), + Some(asset) => { + let value = rng.gen_range(Default::default()..asset.value); + Ok(Some(asset::Asset { + id: asset.id, + value, + })) + } _ => Ok(None), } } @@ -482,13 +495,19 @@ 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(&mut self, rng: &mut R) -> Result, Error> + async fn sample_withdraw(&mut self, rng: &mut R) -> Result>, Error> where R: RngCore + ?Sized, { self.wallet.sync().await?; match rng.select_item(self.wallet.assets()) { - Some((id, value)) => Ok(Some(id.sample_up_to(*value, rng))), + Some((id, value)) => { + let value = rng.gen_range(Default::default()..*value); + Ok(Some(asset::Asset { + id: id.clone(), + value, + })) + } _ => Ok(None), } } @@ -500,14 +519,14 @@ where &mut self, action: ActionType, rng: &mut R, - ) -> Result, ActionLabelledError> + ) -> Result>, ActionLabelledError> where R: RngCore + ?Sized, { self.sync_with(action).await?; Ok(rng .select_item(self.wallet.assets()) - .map(|(id, value)| Asset::new(*id, *value))) + .map(|(id, value)| Asset::::new(id.clone(), *value))) } /// Samples a [`Mint`] against `self` using `rng`, returning a [`Skip`] if [`Mint`] is @@ -518,7 +537,7 @@ where #[inline] async fn sample_mint(&mut self, rng: &mut R) -> MaybeAction where - L: PublicBalanceOracle, + L: PublicBalanceOracle, R: RngCore + ?Sized, { match self.sample_deposit(rng).await { @@ -537,12 +556,12 @@ where #[inline] async fn sample_zero_mint(&mut self, rng: &mut R) -> MaybeAction where - L: PublicBalanceOracle, + L: PublicBalanceOracle, R: RngCore + ?Sized, { match self.public_balances().await { Ok(Some(assets)) => match rng.select_item(assets) { - Some(asset) => Ok(Action::mint(asset.id.value(0))), + Some(asset) => Ok(Action::mint(Asset::::zero(asset.id))), _ => Ok(Action::Skip), }, Ok(_) => Ok(Action::Skip), @@ -564,7 +583,7 @@ where key: K, ) -> MaybeAction where - L: PublicBalanceOracle, + L: PublicBalanceOracle, R: RngCore + ?Sized, K: FnOnce(&mut R) -> Result>, Error>, { @@ -599,7 +618,7 @@ where key: K, ) -> MaybeAction where - L: PublicBalanceOracle, + L: PublicBalanceOracle, R: RngCore + ?Sized, K: FnOnce(&mut R) -> Result>, Error>, { @@ -610,7 +629,11 @@ where }; match self.sample_asset(action, rng).await { Ok(Some(asset)) => match key(rng) { - Ok(Some(key)) => Ok(Action::private_transfer(is_self, asset.id.value(0), key)), + Ok(Some(key)) => Ok(Action::private_transfer( + is_self, + Asset::::zero(asset.id), + key, + )), Ok(_) => Ok(Action::GenerateReceivingKeys { count: 1 }), Err(err) => Err(action.label(err)), }, @@ -627,7 +650,7 @@ where #[inline] async fn sample_reclaim(&mut self, rng: &mut R) -> MaybeAction where - L: PublicBalanceOracle, + L: PublicBalanceOracle, R: RngCore + ?Sized, { match self.sample_withdraw(rng).await { @@ -650,7 +673,7 @@ where Ok(self .sample_asset(ActionType::ReclaimZero, rng) .await? - .map(|asset| Action::reclaim(false, asset.id.value(0))) + .map(|asset| Action::reclaim(false, Asset::::zero(asset.id))) .unwrap_or(Action::Skip)) } @@ -741,9 +764,10 @@ where impl sim::ActionSimulation for Simulation where C: transfer::Configuration, - L: Ledger + PublicBalanceOracle, + L: Ledger + PublicBalanceOracle, S: signer::Connection, PublicKey: Eq + Hash, + C::AssetValue: CheckedSub + SampleUniform, { type Actor = Actor; type Action = MaybeAction; @@ -868,22 +892,30 @@ where /// Measures the public and secret balances for each wallet, summing them all together. #[inline] -pub async fn measure_balances<'w, C, L, S, I>(wallets: I) -> Result> +pub async fn measure_balances<'w, C, L, S, I>( + wallets: I, +) -> Result, Error> where C: 'w + transfer::Configuration, - L: 'w + Ledger + PublicBalanceOracle, + L: 'w + Ledger + PublicBalanceOracle, S: 'w + signer::Connection, I: IntoIterator>, + C::AssetValue: CheckedSub, + for<'v> &'v C::AssetValue: CheckedSub, { let mut balances = AssetList::new(); for wallet in wallets { wallet.sync().await?; - balances.deposit_all(wallet.ledger().public_balances().await.unwrap()); - balances.deposit_all( + BalanceState::::deposit_all( + &mut balances, + wallet.ledger().public_balances().await.unwrap(), + ); + BalanceState::::deposit_all( + &mut balances, wallet .assets() .iter() - .map(|(id, value)| Asset::new(*id, *value)), + .map(|(id, value)| Asset::::new(id.clone(), *value)), ); } Ok(balances) @@ -915,7 +947,7 @@ impl Config { ) -> Result> where C: transfer::Configuration, - L: Ledger + PublicBalanceOracle, + L: Ledger + PublicBalanceOracle, S: signer::Connection, R: CryptoRng + RngCore, GL: FnMut(usize) -> L, @@ -925,6 +957,8 @@ impl Config { ESFut: Future, Error: Debug, PublicKey: Eq + Hash, + C::AssetValue: CheckedSub + SampleUniform, + for<'v> &'v C::AssetValue: CheckedSub, { let action_distribution = ActionDistribution::try_from(self.action_distribution) .expect("Unable to sample from action distribution."); From ba43ed5ebc66c66fe561e2903ec1fe5e6edbe307 Mon Sep 17 00:00:00 2001 From: Francisco Hernandez Iglesias Date: Wed, 16 Nov 2022 18:20:28 +0100 Subject: [PATCH 12/20] everything compiles Signed-off-by: Francisco Hernandez Iglesias --- CHANGELOG.md | 1 + manta-accounting/src/asset.rs | 70 ++++++- manta-accounting/src/transfer/mod.rs | 17 +- manta-accounting/src/transfer/test.rs | 69 ++++--- manta-accounting/src/wallet/balance.rs | 73 +++---- manta-pay/src/config.rs | 272 ++++++++++++++++++++++++- manta-pay/src/signer/base.rs | 7 +- manta-pay/src/signer/mod.rs | 4 +- manta-pay/src/simulation/ledger/mod.rs | 81 ++++++-- manta-pay/src/simulation/mod.rs | 6 +- manta-pay/src/test/payment.rs | 33 ++- manta-util/src/codec.rs | 17 ++ 12 files changed, 525 insertions(+), 125 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 796964a3b..bab2a46fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Added ### Changed +- [\#283](https://github.com/Manta-Network/manta-rs/pull/283) Upgrade asset system. - [\#282](https://github.com/Manta-Network/manta-rs/pull/282) Upgrade key system. ### Deprecated diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 686059253..b8115e829 100644 --- a/manta-accounting/src/asset.rs +++ b/manta-accounting/src/asset.rs @@ -55,8 +55,9 @@ use manta_crypto::{ rand::{Rand, RngCore, Sample}, }; use manta_util::{ - codec::{Encode, Write}, + codec::{Decode, DecodeError, Encode, Write}, num::CheckedSub, + SizeLimit, }; #[cfg(feature = "serde")] @@ -176,6 +177,14 @@ where } } +impl SizeLimit for Asset +where + I: SizeLimit, + V: SizeLimit, +{ + const SIZE: usize = I::SIZE + V::SIZE; +} + impl Sub for Asset where V: SubAssign, @@ -290,6 +299,57 @@ where } } +/// Asset Decode Error +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "I::Error: Clone, V::Error: Clone"), + Copy(bound = "I::Error: Copy, V::Error: Copy"), + Debug(bound = "I::Error: Debug, V::Error: Debug"), + Eq(bound = "I::Error: Eq, V::Error: Eq"), + Hash(bound = "I::Error: Hash, V::Error: Hash"), + PartialEq(bound = "I::Error: PartialEq, V::Error: PartialEq") +)] +pub enum AssetDecodeError +where + I: Decode, + V: Decode, +{ + /// Asset ID Decode Error + AssetIdDecodeError(I::Error), + + /// Asset Value Decode Error + AssetValueDecodeError(V::Error), +} + +impl Decode for Asset +where + I: Decode, + V: Decode, +{ + type Error = AssetDecodeError; + + #[inline] + fn decode(mut reader: R) -> Result> + where + R: manta_util::codec::Read, + { + let asset_id = I::decode(&mut reader).map_err(|e| match e { + DecodeError::Decode(r) => DecodeError::Decode(AssetDecodeError::AssetIdDecodeError(r)), + DecodeError::Read(e) => DecodeError::Read(e), + })?; + let asset_value = V::decode(&mut reader).map_err(|e| match e { + DecodeError::Decode(r) => { + DecodeError::Decode(AssetDecodeError::AssetValueDecodeError(r)) + } + DecodeError::Read(e) => DecodeError::Read(e), + })?; + Ok(Self { + id: asset_id, + value: asset_value, + }) + } +} + impl Input