From b45ecb53f9fc2d50c4037880ab0d930335a5fb52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Hern=C3=A1ndez=20Iglesias?= <38819712+SupremoUGH@users.noreply.github.com> Date: Tue, 22 Nov 2022 18:07:53 +0100 Subject: [PATCH] Feat: upgrade asset system (#283) * key system upgraded in manta-accounting * it compiles * fmt and sort * clippy issues * final touches * fmt * build documentation fix * documentation * changelog * asset is compatible with transfer now * wallet compiles * everything compiles * documentation fixed * key system upgraded to bip0039 * clippy * clippy issue fixed * AccountCollection exposure from Account and Accounttable * fixed bug note encryption * chore: fix input impl * chore: simplify reading Signed-off-by: Francisco Hernandez Iglesias Signed-off-by: Brandon H. Gomes Co-authored-by: Brandon H. Gomes --- CHANGELOG.md | 1 + manta-accounting/src/asset.rs | 1047 +++++++++----------- manta-accounting/src/transfer/batch.rs | 12 +- manta-accounting/src/transfer/canonical.rs | 95 +- manta-accounting/src/transfer/mod.rs | 268 +++-- manta-accounting/src/transfer/receiver.rs | 21 +- manta-accounting/src/transfer/sender.rs | 28 +- manta-accounting/src/transfer/test.rs | 90 +- manta-accounting/src/wallet/balance.rs | 123 ++- manta-accounting/src/wallet/mod.rs | 38 +- manta-accounting/src/wallet/signer.rs | 159 ++- manta-accounting/src/wallet/test/mod.rs | 104 +- manta-crypto/src/rand.rs | 9 +- manta-pay/src/config.rs | 283 +++++- 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 + 20 files changed, 1452 insertions(+), 974 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 509b7800c..d9021f8a5 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. - [\#284](https://github.com/Manta-Network/manta-rs/pull/284) Moved `R1CS` implementation to `manta-crypto` - [\#282](https://github.com/Manta-Network/manta-rs/pull/282) Upgrade key system. diff --git a/manta-accounting/src/asset.rs b/manta-accounting/src/asset.rs index 54cd32042..b8115e829 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,17 +34,31 @@ 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::{ - constraint::{Input, ProofSystem}, - 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::{Decode, DecodeError, Encode, Write}, + num::CheckedSub, + SizeLimit, +}; #[cfg(feature = "serde")] use manta_util::serde::{Deserialize, Serialize}; @@ -55,519 +69,318 @@ use std::{ hash::BuildHasher, }; -/// [`AssetId`] Base Type -pub type AssetIdType = u32; - -/// Asset Id Type +/// 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)] -#[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)) - } +#[derive(derivative::Derivative, Display, From)] +#[derivative(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[display(fmt = "{{id: {}, value: {}}}", id, value)] +pub struct Asset { + /// Asset Id + pub id: I, - /// Converts a byte array into `self`. - #[inline] - pub const fn from_bytes(bytes: [u8; Self::SIZE]) -> Self { - Self(AssetIdType::from_le_bytes(bytes)) - } + /// Asset Value + pub value: V, +} - /// Converts `self` into a byte array. +impl Asset { + /// Builds a new [`Asset`] from an `id` and a `value`. #[inline] - pub const fn into_bytes(self) -> [u8; Self::SIZE] { - self.0.to_le_bytes() + pub const fn new(id: I, value: V) -> Self { + Self { id, value } } - /// Samples an [`Asset`] by uniformly choosing between zero and `maximum` when selecting coins. - #[cfg(feature = "test")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "test")))] + /// Builds a new zero [`Asset`] with the given `id`. #[inline] - pub fn sample_up_to(self, maximum: AssetValue, rng: &mut R) -> Asset + pub fn zero(id: I) -> Self where - R: RngCore + ?Sized, + V: Default, { - self.value(rng.gen_range(0..maximum.0)) + Self::new(id, Default::default()) } -} -impl From for [u8; AssetId::SIZE] { + /// Returns `true` if `self` is a zero [`Asset`] of some asset id. #[inline] - fn from(id: AssetId) -> Self { - id.into_bytes() - } -} - -impl

Input

for AssetId -where - P: ProofSystem, - AssetIdType: Input

, -{ - #[inline] - fn extend(&self, input: &mut P::Input) { - self.0.extend(input); - } -} - -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 + pub fn is_zero(&self) -> bool where - R: RngCore + ?Sized, + V: Default + PartialEq, { - Self(rng.sample(distribution)) + self.value == Default::default() } -} - -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`]. + /// Returns `true` if `self` is an empty [`Asset`], i.e. both the `id` and `value` are zero. #[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, - } + 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) } } -impl Add for AssetValue { +impl Add for Asset +where + V: AddAssign, +{ type Output = Self; #[inline] - fn add(mut self, rhs: AssetValueType) -> Self { - self.add_assign(rhs); + fn add(mut self, rhs: V) -> Self::Output { + self += rhs; self } } -impl AddAssign for AssetValue { - #[inline] - fn add_assign(&mut self, rhs: AssetValueType) { - self.0 += rhs; - } -} - -impl From for [u8; AssetValue::SIZE] { +impl AddAssign for Asset +where + V: AddAssign, +{ #[inline] - fn from(value: AssetValue) -> Self { - value.into_bytes() + fn add_assign(&mut self, rhs: V) { + self.value += rhs; } } -impl

Input

for AssetValue -where - P: ProofSystem, - AssetValueType: Input

, -{ +impl From> for (I, V) { #[inline] - fn extend(&self, input: &mut P::Input) { - self.0.extend(input); + fn from(asset: Asset) -> Self { + (asset.id, asset.value) } } -impl PartialEq for AssetValue { +impl<'a, I, V> From<&'a Asset> for (&'a I, &'a V) { #[inline] - fn eq(&self, rhs: &AssetValueType) -> bool { - self.0 == *rhs + fn from(asset: &'a Asset) -> Self { + (&asset.id, &asset.value) } } -impl Sample for AssetValue +impl Sample for Asset where - AssetValueType: Sample, + I: Sample, + V: Sample, { #[inline] - fn sample(distribution: D, rng: &mut R) -> Self + fn sample(_: (), rng: &mut R) -> Self where R: RngCore + ?Sized, { - Self(rng.sample(distribution)) + Self::new(rng.gen(), rng.gen()) } } -impl SizeLimit for AssetValue { - const SIZE: usize = Self::SIZE; +impl SizeLimit for Asset +where + I: SizeLimit, + V: SizeLimit, +{ + const SIZE: usize = I::SIZE + V::SIZE; } -impl Sub for AssetValue { +impl Sub for Asset +where + V: SubAssign, +{ type Output = Self; #[inline] - fn sub(mut self, rhs: AssetValueType) -> Self { - self.sub_assign(rhs); + fn sub(mut self, rhs: V) -> Self::Output { + self -= 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)] -#[display(fmt = "{{id: {}, value: {}}}", id, value)] -pub struct Asset { - /// Asset Id - pub id: I, - - /// Asset Value - pub value: V, -} - -impl Asset { - /// Builds a new [`Asset`] from an `id` and a `value`. +impl SubAssign for Asset +where + V: SubAssign, +{ #[inline] - pub const fn new(id: I, value: V) -> Self { - Self { id, value } + fn sub_assign(&mut self, rhs: V) { + self.value -= rhs; } } -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`. +impl ConditionalSelect for Asset +where + COM: Has, + I: ConditionalSelect, + V: ConditionalSelect, +{ #[inline] - pub fn from_bytes(bytes: [u8; Self::SIZE]) -> Self { + fn select(bit: &Bool, true_value: &Self, false_value: &Self, compiler: &mut COM) -> Self { Self::new( - AssetId::from_bytes(into_array_unchecked(&bytes[..AssetId::SIZE])), - AssetValue::from_bytes(into_array_unchecked(&bytes[AssetId::SIZE..])), + I::select(bit, &true_value.id, &false_value.id, compiler), + V::select(bit, &true_value.value, &false_value.value, compiler), ) } - - /// 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 - } - } - - /// Adds the value of `asset` to `self` if it has the same [`AssetId`]. - #[inline] - pub fn try_add_assign(&mut self, asset: Asset) -> bool { - if self.id == asset.id { - self.value += asset.value; - true - } else { - false - } - } - - /// Subtracts the value of `asset` from `self` if it has the same [`AssetId`]. - #[inline] - pub fn try_sub_assign(&mut self, asset: Asset) -> bool { - if self.id == asset.id { - self.value -= asset.value; - true - } else { - false - } - } } -impl Add for Asset +impl eclair::cmp::PartialEq for Asset where - V: AddAssign, + COM: Has, + Bool: BitAnd, COM, Output = Bool>, + I: eclair::cmp::PartialEq, + V: eclair::cmp::PartialEq, { - type Output = Self; + #[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 add(mut self, rhs: V) -> Self::Output { - self += rhs; - self + 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 AddAssign for Asset +impl Variable for Asset where - V: AddAssign, + I: Variable, + V: Variable, { - #[inline] - fn add_assign(&mut self, rhs: V) { - self.value += rhs; - } -} + type Type = Asset; -impl From<[u8; Self::SIZE]> for Asset { #[inline] - fn from(array: [u8; Self::SIZE]) -> Self { - Self::from_bytes(array) + fn new_unknown(compiler: &mut COM) -> Self { + Self::new(compiler.allocate_unknown(), compiler.allocate_unknown()) } -} -impl From> for Asset { #[inline] - fn from(array: Array) -> Self { - array.0.into() + fn new_known(this: &Self::Type, compiler: &mut COM) -> Self { + Self::new(this.id.as_known(compiler), this.value.as_known(compiler)) } } -impl From for [u8; Asset::SIZE] { - #[inline] - fn from(asset: Asset) -> Self { - asset.into_bytes() - } -} +impl Variable for Asset +where + I: Variable, + V: Variable, +{ + type Type = Asset; -impl From for Array { #[inline] - fn from(asset: Asset) -> Self { - Self(asset.into_bytes()) + fn new_unknown(compiler: &mut COM) -> Self { + Self::new(compiler.allocate_unknown(), compiler.allocate_unknown()) } -} -impl From> for (I, V) { #[inline] - fn from(asset: Asset) -> Self { - (asset.id, asset.value) + fn new_known(this: &Self::Type, compiler: &mut COM) -> Self { + Self::new(this.id.as_known(compiler), this.value.as_known(compiler)) } } -impl Sample for Asset { +impl Encode for Asset +where + I: Encode, + V: Encode, +{ #[inline] - fn sample(_: (), rng: &mut R) -> Self + fn encode(&self, mut writer: W) -> Result<(), W::Error> where - R: RngCore + ?Sized, + W: Write, { - Self::new(rng.gen(), rng.gen()) + self.id.encode(&mut writer)?; + self.value.encode(&mut writer)?; + Ok(()) } } -impl SizeLimit for Asset { - const SIZE: usize = Self::SIZE; -} - -impl Sub for Asset +/// 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 - V: SubAssign, + I: Decode, + V: Decode, { - type Output = Self; + /// Asset ID Decode Error + AssetIdDecodeError(I::Error), - #[inline] - fn sub(mut self, rhs: V) -> Self::Output { - self -= rhs; - self - } + /// Asset Value Decode Error + AssetValueDecodeError(V::Error), } -impl SubAssign for Asset +impl Decode for Asset where - V: SubAssign, + I: Decode, + V: Decode, { + type Error = AssetDecodeError; + #[inline] - fn sub_assign(&mut self, rhs: V) { - self.value -= rhs; + 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 Variable for Asset +impl Input

for Asset where - I: Variable, - V: Variable, + P: HasInput + HasInput + ?Sized, { - type Type = Asset; - - #[inline] - fn new_known(this: &Self::Type, compiler: &mut COM) -> Self { - Self::new(this.id.as_known(compiler), this.value.as_known(compiler)) - } - #[inline] - fn new_unknown(compiler: &mut COM) -> Self { - Self::new(compiler.allocate_unknown(), compiler.allocate_unknown()) + 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 { @@ -586,7 +399,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 { @@ -596,37 +409,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), } @@ -634,24 +476,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; } @@ -659,19 +509,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."), } } @@ -680,9 +535,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`. @@ -694,53 +549,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)) @@ -748,11 +614,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)); @@ -760,21 +630,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 { @@ -786,9 +664,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 { @@ -796,9 +674,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 { @@ -813,29 +691,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)) @@ -843,32 +721,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); @@ -878,70 +759,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.clone()); + 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) @@ -949,7 +833,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]); @@ -964,7 +848,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) { @@ -981,7 +865,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)); } @@ -989,28 +873,36 @@ 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> &'v V: Sub, + for<'v> &'v V: AddAssign<&'v V>, { - 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> &'v V: Sub, + for<'v> &'v V: AddAssign<&'v V>, S: BuildHasher + Default, { - impl_asset_map_for_maps_body! { K, HashMapEntry } + impl_asset_map_for_maps_body! { K, I, V, HashMapEntry } } /// Asset Selection @@ -1019,31 +911,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 } } @@ -1055,51 +947,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 { @@ -1112,44 +1004,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; @@ -1164,7 +1057,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( @@ -1184,12 +1077,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}") } @@ -1197,74 +1093,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/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..c251b78d2 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, Sub}, +}; 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 + Hash + Ord; // Hash + + /// Asset Value Type + type AssetValue: AddAssign + Clone + Copy + Default + PartialOrd + Sub + Sum; // Sub, Copy + /// 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 @@ -822,8 +838,9 @@ where impl SizeLimit for Note where C: Configuration, + Asset: SizeLimit, { - const SIZE: usize = SecretKey::::SIZE + Asset::SIZE; + const SIZE: usize = SecretKey::::SIZE + Asset::::SIZE; } /// Transfer @@ -837,10 +854,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 +866,7 @@ pub struct Transfer< receivers: [Receiver; RECEIVERS], /// Sinks - sinks: [AssetValue; SINKS], + sinks: [C::AssetValue; SINKS], } impl @@ -860,11 +877,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 +892,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 +958,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 +1132,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() @@ -1189,21 +1209,21 @@ where /// track this condition. type UpdateError; - /// Valid [`AssetValue`] for [`TransferPost`] Source + /// Valid [`AssetValue`](Configuration::AssetValue) for [`TransferPost`] Source /// /// # Safety /// /// 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 + /// Valid [`AssetValue`](Configuration::AssetValue) for [`TransferPost`] Sink /// /// # Safety /// /// This type must be restricted so that it can only be constructed by this implementation of /// [`TransferLedger`]. - type ValidSinkAccount: AsRef; + type ValidSinkAccount: AsRef; /// Valid [`Proof`] Posting Key /// @@ -1225,25 +1245,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 +1280,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 +1307,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 +1331,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 +1352,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 +1415,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 +1466,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 +1486,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 +1519,7 @@ where pub receiver_posts: Vec>, /// Sinks - pub sinks: Vec, + pub sinks: Vec, /// Validity Proof pub validity_proof: Proof, @@ -1454,8 +1533,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 +1569,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 +1595,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 +1613,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 +1663,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 +1693,7 @@ where sink_accounts: Vec, super_key: &TransferLedgerSuperPostingKey, ledger: &mut L, - ) -> Result> + ) -> Result> where L: TransferLedger, { @@ -1630,7 +1710,7 @@ where L: TransferLedger, { /// Asset Id - asset_id: Option, + asset_id: Option, /// Source Posting Keys source_posting_keys: Vec>, @@ -1659,7 +1739,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..1fc941fc7 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,7 +197,7 @@ where { /// Returns the asset value sent by `self` in the transaction. #[inline] - pub fn asset_value(&self) -> AssetValue { + pub fn asset_value(&self) -> C::AssetValue { self.asset.value } @@ -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..73d0a8305 100644 --- a/manta-accounting/src/transfer/test.rs +++ b/manta-accounting/src/transfer/test.rs @@ -17,15 +17,18 @@ //! 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::{Rem, Sub}, +}; use manta_crypto::{ accumulator::Accumulator, constraint::ProofSystem, @@ -41,17 +44,23 @@ use super::ProofInput; /// /// 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(); @@ -70,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 @@ -155,25 +169,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::AssetId: Sample, + C::AssetValue: Rem + Sample + Sub, { /// Samples a [`TransferPost`] from `parameters` and `utxo_accumulator` using `proving_context` /// and `rng`. @@ -187,6 +203,7 @@ where where A: Accumulator, Model = C::UtxoAccumulatorModel>, R: CryptoRng + RngCore + ?Sized, + C::AssetValue: Ord + Rem + Sample + Sub, { Self::sample( TransferDistribution { @@ -214,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, @@ -242,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( @@ -264,6 +283,7 @@ where R: CryptoRng + RngCore + ?Sized, ProofInput: PartialEq, ProofSystemError: Debug, + C::AssetValue: Ord + Rem + Sample + Sub, { let transfer = Self::sample( TransferDistribution { @@ -305,9 +325,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 +340,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, + }, + ); sender.insert_utxo(utxo_accumulator); sender.try_upgrade(utxo_accumulator).unwrap() }) @@ -333,7 +361,10 @@ where parameters.derive(&rng.gen()), parameters.derive(&rng.gen()), rng.gen(), - asset_id.with(*v), + asset::Asset { + id: asset_id.clone(), + value: *v, + }, ) }) .collect(), @@ -350,6 +381,8 @@ impl< > Sample> for Transfer where C: Configuration, + C::AssetId: Sample, + C::AssetValue: Ord + Rem + Sample + Sub, A: Accumulator, Model = C::UtxoAccumulatorModel>, { #[inline] @@ -357,14 +390,14 @@ where 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 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, @@ -391,6 +424,7 @@ impl< for Transfer where C: Configuration, + C::AssetValue: Ord + Rem + Sample + Sub, A: Accumulator, Model = C::UtxoAccumulatorModel>, { #[inline] @@ -400,18 +434,18 @@ where { let (senders, receivers) = sample_senders_and_receivers( distribution.base.parameters, - distribution.asset_id, - &value_distribution(SENDERS, distribution.sender_sum, rng), - &value_distribution(RECEIVERS, distribution.receiver_sum, rng), + distribution.asset_id.clone(), + &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), ) } } @@ -423,7 +457,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 diff --git a/manta-accounting/src/wallet/balance.rs b/manta-accounting/src/wallet/balance.rs index a72f4f9db..999d41236 100644 --- a/manta-accounting/src/wallet/balance.rs +++ b/manta-accounting/src/wallet/balance.rs @@ -20,8 +20,12 @@ //! protocol. Applications which define balances beyond fungible assets should extend these //! abstractions. -use crate::asset::{Asset, AssetId, AssetList, AssetValue}; +use crate::{ + asset::AssetList, + transfer::{Asset, Configuration}, +}; use alloc::collections::btree_map::{BTreeMap, Entry as BTreeMapEntry}; +use manta_util::num::CheckedSub; #[cfg(feature = "std")] use std::{ @@ -30,38 +34,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 +82,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 +110,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 +130,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,52 +157,63 @@ 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 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) + 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 +224,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!( @@ -232,10 +260,15 @@ 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 { ($(( $type:ty, + $config: ty, $doc:expr, $valid_withdraw:ident, $full_withdraw:ident @@ -249,7 +282,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,7 +291,7 @@ 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); } )* } @@ -292,4 +325,6 @@ pub mod 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 8aeef190d..e6fb136bc 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,22 +137,22 @@ 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) } /// 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 - 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 7d46cb633..9f63524ed 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..88643ef6b 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 { @@ -528,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 @@ -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,11 +673,11 @@ 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)) } - /// 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 @@ -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."); 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 18442cd0c..366a125be 100644 --- a/manta-pay/src/config.rs +++ b/manta-pay/src/config.rs @@ -27,9 +27,13 @@ use blake2::{ digest::{Update, VariableOutput}, Blake2sVar, }; +use core::{ + iter::Sum, + ops::{AddAssign, Rem, Sub, SubAssign}, +}; use manta_accounting::{ - asset::{Asset, AssetId, AssetValue}, - transfer, + asset, + transfer::{self, Asset}, }; use manta_crypto::{ accumulator, @@ -58,20 +62,227 @@ use manta_crypto::{ hash::ArrayHashFunction, key::{self, kdf::KeyDerivationFunction}, merkle_tree, + rand::{RngCore, Sample}, }; use manta_util::{ codec::{Decode, DecodeError, Encode, Read, Write}, - into_array_unchecked, Array, AsBytes, SizeLimit, + num::CheckedSub, + Array, AsBytes, SizeLimit, }; #[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}; 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)) + } +} + +impl Input for AssetId { + #[inline] + fn extend(&self, input: &mut Vec) { + input.push(self.0.into()); + } +} + +/// 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)) + } +} + +impl Input for AssetValue { + #[inline] + fn extend(&self, input: &mut Vec) { + input.push(self.0.into()); + } +} + /// Pairing Curve Type pub type PairingCurve = Bls12_381; @@ -178,7 +389,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] @@ -245,7 +456,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] @@ -651,7 +862,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) } @@ -668,10 +879,11 @@ 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(slice.to_vec()) + .expect("Decoding Asset is not allowed to fail."), }) } } @@ -722,6 +934,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; @@ -838,3 +1052,52 @@ 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 manta_crypto::rand::{rand::Rng, SampleBorrow, SampleUniform, UniformInt, UniformSampler}; + + /// 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 6b6819aa6..479e010c0 100644 --- a/manta-pay/src/signer/base.rs +++ b/manta-pay/src/signer/base.rs @@ -26,6 +26,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]