From e0035410299d589d1232ce7c17dfd86b7d8a3f45 Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Thu, 19 Jan 2023 16:40:10 +0000 Subject: [PATCH] fix: find_parent --- --- a/pallets/common/src/lib.rs +++ b/pallets/common/src/lib.rs @@ -155,6 +155,11 @@ } impl CollectionHandle { + /// Get the mode of the collection: NFT/FT/RFT. + pub fn mode(&self) -> CollectionMode { + self.mode + } + /// Same as [CollectionHandle::new] but with an explicit gas limit. pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option { >::get(id).map(|collection| Self { @@ -1872,6 +1877,9 @@ /// It wraps methods in Fungible, Nonfungible and Refungible pallets /// and adds weight info. pub trait CommonCollectionOperations { + /// Get the mode of the collection: NFT/FT/RFT. + fn mode(&self) -> CollectionMode; + /// Create token. /// /// * `sender` - The user who mint the token and pays for the transaction. --- a/pallets/fungible/src/common.rs +++ b/pallets/fungible/src/common.rs @@ -125,6 +125,10 @@ /// Implementation of `CommonCollectionOperations` for `FungibleHandle`. It wraps FungibleHandle Pallete /// methods and adds weight info. impl CommonCollectionOperations for FungibleHandle { + fn mode(&self) -> up_data_structs::CollectionMode { + self.0.mode() + } + fn create_item( &self, sender: T::CrossAccountId, --- a/pallets/nonfungible/src/common.rs +++ b/pallets/nonfungible/src/common.rs @@ -152,6 +152,10 @@ /// Implementation of `CommonCollectionOperations` for `NonfungibleHandle`. It wraps Nonfungible Pallete /// methods and adds weight info. impl CommonCollectionOperations for NonfungibleHandle { + fn mode(&self) -> up_data_structs::CollectionMode { + self.0.mode() + } + fn create_item( &self, sender: T::CrossAccountId, --- a/pallets/proxy-rmrk-core/src/lib.rs +++ b/pallets/proxy-rmrk-core/src/lib.rs @@ -741,7 +741,8 @@ Some((collection_id, nft_id)), &target_nft_budget, ) - .map_err(Self::map_unique_err_to_proxy)?; + .map_err(Self::map_unique_err_to_proxy)? + .ok_or::(>::NoPermission.into())?; approval_required = cross_sender != target_nft_owner; @@ -989,7 +990,8 @@ let nft_owner = >::find_topmost_owner(collection_id, nft_id, &budget) - .map_err(|_| >::ResourceDoesntExist)?; + .map_err(|_| >::ResourceDoesntExist)? + .ok_or::(>::NoPermission.into())?; Self::try_mutate_resource_info(collection_id, nft_id, resource_id, |res| { ensure!(res.pending, >::ResourceNotPending); @@ -1044,7 +1046,8 @@ let nft_owner = >::find_topmost_owner(collection_id, nft_id, &budget) - .map_err(|_| >::ResourceDoesntExist)?; + .map_err(|_| >::ResourceDoesntExist)? + .ok_or::(>::NoPermission.into())?; ensure!(cross_sender == nft_owner, >::NoPermission); @@ -1666,7 +1669,8 @@ let budget = budget::Value::new(NESTING_BUDGET); let nft_owner = >::find_topmost_owner(collection_id, nft_id, &budget) - .map_err(Self::map_unique_err_to_proxy)?; + .map_err(Self::map_unique_err_to_proxy)? + .ok_or::(>::NoPermission.into())?; let pending = sender != nft_owner; @@ -1720,7 +1724,8 @@ let budget = up_data_structs::budget::Value::new(NESTING_BUDGET); let topmost_owner = - >::find_topmost_owner(collection_id, nft_id, &budget)?; + >::find_topmost_owner(collection_id, nft_id, &budget)? + .ok_or::(>::NoPermission.into())?; let sender = T::CrossAccountId::from_sub(sender); if topmost_owner == sender { --- a/pallets/refungible/src/common.rs +++ b/pallets/refungible/src/common.rs @@ -186,6 +186,10 @@ /// Implementation of `CommonCollectionOperations` for `RefungibleHandle`. It wraps Refungible Pallete /// methods and adds weight info. impl CommonCollectionOperations for RefungibleHandle { + fn mode(&self) -> up_data_structs::CollectionMode { + self.0.mode() + } + fn create_item( &self, sender: T::CrossAccountId, --- a/pallets/structure/src/lib.rs +++ b/pallets/structure/src/lib.rs @@ -61,6 +61,7 @@ use frame_support::fail; pub use pallet::*; use pallet_common::{dispatch::CollectionDispatch, CollectionHandle}; +use up_data_structs::CollectionMode; use up_data_structs::{CollectionId, TokenId, mapping::TokenAddressMapping, budget::Budget}; #[cfg(feature = "runtime-benchmarks")] @@ -135,6 +136,8 @@ User(CrossAccountId), /// Could not find the token provided as the owner. TokenNotFound, + /// Nested token has multiple owners. + MultipleOwners, /// Token owner is another token (still, the target token may not exist). Token(CollectionId, TokenId), } @@ -163,6 +166,10 @@ Some((collection, token)) => Parent::Token(collection, token), None => Parent::User(owner), }, + None if handle.mode() == CollectionMode::ReFungible => handle + .total_pieces(token) + .map(|_| Parent::MultipleOwners) + .unwrap_or(Parent::TokenNotFound), None => Parent::TokenNotFound, }) } @@ -203,19 +210,27 @@ /// /// May return token address if parent token not yet exists /// + /// Returns `None` if the token has multiple owners. + /// /// - `budget`: Limit for searching parents in depth. pub fn find_topmost_owner( collection: CollectionId, token: TokenId, budget: &dyn Budget, - ) -> Result { + ) -> Result, DispatchError> { let owner = Self::parent_chain(collection, token) .take_while(|_| budget.consume()) - .find(|p| matches!(p, Ok(Parent::User(_) | Parent::TokenNotFound))) + .find(|p| { + matches!( + p, + Ok(Parent::User(_) | Parent::TokenNotFound | Parent::MultipleOwners) + ) + }) .ok_or(>::DepthLimit)??; Ok(match owner { - Parent::User(v) => v, + Parent::User(v) => Some(v), + Parent::MultipleOwners => None, _ => fail!(>::TokenNotFound), }) } @@ -223,13 +238,15 @@ /// Find the topmost parent and check that assigning `for_nest` token as a child for /// `token` wouldn't create a cycle. /// + /// Returns `None` if the token has multiple owners. + /// /// - `budget`: Limit for searching parents in depth. pub fn get_checked_topmost_owner( collection: CollectionId, token: TokenId, for_nest: Option<(CollectionId, TokenId)>, budget: &dyn Budget, - ) -> Result { + ) -> Result, DispatchError> { // Tried to nest token in itself if Some((collection, token)) == for_nest { return Err(>::OuroborosDetected.into()); @@ -242,8 +259,9 @@ return Err(>::OuroborosDetected.into()) } // Token is owned by other user - Parent::User(user) => return Ok(user), + Parent::User(user) => return Ok(Some(user)), Parent::TokenNotFound => return Err(>::TokenNotFound.into()), + Parent::MultipleOwners => return Ok(None), // Continue parent chain Parent::Token(_, _) => {} } @@ -284,12 +302,17 @@ budget: &dyn Budget, ) -> Result { let target_parent = match T::CrossTokenAddressMapping::address_to_token(&user) { - Some((collection, token)) => Self::find_topmost_owner(collection, token, budget)?, + Some((collection, token)) => match Self::find_topmost_owner(collection, token, budget)? + { + Some(topmost_owner) => topmost_owner, + None => return Ok(false), + }, None => user, }; - Self::get_checked_topmost_owner(collection, token, for_nest, budget) - .map(|indirect_owner| indirect_owner == target_parent) + Self::get_checked_topmost_owner(collection, token, for_nest, budget).map(|indirect_owner| { + indirect_owner.map_or(false, |indirect_owner| indirect_owner == target_parent) + }) } /// Checks that `under` is valid token and that `token_id` could be nested under it --- a/primitives/data-structs/src/lib.rs +++ b/primitives/data-structs/src/lib.rs @@ -252,7 +252,7 @@ /// Collection can represent various types of tokens. /// Each collection can contain only one type of tokens at a time. /// This type helps to understand which tokens the collection contains. -#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)] +#[derive(Encode, Decode, Eq, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub enum CollectionMode { /// Non fungible tokens. --- a/runtime/common/runtime_apis.rs +++ b/runtime/common/runtime_apis.rs @@ -83,7 +83,7 @@ fn topmost_token_owner(collection: CollectionId, token: TokenId) -> Result, DispatchError> { let budget = up_data_structs::budget::Value::new(10); - Ok(Some(>::find_topmost_owner(collection, token, &budget)?)) + Ok(>::find_topmost_owner(collection, token, &budget)?) } fn token_children(collection: CollectionId, token: TokenId) -> Result, DispatchError> { Ok(>::token_children_ids(collection, token)) -- gitstuff