From 748bdf65d3c44ce8b0471aa850233438c2af3edf Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi <shawntabrizi@gmail.com> Date: Tue, 19 Jan 2021 07:14:32 -0400 Subject: [PATCH] Freeze Assets and Asset Metadata (#7346) * Features needed for reserve-backed stablecoins * Builds & tests. * Double map for an efficient destroy. * Update frame/assets/src/lib.rs Co-authored-by: Nikolay Volf <nikvolf@gmail.com> * ED/zombie-count/refs Feature: ED/minimum balance enforcement Feature: enforce zombie count Feature: allow system-alive accounts to exist, but add reference * Update frame/assets/src/lib.rs Co-authored-by: Nikolay Volf <nikvolf@gmail.com> * Update frame/assets/Cargo.toml Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com> * Docs * Some tests * More tests * Allow for max_zombies to be adjusted * Test for set_max_zombies * Tests and a couple of fixes * First few benchmarks * Benchmarks. * Fix error message in test * Fixes * Fixes * Fixes * 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 * Update frame/assets/src/lib.rs Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com> * Fixes * Fixes * Fixes * 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 * Fixes * Update default weight * Add proper verification to benchmarks * minor improvements to tests * Add `freeze_asset` and `thaw_asset` * Add metadata * fix build * Update benchmarks * fix line width * 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 * update default weights * destroy cleans up metadata * more comprehensive lifecycle test * update docs * Update frame/assets/src/benchmarking.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Fix * New weights system * fix compile * 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 * fix compile * fix up * 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 * fixes to pallet compile * fix node build * remote diff artifacts * less diff * 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 * Update frame/assets/src/lib.rs * Update frame/assets/src/lib.rs * usize to u32 * missed some usize * 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 Co-authored-by: Gav Wood <gavin@parity.io> Co-authored-by: Nikolay Volf <nikvolf@gmail.com> Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com> Co-authored-by: Parity Benchmarking Bot <admin@parity.io> Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com> Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- substrate/bin/node/runtime/src/lib.rs | 6 + substrate/frame/assets/src/benchmarking.rs | 61 ++++- substrate/frame/assets/src/lib.rs | 279 ++++++++++++++++++++- substrate/frame/assets/src/weights.rs | 111 +++++--- 4 files changed, 416 insertions(+), 41 deletions(-) diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index e88484e4729..92e5dfa7830 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -979,6 +979,9 @@ impl pallet_lottery::Config for Runtime { parameter_types! { pub const AssetDepositBase: Balance = 100 * DOLLARS; pub const AssetDepositPerZombie: Balance = 1 * DOLLARS; + pub const StringLimit: u32 = 50; + pub const MetadataDepositBase: Balance = 10 * DOLLARS; + pub const MetadataDepositPerByte: Balance = 1 * DOLLARS; } impl pallet_assets::Config for Runtime { @@ -989,6 +992,9 @@ impl pallet_assets::Config for Runtime { type ForceOrigin = EnsureRoot<AccountId>; type AssetDepositBase = AssetDepositBase; type AssetDepositPerZombie = AssetDepositPerZombie; + type StringLimit = StringLimit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; type WeightInfo = pallet_assets::weights::SubstrateWeight<Runtime>; } diff --git a/substrate/frame/assets/src/benchmarking.rs b/substrate/frame/assets/src/benchmarking.rs index 63258c2f591..90b6f65b398 100644 --- a/substrate/frame/assets/src/benchmarking.rs +++ b/substrate/frame/assets/src/benchmarking.rs @@ -18,7 +18,6 @@ //! Assets pallet benchmarking. use super::*; -use sp_std::prelude::*; use sp_runtime::traits::Bounded; use frame_system::RawOrigin as SystemOrigin; use frame_benchmarking::{benchmarks, account, whitelisted_caller}; @@ -154,16 +153,34 @@ benchmarks! { thaw { let (caller, caller_lookup) = create_default_minted_asset::<T>(10, 100u32.into()); - assert!(Assets::<T>::freeze( + Assets::<T>::freeze( SystemOrigin::Signed(caller.clone()).into(), Default::default(), - caller_lookup.clone() - ).is_ok()); + caller_lookup.clone(), + )?; }: _(SystemOrigin::Signed(caller.clone()), Default::default(), caller_lookup) verify { assert_last_event::<T>(RawEvent::Thawed(Default::default(), caller).into()); } + freeze_asset { + let (caller, caller_lookup) = create_default_minted_asset::<T>(10, 100u32.into()); + }: _(SystemOrigin::Signed(caller.clone()), Default::default()) + verify { + assert_last_event::<T>(RawEvent::AssetFrozen(Default::default()).into()); + } + + thaw_asset { + let (caller, caller_lookup) = create_default_minted_asset::<T>(10, 100u32.into()); + Assets::<T>::freeze_asset( + SystemOrigin::Signed(caller.clone()).into(), + Default::default(), + )?; + }: _(SystemOrigin::Signed(caller.clone()), Default::default()) + verify { + assert_last_event::<T>(RawEvent::AssetThawed(Default::default()).into()); + } + transfer_ownership { let (caller, _) = create_default_asset::<T>(10); let target: T::AccountId = account("target", 0, SEED); @@ -196,6 +213,21 @@ benchmarks! { verify { assert_last_event::<T>(RawEvent::MaxZombiesChanged(Default::default(), max_zombies).into()); } + + set_metadata { + let n in 0 .. T::StringLimit::get(); + let s in 0 .. T::StringLimit::get(); + + let name = vec![0u8; n as usize]; + let symbol = vec![0u8; s as usize]; + let decimals = 12; + + let (caller, _) = create_default_asset::<T>(10); + T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value()); + }: _(SystemOrigin::Signed(caller), Default::default(), name.clone(), symbol.clone(), decimals) + verify { + assert_last_event::<T>(RawEvent::MetadataSet(Default::default(), name, symbol, decimals).into()); + } } #[cfg(test)] @@ -273,6 +305,20 @@ mod tests { }); } + #[test] + fn freeze_asset() { + new_test_ext().execute_with(|| { + assert!(test_benchmark_freeze_asset::<Test>().is_ok()); + }); + } + + #[test] + fn thaw_asset() { + new_test_ext().execute_with(|| { + assert!(test_benchmark_thaw_asset::<Test>().is_ok()); + }); + } + #[test] fn transfer_ownership() { new_test_ext().execute_with(|| { @@ -293,4 +339,11 @@ mod tests { assert!(test_benchmark_set_max_zombies::<Test>().is_ok()); }); } + + #[test] + fn set_metadata() { + new_test_ext().execute_with(|| { + assert!(test_benchmark_set_metadata::<Test>().is_ok()); + }); + } } diff --git a/substrate/frame/assets/src/lib.rs b/substrate/frame/assets/src/lib.rs index dcb77cc6ebf..8f1ad02c08b 100644 --- a/substrate/frame/assets/src/lib.rs +++ b/substrate/frame/assets/src/lib.rs @@ -113,7 +113,7 @@ mod benchmarking; pub mod weights; -use sp_std::{fmt::Debug}; +use sp_std::{fmt::Debug, prelude::*}; use sp_runtime::{RuntimeDebug, traits::{ Member, AtLeast32BitUnsigned, Zero, StaticLookup, Saturating, CheckedSub, CheckedAdd }}; @@ -151,6 +151,16 @@ pub trait Config: frame_system::Config { /// supports. type AssetDepositPerZombie: Get<BalanceOf<Self>>; + /// The maximum length of a name or symbol stored on-chain. + type StringLimit: Get<u32>; + + /// The basic amount of funds that must be reserved when adding metadata to your asset. + type MetadataDepositBase: Get<BalanceOf<Self>>; + + /// The additional funds that must be reserved for the number of bytes you store in your + /// metadata. + type MetadataDepositPerByte: Get<BalanceOf<Self>>; + /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; } @@ -184,6 +194,8 @@ pub struct AssetDetails< zombies: u32, /// The total number of accounts. accounts: u32, + /// Whether the asset is frozen for permissionless transfers. + is_frozen: bool, } #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default)] @@ -198,6 +210,20 @@ pub struct AssetBalance< is_zombie: bool, } +#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default)] +pub struct AssetMetadata<DepositBalance> { + /// The balance deposited for this metadata. + /// + /// This pays for the data stored in this struct. + deposit: DepositBalance, + /// The user friendly name of this asset. Limited in length by `StringLimit`. + name: Vec<u8>, + /// The ticker symbol for this asset. Limited in length by `StringLimit`. + symbol: Vec<u8>, + /// The number of decimals this asset uses to represent one unit. + decimals: u8, +} + decl_storage! { trait Store for Module<T: Config> as Assets { /// Details of an asset. @@ -212,6 +238,9 @@ decl_storage! { hasher(blake2_128_concat) T::AssetId, hasher(blake2_128_concat) T::AccountId => AssetBalance<T::Balance>; + + /// Metadata of an asset. + Metadata: map hasher(blake2_128_concat) T::AssetId => AssetMetadata<BalanceOf<T>>; } } @@ -239,12 +268,18 @@ decl_event! { Frozen(AssetId, AccountId), /// Some account `who` was thawed. \[asset_id, who\] Thawed(AssetId, AccountId), + /// Some asset `asset_id` was frozen. \[asset_id\] + AssetFrozen(AssetId), + /// Some asset `asset_id` was thawed. \[asset_id\] + AssetThawed(AssetId), /// An asset class was destroyed. 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), + /// New metadata has been set for an asset. \[asset_id, name, symbol, decimals\] + MetadataSet(AssetId, Vec<u8>, Vec<u8>, u8), } } @@ -276,6 +311,8 @@ decl_error! { Overflow, /// Some internal state is broken. BadState, + /// Invalid metadata given. + BadMetadata, } } @@ -337,6 +374,7 @@ decl_module! { min_balance, zombies: Zero::zero(), accounts: Zero::zero(), + is_frozen: false, }); Self::deposit_event(RawEvent::Created(id, owner, admin)); } @@ -386,6 +424,7 @@ decl_module! { min_balance, zombies: Zero::zero(), accounts: Zero::zero(), + is_frozen: false, }); Self::deposit_event(RawEvent::ForceCreated(id, owner)); } @@ -412,7 +451,9 @@ decl_module! { ensure!(details.owner == origin, Error::<T>::NoPermission); ensure!(details.accounts == details.zombies, Error::<T>::RefsLeft); ensure!(details.zombies <= zombies_witness, Error::<T>::BadWitness); - T::Currency::unreserve(&details.owner, details.deposit); + + let metadata = Metadata::<T>::take(&id); + T::Currency::unreserve(&details.owner, details.deposit.saturating_add(metadata.deposit)); *maybe_details = None; Account::<T>::remove_prefix(&id); @@ -442,7 +483,9 @@ decl_module! { let details = maybe_details.take().ok_or(Error::<T>::Unknown)?; ensure!(details.accounts == details.zombies, Error::<T>::RefsLeft); ensure!(details.zombies <= zombies_witness, Error::<T>::BadWitness); - T::Currency::unreserve(&details.owner, details.deposit); + + let metadata = Metadata::<T>::take(&id); + T::Currency::unreserve(&details.owner, details.deposit.saturating_add(metadata.deposit)); *maybe_details = None; Account::<T>::remove_prefix(&id); @@ -580,6 +623,7 @@ decl_module! { let dest = T::Lookup::lookup(target)?; Asset::<T>::try_mutate(id, |maybe_details| { let details = maybe_details.as_mut().ok_or(Error::<T>::Unknown)?; + ensure!(!details.is_frozen, Error::<T>::Frozen); if dest == origin { return Ok(()) @@ -739,6 +783,54 @@ decl_module! { Self::deposit_event(Event::<T>::Thawed(id, who)); } + /// Disallow further unprivileged transfers for the asset class. + /// + /// Origin must be Signed and the sender should be the Freezer of the asset `id`. + /// + /// - `id`: The identifier of the asset to be frozen. + /// + /// Emits `Frozen`. + /// + /// Weight: `O(1)` + #[weight = T::WeightInfo::freeze_asset()] + fn freeze_asset(origin, #[compact] id: T::AssetId) -> DispatchResult { + let origin = ensure_signed(origin)?; + + Asset::<T>::try_mutate(id, |maybe_details| { + let d = maybe_details.as_mut().ok_or(Error::<T>::Unknown)?; + ensure!(&origin == &d.freezer, Error::<T>::NoPermission); + + d.is_frozen = true; + + Self::deposit_event(Event::<T>::AssetFrozen(id)); + Ok(()) + }) + } + + /// Allow unprivileged transfers for the asset 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. + /// + /// Emits `Thawed`. + /// + /// Weight: `O(1)` + #[weight = T::WeightInfo::thaw_asset()] + fn thaw_asset(origin, #[compact] id: T::AssetId) -> DispatchResult { + let origin = ensure_signed(origin)?; + + Asset::<T>::try_mutate(id, |maybe_details| { + let d = maybe_details.as_mut().ok_or(Error::<T>::Unknown)?; + ensure!(&origin == &d.admin, Error::<T>::NoPermission); + + d.is_frozen = false; + + Self::deposit_event(Event::<T>::AssetThawed(id)); + Ok(()) + }) + } + /// Change the Owner of an asset. /// /// Origin must be Signed and the sender should be the Owner of the asset `id`. @@ -809,6 +901,20 @@ decl_module! { }) } + /// Set the maximum number of zombie accounts for an asset. + /// + /// Origin must be Signed and the sender should be the Owner of the asset `id`. + /// + /// Funds of sender are reserved according to the formula: + /// `AssetDepositBase + AssetDepositPerZombie * max_zombies` taking into account + /// any already reserved funds. + /// + /// - `id`: The identifier of the asset to update zombie count. + /// - `max_zombies`: The new number of zombies allowed for this asset. + /// + /// Emits `MaxZombiesChanged`. + /// + /// Weight: `O(1)` #[weight = T::WeightInfo::set_max_zombies()] fn set_max_zombies(origin, #[compact] id: T::AssetId, @@ -837,6 +943,76 @@ decl_module! { Ok(()) }) } + + /// Set the metadata for an asset. + /// + /// NOTE: There is no `unset_metadata` call. Simply pass an empty name, symbol, + /// and 0 decimals to this function to remove the metadata of an asset and + /// return your deposit. + /// + /// Origin must be Signed and the sender should be the Owner of the asset `id`. + /// + /// Funds of sender are reserved according to the formula: + /// `MetadataDepositBase + MetadataDepositPerByte * (name.len + symbol.len)` taking into + /// account any already reserved funds. + /// + /// - `id`: The identifier of the asset to update. + /// - `name`: The user friendly name of this asset. Limited in length by `StringLimit`. + /// - `symbol`: The exchange symbol for this asset. Limited in length by `StringLimit`. + /// - `decimals`: The number of decimals this asset uses to represent one unit. + /// + /// Emits `MaxZombiesChanged`. + /// + /// Weight: `O(1)` + #[weight = T::WeightInfo::set_metadata(name.len() as u32, symbol.len() as u32)] + fn set_metadata(origin, + #[compact] id: T::AssetId, + name: Vec<u8>, + symbol: Vec<u8>, + decimals: u8, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + + ensure!(name.len() <= T::StringLimit::get() as usize, Error::<T>::BadMetadata); + ensure!(symbol.len() <= T::StringLimit::get() as usize, Error::<T>::BadMetadata); + + let d = Asset::<T>::get(id).ok_or(Error::<T>::Unknown)?; + ensure!(&origin == &d.owner, Error::<T>::NoPermission); + + Metadata::<T>::try_mutate_exists(id, |metadata| { + let bytes_used = name.len() + symbol.len(); + let old_deposit = match metadata { + Some(m) => m.deposit, + None => Default::default() + }; + + // Metadata is being removed + if bytes_used.is_zero() && decimals.is_zero() { + T::Currency::unreserve(&origin, old_deposit); + *metadata = None; + } else { + let new_deposit = T::MetadataDepositPerByte::get() + .saturating_mul(((name.len() + symbol.len()) as u32).into()) + .saturating_add(T::MetadataDepositBase::get()); + + if new_deposit > old_deposit { + T::Currency::reserve(&origin, new_deposit - old_deposit)?; + } else { + T::Currency::unreserve(&origin, old_deposit - new_deposit); + } + + *metadata = Some(AssetMetadata { + deposit: new_deposit, + name: name.clone(), + symbol: symbol.clone(), + decimals, + }) + } + + Self::deposit_event(RawEvent::MetadataSet(id, name, symbol, decimals)); + Ok(()) + }) + } } } @@ -912,6 +1088,7 @@ mod tests { use frame_support::{impl_outer_origin, assert_ok, assert_noop, parameter_types, impl_outer_event}; use sp_core::H256; use sp_runtime::{traits::{BlakeTwo256, IdentityLookup}, testing::Header}; + use pallet_balances::Error as BalancesError; mod pallet_assets { pub use crate::Event; @@ -976,6 +1153,9 @@ mod tests { parameter_types! { pub const AssetDepositBase: u64 = 1; pub const AssetDepositPerZombie: u64 = 1; + pub const StringLimit: u32 = 50; + pub const MetadataDepositBase: u64 = 1; + pub const MetadataDepositPerByte: u64 = 1; } impl Config for Test { @@ -986,6 +1166,9 @@ mod tests { type ForceOrigin = frame_system::EnsureRoot<u64>; type AssetDepositBase = AssetDepositBase; type AssetDepositPerZombie = AssetDepositPerZombie; + type StringLimit = StringLimit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; type WeightInfo = (); } type System = frame_system::Module<Test>; @@ -1013,15 +1196,41 @@ mod tests { 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!(Asset::<Test>::contains_key(0)); + + assert_ok!(Assets::set_metadata(Origin::signed(1), 0, vec![0], vec![0], 12)); + assert_eq!(Balances::reserved_balance(&1), 14); + assert!(Metadata::<Test>::contains_key(0)); + + assert_ok!(Assets::mint(Origin::signed(1), 0, 10, 100)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 20, 100)); + assert_eq!(Account::<Test>::iter_prefix(0).count(), 2); assert_ok!(Assets::destroy(Origin::signed(1), 0, 100)); assert_eq!(Balances::reserved_balance(&1), 0); + assert!(!Asset::<Test>::contains_key(0)); + assert!(!Metadata::<Test>::contains_key(0)); + assert_eq!(Account::<Test>::iter_prefix(0).count(), 0); + assert_ok!(Assets::create(Origin::signed(1), 0, 1, 10, 1)); assert_eq!(Balances::reserved_balance(&1), 11); + assert!(Asset::<Test>::contains_key(0)); + + assert_ok!(Assets::set_metadata(Origin::signed(1), 0, vec![0], vec![0], 12)); + assert_eq!(Balances::reserved_balance(&1), 14); + assert!(Metadata::<Test>::contains_key(0)); + + assert_ok!(Assets::mint(Origin::signed(1), 0, 10, 100)); + assert_ok!(Assets::mint(Origin::signed(1), 0, 20, 100)); + assert_eq!(Account::<Test>::iter_prefix(0).count(), 2); assert_ok!(Assets::force_destroy(Origin::root(), 0, 100)); assert_eq!(Balances::reserved_balance(&1), 0); + + assert!(!Asset::<Test>::contains_key(0)); + assert!(!Metadata::<Test>::contains_key(0)); + assert_eq!(Account::<Test>::iter_prefix(0).count(), 0); }); } @@ -1172,7 +1381,7 @@ mod tests { } #[test] - fn transferring_frozen_balance_should_not_work() { + fn transferring_frozen_user_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)); @@ -1184,6 +1393,19 @@ mod tests { }); } + #[test] + fn transferring_frozen_asset_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_asset(Origin::signed(1), 0)); + assert_noop!(Assets::transfer(Origin::signed(1), 0, 2, 50), Error::<Test>::Frozen); + assert_ok!(Assets::thaw_asset(Origin::signed(1), 0)); + assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); + }); + } + #[test] fn origin_guards_should_work() { new_test_ext().execute_with(|| { @@ -1306,4 +1528,53 @@ mod tests { assert_noop!(Assets::burn(Origin::signed(1), 0, 2, u64::max_value()), Error::<Test>::BalanceZero); }); } + + #[test] + fn set_metadata_should_work() { + new_test_ext().execute_with(|| { + // Cannot add metadata to unknown asset + assert_noop!( + Assets::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 10], 12), + Error::<Test>::Unknown, + ); + assert_ok!(Assets::force_create(Origin::root(), 0, 1, 10, 1)); + // Cannot add metadata to unowned asset + assert_noop!( + Assets::set_metadata(Origin::signed(2), 0, vec![0u8; 10], vec![0u8; 10], 12), + Error::<Test>::NoPermission, + ); + + // Cannot add oversized metadata + assert_noop!( + Assets::set_metadata(Origin::signed(1), 0, vec![0u8; 100], vec![0u8; 10], 12), + Error::<Test>::BadMetadata, + ); + assert_noop!( + Assets::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 100], 12), + Error::<Test>::BadMetadata, + ); + + // Successfully add metadata and take deposit + Balances::make_free_balance_be(&1, 30); + assert_ok!(Assets::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 10], 12)); + assert_eq!(Balances::free_balance(&1), 9); + + // Update deposit + assert_ok!(Assets::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 5], 12)); + assert_eq!(Balances::free_balance(&1), 14); + assert_ok!(Assets::set_metadata(Origin::signed(1), 0, vec![0u8; 10], vec![0u8; 15], 12)); + assert_eq!(Balances::free_balance(&1), 4); + + // Cannot over-reserve + assert_noop!( + Assets::set_metadata(Origin::signed(1), 0, vec![0u8; 20], vec![0u8; 20], 12), + BalancesError::<Test, _>::InsufficientBalance, + ); + + // Clear Metadata + assert!(Metadata::<Test>::contains_key(0)); + assert_ok!(Assets::set_metadata(Origin::signed(1), 0, vec![], vec![], 0)); + assert!(!Metadata::<Test>::contains_key(0)); + }); + } } diff --git a/substrate/frame/assets/src/weights.rs b/substrate/frame/assets/src/weights.rs index a8e17615d28..1858fe708e1 100644 --- a/substrate/frame/assets/src/weights.rs +++ b/substrate/frame/assets/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2021 Parity Technologies (UK) Ltd. +// Copyright (C) 2021 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,8 +17,8 @@ //! Autogenerated weights for pallet_assets //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0 -//! DATE: 2020-12-03, STEPS: [50, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.1 +//! DATE: 2021-01-18, STEPS: [50, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: @@ -54,154 +54,199 @@ pub trait WeightInfo { fn force_transfer() -> Weight; fn freeze() -> Weight; fn thaw() -> Weight; + fn freeze_asset() -> Weight; + fn thaw_asset() -> Weight; fn transfer_ownership() -> Weight; fn set_team() -> Weight; fn set_max_zombies() -> Weight; + fn set_metadata(n: u32, s: u32, ) -> Weight; } /// Weights for pallet_assets using the Substrate node and recommended hardware. pub struct SubstrateWeight<T>(PhantomData<T>); impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { fn create() -> Weight { - (58_077_000 as Weight) + (44_459_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_497_000 as Weight) + (21_480_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_153_000 as Weight).saturating_mul(z as Weight)) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) + // Standard Error: 2_000 + .saturating_add((1_149_000 as Weight).saturating_mul(z as Weight)) + .saturating_add(T::DbWeight::get().reads(2 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_153_000 as Weight).saturating_mul(z as Weight)) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) + // Standard Error: 2_000 + .saturating_add((1_146_000 as Weight).saturating_mul(z as Weight)) + .saturating_add(T::DbWeight::get().reads(2 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 { - (45_600_000 as Weight) + (32_995_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn burn() -> Weight { - (40_143_000 as Weight) + (29_245_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_903_000 as Weight) + (42_211_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 { - (59_025_000 as Weight) + (42_218_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_308_000 as Weight) + (31_079_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn thaw() -> Weight { - (43_383_000 as Weight) + (30_853_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } + fn freeze_asset() -> Weight { + (22_383_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn thaw_asset() -> Weight { + (22_341_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } fn transfer_ownership() -> Weight { - (31_380_000 as Weight) + (22_782_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 { - (32_049_000 as Weight) + (23_293_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 { - (57_745_000 as Weight) + (44_525_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } + fn set_metadata(n: u32, s: u32, ) -> Weight { + (49_456_000 as Weight) + // Standard Error: 0 + .saturating_add((1_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 0 + .saturating_add((6_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } } // For backwards compatibility and tests impl WeightInfo for () { fn create() -> Weight { - (58_077_000 as Weight) + (44_459_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn force_create() -> Weight { - (30_497_000 as Weight) + (21_480_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_153_000 as Weight).saturating_mul(z as Weight)) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + // Standard Error: 2_000 + .saturating_add((1_149_000 as Weight).saturating_mul(z as Weight)) + .saturating_add(RocksDbWeight::get().reads(2 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_153_000 as Weight).saturating_mul(z as Weight)) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + // Standard Error: 2_000 + .saturating_add((1_146_000 as Weight).saturating_mul(z as Weight)) + .saturating_add(RocksDbWeight::get().reads(2 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 { - (45_600_000 as Weight) + (32_995_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn burn() -> Weight { - (40_143_000 as Weight) + (29_245_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn transfer() -> Weight { - (58_903_000 as Weight) + (42_211_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn force_transfer() -> Weight { - (59_025_000 as Weight) + (42_218_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn freeze() -> Weight { - (43_308_000 as Weight) + (31_079_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn thaw() -> Weight { - (43_383_000 as Weight) + (30_853_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } + fn freeze_asset() -> Weight { + (22_383_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn thaw_asset() -> Weight { + (22_341_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } fn transfer_ownership() -> Weight { - (31_380_000 as Weight) + (22_782_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn set_team() -> Weight { - (32_049_000 as Weight) + (23_293_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn set_max_zombies() -> Weight { - (57_745_000 as Weight) + (44_525_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } + fn set_metadata(n: u32, s: u32, ) -> Weight { + (49_456_000 as Weight) + // Standard Error: 0 + .saturating_add((1_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 0 + .saturating_add((6_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } } -- GitLab