difftreelog
fieat: add repair_item extrinsic + test
in: master
15 files changed
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -1815,6 +1815,9 @@
/// The price of setting approval for all
fn set_allowance_for_all() -> Weight;
+
+ /// The price of repairing an item.
+ fn repair_item() -> Weight;
}
/// Weight info extension trait for refungible pallet.
@@ -2136,6 +2139,9 @@
/// Tells whether the given `owner` approves the `operator`.
fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;
+
+ /// Repairs a possibly broken item.
+ fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;
}
/// Extension for RFT collection.
pallets/fungible/src/common.rsdiffbeforeafterboth--- a/pallets/fungible/src/common.rs
+++ b/pallets/fungible/src/common.rs
@@ -111,6 +111,10 @@
fn set_allowance_for_all() -> Weight {
Weight::zero()
}
+
+ fn repair_item() -> Weight {
+ Weight::zero()
+ }
}
/// Implementation of `CommonCollectionOperations` for `FungibleHandle`. It wraps FungibleHandle Pallete
@@ -441,4 +445,9 @@
fn allowance_for_all(&self, _owner: T::CrossAccountId, _operator: T::CrossAccountId) -> bool {
false
}
+
+ /// Repairs a possibly broken item.
+ fn repair_item(&self, _token: TokenId) -> DispatchResultWithPostInfo {
+ fail!(<Error<T>>::FungibleTokensAreAlwaysValid)
+ }
}
pallets/fungible/src/lib.rsdiffbeforeafterboth--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -129,6 +129,8 @@
SettingPropertiesNotAllowed,
/// Setting allowance for all is not allowed.
SettingAllowanceForAllNotAllowed,
+ /// Only a fungible collection could be possibly broken; any fungible token is valid.
+ FungibleTokensAreAlwaysValid,
}
#[pallet::config]
pallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/benchmarking.rs
+++ b/pallets/nonfungible/src/benchmarking.rs
@@ -236,4 +236,12 @@
operator: cross_sub;
};
}: {<Pallet<T>>::allowance_for_all(&collection, &owner, &operator)}
+
+ repair_item {
+ bench_init!{
+ owner: sub; collection: collection(owner);
+ owner: cross_from_sub;
+ };
+ let item = create_max_item(&collection, &owner, owner.clone())?;
+ }: {<Pallet<T>>::repair_item(&collection, item)?}
}
pallets/nonfungible/src/common.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -126,6 +126,10 @@
fn set_allowance_for_all() -> Weight {
<SelfWeightOf<T>>::set_allowance_for_all()
}
+
+ fn repair_item() -> Weight {
+ <SelfWeightOf<T>>::repair_item()
+ }
}
fn map_create_data<T: Config>(
@@ -532,4 +536,11 @@
fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool {
<Pallet<T>>::allowance_for_all(self, &owner, &operator)
}
+
+ fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo {
+ with_weight(
+ <Pallet<T>>::repair_item(self, token),
+ <CommonWeights<T>>::repair_item(),
+ )
+ }
}
pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -1398,4 +1398,12 @@
) -> bool {
<CollectionAllowance<T>>::get((collection.id, owner, operator))
}
+
+ pub fn repair_item(collection: &NonfungibleHandle<T>, token: TokenId) -> DispatchResult {
+ <TokenProperties<T>>::mutate((collection.id, token), |properties| {
+ properties.recompute_consumed_space();
+ });
+
+ Ok(())
+ }
}
pallets/nonfungible/src/weights.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/weights.rs
+++ b/pallets/nonfungible/src/weights.rs
@@ -50,6 +50,7 @@
fn token_owner() -> Weight;
fn set_allowance_for_all() -> Weight;
fn allowance_for_all() -> Weight;
+ fn repair_item() -> Weight;
}
/// Weights for pallet_nonfungible using the Substrate node and recommended hardware.
@@ -208,6 +209,12 @@
Weight::from_ref_time(6_161_000 as u64)
.saturating_add(T::DbWeight::get().reads(1 as u64))
}
+ // Storage: Nonfungible TokenProperties (r:1 w:1)
+ fn repair_item() -> Weight {
+ Weight::from_ref_time(5_701_000 as u64)
+ .saturating_add(T::DbWeight::get().reads(1 as u64))
+ .saturating_add(T::DbWeight::get().writes(1 as u64))
+ }
}
// For backwards compatibility and tests
@@ -365,4 +372,10 @@
Weight::from_ref_time(6_161_000 as u64)
.saturating_add(RocksDbWeight::get().reads(1 as u64))
}
+ // Storage: Nonfungible TokenProperties (r:1 w:1)
+ fn repair_item() -> Weight {
+ Weight::from_ref_time(5_701_000 as u64)
+ .saturating_add(RocksDbWeight::get().reads(1 as u64))
+ .saturating_add(RocksDbWeight::get().writes(1 as u64))
+ }
}
pallets/refungible/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/refungible/src/benchmarking.rs
+++ b/pallets/refungible/src/benchmarking.rs
@@ -304,4 +304,12 @@
operator: cross_sub;
};
}: {<Pallet<T>>::allowance_for_all(&collection, &owner, &operator)}
+
+ repair_item {
+ bench_init!{
+ owner: sub; collection: collection(owner);
+ owner: cross_from_sub;
+ };
+ let item = create_max_item(&collection, &owner, [(owner.clone(), 100)])?;
+ }: {<Pallet<T>>::repair_item(&collection, item)?}
}
pallets/refungible/src/common.rsdiffbeforeafterboth--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -156,6 +156,10 @@
fn set_allowance_for_all() -> Weight {
<SelfWeightOf<T>>::set_allowance_for_all()
}
+
+ fn repair_item() -> Weight {
+ <SelfWeightOf<T>>::repair_item()
+ }
}
fn map_create_data<T: Config>(
@@ -536,6 +540,13 @@
fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool {
<Pallet<T>>::allowance_for_all(self, &owner, &operator)
}
+
+ fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo {
+ with_weight(
+ <Pallet<T>>::repair_item(self, token),
+ <CommonWeights<T>>::repair_item(),
+ )
+ }
}
impl<T: Config> RefungibleExtensions<T> for RefungibleHandle<T> {
pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -1461,4 +1461,12 @@
) -> bool {
<CollectionAllowance<T>>::get((collection.id, owner, operator))
}
+
+ pub fn repair_item(collection: &RefungibleHandle<T>, token: TokenId) -> DispatchResult {
+ <TokenProperties<T>>::mutate((collection.id, token), |properties| {
+ properties.recompute_consumed_space();
+ });
+
+ Ok(())
+ }
}
pallets/refungible/src/weights.rsdiffbeforeafterboth--- a/pallets/refungible/src/weights.rs
+++ b/pallets/refungible/src/weights.rs
@@ -57,6 +57,7 @@
fn token_owner() -> Weight;
fn set_allowance_for_all() -> Weight;
fn allowance_for_all() -> Weight;
+ fn repair_item() -> Weight;
}
/// Weights for pallet_refungible using the Substrate node and recommended hardware.
@@ -272,6 +273,12 @@
Weight::from_ref_time(5_901_000 as u64)
.saturating_add(T::DbWeight::get().reads(1 as u64))
}
+ // Storage: Refungible TokenProperties (r:1 w:1)
+ fn repair_item() -> Weight {
+ Weight::from_ref_time(5_489_000 as u64)
+ .saturating_add(T::DbWeight::get().reads(1 as u64))
+ .saturating_add(T::DbWeight::get().writes(1 as u64))
+ }
}
// For backwards compatibility and tests
@@ -486,4 +493,10 @@
Weight::from_ref_time(5_901_000 as u64)
.saturating_add(RocksDbWeight::get().reads(1 as u64))
}
+ // Storage: Refungible TokenProperties (r:1 w:1)
+ fn repair_item() -> Weight {
+ Weight::from_ref_time(5_489_000 as u64)
+ .saturating_add(RocksDbWeight::get().reads(1 as u64))
+ .saturating_add(RocksDbWeight::get().writes(1 as u64))
+ }
}
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -982,6 +982,23 @@
d.set_allowance_for_all(sender, operator, approve)
})
}
+
+ /// Repairs a broken item
+ ///
+ /// # Arguments
+ ///
+ /// * `collection_id`: ID of the collection the item belongs to.
+ /// * `item_id`: ID of the item.
+ #[weight = T::CommonWeightInfo::repair_item()]
+ pub fn repair_item(
+ _origin,
+ collection_id: CollectionId,
+ item_id: TokenId,
+ ) -> DispatchResultWithPostInfo {
+ dispatch_tx::<T, _>(collection_id, |d| {
+ d.repair_item(item_id)
+ })
+ }
}
}
primitives/data-structs/src/lib.rsdiffbeforeafterboth--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -1218,6 +1218,10 @@
Ok(())
}
+
+ pub fn values(&self) -> impl Iterator<Item = &Value> {
+ self.0.values()
+ }
}
impl<Value> IntoIterator for PropertiesMap<Value> {
@@ -1290,6 +1294,12 @@
pub fn get(&self, key: &PropertyKey) -> Option<&PropertyValue> {
self.map.get(key)
}
+
+ /// Recomputes the consumed space for the current properties state.
+ /// Needed to repair a token due to a bug fixed in the [PR #733](https://github.com/UniqueNetwork/unique-chain/pull/773).
+ pub fn recompute_consumed_space(&mut self) {
+ self.consumed_space = self.map.values().map(|value| value.len() as u32).sum();
+ }
}
impl IntoIterator for Properties {
runtime/common/weights.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617use core::marker::PhantomData;18use frame_support::{weights::Weight};19use pallet_common::{CommonWeightInfo, dispatch::dispatch_weight, RefungibleExtensionsWeightInfo};2021use pallet_fungible::{Config as FungibleConfig, common::CommonWeights as FungibleWeights};22use pallet_nonfungible::{Config as NonfungibleConfig, common::CommonWeights as NonfungibleWeights};2324#[cfg(feature = "refungible")]25use pallet_refungible::{26 Config as RefungibleConfig, weights::WeightInfo, common::CommonWeights as RefungibleWeights,27};28use up_data_structs::{CreateItemExData, CreateItemData};2930macro_rules! max_weight_of {31 ($method:ident ( $($args:tt)* )) => {{32 let max_weight = <FungibleWeights<T>>::$method($($args)*)33 .max(<NonfungibleWeights<T>>::$method($($args)*));3435 #[cfg(feature = "refungible")]36 let max_weight = max_weight.max(<RefungibleWeights<T>>::$method($($args)*));3738 max_weight39 }};40}4142#[cfg(not(feature = "refungible"))]43pub trait CommonWeightConfigs: FungibleConfig + NonfungibleConfig {}4445#[cfg(not(feature = "refungible"))]46impl<T: FungibleConfig + NonfungibleConfig> CommonWeightConfigs for T {}4748#[cfg(feature = "refungible")]49pub trait CommonWeightConfigs: FungibleConfig + NonfungibleConfig + RefungibleConfig {}5051#[cfg(feature = "refungible")]52impl<T: FungibleConfig + NonfungibleConfig + RefungibleConfig> CommonWeightConfigs for T {}5354pub struct CommonWeights<T>(PhantomData<T>);5556impl<T> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T>57where58 T: CommonWeightConfigs,59{60 fn create_item() -> Weight {61 dispatch_weight::<T>() + max_weight_of!(create_item())62 }6364 fn create_multiple_items(data: &[CreateItemData]) -> Weight {65 dispatch_weight::<T>() + max_weight_of!(create_multiple_items(data))66 }6768 fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {69 dispatch_weight::<T>() + max_weight_of!(create_multiple_items_ex(data))70 }7172 fn burn_item() -> Weight {73 dispatch_weight::<T>() + max_weight_of!(burn_item())74 }7576 fn set_collection_properties(amount: u32) -> Weight {77 dispatch_weight::<T>() + max_weight_of!(set_collection_properties(amount))78 }7980 fn delete_collection_properties(amount: u32) -> Weight {81 dispatch_weight::<T>() + max_weight_of!(delete_collection_properties(amount))82 }8384 fn set_token_properties(amount: u32) -> Weight {85 dispatch_weight::<T>() + max_weight_of!(set_token_properties(amount))86 }8788 fn delete_token_properties(amount: u32) -> Weight {89 dispatch_weight::<T>() + max_weight_of!(delete_token_properties(amount))90 }9192 fn set_token_property_permissions(amount: u32) -> Weight {93 dispatch_weight::<T>() + max_weight_of!(set_token_property_permissions(amount))94 }9596 fn transfer() -> Weight {97 dispatch_weight::<T>() + max_weight_of!(transfer())98 }99100 fn approve() -> Weight {101 dispatch_weight::<T>() + max_weight_of!(approve())102 }103104 fn transfer_from() -> Weight {105 dispatch_weight::<T>() + max_weight_of!(transfer_from())106 }107108 fn burn_from() -> Weight {109 dispatch_weight::<T>() + max_weight_of!(burn_from())110 }111112 fn burn_recursively_self_raw() -> Weight {113 max_weight_of!(burn_recursively_self_raw())114 }115116 fn burn_recursively_breadth_raw(amount: u32) -> Weight {117 max_weight_of!(burn_recursively_breadth_raw(amount))118 }119120 fn token_owner() -> Weight {121 max_weight_of!(token_owner())122 }123124 fn set_allowance_for_all() -> Weight {125 max_weight_of!(set_allowance_for_all())126 }127}128129#[cfg(feature = "refungible")]130impl<T> RefungibleExtensionsWeightInfo for CommonWeights<T>131where132 T: FungibleConfig + NonfungibleConfig + RefungibleConfig,133{134 fn repartition() -> Weight {135 dispatch_weight::<T>() + <<T as RefungibleConfig>::WeightInfo>::repartition_item()136 }137}138139#[cfg(not(feature = "refungible"))]140impl<T> RefungibleExtensionsWeightInfo for CommonWeights<T>141where142 T: FungibleConfig + NonfungibleConfig,143{144 fn repartition() -> Weight {145 dispatch_weight::<T>()146 }147}1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617use core::marker::PhantomData;18use frame_support::{weights::Weight};19use pallet_common::{CommonWeightInfo, dispatch::dispatch_weight, RefungibleExtensionsWeightInfo};2021use pallet_fungible::{Config as FungibleConfig, common::CommonWeights as FungibleWeights};22use pallet_nonfungible::{Config as NonfungibleConfig, common::CommonWeights as NonfungibleWeights};2324#[cfg(feature = "refungible")]25use pallet_refungible::{26 Config as RefungibleConfig, weights::WeightInfo, common::CommonWeights as RefungibleWeights,27};28use up_data_structs::{CreateItemExData, CreateItemData};2930macro_rules! max_weight_of {31 ($method:ident ( $($args:tt)* )) => {{32 let max_weight = <FungibleWeights<T>>::$method($($args)*)33 .max(<NonfungibleWeights<T>>::$method($($args)*));3435 #[cfg(feature = "refungible")]36 let max_weight = max_weight.max(<RefungibleWeights<T>>::$method($($args)*));3738 max_weight39 }};40}4142#[cfg(not(feature = "refungible"))]43pub trait CommonWeightConfigs: FungibleConfig + NonfungibleConfig {}4445#[cfg(not(feature = "refungible"))]46impl<T: FungibleConfig + NonfungibleConfig> CommonWeightConfigs for T {}4748#[cfg(feature = "refungible")]49pub trait CommonWeightConfigs: FungibleConfig + NonfungibleConfig + RefungibleConfig {}5051#[cfg(feature = "refungible")]52impl<T: FungibleConfig + NonfungibleConfig + RefungibleConfig> CommonWeightConfigs for T {}5354pub struct CommonWeights<T>(PhantomData<T>);5556impl<T> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T>57where58 T: CommonWeightConfigs,59{60 fn create_item() -> Weight {61 dispatch_weight::<T>() + max_weight_of!(create_item())62 }6364 fn create_multiple_items(data: &[CreateItemData]) -> Weight {65 dispatch_weight::<T>() + max_weight_of!(create_multiple_items(data))66 }6768 fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {69 dispatch_weight::<T>() + max_weight_of!(create_multiple_items_ex(data))70 }7172 fn burn_item() -> Weight {73 dispatch_weight::<T>() + max_weight_of!(burn_item())74 }7576 fn set_collection_properties(amount: u32) -> Weight {77 dispatch_weight::<T>() + max_weight_of!(set_collection_properties(amount))78 }7980 fn delete_collection_properties(amount: u32) -> Weight {81 dispatch_weight::<T>() + max_weight_of!(delete_collection_properties(amount))82 }8384 fn set_token_properties(amount: u32) -> Weight {85 dispatch_weight::<T>() + max_weight_of!(set_token_properties(amount))86 }8788 fn delete_token_properties(amount: u32) -> Weight {89 dispatch_weight::<T>() + max_weight_of!(delete_token_properties(amount))90 }9192 fn set_token_property_permissions(amount: u32) -> Weight {93 dispatch_weight::<T>() + max_weight_of!(set_token_property_permissions(amount))94 }9596 fn transfer() -> Weight {97 dispatch_weight::<T>() + max_weight_of!(transfer())98 }99100 fn approve() -> Weight {101 dispatch_weight::<T>() + max_weight_of!(approve())102 }103104 fn transfer_from() -> Weight {105 dispatch_weight::<T>() + max_weight_of!(transfer_from())106 }107108 fn burn_from() -> Weight {109 dispatch_weight::<T>() + max_weight_of!(burn_from())110 }111112 fn burn_recursively_self_raw() -> Weight {113 max_weight_of!(burn_recursively_self_raw())114 }115116 fn burn_recursively_breadth_raw(amount: u32) -> Weight {117 max_weight_of!(burn_recursively_breadth_raw(amount))118 }119120 fn token_owner() -> Weight {121 max_weight_of!(token_owner())122 }123124 fn set_allowance_for_all() -> Weight {125 max_weight_of!(set_allowance_for_all())126 }127128 fn repair_item() -> Weight {129 max_weight_of!(repair_item())130 }131}132133#[cfg(feature = "refungible")]134impl<T> RefungibleExtensionsWeightInfo for CommonWeights<T>135where136 T: FungibleConfig + NonfungibleConfig + RefungibleConfig,137{138 fn repartition() -> Weight {139 dispatch_weight::<T>() + <<T as RefungibleConfig>::WeightInfo>::repartition_item()140 }141}142143#[cfg(not(feature = "refungible"))]144impl<T> RefungibleExtensionsWeightInfo for CommonWeights<T>145where146 T: FungibleConfig + NonfungibleConfig,147{148 fn repartition() -> Weight {149 dispatch_weight::<T>()150 }151}tests/src/nesting/tokenProperties.test.tsdiffbeforeafterboth--- a/tests/src/nesting/tokenProperties.test.ts
+++ b/tests/src/nesting/tokenProperties.test.ts
@@ -28,7 +28,7 @@
before(async () => {
await usingPlaygrounds(async (helper, privateKey) => {
const donor = await privateKey({filename: __filename});
- [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
+ [alice, bob, charlie] = await helper.arrange.createAccounts([200n, 100n, 100n], donor);
});
permissions = [
@@ -401,6 +401,39 @@
consumedSpace = await token.getTokenPropertiesConsumedSpace();
expect(consumedSpace).to.be.equal(originalSpace);
}));
+
+ [
+ {mode: 'nft' as const, pieces: undefined, requiredPallets: []},
+ {mode: 'rft' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]},
+ ].map(testCase =>
+ itSub.ifWithPallets(`repair_item preserves valid consumed space (${testCase.mode})`, testCase.requiredPallets, async({helper}) => {
+ const propKey = 'tok-prop';
+
+ const collection = await helper[testCase.mode].mintCollection(alice, {
+ tokenPropertyPermissions: [
+ {
+ key: propKey,
+ permission: {mutable: true, tokenOwner: true},
+ },
+ ],
+ });
+ const token = await (
+ testCase.pieces
+ ? collection.mintToken(alice, testCase.pieces)
+ : collection.mintToken(alice)
+ );
+
+ const propDataSize = 4096;
+ const propData = 'a'.repeat(propDataSize);
+
+ await token.setProperties(alice, [{key: propKey, value: propData}]);
+ const originalSpace = await token.getTokenPropertiesConsumedSpace();
+ expect(originalSpace).to.be.equal(propDataSize);
+
+ await helper.executeExtrinsic(alice, 'api.tx.unique.repairItem', [token.collectionId, token.tokenId], true);
+ const recomputedSpace = await token.getTokenPropertiesConsumedSpace();
+ expect(recomputedSpace).to.be.equal(originalSpace);
+ }));
});
describe('Negative Integration Test: Token Properties', () => {