From 944c1290192c75c0aa6207e669482e37a8cefb11 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 20 Sep 2020 15:14:49 +0200 Subject: [PATCH 01/34] Features needed for reserve-backed stablecoins --- Cargo.lock | 1 + frame/assets/Cargo.toml | 3 +- frame/assets/src/lib.rs | 424 ++++++++++++++++++++++++++++++++-------- 3 files changed, 344 insertions(+), 84 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e80ba143d3de8..37d6264554f22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4193,6 +4193,7 @@ version = "2.0.0-rc6" dependencies = [ "frame-support", "frame-system", + "pallet-balances", "parity-scale-codec", "serde", "sp-core", diff --git a/frame/assets/Cargo.toml b/frame/assets/Cargo.toml index bb7c2828c3062..a1eb65ec49ed3 100644 --- a/frame/assets/Cargo.toml +++ b/frame/assets/Cargo.toml @@ -14,6 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false } +sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } # Needed for various traits. In our case, `OnFinalize`. sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } # Needed for type-safe access to storage DB. @@ -23,8 +24,8 @@ frame-system = { version = "2.0.0-rc6", default-features = false, path = "../sys [dev-dependencies] sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -sp-std = { version = "2.0.0-rc6", path = "../../primitives/std" } sp-io = { version = "2.0.0-rc6", path = "../../primitives/io" } +pallet-balances = { version = "2.0.0-rc6", default-features = false, path = "../balances" } [features] default = ["std"] diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index e1303fcd03b0d..6925db805ce13 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -105,10 +105,10 @@ //! //! let asset_id = Self::next_asset_id(); //! -//! >::mutate(|asset_id| *asset_id += 1); -//! >::insert((asset_id, &ACCOUNT_ALICE), TOKENS_FIXED_SUPPLY / COUNT_AIRDROP_RECIPIENTS); -//! >::insert((asset_id, &ACCOUNT_BOB), TOKENS_FIXED_SUPPLY / COUNT_AIRDROP_RECIPIENTS); -//! >::insert(asset_id, TOKENS_FIXED_SUPPLY); +//! NextAssetId::::mutate(|asset_id| *asset_id += 1); +//! Balances::::insert((asset_id, &ACCOUNT_ALICE), TOKENS_FIXED_SUPPLY / COUNT_AIRDROP_RECIPIENTS); +//! Balances::::insert((asset_id, &ACCOUNT_BOB), TOKENS_FIXED_SUPPLY / COUNT_AIRDROP_RECIPIENTS); +//! TotalSupply::::insert(asset_id, TOKENS_FIXED_SUPPLY); //! //! Self::deposit_event(RawEvent::Issued(asset_id, sender, TOKENS_FIXED_SUPPLY)); //! Ok(()) @@ -133,10 +133,17 @@ // Ensure we're `no_std` when compiling for Wasm. #![cfg_attr(not(feature = "std"), no_std)] -use frame_support::{Parameter, decl_module, decl_event, decl_storage, decl_error, ensure}; -use sp_runtime::traits::{Member, AtLeast32Bit, AtLeast32BitUnsigned, Zero, StaticLookup}; +use sp_std::{fmt::Debug, ops::Add, iter::once}; +use sp_runtime::{RuntimeDebug, traits::{ + Member, AtLeast32Bit, AtLeast32BitUnsigned, Zero, StaticLookup, One, Saturating, +}}; +use codec::{Encode, Decode}; +use frame_support::{Parameter, decl_module, decl_event, decl_storage, decl_error, ensure, + traits::{Currency, ReservableCurrency}, dispatch::DispatchResult, +}; use frame_system::ensure_signed; -use sp_runtime::traits::One; + +type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; /// The module configuration trait. pub trait Trait: frame_system::Trait { @@ -148,6 +155,113 @@ pub trait Trait: frame_system::Trait { /// The arithmetic type of asset identifier. type AssetId: Parameter + AtLeast32Bit + Default + Copy; + + /// The currency mechanism. + type Currency: ReservableCurrency; +} + +#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug)] +pub struct AssetDetails< + Balance: Encode + Decode + Clone + Debug + Eq + PartialEq, + AccountId: Encode + Decode + Clone + Debug + Eq + PartialEq, + DepositBalance: Encode + Decode + Clone + Debug + Eq + PartialEq, +> { + /// Can change `owner`, `issuer`, `freezer` and `admin` accounts. + owner: AccountId, + /// Can mint tokens. + issuer: AccountId, + /// Can thaw tokens, force transfers and burn tokens from any account. + admin: AccountId, + /// Can freeze tokens. + freezer: AccountId, + /// The total supply across all accounts. + supply: Balance, + /// The balance deposited for this asset; this pays for the data stored here together with any + /// virtual accounts. + deposit: DepositBalance, + /// The number of balance-holding accounts that this asset may have, excluding those that were + /// created when they had a system-level ED. + max_zombies: u32, + /// The ED for virtual accounts. + minimum_balance: Balance, +} + +#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default)] +pub struct AccountData< + Balance: Encode + Decode + Clone + Debug + Eq + PartialEq, +> { + /// The balance. + balance: Balance, + /// Whether the account is frozen. + is_frozen: bool, +} + +// TODO: accounts holding funds must be in existence first, or... +// `deposit` should be sufficient to allow for N additional accounts in the state, +// and the creater can require a minimum balance here to ensure dust accounts don't use this up. + +decl_storage! { + trait Store for Module as Assets { + /// The number of units of assets held by any given account. + Account: map hasher(blake2_128_concat) (T::AssetId, T::AccountId) + => AccountData; + + /// The next asset identifier up for grabs. + NextAssetId get(fn next_asset_id): T::AssetId; + + /// Details of an asset. + Details: map hasher(twox_64_concat) T::AssetId => Option, + >>; + } +} + +// TODO: force_create, allowing for an assets `virtuals` to be hot-wired. + +decl_event! { + pub enum Event where + ::AccountId, + ::Balance, + ::AssetId, + { + /// Some assets were issued. \[asset_id, owner, total_supply\] + Created(AssetId, AccountId, AccountId), + /// Some assets were issued. \[asset_id, owner, total_supply\] + Issued(AssetId, AccountId, Balance), + /// Some assets were transferred. \[asset_id, from, to, amount\] + Transferred(AssetId, AccountId, AccountId, Balance), + /// Some assets were destroyed. \[asset_id, owner, balance\] + Burned(AssetId, AccountId, Balance), + /// The management team changed \[issuer, admin, freezer\] + TeamChanged(AssetId, AccountId, AccountId, AccountId) + /// The owner changed \[owner\] + OwnerChanged(AssetId, AccountId) + /// Some assets was transferred by an admin. \[asset_id, from, to, amount\] + ForceTransferred(AssetId, AccountId, AccountId, Balance), + /// Some account `who` was frozen. \[who\] + Frozen(AssetId, AccountId), + /// Some account `who` was thawed. \[who\] + Thawed(AssetId, AccountId), + } +} + +decl_error! { + pub enum Error for Module { + /// Transfer amount should be non-zero + AmountZero, + /// Account balance must be greater than or equal to the transfer amount + BalanceLow, + /// Balance should be non-zero + BalanceZero, + /// The signing account has no permission to do the operation. + NoPermission, + /// The given asset ID is unknown. + Unknown, + /// The origin account is frozen. + Frozen, + } } decl_module! { @@ -155,6 +269,7 @@ decl_module! { type Error = Error; fn deposit_event() = default; + /// Issue a new class of fungible assets. There are, and will only ever be, `total` /// such assets and they'll all belong to the `origin` initially. It will have an /// identifier `AssetId` instance: this will be specified in the `Issued` event. @@ -166,16 +281,46 @@ decl_module! { /// - 1 event. /// # #[weight = 0] - fn issue(origin, #[compact] total: T::Balance) { + fn create(origin, owner: ::Source) { let origin = ensure_signed(origin)?; + let owner = T::Lookup::lookup(owner)?; let id = Self::next_asset_id(); - >::mutate(|id| *id += One::one()); - - >::insert((id, &origin), total); - >::insert(id, total); + NextAssetId::::mutate(|id| *id += One::one()); + + Details::::insert(id, AssetDetails { + owner: owner.clone(), + issuer: owner.clone(), + admin: owner.clone(), + freezer: owner.clone(), + supply: Zero::zero(), + deposit: Zero::zero(), + max_zombies: 0, + minimum_balance: Zero::zero(), + }); + Self::deposit_event(RawEvent::Created(id, origin, owner)); + } - Self::deposit_event(RawEvent::Issued(id, origin, total)); + #[weight = 0] + fn mint(origin, + #[compact] id: T::AssetId, + beneficiary: ::Source, + #[compact] amount: T::Balance + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + let beneficiary = T::Lookup::lookup(beneficiary)?; + + Details::::try_mutate(id, |maybe_details| { + let d = maybe_details.as_mut().ok_or(Error::::Unknown)?; + ensure!(&origin == &d.issuer, Error::::NoPermission); + d.supply = d.supply.saturating_add(amount); + Account::::mutate((id, &beneficiary), |t| + t.balance = t.balance.satuating_add(amount) + ); + + Self::deposit_event(RawEvent::Issued(id, beneficiary, amount)); + Ok(()) + }) } /// Move some assets from one holder to another. @@ -192,74 +337,161 @@ decl_module! { target: ::Source, #[compact] amount: T::Balance ) { - let origin = ensure_signed(origin)?; - let origin_account = (id, origin.clone()); - let origin_balance = >::get(&origin_account); - let target = T::Lookup::lookup(target)?; + let origin = (id, ensure_signed(origin)?); ensure!(!amount.is_zero(), Error::::AmountZero); - ensure!(origin_balance >= amount, Error::::BalanceLow); - Self::deposit_event(RawEvent::Transferred(id, origin, target.clone(), amount)); - >::insert(origin_account, origin_balance - amount); - >::mutate((id, target), |balance| *balance += amount); + let origin_account = Accounts::::get(&origin); + ensure!(origin_account.balance >= amount, Error::::BalanceLow); + ensure!(!origin_account.is_frozen, Error::::Frozen); + + let dest = (id, T::Lookup::lookup(target)?); + + if origin_account.balance == amount { + Balance::::remove(&origin); + } else { + Balances::::insert(&origin, origin_account.balance - amount); + } + Balances::::mutate(&dest, |a| a.balance = a.balance.saturating_add(amount)); + + Self::deposit_event(RawEvent::Transferred(id, origin.1, dest.1, amount)); } - /// Destroy any assets of `id` owned by `origin`. + /// Destroy up to `amount` assets of `id` owned by `who`. /// - /// # - /// - `O(1)` - /// - 1 storage mutation (codec `O(1)`). - /// - 1 storage deletion (codec `O(1)`). - /// - 1 event. - /// # + /// Origin must be Signed from the Manager account. + /// + /// Bails with `BalanceZero` if the `who` is already dead. #[weight = 0] - fn destroy(origin, #[compact] id: T::AssetId) { + fn burn(origin, + #[compact] id: T::AssetId, + who: T::::Source, + #[compact] amount: T::Balance + ) -> DispatchResult { let origin = ensure_signed(origin)?; - let balance = >::take((id, &origin)); - ensure!(!balance.is_zero(), Error::::BalanceZero); + let who = T::Lookup::lookup(who)?; + + Details::::try_mutate(id, |maybe_details| { + let d = maybe_details.as_mut().ok_or(Error::::Unknown)?; + ensure!(&origin == &d.admin, Error::::NoPermission); + + let burned = Account::::try_mutate_exists((id, &who), |maybe_account| { + if let Some(mut account) = maybe_account.take() { + let burned = amount.min(account.balance); + account.balance -= burned; + *maybe_account = if account.balance.is_zero() { + None + } else { + Some(account) + }; + Ok(burned) + } else { + Err(Error::::BalanceZero)?; + } + })?; + + d.supply = d.supply.saturating_sub(burned); + + Self::deposit_event(RawEvent::Burned(id, who, burned)); + Ok(()) + }) + } + + #[weight = 0] + fn set_owner(origin, owner: ::Source) { + let origin = ensure_signed(origin)?; + let owner = T::Lookup::lookup(owner)?; - >::mutate(id, |total_supply| *total_supply -= balance); - Self::deposit_event(RawEvent::Destroyed(id, origin, balance)); + Details::::try_mutate(id, |maybe_details| { + let d = maybe_details.as_mut().ok_or(Error::::Unknown)?; + ensure!(&origin == &d.owner, Error::::NoPermission); + + d.owner = owner.clone(); + + Self::deposit_event(OwnerChanged(id, owner)) + }) } - } -} -decl_event! { - pub enum Event where - ::AccountId, - ::Balance, - ::AssetId, - { - /// Some assets were issued. \[asset_id, owner, total_supply\] - Issued(AssetId, AccountId, Balance), - /// Some assets were transferred. \[asset_id, from, to, amount\] - Transferred(AssetId, AccountId, AccountId, Balance), - /// Some assets were destroyed. \[asset_id, owner, balance\] - Destroyed(AssetId, AccountId, Balance), - } -} -decl_error! { - pub enum Error for Module { - /// Transfer amount should be non-zero - AmountZero, - /// Account balance must be greater than or equal to the transfer amount - BalanceLow, - /// Balance should be non-zero - BalanceZero, - } -} + #[weight = 0] + fn set_team(origin, + issuer: ::Source, + admin: ::Source, + freezer: ::Source, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + let issuer = T::Lookup::lookup(issuer)?; + let admin = T::Lookup::lookup(admin)?; + let freezer = T::Lookup::lookup(freezer)?; -decl_storage! { - trait Store for Module as Assets { - /// The number of units of assets held by any given account. - Balances: map hasher(blake2_128_concat) (T::AssetId, T::AccountId) => T::Balance; - /// The next asset identifier up for grabs. - NextAssetId get(fn next_asset_id): T::AssetId; - /// The total unit supply of an asset. - /// - /// TWOX-NOTE: `AssetId` is trusted, so this is safe. - TotalSupply: map hasher(twox_64_concat) T::AssetId => T::Balance; + Details::::try_mutate(id, |maybe_details| { + let d = maybe_details.as_mut().ok_or(Error::::Unknown)?; + ensure!(&origin == &d.owner, Error::::NoPermission); + + d.issuer = issuer.clone(); + d.admin = admin.clone(); + d.freezer = freezer.clone(); + + Self::deposit_event(TeamChanged(id, issuer, admin, freezer)) + }) + } + + #[weight = 0] + fn freeze(origin, who: ::Source) { + let origin = ensure_signed(origin)?; + + let d = Details::::get(id).ok_or(Error::::Unknown)?; + ensure!(&origin == &d.admin, Error::::NoPermission); + + let who = (id, T::Lookup::lookup(who)?); + Account::::mutate(&who, |a| a.is_frozen = true); + + Self::deposit_event(Event::::Frozen(id, who)); + } + + #[weight = 0] + fn thaw(origin, who: ::Source) { + let origin = ensure_signed(origin)?; + + let d = Details::::get(id).ok_or(Error::::Unknown)?; + ensure!(&origin == &d.admin, Error::::NoPermission); + + let who = (id, T::Lookup::lookup(who)?); + Account::::mutate(&who, |a| a.is_frozen = false); + + Self::deposit_event(Event::::Thawed(id, who)); + } + + #[weight = 0] + fn force_transfer(origin, + #[compact] id: T::AssetId, + source: ::Source, + dest: ::Source, + #[compact] amount: T::Balance, + ) { + let origin = ensure_signed(origin)?; + + let d = Details::::get(id).ok_or(Error::::Unknown)?; + ensure!(&origin == &d.admin, Error::::NoPermission); + + let source = (id, T::Lookup::lookup(source)?); + let mut source_account = Accounts::::get(&source); + let amount = amount.min(source_account.balance); + + ensure!(!amount.is_zero(), Error::::AmountZero); + + let dest = (id, T::Lookup::lookup(dest)?); + + source_account.balance -= amount; + if source_account.balance.is_zero() { + Account::::remove(&source); + } else { + Account::::insert(&source, source_account); + } + + Balances::::mutate(&dest, |a| a.balance = a.balance.saturating_add(amount)); + + Self::deposit_event(RawEvent::ForceTransferred(id, source.1, dest.1, amount)); + } } } @@ -269,12 +501,12 @@ impl Module { /// Get the asset `id` balance of `who`. pub fn balance(id: T::AssetId, who: T::AccountId) -> T::Balance { - >::get((id, who)) + Balances::::get((id, who)) } /// Get the total supply of an asset `id`. pub fn total_supply(id: T::AssetId) -> T::Balance { - >::get(id) + Details::::get(id).map(|x| x.supply).unwrap_or_else(Zero::zero) } } @@ -320,16 +552,34 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); - type AccountData = (); + type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); type SystemWeightInfo = (); } + + parameter_types! { + pub const ExistentialDeposit: u64 = 1; + } + + impl pallet_balances::Trait for Test { + type MaxLocks = (); + type Balance = u64; + type DustRemoval = (); + type Event = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + } + impl Trait for Test { + type Currency = Balances; type Event = (); type Balance = u64; type AssetId = u32; } + type System = frame_system::Module; + type Balances = pallet_balances::Module; type Assets = Module; fn new_test_ext() -> sp_io::TestExternalities { @@ -339,7 +589,8 @@ mod tests { #[test] fn issuing_asset_units_to_issuer_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::issue(Origin::signed(1), 100)); + assert_ok!(Assets::create(Origin::signed(1), 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); }); } @@ -347,7 +598,8 @@ mod tests { #[test] fn querying_total_supply_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::issue(Origin::signed(1), 100)); + assert_ok!(Assets::create(Origin::signed(1), 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); assert_eq!(Assets::balance(0, 1), 50); @@ -356,7 +608,7 @@ mod tests { assert_eq!(Assets::balance(0, 1), 50); assert_eq!(Assets::balance(0, 2), 19); assert_eq!(Assets::balance(0, 3), 31); - assert_ok!(Assets::destroy(Origin::signed(3), 0)); + assert_ok!(Assets::destroy(Origin::signed(3), 0, u64::max_value())); assert_eq!(Assets::total_supply(0), 69); }); } @@ -364,7 +616,8 @@ mod tests { #[test] fn transferring_amount_above_available_balance_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::issue(Origin::signed(1), 100)); + assert_ok!(Assets::create(Origin::signed(1), 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); assert_eq!(Assets::balance(0, 1), 50); @@ -375,7 +628,8 @@ mod tests { #[test] fn transferring_amount_more_than_available_balance_should_not_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::issue(Origin::signed(1), 100)); + assert_ok!(Assets::create(Origin::signed(1), 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); assert_eq!(Assets::balance(0, 1), 50); @@ -389,7 +643,8 @@ mod tests { #[test] fn transferring_less_than_one_unit_should_not_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::issue(Origin::signed(1), 100)); + assert_ok!(Assets::create(Origin::signed(1), 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); assert_noop!(Assets::transfer(Origin::signed(1), 0, 2, 0), Error::::AmountZero); }); @@ -398,7 +653,8 @@ mod tests { #[test] fn transferring_more_units_than_total_supply_should_not_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::issue(Origin::signed(1), 100)); + assert_ok!(Assets::create(Origin::signed(1), 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); assert_noop!(Assets::transfer(Origin::signed(1), 0, 2, 101), Error::::BalanceLow); }); @@ -407,18 +663,20 @@ mod tests { #[test] fn destroying_asset_balance_with_positive_balance_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::issue(Origin::signed(1), 100)); + assert_ok!(Assets::create(Origin::signed(1), 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); - assert_ok!(Assets::destroy(Origin::signed(1), 0)); + assert_ok!(Assets::burn(Origin::signed(1), 0, u64::max_value())); }); } #[test] fn destroying_asset_balance_with_zero_balance_should_not_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::issue(Origin::signed(1), 100)); + assert_ok!(Assets::create(Origin::signed(1), 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 2), 0); - assert_noop!(Assets::destroy(Origin::signed(2), 0), Error::::BalanceZero); + assert_noop!(Assets::burn(Origin::signed(2), 0, u64::max_value()), Error::::BalanceZero); }); } } From 911795a67e657d6525cefce1e56e2968d0c489b2 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 20 Sep 2020 16:55:21 +0200 Subject: [PATCH 02/34] Builds & tests. --- frame/assets/src/lib.rs | 70 ++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 6925db805ce13..2308d03f92426 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -133,9 +133,9 @@ // Ensure we're `no_std` when compiling for Wasm. #![cfg_attr(not(feature = "std"), no_std)] -use sp_std::{fmt::Debug, ops::Add, iter::once}; +use sp_std::{fmt::Debug}; use sp_runtime::{RuntimeDebug, traits::{ - Member, AtLeast32Bit, AtLeast32BitUnsigned, Zero, StaticLookup, One, Saturating, + Member, AtLeast32Bit, AtLeast32BitUnsigned, Zero, StaticLookup, One, Saturating, CheckedSub }}; use codec::{Encode, Decode}; use frame_support::{Parameter, decl_module, decl_event, decl_storage, decl_error, ensure, @@ -234,15 +234,15 @@ decl_event! { Transferred(AssetId, AccountId, AccountId, Balance), /// Some assets were destroyed. \[asset_id, owner, balance\] Burned(AssetId, AccountId, Balance), - /// The management team changed \[issuer, admin, freezer\] - TeamChanged(AssetId, AccountId, AccountId, AccountId) - /// The owner changed \[owner\] - OwnerChanged(AssetId, AccountId) + /// The management team changed \[asset_id, issuer, admin, freezer\] + TeamChanged(AssetId, AccountId, AccountId, AccountId), + /// The owner changed \[asset_id, owner\] + OwnerChanged(AssetId, AccountId), /// Some assets was transferred by an admin. \[asset_id, from, to, amount\] ForceTransferred(AssetId, AccountId, AccountId, Balance), - /// Some account `who` was frozen. \[who\] + /// Some account `who` was frozen. \[asset_id, who\] Frozen(AssetId, AccountId), - /// Some account `who` was thawed. \[who\] + /// Some account `who` was thawed. \[asset_id, who\] Thawed(AssetId, AccountId), } } @@ -315,7 +315,7 @@ decl_module! { ensure!(&origin == &d.issuer, Error::::NoPermission); d.supply = d.supply.saturating_add(amount); Account::::mutate((id, &beneficiary), |t| - t.balance = t.balance.satuating_add(amount) + t.balance = t.balance.saturating_add(amount) ); Self::deposit_event(RawEvent::Issued(id, beneficiary, amount)); @@ -340,18 +340,19 @@ decl_module! { let origin = (id, ensure_signed(origin)?); ensure!(!amount.is_zero(), Error::::AmountZero); - let origin_account = Accounts::::get(&origin); - ensure!(origin_account.balance >= amount, Error::::BalanceLow); + let mut origin_account = Account::::get(&origin); ensure!(!origin_account.is_frozen, Error::::Frozen); + origin_account.balance = origin_account.balance.checked_sub(&amount) + .ok_or(Error::::BalanceLow)?; let dest = (id, T::Lookup::lookup(target)?); - if origin_account.balance == amount { - Balance::::remove(&origin); + if origin_account.balance.is_zero() { + Account::::remove(&origin); } else { - Balances::::insert(&origin, origin_account.balance - amount); + Account::::insert(&origin, origin_account); } - Balances::::mutate(&dest, |a| a.balance = a.balance.saturating_add(amount)); + Account::::mutate(&dest, |a| a.balance = a.balance.saturating_add(amount)); Self::deposit_event(RawEvent::Transferred(id, origin.1, dest.1, amount)); } @@ -364,7 +365,7 @@ decl_module! { #[weight = 0] fn burn(origin, #[compact] id: T::AssetId, - who: T::::Source, + who: ::Source, #[compact] amount: T::Balance ) -> DispatchResult { let origin = ensure_signed(origin)?; @@ -385,7 +386,7 @@ decl_module! { }; Ok(burned) } else { - Err(Error::::BalanceZero)?; + Err(Error::::BalanceZero) } })?; @@ -397,7 +398,10 @@ decl_module! { } #[weight = 0] - fn set_owner(origin, owner: ::Source) { + fn set_owner(origin, + #[compact] id: T::AssetId, + owner: ::Source, + ) -> DispatchResult { let origin = ensure_signed(origin)?; let owner = T::Lookup::lookup(owner)?; @@ -407,13 +411,14 @@ decl_module! { d.owner = owner.clone(); - Self::deposit_event(OwnerChanged(id, owner)) + Self::deposit_event(RawEvent::OwnerChanged(id, owner)); + Ok(()) }) } - #[weight = 0] fn set_team(origin, + #[compact] id: T::AssetId, issuer: ::Source, admin: ::Source, freezer: ::Source, @@ -431,12 +436,13 @@ decl_module! { d.admin = admin.clone(); d.freezer = freezer.clone(); - Self::deposit_event(TeamChanged(id, issuer, admin, freezer)) + Self::deposit_event(RawEvent::TeamChanged(id, issuer, admin, freezer)); + Ok(()) }) } #[weight = 0] - fn freeze(origin, who: ::Source) { + fn freeze(origin, #[compact] id: T::AssetId, who: ::Source) { let origin = ensure_signed(origin)?; let d = Details::::get(id).ok_or(Error::::Unknown)?; @@ -445,11 +451,11 @@ decl_module! { let who = (id, T::Lookup::lookup(who)?); Account::::mutate(&who, |a| a.is_frozen = true); - Self::deposit_event(Event::::Frozen(id, who)); + Self::deposit_event(Event::::Frozen(id, who.1)); } #[weight = 0] - fn thaw(origin, who: ::Source) { + fn thaw(origin, #[compact] id: T::AssetId, who: ::Source) { let origin = ensure_signed(origin)?; let d = Details::::get(id).ok_or(Error::::Unknown)?; @@ -458,7 +464,7 @@ decl_module! { let who = (id, T::Lookup::lookup(who)?); Account::::mutate(&who, |a| a.is_frozen = false); - Self::deposit_event(Event::::Thawed(id, who)); + Self::deposit_event(Event::::Thawed(id, who.1)); } #[weight = 0] @@ -474,7 +480,7 @@ decl_module! { ensure!(&origin == &d.admin, Error::::NoPermission); let source = (id, T::Lookup::lookup(source)?); - let mut source_account = Accounts::::get(&source); + let mut source_account = Account::::get(&source); let amount = amount.min(source_account.balance); ensure!(!amount.is_zero(), Error::::AmountZero); @@ -488,7 +494,7 @@ decl_module! { Account::::insert(&source, source_account); } - Balances::::mutate(&dest, |a| a.balance = a.balance.saturating_add(amount)); + Account::::mutate(&dest, |a| a.balance = a.balance.saturating_add(amount)); Self::deposit_event(RawEvent::ForceTransferred(id, source.1, dest.1, amount)); } @@ -501,7 +507,7 @@ impl Module { /// Get the asset `id` balance of `who`. pub fn balance(id: T::AssetId, who: T::AccountId) -> T::Balance { - Balances::::get((id, who)) + Account::::get((id, who)).balance } /// Get the total supply of an asset `id`. @@ -608,7 +614,7 @@ mod tests { assert_eq!(Assets::balance(0, 1), 50); assert_eq!(Assets::balance(0, 2), 19); assert_eq!(Assets::balance(0, 3), 31); - assert_ok!(Assets::destroy(Origin::signed(3), 0, u64::max_value())); + assert_ok!(Assets::burn(Origin::signed(1), 0, 3, u64::max_value())); assert_eq!(Assets::total_supply(0), 69); }); } @@ -634,7 +640,7 @@ mod tests { assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); assert_eq!(Assets::balance(0, 1), 50); assert_eq!(Assets::balance(0, 2), 50); - assert_ok!(Assets::destroy(Origin::signed(1), 0)); + assert_ok!(Assets::burn(Origin::signed(1), 0, 1, u64::max_value())); assert_eq!(Assets::balance(0, 1), 0); assert_noop!(Assets::transfer(Origin::signed(1), 0, 1, 50), Error::::BalanceLow); }); @@ -666,7 +672,7 @@ mod tests { assert_ok!(Assets::create(Origin::signed(1), 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); - assert_ok!(Assets::burn(Origin::signed(1), 0, u64::max_value())); + assert_ok!(Assets::burn(Origin::signed(1), 0, 1, u64::max_value())); }); } @@ -676,7 +682,7 @@ mod tests { assert_ok!(Assets::create(Origin::signed(1), 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 2), 0); - assert_noop!(Assets::burn(Origin::signed(2), 0, u64::max_value()), Error::::BalanceZero); + assert_noop!(Assets::burn(Origin::signed(1), 0, 2, u64::max_value()), Error::::BalanceZero); }); } } From ddf83eb5ab514588281fb20aac9df4e048a5508b Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 20 Sep 2020 17:19:38 +0200 Subject: [PATCH 03/34] Double map for an efficient destroy. --- frame/assets/src/lib.rs | 96 +++++++++++++++++++++++++---------------- 1 file changed, 58 insertions(+), 38 deletions(-) diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 2308d03f92426..e11283777cd8f 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -135,7 +135,7 @@ use sp_std::{fmt::Debug}; use sp_runtime::{RuntimeDebug, traits::{ - Member, AtLeast32Bit, AtLeast32BitUnsigned, Zero, StaticLookup, One, Saturating, CheckedSub + Member, AtLeast32Bit, AtLeast32BitUnsigned, Zero, StaticLookup, Saturating, CheckedSub }}; use codec::{Encode, Decode}; use frame_support::{Parameter, decl_module, decl_event, decl_storage, decl_error, ensure, @@ -203,7 +203,9 @@ pub struct AccountData< decl_storage! { trait Store for Module as Assets { /// The number of units of assets held by any given account. - Account: map hasher(blake2_128_concat) (T::AssetId, T::AccountId) + Account: double_map + hasher(blake2_128_concat) T::AssetId, + hasher(blake2_128_concat) T::AccountId => AccountData; /// The next asset identifier up for grabs. @@ -226,7 +228,7 @@ decl_event! { ::Balance, ::AssetId, { - /// Some assets were issued. \[asset_id, owner, total_supply\] + /// Some asset class was created. \[asset_id, creator, owner\] Created(AssetId, AccountId, AccountId), /// Some assets were issued. \[asset_id, owner, total_supply\] Issued(AssetId, AccountId, Balance), @@ -244,6 +246,8 @@ decl_event! { Frozen(AssetId, AccountId), /// Some account `who` was thawed. \[asset_id, who\] Thawed(AssetId, AccountId), + /// An asset class was destroyed. + Destroyed(AssetId), } } @@ -261,6 +265,8 @@ decl_error! { Unknown, /// The origin account is frozen. Frozen, + /// The asset ID is already taken. + InUse, } } @@ -270,6 +276,8 @@ decl_module! { fn deposit_event() = default; + // TODO: destroy_asset + /// Issue a new class of fungible assets. There are, and will only ever be, `total` /// such assets and they'll all belong to the `origin` initially. It will have an /// identifier `AssetId` instance: this will be specified in the `Issued` event. @@ -281,13 +289,11 @@ decl_module! { /// - 1 event. /// # #[weight = 0] - fn create(origin, owner: ::Source) { + fn create(origin, #[compact] id: T::AssetId, owner: ::Source) { let origin = ensure_signed(origin)?; let owner = T::Lookup::lookup(owner)?; - let id = Self::next_asset_id(); - NextAssetId::::mutate(|id| *id += One::one()); - + ensure!(!Details::::contains_key(id), Error::::InUse); Details::::insert(id, AssetDetails { owner: owner.clone(), issuer: owner.clone(), @@ -301,6 +307,20 @@ decl_module! { Self::deposit_event(RawEvent::Created(id, origin, owner)); } + #[weight = 0] + fn destroy(origin, #[compact] id: T::AssetId) -> DispatchResult { + let origin = ensure_signed(origin)?; + + Details::::try_mutate_exists(id, |maybe_details| { + let details = maybe_details.take().ok_or(Error::::Unknown)?; + ensure!(details.owner == origin, Error::::NoPermission); + *maybe_details = None; + Account::::remove_prefix(&id); + Self::deposit_event(RawEvent::Destroyed(id)); + Ok(()) + }) + } + #[weight = 0] fn mint(origin, #[compact] id: T::AssetId, @@ -314,7 +334,7 @@ decl_module! { let d = maybe_details.as_mut().ok_or(Error::::Unknown)?; ensure!(&origin == &d.issuer, Error::::NoPermission); d.supply = d.supply.saturating_add(amount); - Account::::mutate((id, &beneficiary), |t| + Account::::mutate(id, &beneficiary, |t| t.balance = t.balance.saturating_add(amount) ); @@ -337,24 +357,24 @@ decl_module! { target: ::Source, #[compact] amount: T::Balance ) { - let origin = (id, ensure_signed(origin)?); + let origin = ensure_signed(origin)?; ensure!(!amount.is_zero(), Error::::AmountZero); - let mut origin_account = Account::::get(&origin); + let mut origin_account = Account::::get(id, &origin); ensure!(!origin_account.is_frozen, Error::::Frozen); origin_account.balance = origin_account.balance.checked_sub(&amount) .ok_or(Error::::BalanceLow)?; - let dest = (id, T::Lookup::lookup(target)?); + let dest = T::Lookup::lookup(target)?; if origin_account.balance.is_zero() { - Account::::remove(&origin); + Account::::remove(id, &origin); } else { - Account::::insert(&origin, origin_account); + Account::::insert(id, &origin, origin_account); } - Account::::mutate(&dest, |a| a.balance = a.balance.saturating_add(amount)); + Account::::mutate(id, &dest, |a| a.balance = a.balance.saturating_add(amount)); - Self::deposit_event(RawEvent::Transferred(id, origin.1, dest.1, amount)); + Self::deposit_event(RawEvent::Transferred(id, origin, dest, amount)); } /// Destroy up to `amount` assets of `id` owned by `who`. @@ -375,7 +395,7 @@ decl_module! { let d = maybe_details.as_mut().ok_or(Error::::Unknown)?; ensure!(&origin == &d.admin, Error::::NoPermission); - let burned = Account::::try_mutate_exists((id, &who), |maybe_account| { + let burned = Account::::try_mutate_exists(id, &who, |maybe_account| { if let Some(mut account) = maybe_account.take() { let burned = amount.min(account.balance); account.balance -= burned; @@ -448,10 +468,10 @@ decl_module! { let d = Details::::get(id).ok_or(Error::::Unknown)?; ensure!(&origin == &d.admin, Error::::NoPermission); - let who = (id, T::Lookup::lookup(who)?); - Account::::mutate(&who, |a| a.is_frozen = true); + let who = T::Lookup::lookup(who)?; + Account::::mutate(id, &who, |a| a.is_frozen = true); - Self::deposit_event(Event::::Frozen(id, who.1)); + Self::deposit_event(Event::::Frozen(id, who)); } #[weight = 0] @@ -461,10 +481,10 @@ decl_module! { let d = Details::::get(id).ok_or(Error::::Unknown)?; ensure!(&origin == &d.admin, Error::::NoPermission); - let who = (id, T::Lookup::lookup(who)?); - Account::::mutate(&who, |a| a.is_frozen = false); + let who = T::Lookup::lookup(who)?; + Account::::mutate(id, &who, |a| a.is_frozen = false); - Self::deposit_event(Event::::Thawed(id, who.1)); + Self::deposit_event(Event::::Thawed(id, who)); } #[weight = 0] @@ -479,24 +499,24 @@ decl_module! { let d = Details::::get(id).ok_or(Error::::Unknown)?; ensure!(&origin == &d.admin, Error::::NoPermission); - let source = (id, T::Lookup::lookup(source)?); - let mut source_account = Account::::get(&source); + let source = T::Lookup::lookup(source)?; + let mut source_account = Account::::get(id, &source); let amount = amount.min(source_account.balance); ensure!(!amount.is_zero(), Error::::AmountZero); - let dest = (id, T::Lookup::lookup(dest)?); + let dest = T::Lookup::lookup(dest)?; source_account.balance -= amount; if source_account.balance.is_zero() { - Account::::remove(&source); + Account::::remove(id, &source); } else { - Account::::insert(&source, source_account); + Account::::insert(id, &source, source_account); } - Account::::mutate(&dest, |a| a.balance = a.balance.saturating_add(amount)); + Account::::mutate(id, &dest, |a| a.balance = a.balance.saturating_add(amount)); - Self::deposit_event(RawEvent::ForceTransferred(id, source.1, dest.1, amount)); + Self::deposit_event(RawEvent::ForceTransferred(id, source, dest, amount)); } } } @@ -507,7 +527,7 @@ impl Module { /// Get the asset `id` balance of `who`. pub fn balance(id: T::AssetId, who: T::AccountId) -> T::Balance { - Account::::get((id, who)).balance + Account::::get(id, who).balance } /// Get the total supply of an asset `id`. @@ -595,7 +615,7 @@ mod tests { #[test] fn issuing_asset_units_to_issuer_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::create(Origin::signed(1), 1)); + assert_ok!(Assets::create(Origin::signed(1), 0, 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); }); @@ -604,7 +624,7 @@ mod tests { #[test] fn querying_total_supply_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::create(Origin::signed(1), 1)); + assert_ok!(Assets::create(Origin::signed(1), 0, 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); @@ -622,7 +642,7 @@ mod tests { #[test] fn transferring_amount_above_available_balance_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::create(Origin::signed(1), 1)); + assert_ok!(Assets::create(Origin::signed(1), 0, 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); @@ -634,7 +654,7 @@ mod tests { #[test] fn transferring_amount_more_than_available_balance_should_not_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::create(Origin::signed(1), 1)); + assert_ok!(Assets::create(Origin::signed(1), 0, 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); @@ -649,7 +669,7 @@ mod tests { #[test] fn transferring_less_than_one_unit_should_not_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::create(Origin::signed(1), 1)); + assert_ok!(Assets::create(Origin::signed(1), 0, 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); assert_noop!(Assets::transfer(Origin::signed(1), 0, 2, 0), Error::::AmountZero); @@ -659,7 +679,7 @@ mod tests { #[test] fn transferring_more_units_than_total_supply_should_not_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::create(Origin::signed(1), 1)); + assert_ok!(Assets::create(Origin::signed(1), 0, 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); assert_noop!(Assets::transfer(Origin::signed(1), 0, 2, 101), Error::::BalanceLow); @@ -669,7 +689,7 @@ mod tests { #[test] fn destroying_asset_balance_with_positive_balance_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::create(Origin::signed(1), 1)); + assert_ok!(Assets::create(Origin::signed(1), 0, 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); assert_ok!(Assets::burn(Origin::signed(1), 0, 1, u64::max_value())); @@ -679,7 +699,7 @@ mod tests { #[test] fn destroying_asset_balance_with_zero_balance_should_not_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::create(Origin::signed(1), 1)); + assert_ok!(Assets::create(Origin::signed(1), 0, 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 2), 0); assert_noop!(Assets::burn(Origin::signed(1), 0, 2, u64::max_value()), Error::::BalanceZero); From 9c79b6dc9dac36fede0f229b79a61ca818744933 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Mon, 21 Sep 2020 14:15:22 +0200 Subject: [PATCH 04/34] Update frame/assets/src/lib.rs Co-authored-by: Nikolay Volf --- frame/assets/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index e11283777cd8f..a7b93409375ee 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -135,7 +135,7 @@ use sp_std::{fmt::Debug}; use sp_runtime::{RuntimeDebug, traits::{ - Member, AtLeast32Bit, AtLeast32BitUnsigned, Zero, StaticLookup, Saturating, CheckedSub + Member, AtLeast32Bit, AtLeast32BitUnsigned, Zero, StaticLookup, Saturating, CheckedSub, }}; use codec::{Encode, Decode}; use frame_support::{Parameter, decl_module, decl_event, decl_storage, decl_error, ensure, From b8ef2fbb60189e06e8fe985ebcfba4be83991e4d Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 21 Sep 2020 16:18:57 +0200 Subject: [PATCH 05/34] ED/zombie-count/refs Feature: ED/minimum balance enforcement Feature: enforce zombie count Feature: allow system-alive accounts to exist, but add reference --- frame/assets/src/lib.rs | 406 ++++++++++++++++++++++++++++------------ frame/system/src/lib.rs | 4 + 2 files changed, 289 insertions(+), 121 deletions(-) diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index e11283777cd8f..6a4fd930b1245 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -139,7 +139,8 @@ use sp_runtime::{RuntimeDebug, traits::{ }}; use codec::{Encode, Decode}; use frame_support::{Parameter, decl_module, decl_event, decl_storage, decl_error, ensure, - traits::{Currency, ReservableCurrency}, dispatch::DispatchResult, + traits::{Currency, ReservableCurrency, EnsureOrigin, Get}, + dispatch::{DispatchResult, DispatchError}, }; use frame_system::ensure_signed; @@ -158,6 +159,12 @@ pub trait Trait: frame_system::Trait { /// The currency mechanism. type Currency: ReservableCurrency; + + /// The origin which may forcibly create or destroy an asset. Root can always do this. + type ForceOrigin: EnsureOrigin; + + type AssetDepositBase: Get>; + type AssetDepositPerZombie: Get>; } #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug)] @@ -184,6 +191,8 @@ pub struct AssetDetails< max_zombies: u32, /// The ED for virtual accounts. minimum_balance: Balance, + /// The current number of zombie accounts. + zombies: u32, } #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default)] @@ -194,6 +203,8 @@ pub struct AccountData< balance: Balance, /// Whether the account is frozen. is_frozen: bool, + /// Whether the account is a zombie. If not, then it has a reference. + is_zombie: bool, } // TODO: accounts holding funds must be in existence first, or... @@ -202,26 +213,21 @@ pub struct AccountData< decl_storage! { trait Store for Module as Assets { - /// The number of units of assets held by any given account. - Account: double_map - hasher(blake2_128_concat) T::AssetId, - hasher(blake2_128_concat) T::AccountId - => AccountData; - - /// The next asset identifier up for grabs. - NextAssetId get(fn next_asset_id): T::AssetId; - /// Details of an asset. - Details: map hasher(twox_64_concat) T::AssetId => Option Option, >>; + + /// The number of units of assets held by any given account. + Account: double_map + hasher(blake2_128_concat) T::AssetId, + hasher(blake2_128_concat) T::AccountId + => AccountData; } } -// TODO: force_create, allowing for an assets `virtuals` to be hot-wired. - decl_event! { pub enum Event where ::AccountId, @@ -248,6 +254,8 @@ decl_event! { Thawed(AssetId, AccountId), /// An asset class was destroyed. Destroyed(AssetId), + /// Some asset class was force-created. \[asset_id, owner\] + ForceCreated(AssetId, AccountId), } } @@ -267,17 +275,19 @@ decl_error! { Frozen, /// The asset ID is already taken. InUse, + /// Too many zombie accounts in use. + TooManyZombies, } } +// TODO: refs should be handled in this module because system refs are u8 only. + decl_module! { pub struct Module for enum Call where origin: T::Origin { type Error = Error; fn deposit_event() = default; - // TODO: destroy_asset - /// Issue a new class of fungible assets. There are, and will only ever be, `total` /// such assets and they'll all belong to the `origin` initially. It will have an /// identifier `AssetId` instance: this will be specified in the `Issued` event. @@ -289,29 +299,67 @@ decl_module! { /// - 1 event. /// # #[weight = 0] - fn create(origin, #[compact] id: T::AssetId, owner: ::Source) { + fn create(origin, + #[compact] id: T::AssetId, + owner: ::Source, + max_zombies: u32, + minimum_balance: T::Balance, + ) { let origin = ensure_signed(origin)?; let owner = T::Lookup::lookup(owner)?; - ensure!(!Details::::contains_key(id), Error::::InUse); - Details::::insert(id, AssetDetails { + ensure!(!Asset::::contains_key(id), Error::::InUse); + + let deposit = T::AssetDepositPerZombie::get() + .saturating_mul(max_zombies.into()) + .saturating_add(T::AssetDepositBase::get()); + T::Currency::reserve(&origin, deposit)?; + + Asset::::insert(id, AssetDetails { owner: owner.clone(), issuer: owner.clone(), admin: owner.clone(), freezer: owner.clone(), supply: Zero::zero(), - deposit: Zero::zero(), - max_zombies: 0, - minimum_balance: Zero::zero(), + deposit, + max_zombies, + minimum_balance, + zombies: Zero::zero(), }); Self::deposit_event(RawEvent::Created(id, origin, owner)); } + #[weight = 0] + fn force_create(origin, + #[compact] id: T::AssetId, + owner: ::Source, + max_zombies: u32, + minimum_balance: T::Balance, + ) { + T::ForceOrigin::ensure_origin(origin)?; + let owner = T::Lookup::lookup(owner)?; + + ensure!(!Asset::::contains_key(id), Error::::InUse); + + Asset::::insert(id, AssetDetails { + owner: owner.clone(), + issuer: owner.clone(), + admin: owner.clone(), + freezer: owner.clone(), + supply: Zero::zero(), + deposit: Zero::zero(), + max_zombies, + minimum_balance, + zombies: Zero::zero(), + }); + Self::deposit_event(RawEvent::ForceCreated(id, owner)); + } + #[weight = 0] fn destroy(origin, #[compact] id: T::AssetId) -> DispatchResult { let origin = ensure_signed(origin)?; - Details::::try_mutate_exists(id, |maybe_details| { + Asset::::try_mutate_exists(id, |maybe_details| { let details = maybe_details.take().ok_or(Error::::Unknown)?; ensure!(details.owner == origin, Error::::NoPermission); *maybe_details = None; @@ -321,6 +369,18 @@ decl_module! { }) } + #[weight = 0] + fn force_destroy(origin, #[compact] id: T::AssetId) -> DispatchResult { + T::ForceOrigin::ensure_origin(origin)?; + + Asset::::try_mutate_exists(id, |maybe_details| { + *maybe_details = None; + Account::::remove_prefix(&id); + Self::deposit_event(RawEvent::Destroyed(id)); + Ok(()) + }) + } + #[weight = 0] fn mint(origin, #[compact] id: T::AssetId, @@ -330,53 +390,24 @@ decl_module! { let origin = ensure_signed(origin)?; let beneficiary = T::Lookup::lookup(beneficiary)?; - Details::::try_mutate(id, |maybe_details| { + Asset::::try_mutate(id, |maybe_details| { let d = maybe_details.as_mut().ok_or(Error::::Unknown)?; + ensure!(&origin == &d.issuer, Error::::NoPermission); d.supply = d.supply.saturating_add(amount); - Account::::mutate(id, &beneficiary, |t| - t.balance = t.balance.saturating_add(amount) - ); + Account::::try_mutate(id, &beneficiary, |t| -> DispatchResult { + if t.balance.is_zero() { + t.is_zombie = Self::new_account(&beneficiary, d)?; + } + t.balance = t.balance.saturating_add(amount); + Ok(()) + })?; Self::deposit_event(RawEvent::Issued(id, beneficiary, amount)); Ok(()) }) } - /// Move some assets from one holder to another. - /// - /// # - /// - `O(1)` - /// - 1 static lookup - /// - 2 storage mutations (codec `O(1)`). - /// - 1 event. - /// # - #[weight = 0] - fn transfer(origin, - #[compact] id: T::AssetId, - target: ::Source, - #[compact] amount: T::Balance - ) { - let origin = ensure_signed(origin)?; - ensure!(!amount.is_zero(), Error::::AmountZero); - - let mut origin_account = Account::::get(id, &origin); - ensure!(!origin_account.is_frozen, Error::::Frozen); - origin_account.balance = origin_account.balance.checked_sub(&amount) - .ok_or(Error::::BalanceLow)?; - - let dest = T::Lookup::lookup(target)?; - - if origin_account.balance.is_zero() { - Account::::remove(id, &origin); - } else { - Account::::insert(id, &origin, origin_account); - } - Account::::mutate(id, &dest, |a| a.balance = a.balance.saturating_add(amount)); - - Self::deposit_event(RawEvent::Transferred(id, origin, dest, amount)); - } - /// Destroy up to `amount` assets of `id` owned by `who`. /// /// Origin must be Signed from the Manager account. @@ -391,24 +422,27 @@ decl_module! { let origin = ensure_signed(origin)?; let who = T::Lookup::lookup(who)?; - Details::::try_mutate(id, |maybe_details| { + Asset::::try_mutate(id, |maybe_details| { let d = maybe_details.as_mut().ok_or(Error::::Unknown)?; ensure!(&origin == &d.admin, Error::::NoPermission); - let burned = Account::::try_mutate_exists(id, &who, |maybe_account| { - if let Some(mut account) = maybe_account.take() { - let burned = amount.min(account.balance); + let burned = Account::::try_mutate_exists( + id, + &who, + |maybe_account| -> Result { + let mut account = maybe_account.take().ok_or(Error::::BalanceZero)?; + let mut burned = amount.min(account.balance); account.balance -= burned; - *maybe_account = if account.balance.is_zero() { + *maybe_account = if account.balance < d.minimum_balance { + burned += account.balance; + Self::dead_account(&who, d, account.is_zombie); None } else { Some(account) }; Ok(burned) - } else { - Err(Error::::BalanceZero) } - })?; + )?; d.supply = d.supply.saturating_sub(burned); @@ -417,46 +451,115 @@ decl_module! { }) } + /// Move some assets from one holder to another. + /// + /// # + /// - `O(1)` + /// - 1 static lookup + /// - 2 storage mutations (codec `O(1)`). + /// - 1 event. + /// # #[weight = 0] - fn set_owner(origin, + fn transfer(origin, #[compact] id: T::AssetId, - owner: ::Source, + target: ::Source, + #[compact] amount: T::Balance ) -> DispatchResult { let origin = ensure_signed(origin)?; - let owner = T::Lookup::lookup(owner)?; + ensure!(!amount.is_zero(), Error::::AmountZero); + + let mut origin_account = Account::::get(id, &origin); + ensure!(!origin_account.is_frozen, Error::::Frozen); + origin_account.balance = origin_account.balance.checked_sub(&amount) + .ok_or(Error::::BalanceLow)?; - Details::::try_mutate(id, |maybe_details| { + let dest = T::Lookup::lookup(target)?; + Asset::::try_mutate(id, |maybe_details| { let d = maybe_details.as_mut().ok_or(Error::::Unknown)?; - ensure!(&origin == &d.owner, Error::::NoPermission); - d.owner = owner.clone(); + if dest == origin { + return Ok(()) + } - Self::deposit_event(RawEvent::OwnerChanged(id, owner)); + let mut amount = amount; + if origin_account.balance < d.minimum_balance { + amount += origin_account.balance; + origin_account.balance = Zero::zero(); + } + + Account::::try_mutate(id, &dest, |a| -> DispatchResult { + if a.balance.is_zero() { + a.is_zombie = Self::new_account(&dest, d)?; + } + a.balance = a.balance.saturating_add(amount); + Ok(()) + })?; + + match origin_account.balance.is_zero() { + false => { + Self::dezombify(&origin, d, &mut origin_account.is_zombie); + Account::::insert(id, &origin, &origin_account) + } + true => { + Self::dead_account(&origin, d, origin_account.is_zombie); + Account::::remove(id, &origin); + } + } + + Self::deposit_event(RawEvent::Transferred(id, origin, dest, amount)); Ok(()) }) } #[weight = 0] - fn set_team(origin, + fn force_transfer(origin, #[compact] id: T::AssetId, - issuer: ::Source, - admin: ::Source, - freezer: ::Source, + source: ::Source, + dest: ::Source, + #[compact] amount: T::Balance, ) -> DispatchResult { let origin = ensure_signed(origin)?; - let issuer = T::Lookup::lookup(issuer)?; - let admin = T::Lookup::lookup(admin)?; - let freezer = T::Lookup::lookup(freezer)?; - Details::::try_mutate(id, |maybe_details| { + let source = T::Lookup::lookup(source)?; + let mut source_account = Account::::get(id, &source); + let mut amount = amount.min(source_account.balance); + ensure!(!amount.is_zero(), Error::::AmountZero); + + let dest = T::Lookup::lookup(dest)?; + if dest == source { + return Ok(()) + } + + Asset::::try_mutate(id, |maybe_details| { let d = maybe_details.as_mut().ok_or(Error::::Unknown)?; - ensure!(&origin == &d.owner, Error::::NoPermission); + ensure!(&origin == &d.admin, Error::::NoPermission); - d.issuer = issuer.clone(); - d.admin = admin.clone(); - d.freezer = freezer.clone(); + source_account.balance -= amount; + if source_account.balance < d.minimum_balance { + amount += source_account.balance; + source_account.balance = Zero::zero(); + } - Self::deposit_event(RawEvent::TeamChanged(id, issuer, admin, freezer)); + Account::::try_mutate(id, &dest, |a| -> DispatchResult { + if a.balance.is_zero() { + a.is_zombie = Self::new_account(&dest, d)?; + } + a.balance = a.balance.saturating_add(amount); + Ok(()) + })?; + + match source_account.balance.is_zero() { + false => { + Self::dezombify(&source, d, &mut source_account.is_zombie); + Account::::insert(id, &source, &source_account) + } + true => { + Self::dead_account(&source, d, source_account.is_zombie); + Account::::remove(id, &source); + } + } + + Self::deposit_event(RawEvent::ForceTransferred(id, source, dest, amount)); Ok(()) }) } @@ -465,10 +568,11 @@ decl_module! { fn freeze(origin, #[compact] id: T::AssetId, who: ::Source) { let origin = ensure_signed(origin)?; - let d = Details::::get(id).ok_or(Error::::Unknown)?; + let d = Asset::::get(id).ok_or(Error::::Unknown)?; ensure!(&origin == &d.admin, Error::::NoPermission); - let who = T::Lookup::lookup(who)?; + ensure!(Account::::contains_key(id, &who), Error::::BalanceZero); + Account::::mutate(id, &who, |a| a.is_frozen = true); Self::deposit_event(Event::::Frozen(id, who)); @@ -478,45 +582,58 @@ decl_module! { fn thaw(origin, #[compact] id: T::AssetId, who: ::Source) { let origin = ensure_signed(origin)?; - let d = Details::::get(id).ok_or(Error::::Unknown)?; + let d = Asset::::get(id).ok_or(Error::::Unknown)?; ensure!(&origin == &d.admin, Error::::NoPermission); - let who = T::Lookup::lookup(who)?; + ensure!(Account::::contains_key(id, &who), Error::::BalanceZero); + Account::::mutate(id, &who, |a| a.is_frozen = false); Self::deposit_event(Event::::Thawed(id, who)); } #[weight = 0] - fn force_transfer(origin, + fn set_owner(origin, #[compact] id: T::AssetId, - source: ::Source, - dest: ::Source, - #[compact] amount: T::Balance, - ) { + owner: ::Source, + ) -> DispatchResult { let origin = ensure_signed(origin)?; + let owner = T::Lookup::lookup(owner)?; - let d = Details::::get(id).ok_or(Error::::Unknown)?; - ensure!(&origin == &d.admin, Error::::NoPermission); + Asset::::try_mutate(id, |maybe_details| { + let d = maybe_details.as_mut().ok_or(Error::::Unknown)?; + ensure!(&origin == &d.owner, Error::::NoPermission); - let source = T::Lookup::lookup(source)?; - let mut source_account = Account::::get(id, &source); - let amount = amount.min(source_account.balance); + d.owner = owner.clone(); - ensure!(!amount.is_zero(), Error::::AmountZero); + Self::deposit_event(RawEvent::OwnerChanged(id, owner)); + Ok(()) + }) + } - let dest = T::Lookup::lookup(dest)?; + #[weight = 0] + fn set_team(origin, + #[compact] id: T::AssetId, + issuer: ::Source, + admin: ::Source, + freezer: ::Source, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + let issuer = T::Lookup::lookup(issuer)?; + let admin = T::Lookup::lookup(admin)?; + let freezer = T::Lookup::lookup(freezer)?; - source_account.balance -= amount; - if source_account.balance.is_zero() { - Account::::remove(id, &source); - } else { - Account::::insert(id, &source, source_account); - } + Asset::::try_mutate(id, |maybe_details| { + let d = maybe_details.as_mut().ok_or(Error::::Unknown)?; + ensure!(&origin == &d.owner, Error::::NoPermission); - Account::::mutate(id, &dest, |a| a.balance = a.balance.saturating_add(amount)); + d.issuer = issuer.clone(); + d.admin = admin.clone(); + d.freezer = freezer.clone(); - Self::deposit_event(RawEvent::ForceTransferred(id, source, dest, amount)); + Self::deposit_event(RawEvent::TeamChanged(id, issuer, admin, freezer)); + Ok(()) + }) } } } @@ -532,7 +649,46 @@ impl Module { /// Get the total supply of an asset `id`. pub fn total_supply(id: T::AssetId) -> T::Balance { - Details::::get(id).map(|x| x.supply).unwrap_or_else(Zero::zero) + Asset::::get(id).map(|x| x.supply).unwrap_or_else(Zero::zero) + } + + fn new_account( + who: &T::AccountId, + d: &mut AssetDetails>, + ) -> Result { + Ok(if frame_system::Module::::account_exists(who) { + frame_system::Module::::inc_ref(who); + false + } else { + ensure!(d.zombies < d.max_zombies, Error::::TooManyZombies); + d.zombies += 1; + true + }) + } + + /// If `who`` exists in system and it's a zombie, dezombify it. + fn dezombify( + who: &T::AccountId, + d: &mut AssetDetails>, + is_zombie: &mut bool, + ) { + if *is_zombie && frame_system::Module::::account_exists(who) { + frame_system::Module::::inc_ref(who); + *is_zombie = false; + d.zombies = d.zombies.saturating_sub(1); + } + } + + fn dead_account( + who: &T::AccountId, + d: &mut AssetDetails>, + is_zombie: bool, + ) { + if is_zombie { + frame_system::Module::::dec_ref(who); + } else { + d.zombies = d.zombies.saturating_sub(1); + } } } @@ -598,11 +754,19 @@ mod tests { type WeightInfo = (); } + parameter_types! { + pub const AssetDepositBase: u64 = 1; + pub const AssetDepositPerZombie: u64 = 1; + } + impl Trait for Test { type Currency = Balances; type Event = (); type Balance = u64; type AssetId = u32; + type ForceOrigin = frame_system::EnsureRoot; + type AssetDepositBase = AssetDepositBase; + type AssetDepositPerZombie = AssetDepositPerZombie; } type System = frame_system::Module; type Balances = pallet_balances::Module; @@ -615,7 +779,7 @@ mod tests { #[test] fn issuing_asset_units_to_issuer_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::create(Origin::signed(1), 0, 1)); + assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); }); @@ -624,7 +788,7 @@ mod tests { #[test] fn querying_total_supply_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::create(Origin::signed(1), 0, 1)); + assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); @@ -642,7 +806,7 @@ mod tests { #[test] fn transferring_amount_above_available_balance_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::create(Origin::signed(1), 0, 1)); + assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); @@ -654,7 +818,7 @@ mod tests { #[test] fn transferring_amount_more_than_available_balance_should_not_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::create(Origin::signed(1), 0, 1)); + assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); @@ -669,7 +833,7 @@ mod tests { #[test] fn transferring_less_than_one_unit_should_not_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::create(Origin::signed(1), 0, 1)); + assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); assert_noop!(Assets::transfer(Origin::signed(1), 0, 2, 0), Error::::AmountZero); @@ -679,7 +843,7 @@ mod tests { #[test] fn transferring_more_units_than_total_supply_should_not_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::create(Origin::signed(1), 0, 1)); + assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); assert_noop!(Assets::transfer(Origin::signed(1), 0, 2, 101), Error::::BalanceLow); @@ -689,7 +853,7 @@ mod tests { #[test] fn destroying_asset_balance_with_positive_balance_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::create(Origin::signed(1), 0, 1)); + assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); assert_ok!(Assets::burn(Origin::signed(1), 0, 1, u64::max_value())); @@ -699,7 +863,7 @@ mod tests { #[test] fn destroying_asset_balance_with_zero_balance_should_not_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::create(Origin::signed(1), 0, 1)); + assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 2), 0); assert_noop!(Assets::burn(Origin::signed(1), 0, 2, u64::max_value()), Error::::BalanceZero); diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 93dea5f473075..a0e735f6f89fa 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -895,6 +895,10 @@ impl Module { Self::deposit_event_indexed(&[], event.into()); } + pub fn account_exists(who: &T::AccountId) -> bool { + Account::::contains_key(who) + } + /// Increment the reference counter on an account. pub fn inc_ref(who: &T::AccountId) { Account::::mutate(who, |a| a.refcount = a.refcount.saturating_add(1)); From 4df1054bf0884445a683e0d09e4de0b15e12809c Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Fri, 25 Sep 2020 09:27:19 +0200 Subject: [PATCH 06/34] Update frame/assets/src/lib.rs Co-authored-by: Nikolay Volf --- frame/assets/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 9585c3abf7294..a03dbb1c2ca92 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -183,8 +183,9 @@ pub struct AssetDetails< freezer: AccountId, /// The total supply across all accounts. supply: Balance, - /// The balance deposited for this asset; this pays for the data stored here together with any - /// virtual accounts. + /// The balance deposited for this asset. + /// + /// This pays for the data stored here together with any virtual accounts. deposit: DepositBalance, /// The number of balance-holding accounts that this asset may have, excluding those that were /// created when they had a system-level ED. From aef1d0c0db576ddd46fbc8bafda961d34e0e9e68 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Fri, 25 Sep 2020 09:28:30 +0200 Subject: [PATCH 07/34] Update frame/assets/Cargo.toml Co-authored-by: Niklas Adolfsson --- frame/assets/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/assets/Cargo.toml b/frame/assets/Cargo.toml index 9f94f196ccf17..3ea11b9d8e249 100644 --- a/frame/assets/Cargo.toml +++ b/frame/assets/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } # Needed for various traits. In our case, `OnFinalize`. sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } # Needed for type-safe access to storage DB. From b173c379620e4898adc58e990ab635b9dc3ba43f Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 2 Oct 2020 15:56:38 +0200 Subject: [PATCH 08/34] Docs --- frame/assets/src/lib.rs | 268 +++++++++++++++++++++++++++++++++------- 1 file changed, 222 insertions(+), 46 deletions(-) diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index a03dbb1c2ca92..492cdb308eee1 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -24,9 +24,10 @@ //! The Assets module provides functionality for asset management of fungible asset classes //! with a fixed supply, including: //! -//! * Asset Issuance -//! * Asset Transfer -//! * Asset Destruction +//! * Asset Issuance (Minting) +//! * Asset Transferal +//! * Asset Freezing +//! * Asset Destruction (Burning) //! //! To use it in your runtime, you need to implement the assets [`Trait`](./trait.Trait.html). //! @@ -34,31 +35,61 @@ //! //! ### Terminology //! -//! * **Asset issuance:** The creation of a new asset, whose total supply will belong to the -//! account that issues the asset. -//! * **Asset transfer:** The action of transferring assets from one account to another. -//! * **Asset destruction:** The process of an account removing its entire holding of an asset. -//! * **Fungible asset:** An asset whose units are interchangeable. -//! * **Non-fungible asset:** An asset for which each unit has unique characteristics. +//! * **Admin**: An account ID uniquely privileged to be able to unfreeze (thaw) an account and it's +//! assets, as well as forcibly transfer a particular class of assets between arbitrary accounts +//! and reduce the balance of a particular class of assets of arbitrary accounts. +//! * **Asset issuance/minting**: The creation of a new asset, whose total supply will belong to the +//! account that issues the asset. This is a privileged operation. +//! * **Asset transfer**: The reduction of the balance of an asset of one account with the +//! corresponding increase in the balance of another. +//! * **Asset destruction**: The process of reduce the balance of an asset of one account. This is +//! a privileged operation. +//! * **Fungible asset**: An asset whose units are interchangeable. +//! * **Issuer**: An account ID uniquely privileged to be able to mint a particular class of assets. +//! * **Freezer**: An account ID uniquely privileged to be able to freeze an account from +//! transferring a particular class of assets. +//! * **Freezing**: Removing the possibility of an unpermissioned transfer of an asset from a +//! particular account. +//! * **Non-fungible asset**: An asset for which each unit has unique characteristics. +//! * **Owner**: An account ID uniquely privileged to be able to destroy a particular asset class, +//! or to set the Issuer, Freezer or Admin of that asset class. +//! * **Zombie**: An account which has a balance of some assets in this pallet, but no other +//! footprint on-chain, in particular no account managed in the `frame_system` pallet. //! //! ### Goals //! //! The assets system in Substrate is designed to make the following possible: //! -//! * Issue a unique asset to its creator's account. +//! * Issue a new assets in a permissioned or permissionless way, if permissionless, then with a +//! deposit required. +//! * Allow accounts to hold these assets without otherwise existing on-chain (*zombies*). //! * Move assets between accounts. -//! * Remove an account's balance of an asset when requested by that account's owner and update -//! the asset's total supply. +//! * Update the asset's total supply. +//! * Allow administrative activities by specially privileged accounts including freezing account +//! balances and minting/burning assets. //! //! ## Interface //! -//! ### Dispatchable Functions +//! ### Permissionless Functions //! -//! * `issue` - Issues the total supply of a new fungible asset to the account of the caller of the function. -//! * `transfer` - Transfers an `amount` of units of fungible asset `id` from the balance of -//! the function caller's account (`origin`) to a `target` account. -//! * `destroy` - Destroys the entire holding of a fungible asset `id` associated with the account -//! that called the function. +//! * `create`: Creates a new asset class, taking the required deposit. +//! * `transfer`: Transfer sender's assets to another account. +//! +//! ### Permissioned Functions +//! +//! * `force_create`: Creates a new asset class without taking any deposit. +//! * `force_destroy`: Destroys an asset class. +//! +//! ### Privileged Functions +//! * `destroy`: Destroys an entire asset class; called by the asset class's Owner. +//! * `mint`: Increases the asset balance of an account; called by the asset class's Issuer. +//! * `burn`: Decreases the asset balance of an account; called by the asset class's Admin. +//! * `force_transfer`: Transfers between arbitrary accounts; called by the asset class's Admin. +//! * `freeze`: Disallows further `transfer`s from an account; called by the asset class's Freezer. +//! * `thaw`: Allows further `transfer`s from an account; called by the asset class's Admin. +//! * `set_owner`: Changes an asset class's Owner; called by the asset class's Owner. +//! * `set_team`: Changes an asset class's Admin, Freezer and Issuer; called by the asset class's +//! Owner. //! //! Please refer to the [`Call`](./enum.Call.html) enum and its associated variants for documentation on each function. //! @@ -120,7 +151,7 @@ //! ## Assumptions //! //! Below are assumptions that must be held when using this module. If any of -//! them are violated, the behavior of this module is undefined. +//! these are violated, the behavior of this module is undefined. //! //! * The total count of assets should be less than //! `Trait::AssetId::max_value()`. @@ -208,10 +239,6 @@ pub struct AccountData< is_zombie: bool, } -// TODO: accounts holding funds must be in existence first, or... -// `deposit` should be sufficient to allow for N additional accounts in the state, -// and the creater can require a minimum balance here to ensure dust accounts don't use this up. - decl_storage! { trait Store for Module as Assets { /// Details of an asset. @@ -281,24 +308,40 @@ decl_error! { } } -// TODO: refs should be handled in this module because system refs are u8 only. - decl_module! { pub struct Module for enum Call where origin: T::Origin { type Error = Error; + // TODO: Allow for max_zombies to be adjusted. + // TODO: Allow for easy integration with system accounts so that an ED in an asset here is + // sufficient for account existence and tx fees may be paid through assets here. An + // `Exchange` trait is probably best for this so AMM pallets can be wired in. + fn deposit_event() = default; - /// Issue a new class of fungible assets. There are, and will only ever be, `total` - /// such assets and they'll all belong to the `origin` initially. It will have an - /// identifier `AssetId` instance: this will be specified in the `Issued` event. + /// Issue a new class of fungible assets from a public origin. + /// + /// This new asset class has no assets initially. /// - /// # - /// - `O(1)` - /// - 1 storage mutation (codec `O(1)`). - /// - 2 storage writes (condec `O(1)`). - /// - 1 event. - /// # + /// The origin must be Signed and the sender must have sufficient funds free. + /// + /// Funds of sender are reserved according to the formula: + /// `AssetDepositBase + AssetDepositPerZombie * max_zombies`. + /// + /// Parameters: + /// - `id`: The identifier of the new asset. This must not be currently in use to identify + /// an existing asset. + /// - `owner`: The owner of this class of assets. The owner has full superuser permissions + /// over this asset, but may later change and configure the permissions using `set_owner` + /// and `set_team`. + /// - `max_zombies`: The total number of accounts which may hold assets in this class yet + /// have no existential deposit. + /// - `minimum_balance`: The minimum balance of this new asset that any single account must + /// have. If an account's balance is reduced below this, then it collapses to zero. + /// + /// Emits `Created` event when successful. + /// + /// Weight: `O(1)` #[weight = 0] fn create(origin, #[compact] id: T::AssetId, @@ -330,6 +373,27 @@ decl_module! { Self::deposit_event(RawEvent::Created(id, origin, owner)); } + /// Issue a new class of fungible assets from a privileged origin. + /// + /// This new asset class has no assets initially. + /// + /// The origin must conform to `ForceOrigin`. + /// + /// Unlike `create`, no funds are reserved. + /// + /// - `id`: The identifier of the new asset. This must not be currently in use to identify + /// an existing asset. + /// - `owner`: The owner of this class of assets. The owner has full superuser permissions + /// over this asset, but may later change and configure the permissions using `set_owner` + /// and `set_team`. + /// - `max_zombies`: The total number of accounts which may hold assets in this class yet + /// have no existential deposit. + /// - `minimum_balance`: The minimum balance of this new asset that any single account must + /// have. If an account's balance is reduced below this, then it collapses to zero. + /// + /// Emits `ForceCreated` event when successful. + /// + /// Weight: `O(1)` #[weight = 0] fn force_create(origin, #[compact] id: T::AssetId, @@ -356,6 +420,16 @@ decl_module! { Self::deposit_event(RawEvent::ForceCreated(id, owner)); } + /// Destroy a class of fungible assets owned by the sender. + /// + /// The origin must be Signed and the sender must be the owner of the asset `id`. + /// + /// - `id`: The identifier of the asset to be destroyed. This must identify an existing + /// asset. + /// + /// Emits `Destroyed` event when successful. + /// + /// Weight: `O(1)` #[weight = 0] fn destroy(origin, #[compact] id: T::AssetId) -> DispatchResult { let origin = ensure_signed(origin)?; @@ -370,6 +444,16 @@ decl_module! { }) } + /// Destroy a class of fungible assets. + /// + /// The origin must conform to `ForceOrigin`. + /// + /// - `id`: The identifier of the asset to be destroyed. This must identify an existing + /// asset. + /// + /// Emits `Destroyed` event when successful. + /// + /// Weight: `O(1)` #[weight = 0] fn force_destroy(origin, #[compact] id: T::AssetId) -> DispatchResult { T::ForceOrigin::ensure_origin(origin)?; @@ -382,6 +466,18 @@ decl_module! { }) } + /// Mint assets of a particular class. + /// + /// The origin must be Signed and the sender must be the Issuer of the asset `id`. + /// + /// - `id`: The identifier of the asset to have some amount minted. + /// - `beneficiary`: The account to be credited with the minted assets. + /// - `amount`: The amount of the asset to be minted. + /// + /// Emits `Destroyed` event when successful. + /// + /// Weight: `O(1)` + /// Modes: Pre-existence of `beneficiary` #[weight = 0] fn mint(origin, #[compact] id: T::AssetId, @@ -409,11 +505,21 @@ decl_module! { }) } - /// Destroy up to `amount` assets of `id` owned by `who`. + /// Reduce the balance of `who` by as much as possible up to `amount` assets of `id`. /// - /// Origin must be Signed from the Manager account. + /// Origin must be Signed and the sender should be the Manager of the asset `id`. /// /// Bails with `BalanceZero` if the `who` is already dead. + /// + /// - `id`: The identifier of the asset to have some amount burned. + /// - `who`: The account to be debited from. + /// - `amount`: The maximum amount by which `who`'s balance should be reduced. + /// + /// Emits `Burned` with the actual amount burned. If this takes the balance to below the + /// minimum for the asset, then the amount burned is increased to take it to zero. + /// + /// Weight: `O(1)` + /// Modes: Post-existence of `who` #[weight = 0] fn burn(origin, #[compact] id: T::AssetId, @@ -452,14 +558,24 @@ decl_module! { }) } - /// Move some assets from one holder to another. + /// Move some assets from the sender account to another. + /// + /// Origin must be Signed. + /// + /// - `id`: The identifier of the asset to have some amount transferred. + /// - `target`: The account to be credited. + /// - `amount`: The amount by which the sender's balance of assets should be reduced and + /// `target`'s balance increased. The amount actually transferred may be slightly greater in + /// the case that the transfer would otherwise take the sender balance above zero but below + /// the minimum balance. Must be greater than zero. + /// + /// Emits `Transferred` with the actual amount transferred. If this takes the source balance + /// to below the minimum for the asset, then the amount transferred is increased to take it + /// to zero. /// - /// # - /// - `O(1)` - /// - 1 static lookup - /// - 2 storage mutations (codec `O(1)`). - /// - 1 event. - /// # + /// Weight: `O(1)` + /// Modes: Pre-existence of `target`; Post-existence of sender; Prior & post zombie-status + /// of sender. #[weight = 0] fn transfer(origin, #[compact] id: T::AssetId, @@ -512,6 +628,25 @@ decl_module! { }) } + /// Move some assets from one account to another. + /// + /// Origin must be Signed and the sender should be the Admin of the asset `id`. + /// + /// - `id`: The identifier of the asset to have some amount transferred. + /// - `source`: The account to be debited. + /// - `dest`: The account to be credited. + /// - `amount`: The amount by which the `source`'s balance of assets should be reduced and + /// `dest`'s balance increased. The amount actually transferred may be slightly greater in + /// the case that the transfer would otherwise take the `source` balance above zero but + /// below the minimum balance. Must be greater than zero. + /// + /// Emits `Transferred` with the actual amount transferred. If this takes the source balance + /// to below the minimum for the asset, then the amount transferred is increased to take it + /// to zero. + /// + /// Weight: `O(1)` + /// Modes: Pre-existence of `dest`; Post-existence of `source`. Prior & post zombie-status + /// of `source`. #[weight = 0] fn force_transfer(origin, #[compact] id: T::AssetId, @@ -551,7 +686,6 @@ decl_module! { match source_account.balance.is_zero() { false => { - Self::dezombify(&source, d, &mut source_account.is_zombie); Account::::insert(id, &source, &source_account) } true => { @@ -565,12 +699,22 @@ decl_module! { }) } + /// Disallow further unprivileged transfers from an account. + /// + /// Origin must be Signed and the sender should be the Freezer of the asset `id`. + /// + /// - `id`: The identifier of the asset to be frozen. + /// - `who`: The account to be frozen. + /// + /// Emits `Frozen`. + /// + /// Weight: `O(1)` #[weight = 0] fn freeze(origin, #[compact] id: T::AssetId, who: ::Source) { let origin = ensure_signed(origin)?; let d = Asset::::get(id).ok_or(Error::::Unknown)?; - ensure!(&origin == &d.admin, Error::::NoPermission); + ensure!(&origin == &d.freezer, Error::::NoPermission); let who = T::Lookup::lookup(who)?; ensure!(Account::::contains_key(id, &who), Error::::BalanceZero); @@ -579,6 +723,16 @@ decl_module! { Self::deposit_event(Event::::Frozen(id, who)); } + /// Allow unprivileged transfers from an account again. + /// + /// Origin must be Signed and the sender should be the Admin of the asset `id`. + /// + /// - `id`: The identifier of the asset to be frozen. + /// - `who`: The account to be unfrozen. + /// + /// Emits `Thawed`. + /// + /// Weight: `O(1)` #[weight = 0] fn thaw(origin, #[compact] id: T::AssetId, who: ::Source) { let origin = ensure_signed(origin)?; @@ -593,6 +747,16 @@ decl_module! { Self::deposit_event(Event::::Thawed(id, who)); } + /// Change the Owner of an asset. + /// + /// Origin must be Signed and the sender should be the Owner of the asset `id`. + /// + /// - `id`: The identifier of the asset to be frozen. + /// - `owner`: The new Owner of this asset. + /// + /// Emits `OwnerChanged`. + /// + /// Weight: `O(1)` #[weight = 0] fn set_owner(origin, #[compact] id: T::AssetId, @@ -612,6 +776,18 @@ decl_module! { }) } + /// Change the Issuer, Admin and Freezer of an asset. + /// + /// Origin must be Signed and the sender should be the Owner of the asset `id`. + /// + /// - `id`: The identifier of the asset to be frozen. + /// - `issuer`: The new Issuer of this asset. + /// - `admin`: The new Admin of this asset. + /// - `freezer`: The new Freezer of this asset. + /// + /// Emits `TeamChanged`. + /// + /// Weight: `O(1)` #[weight = 0] fn set_team(origin, #[compact] id: T::AssetId, @@ -735,7 +911,7 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type Version = (); type PalletInfo = (); - type pallet_balances::AccountData = (); + type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); type SystemWeightInfo = (); From fd75591427ea193d1b7874470ef44ea562f041b1 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 5 Oct 2020 10:03:37 +0200 Subject: [PATCH 09/34] Some tests --- frame/assets/src/lib.rs | 125 ++++++++++++++++++++++------------------ 1 file changed, 69 insertions(+), 56 deletions(-) diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 492cdb308eee1..b3b8cfe6c9913 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -101,61 +101,6 @@ //! //! Please refer to the [`Module`](./struct.Module.html) struct for details on publicly available functions. //! -//! ## Usage -//! -//! The following example shows how to use the Assets module in your runtime by exposing public functions to: -//! -//! * Issue a new fungible asset for a token distribution event (airdrop). -//! * Query the fungible asset holding balance of an account. -//! * Query the total supply of a fungible asset that has been issued. -//! -//! ### Prerequisites -//! -//! Import the Assets module and types and derive your runtime's configuration traits from the Assets module trait. -//! -//! ### Simple Code Snippet -//! -//! ```rust,ignore -//! use pallet_assets as assets; -//! use frame_support::{decl_module, dispatch, ensure}; -//! use frame_system::ensure_signed; -//! -//! pub trait Trait: assets::Trait { } -//! -//! decl_module! { -//! pub struct Module for enum Call where origin: T::Origin { -//! pub fn issue_token_airdrop(origin) -> dispatch::DispatchResult { -//! let sender = ensure_signed(origin).map_err(|e| e.as_str())?; -//! -//! const ACCOUNT_ALICE: u64 = 1; -//! const ACCOUNT_BOB: u64 = 2; -//! const COUNT_AIRDROP_RECIPIENTS: u64 = 2; -//! const TOKENS_FIXED_SUPPLY: u64 = 100; -//! -//! ensure!(!COUNT_AIRDROP_RECIPIENTS.is_zero(), "Divide by zero error."); -//! -//! let asset_id = Self::next_asset_id(); -//! -//! NextAssetId::::mutate(|asset_id| *asset_id += 1); -//! Balances::::insert((asset_id, &ACCOUNT_ALICE), TOKENS_FIXED_SUPPLY / COUNT_AIRDROP_RECIPIENTS); -//! Balances::::insert((asset_id, &ACCOUNT_BOB), TOKENS_FIXED_SUPPLY / COUNT_AIRDROP_RECIPIENTS); -//! TotalSupply::::insert(asset_id, TOKENS_FIXED_SUPPLY); -//! -//! Self::deposit_event(RawEvent::Issued(asset_id, sender, TOKENS_FIXED_SUPPLY)); -//! Ok(()) -//! } -//! } -//! } -//! ``` -//! -//! ## Assumptions -//! -//! Below are assumptions that must be held when using this module. If any of -//! these are violated, the behavior of this module is undefined. -//! -//! * The total count of assets should be less than -//! `Trait::AssetId::max_value()`. -//! //! ## Related Modules //! //! * [`System`](../frame_system/index.html) @@ -317,6 +262,8 @@ decl_module! { // sufficient for account existence and tx fees may be paid through assets here. An // `Exchange` trait is probably best for this so AMM pallets can be wired in. + // TODO: Test max_zombies, zombie counting, referencing. + fn deposit_event() = default; /// Issue a new class of fungible assets from a public origin. @@ -981,7 +928,7 @@ mod tests { } #[test] - fn transferring_amount_above_available_balance_should_work() { + fn transferring_amount_below_available_balance_should_work() { new_test_ext().execute_with(|| { assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); @@ -992,6 +939,72 @@ mod tests { }); } + #[test] + fn transferring_frozen_balance_should_not_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Assets::balance(0, 1), 100); + assert_ok!(Assets::freeze(Origin::signed(1), 0, 1)); + assert_noop!(Assets::transfer(Origin::signed(1), 0, 2, 50), Error::::Frozen); + assert_ok!(Assets::thaw(Origin::signed(1), 0, 1)); + assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); + }); + } + + #[test] + fn origin_guards_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + assert_noop!(Assets::set_owner(Origin::signed(2), 0, 2), Error::::NoPermission); + assert_noop!(Assets::set_team(Origin::signed(2), 0, 2, 2, 2), Error::::NoPermission); + assert_noop!(Assets::freeze(Origin::signed(2), 0, 1), Error::::NoPermission); + assert_noop!(Assets::thaw(Origin::signed(2), 0, 2), Error::::NoPermission); + assert_noop!(Assets::mint(Origin::signed(2), 0, 2, 100), Error::::NoPermission); + assert_noop!(Assets::burn(Origin::signed(2), 0, 1, 100), Error::::NoPermission); + assert_noop!(Assets::force_transfer(Origin::signed(2), 0, 1, 2, 100), Error::::NoPermission); + }); + } + + #[test] + fn set_owner_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); + assert_ok!(Assets::set_owner(Origin::signed(1), 0, 2)); + assert_noop!(Assets::set_owner(Origin::signed(1), 0, 1), Error::::NoPermission); + assert_ok!(Assets::set_owner(Origin::signed(2), 0, 1)); + }); + } + + #[test] + fn set_team_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); + assert_ok!(Assets::set_team(Origin::signed(1), 0, 2, 3, 4)); + + assert_ok!(Assets::mint(Origin::signed(2), 0, 2, 100)); + assert_ok!(Assets::freeze(Origin::signed(4), 0, 2)); + assert_ok!(Assets::thaw(Origin::signed(3), 0, 2)); + assert_ok!(Assets::force_transfer(Origin::signed(3), 0, 2, 3, 100)); + assert_ok!(Assets::burn(Origin::signed(3), 0, 3, 100)); + }); + } + + #[test] + fn transferring_to_frozen_account_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 2, 100)); + assert_eq!(Assets::balance(0, 1), 100); + assert_eq!(Assets::balance(0, 2), 100); + assert_ok!(Assets::freeze(Origin::signed(1), 0, 2)); + assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); + assert_eq!(Assets::balance(0, 2), 150); + }); + } + #[test] fn transferring_amount_more_than_available_balance_should_not_work() { new_test_ext().execute_with(|| { From 3d0335881e8f3463abc21f0b8a2f3cb141149628 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 5 Oct 2020 11:31:41 +0200 Subject: [PATCH 10/34] More tests --- frame/assets/src/lib.rs | 94 +++++++++++++++++++++++++++++++++-------- 1 file changed, 76 insertions(+), 18 deletions(-) diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index b3b8cfe6c9913..62ea60d25fb55 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -167,7 +167,7 @@ pub struct AssetDetails< /// created when they had a system-level ED. max_zombies: u32, /// The ED for virtual accounts. - minimum_balance: Balance, + min_balance: Balance, /// The current number of zombie accounts. zombies: u32, } @@ -262,8 +262,6 @@ decl_module! { // sufficient for account existence and tx fees may be paid through assets here. An // `Exchange` trait is probably best for this so AMM pallets can be wired in. - // TODO: Test max_zombies, zombie counting, referencing. - fn deposit_event() = default; /// Issue a new class of fungible assets from a public origin. @@ -283,7 +281,7 @@ decl_module! { /// and `set_team`. /// - `max_zombies`: The total number of accounts which may hold assets in this class yet /// have no existential deposit. - /// - `minimum_balance`: The minimum balance of this new asset that any single account must + /// - `min_balance`: The minimum balance of this new asset that any single account must /// have. If an account's balance is reduced below this, then it collapses to zero. /// /// Emits `Created` event when successful. @@ -294,7 +292,7 @@ decl_module! { #[compact] id: T::AssetId, owner: ::Source, max_zombies: u32, - minimum_balance: T::Balance, + min_balance: T::Balance, ) { let origin = ensure_signed(origin)?; let owner = T::Lookup::lookup(owner)?; @@ -314,7 +312,7 @@ decl_module! { supply: Zero::zero(), deposit, max_zombies, - minimum_balance, + min_balance, zombies: Zero::zero(), }); Self::deposit_event(RawEvent::Created(id, origin, owner)); @@ -335,7 +333,7 @@ decl_module! { /// and `set_team`. /// - `max_zombies`: The total number of accounts which may hold assets in this class yet /// have no existential deposit. - /// - `minimum_balance`: The minimum balance of this new asset that any single account must + /// - `min_balance`: The minimum balance of this new asset that any single account must /// have. If an account's balance is reduced below this, then it collapses to zero. /// /// Emits `ForceCreated` event when successful. @@ -346,7 +344,7 @@ decl_module! { #[compact] id: T::AssetId, owner: ::Source, max_zombies: u32, - minimum_balance: T::Balance, + min_balance: T::Balance, ) { T::ForceOrigin::ensure_origin(origin)?; let owner = T::Lookup::lookup(owner)?; @@ -361,7 +359,7 @@ decl_module! { supply: Zero::zero(), deposit: Zero::zero(), max_zombies, - minimum_balance, + min_balance, zombies: Zero::zero(), }); Self::deposit_event(RawEvent::ForceCreated(id, owner)); @@ -441,10 +439,12 @@ decl_module! { d.supply = d.supply.saturating_add(amount); Account::::try_mutate(id, &beneficiary, |t| -> DispatchResult { + let new_balance = t.balance.saturating_add(amount); + ensure!(new_balance >= d.min_balance, Error::::BalanceLow); if t.balance.is_zero() { t.is_zombie = Self::new_account(&beneficiary, d)?; } - t.balance = t.balance.saturating_add(amount); + t.balance = new_balance; Ok(()) })?; Self::deposit_event(RawEvent::Issued(id, beneficiary, amount)); @@ -487,7 +487,7 @@ decl_module! { let mut account = maybe_account.take().ok_or(Error::::BalanceZero)?; let mut burned = amount.min(account.balance); account.balance -= burned; - *maybe_account = if account.balance < d.minimum_balance { + *maybe_account = if account.balance < d.min_balance { burned += account.balance; Self::dead_account(&who, d, account.is_zombie); None @@ -546,16 +546,18 @@ decl_module! { } let mut amount = amount; - if origin_account.balance < d.minimum_balance { + if origin_account.balance < d.min_balance { amount += origin_account.balance; origin_account.balance = Zero::zero(); } Account::::try_mutate(id, &dest, |a| -> DispatchResult { + let new_balance = a.balance.saturating_add(amount); + ensure!(new_balance >= d.min_balance, Error::::BalanceLow); if a.balance.is_zero() { a.is_zombie = Self::new_account(&dest, d)?; } - a.balance = a.balance.saturating_add(amount); + a.balance = new_balance; Ok(()) })?; @@ -618,16 +620,18 @@ decl_module! { ensure!(&origin == &d.admin, Error::::NoPermission); source_account.balance -= amount; - if source_account.balance < d.minimum_balance { + if source_account.balance < d.min_balance { amount += source_account.balance; source_account.balance = Zero::zero(); } Account::::try_mutate(id, &dest, |a| -> DispatchResult { + let new_balance = a.balance.saturating_add(amount); + ensure!(new_balance >= d.min_balance, Error::::BalanceLow); if a.balance.is_zero() { a.is_zombie = Self::new_account(&dest, d)?; } - a.balance = a.balance.saturating_add(amount); + a.balance = new_balance; Ok(()) })?; @@ -776,6 +780,11 @@ impl Module { Asset::::get(id).map(|x| x.supply).unwrap_or_else(Zero::zero) } + /// Check the number of zombies allow yet for an asset. + pub fn zombie_allowance(id: T::AssetId) -> u32 { + Asset::::get(id).map(|x| x.max_zombies - x.zombies).unwrap_or_else(Zero::zero) + } + fn new_account( who: &T::AccountId, d: &mut AssetDetails>, @@ -809,9 +818,9 @@ impl Module { is_zombie: bool, ) { if is_zombie { - frame_system::Module::::dec_ref(who); - } else { d.zombies = d.zombies.saturating_sub(1); + } else { + frame_system::Module::::dec_ref(who); } } } @@ -901,11 +910,60 @@ mod tests { } #[test] - fn issuing_asset_units_to_issuer_should_work() { + fn issuing_asset_units_should_work() { new_test_ext().execute_with(|| { assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); + assert_ok!(Assets::mint(Origin::signed(1), 0, 2, 100)); + assert_eq!(Assets::balance(0, 2), 100); + }); + } + + #[test] + fn max_zombies_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, 2, 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 0, 100)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + + assert_eq!(Assets::zombie_allowance(0), 0); + assert_noop!(Assets::mint(Origin::signed(1), 0, 2, 100), Error::::TooManyZombies); + assert_noop!(Assets::transfer(Origin::signed(1), 0, 2, 50), Error::::TooManyZombies); + assert_noop!(Assets::force_transfer(Origin::signed(1), 0, 1, 2, 50), Error::::TooManyZombies); + + Balances::make_free_balance_be(&3, 100); + assert_ok!(Assets::mint(Origin::signed(1), 0, 3, 100)); + + assert_ok!(Assets::transfer(Origin::signed(0), 0, 1, 100)); + assert_eq!(Assets::zombie_allowance(0), 1); + assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); + }); + } + + #[test] + fn min_balance_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 10)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + + // Cannot create a new account with a balance that is below minimum... + assert_noop!(Assets::mint(Origin::signed(1), 0, 2, 9), Error::::BalanceLow); + assert_noop!(Assets::transfer(Origin::signed(1), 0, 2, 9), Error::::BalanceLow); + assert_noop!(Assets::force_transfer(Origin::signed(1), 0, 1, 2, 9), Error::::BalanceLow); + + // When deducting from an account to below minimum, it should be reaped. + + assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 91)); + assert!(Assets::balance(0, 1).is_zero()); + assert_eq!(Assets::balance(0, 2), 100); + + assert_ok!(Assets::force_transfer(Origin::signed(1), 0, 2, 1, 91)); + assert!(Assets::balance(0, 2).is_zero()); + assert_eq!(Assets::balance(0, 1), 100); + + assert_ok!(Assets::burn(Origin::signed(1), 0, 1, 91)); + assert!(Assets::balance(0, 1).is_zero()); }); } From 8aa7cc5948977771fbd99101d87cdd8e5b5bcc62 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 5 Oct 2020 11:48:26 +0200 Subject: [PATCH 11/34] Allow for max_zombies to be adjusted --- frame/assets/src/lib.rs | 55 ++++++++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 62ea60d25fb55..f9d1cb4b26437 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -87,7 +87,7 @@ //! * `force_transfer`: Transfers between arbitrary accounts; called by the asset class's Admin. //! * `freeze`: Disallows further `transfer`s from an account; called by the asset class's Freezer. //! * `thaw`: Allows further `transfer`s from an account; called by the asset class's Admin. -//! * `set_owner`: Changes an asset class's Owner; called by the asset class's Owner. +//! * `transfer_ownership`: Changes an asset class's Owner; called by the asset class's Owner. //! * `set_team`: Changes an asset class's Admin, Freezer and Issuer; called by the asset class's //! Owner. //! @@ -115,7 +115,7 @@ use sp_runtime::{RuntimeDebug, traits::{ }}; use codec::{Encode, Decode}; use frame_support::{Parameter, decl_module, decl_event, decl_storage, decl_error, ensure, - traits::{Currency, ReservableCurrency, EnsureOrigin, Get}, + traits::{Currency, ReservableCurrency, EnsureOrigin, Get, BalanceStatus::Reserved}, dispatch::{DispatchResult, DispatchError}, }; use frame_system::ensure_signed; @@ -229,6 +229,8 @@ decl_event! { Destroyed(AssetId), /// Some asset class was force-created. \[asset_id, owner\] ForceCreated(AssetId, AccountId), + /// The maximum amount of zombies allowed has changed. \[asset_id, max_zombies\] + MaxZombiesChanged(AssetId, u32), } } @@ -257,7 +259,7 @@ decl_module! { pub struct Module for enum Call where origin: T::Origin { type Error = Error; - // TODO: Allow for max_zombies to be adjusted. + // TODO: Test set_max_zombies. // TODO: Allow for easy integration with system accounts so that an ED in an asset here is // sufficient for account existence and tx fees may be paid through assets here. An // `Exchange` trait is probably best for this so AMM pallets can be wired in. @@ -277,7 +279,7 @@ decl_module! { /// - `id`: The identifier of the new asset. This must not be currently in use to identify /// an existing asset. /// - `owner`: The owner of this class of assets. The owner has full superuser permissions - /// over this asset, but may later change and configure the permissions using `set_owner` + /// over this asset, but may later change and configure the permissions using `transfer_ownership` /// and `set_team`. /// - `max_zombies`: The total number of accounts which may hold assets in this class yet /// have no existential deposit. @@ -329,7 +331,7 @@ decl_module! { /// - `id`: The identifier of the new asset. This must not be currently in use to identify /// an existing asset. /// - `owner`: The owner of this class of assets. The owner has full superuser permissions - /// over this asset, but may later change and configure the permissions using `set_owner` + /// over this asset, but may later change and configure the permissions using `transfer_ownership` /// and `set_team`. /// - `max_zombies`: The total number of accounts which may hold assets in this class yet /// have no existential deposit. @@ -709,7 +711,7 @@ decl_module! { /// /// Weight: `O(1)` #[weight = 0] - fn set_owner(origin, + fn transfer_ownership(origin, #[compact] id: T::AssetId, owner: ::Source, ) -> DispatchResult { @@ -719,6 +721,10 @@ decl_module! { Asset::::try_mutate(id, |maybe_details| { let d = maybe_details.as_mut().ok_or(Error::::Unknown)?; ensure!(&origin == &d.owner, Error::::NoPermission); + if d.owner == owner { return Ok(()) } + + // Move the deposit to the new owner. + T::Currency::repatriate_reserved(&d.owner, &owner, d.deposit, Reserved)?; d.owner = owner.clone(); @@ -763,6 +769,33 @@ decl_module! { Ok(()) }) } + + #[weight = 0] + fn set_max_zombies(origin, + #[compact] id: T::AssetId, + #[compact] max_zombies: u32, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + + Asset::::try_mutate(id, |maybe_details| { + let d = maybe_details.as_mut().ok_or(Error::::Unknown)?; + ensure!(&origin == &d.owner, Error::::NoPermission); + ensure!(max_zombies >= d.zombies, Error::::TooManyZombies); + + let new_deposit = T::AssetDepositPerZombie::get() + .saturating_mul(max_zombies.into()) + .saturating_add(T::AssetDepositBase::get()); + + if new_deposit > d.deposit { + T::Currency::reserve(&origin, new_deposit - d.deposit)?; + } else { + T::Currency::unreserve(&origin, d.deposit - new_deposit); + } + + Self::deposit_event(RawEvent::MaxZombiesChanged(id, max_zombies)); + Ok(()) + }) + } } } @@ -1015,7 +1048,7 @@ mod tests { new_test_ext().execute_with(|| { assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); - assert_noop!(Assets::set_owner(Origin::signed(2), 0, 2), Error::::NoPermission); + assert_noop!(Assets::transfer_ownership(Origin::signed(2), 0, 2), Error::::NoPermission); assert_noop!(Assets::set_team(Origin::signed(2), 0, 2, 2, 2), Error::::NoPermission); assert_noop!(Assets::freeze(Origin::signed(2), 0, 1), Error::::NoPermission); assert_noop!(Assets::thaw(Origin::signed(2), 0, 2), Error::::NoPermission); @@ -1026,12 +1059,12 @@ mod tests { } #[test] - fn set_owner_should_work() { + fn transfer_owner_should_workship() { new_test_ext().execute_with(|| { assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); - assert_ok!(Assets::set_owner(Origin::signed(1), 0, 2)); - assert_noop!(Assets::set_owner(Origin::signed(1), 0, 1), Error::::NoPermission); - assert_ok!(Assets::set_owner(Origin::signed(2), 0, 1)); + assert_ok!(Assets::transfer_ownership(Origin::signed(1), 0, 2)); + assert_noop!(Assets::transfer_ownership(Origin::signed(1), 0, 1), Error::::NoPermission); + assert_ok!(Assets::transfer_ownership(Origin::signed(2), 0, 1)); }); } From 347f2dbbe74928ea5ce917d4ee940399c1425d23 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 5 Oct 2020 11:55:53 +0200 Subject: [PATCH 12/34] Test for set_max_zombies --- frame/assets/src/lib.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index f9d1cb4b26437..bc5ee31a3b027 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -792,6 +792,8 @@ decl_module! { T::Currency::unreserve(&origin, d.deposit - new_deposit); } + d.max_zombies = max_zombies; + Self::deposit_event(RawEvent::MaxZombiesChanged(id, max_zombies)); Ok(()) }) @@ -971,6 +973,26 @@ mod tests { assert_ok!(Assets::transfer(Origin::signed(0), 0, 1, 100)); assert_eq!(Assets::zombie_allowance(0), 1); assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); + + // TODO: Test dezombify + }); + } + + #[test] + fn resetting_max_zombies_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, 2, 1)); + Balances::make_free_balance_be(&1, 100); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 2, 100)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 3, 100)); + + assert_eq!(Assets::zombie_allowance(0), 0); + + assert_noop!(Assets::set_max_zombies(Origin::signed(1), 0, 1), Error::::TooManyZombies); + + assert_ok!(Assets::set_max_zombies(Origin::signed(1), 0, 3)); + assert_eq!(Assets::zombie_allowance(0), 1); }); } @@ -1055,6 +1077,7 @@ mod tests { assert_noop!(Assets::mint(Origin::signed(2), 0, 2, 100), Error::::NoPermission); assert_noop!(Assets::burn(Origin::signed(2), 0, 1, 100), Error::::NoPermission); assert_noop!(Assets::force_transfer(Origin::signed(2), 0, 1, 2, 100), Error::::NoPermission); + assert_noop!(Assets::set_max_zombies(Origin::signed(2), 0, 11), Error::::NoPermission); }); } From 2d5f7529075eb299cbd703cacd2c811f27ba20f6 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 5 Oct 2020 16:14:57 +0200 Subject: [PATCH 13/34] Tests and a couple of fixes --- frame/assets/src/lib.rs | 97 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 89 insertions(+), 8 deletions(-) diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index bc5ee31a3b027..39cd913c6b053 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -170,6 +170,8 @@ pub struct AssetDetails< min_balance: Balance, /// The current number of zombie accounts. zombies: u32, + /// The total number of accounts. + accounts: u32, } #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default)] @@ -252,6 +254,8 @@ decl_error! { InUse, /// Too many zombie accounts in use. TooManyZombies, + /// Attempt to destroy an asset class when non-zombie, reference-bearing accounts exist. + RefsLeft, } } @@ -259,7 +263,6 @@ decl_module! { pub struct Module for enum Call where origin: T::Origin { type Error = Error; - // TODO: Test set_max_zombies. // TODO: Allow for easy integration with system accounts so that an ED in an asset here is // sufficient for account existence and tx fees may be paid through assets here. An // `Exchange` trait is probably best for this so AMM pallets can be wired in. @@ -316,6 +319,7 @@ decl_module! { max_zombies, min_balance, zombies: Zero::zero(), + accounts: Zero::zero(), }); Self::deposit_event(RawEvent::Created(id, origin, owner)); } @@ -363,6 +367,7 @@ decl_module! { max_zombies, min_balance, zombies: Zero::zero(), + accounts: Zero::zero(), }); Self::deposit_event(RawEvent::ForceCreated(id, owner)); } @@ -384,6 +389,9 @@ decl_module! { Asset::::try_mutate_exists(id, |maybe_details| { let details = maybe_details.take().ok_or(Error::::Unknown)?; ensure!(details.owner == origin, Error::::NoPermission); + ensure!(details.accounts == details.zombies, Error::::RefsLeft); + T::Currency::unreserve(&details.owner, details.deposit); + *maybe_details = None; Account::::remove_prefix(&id); Self::deposit_event(RawEvent::Destroyed(id)); @@ -406,6 +414,10 @@ decl_module! { T::ForceOrigin::ensure_origin(origin)?; Asset::::try_mutate_exists(id, |maybe_details| { + let details = maybe_details.take().ok_or(Error::::Unknown)?; + ensure!(details.accounts == details.zombies, Error::::RefsLeft); + T::Currency::unreserve(&details.owner, details.deposit); + *maybe_details = None; Account::::remove_prefix(&id); Self::deposit_event(RawEvent::Destroyed(id)); @@ -824,14 +836,16 @@ impl Module { who: &T::AccountId, d: &mut AssetDetails>, ) -> Result { - Ok(if frame_system::Module::::account_exists(who) { + let r = Ok(if frame_system::Module::::account_exists(who) { frame_system::Module::::inc_ref(who); false } else { ensure!(d.zombies < d.max_zombies, Error::::TooManyZombies); d.zombies += 1; true - }) + }); + d.accounts = d.accounts.saturating_add(1); + r } /// If `who`` exists in system and it's a zombie, dezombify it. @@ -857,6 +871,7 @@ impl Module { } else { frame_system::Module::::dec_ref(who); } + d.accounts = d.accounts.saturating_sub(1); } } @@ -945,7 +960,7 @@ mod tests { } #[test] - fn issuing_asset_units_should_work() { + fn basic_minting_should_work() { new_test_ext().execute_with(|| { assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); @@ -955,6 +970,37 @@ mod tests { }); } + #[test] + fn lifecycle_should_work() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + assert_ok!(Assets::create(Origin::signed(1), 0, 1, 10, 1)); + assert_eq!(Balances::reserved_balance(&1), 11); + + assert_ok!(Assets::destroy(Origin::signed(1), 0)); + assert_eq!(Balances::reserved_balance(&1), 0); + + assert_ok!(Assets::create(Origin::signed(1), 0, 1, 10, 1)); + assert_eq!(Balances::reserved_balance(&1), 11); + + assert_ok!(Assets::force_destroy(Origin::root(), 0)); + assert_eq!(Balances::reserved_balance(&1), 0); + }); + } + + #[test] + fn destroy_with_non_zombies_should_not_work() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + assert_noop!(Assets::destroy(Origin::signed(1), 0), Error::::RefsLeft); + assert_noop!(Assets::force_destroy(Origin::root(), 0), Error::::RefsLeft); + assert_ok!(Assets::burn(Origin::signed(1), 0, 1, 100)); + assert_ok!(Assets::destroy(Origin::signed(1), 0)); + }); + } + #[test] fn max_zombies_should_work() { new_test_ext().execute_with(|| { @@ -996,6 +1042,29 @@ mod tests { }); } + #[test] + fn dezombifying_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 10)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Assets::zombie_allowance(0), 9); + + // introduce a bit of balance for account 2. + Balances::make_free_balance_be(&2, 100); + + // transfer 25 units, nothing changes. + assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 25)); + assert_eq!(Assets::zombie_allowance(0), 9); + + // introduce a bit of balance; this will create the account. + Balances::make_free_balance_be(&1, 100); + + // now transferring 25 units will create it. + assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 25)); + assert_eq!(Assets::zombie_allowance(0), 10); + }); + } + #[test] fn min_balance_should_work() { new_test_ext().execute_with(|| { @@ -1078,16 +1147,28 @@ mod tests { assert_noop!(Assets::burn(Origin::signed(2), 0, 1, 100), Error::::NoPermission); assert_noop!(Assets::force_transfer(Origin::signed(2), 0, 1, 2, 100), Error::::NoPermission); assert_noop!(Assets::set_max_zombies(Origin::signed(2), 0, 11), Error::::NoPermission); + assert_noop!(Assets::destroy(Origin::signed(2), 0), Error::::NoPermission); }); } #[test] - fn transfer_owner_should_workship() { + fn transfer_owner_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 1); + assert_ok!(Assets::create(Origin::signed(1), 0, 1, 10, 1)); + + assert_eq!(Balances::reserved_balance(&1), 11); + assert_ok!(Assets::transfer_ownership(Origin::signed(1), 0, 2)); + assert_eq!(Balances::reserved_balance(&2), 11); + assert_eq!(Balances::reserved_balance(&1), 0); + assert_noop!(Assets::transfer_ownership(Origin::signed(1), 0, 1), Error::::NoPermission); + assert_ok!(Assets::transfer_ownership(Origin::signed(2), 0, 1)); + assert_eq!(Balances::reserved_balance(&1), 11); + assert_eq!(Balances::reserved_balance(&2), 0); }); } @@ -1155,7 +1236,7 @@ mod tests { } #[test] - fn destroying_asset_balance_with_positive_balance_should_work() { + fn burning_asset_balance_with_positive_balance_should_work() { new_test_ext().execute_with(|| { assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); @@ -1165,7 +1246,7 @@ mod tests { } #[test] - fn destroying_asset_balance_with_zero_balance_should_not_work() { + fn burning_asset_balance_with_zero_balance_should_not_work() { new_test_ext().execute_with(|| { assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); From 760ba444cc04c7afeee7051c46a943ad9d004ccc Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 8 Oct 2020 15:06:53 +0200 Subject: [PATCH 14/34] First few benchmarks --- Cargo.lock | 1 + frame/assets/Cargo.toml | 8 + frame/assets/src/benchmarking.rs | 239 +++++++++++++++++++++++++++++ frame/assets/src/default_weight.rs | 38 +++++ frame/assets/src/lib.rs | 71 ++++++--- frame/balances/src/lib.rs | 2 +- 6 files changed, 334 insertions(+), 25 deletions(-) create mode 100644 frame/assets/src/benchmarking.rs create mode 100644 frame/assets/src/default_weight.rs diff --git a/Cargo.lock b/Cargo.lock index ee7e2b2bb82b1..1c74bce4ec45f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4194,6 +4194,7 @@ dependencies = [ name = "pallet-assets" version = "2.0.0" dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", "pallet-balances", diff --git a/frame/assets/Cargo.toml b/frame/assets/Cargo.toml index 3ea11b9d8e249..380b561dba407 100644 --- a/frame/assets/Cargo.toml +++ b/frame/assets/Cargo.toml @@ -22,6 +22,7 @@ sp-runtime = { version = "2.0.0", default-features = false, path = "../../primit frame-support = { version = "2.0.0", default-features = false, path = "../support" } # `system` module provides us with all sorts of useful stuff and macros depend on it being around. frame-system = { version = "2.0.0", default-features = false, path = "../system" } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking", optional = true } [dev-dependencies] sp-core = { version = "2.0.0", path = "../../primitives/core" } @@ -34,7 +35,14 @@ default = ["std"] std = [ "serde", "codec/std", + "sp-std/std", "sp-runtime/std", "frame-support/std", "frame-system/std", + "frame-benchmarking/std", +] +runtime-benchmarks = [ + "frame-benchmarking", + "sp-runtime/runtime-benchmarks", + "frame-system/runtime-benchmarks", ] diff --git a/frame/assets/src/benchmarking.rs b/frame/assets/src/benchmarking.rs new file mode 100644 index 0000000000000..ee0f95da08194 --- /dev/null +++ b/frame/assets/src/benchmarking.rs @@ -0,0 +1,239 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Staking pallet benchmarking. + +use super::*; + +use sp_runtime::traits::Bounded; +use frame_system::RawOrigin as SystemOrigin; +use frame_support::assert_ok; +use frame_benchmarking::{benchmarks, whitelisted_caller}; + +use crate::Module as Assets; + +/* +use frame_system::EventRecord; +fn assert_last_event(generic_event: ::Event) { + let events = System::::events(); + let system_event: ::Event = generic_event.into(); + // compare to the last event record + let EventRecord { event, .. } = &events[events.len() - 1]; + assert_eq!(event, &system_event); +} +*/ + +benchmarks! { + _ { } + + create { + let caller: T::AccountId = whitelisted_caller(); + let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); + T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + }: _(SystemOrigin::Signed(caller), Default::default(), caller_lookup, 1, 1.into()) + verify { + assert!(true) + } + + force_create { + let caller: T::AccountId = whitelisted_caller(); + let caller_lookup: ::Source = T::Lookup::unlookup(caller); + }: _(SystemOrigin::Root, Default::default(), caller_lookup, 1, 1.into()) + verify { + assert!(true) + } + + destroy { + let caller: T::AccountId = whitelisted_caller(); + let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); + let root = SystemOrigin::Root.into(); + assert_ok!(Assets::::force_create(root, Default::default(), caller_lookup, 1, 1.into())); + }: _(SystemOrigin::Signed(caller), Default::default()) + verify { + assert!(true) + } + + force_destroy { + let caller: T::AccountId = whitelisted_caller(); + let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); + let root = SystemOrigin::Root.into(); + assert_ok!(Assets::::force_create(root, Default::default(), caller_lookup, 1, 1.into())); + }: _(SystemOrigin::Root, Default::default()) + verify { + assert!(true) + } + + /*mint { + let caller: T::AccountId = whitelisted_caller(); + }: _(SystemOrigin::Signed(caller)) + verify { + assert(true) + } + + burn { + let caller: T::AccountId = whitelisted_caller(); + }: _(SystemOrigin::Signed(caller)) + verify { + assert(true) + } + + transfer { + let caller: T::AccountId = whitelisted_caller(); + }: _(SystemOrigin::Signed(caller)) + verify { + assert(true) + } + + force_transfer { + let caller: T::AccountId = whitelisted_caller(); + }: _(SystemOrigin::Signed(caller)) + verify { + assert(true) + } + + freeze { + let caller: T::AccountId = whitelisted_caller(); + }: _(SystemOrigin::Signed(caller)) + verify { + assert(true) + } + + thaw { + let caller: T::AccountId = whitelisted_caller(); + }: _(SystemOrigin::Signed(caller)) + verify { + assert(true) + } + + transfer_ownership { + let caller: T::AccountId = whitelisted_caller(); + }: _(SystemOrigin::Signed(caller)) + verify { + assert(true) + } + + set_team { + let caller: T::AccountId = whitelisted_caller(); + }: _(SystemOrigin::Signed(caller)) + verify { + assert(true) + } + + set_max_zombies { + let caller: T::AccountId = whitelisted_caller(); + }: _(SystemOrigin::Signed(caller)) + verify { + assert(true) + }*/ +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::tests::{new_test_ext, Test}; + + #[test] + fn create() { + new_test_ext().execute_with(|| { + assert_ok!(test_benchmark_create::()); + }); + } + + #[test] + fn force_create() { + new_test_ext().execute_with(|| { + assert_ok!(test_benchmark_force_create::()); + }); + } + + #[test] + fn destroy() { + new_test_ext().execute_with(|| { + assert_ok!(test_benchmark_destroy::()); + }); + } + + #[test] + fn force_destroy() { + new_test_ext().execute_with(|| { + assert_ok!(test_benchmark_force_destroy::()); + }); + } +/* + #[test] + fn mint() { + new_test_ext().execute_with(|| { + assert_ok!(test_benchmark_mint::()); + }); + } + + #[test] + fn burn() { + new_test_ext().execute_with(|| { + assert_ok!(test_benchmark_burn::()); + }); + } + + #[test] + fn transfer() { + new_test_ext().execute_with(|| { + assert_ok!(test_benchmark_transfer::()); + }); + } + + #[test] + fn force_transfer() { + new_test_ext().execute_with(|| { + assert_ok!(test_benchmark_force_transfer::()); + }); + } + + #[test] + fn freeze() { + new_test_ext().execute_with(|| { + assert_ok!(test_benchmark_freeze::()); + }); + } + + #[test] + fn thaw() { + new_test_ext().execute_with(|| { + assert_ok!(test_benchmark_thaw::()); + }); + } + + #[test] + fn transfer_ownership() { + new_test_ext().execute_with(|| { + assert_ok!(test_benchmark_transfer_ownership::()); + }); + } + + #[test] + fn set_team() { + new_test_ext().execute_with(|| { + assert_ok!(test_benchmark_set_team::()); + }); + } + + #[test] + fn set_max_zombies() { + new_test_ext().execute_with(|| { + assert_ok!(test_benchmark_set_max_zombies::()); + }); + }*/ +} diff --git a/frame/assets/src/default_weight.rs b/frame/assets/src/default_weight.rs new file mode 100644 index 0000000000000..b2954c765b1f6 --- /dev/null +++ b/frame/assets/src/default_weight.rs @@ -0,0 +1,38 @@ +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Default weights for the Collective Pallet +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6 + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight}; + +impl crate::WeightInfo for () { + fn create() -> Weight { 0 as Weight } + fn force_create() -> Weight { 0 as Weight } + fn destroy() -> Weight { 0 as Weight } + fn force_destroy() -> Weight { 0 as Weight } + fn mint() -> Weight { 0 as Weight } + fn burn() -> Weight { 0 as Weight } + fn transfer() -> Weight { 0 as Weight } + fn force_transfer() -> Weight { 0 as Weight } + fn freeze() -> Weight { 0 as Weight } + fn thaw() -> Weight { 0 as Weight } + fn transfer_ownership() -> Weight { 0 as Weight } + fn set_team() -> Weight { 0 as Weight } + fn set_max_zombies() -> Weight { 0 as Weight } +} diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 39cd913c6b053..95d76796545b8 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -111,15 +111,20 @@ use sp_std::{fmt::Debug}; use sp_runtime::{RuntimeDebug, traits::{ - Member, AtLeast32Bit, AtLeast32BitUnsigned, Zero, StaticLookup, Saturating, CheckedSub, + Member, AtLeast32BitUnsigned, Zero, StaticLookup, Saturating, CheckedSub, }}; -use codec::{Encode, Decode}; +use codec::{Encode, Decode, HasCompact}; use frame_support::{Parameter, decl_module, decl_event, decl_storage, decl_error, ensure, traits::{Currency, ReservableCurrency, EnsureOrigin, Get, BalanceStatus::Reserved}, - dispatch::{DispatchResult, DispatchError}, + dispatch::{DispatchResult, DispatchError}, weights::Weight }; use frame_system::ensure_signed; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +mod default_weight; + type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; /// The module configuration trait. @@ -131,7 +136,7 @@ pub trait Trait: frame_system::Trait { type Balance: Member + Parameter + AtLeast32BitUnsigned + Default + Copy; /// The arithmetic type of asset identifier. - type AssetId: Parameter + AtLeast32Bit + Default + Copy; + type AssetId: Member + Parameter + Default + Copy + HasCompact; /// The currency mechanism. type Currency: ReservableCurrency; @@ -139,8 +144,15 @@ pub trait Trait: frame_system::Trait { /// The origin which may forcibly create or destroy an asset. Root can always do this. type ForceOrigin: EnsureOrigin; + /// The basic amount of funds that must be reserved when creating a new asset class. type AssetDepositBase: Get>; + + /// The additional funds that must be reserved for every zombie account that an asset class + /// supports. type AssetDepositPerZombie: Get>; + + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; } #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug)] @@ -259,14 +271,26 @@ decl_error! { } } +pub trait WeightInfo { + fn create() -> Weight; + fn force_create() -> Weight; + fn destroy() -> Weight; + fn force_destroy() -> Weight; + fn mint() -> Weight; + fn burn() -> Weight; + fn transfer() -> Weight; + fn force_transfer() -> Weight; + fn freeze() -> Weight; + fn thaw() -> Weight; + fn transfer_ownership() -> Weight; + fn set_team() -> Weight; + fn set_max_zombies() -> Weight; +} + decl_module! { pub struct Module for enum Call where origin: T::Origin { type Error = Error; - // TODO: Allow for easy integration with system accounts so that an ED in an asset here is - // sufficient for account existence and tx fees may be paid through assets here. An - // `Exchange` trait is probably best for this so AMM pallets can be wired in. - fn deposit_event() = default; /// Issue a new class of fungible assets from a public origin. @@ -292,7 +316,7 @@ decl_module! { /// Emits `Created` event when successful. /// /// Weight: `O(1)` - #[weight = 0] + #[weight = T::WeightInfo::create()] fn create(origin, #[compact] id: T::AssetId, owner: ::Source, @@ -345,7 +369,7 @@ decl_module! { /// Emits `ForceCreated` event when successful. /// /// Weight: `O(1)` - #[weight = 0] + #[weight = T::WeightInfo::force_create()] fn force_create(origin, #[compact] id: T::AssetId, owner: ::Source, @@ -382,7 +406,7 @@ decl_module! { /// Emits `Destroyed` event when successful. /// /// Weight: `O(1)` - #[weight = 0] + #[weight = T::WeightInfo::destroy()] fn destroy(origin, #[compact] id: T::AssetId) -> DispatchResult { let origin = ensure_signed(origin)?; @@ -409,7 +433,7 @@ decl_module! { /// Emits `Destroyed` event when successful. /// /// Weight: `O(1)` - #[weight = 0] + #[weight = T::WeightInfo::force_destroy()] fn force_destroy(origin, #[compact] id: T::AssetId) -> DispatchResult { T::ForceOrigin::ensure_origin(origin)?; @@ -437,7 +461,7 @@ decl_module! { /// /// Weight: `O(1)` /// Modes: Pre-existence of `beneficiary` - #[weight = 0] + #[weight = T::WeightInfo::mint()] fn mint(origin, #[compact] id: T::AssetId, beneficiary: ::Source, @@ -481,7 +505,7 @@ decl_module! { /// /// Weight: `O(1)` /// Modes: Post-existence of `who` - #[weight = 0] + #[weight = T::WeightInfo::burn()] fn burn(origin, #[compact] id: T::AssetId, who: ::Source, @@ -537,7 +561,7 @@ decl_module! { /// Weight: `O(1)` /// Modes: Pre-existence of `target`; Post-existence of sender; Prior & post zombie-status /// of sender. - #[weight = 0] + #[weight = T::WeightInfo::transfer()] fn transfer(origin, #[compact] id: T::AssetId, target: ::Source, @@ -610,7 +634,7 @@ decl_module! { /// Weight: `O(1)` /// Modes: Pre-existence of `dest`; Post-existence of `source`. Prior & post zombie-status /// of `source`. - #[weight = 0] + #[weight = T::WeightInfo::force_transfer()] fn force_transfer(origin, #[compact] id: T::AssetId, source: ::Source, @@ -674,7 +698,7 @@ decl_module! { /// Emits `Frozen`. /// /// Weight: `O(1)` - #[weight = 0] + #[weight = T::WeightInfo::freeze()] fn freeze(origin, #[compact] id: T::AssetId, who: ::Source) { let origin = ensure_signed(origin)?; @@ -698,7 +722,7 @@ decl_module! { /// Emits `Thawed`. /// /// Weight: `O(1)` - #[weight = 0] + #[weight = T::WeightInfo::thaw()] fn thaw(origin, #[compact] id: T::AssetId, who: ::Source) { let origin = ensure_signed(origin)?; @@ -722,7 +746,7 @@ decl_module! { /// Emits `OwnerChanged`. /// /// Weight: `O(1)` - #[weight = 0] + #[weight = T::WeightInfo::transfer_ownership()] fn transfer_ownership(origin, #[compact] id: T::AssetId, owner: ::Source, @@ -757,7 +781,7 @@ decl_module! { /// Emits `TeamChanged`. /// /// Weight: `O(1)` - #[weight = 0] + #[weight = T::WeightInfo::set_team()] fn set_team(origin, #[compact] id: T::AssetId, issuer: ::Source, @@ -782,7 +806,7 @@ decl_module! { }) } - #[weight = 0] + #[weight = T::WeightInfo::set_max_zombies()] fn set_max_zombies(origin, #[compact] id: T::AssetId, #[compact] max_zombies: u32, @@ -950,12 +974,13 @@ mod tests { type ForceOrigin = frame_system::EnsureRoot; type AssetDepositBase = AssetDepositBase; type AssetDepositPerZombie = AssetDepositPerZombie; + type WeightInfo = (); } type System = frame_system::Module; type Balances = pallet_balances::Module; type Assets = Module; - fn new_test_ext() -> sp_io::TestExternalities { + pub(crate) fn new_test_ext() -> sp_io::TestExternalities { frame_system::GenesisConfig::default().build_storage::().unwrap().into() } @@ -1019,8 +1044,6 @@ mod tests { assert_ok!(Assets::transfer(Origin::signed(0), 0, 1, 100)); assert_eq!(Assets::zombie_allowance(0), 1); assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); - - // TODO: Test dezombify }); } diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index 422e112bdf276..203f8b5d1e69c 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -417,7 +417,7 @@ decl_storage! { for (_, balance) in &config.balances { assert!( *balance >= >::ExistentialDeposit::get(), - "the balance of any account should always be more than existential deposit.", + "the balance of any account should always be at least the existential deposit.", ) } for &(ref who, free) in config.balances.iter() { From b630906a918920dc8b5845a68f1017971875ade3 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 8 Oct 2020 18:55:39 +0200 Subject: [PATCH 15/34] Benchmarks. --- frame/assets/src/benchmarking.rs | 131 +++++++++++++++++++------------ 1 file changed, 80 insertions(+), 51 deletions(-) diff --git a/frame/assets/src/benchmarking.rs b/frame/assets/src/benchmarking.rs index ee0f95da08194..203b6660966d7 100644 --- a/frame/assets/src/benchmarking.rs +++ b/frame/assets/src/benchmarking.rs @@ -22,27 +22,47 @@ use super::*; use sp_runtime::traits::Bounded; use frame_system::RawOrigin as SystemOrigin; use frame_support::assert_ok; -use frame_benchmarking::{benchmarks, whitelisted_caller}; +use frame_benchmarking::{benchmarks, account, whitelisted_caller}; use crate::Module as Assets; -/* -use frame_system::EventRecord; -fn assert_last_event(generic_event: ::Event) { - let events = System::::events(); - let system_event: ::Event = generic_event.into(); - // compare to the last event record - let EventRecord { event, .. } = &events[events.len() - 1]; - assert_eq!(event, &system_event); +const SEED: u32 = 0; + +fn create_default_asset(max_zombies: u32) + -> (T::AccountId, ::Source) +{ + let caller: T::AccountId = whitelisted_caller(); + let caller_lookup = T::Lookup::unlookup(caller.clone()); + let root = SystemOrigin::Root.into(); + assert_ok!(Assets::::force_create( + root, + Default::default(), + caller_lookup.clone(), + max_zombies, + 1.into(), + )); + (caller, caller_lookup) +} + +fn create_default_minted_asset(max_zombies: u32, amount: T::Balance) + -> (T::AccountId, ::Source) +{ + let (caller, caller_lookup) = create_default_asset::(max_zombies); + assert_ok!(Assets::::mint( + SystemOrigin::Signed(caller.clone()).into(), + Default::default(), + caller_lookup.clone(), + amount, + )); + (caller, caller_lookup) } -*/ benchmarks! { _ { } create { let caller: T::AccountId = whitelisted_caller(); - let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); + let caller_lookup = T::Lookup::unlookup(caller.clone()); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); }: _(SystemOrigin::Signed(caller), Default::default(), caller_lookup, 1, 1.into()) verify { @@ -51,94 +71,103 @@ benchmarks! { force_create { let caller: T::AccountId = whitelisted_caller(); - let caller_lookup: ::Source = T::Lookup::unlookup(caller); + let caller_lookup = T::Lookup::unlookup(caller); }: _(SystemOrigin::Root, Default::default(), caller_lookup, 1, 1.into()) verify { assert!(true) } destroy { - let caller: T::AccountId = whitelisted_caller(); - let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); - let root = SystemOrigin::Root.into(); - assert_ok!(Assets::::force_create(root, Default::default(), caller_lookup, 1, 1.into())); + let (caller, _) = create_default_asset::(10); }: _(SystemOrigin::Signed(caller), Default::default()) verify { assert!(true) } force_destroy { - let caller: T::AccountId = whitelisted_caller(); - let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); - let root = SystemOrigin::Root.into(); - assert_ok!(Assets::::force_create(root, Default::default(), caller_lookup, 1, 1.into())); + let _ = create_default_asset::(10); }: _(SystemOrigin::Root, Default::default()) verify { assert!(true) } - /*mint { - let caller: T::AccountId = whitelisted_caller(); - }: _(SystemOrigin::Signed(caller)) + mint { + let (caller, caller_lookup) = create_default_asset::(10); + }: _(SystemOrigin::Signed(caller), Default::default(), caller_lookup, 100.into()) verify { - assert(true) + assert!(true) } burn { - let caller: T::AccountId = whitelisted_caller(); - }: _(SystemOrigin::Signed(caller)) + let (caller, caller_lookup) = create_default_minted_asset::(10, 100.into()); + }: _(SystemOrigin::Signed(caller), Default::default(), caller_lookup, 100.into()) verify { - assert(true) + assert!(true) } transfer { - let caller: T::AccountId = whitelisted_caller(); - }: _(SystemOrigin::Signed(caller)) + let (caller, caller_lookup) = create_default_minted_asset::(10, 100.into()); + let target = account("target", 0, SEED); + let target_lookup = T::Lookup::unlookup(target); + }: _(SystemOrigin::Signed(caller), Default::default(), target_lookup, 100.into()) verify { - assert(true) + assert!(true) } force_transfer { - let caller: T::AccountId = whitelisted_caller(); - }: _(SystemOrigin::Signed(caller)) + let (caller, caller_lookup) = create_default_minted_asset::(10, 100.into()); + let target = account("target", 0, SEED); + let target_lookup = T::Lookup::unlookup(target); + }: _(SystemOrigin::Signed(caller), Default::default(), caller_lookup, target_lookup, 100.into()) verify { - assert(true) + assert!(true) } freeze { - let caller: T::AccountId = whitelisted_caller(); - }: _(SystemOrigin::Signed(caller)) + let (caller, caller_lookup) = create_default_minted_asset::(10, 100.into()); + }: _(SystemOrigin::Signed(caller), Default::default(), caller_lookup) verify { - assert(true) + assert!(true) } thaw { - let caller: T::AccountId = whitelisted_caller(); - }: _(SystemOrigin::Signed(caller)) + let (caller, caller_lookup) = create_default_minted_asset::(10, 100.into()); + assert_ok!(Assets::::freeze( + SystemOrigin::Signed(caller.clone()).into(), + Default::default(), + caller_lookup.clone() + )); + }: _(SystemOrigin::Signed(caller), Default::default(), caller_lookup) verify { - assert(true) + assert!(true) } transfer_ownership { - let caller: T::AccountId = whitelisted_caller(); - }: _(SystemOrigin::Signed(caller)) + let (caller, _) = create_default_asset::(10); + let target = account("target", 0, SEED); + let target_lookup = T::Lookup::unlookup(target); + }: _(SystemOrigin::Signed(caller), Default::default(), target_lookup) verify { - assert(true) + assert!(true) } set_team { - let caller: T::AccountId = whitelisted_caller(); - }: _(SystemOrigin::Signed(caller)) + let (caller, _) = create_default_asset::(10); + let target0 = T::Lookup::unlookup(account("target", 0, SEED)); + let target1 = T::Lookup::unlookup(account("target", 1, SEED)); + let target2 = T::Lookup::unlookup(account("target", 2, SEED)); + }: _(SystemOrigin::Signed(caller), Default::default(), target0, target1, target2) verify { - assert(true) + assert!(true) } set_max_zombies { - let caller: T::AccountId = whitelisted_caller(); - }: _(SystemOrigin::Signed(caller)) + let (caller, _) = create_default_asset::(10); + T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + }: _(SystemOrigin::Signed(caller), Default::default(), 100) verify { - assert(true) - }*/ + assert!(true) + } } #[cfg(test)] @@ -173,7 +202,7 @@ mod tests { assert_ok!(test_benchmark_force_destroy::()); }); } -/* + #[test] fn mint() { new_test_ext().execute_with(|| { @@ -235,5 +264,5 @@ mod tests { new_test_ext().execute_with(|| { assert_ok!(test_benchmark_set_max_zombies::()); }); - }*/ + } } From 4664812c2913101775c006c3c36c663356935e94 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 15 Oct 2020 14:56:17 +0200 Subject: [PATCH 16/34] Fix error message in test --- frame/balances/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/balances/src/tests.rs b/frame/balances/src/tests.rs index 210c75631da63..70d113b69a54d 100644 --- a/frame/balances/src/tests.rs +++ b/frame/balances/src/tests.rs @@ -630,7 +630,7 @@ macro_rules! decl_tests { } #[test] - #[should_panic = "the balance of any account should always be more than existential deposit."] + #[should_panic = "the balance of any account should always be at least the existential deposit."] fn cannot_set_genesis_value_below_ed() { ($existential_deposit).with(|v| *v.borrow_mut() = 11); let mut t = frame_system::GenesisConfig::default().build_storage::<$test>().unwrap(); From 89547643aaad4ce6deb24c964c098a369ce330c7 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 15 Oct 2020 15:19:05 +0200 Subject: [PATCH 17/34] Fixes --- Cargo.lock | 1 + bin/node/runtime/Cargo.toml | 3 ++ bin/node/runtime/src/lib.rs | 18 ++++++++++ bin/node/runtime/src/weights/mod.rs | 1 + bin/node/runtime/src/weights/pallet_assets.rs | 36 +++++++++++++++++++ 5 files changed, 59 insertions(+) create mode 100644 bin/node/runtime/src/weights/pallet_assets.rs diff --git a/Cargo.lock b/Cargo.lock index 70bd2c95a2135..4728dca2a57a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3874,6 +3874,7 @@ dependencies = [ "hex-literal", "integer-sqrt", "node-primitives", + "pallet-assets", "pallet-authority-discovery", "pallet-authorship", "pallet-babe", diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 6142998ac063c..fd3339b81bd33 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -44,6 +44,7 @@ frame-support = { version = "2.0.0", default-features = false, path = "../../../ frame-system = { version = "2.0.0", default-features = false, path = "../../../frame/system" } frame-system-benchmarking = { version = "2.0.0", default-features = false, path = "../../../frame/system/benchmarking", optional = true } frame-system-rpc-runtime-api = { version = "2.0.0", default-features = false, path = "../../../frame/system/rpc/runtime-api/" } +pallet-assets = { version = "2.0.0", default-features = false, path = "../../../frame/assets" } pallet-authority-discovery = { version = "2.0.0", default-features = false, path = "../../../frame/authority-discovery" } pallet-authorship = { version = "2.0.0", default-features = false, path = "../../../frame/authorship" } pallet-babe = { version = "2.0.0", default-features = false, path = "../../../frame/babe" } @@ -91,6 +92,7 @@ default = ["std"] with-tracing = [ "frame-executive/with-tracing" ] std = [ "sp-authority-discovery/std", + "pallet-assets/std", "pallet-authority-discovery/std", "pallet-authorship/std", "sp-consensus-babe/std", @@ -150,6 +152,7 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", "pallet-babe/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-collective/runtime-benchmarks", diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 04381d50a2afa..4deb86d9729ad 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -906,6 +906,22 @@ impl pallet_vesting::Trait for Runtime { type WeightInfo = weights::pallet_vesting::WeightInfo; } +parameter_types! { + pub const AssetDepositBase: Balance = 100 * DOLLARS; + pub const AssetDepositPerZombie: Balance = 1 * DOLLARS; +} + +impl pallet_assets::Trait for Runtime { + type Event = Event; + type Balance = u64; + type AssetId = u32; + type Currency = Balances; + type ForceOrigin = EnsureRoot; + type AssetDepositBase = AssetDepositBase; + type AssetDepositPerZombie = AssetDepositPerZombie; + type WeightInfo = weights::pallet_assets::WeightInfo; +} + construct_runtime!( pub enum Runtime where Block = Block, @@ -944,6 +960,7 @@ construct_runtime!( Scheduler: pallet_scheduler::{Module, Call, Storage, Event}, Proxy: pallet_proxy::{Module, Call, Storage, Event}, Multisig: pallet_multisig::{Module, Call, Storage, Event}, + Assets: pallet_assets::{Module, Call, Storage, Event}, } ); @@ -1219,6 +1236,7 @@ impl_runtime_apis! { let mut batches = Vec::::new(); let params = (&config, &whitelist); + add_benchmark!(params, batches, pallet_assets, Assets); add_benchmark!(params, batches, pallet_babe, Babe); add_benchmark!(params, batches, pallet_balances, Balances); add_benchmark!(params, batches, pallet_collective, Council); diff --git a/bin/node/runtime/src/weights/mod.rs b/bin/node/runtime/src/weights/mod.rs index c75ff83085b6e..60af3b8929c28 100644 --- a/bin/node/runtime/src/weights/mod.rs +++ b/bin/node/runtime/src/weights/mod.rs @@ -16,6 +16,7 @@ //! A list of the different weight modules for our runtime. pub mod frame_system; +pub mod pallet_assets; pub mod pallet_balances; pub mod pallet_collective; pub mod pallet_contracts; diff --git a/bin/node/runtime/src/weights/pallet_assets.rs b/bin/node/runtime/src/weights/pallet_assets.rs new file mode 100644 index 0000000000000..99fb6142e23cd --- /dev/null +++ b/bin/node/runtime/src/weights/pallet_assets.rs @@ -0,0 +1,36 @@ +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc5 + +use frame_support::weights::Weight; +use sp_std::marker::PhantomData; + +pub struct WeightInfo(PhantomData); +impl pallet_assets::WeightInfo for WeightInfo { + fn create() -> Weight { 0 as Weight } + fn force_create() -> Weight { 0 as Weight } + fn destroy() -> Weight { 0 as Weight } + fn force_destroy() -> Weight { 0 as Weight } + fn mint() -> Weight { 0 as Weight } + fn burn() -> Weight { 0 as Weight } + fn transfer() -> Weight { 0 as Weight } + fn force_transfer() -> Weight { 0 as Weight } + fn freeze() -> Weight { 0 as Weight } + fn thaw() -> Weight { 0 as Weight } + fn transfer_ownership() -> Weight { 0 as Weight } + fn set_team() -> Weight { 0 as Weight } + fn set_max_zombies() -> Weight { 0 as Weight } +} From 87f311e3aa6920b30f31cdc22e1e4b9db4eb0e30 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 15 Oct 2020 15:23:52 +0200 Subject: [PATCH 18/34] Fixes --- frame/assets/src/benchmarking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/assets/src/benchmarking.rs b/frame/assets/src/benchmarking.rs index 203b6660966d7..d015363b1f7c2 100644 --- a/frame/assets/src/benchmarking.rs +++ b/frame/assets/src/benchmarking.rs @@ -18,7 +18,7 @@ //! Staking pallet benchmarking. use super::*; - +use sp_std::prelude::*; use sp_runtime::traits::Bounded; use frame_system::RawOrigin as SystemOrigin; use frame_support::assert_ok; From d12fe90f84538445c652ec9027654cb954936b60 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 15 Oct 2020 15:40:41 +0200 Subject: [PATCH 19/34] Fixes --- frame/assets/src/benchmarking.rs | 39 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/frame/assets/src/benchmarking.rs b/frame/assets/src/benchmarking.rs index d015363b1f7c2..674c4bd7c7a35 100644 --- a/frame/assets/src/benchmarking.rs +++ b/frame/assets/src/benchmarking.rs @@ -21,7 +21,6 @@ use super::*; use sp_std::prelude::*; use sp_runtime::traits::Bounded; use frame_system::RawOrigin as SystemOrigin; -use frame_support::assert_ok; use frame_benchmarking::{benchmarks, account, whitelisted_caller}; use crate::Module as Assets; @@ -34,13 +33,13 @@ fn create_default_asset(max_zombies: u32) let caller: T::AccountId = whitelisted_caller(); let caller_lookup = T::Lookup::unlookup(caller.clone()); let root = SystemOrigin::Root.into(); - assert_ok!(Assets::::force_create( + assert!(Assets::::force_create( root, Default::default(), caller_lookup.clone(), max_zombies, 1.into(), - )); + ).is_ok()); (caller, caller_lookup) } @@ -48,12 +47,12 @@ fn create_default_minted_asset(max_zombies: u32, amount: T::Balance) -> (T::AccountId, ::Source) { let (caller, caller_lookup) = create_default_asset::(max_zombies); - assert_ok!(Assets::::mint( + assert!(Assets::::mint( SystemOrigin::Signed(caller.clone()).into(), Default::default(), caller_lookup.clone(), amount, - )); + ).is_ok()); (caller, caller_lookup) } @@ -132,11 +131,11 @@ benchmarks! { thaw { let (caller, caller_lookup) = create_default_minted_asset::(10, 100.into()); - assert_ok!(Assets::::freeze( + assert!(Assets::::freeze( SystemOrigin::Signed(caller.clone()).into(), Default::default(), caller_lookup.clone() - )); + ).is_ok()); }: _(SystemOrigin::Signed(caller), Default::default(), caller_lookup) verify { assert!(true) @@ -178,91 +177,91 @@ mod tests { #[test] fn create() { new_test_ext().execute_with(|| { - assert_ok!(test_benchmark_create::()); + assert!(test_benchmark_create::().is_ok()); }); } #[test] fn force_create() { new_test_ext().execute_with(|| { - assert_ok!(test_benchmark_force_create::()); + assert!(test_benchmark_force_create::().is_ok()); }); } #[test] fn destroy() { new_test_ext().execute_with(|| { - assert_ok!(test_benchmark_destroy::()); + assert!(test_benchmark_destroy::().is_ok()); }); } #[test] fn force_destroy() { new_test_ext().execute_with(|| { - assert_ok!(test_benchmark_force_destroy::()); + assert!(test_benchmark_force_destroy::().is_ok()); }); } #[test] fn mint() { new_test_ext().execute_with(|| { - assert_ok!(test_benchmark_mint::()); + assert!(test_benchmark_mint::().is_ok()); }); } #[test] fn burn() { new_test_ext().execute_with(|| { - assert_ok!(test_benchmark_burn::()); + assert!(test_benchmark_burn::().is_ok()); }); } #[test] fn transfer() { new_test_ext().execute_with(|| { - assert_ok!(test_benchmark_transfer::()); + assert!(test_benchmark_transfer::().is_ok()); }); } #[test] fn force_transfer() { new_test_ext().execute_with(|| { - assert_ok!(test_benchmark_force_transfer::()); + assert!(test_benchmark_force_transfer::().is_ok()); }); } #[test] fn freeze() { new_test_ext().execute_with(|| { - assert_ok!(test_benchmark_freeze::()); + assert!(test_benchmark_freeze::().is_ok()); }); } #[test] fn thaw() { new_test_ext().execute_with(|| { - assert_ok!(test_benchmark_thaw::()); + assert!(test_benchmark_thaw::().is_ok()); }); } #[test] fn transfer_ownership() { new_test_ext().execute_with(|| { - assert_ok!(test_benchmark_transfer_ownership::()); + assert!(test_benchmark_transfer_ownership::().is_ok()); }); } #[test] fn set_team() { new_test_ext().execute_with(|| { - assert_ok!(test_benchmark_set_team::()); + assert!(test_benchmark_set_team::().is_ok()); }); } #[test] fn set_max_zombies() { new_test_ext().execute_with(|| { - assert_ok!(test_benchmark_set_max_zombies::()); + assert!(test_benchmark_set_max_zombies::().is_ok()); }); } } From e29b3fa3c607e79cb50e3d7071cf9aa1209a3d97 Mon Sep 17 00:00:00 2001 From: Parity Benchmarking Bot Date: Thu, 15 Oct 2020 13:43:53 +0000 Subject: [PATCH 20/34] cargo run --release --features runtime-benchmarks --manifest-path bin/node/cli/Cargo.toml -- benchmark --chain dev --steps 50 --repeat 20 --extrinsic * --execution=wasm --wasm-execution=compiled --output ./bin/node/runtime/src/weights --header ./HEADER --pallet pallet_assets --- bin/node/runtime/src/weights/pallet_assets.rs | 89 +++++++++++++++---- 1 file changed, 74 insertions(+), 15 deletions(-) diff --git a/bin/node/runtime/src/weights/pallet_assets.rs b/bin/node/runtime/src/weights/pallet_assets.rs index 99fb6142e23cd..4b721bc9c5ad9 100644 --- a/bin/node/runtime/src/weights/pallet_assets.rs +++ b/bin/node/runtime/src/weights/pallet_assets.rs @@ -1,3 +1,5 @@ +// This file is part of Substrate. + // Copyright (C) 2020 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 @@ -13,24 +15,81 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc5 +//! Weights for pallet_assets +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0 +//! DATE: 2020-10-15, STEPS: [50], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] + +#![allow(unused_parens)] +#![allow(unused_imports)] -use frame_support::weights::Weight; +use frame_support::{traits::Get, weights::Weight}; use sp_std::marker::PhantomData; pub struct WeightInfo(PhantomData); impl pallet_assets::WeightInfo for WeightInfo { - fn create() -> Weight { 0 as Weight } - fn force_create() -> Weight { 0 as Weight } - fn destroy() -> Weight { 0 as Weight } - fn force_destroy() -> Weight { 0 as Weight } - fn mint() -> Weight { 0 as Weight } - fn burn() -> Weight { 0 as Weight } - fn transfer() -> Weight { 0 as Weight } - fn force_transfer() -> Weight { 0 as Weight } - fn freeze() -> Weight { 0 as Weight } - fn thaw() -> Weight { 0 as Weight } - fn transfer_ownership() -> Weight { 0 as Weight } - fn set_team() -> Weight { 0 as Weight } - fn set_max_zombies() -> Weight { 0 as Weight } + fn create() -> Weight { + (57_381_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn force_create() -> Weight { + (29_917_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn destroy() -> Weight { + (31_889_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn force_destroy() -> Weight { + (31_274_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn mint() -> Weight { + (43_885_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn burn() -> Weight { + (38_968_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn transfer() -> Weight { + (58_024_000 as Weight) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } + fn force_transfer() -> Weight { + (57_982_000 as Weight) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } + fn freeze() -> Weight { + (42_506_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn thaw() -> Weight { + (42_463_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn transfer_ownership() -> Weight { + (30_055_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn set_team() -> Weight { + (30_661_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn set_max_zombies() -> Weight { + (56_152_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } } From 85443d2bf1bd842fdf20ca7be35413dcdfde4242 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Fri, 16 Oct 2020 17:52:44 +0200 Subject: [PATCH 21/34] Update frame/assets/src/lib.rs Co-authored-by: Guillaume Thiolliere --- frame/assets/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 95d76796545b8..57dc0bb5caf9c 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -141,7 +141,7 @@ pub trait Trait: frame_system::Trait { /// The currency mechanism. type Currency: ReservableCurrency; - /// The origin which may forcibly create or destroy an asset. Root can always do this. + /// The origin which may forcibly create or destroy an asset. type ForceOrigin: EnsureOrigin; /// The basic amount of funds that must be reserved when creating a new asset class. From b53c1ba5dd954a0eb6fb8f91015fd17d2382c534 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 16 Oct 2020 18:19:31 +0200 Subject: [PATCH 22/34] Fixes --- frame/assets/src/benchmarking.rs | 21 ++++++++++--- frame/assets/src/default_weight.rs | 4 +-- frame/assets/src/lib.rs | 47 +++++++++++++++++++++--------- 3 files changed, 53 insertions(+), 19 deletions(-) diff --git a/frame/assets/src/benchmarking.rs b/frame/assets/src/benchmarking.rs index 674c4bd7c7a35..10570db96a01e 100644 --- a/frame/assets/src/benchmarking.rs +++ b/frame/assets/src/benchmarking.rs @@ -56,6 +56,15 @@ fn create_default_minted_asset(max_zombies: u32, amount: T::Balance) (caller, caller_lookup) } +fn add_zombies(minter: T::AccountId, n: u32) { + let origin = SystemOrigin::Signed(minter); + for i in 0..n { + let target = account("zombie", i, SEED); + let target_lookup = T::Lookup::unlookup(target); + assert!(Assets::::mint(origin.clone().into(), Default::default(), target_lookup, 100.into()).is_ok()); + } +} + benchmarks! { _ { } @@ -77,15 +86,19 @@ benchmarks! { } destroy { - let (caller, _) = create_default_asset::(10); - }: _(SystemOrigin::Signed(caller), Default::default()) + let z in 0 .. 10_000; + let (caller, _) = create_default_asset::(10_000); + add_zombies::(caller.clone(), z); + }: _(SystemOrigin::Signed(caller), Default::default(), 10_000) verify { assert!(true) } force_destroy { - let _ = create_default_asset::(10); - }: _(SystemOrigin::Root, Default::default()) + let z in 0 .. 10_000; + let (caller, _) = create_default_asset::(10_000); + add_zombies::(caller.clone(), z); + }: _(SystemOrigin::Root, Default::default(), 10_000) verify { assert!(true) } diff --git a/frame/assets/src/default_weight.rs b/frame/assets/src/default_weight.rs index b2954c765b1f6..3437ded53d052 100644 --- a/frame/assets/src/default_weight.rs +++ b/frame/assets/src/default_weight.rs @@ -24,8 +24,8 @@ use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight}; impl crate::WeightInfo for () { fn create() -> Weight { 0 as Weight } fn force_create() -> Weight { 0 as Weight } - fn destroy() -> Weight { 0 as Weight } - fn force_destroy() -> Weight { 0 as Weight } + fn destroy(_z: u32, ) -> Weight { 0 as Weight } + fn force_destroy(_z: u32, ) -> Weight { 0 as Weight } fn mint() -> Weight { 0 as Weight } fn burn() -> Weight { 0 as Weight } fn transfer() -> Weight { 0 as Weight } diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 95d76796545b8..85624c5ed57db 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -268,14 +268,16 @@ decl_error! { TooManyZombies, /// Attempt to destroy an asset class when non-zombie, reference-bearing accounts exist. RefsLeft, + /// Invalid witness data given. + BadWitness, } } pub trait WeightInfo { fn create() -> Weight; fn force_create() -> Weight; - fn destroy() -> Weight; - fn force_destroy() -> Weight; + fn destroy(_z: u32, ) -> Weight; + fn force_destroy(_z: u32, ) -> Weight; fn mint() -> Weight; fn burn() -> Weight; fn transfer() -> Weight; @@ -405,15 +407,19 @@ decl_module! { /// /// Emits `Destroyed` event when successful. /// - /// Weight: `O(1)` - #[weight = T::WeightInfo::destroy()] - fn destroy(origin, #[compact] id: T::AssetId) -> DispatchResult { + /// Weight: `O(z)` where `z` is the number of zombie accounts. + #[weight = T::WeightInfo::destroy(*zombies_witness)] + fn destroy(origin, + #[compact] id: T::AssetId, + #[compact] zombies_witness: u32, + ) -> DispatchResult { let origin = ensure_signed(origin)?; Asset::::try_mutate_exists(id, |maybe_details| { let details = maybe_details.take().ok_or(Error::::Unknown)?; ensure!(details.owner == origin, Error::::NoPermission); ensure!(details.accounts == details.zombies, Error::::RefsLeft); + ensure!(details.zombies <= zombies_witness, Error::::BadWitness); T::Currency::unreserve(&details.owner, details.deposit); *maybe_details = None; @@ -433,13 +439,17 @@ decl_module! { /// Emits `Destroyed` event when successful. /// /// Weight: `O(1)` - #[weight = T::WeightInfo::force_destroy()] - fn force_destroy(origin, #[compact] id: T::AssetId) -> DispatchResult { + #[weight = T::WeightInfo::force_destroy(*zombies_witness)] + fn force_destroy(origin, + #[compact] id: T::AssetId, + #[compact] zombies_witness: u32, + ) -> DispatchResult { T::ForceOrigin::ensure_origin(origin)?; Asset::::try_mutate_exists(id, |maybe_details| { let details = maybe_details.take().ok_or(Error::::Unknown)?; ensure!(details.accounts == details.zombies, Error::::RefsLeft); + ensure!(details.zombies <= zombies_witness, Error::::BadWitness); T::Currency::unreserve(&details.owner, details.deposit); *maybe_details = None; @@ -1002,13 +1012,13 @@ mod tests { assert_ok!(Assets::create(Origin::signed(1), 0, 1, 10, 1)); assert_eq!(Balances::reserved_balance(&1), 11); - assert_ok!(Assets::destroy(Origin::signed(1), 0)); + assert_ok!(Assets::destroy(Origin::signed(1), 0, 100)); assert_eq!(Balances::reserved_balance(&1), 0); assert_ok!(Assets::create(Origin::signed(1), 0, 1, 10, 1)); assert_eq!(Balances::reserved_balance(&1), 11); - assert_ok!(Assets::force_destroy(Origin::root(), 0)); + assert_ok!(Assets::force_destroy(Origin::root(), 0, 100)); assert_eq!(Balances::reserved_balance(&1), 0); }); } @@ -1019,10 +1029,21 @@ mod tests { Balances::make_free_balance_be(&1, 100); assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); - assert_noop!(Assets::destroy(Origin::signed(1), 0), Error::::RefsLeft); - assert_noop!(Assets::force_destroy(Origin::root(), 0), Error::::RefsLeft); + assert_noop!(Assets::destroy(Origin::signed(1), 0, 100), Error::::RefsLeft); + assert_noop!(Assets::force_destroy(Origin::root(), 0, 100), Error::::RefsLeft); assert_ok!(Assets::burn(Origin::signed(1), 0, 1, 100)); - assert_ok!(Assets::destroy(Origin::signed(1), 0)); + assert_ok!(Assets::destroy(Origin::signed(1), 0, 100)); + }); + } + + #[test] + fn destroy_with_bad_witness_should_not_work() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 10, 100)); + assert_noop!(Assets::destroy(Origin::signed(1), 0, 0), Error::::BadWitness); + assert_noop!(Assets::force_destroy(Origin::root(), 0, 0), Error::::BadWitness); }); } @@ -1170,7 +1191,7 @@ mod tests { assert_noop!(Assets::burn(Origin::signed(2), 0, 1, 100), Error::::NoPermission); assert_noop!(Assets::force_transfer(Origin::signed(2), 0, 1, 2, 100), Error::::NoPermission); assert_noop!(Assets::set_max_zombies(Origin::signed(2), 0, 11), Error::::NoPermission); - assert_noop!(Assets::destroy(Origin::signed(2), 0), Error::::NoPermission); + assert_noop!(Assets::destroy(Origin::signed(2), 0, 100), Error::::NoPermission); }); } From 27353a7ec82da5d22240e34402e22220f7edb031 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 16 Oct 2020 18:27:28 +0200 Subject: [PATCH 23/34] Fixes --- frame/assets/src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 2024789a91c38..2fa71bff4a992 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -321,25 +321,25 @@ decl_module! { #[weight = T::WeightInfo::create()] fn create(origin, #[compact] id: T::AssetId, - owner: ::Source, + admin: ::Source, max_zombies: u32, min_balance: T::Balance, ) { - let origin = ensure_signed(origin)?; - let owner = T::Lookup::lookup(owner)?; + let owner = ensure_signed(origin)?; + let admin = T::Lookup::lookup(admin)?; ensure!(!Asset::::contains_key(id), Error::::InUse); let deposit = T::AssetDepositPerZombie::get() .saturating_mul(max_zombies.into()) .saturating_add(T::AssetDepositBase::get()); - T::Currency::reserve(&origin, deposit)?; + T::Currency::reserve(&owner, deposit)?; Asset::::insert(id, AssetDetails { owner: owner.clone(), - issuer: owner.clone(), - admin: owner.clone(), - freezer: owner.clone(), + issuer: admin.clone(), + admin: admin.clone(), + freezer: admin.clone(), supply: Zero::zero(), deposit, max_zombies, @@ -347,7 +347,7 @@ decl_module! { zombies: Zero::zero(), accounts: Zero::zero(), }); - Self::deposit_event(RawEvent::Created(id, origin, owner)); + Self::deposit_event(RawEvent::Created(id, owner, admin)); } /// Issue a new class of fungible assets from a privileged origin. From 0b5fa744ec81c406b192e7e716f92b6895cda764 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 16 Oct 2020 18:31:43 +0200 Subject: [PATCH 24/34] Fixes --- bin/node/runtime/src/weights/pallet_assets.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/node/runtime/src/weights/pallet_assets.rs b/bin/node/runtime/src/weights/pallet_assets.rs index 4b721bc9c5ad9..9a942c35a5562 100644 --- a/bin/node/runtime/src/weights/pallet_assets.rs +++ b/bin/node/runtime/src/weights/pallet_assets.rs @@ -37,12 +37,12 @@ impl pallet_assets::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn destroy() -> Weight { + fn destroy(_z: u32, ) -> Weight { (31_889_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn force_destroy() -> Weight { + fn force_destroy(_z: u32, ) -> Weight { (31_274_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) From 1eff9959253a644567915cf0925d791f52bedb21 Mon Sep 17 00:00:00 2001 From: Parity Benchmarking Bot Date: Fri, 16 Oct 2020 16:41:27 +0000 Subject: [PATCH 25/34] cargo run --release --features runtime-benchmarks --manifest-path bin/node/cli/Cargo.toml -- benchmark --chain dev --steps 50 --repeat 20 --extrinsic * --execution=wasm --wasm-execution=compiled --output ./bin/node/runtime/src/weights --header ./HEADER --pallet pallet_assets --- bin/node/runtime/src/weights/pallet_assets.rs | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/bin/node/runtime/src/weights/pallet_assets.rs b/bin/node/runtime/src/weights/pallet_assets.rs index 9a942c35a5562..e301f909ae281 100644 --- a/bin/node/runtime/src/weights/pallet_assets.rs +++ b/bin/node/runtime/src/weights/pallet_assets.rs @@ -17,7 +17,7 @@ //! Weights for pallet_assets //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0 -//! DATE: 2020-10-15, STEPS: [50], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] +//! DATE: 2020-10-16, STEPS: [50], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] #![allow(unused_parens)] #![allow(unused_imports)] @@ -28,67 +28,71 @@ use sp_std::marker::PhantomData; pub struct WeightInfo(PhantomData); impl pallet_assets::WeightInfo for WeightInfo { fn create() -> Weight { - (57_381_000 as Weight) + (58_069_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn force_create() -> Weight { - (29_917_000 as Weight) + (30_401_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn destroy(_z: u32, ) -> Weight { - (31_889_000 as Weight) + fn destroy(z: u32, ) -> Weight { + (0 as Weight) + .saturating_add((1_151_000 as Weight).saturating_mul(z as Weight)) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(z as Weight))) } - fn force_destroy(_z: u32, ) -> Weight { - (31_274_000 as Weight) + fn force_destroy(z: u32, ) -> Weight { + (0 as Weight) + .saturating_add((1_151_000 as Weight).saturating_mul(z as Weight)) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(z as Weight))) } fn mint() -> Weight { - (43_885_000 as Weight) + (44_772_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn burn() -> Weight { - (38_968_000 as Weight) + (39_564_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn transfer() -> Weight { - (58_024_000 as Weight) + (58_453_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn force_transfer() -> Weight { - (57_982_000 as Weight) + (58_704_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn freeze() -> Weight { - (42_506_000 as Weight) + (43_141_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn thaw() -> Weight { - (42_463_000 as Weight) + (42_965_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn transfer_ownership() -> Weight { - (30_055_000 as Weight) + (30_110_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn set_team() -> Weight { - (30_661_000 as Weight) + (30_704_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn set_max_zombies() -> Weight { - (56_152_000 as Weight) + (56_445_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } From 1fe69b17ef2f8041b4ba7674ef37f3eb93a41286 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 16 Oct 2020 19:29:20 +0200 Subject: [PATCH 26/34] Fixes --- frame/assets/src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 2fa71bff4a992..76e2fa537ab52 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -470,7 +470,7 @@ decl_module! { /// Emits `Destroyed` event when successful. /// /// Weight: `O(1)` - /// Modes: Pre-existence of `beneficiary` + /// Modes: Pre-existing balance of `beneficiary`; Account pre-existence of `beneficiary`. #[weight = T::WeightInfo::mint()] fn mint(origin, #[compact] id: T::AssetId, @@ -514,7 +514,7 @@ decl_module! { /// minimum for the asset, then the amount burned is increased to take it to zero. /// /// Weight: `O(1)` - /// Modes: Post-existence of `who` + /// Modes: Post-existence of `who`; Pre & post Zombie-status of `who`. #[weight = T::WeightInfo::burn()] fn burn(origin, #[compact] id: T::AssetId, @@ -570,7 +570,7 @@ decl_module! { /// /// Weight: `O(1)` /// Modes: Pre-existence of `target`; Post-existence of sender; Prior & post zombie-status - /// of sender. + /// of sender; Account pre-existence of `target`. #[weight = T::WeightInfo::transfer()] fn transfer(origin, #[compact] id: T::AssetId, @@ -642,8 +642,8 @@ decl_module! { /// to zero. /// /// Weight: `O(1)` - /// Modes: Pre-existence of `dest`; Post-existence of `source`. Prior & post zombie-status - /// of `source`. + /// Modes: Pre-existence of `dest`; Post-existence of `source`; Prior & post zombie-status + /// of `source`; Account pre-existence of `dest`. #[weight = T::WeightInfo::force_transfer()] fn force_transfer(origin, #[compact] id: T::AssetId, From a452393212f98586f743d1a5c5c50df077b9191c Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Sat, 17 Oct 2020 16:45:13 +0200 Subject: [PATCH 27/34] Update default weight --- frame/assets/src/default_weight.rs | 89 +++++++++++++++++++++++++----- frame/assets/src/lib.rs | 4 +- 2 files changed, 76 insertions(+), 17 deletions(-) diff --git a/frame/assets/src/default_weight.rs b/frame/assets/src/default_weight.rs index 3437ded53d052..7ec7a30b7b3ba 100644 --- a/frame/assets/src/default_weight.rs +++ b/frame/assets/src/default_weight.rs @@ -1,3 +1,5 @@ +// This file is part of Substrate. + // Copyright (C) 2020 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 @@ -13,8 +15,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Default weights for the Collective Pallet -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6 +//! Weights for pallet_assets +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0 +//! DATE: 2020-10-16, STEPS: [50], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] #![allow(unused_parens)] #![allow(unused_imports)] @@ -22,17 +25,73 @@ use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight}; impl crate::WeightInfo for () { - fn create() -> Weight { 0 as Weight } - fn force_create() -> Weight { 0 as Weight } - fn destroy(_z: u32, ) -> Weight { 0 as Weight } - fn force_destroy(_z: u32, ) -> Weight { 0 as Weight } - fn mint() -> Weight { 0 as Weight } - fn burn() -> Weight { 0 as Weight } - fn transfer() -> Weight { 0 as Weight } - fn force_transfer() -> Weight { 0 as Weight } - fn freeze() -> Weight { 0 as Weight } - fn thaw() -> Weight { 0 as Weight } - fn transfer_ownership() -> Weight { 0 as Weight } - fn set_team() -> Weight { 0 as Weight } - fn set_max_zombies() -> Weight { 0 as Weight } + fn create() -> Weight { + (58_069_000 as Weight) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn force_create() -> Weight { + (30_401_000 as Weight) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn destroy(z: u32, ) -> Weight { + (0 as Weight) + .saturating_add((1_151_000 as Weight).saturating_mul(z as Weight)) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(z as Weight))) + } + fn force_destroy(z: u32, ) -> Weight { + (0 as Weight) + .saturating_add((1_151_000 as Weight).saturating_mul(z as Weight)) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(z as Weight))) + } + fn mint() -> Weight { + (44_772_000 as Weight) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn burn() -> Weight { + (39_564_000 as Weight) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn transfer() -> Weight { + (58_453_000 as Weight) + .saturating_add(DbWeight::get().reads(4 as Weight)) + .saturating_add(DbWeight::get().writes(3 as Weight)) + } + fn force_transfer() -> Weight { + (58_704_000 as Weight) + .saturating_add(DbWeight::get().reads(4 as Weight)) + .saturating_add(DbWeight::get().writes(3 as Weight)) + } + fn freeze() -> Weight { + (43_141_000 as Weight) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn thaw() -> Weight { + (42_965_000 as Weight) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn transfer_ownership() -> Weight { + (30_110_000 as Weight) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn set_team() -> Weight { + (30_704_000 as Weight) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn set_max_zombies() -> Weight { + (56_445_000 as Weight) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } } diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 2fa71bff4a992..5018b0b707814 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -276,8 +276,8 @@ decl_error! { pub trait WeightInfo { fn create() -> Weight; fn force_create() -> Weight; - fn destroy(_z: u32, ) -> Weight; - fn force_destroy(_z: u32, ) -> Weight; + fn destroy(z: u32, ) -> Weight; + fn force_destroy(z: u32, ) -> Weight; fn mint() -> Weight; fn burn() -> Weight; fn transfer() -> Weight; From 373f4d7fe671cb71eb1c126a21444691e6c75d9b Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Sun, 18 Oct 2020 09:47:24 +0200 Subject: [PATCH 28/34] Add proper verification to benchmarks --- frame/assets/src/benchmarking.rs | 82 +++++++++++++++++++------------- frame/assets/src/lib.rs | 23 +++++++-- 2 files changed, 69 insertions(+), 36 deletions(-) diff --git a/frame/assets/src/benchmarking.rs b/frame/assets/src/benchmarking.rs index 10570db96a01e..28a7292bf6da8 100644 --- a/frame/assets/src/benchmarking.rs +++ b/frame/assets/src/benchmarking.rs @@ -65,6 +65,14 @@ fn add_zombies(minter: T::AccountId, n: u32) { } } +fn assert_last_event(generic_event: ::Event) { + let events = frame_system::Module::::events(); + let system_event: ::Event = generic_event.into(); + // compare to the last event record + let frame_system::EventRecord { event, .. } = &events[events.len() - 1]; + assert_eq!(event, &system_event); +} + benchmarks! { _ { } @@ -72,17 +80,17 @@ benchmarks! { let caller: T::AccountId = whitelisted_caller(); let caller_lookup = T::Lookup::unlookup(caller.clone()); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); - }: _(SystemOrigin::Signed(caller), Default::default(), caller_lookup, 1, 1.into()) + }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup, 1, 1.into()) verify { - assert!(true) + assert_last_event::(RawEvent::Created(Default::default(), caller.clone(), caller).into()); } force_create { let caller: T::AccountId = whitelisted_caller(); - let caller_lookup = T::Lookup::unlookup(caller); + let caller_lookup = T::Lookup::unlookup(caller.clone()); }: _(SystemOrigin::Root, Default::default(), caller_lookup, 1, 1.into()) verify { - assert!(true) + assert_last_event::(RawEvent::ForceCreated(Default::default(), caller).into()); } destroy { @@ -91,7 +99,7 @@ benchmarks! { add_zombies::(caller.clone(), z); }: _(SystemOrigin::Signed(caller), Default::default(), 10_000) verify { - assert!(true) + assert_last_event::(RawEvent::Destroyed(Default::default()).into()); } force_destroy { @@ -100,46 +108,50 @@ benchmarks! { add_zombies::(caller.clone(), z); }: _(SystemOrigin::Root, Default::default(), 10_000) verify { - assert!(true) + assert_last_event::(RawEvent::Destroyed(Default::default()).into()); } mint { let (caller, caller_lookup) = create_default_asset::(10); - }: _(SystemOrigin::Signed(caller), Default::default(), caller_lookup, 100.into()) + let amount = T::Balance::from(100); + }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup, amount) verify { - assert!(true) + assert_last_event::(RawEvent::Issued(Default::default(), caller, amount).into()); } burn { - let (caller, caller_lookup) = create_default_minted_asset::(10, 100.into()); - }: _(SystemOrigin::Signed(caller), Default::default(), caller_lookup, 100.into()) + let amount = T::Balance::from(100); + let (caller, caller_lookup) = create_default_minted_asset::(10, amount); + }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup, amount) verify { - assert!(true) + assert_last_event::(RawEvent::Burned(Default::default(), caller, amount).into()); } transfer { - let (caller, caller_lookup) = create_default_minted_asset::(10, 100.into()); - let target = account("target", 0, SEED); - let target_lookup = T::Lookup::unlookup(target); - }: _(SystemOrigin::Signed(caller), Default::default(), target_lookup, 100.into()) + let amount = T::Balance::from(100); + let (caller, caller_lookup) = create_default_minted_asset::(10, amount); + let target: T::AccountId = account("target", 0, SEED); + let target_lookup = T::Lookup::unlookup(target.clone()); + }: _(SystemOrigin::Signed(caller.clone()), Default::default(), target_lookup, amount) verify { - assert!(true) + assert_last_event::(RawEvent::Transferred(Default::default(), caller, target, amount).into()); } force_transfer { - let (caller, caller_lookup) = create_default_minted_asset::(10, 100.into()); - let target = account("target", 0, SEED); - let target_lookup = T::Lookup::unlookup(target); - }: _(SystemOrigin::Signed(caller), Default::default(), caller_lookup, target_lookup, 100.into()) + let amount = T::Balance::from(100); + let (caller, caller_lookup) = create_default_minted_asset::(10, amount); + let target: T::AccountId = account("target", 0, SEED); + let target_lookup = T::Lookup::unlookup(target.clone()); + }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup, target_lookup, amount) verify { - assert!(true) + assert_last_event::(RawEvent::ForceTransferred(Default::default(), caller, target, amount).into()); } freeze { let (caller, caller_lookup) = create_default_minted_asset::(10, 100.into()); - }: _(SystemOrigin::Signed(caller), Default::default(), caller_lookup) + }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup) verify { - assert!(true) + assert_last_event::(RawEvent::Frozen(Default::default(), caller).into()); } thaw { @@ -149,18 +161,18 @@ benchmarks! { Default::default(), caller_lookup.clone() ).is_ok()); - }: _(SystemOrigin::Signed(caller), Default::default(), caller_lookup) + }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup) verify { - assert!(true) + assert_last_event::(RawEvent::Thawed(Default::default(), caller).into()); } transfer_ownership { let (caller, _) = create_default_asset::(10); - let target = account("target", 0, SEED); - let target_lookup = T::Lookup::unlookup(target); + let target: T::AccountId = account("target", 0, SEED); + let target_lookup = T::Lookup::unlookup(target.clone()); }: _(SystemOrigin::Signed(caller), Default::default(), target_lookup) verify { - assert!(true) + assert_last_event::(RawEvent::OwnerChanged(Default::default(), target).into()); } set_team { @@ -168,17 +180,23 @@ benchmarks! { let target0 = T::Lookup::unlookup(account("target", 0, SEED)); let target1 = T::Lookup::unlookup(account("target", 1, SEED)); let target2 = T::Lookup::unlookup(account("target", 2, SEED)); - }: _(SystemOrigin::Signed(caller), Default::default(), target0, target1, target2) + }: _(SystemOrigin::Signed(caller), Default::default(), target0.clone(), target1.clone(), target2.clone()) verify { - assert!(true) + assert_last_event::(RawEvent::TeamChanged( + Default::default(), + account("target", 0, SEED), + account("target", 1, SEED), + account("target", 2, SEED), + ).into()); } set_max_zombies { let (caller, _) = create_default_asset::(10); + let max_zombies: u32 = 100; T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); - }: _(SystemOrigin::Signed(caller), Default::default(), 100) + }: _(SystemOrigin::Signed(caller), Default::default(), max_zombies) verify { - assert!(true) + assert_last_event::(RawEvent::MaxZombiesChanged(Default::default(), max_zombies).into()); } } diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 5018b0b707814..3da79c5e00144 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -913,10 +913,25 @@ impl Module { mod tests { use super::*; - use frame_support::{impl_outer_origin, assert_ok, assert_noop, parameter_types, weights::Weight}; + use frame_support::{ + impl_outer_origin, impl_outer_event, assert_ok, assert_noop, parameter_types, + weights::Weight + }; use sp_core::H256; use sp_runtime::{Perbill, traits::{BlakeTwo256, IdentityLookup}, testing::Header}; + mod pallet_assets { + pub use crate::Event; + } + + impl_outer_event! { + pub enum Event for Test { + frame_system, + pallet_balances, + pallet_assets, + } + } + impl_outer_origin! { pub enum Origin for Test where system = frame_system {} } @@ -940,7 +955,7 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type Event = (); + type Event = Event; type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; type DbWeight = (); @@ -965,7 +980,7 @@ mod tests { type MaxLocks = (); type Balance = u64; type DustRemoval = (); - type Event = (); + type Event = Event; type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type WeightInfo = (); @@ -978,7 +993,7 @@ mod tests { impl Trait for Test { type Currency = Balances; - type Event = (); + type Event = Event; type Balance = u64; type AssetId = u32; type ForceOrigin = frame_system::EnsureRoot; From d8b840addb54d6964da0dfbb412ba58c4c520ed0 Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Sun, 18 Oct 2020 10:16:12 +0200 Subject: [PATCH 29/34] minor improvements to tests --- frame/assets/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 3da79c5e00144..6bd7284da8afd 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -1129,6 +1129,7 @@ mod tests { new_test_ext().execute_with(|| { assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 10)); assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); + assert_eq!(Asset::::get(0).unwrap().accounts, 1); // Cannot create a new account with a balance that is below minimum... assert_noop!(Assets::mint(Origin::signed(1), 0, 2, 9), Error::::BalanceLow); @@ -1140,13 +1141,16 @@ mod tests { assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 91)); assert!(Assets::balance(0, 1).is_zero()); assert_eq!(Assets::balance(0, 2), 100); + assert_eq!(Asset::::get(0).unwrap().accounts, 1); assert_ok!(Assets::force_transfer(Origin::signed(1), 0, 2, 1, 91)); assert!(Assets::balance(0, 2).is_zero()); assert_eq!(Assets::balance(0, 1), 100); + assert_eq!(Asset::::get(0).unwrap().accounts, 1); assert_ok!(Assets::burn(Origin::signed(1), 0, 1, 91)); assert!(Assets::balance(0, 1).is_zero()); + assert_eq!(Asset::::get(0).unwrap().accounts, 0); }); } @@ -1271,6 +1275,7 @@ mod tests { assert_ok!(Assets::burn(Origin::signed(1), 0, 1, u64::max_value())); assert_eq!(Assets::balance(0, 1), 0); assert_noop!(Assets::transfer(Origin::signed(1), 0, 1, 50), Error::::BalanceLow); + assert_noop!(Assets::transfer(Origin::signed(2), 0, 1, 51), Error::::BalanceLow); }); } @@ -1301,6 +1306,7 @@ mod tests { assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100)); assert_eq!(Assets::balance(0, 1), 100); assert_ok!(Assets::burn(Origin::signed(1), 0, 1, u64::max_value())); + assert_eq!(Assets::balance(0, 1), 0); }); } From dc0839dc3698910ca7162ec6cfb9ed4b4539da66 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Mon, 9 Nov 2020 17:59:34 +0100 Subject: [PATCH 30/34] Update frame/assets/src/benchmarking.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- frame/assets/src/benchmarking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/assets/src/benchmarking.rs b/frame/assets/src/benchmarking.rs index 28a7292bf6da8..1c33476e07381 100644 --- a/frame/assets/src/benchmarking.rs +++ b/frame/assets/src/benchmarking.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Staking pallet benchmarking. +//! Assets pallet benchmarking. use super::*; use sp_std::prelude::*; From d0f1829b2f7f83abda01765a95947eb5d670d567 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 9 Nov 2020 19:07:00 +0100 Subject: [PATCH 31/34] Fix --- frame/assets/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index e3b06006319be..89e09dd72e04a 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -187,7 +187,7 @@ pub struct AssetDetails< } #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default)] -pub struct AccountData< +pub struct AssetBalance< Balance: Encode + Decode + Clone + Debug + Eq + PartialEq, > { /// The balance. @@ -211,7 +211,7 @@ decl_storage! { Account: double_map hasher(blake2_128_concat) T::AssetId, hasher(blake2_128_concat) T::AccountId - => AccountData; + => AssetBalance; } } From 927bcc6d44f86c4758750661cdb75b49d974d8fc Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 2 Dec 2020 19:29:04 +0100 Subject: [PATCH 32/34] New weights system --- bin/node/runtime/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 5a0429fa7e324..285f37889ae9d 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -897,7 +897,7 @@ parameter_types! { pub const AssetDepositPerZombie: Balance = 1 * DOLLARS; } -impl pallet_assets::Trait for Runtime { +impl pallet_assets::Config for Runtime { type Event = Event; type Balance = u64; type AssetId = u32; @@ -905,7 +905,7 @@ impl pallet_assets::Trait for Runtime { type ForceOrigin = EnsureRoot; type AssetDepositBase = AssetDepositBase; type AssetDepositPerZombie = AssetDepositPerZombie; - type WeightInfo = weights::pallet_assets::WeightInfo; + type WeightInfo = pallet_assets::weights::SubstrateWeight; } construct_runtime!( From a1c43f97d91238c0eaafd4a568a25f5a54d41b88 Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Wed, 2 Dec 2020 21:31:49 -0800 Subject: [PATCH 33/34] fix compile --- bin/node/runtime/src/weights/mod.rs | 0 bin/node/runtime/src/weights/pallet_assets.rs | 99 ---------- frame/assets/src/benchmarking.rs | 30 +-- frame/assets/src/default_weight.rs | 97 --------- frame/assets/src/lib.rs | 28 +-- frame/assets/src/weights.rs | 187 ++++++++++++++++++ 6 files changed, 208 insertions(+), 233 deletions(-) delete mode 100644 bin/node/runtime/src/weights/mod.rs delete mode 100644 bin/node/runtime/src/weights/pallet_assets.rs delete mode 100644 frame/assets/src/default_weight.rs create mode 100644 frame/assets/src/weights.rs diff --git a/bin/node/runtime/src/weights/mod.rs b/bin/node/runtime/src/weights/mod.rs deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/bin/node/runtime/src/weights/pallet_assets.rs b/bin/node/runtime/src/weights/pallet_assets.rs deleted file mode 100644 index e301f909ae281..0000000000000 --- a/bin/node/runtime/src/weights/pallet_assets.rs +++ /dev/null @@ -1,99 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2020 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Weights for pallet_assets -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0 -//! DATE: 2020-10-16, STEPS: [50], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] - -#![allow(unused_parens)] -#![allow(unused_imports)] - -use frame_support::{traits::Get, weights::Weight}; -use sp_std::marker::PhantomData; - -pub struct WeightInfo(PhantomData); -impl pallet_assets::WeightInfo for WeightInfo { - fn create() -> Weight { - (58_069_000 as Weight) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) - } - fn force_create() -> Weight { - (30_401_000 as Weight) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) - } - fn destroy(z: u32, ) -> Weight { - (0 as Weight) - .saturating_add((1_151_000 as Weight).saturating_mul(z as Weight)) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) - .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(z as Weight))) - } - fn force_destroy(z: u32, ) -> Weight { - (0 as Weight) - .saturating_add((1_151_000 as Weight).saturating_mul(z as Weight)) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) - .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(z as Weight))) - } - fn mint() -> Weight { - (44_772_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(2 as Weight)) - } - fn burn() -> Weight { - (39_564_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(2 as Weight)) - } - fn transfer() -> Weight { - (58_453_000 as Weight) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) - } - fn force_transfer() -> Weight { - (58_704_000 as Weight) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) - } - fn freeze() -> Weight { - (43_141_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) - } - fn thaw() -> Weight { - (42_965_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) - } - fn transfer_ownership() -> Weight { - (30_110_000 as Weight) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) - } - fn set_team() -> Weight { - (30_704_000 as Weight) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) - } - fn set_max_zombies() -> Weight { - (56_445_000 as Weight) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) - } -} diff --git a/frame/assets/src/benchmarking.rs b/frame/assets/src/benchmarking.rs index 1c33476e07381..cecb2ccae58b4 100644 --- a/frame/assets/src/benchmarking.rs +++ b/frame/assets/src/benchmarking.rs @@ -27,7 +27,7 @@ use crate::Module as Assets; const SEED: u32 = 0; -fn create_default_asset(max_zombies: u32) +fn create_default_asset(max_zombies: u32) -> (T::AccountId, ::Source) { let caller: T::AccountId = whitelisted_caller(); @@ -38,12 +38,12 @@ fn create_default_asset(max_zombies: u32) Default::default(), caller_lookup.clone(), max_zombies, - 1.into(), + 1u32.into(), ).is_ok()); (caller, caller_lookup) } -fn create_default_minted_asset(max_zombies: u32, amount: T::Balance) +fn create_default_minted_asset(max_zombies: u32, amount: T::Balance) -> (T::AccountId, ::Source) { let (caller, caller_lookup) = create_default_asset::(max_zombies); @@ -56,18 +56,18 @@ fn create_default_minted_asset(max_zombies: u32, amount: T::Balance) (caller, caller_lookup) } -fn add_zombies(minter: T::AccountId, n: u32) { +fn add_zombies(minter: T::AccountId, n: u32) { let origin = SystemOrigin::Signed(minter); for i in 0..n { let target = account("zombie", i, SEED); let target_lookup = T::Lookup::unlookup(target); - assert!(Assets::::mint(origin.clone().into(), Default::default(), target_lookup, 100.into()).is_ok()); + assert!(Assets::::mint(origin.clone().into(), Default::default(), target_lookup, 100u32.into()).is_ok()); } } -fn assert_last_event(generic_event: ::Event) { +fn assert_last_event(generic_event: ::Event) { let events = frame_system::Module::::events(); - let system_event: ::Event = generic_event.into(); + let system_event: ::Event = generic_event.into(); // compare to the last event record let frame_system::EventRecord { event, .. } = &events[events.len() - 1]; assert_eq!(event, &system_event); @@ -80,7 +80,7 @@ benchmarks! { let caller: T::AccountId = whitelisted_caller(); let caller_lookup = T::Lookup::unlookup(caller.clone()); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); - }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup, 1, 1.into()) + }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup, 1, 1u32.into()) verify { assert_last_event::(RawEvent::Created(Default::default(), caller.clone(), caller).into()); } @@ -88,7 +88,7 @@ benchmarks! { force_create { let caller: T::AccountId = whitelisted_caller(); let caller_lookup = T::Lookup::unlookup(caller.clone()); - }: _(SystemOrigin::Root, Default::default(), caller_lookup, 1, 1.into()) + }: _(SystemOrigin::Root, Default::default(), caller_lookup, 1, 1u32.into()) verify { assert_last_event::(RawEvent::ForceCreated(Default::default(), caller).into()); } @@ -113,14 +113,14 @@ benchmarks! { mint { let (caller, caller_lookup) = create_default_asset::(10); - let amount = T::Balance::from(100); + let amount = T::Balance::from(100u32); }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup, amount) verify { assert_last_event::(RawEvent::Issued(Default::default(), caller, amount).into()); } burn { - let amount = T::Balance::from(100); + let amount = T::Balance::from(100u32); let (caller, caller_lookup) = create_default_minted_asset::(10, amount); }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup, amount) verify { @@ -128,7 +128,7 @@ benchmarks! { } transfer { - let amount = T::Balance::from(100); + let amount = T::Balance::from(100u32); let (caller, caller_lookup) = create_default_minted_asset::(10, amount); let target: T::AccountId = account("target", 0, SEED); let target_lookup = T::Lookup::unlookup(target.clone()); @@ -138,7 +138,7 @@ benchmarks! { } force_transfer { - let amount = T::Balance::from(100); + let amount = T::Balance::from(100u32); let (caller, caller_lookup) = create_default_minted_asset::(10, amount); let target: T::AccountId = account("target", 0, SEED); let target_lookup = T::Lookup::unlookup(target.clone()); @@ -148,14 +148,14 @@ benchmarks! { } freeze { - let (caller, caller_lookup) = create_default_minted_asset::(10, 100.into()); + let (caller, caller_lookup) = create_default_minted_asset::(10, 100u32.into()); }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup) verify { assert_last_event::(RawEvent::Frozen(Default::default(), caller).into()); } thaw { - let (caller, caller_lookup) = create_default_minted_asset::(10, 100.into()); + let (caller, caller_lookup) = create_default_minted_asset::(10, 100u32.into()); assert!(Assets::::freeze( SystemOrigin::Signed(caller.clone()).into(), Default::default(), diff --git a/frame/assets/src/default_weight.rs b/frame/assets/src/default_weight.rs deleted file mode 100644 index 7ec7a30b7b3ba..0000000000000 --- a/frame/assets/src/default_weight.rs +++ /dev/null @@ -1,97 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2020 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Weights for pallet_assets -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0 -//! DATE: 2020-10-16, STEPS: [50], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] - -#![allow(unused_parens)] -#![allow(unused_imports)] - -use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight}; - -impl crate::WeightInfo for () { - fn create() -> Weight { - (58_069_000 as Weight) - .saturating_add(DbWeight::get().reads(1 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) - } - fn force_create() -> Weight { - (30_401_000 as Weight) - .saturating_add(DbWeight::get().reads(1 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) - } - fn destroy(z: u32, ) -> Weight { - (0 as Weight) - .saturating_add((1_151_000 as Weight).saturating_mul(z as Weight)) - .saturating_add(DbWeight::get().reads(1 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) - .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(z as Weight))) - } - fn force_destroy(z: u32, ) -> Weight { - (0 as Weight) - .saturating_add((1_151_000 as Weight).saturating_mul(z as Weight)) - .saturating_add(DbWeight::get().reads(1 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) - .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(z as Weight))) - } - fn mint() -> Weight { - (44_772_000 as Weight) - .saturating_add(DbWeight::get().reads(2 as Weight)) - .saturating_add(DbWeight::get().writes(2 as Weight)) - } - fn burn() -> Weight { - (39_564_000 as Weight) - .saturating_add(DbWeight::get().reads(2 as Weight)) - .saturating_add(DbWeight::get().writes(2 as Weight)) - } - fn transfer() -> Weight { - (58_453_000 as Weight) - .saturating_add(DbWeight::get().reads(4 as Weight)) - .saturating_add(DbWeight::get().writes(3 as Weight)) - } - fn force_transfer() -> Weight { - (58_704_000 as Weight) - .saturating_add(DbWeight::get().reads(4 as Weight)) - .saturating_add(DbWeight::get().writes(3 as Weight)) - } - fn freeze() -> Weight { - (43_141_000 as Weight) - .saturating_add(DbWeight::get().reads(2 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) - } - fn thaw() -> Weight { - (42_965_000 as Weight) - .saturating_add(DbWeight::get().reads(2 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) - } - fn transfer_ownership() -> Weight { - (30_110_000 as Weight) - .saturating_add(DbWeight::get().reads(1 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) - } - fn set_team() -> Weight { - (30_704_000 as Weight) - .saturating_add(DbWeight::get().reads(1 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) - } - fn set_max_zombies() -> Weight { - (56_445_000 as Weight) - .saturating_add(DbWeight::get().reads(1 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) - } -} diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 47d2ab6bd269f..630f4fcc317d9 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -109,6 +109,10 @@ // Ensure we're `no_std` when compiling for Wasm. #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +pub mod weights; + use sp_std::{fmt::Debug}; use sp_runtime::{RuntimeDebug, traits::{ Member, AtLeast32BitUnsigned, Zero, StaticLookup, Saturating, CheckedSub, CheckedAdd @@ -116,14 +120,10 @@ use sp_runtime::{RuntimeDebug, traits::{ use codec::{Encode, Decode, HasCompact}; use frame_support::{Parameter, decl_module, decl_event, decl_storage, decl_error, ensure, traits::{Currency, ReservableCurrency, EnsureOrigin, Get, BalanceStatus::Reserved}, - dispatch::{DispatchResult, DispatchError}, weights::Weight + dispatch::{DispatchResult, DispatchError}, }; use frame_system::ensure_signed; - -#[cfg(feature = "runtime-benchmarks")] -mod benchmarking; - -mod default_weight; +pub use weights::WeightInfo; type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; @@ -277,22 +277,6 @@ decl_error! { } } -pub trait WeightInfo { - fn create() -> Weight; - fn force_create() -> Weight; - fn destroy(z: u32, ) -> Weight; - fn force_destroy(z: u32, ) -> Weight; - fn mint() -> Weight; - fn burn() -> Weight; - fn transfer() -> Weight; - fn force_transfer() -> Weight; - fn freeze() -> Weight; - fn thaw() -> Weight; - fn transfer_ownership() -> Weight; - fn set_team() -> Weight; - fn set_max_zombies() -> Weight; -} - decl_module! { pub struct Module for enum Call where origin: T::Origin { type Error = Error; diff --git a/frame/assets/src/weights.rs b/frame/assets/src/weights.rs new file mode 100644 index 0000000000000..75e10458c7131 --- /dev/null +++ b/frame/assets/src/weights.rs @@ -0,0 +1,187 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Weights for pallet_assets +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0 +//! DATE: 2020-10-16, STEPS: [50], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +pub trait WeightInfo { + fn create() -> Weight; + fn force_create() -> Weight; + fn destroy(z: u32, ) -> Weight; + fn force_destroy(z: u32, ) -> Weight; + fn mint() -> Weight; + fn burn() -> Weight; + fn transfer() -> Weight; + fn force_transfer() -> Weight; + fn freeze() -> Weight; + fn thaw() -> Weight; + fn transfer_ownership() -> Weight; + fn set_team() -> Weight; + fn set_max_zombies() -> Weight; +} + +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + fn create() -> Weight { + (58_069_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn force_create() -> Weight { + (30_401_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn destroy(z: u32, ) -> Weight { + (0 as Weight) + .saturating_add((1_151_000 as Weight).saturating_mul(z as Weight)) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(z as Weight))) + } + fn force_destroy(z: u32, ) -> Weight { + (0 as Weight) + .saturating_add((1_151_000 as Weight).saturating_mul(z as Weight)) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(z as Weight))) + } + fn mint() -> Weight { + (44_772_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) + } + fn burn() -> Weight { + (39_564_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) + } + fn transfer() -> Weight { + (58_453_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + } + fn force_transfer() -> Weight { + (58_704_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + } + fn freeze() -> Weight { + (43_141_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn thaw() -> Weight { + (42_965_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn transfer_ownership() -> Weight { + (30_110_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn set_team() -> Weight { + (30_704_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn set_max_zombies() -> Weight { + (56_445_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } +} + +impl crate::WeightInfo for () { + fn create() -> Weight { + (58_069_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn force_create() -> Weight { + (30_401_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn destroy(z: u32, ) -> Weight { + (0 as Weight) + .saturating_add((1_151_000 as Weight).saturating_mul(z as Weight)) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(z as Weight))) + } + fn force_destroy(z: u32, ) -> Weight { + (0 as Weight) + .saturating_add((1_151_000 as Weight).saturating_mul(z as Weight)) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(z as Weight))) + } + fn mint() -> Weight { + (44_772_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) + } + fn burn() -> Weight { + (39_564_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) + } + fn transfer() -> Weight { + (58_453_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + } + fn force_transfer() -> Weight { + (58_704_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + } + fn freeze() -> Weight { + (43_141_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn thaw() -> Weight { + (42_965_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn transfer_ownership() -> Weight { + (30_110_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn set_team() -> Weight { + (30_704_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn set_max_zombies() -> Weight { + (56_445_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } +} From 82bbd66c46f3cdaa4b788accd4ff69e5865c044b Mon Sep 17 00:00:00 2001 From: Parity Benchmarking Bot Date: Thu, 3 Dec 2020 05:43:42 +0000 Subject: [PATCH 34/34] cargo run --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_assets --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/assets/src/weights.rs --template=./.maintain/frame-weight-template.hbs --- frame/assets/src/weights.rs | 136 +++++++++++++++++++++--------------- 1 file changed, 78 insertions(+), 58 deletions(-) diff --git a/frame/assets/src/weights.rs b/frame/assets/src/weights.rs index 75e10458c7131..f6408e527f51b 100644 --- a/frame/assets/src/weights.rs +++ b/frame/assets/src/weights.rs @@ -7,7 +7,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,9 +15,26 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Weights for pallet_assets +//! Autogenerated weights for pallet_assets +//! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0 -//! DATE: 2020-10-16, STEPS: [50], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] +//! DATE: 2020-12-03, STEPS: [50, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 + +// Executed Command: +// target/release/substrate +// benchmark +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_assets +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./frame/assets/src/weights.rs +// --template=./.maintain/frame-weight-template.hbs + #![allow(unused_parens)] #![allow(unused_imports)] @@ -25,6 +42,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use sp_std::marker::PhantomData; +/// Weight functions needed for pallet_assets. pub trait WeightInfo { fn create() -> Weight; fn force_create() -> Weight; @@ -41,146 +59,148 @@ pub trait WeightInfo { fn set_max_zombies() -> Weight; } +/// Weights for pallet_assets using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { fn create() -> Weight { - (58_069_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (58_077_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn force_create() -> Weight { - (30_401_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (30_497_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn destroy(z: u32, ) -> Weight { (0 as Weight) - .saturating_add((1_151_000 as Weight).saturating_mul(z as Weight)) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(z as Weight))) + .saturating_add((1_153_000 as Weight).saturating_mul(z as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(z as Weight))) } fn force_destroy(z: u32, ) -> Weight { (0 as Weight) - .saturating_add((1_151_000 as Weight).saturating_mul(z as Weight)) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(z as Weight))) + .saturating_add((1_153_000 as Weight).saturating_mul(z as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(z as Weight))) } fn mint() -> Weight { - (44_772_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(2 as Weight)) + (45_600_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn burn() -> Weight { - (39_564_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(2 as Weight)) + (40_143_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn transfer() -> Weight { - (58_453_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(4 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + (58_903_000 as Weight) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn force_transfer() -> Weight { - (58_704_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(4 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + (59_025_000 as Weight) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn freeze() -> Weight { - (43_141_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (43_308_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn thaw() -> Weight { - (42_965_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (43_383_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn transfer_ownership() -> Weight { - (30_110_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (31_380_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn set_team() -> Weight { - (30_704_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (32_049_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn set_max_zombies() -> Weight { - (56_445_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (57_745_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } } -impl crate::WeightInfo for () { +// For backwards compatibility and tests +impl WeightInfo for () { fn create() -> Weight { - (58_069_000 as Weight) + (58_077_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn force_create() -> Weight { - (30_401_000 as Weight) + (30_497_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn destroy(z: u32, ) -> Weight { (0 as Weight) - .saturating_add((1_151_000 as Weight).saturating_mul(z as Weight)) + .saturating_add((1_153_000 as Weight).saturating_mul(z as Weight)) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(z as Weight))) } fn force_destroy(z: u32, ) -> Weight { (0 as Weight) - .saturating_add((1_151_000 as Weight).saturating_mul(z as Weight)) + .saturating_add((1_153_000 as Weight).saturating_mul(z as Weight)) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(z as Weight))) } fn mint() -> Weight { - (44_772_000 as Weight) + (45_600_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn burn() -> Weight { - (39_564_000 as Weight) + (40_143_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn transfer() -> Weight { - (58_453_000 as Weight) + (58_903_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn force_transfer() -> Weight { - (58_704_000 as Weight) + (59_025_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn freeze() -> Weight { - (43_141_000 as Weight) + (43_308_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn thaw() -> Weight { - (42_965_000 as Weight) + (43_383_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn transfer_ownership() -> Weight { - (30_110_000 as Weight) + (31_380_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn set_team() -> Weight { - (30_704_000 as Weight) + (32_049_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn set_max_zombies() -> Weight { - (56_445_000 as Weight) + (57_745_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) }