git.delta.rocks / unique-network / refs/commits / 6ad82a288c79

difftreelog

fix disallow nesting under non-existing tokens

Yaroslav Bolyukin2023-01-18parent: #d3f529b.patch.diff
in: master

3 files changed

modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -1332,7 +1332,10 @@
 		let permissive = nesting.permissive;
 
 		if permissive {
-			// Pass
+			ensure!(
+				<TokenData<T>>::contains_key((handle.id, under)),
+				<CommonError<T>>::TokenNotFound
+			);
 		} else if nesting.token_owner
 			&& <PalletStructure<T>>::check_indirectly_owned(
 				sender.clone(),
@@ -1341,9 +1344,12 @@
 				Some(from),
 				nesting_budget,
 			)? {
-			// Pass
+			// Pass, token existence is checked in `check_indirectly_owned`
 		} else if nesting.collection_admin && handle.is_owner_or_admin(&sender) {
-			// Pass
+			ensure!(
+				<TokenData<T>>::contains_key((handle.id, under)),
+				<CommonError<T>>::TokenNotFound
+			);
 		} else {
 			fail!(<CommonError<T>>::UserIsNotAllowedToNest);
 		}
modifiedpallets/structure/Cargo.tomldiffbeforeafterboth
--- a/pallets/structure/Cargo.toml
+++ b/pallets/structure/Cargo.toml
@@ -17,19 +17,10 @@
 ] }
 up-data-structs = { path = "../../primitives/data-structs", default-features = false }
 pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.36" }
+log = { version = "0.4.17", default-features = false }
 
 [features]
 default = ["std"]
-std = [
-	"frame-support/std",
-	"frame-system/std",
-	"frame-benchmarking/std",
-	"sp-std/std",
-	"pallet-common/std",
-	"scale-info/std",
-	"parity-scale-codec/std",
-	"up-data-structs/std",
-	"pallet-evm/std",
-]
+std = ["frame-support/std", "frame-system/std", "frame-benchmarking/std", "sp-std/std", "pallet-common/std", "scale-info/std", "parity-scale-codec/std", "up-data-structs/std", "pallet-evm/std"]
 runtime-benchmarks = ['frame-benchmarking', 'pallet-common/runtime-benchmarks']
 try-runtime = ["frame-support/try-runtime"]
modifiedpallets/structure/src/lib.rsdiffbeforeafterboth
54#![cfg_attr(not(feature = "std"), no_std)]54#![cfg_attr(not(feature = "std"), no_std)]
5555
56use pallet_common::CommonCollectionOperations;56use pallet_common::CommonCollectionOperations;
57use pallet_common::{erc::CrossAccountId, eth::is_collection};
57use sp_std::collections::btree_set::BTreeSet;58use sp_std::collections::btree_set::BTreeSet;
5859
59use frame_support::dispatch::{DispatchError, DispatchResult, DispatchResultWithPostInfo};60use frame_support::dispatch::{DispatchError, DispatchResult, DispatchResultWithPostInfo};
86 BreadthLimit,87 BreadthLimit,
87 /// Couldn't find the token owner that is itself a token.88 /// Couldn't find the token owner that is itself a token.
88 TokenNotFound,89 TokenNotFound,
90 /// Tried to nest token under collection contract address, instead of token address
91 CantNestTokenUnderCollection,
89 }92 }
9093
91 #[pallet::event]94 #[pallet::event]
302 token_id: TokenId,305 token_id: TokenId,
303 nesting_budget: &dyn Budget,306 nesting_budget: &dyn Budget,
304 ) -> DispatchResult {307 ) -> DispatchResult {
305 Self::try_exec_if_owner_is_valid_nft(under, |collection, parent_id| {308 Self::try_exec_if_token(under, |collection, parent_id| {
306 collection.check_nesting(from, (collection_id, token_id), parent_id, nesting_budget)309 collection.check_nesting(from, (collection_id, token_id), parent_id, nesting_budget)
307 })310 })
308 }311 }
319 token_id: TokenId,322 token_id: TokenId,
320 nesting_budget: &dyn Budget,323 nesting_budget: &dyn Budget,
321 ) -> DispatchResult {324 ) -> DispatchResult {
322 Self::try_exec_if_owner_is_valid_nft(under, |collection, parent_id| {325 Self::try_exec_if_token(under, |collection, parent_id| {
323 collection.check_nesting(from, (collection_id, token_id), parent_id, nesting_budget)?;326 collection.check_nesting(from, (collection_id, token_id), parent_id, nesting_budget)?;
324327
325 collection.nest(parent_id, (collection_id, token_id));328 collection.nest(parent_id, (collection_id, token_id));
336 collection_id: CollectionId,339 collection_id: CollectionId,
337 token_id: TokenId,340 token_id: TokenId,
338 ) {341 ) {
339 Self::exec_if_owner_is_valid_nft(owner, |collection, parent_id| {342 Self::exec_if_token(owner, |collection, parent_id| {
340 collection.nest(parent_id, (collection_id, token_id))343 collection.nest(parent_id, (collection_id, token_id))
341 });344 });
342 }345 }
347 collection_id: CollectionId,350 collection_id: CollectionId,
348 token_id: TokenId,351 token_id: TokenId,
349 ) {352 ) {
350 Self::exec_if_owner_is_valid_nft(owner, |collection, parent_id| {353 if let Err(e) = Self::try_exec_if_token(owner, |collection, parent_id| {
351 collection.unnest(parent_id, (collection_id, token_id))354 collection.unnest(parent_id, (collection_id, token_id));
355 Ok(())
352 });356 }) {
357 log::warn!("unnest precondition failed: {e:?}")
358 }
353 }359 }
354360
361 /// # Panics
362 /// If [`Self::try_exec_if_token`] fails
355 fn exec_if_owner_is_valid_nft(363 fn exec_if_token(
356 account: &T::CrossAccountId,364 account: &T::CrossAccountId,
357 action: impl FnOnce(&dyn CommonCollectionOperations<T>, TokenId),365 action: impl FnOnce(&dyn CommonCollectionOperations<T>, TokenId),
358 ) {366 ) {
359 Self::try_exec_if_owner_is_valid_nft(account, |collection, id| {367 Self::try_exec_if_token(account, |collection, id| {
360 action(collection, id);368 action(collection, id);
361 Ok(())369 Ok(())
362 })370 })
363 .unwrap();371 .unwrap();
364 }372 }
365373
374 /// If `account` is a token address, execute `action` providing found collection as an argument
375 /// Token may not exist, it is expected it will be checked in the callback.
366 fn try_exec_if_owner_is_valid_nft(376 fn try_exec_if_token(
367 account: &T::CrossAccountId,377 account: &T::CrossAccountId,
368 action: impl FnOnce(&dyn CommonCollectionOperations<T>, TokenId) -> DispatchResult,378 action: impl FnOnce(&dyn CommonCollectionOperations<T>, TokenId) -> DispatchResult,
369 ) -> DispatchResult {379 ) -> DispatchResult {
380 if is_collection(&account.as_eth()) {
381 fail!(<Error<T>>::CantNestTokenUnderCollection);
382 }
370 let account = T::CrossTokenAddressMapping::address_to_token(account);383 let Some((collection, token)) = T::CrossTokenAddressMapping::address_to_token(account) else {
371
372 if account.is_none() {
373 return Ok(());384 return Ok(())
374 }385 };
375
376 let account = account.unwrap();
377386
378 let handle = <CollectionHandle<T>>::try_get(account.0);387 let handle = <CollectionHandle<T>>::try_get(collection)?;
379
380 if handle.is_err() {
381 return Ok(());
382 }
383
384 let handle = handle.unwrap();
385388
386 let dispatch = T::CollectionDispatch::dispatch(handle);389 let dispatch = T::CollectionDispatch::dispatch(handle);
387 let dispatch = dispatch.as_dyn();390 let dispatch = dispatch.as_dyn();
388391
389 action(dispatch, account.1)392 action(dispatch, token)
390 }393 }
391}394}
392395