git.delta.rocks / unique-network / refs/commits / 2a4f1af36afb

difftreelog

feat(repair-item) change to force_repair_item + add force_repair_collection + tests

Fahrrader2022-12-16parent: #669456d.patch.diff
in: master

10 files changed

modifiedpallets/common/src/lib.rsdiffbeforeafterboth
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -1730,6 +1730,15 @@
 		);
 		Ok(new_permission)
 	}
+
+	/// Repair possibly broken properties of a collection.
+	pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {
+		CollectionProperties::<T>::mutate(collection_id, |properties| {
+			properties.recompute_consumed_space();
+		});
+
+		Ok(())
+	}
 }
 
 /// Indicates unsupported methods by returning [Error::UnsupportedOperation].
@@ -1819,7 +1828,7 @@
 	fn set_allowance_for_all() -> Weight;
 
 	/// The price of repairing an item.
-	fn repair_item() -> Weight;
+	fn force_repair_item() -> Weight;
 }
 
 /// Weight info extension trait for refungible pallet.
modifiedpallets/fungible/src/common.rsdiffbeforeafterboth
--- a/pallets/fungible/src/common.rs
+++ b/pallets/fungible/src/common.rs
@@ -113,7 +113,7 @@
 		Weight::zero()
 	}
 
-	fn repair_item() -> Weight {
+	fn force_repair_item() -> Weight {
 		Weight::zero()
 	}
 }
modifiedpallets/nonfungible/src/common.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -127,7 +127,7 @@
 		<SelfWeightOf<T>>::set_allowance_for_all()
 	}
 
-	fn repair_item() -> Weight {
+	fn force_repair_item() -> Weight {
 		<SelfWeightOf<T>>::repair_item()
 	}
 }
@@ -540,7 +540,7 @@
 	fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo {
 		with_weight(
 			<Pallet<T>>::repair_item(self, token),
-			<CommonWeights<T>>::repair_item(),
+			<CommonWeights<T>>::force_repair_item(),
 		)
 	}
 }
modifiedpallets/refungible/src/common.rsdiffbeforeafterboth
--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -157,7 +157,7 @@
 		<SelfWeightOf<T>>::set_allowance_for_all()
 	}
 
-	fn repair_item() -> Weight {
+	fn force_repair_item() -> Weight {
 		<SelfWeightOf<T>>::repair_item()
 	}
 }
@@ -544,7 +544,7 @@
 	fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo {
 		with_weight(
 			<Pallet<T>>::repair_item(self, token),
-			<CommonWeights<T>>::repair_item(),
+			<CommonWeights<T>>::force_repair_item(),
 		)
 	}
 }
modifiedpallets/unique/src/lib.rsdiffbeforeafterboth
--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -82,7 +82,7 @@
 	BoundedVec,
 };
 use scale_info::TypeInfo;
-use frame_system::{self as system, ensure_signed};
+use frame_system::{self as system, ensure_signed, ensure_root};
 use sp_std::{vec, vec::Vec};
 use up_data_structs::{
 	MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,
@@ -983,18 +983,33 @@
 			})
 		}
 
-		/// Repairs a broken item
+		/// Repairs a collection's properties if the data was somehow corrupted.
 		///
 		/// # Arguments
 		///
+		/// * `collection_id`: ID of the collection to repair.
+		#[weight = <SelfWeightOf<T>>::force_repair_collection()]
+		pub fn force_repair_collection(
+			origin,
+			collection_id: CollectionId,
+		) -> DispatchResult {
+			ensure_root(origin)?;
+			<PalletCommon<T>>::repair_collection(collection_id)
+		}
+
+		/// Repairs a token's properties if the data was somehow corrupted.
+		///
+		/// # 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,
+		#[weight = T::CommonWeightInfo::force_repair_item()]
+		pub fn force_repair_item(
+			origin,
 			collection_id: CollectionId,
 			item_id: TokenId,
 		) -> DispatchResultWithPostInfo {
+			ensure_root(origin)?;
 			dispatch_tx::<T, _>(collection_id, |d| {
 				d.repair_item(item_id)
 			})
modifiedpallets/unique/src/weights.rsdiffbeforeafterboth
before · pallets/unique/src/weights.rs
1// Template adopted from https://github.com/paritytech/substrate/blob/master/.maintain/frame-weight-template.hbs23//! Autogenerated weights for pallet_unique4//!5//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev6//! DATE: 2022-08-15, STEPS: `50`, REPEAT: 80, LOW RANGE: `[]`, HIGH RANGE: `[]`7//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 102489// Executed Command:10// target/release/unique-collator11// benchmark12// pallet13// --pallet14// pallet-unique15// --wasm-execution16// compiled17// --extrinsic18// *19// --template20// .maintain/frame-weight-template.hbs21// --steps=5022// --repeat=8023// --heap-pages=409624// --output=./pallets/unique/src/weights.rs2526#![cfg_attr(rustfmt, rustfmt_skip)]27#![allow(unused_parens)]28#![allow(unused_imports)]29#![allow(clippy::unnecessary_cast)]3031use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};32use sp_std::marker::PhantomData;3334/// Weight functions needed for pallet_unique.35pub trait WeightInfo {36	fn create_collection() -> Weight;37	fn destroy_collection() -> Weight;38	fn add_to_allow_list() -> Weight;39	fn remove_from_allow_list() -> Weight;40	fn change_collection_owner() -> Weight;41	fn add_collection_admin() -> Weight;42	fn remove_collection_admin() -> Weight;43	fn set_collection_sponsor() -> Weight;44	fn confirm_sponsorship() -> Weight;45	fn remove_collection_sponsor() -> Weight;46	fn set_transfers_enabled_flag() -> Weight;47	fn set_collection_limits() -> Weight;48}4950/// Weights for pallet_unique using the Substrate node and recommended hardware.51pub struct SubstrateWeight<T>(PhantomData<T>);52impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {53	// Storage: Common CreatedCollectionCount (r:1 w:1)54	// Storage: Common DestroyedCollectionCount (r:1 w:0)55	// Storage: System Account (r:2 w:2)56	// Storage: Common CollectionPropertyPermissions (r:0 w:1)57	// Storage: Common CollectionProperties (r:0 w:1)58	// Storage: Common CollectionById (r:0 w:1)59	fn create_collection() -> Weight {60		Weight::from_ref_time(43_143_000)61			.saturating_add(T::DbWeight::get().reads(4 as u64))62			.saturating_add(T::DbWeight::get().writes(6 as u64))63	}64	// Storage: Common CollectionById (r:1 w:1)65	// Storage: Nonfungible TokenData (r:1 w:0)66	// Storage: Common DestroyedCollectionCount (r:1 w:1)67	// Storage: Nonfungible TokensMinted (r:0 w:1)68	// Storage: Nonfungible TokensBurnt (r:0 w:1)69	// Storage: Common AdminAmount (r:0 w:1)70	// Storage: Common CollectionProperties (r:0 w:1)71	fn destroy_collection() -> Weight {72		Weight::from_ref_time(50_188_000)73			.saturating_add(T::DbWeight::get().reads(3 as u64))74			.saturating_add(T::DbWeight::get().writes(6 as u64))75	}76	// Storage: Common CollectionById (r:1 w:0)77	// Storage: Common Allowlist (r:0 w:1)78	fn add_to_allow_list() -> Weight {79		Weight::from_ref_time(18_238_000)80			.saturating_add(T::DbWeight::get().reads(1 as u64))81			.saturating_add(T::DbWeight::get().writes(1 as u64))82	}83	// Storage: Common CollectionById (r:1 w:0)84	// Storage: Common Allowlist (r:0 w:1)85	fn remove_from_allow_list() -> Weight {86		Weight::from_ref_time(18_084_000)87			.saturating_add(T::DbWeight::get().reads(1 as u64))88			.saturating_add(T::DbWeight::get().writes(1 as u64))89	}90	// Storage: Common CollectionById (r:1 w:1)91	fn change_collection_owner() -> Weight {92		Weight::from_ref_time(18_265_000)93			.saturating_add(T::DbWeight::get().reads(1 as u64))94			.saturating_add(T::DbWeight::get().writes(1 as u64))95	}96	// Storage: Common CollectionById (r:1 w:0)97	// Storage: Common IsAdmin (r:1 w:1)98	// Storage: Common AdminAmount (r:1 w:1)99	fn add_collection_admin() -> Weight {100		Weight::from_ref_time(23_558_000)101			.saturating_add(T::DbWeight::get().reads(3 as u64))102			.saturating_add(T::DbWeight::get().writes(2 as u64))103	}104	// Storage: Common CollectionById (r:1 w:0)105	// Storage: Common IsAdmin (r:1 w:1)106	// Storage: Common AdminAmount (r:1 w:1)107	fn remove_collection_admin() -> Weight {108		Weight::from_ref_time(25_285_000)109			.saturating_add(T::DbWeight::get().reads(3 as u64))110			.saturating_add(T::DbWeight::get().writes(2 as u64))111	}112	// Storage: Common CollectionById (r:1 w:1)113	fn set_collection_sponsor() -> Weight {114		Weight::from_ref_time(17_885_000)115			.saturating_add(T::DbWeight::get().reads(1 as u64))116			.saturating_add(T::DbWeight::get().writes(1 as u64))117	}118	// Storage: Common CollectionById (r:1 w:1)119	fn confirm_sponsorship() -> Weight {120		Weight::from_ref_time(17_897_000)121			.saturating_add(T::DbWeight::get().reads(1 as u64))122			.saturating_add(T::DbWeight::get().writes(1 as u64))123	}124	// Storage: Common CollectionById (r:1 w:1)125	fn remove_collection_sponsor() -> Weight {126		Weight::from_ref_time(17_836_000)127			.saturating_add(T::DbWeight::get().reads(1 as u64))128			.saturating_add(T::DbWeight::get().writes(1 as u64))129	}130	// Storage: Common CollectionById (r:1 w:1)131	fn set_transfers_enabled_flag() -> Weight {132		Weight::from_ref_time(9_714_000)133			.saturating_add(T::DbWeight::get().reads(1 as u64))134			.saturating_add(T::DbWeight::get().writes(1 as u64))135	}136	// Storage: Common CollectionById (r:1 w:1)137	fn set_collection_limits() -> Weight {138		Weight::from_ref_time(18_166_000)139			.saturating_add(T::DbWeight::get().reads(1 as u64))140			.saturating_add(T::DbWeight::get().writes(1 as u64))141	}142}143144// For backwards compatibility and tests145impl WeightInfo for () {146	// Storage: Common CreatedCollectionCount (r:1 w:1)147	// Storage: Common DestroyedCollectionCount (r:1 w:0)148	// Storage: System Account (r:2 w:2)149	// Storage: Common CollectionPropertyPermissions (r:0 w:1)150	// Storage: Common CollectionProperties (r:0 w:1)151	// Storage: Common CollectionById (r:0 w:1)152	fn create_collection() -> Weight {153		Weight::from_ref_time(43_143_000)154			.saturating_add(RocksDbWeight::get().reads(4 as u64))155			.saturating_add(RocksDbWeight::get().writes(6 as u64))156	}157	// Storage: Common CollectionById (r:1 w:1)158	// Storage: Nonfungible TokenData (r:1 w:0)159	// Storage: Common DestroyedCollectionCount (r:1 w:1)160	// Storage: Nonfungible TokensMinted (r:0 w:1)161	// Storage: Nonfungible TokensBurnt (r:0 w:1)162	// Storage: Common AdminAmount (r:0 w:1)163	// Storage: Common CollectionProperties (r:0 w:1)164	fn destroy_collection() -> Weight {165		Weight::from_ref_time(50_188_000)166			.saturating_add(RocksDbWeight::get().reads(3 as u64))167			.saturating_add(RocksDbWeight::get().writes(6 as u64))168	}169	// Storage: Common CollectionById (r:1 w:0)170	// Storage: Common Allowlist (r:0 w:1)171	fn add_to_allow_list() -> Weight {172		Weight::from_ref_time(18_238_000)173			.saturating_add(RocksDbWeight::get().reads(1 as u64))174			.saturating_add(RocksDbWeight::get().writes(1 as u64))175	}176	// Storage: Common CollectionById (r:1 w:0)177	// Storage: Common Allowlist (r:0 w:1)178	fn remove_from_allow_list() -> Weight {179		Weight::from_ref_time(18_084_000)180			.saturating_add(RocksDbWeight::get().reads(1 as u64))181			.saturating_add(RocksDbWeight::get().writes(1 as u64))182	}183	// Storage: Common CollectionById (r:1 w:1)184	fn change_collection_owner() -> Weight {185		Weight::from_ref_time(18_265_000)186			.saturating_add(RocksDbWeight::get().reads(1 as u64))187			.saturating_add(RocksDbWeight::get().writes(1 as u64))188	}189	// Storage: Common CollectionById (r:1 w:0)190	// Storage: Common IsAdmin (r:1 w:1)191	// Storage: Common AdminAmount (r:1 w:1)192	fn add_collection_admin() -> Weight {193		Weight::from_ref_time(23_558_000)194			.saturating_add(RocksDbWeight::get().reads(3 as u64))195			.saturating_add(RocksDbWeight::get().writes(2 as u64))196	}197	// Storage: Common CollectionById (r:1 w:0)198	// Storage: Common IsAdmin (r:1 w:1)199	// Storage: Common AdminAmount (r:1 w:1)200	fn remove_collection_admin() -> Weight {201		Weight::from_ref_time(25_285_000)202			.saturating_add(RocksDbWeight::get().reads(3 as u64))203			.saturating_add(RocksDbWeight::get().writes(2 as u64))204	}205	// Storage: Common CollectionById (r:1 w:1)206	fn set_collection_sponsor() -> Weight {207		Weight::from_ref_time(17_885_000)208			.saturating_add(RocksDbWeight::get().reads(1 as u64))209			.saturating_add(RocksDbWeight::get().writes(1 as u64))210	}211	// Storage: Common CollectionById (r:1 w:1)212	fn confirm_sponsorship() -> Weight {213		Weight::from_ref_time(17_897_000)214			.saturating_add(RocksDbWeight::get().reads(1 as u64))215			.saturating_add(RocksDbWeight::get().writes(1 as u64))216	}217	// Storage: Common CollectionById (r:1 w:1)218	fn remove_collection_sponsor() -> Weight {219		Weight::from_ref_time(17_836_000)220			.saturating_add(RocksDbWeight::get().reads(1 as u64))221			.saturating_add(RocksDbWeight::get().writes(1 as u64))222	}223	// Storage: Common CollectionById (r:1 w:1)224	fn set_transfers_enabled_flag() -> Weight {225		Weight::from_ref_time(9_714_000)226			.saturating_add(RocksDbWeight::get().reads(1 as u64))227			.saturating_add(RocksDbWeight::get().writes(1 as u64))228	}229	// Storage: Common CollectionById (r:1 w:1)230	fn set_collection_limits() -> Weight {231		Weight::from_ref_time(18_166_000)232			.saturating_add(RocksDbWeight::get().reads(1 as u64))233			.saturating_add(RocksDbWeight::get().writes(1 as u64))234	}235}
modifiedruntime/common/weights.rsdiffbeforeafterboth
--- a/runtime/common/weights.rs
+++ b/runtime/common/weights.rs
@@ -125,8 +125,8 @@
 		max_weight_of!(set_allowance_for_all())
 	}
 
-	fn repair_item() -> Weight {
-		max_weight_of!(repair_item())
+	fn force_repair_item() -> Weight {
+		max_weight_of!(force_repair_item())
 	}
 }
 
modifiedtests/package.jsondiffbeforeafterboth
--- a/tests/package.json
+++ b/tests/package.json
@@ -46,6 +46,7 @@
     "testUnnesting": "mocha --timeout 9999999 -r ts-node/register ./**/unnest.test.ts",
     "testProperties": "mocha --timeout 9999999 -r ts-node/register ./**/collectionProperties.test.ts ./**/tokenProperties.test.ts ./**/getPropertiesRpc.test.ts",
     "testCollectionProperties": "mocha --timeout 9999999 -r ts-node/register ./**/collectionProperties.test.ts",
+    "testTokenProperties": "mocha --timeout 9999999 -r ts-node/register ./**/tokenProperties.test.ts",
     "testMigration": "mocha --timeout 9999999 -r ts-node/register ./**/nesting/migration-check.test.ts",
     "testAddCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/addCollectionAdmin.test.ts",
     "testSetCollectionLimits": "mocha --timeout 9999999 -r ts-node/register ./**/setCollectionLimits.test.ts",
modifiedtests/src/nesting/collectionProperties.test.tsdiffbeforeafterboth
--- a/tests/src/nesting/collectionProperties.test.ts
+++ b/tests/src/nesting/collectionProperties.test.ts
@@ -18,11 +18,13 @@
 import {itSub, Pallets, usingPlaygrounds, expect, requirePalletsOrSkip} from '../util';
 
 describe('Integration Test: Collection Properties', () => {
+  let superuser: IKeyringPair;
   let alice: IKeyringPair;
   let bob: IKeyringPair;
   
   before(async () => {
     await usingPlaygrounds(async (helper, privateKey) => {
+      superuser = await privateKey('//Alice');
       const donor = await privateKey({filename: __filename});
       [alice, bob] = await helper.arrange.createAccounts([200n, 10n], donor);
     });
@@ -199,6 +201,23 @@
       expectedConsumedSpaceDiff = biggerPropDataSize - smallerPropDataSize;
       expect(consumedSpace).to.be.equal(biggerPropDataSize - expectedConsumedSpaceDiff);
     });
+
+    itSub('Modifying a collection property with different sizes correctly changes the consumed space', async({helper}) => {
+      const properties = [
+        {key: 'sea-creatures', value: 'mermaids'},
+        {key: 'goldenratio', value: '1.6180339887498948482045868343656381177203091798057628621354486227052604628189'},
+      ];
+      const collection = await helper[testSuite.mode].mintCollection(alice, {properties});
+
+      const newProperty = ' '.repeat(4096);
+      await collection.setProperties(alice, [{key: 'space', value: newProperty}]);
+      const originalSpace = await collection.getPropertiesConsumedSpace();
+      expect(originalSpace).to.be.equal(properties[0].value.length + properties[1].value.length + newProperty.length);
+
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.unique.forceRepairCollection', [collection.collectionId], true);
+      const recomputedSpace = await collection.getPropertiesConsumedSpace();
+      expect(recomputedSpace).to.be.equal(originalSpace);
+    });
   }));
 });
   
@@ -314,6 +333,16 @@
         ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);
       }
     });
+
+    itSub('Modifying a collection property with different sizes correctly changes the consumed space', async({helper}) => {
+      const collection = await helper[testSuite.mode].mintCollection(alice, {properties: [
+        {key: 'sea-creatures', value: 'mermaids'},
+        {key: 'goldenratio', value: '1.6180339887498948482045868343656381177203091798057628621354486227052604628189'},
+      ]});
+
+      await expect(helper.executeExtrinsic(alice, 'api.tx.unique.forceRepairCollection', [collection.collectionId], true))
+        .to.be.rejectedWith(/BadOrigin/);
+    });
   }));
 });
   
\ No newline at end of file
modifiedtests/src/nesting/tokenProperties.test.tsdiffbeforeafterboth
--- a/tests/src/nesting/tokenProperties.test.ts
+++ b/tests/src/nesting/tokenProperties.test.ts
@@ -19,6 +19,7 @@
 import {UniqueHelper, UniqueNFToken, UniqueRFToken} from '../util/playgrounds/unique';
 
 describe('Integration Test: Token Properties', () => {
+  let superuser: IKeyringPair;
   let alice: IKeyringPair; // collection owner
   let bob: IKeyringPair; // collection admin
   let charlie: IKeyringPair; // token owner
@@ -27,6 +28,7 @@
 
   before(async () => {
     await usingPlaygrounds(async (helper, privateKey) => {
+      superuser = await privateKey('//Alice');
       const donor = await privateKey({filename: __filename});
       [alice, bob, charlie] = await helper.arrange.createAccounts([200n, 100n, 100n], donor);
     });
@@ -406,7 +408,7 @@
     {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}) => {
+    itSub.ifWithPallets(`force_repair_item preserves valid consumed space (${testCase.mode})`, testCase.requiredPallets, async({helper}) => {
       const propKey = 'tok-prop';
 
       const collection = await helper[testCase.mode].mintCollection(alice, {
@@ -430,7 +432,7 @@
       const originalSpace = await token.getTokenPropertiesConsumedSpace();
       expect(originalSpace).to.be.equal(propDataSize);
 
-      await helper.executeExtrinsic(alice, 'api.tx.unique.repairItem', [token.collectionId, token.tokenId], true);
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.unique.forceRepairItem', [token.collectionId, token.tokenId], true);
       const recomputedSpace = await token.getTokenPropertiesConsumedSpace();
       expect(recomputedSpace).to.be.equal(originalSpace);
     }));
@@ -697,6 +699,35 @@
         permission: {mutable: true, tokenOwner: true, collectionAdmin: true},
       }])).to.be.rejectedWith(/common\.PropertyLimitReached/);
     }));
+
+  [
+    {mode: 'nft' as const, pieces: undefined, requiredPallets: []},
+    {mode: 'rft' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]}, 
+  ].map(testCase =>
+    itSub.ifWithPallets(`Forbids force_repair_item from non-sudo (${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}]);
+
+      await expect(helper.executeExtrinsic(alice, 'api.tx.unique.forceRepairItem', [token.collectionId, token.tokenId], true))
+        .to.be.rejectedWith(/BadOrigin/);
+    }));
 });
 
 describe('ReFungible token properties permissions tests', () => {