difftreelog
Merge pull request #773 from UniqueNetwork/fix/properties-size-overflow
in: master
Fix/properties size overflow
18 files changed
pallets/common/src/lib.rsdiffbeforeafterboth1816 /// The price of setting approval for all1816 /// The price of setting approval for all1817 fn set_allowance_for_all() -> Weight;1817 fn set_allowance_for_all() -> Weight;18181819 /// The price of repairing an item.1820 fn repair_item() -> Weight;1818}1821}181918221820/// Weight info extension trait for refungible pallet.1823/// Weight info extension trait for refungible pallet.2137 /// Tells whether the given `owner` approves the `operator`.2140 /// Tells whether the given `owner` approves the `operator`.2138 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;2141 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;21422143 /// Repairs a possibly broken item.2144 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2139}2145}214021462141/// Extension for RFT collection.2147/// Extension for RFT collection.pallets/fungible/src/common.rsdiffbeforeafterboth112 Weight::zero()112 Weight::zero()113 }113 }114115 fn repair_item() -> Weight {116 Weight::zero()117 }114}118}115119116/// Implementation of `CommonCollectionOperations` for `FungibleHandle`. It wraps FungibleHandle Pallete120/// Implementation of `CommonCollectionOperations` for `FungibleHandle`. It wraps FungibleHandle Pallete442 false446 false443 }447 }448449 /// Repairs a possibly broken item.450 fn repair_item(&self, _token: TokenId) -> DispatchResultWithPostInfo {451 fail!(<Error<T>>::FungibleTokensAreAlwaysValid)452 }444}453}445454pallets/fungible/src/lib.rsdiffbeforeafterboth129 SettingPropertiesNotAllowed,129 SettingPropertiesNotAllowed,130 /// Setting allowance for all is not allowed.130 /// Setting allowance for all is not allowed.131 SettingAllowanceForAllNotAllowed,131 SettingAllowanceForAllNotAllowed,132 /// Only a fungible collection could be possibly broken; any fungible token is valid.133 FungibleTokensAreAlwaysValid,132 }134 }133135134 #[pallet::config]136 #[pallet::config]pallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth237 };237 };238 }: {<Pallet<T>>::allowance_for_all(&collection, &owner, &operator)}238 }: {<Pallet<T>>::allowance_for_all(&collection, &owner, &operator)}239240 repair_item {241 bench_init!{242 owner: sub; collection: collection(owner);243 owner: cross_from_sub;244 };245 let item = create_max_item(&collection, &owner, owner.clone())?;246 }: {<Pallet<T>>::repair_item(&collection, item)?}239}247}240248pallets/nonfungible/src/common.rsdiffbeforeafterboth127 <SelfWeightOf<T>>::set_allowance_for_all()127 <SelfWeightOf<T>>::set_allowance_for_all()128 }128 }129130 fn repair_item() -> Weight {131 <SelfWeightOf<T>>::repair_item()132 }129}133}130134131fn map_create_data<T: Config>(135fn map_create_data<T: Config>(533 <Pallet<T>>::allowance_for_all(self, &owner, &operator)537 <Pallet<T>>::allowance_for_all(self, &owner, &operator)534 }538 }539540 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo {541 with_weight(542 <Pallet<T>>::repair_item(self, token),543 <CommonWeights<T>>::repair_item(),544 )545 }535}546}536547pallets/nonfungible/src/lib.rsdiffbeforeafterboth1399 <CollectionAllowance<T>>::get((collection.id, owner, operator))1399 <CollectionAllowance<T>>::get((collection.id, owner, operator))1400 }1400 }14011402 pub fn repair_item(collection: &NonfungibleHandle<T>, token: TokenId) -> DispatchResult {1403 <TokenProperties<T>>::mutate((collection.id, token), |properties| {1404 properties.recompute_consumed_space();1405 });14061407 Ok(())1408 }1401}1409}14021410pallets/nonfungible/src/weights.rsdiffbeforeafterboth50 fn token_owner() -> Weight;50 fn token_owner() -> Weight;51 fn set_allowance_for_all() -> Weight;51 fn set_allowance_for_all() -> Weight;52 fn allowance_for_all() -> Weight;52 fn allowance_for_all() -> Weight;53 fn repair_item() -> Weight;53}54}545555/// Weights for pallet_nonfungible using the Substrate node and recommended hardware.56/// Weights for pallet_nonfungible using the Substrate node and recommended hardware.208 Weight::from_ref_time(6_161_000 as u64)209 Weight::from_ref_time(6_161_000 as u64)209 .saturating_add(T::DbWeight::get().reads(1 as u64))210 .saturating_add(T::DbWeight::get().reads(1 as u64))210 }211 }212 // Storage: Nonfungible TokenProperties (r:1 w:1)213 fn repair_item() -> Weight {214 Weight::from_ref_time(5_701_000 as u64)215 .saturating_add(T::DbWeight::get().reads(1 as u64))216 .saturating_add(T::DbWeight::get().writes(1 as u64))217 }211}218}212219213// For backwards compatibility and tests220// For backwards compatibility and tests365 Weight::from_ref_time(6_161_000 as u64)372 Weight::from_ref_time(6_161_000 as u64)366 .saturating_add(RocksDbWeight::get().reads(1 as u64))373 .saturating_add(RocksDbWeight::get().reads(1 as u64))367 }374 }375 // Storage: Nonfungible TokenProperties (r:1 w:1)376 fn repair_item() -> Weight {377 Weight::from_ref_time(5_701_000 as u64)378 .saturating_add(RocksDbWeight::get().reads(1 as u64))379 .saturating_add(RocksDbWeight::get().writes(1 as u64))380 }368}381}369382pallets/refungible/src/benchmarking.rsdiffbeforeafterboth305 };305 };306 }: {<Pallet<T>>::allowance_for_all(&collection, &owner, &operator)}306 }: {<Pallet<T>>::allowance_for_all(&collection, &owner, &operator)}307308 repair_item {309 bench_init!{310 owner: sub; collection: collection(owner);311 owner: cross_from_sub;312 };313 let item = create_max_item(&collection, &owner, [(owner.clone(), 100)])?;314 }: {<Pallet<T>>::repair_item(&collection, item)?}307}315}308316pallets/refungible/src/common.rsdiffbeforeafterboth157 <SelfWeightOf<T>>::set_allowance_for_all()157 <SelfWeightOf<T>>::set_allowance_for_all()158 }158 }159160 fn repair_item() -> Weight {161 <SelfWeightOf<T>>::repair_item()162 }159}163}160164161fn map_create_data<T: Config>(165fn map_create_data<T: Config>(537 <Pallet<T>>::allowance_for_all(self, &owner, &operator)541 <Pallet<T>>::allowance_for_all(self, &owner, &operator)538 }542 }543544 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo {545 with_weight(546 <Pallet<T>>::repair_item(self, token),547 <CommonWeights<T>>::repair_item(),548 )549 }539}550}540551541impl<T: Config> RefungibleExtensions<T> for RefungibleHandle<T> {552impl<T: Config> RefungibleExtensions<T> for RefungibleHandle<T> {pallets/refungible/src/lib.rsdiffbeforeafterboth1462 <CollectionAllowance<T>>::get((collection.id, owner, operator))1462 <CollectionAllowance<T>>::get((collection.id, owner, operator))1463 }1463 }14641465 pub fn repair_item(collection: &RefungibleHandle<T>, token: TokenId) -> DispatchResult {1466 <TokenProperties<T>>::mutate((collection.id, token), |properties| {1467 properties.recompute_consumed_space();1468 });14691470 Ok(())1471 }1464}1472}14651473pallets/refungible/src/weights.rsdiffbeforeafterboth57 fn token_owner() -> Weight;57 fn token_owner() -> Weight;58 fn set_allowance_for_all() -> Weight;58 fn set_allowance_for_all() -> Weight;59 fn allowance_for_all() -> Weight;59 fn allowance_for_all() -> Weight;60 fn repair_item() -> Weight;60}61}616262/// Weights for pallet_refungible using the Substrate node and recommended hardware.63/// Weights for pallet_refungible using the Substrate node and recommended hardware.272 Weight::from_ref_time(5_901_000 as u64)273 Weight::from_ref_time(5_901_000 as u64)273 .saturating_add(T::DbWeight::get().reads(1 as u64))274 .saturating_add(T::DbWeight::get().reads(1 as u64))274 }275 }276 // Storage: Refungible TokenProperties (r:1 w:1)277 fn repair_item() -> Weight {278 Weight::from_ref_time(5_489_000 as u64)279 .saturating_add(T::DbWeight::get().reads(1 as u64))280 .saturating_add(T::DbWeight::get().writes(1 as u64))281 }275}282}276283277// For backwards compatibility and tests284// For backwards compatibility and tests486 Weight::from_ref_time(5_901_000 as u64)493 Weight::from_ref_time(5_901_000 as u64)487 .saturating_add(RocksDbWeight::get().reads(1 as u64))494 .saturating_add(RocksDbWeight::get().reads(1 as u64))488 }495 }496 // Storage: Refungible TokenProperties (r:1 w:1)497 fn repair_item() -> Weight {498 Weight::from_ref_time(5_489_000 as u64)499 .saturating_add(RocksDbWeight::get().reads(1 as u64))500 .saturating_add(RocksDbWeight::get().writes(1 as u64))501 }489}502}490503pallets/unique/src/lib.rsdiffbeforeafterboth983 })983 })984 }984 }985986 /// Repairs a broken item987 ///988 /// # Arguments989 ///990 /// * `collection_id`: ID of the collection the item belongs to.991 /// * `item_id`: ID of the item.992 #[weight = T::CommonWeightInfo::repair_item()]993 pub fn repair_item(994 _origin,995 collection_id: CollectionId,996 item_id: TokenId,997 ) -> DispatchResultWithPostInfo {998 dispatch_tx::<T, _>(collection_id, |d| {999 d.repair_item(item_id)1000 })1001 }985 }1002 }986}1003}9871004primitives/data-structs/src/lib.rsdiffbeforeafterboth1133 scope: PropertyScope,1133 scope: PropertyScope,1134 key: PropertyKey,1134 key: PropertyKey,1135 value: Self::Value,1135 value: Self::Value,1136 ) -> Result<(), PropertiesError>;1136 ) -> Result<Option<Self::Value>, PropertiesError>;113711371138 /// Try to set property with scope from iterator.1138 /// Try to set property with scope from iterator.1139 fn try_scoped_set_from_iter<I, KV>(1139 fn try_scoped_set_from_iter<I, KV>(1154 }1154 }115511551156 /// Try to set property.1156 /// Try to set property.1157 fn try_set(&mut self, key: PropertyKey, value: Self::Value) -> Result<(), PropertiesError> {1157 fn try_set(1158 &mut self,1159 key: PropertyKey,1160 value: Self::Value,1161 ) -> Result<Option<Self::Value>, PropertiesError> {1158 self.try_scoped_set(PropertyScope::None, key, value)1162 self.try_scoped_set(PropertyScope::None, key, value)1159 }1163 }116011641215 Ok(())1219 Ok(())1216 }1220 }12211222 pub fn values(&self) -> impl Iterator<Item = &Value> {1223 self.0.values()1224 }1217}1225}121812261219impl<Value> IntoIterator for PropertiesMap<Value> {1227impl<Value> IntoIterator for PropertiesMap<Value> {1239 scope: PropertyScope,1247 scope: PropertyScope,1240 key: PropertyKey,1248 key: PropertyKey,1241 value: Self::Value,1249 value: Self::Value,1242 ) -> Result<(), PropertiesError> {1250 ) -> Result<Option<Self::Value>, PropertiesError> {1243 Self::check_property_key(&key)?;1251 Self::check_property_key(&key)?;124412521245 let key = scope.apply(key)?;1253 let key = scope.apply(key)?;1246 self.01254 self.01247 .try_insert(key, value)1255 .try_insert(key, value)1248 .map_err(|_| PropertiesError::PropertyLimitReached)?;1256 .map_err(|_| PropertiesError::PropertyLimitReached)12491250 Ok(())1251 }1257 }1252}1258}125312591289 self.map.get(key)1295 self.map.get(key)1290 }1296 }12971298 /// Recomputes the consumed space for the current properties state.1299 /// Needed to repair a token due to a bug fixed in the [PR #733](https://github.com/UniqueNetwork/unique-chain/pull/773).1300 pub fn recompute_consumed_space(&mut self) {1301 self.consumed_space = self.map.values().map(|value| value.len() as u32).sum();1302 }1291}1303}129213041293impl IntoIterator for Properties {1305impl IntoIterator for Properties {1307 scope: PropertyScope,1319 scope: PropertyScope,1308 key: PropertyKey,1320 key: PropertyKey,1309 value: Self::Value,1321 value: Self::Value,1310 ) -> Result<(), PropertiesError> {1322 ) -> Result<Option<Self::Value>, PropertiesError> {1311 let value_len = value.len();1323 let value_len = value.len();131213241313 if self.consumed_space as usize + value_len > self.space_limit as usize1325 if self.consumed_space as usize + value_len > self.space_limit as usize1316 return Err(PropertiesError::NoSpaceForProperty);1328 return Err(PropertiesError::NoSpaceForProperty);1317 }1329 }131813301319 self.map.try_scoped_set(scope, key, value)?;1331 let old_value = self.map.try_scoped_set(scope, key, value)?;132013321333 if old_value.is_none() {1321 self.consumed_space += value_len as u32;1334 self.consumed_space += value_len as u32;1335 }132213361323 Ok(())1337 Ok(old_value)1324 }1338 }1325}1339}13261340runtime/common/weights.rsdiffbeforeafterboth125 max_weight_of!(set_allowance_for_all())125 max_weight_of!(set_allowance_for_all())126 }126 }127128 fn repair_item() -> Weight {129 max_weight_of!(repair_item())130 }127}131}128132129#[cfg(feature = "refungible")]133#[cfg(feature = "refungible")]tests/package.jsondiffbeforeafterboth41 "testEvent": "yarn setup && mocha --timeout 9999999 -r ts-node/register ./src/check-event/*.*test.ts",41 "testEvent": "yarn setup && mocha --timeout 9999999 -r ts-node/register ./src/check-event/*.*test.ts",42 "testRmrk": "yarn setup && mocha --timeout 9999999 -r ts-node/register ./**/rmrk/*.*test.ts",42 "testRmrk": "yarn setup && mocha --timeout 9999999 -r ts-node/register ./**/rmrk/*.*test.ts",43 "testEthPayable": "mocha --timeout 9999999 -r ts-node/register './**/eth/payable.test.ts'",43 "testEthPayable": "mocha --timeout 9999999 -r ts-node/register './**/eth/payable.test.ts'",44 "testEthTokenProperties": "mocha --timeout 9999999 -r ts-node/register ./**/eth/tokenProperties.test.ts",45 "testEvmCoder": "mocha --timeout 9999999 -r ts-node/register './**/eth/evmCoder.test.ts'",44 "testEvmCoder": "mocha --timeout 9999999 -r ts-node/register './**/eth/evmCoder.test.ts'",46 "testNesting": "mocha --timeout 9999999 -r ts-node/register ./**/nest.test.ts",45 "testNesting": "mocha --timeout 9999999 -r ts-node/register ./**/nest.test.ts",47 "testUnnesting": "mocha --timeout 9999999 -r ts-node/register ./**/unnest.test.ts",46 "testUnnesting": "mocha --timeout 9999999 -r ts-node/register ./**/unnest.test.ts",48 "testProperties": "mocha --timeout 9999999 -r ts-node/register ./**/properties.test.ts ./**/getPropertiesRpc.test.ts",47 "testProperties": "mocha --timeout 9999999 -r ts-node/register ./**/collectionProperties.test.ts ./**/tokenProperties.test.ts ./**/getPropertiesRpc.test.ts",49 "testMigration": "mocha --timeout 9999999 -r ts-node/register ./**/nesting/migration-check.test.ts",48 "testMigration": "mocha --timeout 9999999 -r ts-node/register ./**/nesting/migration-check.test.ts",50 "testAddCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/addCollectionAdmin.test.ts",49 "testAddCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/addCollectionAdmin.test.ts",51 "testSetCollectionLimits": "mocha --timeout 9999999 -r ts-node/register ./**/setCollectionLimits.test.ts",50 "testSetCollectionLimits": "mocha --timeout 9999999 -r ts-node/register ./**/setCollectionLimits.test.ts",tests/src/nesting/collectionProperties.test.tsdiffbeforeafterboth25 before(async () => {25 before(async () => {26 await usingPlaygrounds(async (helper, privateKey) => {26 await usingPlaygrounds(async (helper, privateKey) => {27 const donor = await privateKey({filename: __filename});27 const donor = await privateKey({filename: __filename});28 [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor);28 [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor);29 });29 });30 });30 });31 31 132 await testDeleteProperties(await helper.rft.mintCollection(alice));132 await testDeleteProperties(await helper.rft.mintCollection(alice));133 });133 });134135 [136 // TODO enable properties for FT collection in Substrate (release 040)137 // {mode: 'ft' as const, requiredPallets: []},138 {mode: 'nft' as const, requiredPallets: []},139 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, 140 ].map(testCase =>141 itSub.ifWithPallets(`Allows modifying a collection property multiple times (${testCase.mode})`, testCase.requiredPallets, async({helper}) => {142 const propKey = 'tok-prop';143144 const collection = await helper[testCase.mode].mintCollection(alice);145146 const maxCollectionPropertiesSize = 40960;147148 const propDataSize = 4096;149150 let propDataChar = 'a';151 const makeNewPropData = () => {152 propDataChar = String.fromCharCode(propDataChar.charCodeAt(0) + 1);153 return `${propDataChar}`.repeat(propDataSize);154 };155156 await collection.setProperties(alice, [{key: propKey, value: makeNewPropData()}]);157 const originalSpace = await collection.getPropertiesConsumedSpace();158 expect(originalSpace).to.be.equal(propDataSize);159160 const sameSizePropertiesPossibleNum = maxCollectionPropertiesSize / propDataSize;161162 // It is possible to modify a property as many times as needed.163 // It will not consume any additional space.164 for (let i = 0; i < sameSizePropertiesPossibleNum + 1; i++) {165 await collection.setProperties(alice, [{key: propKey, value: makeNewPropData()}]);166 const consumedSpace = await collection.getPropertiesConsumedSpace();167 expect(consumedSpace).to.be.equal(originalSpace);168 }169 }));170171 [172 // TODO enable properties for FT collection in Substrate (release 040)173 // {mode: 'ft' as const, requiredPallets: []},174 {mode: 'nft' as const, requiredPallets: []},175 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, 176 ].map(testCase =>177 itSub.ifWithPallets(`Adding then removing a collection property doesn't change the consumed space (${testCase.mode})`, testCase.requiredPallets, async({helper}) => {178 const propKey = 'tok-prop';179180 const collection = await helper[testCase.mode].mintCollection(alice);181 const originalSpace = await collection.getPropertiesConsumedSpace();182183 const propDataSize = 4096;184 const propData = 'a'.repeat(propDataSize);185186 await collection.setProperties(alice, [{key: propKey, value: propData}]);187 let consumedSpace = await collection.getPropertiesConsumedSpace();188 expect(consumedSpace).to.be.equal(propDataSize);189190 await collection.deleteProperties(alice, [propKey]);191 consumedSpace = await collection.getPropertiesConsumedSpace();192 expect(consumedSpace).to.be.equal(originalSpace);193 }));134});194});135 195 136describe('Negative Integration Test: Collection Properties', () => {196describe('Negative Integration Test: Collection Properties', () => {tests/src/nesting/tokenProperties.test.tsdiffbeforeafterboth28 before(async () => {28 before(async () => {29 await usingPlaygrounds(async (helper, privateKey) => {29 await usingPlaygrounds(async (helper, privateKey) => {30 const donor = await privateKey({filename: __filename});30 const donor = await privateKey({filename: __filename});31 [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);31 [alice, bob, charlie] = await helper.arrange.createAccounts([200n, 100n, 100n], donor);32 });32 });333334 permissions = [34 permissions = [321 expect(await targetToken.getProperties()).to.be.empty;321 expect(await targetToken.getProperties()).to.be.empty;322 });322 });323324 [325 {mode: 'nft' as const, storage: 'nonfungible' as const, pieces: undefined, requiredPallets: []},326 {mode: 'rft' as const, storage: 'refungible' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]}, 327 ].map(testCase =>328 itSub.ifWithPallets(`Allows modifying a token property multiple times (${testCase.mode})`, testCase.requiredPallets, async({helper}) => {329 const propKey = 'tok-prop';330331 const collection = await helper[testCase.mode].mintCollection(alice, {332 tokenPropertyPermissions: [333 {334 key: propKey,335 permission: {mutable: true, tokenOwner: true},336 },337 ],338 });339340 const maxTokenPropertiesSize = 32768;341342 const propDataSize = 4096;343344 let propDataChar = 'a';345 const makeNewPropData = () => {346 propDataChar = String.fromCharCode(propDataChar.charCodeAt(0) + 1);347 return `${propDataChar}`.repeat(propDataSize);348 };349350 const token = await (351 testCase.pieces352 ? collection.mintToken(alice, testCase.pieces)353 : collection.mintToken(alice)354 );355356 await token.setProperties(alice, [{key: propKey, value: makeNewPropData()}]);357 const originalSpace = await token.getTokenPropertiesConsumedSpace();358 expect(originalSpace).to.be.equal(propDataSize);359360 const sameSizePropertiesPossibleNum = maxTokenPropertiesSize / propDataSize;361362 // It is possible to modify a property as many times as needed.363 // It will not consume any additional space.364 for (let i = 0; i < sameSizePropertiesPossibleNum + 1; i++) {365 await token.setProperties(alice, [{key: propKey, value: makeNewPropData()}]);366 const consumedSpace = await token.getTokenPropertiesConsumedSpace();367 expect(consumedSpace).to.be.equal(originalSpace);368 }369 }));370371 [372 {mode: 'nft' as const, pieces: undefined, requiredPallets: []},373 {mode: 'rft' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]}, 374 ].map(testCase =>375 itSub.ifWithPallets(`Adding then removing a token property doesn't change the consumed space (${testCase.mode})`, testCase.requiredPallets, async({helper}) => {376 const propKey = 'tok-prop';377378 const collection = await helper[testCase.mode].mintCollection(alice, {379 tokenPropertyPermissions: [380 {381 key: propKey,382 permission: {mutable: true, tokenOwner: true},383 },384 ],385 });386 const token = await (387 testCase.pieces388 ? collection.mintToken(alice, testCase.pieces)389 : collection.mintToken(alice)390 );391 const originalSpace = await token.getTokenPropertiesConsumedSpace();392393 const propDataSize = 4096;394 const propData = 'a'.repeat(propDataSize);395396 await token.setProperties(alice, [{key: propKey, value: propData}]);397 let consumedSpace = await token.getTokenPropertiesConsumedSpace();398 expect(consumedSpace).to.be.equal(propDataSize);399400 await token.deleteProperties(alice, [propKey]);401 consumedSpace = await token.getTokenPropertiesConsumedSpace();402 expect(consumedSpace).to.be.equal(originalSpace);403 }));404405 [406 {mode: 'nft' as const, pieces: undefined, requiredPallets: []},407 {mode: 'rft' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]}, 408 ].map(testCase =>409 itSub.ifWithPallets(`repair_item preserves valid consumed space (${testCase.mode})`, testCase.requiredPallets, async({helper}) => {410 const propKey = 'tok-prop';411412 const collection = await helper[testCase.mode].mintCollection(alice, {413 tokenPropertyPermissions: [414 {415 key: propKey,416 permission: {mutable: true, tokenOwner: true},417 },418 ],419 });420 const token = await (421 testCase.pieces422 ? collection.mintToken(alice, testCase.pieces)423 : collection.mintToken(alice)424 );425426 const propDataSize = 4096;427 const propData = 'a'.repeat(propDataSize);428429 await token.setProperties(alice, [{key: propKey, value: propData}]);430 const originalSpace = await token.getTokenPropertiesConsumedSpace();431 expect(originalSpace).to.be.equal(propDataSize);432433 await helper.executeExtrinsic(alice, 'api.tx.unique.repairItem', [token.collectionId, token.tokenId], true);434 const recomputedSpace = await token.getTokenPropertiesConsumedSpace();435 expect(recomputedSpace).to.be.equal(originalSpace);436 }));323});437});324438325describe('Negative Integration Test: Token Properties', () => {439describe('Negative Integration Test: Token Properties', () => {476 await testForbidsAddingPropertiesIfPropertyNotDeclared(token, amount);590 await testForbidsAddingPropertiesIfPropertyNotDeclared(token, amount);477 });591 });478592479 async function testForbidsAddingTooManyProperties(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {593 async function testForbidsAddingTooLargeProperties(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {480 const originalSpace = await prepare(token, pieces);594 const originalSpace = await prepare(token, pieces);481595482 await expect(596 await expect(504 expect(consumedSpace).to.be.equal(originalSpace);618 expect(consumedSpace).to.be.equal(originalSpace);505 }619 }506620507 itSub('Forbids adding too many properties to a token (NFT)', async ({helper}) => {621 itSub('Forbids adding too large properties to a token (NFT)', async ({helper}) => {508 const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT');622 const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT');509 await testForbidsAddingTooManyProperties(token, amount);623 await testForbidsAddingTooLargeProperties(token, amount);510 });624 });511625512 itSub.ifWithPallets('Forbids adding too many properties to a token (ReFungible)', [Pallets.ReFungible], async ({helper}) => {626 itSub.ifWithPallets('Forbids adding too large properties to a token (ReFungible)', [Pallets.ReFungible], async ({helper}) => {513 const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT');627 const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT');514 await testForbidsAddingTooManyProperties(token, amount);628 await testForbidsAddingTooLargeProperties(token, amount);515 });629 });630631 [632 {mode: 'nft' as const, requiredPallets: []},633 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, 634 ].map(testCase =>635 itSub.ifWithPallets(`Forbids adding too many propeties to a token (${testCase.mode})`, testCase.requiredPallets, async({helper}) => {636 const collection = await helper[testCase.mode].mintCollection(alice);637 const maxPropertiesPerItem = 64;638639 for (let i = 0; i < maxPropertiesPerItem; i++) {640 await collection.setTokenPropertyPermissions(alice, [{641 key: `${i+1}`,642 permission: {mutable: true, tokenOwner: true, collectionAdmin: true},643 }]);644 }645646 await expect(collection.setTokenPropertyPermissions(alice, [{647 key: `${maxPropertiesPerItem}-th`,648 permission: {mutable: true, tokenOwner: true, collectionAdmin: true},649 }])).to.be.rejectedWith(/common\.PropertyLimitReached/);650 }));516});651});517652518describe('ReFungible token properties permissions tests', () => {653describe('ReFungible token properties permissions tests', () => {tests/src/util/playgrounds/unique.tsdiffbeforeafterboth1100 return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, propertyKeys])).toHuman();1100 return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, propertyKeys])).toHuman();1101 }1101 }11021103 async getPropertiesConsumedSpace(collectionId: number): Promise<number> {1104 const api = this.helper.getApi();1105 const props = (await api.query.common.collectionProperties(collectionId)).toJSON();1106 1107 return (props! as any).consumedSpace;1108 }110211091103 async getCollectionOptions(collectionId: number) {1110 async getCollectionOptions(collectionId: number) {1104 return (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1111 return (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();3035 return await this.helper.collection.getProperties(this.collectionId, propertyKeys);3042 return await this.helper.collection.getProperties(this.collectionId, propertyKeys);3036 }3043 }30443045 async getPropertiesConsumedSpace() {3046 return await this.helper.collection.getPropertiesConsumedSpace(this.collectionId);3047 }303730483038 async getTokenNextSponsored(tokenId: number, addressObj: ICrossAccountId) {3049 async getTokenNextSponsored(tokenId: number, addressObj: ICrossAccountId) {3039 return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj);3050 return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj);3158 return await this.helper.nft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3169 return await this.helper.nft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3159 }3170 }31713172 async getTokenPropertiesConsumedSpace(tokenId: number): Promise<number> {3173 const api = this.helper.getApi();3174 const props = (await api.query.nonfungible.tokenProperties(this.collectionId, tokenId)).toJSON();3175 3176 return (props! as any).consumedSpace;3177 }316031783161 async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId) {3179 async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId) {3162 return await this.helper.nft.transferToken(signer, this.collectionId, tokenId, addressObj);3180 return await this.helper.nft.transferToken(signer, this.collectionId, tokenId, addressObj);3269 return await this.helper.rft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3287 return await this.helper.rft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3270 }3288 }32893290 async getTokenPropertiesConsumedSpace(tokenId: number): Promise<number> {3291 const api = this.helper.getApi();3292 const props = (await api.query.refungible.tokenProperties(this.collectionId, tokenId)).toJSON();3293 3294 return (props! as any).consumedSpace;3295 }327132963272 async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount=1n) {3297 async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount=1n) {3273 return await this.helper.rft.transferToken(signer, this.collectionId, tokenId, addressObj, amount);3298 return await this.helper.rft.transferToken(signer, this.collectionId, tokenId, addressObj, amount);3421 return await this.collection.getTokenProperties(this.tokenId, propertyKeys);3446 return await this.collection.getTokenProperties(this.tokenId, propertyKeys);3422 }3447 }34483449 async getTokenPropertiesConsumedSpace() {3450 return await this.collection.getTokenPropertiesConsumedSpace(this.tokenId);3451 }342334523424 async setProperties(signer: TSigner, properties: IProperty[]) {3453 async setProperties(signer: TSigner, properties: IProperty[]) {3425 return await this.collection.setTokenProperties(signer, this.tokenId, properties);3454 return await this.collection.setTokenProperties(signer, this.tokenId, properties);