diff --git a/Cargo.lock b/Cargo.lock index fdc42b2f1..28de73636 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4829,7 +4829,6 @@ dependencies = [ [[package]] name = "manta-accounting" version = "0.4.0" -source = "git+https://github.com/manta-network/manta-rs#5efedbad1f1638df930750fed1860353f1a04fe3" dependencies = [ "derivative", "derive_more", @@ -4870,7 +4869,6 @@ dependencies = [ [[package]] name = "manta-crypto" version = "0.4.0" -source = "git+https://github.com/manta-network/manta-rs#5efedbad1f1638df930750fed1860353f1a04fe3" dependencies = [ "derivative", "manta-util", @@ -4880,7 +4878,6 @@ dependencies = [ [[package]] name = "manta-pay" version = "0.4.0" -source = "git+https://github.com/manta-network/manta-rs#5efedbad1f1638df930750fed1860353f1a04fe3" dependencies = [ "aes-gcm", "ark-bls12-381", @@ -4908,7 +4905,9 @@ name = "manta-primitives" version = "3.1.4" dependencies = [ "frame-support", + "frame-system", "log", + "manta-accounting", "parity-scale-codec", "scale-info", "smallvec", @@ -5000,7 +4999,6 @@ dependencies = [ [[package]] name = "manta-util" version = "0.4.0" -source = "git+https://github.com/manta-network/manta-rs#5efedbad1f1638df930750fed1860353f1a04fe3" [[package]] name = "maplit" @@ -6135,8 +6133,11 @@ dependencies = [ "manta-accounting", "manta-crypto", "manta-pay", + "manta-primitives", "manta-sdk", "manta-util", + "pallet-assets", + "pallet-balances", "parity-scale-codec", "rand 0.8.5", "scale-info", diff --git a/pallets/manta-pay/Cargo.toml b/pallets/manta-pay/Cargo.toml index 4c37d2839..f8ed36422 100644 --- a/pallets/manta-pay/Cargo.toml +++ b/pallets/manta-pay/Cargo.toml @@ -38,8 +38,10 @@ runtime-benchmarks = [ std = [ "frame-benchmarking/std", "frame-system/std", + "sp-runtime/std", "manta-util/std", "manta-sdk/download", + "manta-primitives/std", "tempfile", "rand", ] @@ -55,26 +57,34 @@ precompute-coins = [ ] [dependencies] +# utils anyhow = { version = "1.0.55", optional = true } +indoc = { version = "1.0.3", default-features = false, optional = true} +rand = { version = "0.8.4", default-features = false, optional = true } +tempfile = { version = "3.3.0", optional = true } + +# substrate dependencies frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16", optional = true, default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16", default-features = false } -indoc = { version = "1.0.3", default-features = false, optional = true} -manta-accounting = { git = "https://github.com/manta-network/manta-rs", default-features = false } -manta-crypto = { git = "https://github.com/manta-network/manta-rs", default-features = false } -manta-pay = { git = "https://github.com/manta-network/manta-rs", default-features = false, features = ["groth16", "scale"] } -manta-sdk = { git = "https://github.com/manta-network/sdk", default-features = false } -manta-util = { git = "https://github.com/manta-network/manta-rs", default-features = false } -rand = { version = "0.8.4", default-features = false, optional = true } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16", default-features = false } scale-codec = { package = "parity-scale-codec", version = "2.3.1", default-features = false, features = ["derive", "max-encoded-len"] } scale-info = { version = "1.0.0", default-features = false, features = ["derive"] } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16", default-features = false } -tempfile = { version = "3.3.0", optional = true } + +# manta dependencies +manta-accounting = { path = "../../../manta-rs/manta-accounting", default-features = false } +manta-crypto = { path = "../../../manta-rs/manta-crypto", default-features = false } +manta-pay = { path = "../../../manta-rs/manta-pay", default-features = false, features = ["groth16", "scale"] } +manta-sdk = { git = "https://github.com/manta-network/sdk", default-features = false } +manta-util = { path = "../../../manta-rs/manta-util", default-features = false } +manta-primitives = { path = "../../runtime/primitives", default-features = false} [dev-dependencies] bencher = "0.1.5" criterion = "0.3.4" lazy_static = "1.4.0" -manta-accounting = { git = "https://github.com/manta-network/manta-rs", features = ["test"] } -sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16", default-features = false } -sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16", default-features = false } +manta-accounting = { path = "../../../manta-rs/manta-accounting", features = ["test"] } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16"} +sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16"} +pallet-balances = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.16" } +pallet-assets = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.16" } diff --git a/pallets/manta-pay/src/lib.rs b/pallets/manta-pay/src/lib.rs index 4d2cf2faa..835d31c29 100644 --- a/pallets/manta-pay/src/lib.rs +++ b/pallets/manta-pay/src/lib.rs @@ -58,8 +58,6 @@ //! //! ### Dispatchable Functions //! -//! * `transfer_asset` - Transfers an `amount` of units of fungible asset `id` from the balance of -//! the function caller's account (`origin`) to a `target` account. //! * `mint` - Converting an `amount` of units of fungible asset `id` from the caller //! to a private UTXO. (The caller does not need to be the owner of this UTXO) //! * `private_transfer` - Transfer two input UTXOs into two output UTXOs. Require that 1) the input @@ -73,11 +71,6 @@ //! Please refer to the [`Call`](./enum.Call.html) enum and its associated variants for //! documentation on each function. //! -//! ### Public Functions -//! -//! * `balance` - Get the asset balance of `who`. -//! * `total_supply` - Get the total supply of an asset `id`. -//! //! Please refer to the [`Module`](./struct.Module.html) struct for details on publicly available //! functions. //! @@ -106,14 +99,14 @@ extern crate alloc; use core::marker::PhantomData; -use frame_support::{ensure, require_transactional}; +use frame_support::{require_transactional, PalletId}; use manta_accounting::{ asset, transfer::{ - canonical::TransferShape, AccountBalance, InvalidSinkAccount, InvalidSourceAccount, Proof, + canonical::TransferShape, InvalidSinkAccount, InvalidSourceAccount, Proof, ReceiverLedger, ReceiverPostError, ReceiverPostingKey, SenderLedger, SenderPostError, SenderPostingKey, SinkPostingKey, SourcePostingKey, TransferLedger, - TransferLedgerSuperPostingKey, TransferPostError, + TransferLedgerSuperPostingKey, TransferPostError, LedgerInternalError, }, }; use manta_crypto::{ @@ -122,6 +115,8 @@ use manta_crypto::{ }; use manta_pay::config; use manta_util::codec::Decode as _; +use manta_primitives::types::{AssetId, Balance}; +use manta_primitives::assets::{FungibleLedger}; use scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use types::*; @@ -140,263 +135,7 @@ pub mod weights; pub use pallet::*; /// Type Definitions for Protocol Structures -pub mod types { - use super::*; - - /// Asset Id Type - pub type AssetId = asset::AssetIdType; - - /// Asset Value Type - pub type AssetValue = asset::AssetValueType; - - /// Asset - #[derive( - Clone, - Copy, - Debug, - Decode, - Default, - Encode, - Eq, - Hash, - MaxEncodedLen, - Ord, - PartialEq, - PartialOrd, - TypeInfo, - )] - pub struct Asset { - /// Asset Id - pub id: AssetId, - - /// Asset Value - pub value: AssetValue, - } - - impl Asset { - /// Builds a new [`Asset`] from `id` and `value`. - #[inline] - pub fn new(id: AssetId, value: AssetValue) -> Self { - Self { id, value } - } - } - - /// Encrypted Note - #[derive(Clone, Debug, Decode, Encode, Eq, Hash, MaxEncodedLen, PartialEq, TypeInfo)] - pub struct EncryptedNote { - /// Ciphertext - pub ciphertext: [u8; 36], - - /// Ephemeral Public Key - pub ephemeral_public_key: config::PublicKey, - } - - impl Default for EncryptedNote { - #[inline] - fn default() -> Self { - Self { - ciphertext: [0; 36], - ephemeral_public_key: Default::default(), - } - } - } - - impl From for EncryptedNote { - #[inline] - fn from(note: config::EncryptedNote) -> Self { - Self { - ciphertext: note.ciphertext.into(), - ephemeral_public_key: note.ephemeral_public_key, - } - } - } - - impl From for config::EncryptedNote { - #[inline] - fn from(note: EncryptedNote) -> Self { - Self { - ciphertext: note.ciphertext.into(), - ephemeral_public_key: note.ephemeral_public_key, - } - } - } - - /// Sender Post - #[derive(Clone, Debug, Decode, Encode, Eq, Hash, MaxEncodedLen, PartialEq, TypeInfo)] - pub struct SenderPost { - /// UTXO Accumulator Output - pub utxo_accumulator_output: config::UtxoAccumulatorOutput, - - /// Void Number - pub void_number: config::VoidNumber, - } - - impl From for SenderPost { - #[inline] - fn from(post: config::SenderPost) -> Self { - Self { - utxo_accumulator_output: post.utxo_accumulator_output, - void_number: post.void_number, - } - } - } - - impl From for config::SenderPost { - #[inline] - fn from(post: SenderPost) -> Self { - Self { - utxo_accumulator_output: post.utxo_accumulator_output, - void_number: post.void_number, - } - } - } - - /// Receiver Post - #[derive(Clone, Debug, Decode, Encode, Eq, Hash, MaxEncodedLen, PartialEq, TypeInfo)] - pub struct ReceiverPost { - /// Unspent Transaction Output - pub utxo: config::Utxo, - - /// Encrypted Note - pub note: EncryptedNote, - } - - impl From for ReceiverPost { - #[inline] - fn from(post: config::ReceiverPost) -> Self { - Self { - utxo: post.utxo, - note: post.note.into(), - } - } - } - - impl From for config::ReceiverPost { - #[inline] - fn from(post: ReceiverPost) -> Self { - Self { - utxo: post.utxo, - note: post.note.into(), - } - } - } - - /// Transfer Post - #[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)] - pub struct TransferPost { - /// Asset Id - pub asset_id: Option, - - /// Sources - pub sources: Vec, - - /// Sender Posts - pub sender_posts: Vec, - - /// Receiver Posts - pub receiver_posts: Vec, - - /// Sinks - pub sinks: Vec, - - /// Validity Proof - pub validity_proof: config::Proof, - } - - impl From for TransferPost { - #[inline] - fn from(post: config::TransferPost) -> Self { - Self { - asset_id: post.asset_id.map(|id| id.0), - sources: post.sources.into_iter().map(|s| s.0).collect(), - sender_posts: post.sender_posts.into_iter().map(Into::into).collect(), - receiver_posts: post.receiver_posts.into_iter().map(Into::into).collect(), - sinks: post.sinks.into_iter().map(|s| s.0).collect(), - validity_proof: post.validity_proof, - } - } - } - - impl From for config::TransferPost { - #[inline] - fn from(post: TransferPost) -> Self { - Self { - asset_id: post.asset_id.map(asset::AssetId), - sources: post.sources.into_iter().map(asset::AssetValue).collect(), - sender_posts: post.sender_posts.into_iter().map(Into::into).collect(), - receiver_posts: post.receiver_posts.into_iter().map(Into::into).collect(), - sinks: post.sinks.into_iter().map(asset::AssetValue).collect(), - validity_proof: post.validity_proof, - } - } - } - - /// Leaf Digest Type - pub type LeafDigest = merkle_tree::LeafDigest; - - /// Inner Digest Type - pub type InnerDigest = merkle_tree::InnerDigest; - - /// Merkle Tree Current Path - #[derive(Clone, Debug, Decode, Default, Encode, Eq, PartialEq, TypeInfo)] - pub struct CurrentPath { - /// Sibling Digest - pub sibling_digest: LeafDigest, - - /// Leaf Index - pub leaf_index: u32, - - /// Inner Path - pub inner_path: Vec, - } - - impl MaxEncodedLen for CurrentPath { - #[inline] - fn max_encoded_len() -> usize { - 0_usize - .saturating_add(LeafDigest::max_encoded_len()) - .saturating_add(u32::max_encoded_len()) - .saturating_add( - // NOTE: We know that these paths don't exceed the path length. - InnerDigest::max_encoded_len().saturating_mul( - manta_crypto::merkle_tree::path_length::(), - ), - ) - } - } - - impl From> for CurrentPath { - #[inline] - fn from(path: merkle_tree::CurrentPath) -> Self { - Self { - sibling_digest: path.sibling_digest, - leaf_index: path.inner_path.leaf_index.0 as u32, - inner_path: path.inner_path.path, - } - } - } - - impl From for merkle_tree::CurrentPath { - #[inline] - fn from(path: CurrentPath) -> Self { - Self::new( - path.sibling_digest, - (path.leaf_index as usize).into(), - path.inner_path, - ) - } - } - - /// UTXO Merkle Tree Path - #[derive(Clone, Debug, Decode, Default, Encode, Eq, MaxEncodedLen, PartialEq, TypeInfo)] - pub struct UtxoMerkleTreePath { - /// Current Leaf Digest - pub leaf_digest: Option, - - /// Current Path - pub current_path: CurrentPath, - } -} +pub mod types; /// MantaPay Pallet #[frame_support::pallet] @@ -404,13 +143,10 @@ pub mod pallet { use super::*; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - use sp_runtime::traits::StaticLookup; + use sp_runtime::traits::AccountIdConversion; /// Extrinsic Weight Info pub trait WeightInfo { - /// Returns the [`Weight`] of the [`Pallet::transfer`] extrinsic. - fn transfer() -> Weight; - /// Returns the [`Weight`] of the [`Pallet::mint`] extrinsic. fn mint() -> Weight; @@ -432,123 +168,54 @@ pub mod pallet { /// The overarching event type. type Event: From> + IsType<::Event>; + /// Fungible ledger + type FungibleLedger: FungibleLedger; + /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; + + /// Pallet ID + type PalletId: Get; } #[pallet::hooks] impl Hooks> for Pallet {} - /// Public Balance State - #[pallet::storage] - pub(super) type Balances = StorageDoubleMap< - _, - Blake2_128Concat, - T::AccountId, - Blake2_128Concat, - AssetId, - AssetValue, - ValueQuery, - >; - - /// Total Supply per AssetId - #[pallet::storage] - pub(super) type TotalSupply = - StorageMap<_, Blake2_128Concat, AssetId, AssetValue, ValueQuery>; - - /// + /// Shards of the merkle tree #[pallet::storage] pub(super) type Shards = StorageDoubleMap<_, Identity, u8, Identity, u64, (config::Utxo, EncryptedNote), ValueQuery>; - /// + /// Shard trees #[pallet::storage] pub(super) type ShardTrees = StorageMap<_, Identity, u8, UtxoMerkleTreePath, ValueQuery>; - /// + /// Outputs of Utxo accumulator #[pallet::storage] pub(super) type UtxoAccumulatorOutputs = StorageMap<_, Identity, config::UtxoAccumulatorOutput, (), ValueQuery>; - /// + /// Utxo set #[pallet::storage] pub(super) type UtxoSet = StorageMap<_, Identity, config::Utxo, (), ValueQuery>; - /// + /// Void number set #[pallet::storage] pub(super) type VoidNumberSet = StorageMap<_, Identity, config::VoidNumber, (), ValueQuery>; - /// + /// Void number set insertion order #[pallet::storage] pub(super) type VoidNumberSetInsertionOrder = StorageMap<_, Identity, u64, config::VoidNumber, ValueQuery>; - /// + /// Void number set size #[pallet::storage] pub(super) type VoidNumberSetSize = StorageValue<_, u64, ValueQuery>; - /// Genesis Configuration - #[pallet::genesis_config] - pub struct GenesisConfig { - pub owner: T::AccountId, - pub assets: alloc::collections::btree_set::BTreeSet<(AssetId, AssetValue)>, - } - - #[cfg(feature = "std")] - impl Default for GenesisConfig { - #[inline] - fn default() -> Self { - /* FIXME: `AccountId` does not implement default! - GenesisConfig { - owner: Default::default(), - assets: Default::default(), - } - */ - todo!() - } - } - - #[pallet::genesis_build] - impl GenesisBuild for GenesisConfig { - #[inline] - fn build(&self) { - for (id, value) in &self.assets { - Pallet::::init_asset(&self.owner, *id, *value); - } - } - } - #[pallet::call] impl Pallet { - /// Transfers public `asset` from `origin` to `target`. - #[pallet::weight(T::WeightInfo::transfer())] - #[require_transactional] - pub fn transfer( - origin: OriginFor, - target: ::Source, - asset: Asset, - ) -> DispatchResultWithPostInfo { - let origin = ensure_signed(origin)?; - let target = T::Lookup::lookup(target)?; - ensure!( - TotalSupply::::contains_key(&asset.id), - Error::::UninitializedSupply - ); - let origin_balance = Balances::::get(&origin, asset.id); - ensure!(asset.value > 0, Error::::ZeroTransfer); - ensure!(origin_balance >= asset.value, Error::::BalanceLow); - Balances::::mutate(&origin, asset.id, |balance| *balance -= asset.value); - Balances::::mutate(&target, asset.id, |balance| *balance += asset.value); - Self::deposit_event(Event::Transfer { - asset, - source: origin, - sink: target, - }); - Ok(().into()) - } - /// Mints some assets encoded in `post` to the `origin` account. #[pallet::weight(T::WeightInfo::mint())] #[require_transactional] @@ -697,6 +364,21 @@ pub mod pallet { /// /// The submitted proof did not pass validation, or errored during validation. InvalidProof, + + /// Ledger Internal Error + /// + /// Internal error caused by ledger internals (Ideally should never happen). + LedgerInternalError, + + /// Invalid Source Account + /// + /// At least one of the source accounts is invalid. + InvalidSourceAccount, + + /// Invalid Sink Account + /// + /// At least one of the sink accounts in invalid. + InvalidSinkAccount, } impl From> for Error @@ -704,13 +386,8 @@ pub mod pallet { T: Config, { #[inline] - fn from(err: InvalidSourceAccount) -> Self { - match err.balance { - AccountBalance::Known(_) => Self::BalanceLow, - AccountBalance::UnknownAccount => { - unreachable!("Accounts are checked before reaching this point.") - } - } + fn from(_: InvalidSourceAccount) -> Self { + Self::InvalidSourceAccount } } @@ -719,9 +396,8 @@ pub mod pallet { T: Config, { #[inline] - fn from(err: InvalidSinkAccount) -> Self { - let _ = err; - unimplemented!("Accounts are checked before reaching this point.") + fn from(_: InvalidSinkAccount) -> Self { + Self::InvalidSinkAccount } } @@ -759,39 +435,27 @@ pub mod pallet { TransferPostError::DuplicateSpend => Self::DuplicateSpend, TransferPostError::DuplicateRegister => Self::DuplicateRegister, TransferPostError::InvalidProof => Self::InvalidProof, + TransferPostError::LedgerInternalError => Self::LedgerInternalError, } } } -} - -impl Pallet -where - T: Config, -{ - /// Initializes `asset_id` with a supply of `total`, giving control to `owner`. - #[inline] - fn init_asset(owner: &T::AccountId, asset_id: AssetId, total: AssetValue) { - TotalSupply::::insert(asset_id, total); - Balances::::insert(owner, asset_id, total); - } - /// Returns the balance of `account` for the asset with the given `id`. - #[inline] - pub fn balance(account: T::AccountId, id: AssetId) -> AssetValue { - Balances::::get(account, id) - } + impl Pallet + where + T: Config, + { + /// Returns the ledger implementation for this pallet. + #[inline] + fn ledger() -> Ledger { + Ledger(PhantomData) + } - /// Returns the total supply of the asset with the given `id`. - #[inline] - pub fn total_supply(id: AssetId) -> AssetValue { - TotalSupply::::get(id) + /// The account ID of AssetManager + pub fn account_id() -> T::AccountId { + T::PalletId::get().into_account() + } } - /// Returns the ledger implementation for this pallet. - #[inline] - fn ledger() -> Ledger { - Ledger(PhantomData) - } } /// Preprocessed Event @@ -999,34 +663,21 @@ where #[inline] fn check_source_accounts( &self, - asset_id: asset::AssetId, + asset_id: manta_accounting::asset::AssetId, sources: I, ) -> Result, InvalidSourceAccount> where I: Iterator, { - // NOTE: Existence of accounts is type-checked so we only need check account balances. sources .map(move |(account_id, withdraw)| { - match Balances::::try_get(&account_id, asset_id.0) { - Ok(balance) => { - // FIXME: Check if balance would withdraw more than existential deposit. - if balance >= withdraw.0 { - Ok(WrapPair(account_id, withdraw)) - } else { - Err(InvalidSourceAccount { - account_id, - balance: AccountBalance::Known(asset::AssetValue(balance)), - withdraw, - }) - } - } - _ => Err(InvalidSourceAccount { + T::FungibleLedger::can_withdraw(asset_id.0, &account_id, withdraw.0) + .and_then(| _ | Ok(WrapPair(account_id.clone(), withdraw)) ) + .map_err(| _ | InvalidSourceAccount { account_id, - balance: AccountBalance::Known(asset::AssetValue(0)), + asset_id, withdraw, - }), - } + }) }) .collect() } @@ -1034,6 +685,7 @@ where #[inline] fn check_sink_accounts( &self, + asset_id: manta_accounting::asset::AssetId, sinks: I, ) -> Result, InvalidSinkAccount> where @@ -1041,9 +693,15 @@ where { // NOTE: Existence of accounts is type-checked so we don't need to do anything here, just // pass the data forward. - Ok(sinks - .map(move |(account_id, deposit)| WrapPair(account_id, deposit)) - .collect()) + sinks + .map(move |(account_id, deposit)| { + T::FungibleLedger::can_deposit(asset_id.0, &account_id, deposit.0) + .and_then( | _ | Ok(WrapPair(account_id.clone(), deposit))) + .map_err(| _ | InvalidSinkAccount { + account_id, + asset_id, + deposit})}) + .collect() } #[inline] @@ -1104,13 +762,20 @@ where sinks: Vec>, proof: Self::ValidProof, super_key: &TransferLedgerSuperPostingKey, - ) { + ) -> Result<(), manta_accounting::transfer::LedgerInternalError> { let _ = (proof, super_key); for WrapPair(account_id, withdraw) in sources { - Balances::::mutate(&account_id, asset_id.0, |balance| *balance -= withdraw.0); + T::FungibleLedger::transfer(asset_id.0, + &account_id, + &pallet::Pallet::::account_id(), + withdraw.0).map_err(| _ | LedgerInternalError)?; } for WrapPair(account_id, deposit) in sinks { - Balances::::mutate(&account_id, asset_id.0, |balance| *balance += deposit.0); + T::FungibleLedger::transfer(asset_id.0, + &pallet::Pallet::::account_id(), + &account_id, + deposit.0).map_err(| _ | LedgerInternalError)?; } + Ok(()) } } diff --git a/pallets/manta-pay/src/mock.rs b/pallets/manta-pay/src/mock.rs index 119ffe677..87e25b747 100644 --- a/pallets/manta-pay/src/mock.rs +++ b/pallets/manta-pay/src/mock.rs @@ -15,9 +15,15 @@ // along with pallet-manta-pay. If not, see . use frame_support::{ - parameter_types, - traits::{ConstU32, Everything}, + parameter_types, PalletId, + traits::{ConstU32, Everything, fungible::Inspect, + fungibles::Inspect as AssetInspect, fungibles::Transfer as AssetTransfer, + tokens::{DepositConsequence, WithdrawConsequence, ExistenceRequirement}, Currency}, }; +use frame_system::EnsureRoot; +use manta_primitives::assets::{FungibleLedger, FungibleLedgerConsequence}; +use manta_primitives::types::{AssetId, Balance}; +use manta_primitives::constants::MANTA_PAY_PALLET_ID; use sp_core::H256; use sp_runtime::{ testing::Header, @@ -35,6 +41,8 @@ frame_support::construct_runtime!( { System: frame_system::{Pallet, Call, Config, Storage, Event}, MantaPayPallet: crate::{Pallet, Call, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Assets: pallet_assets::{Pallet, Storage, Event}, } ); @@ -63,7 +71,7 @@ impl frame_system::Config for Test { type BlockHashCount = BlockHashCount; type Version = (); type PalletInfo = PalletInfo; - type AccountData = (); + type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); type SystemWeightInfo = (); @@ -72,9 +80,116 @@ impl frame_system::Config for Test { type MaxConsumers = ConstU32<16>; } +parameter_types! { + pub ExistentialDeposit: Balance = 1; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Test { + type MaxLocks = MaxLocks; + type Balance = Balance; + type Event = Event; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; +} + + +parameter_types! { + pub const AssetDeposit: Balance = 0; // Does not really matter as this will be only called by root + pub const AssetAccountDeposit: Balance = 0; + pub const ApprovalDeposit: Balance = 0; + pub const AssetsStringLimit: u32 = 50; + pub const MetadataDepositBase: Balance = 0; + pub const MetadataDepositPerByte: Balance = 0; +} + +impl pallet_assets::Config for Test { + type Event = Event; + type Balance = Balance; + type AssetId = AssetId; + type Currency = Balances; + type ForceOrigin = EnsureRoot; + type AssetDeposit = AssetDeposit; + type AssetAccountDeposit = AssetAccountDeposit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type ApprovalDeposit = ApprovalDeposit; + type StringLimit = AssetsStringLimit; + type Freezer = (); + type Extra = (); + type WeightInfo = pallet_assets::weights::SubstrateWeight; +} + +pub struct MantaFungibleLedger; +impl FungibleLedger for MantaFungibleLedger{ + fn can_deposit(asset_id: AssetId, + account: &::AccountId, + amount: Balance) -> Result<(), FungibleLedgerConsequence>{ + if asset_id == 0 { // we assume native asset with id 0 + match Balances::can_deposit(account, amount){ + DepositConsequence::Success => Ok(()), + other => Err(other.into()) + } + } else { + match Assets::can_deposit(asset_id, account, amount) { + DepositConsequence::Success => Ok(()), + other => Err(other.into()) + } + } + } + + fn can_withdraw(asset_id: AssetId, + account: &::AccountId, + amount: Balance) -> Result<(), FungibleLedgerConsequence>{ + if asset_id == 0 { // we assume native asset with id 0 + match Balances::can_withdraw(account, amount){ + WithdrawConsequence::Success => Ok(()), + other => Err(other.into()) + } + } else { + match Assets::can_withdraw(asset_id, account, amount){ + WithdrawConsequence::Success => Ok(()), + other => Err(other.into()) + } + } + } + + fn transfer(asset_id: AssetId, + source: &::AccountId, + dest: &::AccountId, + amount: Balance) -> Result<(), FungibleLedgerConsequence>{ + if asset_id == 0 { + ::AccountId>>::transfer(source, + dest, + amount, + ExistenceRequirement::KeepAlive) + .map_err(| _ | FungibleLedgerConsequence::InternalError) + } else { + ::AccountId>>::transfer(asset_id, + source, + dest, + amount, + true) + .and_then(| _ | Ok(())) + .map_err(| _ | FungibleLedgerConsequence::InternalError) + } + } +} + +parameter_types!{ + pub const MantaPayPalletId: PalletId = MANTA_PAY_PALLET_ID; +} + impl crate::Config for Test { type Event = Event; type WeightInfo = crate::weights::WeightInfo; + type FungibleLedger = MantaFungibleLedger; + type PalletId = MantaPayPalletId; } pub fn new_test_ext() -> sp_io::TestExternalities { diff --git a/pallets/manta-pay/src/test/payment.rs b/pallets/manta-pay/src/test/payment.rs index 485842e89..b7733a359 100644 --- a/pallets/manta-pay/src/test/payment.rs +++ b/pallets/manta-pay/src/test/payment.rs @@ -15,7 +15,7 @@ // along with pallet-manta-pay. If not, see . use crate::{ - mock::{new_test_ext, MantaPayPallet, Origin, Test}, + mock::{new_test_ext, MantaPayPallet, Balances, Assets, Origin, Test}, Error, }; use frame_support::{assert_noop, assert_ok}; @@ -260,8 +260,30 @@ where /// Initializes a test by allocating `value`-many assets of the given `id` to the default account. #[inline] fn initialize_test(id: AssetId, value: AssetValue) { - MantaPayPallet::init_asset(&1, id.0, value.0); - assert_eq!(MantaPayPallet::balance(1, id.0), value.0); + if id.0 == 0 { + assert_ok!(Balances::set_balance(Origin::root(), 1, value.0, 0)); + } else { + assert_ok!(Assets::force_create(Origin::root(), + id.0, + MantaPayPallet::account_id(), + true, + 1)); + assert_ok!(Assets::force_asset_status( + Origin::root(), + id.0, + 1u64.into(), + 1u64.into(), + 1u64.into(), + 1u64.into(), + 1, + true, + false, + )); + assert_ok!(Assets::mint(Origin::signed(1u64.into()), + id.0, + 1u64.into(), + value.0)); + } } /// Tests multiple mints from some total supply. diff --git a/pallets/manta-pay/src/types.rs b/pallets/manta-pay/src/types.rs new file mode 100644 index 000000000..4453b179d --- /dev/null +++ b/pallets/manta-pay/src/types.rs @@ -0,0 +1,267 @@ +// Copyright 2019-2022 Manta Network. +// This file is part of pallet-manta-pay. +// +// pallet-manta-pay is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// pallet-manta-pay is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with pallet-manta-pay. If not, see . + +//! Type definitions for pallet-manta-pay + +use super::*; + +/// Asset +#[derive( + Clone, + Copy, + Debug, + Decode, + Default, + Encode, + Eq, + Hash, + MaxEncodedLen, + Ord, + PartialEq, + PartialOrd, + TypeInfo, +)] +pub struct Asset { + /// Asset Id + pub id: AssetId, + + /// Asset Value + pub value: Balance, +} + +impl Asset { + /// Builds a new [`Asset`] from `id` and `value`. + #[inline] + pub fn new(id: AssetId, value: Balance) -> Self { + Self { id, value } + } +} + +/// Encrypted Note +#[derive(Clone, Debug, Decode, Encode, Eq, Hash, MaxEncodedLen, PartialEq, TypeInfo)] +pub struct EncryptedNote { + /// Ciphertext + pub ciphertext: [u8; 36], + + /// Ephemeral Public Key + pub ephemeral_public_key: config::PublicKey, +} + +impl Default for EncryptedNote { + #[inline] + fn default() -> Self { + Self { + ciphertext: [0; 36], + ephemeral_public_key: Default::default(), + } + } +} + +impl From for EncryptedNote { + #[inline] + fn from(note: config::EncryptedNote) -> Self { + Self { + ciphertext: note.ciphertext.into(), + ephemeral_public_key: note.ephemeral_public_key, + } + } +} + +impl From for config::EncryptedNote { + #[inline] + fn from(note: EncryptedNote) -> Self { + Self { + ciphertext: note.ciphertext.into(), + ephemeral_public_key: note.ephemeral_public_key, + } + } +} + +/// Sender Post +#[derive(Clone, Debug, Decode, Encode, Eq, Hash, MaxEncodedLen, PartialEq, TypeInfo)] +pub struct SenderPost { + /// UTXO Accumulator Output + pub utxo_accumulator_output: config::UtxoAccumulatorOutput, + + /// Void Number + pub void_number: config::VoidNumber, +} + +impl From for SenderPost { + #[inline] + fn from(post: config::SenderPost) -> Self { + Self { + utxo_accumulator_output: post.utxo_accumulator_output, + void_number: post.void_number, + } + } +} + +impl From for config::SenderPost { + #[inline] + fn from(post: SenderPost) -> Self { + Self { + utxo_accumulator_output: post.utxo_accumulator_output, + void_number: post.void_number, + } + } +} + +/// Receiver Post +#[derive(Clone, Debug, Decode, Encode, Eq, Hash, MaxEncodedLen, PartialEq, TypeInfo)] +pub struct ReceiverPost { + /// Unspent Transaction Output + pub utxo: config::Utxo, + + /// Encrypted Note + pub note: EncryptedNote, +} + +impl From for ReceiverPost { + #[inline] + fn from(post: config::ReceiverPost) -> Self { + Self { + utxo: post.utxo, + note: post.note.into(), + } + } +} + +impl From for config::ReceiverPost { + #[inline] + fn from(post: ReceiverPost) -> Self { + Self { + utxo: post.utxo, + note: post.note.into(), + } + } +} + +/// Transfer Post +#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)] +pub struct TransferPost { + /// Asset Id + pub asset_id: Option, + + /// Sources + pub sources: Vec, + + /// Sender Posts + pub sender_posts: Vec, + + /// Receiver Posts + pub receiver_posts: Vec, + + /// Sinks + pub sinks: Vec, + + /// Validity Proof + pub validity_proof: config::Proof, +} + +impl From for TransferPost { + #[inline] + fn from(post: config::TransferPost) -> Self { + Self { + asset_id: post.asset_id.map(|id| id.0), + sources: post.sources.into_iter().map(|s| s.0).collect(), + sender_posts: post.sender_posts.into_iter().map(Into::into).collect(), + receiver_posts: post.receiver_posts.into_iter().map(Into::into).collect(), + sinks: post.sinks.into_iter().map(|s| s.0).collect(), + validity_proof: post.validity_proof, + } + } +} + +impl From for config::TransferPost { + #[inline] + fn from(post: TransferPost) -> Self { + Self { + asset_id: post.asset_id.map(asset::AssetId), + sources: post.sources.into_iter().map(asset::AssetValue).collect(), + sender_posts: post.sender_posts.into_iter().map(Into::into).collect(), + receiver_posts: post.receiver_posts.into_iter().map(Into::into).collect(), + sinks: post.sinks.into_iter().map(asset::AssetValue).collect(), + validity_proof: post.validity_proof, + } + } +} + +/// Leaf Digest Type +pub type LeafDigest = merkle_tree::LeafDigest; + +/// Inner Digest Type +pub type InnerDigest = merkle_tree::InnerDigest; + +/// Merkle Tree Current Path +#[derive(Clone, Debug, Decode, Default, Encode, Eq, PartialEq, TypeInfo)] +pub struct CurrentPath { + /// Sibling Digest + pub sibling_digest: LeafDigest, + + /// Leaf Index + pub leaf_index: u32, + + /// Inner Path + pub inner_path: Vec, +} + +impl MaxEncodedLen for CurrentPath { + #[inline] + fn max_encoded_len() -> usize { + 0_usize + .saturating_add(LeafDigest::max_encoded_len()) + .saturating_add(u32::max_encoded_len()) + .saturating_add( + // NOTE: We know that these paths don't exceed the path length. + InnerDigest::max_encoded_len().saturating_mul( + manta_crypto::merkle_tree::path_length::(), + ), + ) + } +} + +impl From> for CurrentPath { + #[inline] + fn from(path: merkle_tree::CurrentPath) -> Self { + Self { + sibling_digest: path.sibling_digest, + leaf_index: path.inner_path.leaf_index.0 as u32, + inner_path: path.inner_path.path, + } + } +} + +impl From for merkle_tree::CurrentPath { + #[inline] + fn from(path: CurrentPath) -> Self { + Self::new( + path.sibling_digest, + (path.leaf_index as usize).into(), + path.inner_path, + ) + } +} + +/// UTXO Merkle Tree Path +#[derive(Clone, Debug, Decode, Default, Encode, Eq, MaxEncodedLen, PartialEq, TypeInfo)] +pub struct UtxoMerkleTreePath { + /// Current Leaf Digest + pub leaf_digest: Option, + + /// Current Path + pub current_path: CurrentPath, +} diff --git a/pallets/manta-pay/src/weights.rs b/pallets/manta-pay/src/weights.rs index 0b784543f..1fda6e0b6 100644 --- a/pallets/manta-pay/src/weights.rs +++ b/pallets/manta-pay/src/weights.rs @@ -38,16 +38,6 @@ impl crate::pallet::WeightInfo for WeightInfo where T: Config, { - /// ```text - /// Storage: MantaPay TotalSupply (r:1 w:0) - /// Storage: MantaPay Balances (r:2 w:2) - /// ``` - fn transfer() -> Weight { - (108_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(2 as Weight)) - } - /// ```text /// Storage: MantaPay Balances (r:1 w:1) /// Storage: MantaPay UtxoSet (r:1 w:1) diff --git a/runtime/primitives/Cargo.toml b/runtime/primitives/Cargo.toml index 067d123d5..431554798 100644 --- a/runtime/primitives/Cargo.toml +++ b/runtime/primitives/Cargo.toml @@ -16,8 +16,12 @@ scale-info = { version = "1.0", default-features = false, features = [ "derive" smallvec = "1.6.1" log = "0.4.14" +# manta-rs dependencies +manta-accounting = { path = "../../../manta-rs/manta-accounting", default-features = false } + # Substrate primitives frame-support = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.16" } +frame-system = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.16" } sp-consensus-aura = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.16" } sp-core = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.16" } sp-std = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.16" } @@ -37,6 +41,7 @@ std = [ 'sp-std/std', 'log/std', 'frame-support/std', + 'frame-system/std', 'sp-consensus-aura/std', 'sp-core/std', 'sp-io/std', diff --git a/runtime/primitives/src/assets.rs b/runtime/primitives/src/assets.rs index 6fbbc78a2..a7b282765 100644 --- a/runtime/primitives/src/assets.rs +++ b/runtime/primitives/src/assets.rs @@ -18,6 +18,8 @@ use codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_core::H160; use sp_std::{borrow::Borrow, marker::PhantomData, prelude::Vec}; +use frame_support::traits::tokens::{WithdrawConsequence, DepositConsequence}; +use crate::types::{AssetId, Balance}; ///! Manta/Calamari/Dolphin Asset use xcm::{ @@ -134,3 +136,83 @@ where .ok_or(()) } } + +pub enum FungibleLedgerConsequence{ + /// Deposit couldn't happen due to the amount being too low. This is usually because the + /// account doesn't yet exist and the deposit wouldn't bring it to at least the minimum needed + /// for existance. + BelowMinimum, + /// Deposit cannot happen since the account cannot be created (usually because it's a consumer + /// and there exists no provider reference). + CannotCreate, + /// The asset is unknown. Usually because an `AssetId` has been presented which doesn't exist + /// on the system. + UnknownAsset, + /// An overflow would occur. This is practically unexpected, but could happen in test systems + /// with extremely small balance types or balances that approach the max value of the balance + /// type. + Overflow, + /// There has been an underflow in the system. This is indicative of a corrupt state and + /// likely unrecoverable. + Underflow, + /// Account continued in existence. + /// Not enough of the funds in the account are unavailable for withdrawal. + Frozen, + /// Account balance would reduce to zero, potentially destroying it. The parameter is the + /// amount of balance which is destroyed. + ReducedToZero(Balance), + /// Withdraw could not happen since the amount to be withdrawn is less than the total funds in + /// the account. + NoFunds, + /// The withdraw would mean the account dying when it needs to exist (usually because it is a + /// provider and there are consumer references on it). + WouldDie, + /// Internal error. + InternalError, + /// Success + Success, +} + +impl From for FungibleLedgerConsequence { + fn from(dc: DepositConsequence) -> Self { + match dc { + DepositConsequence::BelowMinimum => FungibleLedgerConsequence::BelowMinimum, + DepositConsequence::CannotCreate => FungibleLedgerConsequence::CannotCreate, + DepositConsequence::Overflow => FungibleLedgerConsequence::Overflow, + DepositConsequence::Success => FungibleLedgerConsequence::Success, + DepositConsequence::UnknownAsset=> FungibleLedgerConsequence::UnknownAsset + } + } +} + +impl From> for FungibleLedgerConsequence { + fn from(wc: WithdrawConsequence) -> Self { + match wc { + WithdrawConsequence::Frozen => FungibleLedgerConsequence::Frozen, + WithdrawConsequence::NoFunds => FungibleLedgerConsequence::NoFunds, + WithdrawConsequence::Overflow => FungibleLedgerConsequence::Overflow, + WithdrawConsequence::Underflow => FungibleLedgerConsequence::Underflow, + WithdrawConsequence::ReducedToZero(balance) => FungibleLedgerConsequence::ReducedToZero(balance), + WithdrawConsequence::Success => FungibleLedgerConsequence::Success, + WithdrawConsequence::UnknownAsset => FungibleLedgerConsequence::UnknownAsset, + WithdrawConsequence::WouldDie => FungibleLedgerConsequence::WouldDie, + } + } +} + + +/// Unified interface for fungible ledger +/// It unifies `fungible` and `fungibles` +pub trait FungibleLedger +where C: frame_system::Config, +{ + + /// check whether `asset_id`, `account` can increase certain balance + fn can_deposit(asset_id: AssetId, account: &C::AccountId, amount: Balance) -> Result<(), FungibleLedgerConsequence>; + + /// check whether `asset_id`, `account` can decrease certain balance + fn can_withdraw(asset_d: AssetId, account: &C::AccountId, amount: Balance) -> Result<(), FungibleLedgerConsequence>; + + /// transfer asset + fn transfer(asset_id: AssetId, source: &C::AccountId, dest: &C::AccountId, amount: Balance) -> Result<(), FungibleLedgerConsequence>; +} \ No newline at end of file diff --git a/runtime/primitives/src/constants.rs b/runtime/primitives/src/constants.rs index 966285b4f..3e0f6dc6c 100644 --- a/runtime/primitives/src/constants.rs +++ b/runtime/primitives/src/constants.rs @@ -54,3 +54,4 @@ pub const ASSET_STRING_LIMIT: u32 = 50; pub const STAKING_PALLET_ID: PalletId = PalletId(*b"PotStake"); pub const TREASURY_PALLET_ID: PalletId = PalletId(*b"py/trsry"); pub const ASSET_MANAGER_PALLET_ID: PalletId = PalletId(*b"asstmngr"); +pub const MANTA_PAY_PALLET_ID: PalletId = PalletId(*b"mantapay"); diff --git a/runtime/primitives/src/types.rs b/runtime/primitives/src/types.rs index 00d9ce9ec..9d13d037d 100644 --- a/runtime/primitives/src/types.rs +++ b/runtime/primitives/src/types.rs @@ -16,6 +16,8 @@ use sp_runtime::traits::{BlakeTwo256, IdentifyAccount, Verify}; +pub use manta_accounting::asset::{AssetIdType, AssetValueType}; + /// An index to a block. pub type BlockNumber = u32; @@ -31,7 +33,7 @@ pub type AccountId = <::Signer as IdentifyAccount>::Account pub type AccountIndex = u32; /// Balance of an account. -pub type Balance = u128; +pub type Balance = AssetValueType; /// Index of a transaction in the chain. pub type Index = u32; @@ -53,4 +55,4 @@ pub type AuraId = sp_consensus_aura::sr25519::AuthorityId; pub type Moment = u64; // AssetId -pub type AssetId = u32; +pub type AssetId = AssetIdType;