git.delta.rocks / unique-network / refs/commits / 51e15a07c4a1

difftreelog

fix(rmrk) include pending children into nft_children RPC

Daniel Shiposha2022-06-30parent: #66c5541.patch.diff
in: master

3 files changed

modifiedpallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth
--- a/pallets/proxy-rmrk-core/src/lib.rs
+++ b/pallets/proxy-rmrk-core/src/lib.rs
@@ -16,10 +16,15 @@
 
 #![cfg_attr(not(feature = "std"), no_std)]
 
-use frame_support::{pallet_prelude::*, transactional, BoundedVec, dispatch::DispatchResult};
+use frame_support::{
+	pallet_prelude::*,
+	transactional,
+	BoundedVec,
+	dispatch::DispatchResult,
+};
 use frame_system::{pallet_prelude::*, ensure_signed};
 use sp_runtime::{DispatchError, Permill, traits::StaticLookup};
-use sp_std::vec::Vec;
+use sp_std::{vec::Vec, collections::btree_set::BTreeSet};
 use up_data_structs::{*, mapping::TokenAddressMapping};
 use pallet_common::{
 	Pallet as PalletCommon, Error as CommonError, CollectionHandle, CommonCollectionOperations,
@@ -48,6 +53,10 @@
 
 pub const NESTING_BUDGET: u32 = 5;
 
+type PendingTarget = (CollectionId, TokenId);
+type PendingChild = (RmrkCollectionId, RmrkNftId);
+type PendingChildrenMap = BTreeSet<PendingChild>;
+
 #[frame_support::pallet]
 pub mod pallet {
 	use super::*;
@@ -383,12 +392,13 @@
 				[
 					Self::rmrk_property(TokenType, &NftType::Regular)?,
 					Self::rmrk_property(Transferable, &transferable)?,
-					Self::rmrk_property(PendingNftAccept, &false)?,
+					Self::rmrk_property(PendingNftAccept, &None::<PendingTarget>)?,
 					Self::rmrk_property(RoyaltyInfo, &royalty_info)?,
 					Self::rmrk_property(Metadata, &metadata)?,
 					Self::rmrk_property(Equipped, &false)?,
 					Self::rmrk_property(ResourcePriorities, &<Vec<u8>>::new())?,
 					Self::rmrk_property(NextResourceId, &(0 as RmrkResourceId))?,
+					Self::rmrk_property(PendingChildren, &PendingChildrenMap::new())?,
 				]
 				.into_iter(),
 			)
@@ -483,11 +493,11 @@
 			);
 
 			ensure!(
-				!Self::get_nft_property_decoded(
+				Self::get_nft_property_decoded::<Option<PendingTarget>>(
 					collection_id,
 					nft_id,
 					RmrkProperty::PendingNftAccept
-				)?,
+				)?.is_none(),
 				<Error<T>>::NoPermission
 			);
 
@@ -524,7 +534,15 @@
 							collection.id,
 							nft_id,
 							PropertyScope::Rmrk,
-							Self::rmrk_property(PendingNftAccept, &approval_required)?,
+							Self::rmrk_property::<Option<PendingTarget>>(
+								PendingNftAccept,
+								&Some((target_collection_id, target_nft_id.into()))
+							)?,
+						)?;
+
+						Self::insert_pending_child(
+							(target_collection_id, target_nft_id.into()),
+							(rmrk_collection_id, rmrk_nft_id),
 						)?;
 					} else {
 						target_owner = T::CrossTokenAddressMapping::token_to_address(
@@ -618,13 +636,23 @@
 				}
 			})?;
 
-			<PalletNft<T>>::set_scoped_token_property(
-				collection.id,
+			let pending_target = Self::get_nft_property_decoded::<Option<PendingTarget>>(
+				collection_id,
 				nft_id,
-				PropertyScope::Rmrk,
-				Self::rmrk_property(PendingNftAccept, &false)?,
+				RmrkProperty::PendingNftAccept
 			)?;
 
+			if let Some(pending_target) = pending_target {
+				Self::remove_pending_child(pending_target, (rmrk_collection_id, rmrk_nft_id))?;
+
+				<PalletNft<T>>::set_scoped_token_property(
+					collection.id,
+					nft_id,
+					PropertyScope::Rmrk,
+					Self::rmrk_property(PendingNftAccept, &None::<PendingTarget>)?,
+				)?;
+			}
+
 			Self::deposit_event(Event::NFTAccepted {
 				sender,
 				recipient: new_owner,
@@ -663,15 +691,18 @@
 				<Error<T>>::NoAvailableNftId
 			);
 
-			ensure!(
-				Self::get_nft_property_decoded(
-					collection_id,
-					nft_id,
-					RmrkProperty::PendingNftAccept
-				)?,
-				<Error<T>>::CannotRejectNonPendingNft
-			);
 
+			let pending_target = Self::get_nft_property_decoded::<Option<PendingTarget>>(
+				collection_id,
+				nft_id,
+				RmrkProperty::PendingNftAccept
+			)?;
+
+			match pending_target {
+				Some(pending_target) => Self::remove_pending_child(pending_target, (rmrk_collection_id, rmrk_nft_id))?,
+				None => return Err(<Error<T>>::CannotRejectNonPendingNft.into()),
+			}
+
 			Self::destroy_nft(
 				cross_sender,
 				collection_id,
@@ -1147,6 +1178,64 @@
 		)
 	}
 
+	fn insert_pending_child(
+		target: (CollectionId, TokenId),
+		child: (RmrkCollectionId, RmrkNftId),
+	) -> DispatchResult {
+		Self::mutate_pending_child(target, |pending_children| {
+			pending_children.insert(child);
+		})
+	}
+
+	fn remove_pending_child(
+		target: (CollectionId, TokenId),
+		child: (RmrkCollectionId, RmrkNftId),
+	) -> DispatchResult {
+		Self::mutate_pending_child(target, |pending_children| {
+			pending_children.remove(&child);
+		})
+	}
+
+	fn mutate_pending_child(
+		(target_collection_id, target_nft_id): (CollectionId, TokenId),
+		f: impl FnOnce(&mut PendingChildrenMap),
+	) -> DispatchResult {
+		<PalletNft<T>>::try_mutate_token_aux_property(
+			target_collection_id,
+			target_nft_id,
+			PropertyScope::Rmrk,
+			Self::rmrk_property_key(PendingChildren)?,
+			|pending_children| -> DispatchResult {
+				let mut map = match pending_children {
+					Some(map) => Self::decode_property(map)?,
+					None => PendingChildrenMap::new(),
+				};
+
+				f(&mut map);
+
+				*pending_children = Some(Self::encode_property(&map)?);
+
+				Ok(())
+			},
+		)
+	}
+
+	fn iterate_pending_children(collection_id: CollectionId, nft_id: TokenId) -> Result<impl Iterator<Item=PendingChild>, DispatchError> {
+		let property = <PalletNft<T>>::token_aux_property((
+			collection_id,
+			nft_id,
+			PropertyScope::Rmrk,
+			Self::rmrk_property_key(PendingChildren)?
+		));
+
+		let pending_children = match property {
+			Some(map) => Self::decode_property(&map)?,
+			None => PendingChildrenMap::new(),
+		};
+
+		Ok(pending_children.into_iter())
+	}
+
 	fn acquire_next_resource_id(
 		collection_id: CollectionId,
 		nft_id: TokenId,
@@ -1526,15 +1615,13 @@
 		Value: Decode + Default,
 		Mapper: Fn(Key, Value) -> R,
 	{
-		let key_prefix = Self::rmrk_property_key(UserProperty(b""))?;
-
 		let properties = match token_id {
 			Some(token_id) => <PalletNft<T>>::token_properties((collection_id, token_id)),
 			None => <PalletCommon<T>>::collection_properties(collection_id),
 		};
 
 		let properties = properties.into_iter().filter_map(move |(key, value)| {
-			let key = key.as_slice().strip_prefix(key_prefix.as_slice())?;
+			let key = strip_key_prefix(&key, USER_PROPERTY_PREFIX)?;
 
 			let key: Key = key.to_vec().try_into().ok()?;
 			let value: Value = value.decode().ok()?;
modifiedpallets/proxy-rmrk-core/src/property.rsdiffbeforeafterboth
before · pallets/proxy-rmrk-core/src/property.rs
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 super::*;18use core::convert::AsRef;1920const RESOURCE_ID_PREFIX: &str = "rsid-";2122pub enum RmrkProperty<'r> {23	Metadata,24	CollectionType,25	RmrkInternalCollectionId,26	TokenType,27	Transferable,28	RoyaltyInfo,29	Equipped,30	ResourcePriorities,31	NextResourceId,32	ResourceId(RmrkResourceId),33	PendingNftAccept,34	Parts,35	Base,36	Src,37	EquippedNft,38	BaseType,39	ExternalPartId,40	EquippableList,41	ZIndex,42	ThemeName,43	ThemeInherit,44	UserProperty(&'r [u8]),45}4647impl<'r> RmrkProperty<'r> {48	pub fn to_key<T: Config>(self) -> Result<PropertyKey, Error<T>> {49		fn get_bytes<T: AsRef<[u8]>>(container: &T) -> &[u8] {50			container.as_ref()51		}5253		macro_rules! key {54            ($($component:expr),+) => {55                PropertyKey::try_from([$(key!(@ &$component)),+].concat())56                    .map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)57            };5859            (@ $key:expr) => {60                get_bytes($key)61            };62        }6364		match self {65			Self::Metadata => key!("metadata"),66			Self::CollectionType => key!("collection-type"),67			Self::RmrkInternalCollectionId => key!("internal-id"),68			Self::TokenType => key!("token-type"),69			Self::Transferable => key!("transferable"),70			Self::RoyaltyInfo => key!("royalty-info"),71			Self::Equipped => key!("equipped"),72			Self::ResourcePriorities => key!("resource-priorities"),73			Self::NextResourceId => key!("next-resource-id"),74			Self::ResourceId(id) => key!(RESOURCE_ID_PREFIX, id.to_le_bytes()),75			Self::PendingNftAccept => key!("pending-nft-accept"),76			Self::Parts => key!("parts"),77			Self::Base => key!("base"),78			Self::Src => key!("src"),79			Self::EquippedNft => key!("equipped-nft"),80			Self::BaseType => key!("base-type"),81			Self::ExternalPartId => key!("ext-part-id"),82			Self::EquippableList => key!("equippable-list"),83			Self::ZIndex => key!("z-index"),84			Self::ThemeName => key!("theme-name"),85			Self::ThemeInherit => key!("theme-inherit"),86			Self::UserProperty(name) => key!("userprop-", name),87		}88	}89}
after · pallets/proxy-rmrk-core/src/property.rs
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 super::*;18use up_data_structs::PropertyScope;19use core::convert::AsRef;2021pub const RESOURCE_ID_PREFIX: &str = "rsid-";22pub const USER_PROPERTY_PREFIX: &str = "userprop-";2324pub enum RmrkProperty<'r> {25	Metadata,26	CollectionType,27	RmrkInternalCollectionId,28	TokenType,29	Transferable,30	RoyaltyInfo,31	Equipped,32	ResourcePriorities,33	NextResourceId,34	ResourceId(RmrkResourceId),35	PendingNftAccept,36	PendingChildren,37	Parts,38	Base,39	Src,40	EquippedNft,41	BaseType,42	ExternalPartId,43	EquippableList,44	ZIndex,45	ThemeName,46	ThemeInherit,47	UserProperty(&'r [u8]),48}4950impl<'r> RmrkProperty<'r> {51	pub fn to_key<T: Config>(self) -> Result<PropertyKey, Error<T>> {52		fn get_bytes<T: AsRef<[u8]>>(container: &T) -> &[u8] {53			container.as_ref()54		}5556		macro_rules! key {57            ($($component:expr),+) => {58                PropertyKey::try_from([$(key!(@ &$component)),+].concat())59                    .map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)60            };6162            (@ $key:expr) => {63                get_bytes($key)64            };65        }6667		match self {68			Self::Metadata => key!("metadata"),69			Self::CollectionType => key!("collection-type"),70			Self::RmrkInternalCollectionId => key!("internal-id"),71			Self::TokenType => key!("token-type"),72			Self::Transferable => key!("transferable"),73			Self::RoyaltyInfo => key!("royalty-info"),74			Self::Equipped => key!("equipped"),75			Self::ResourcePriorities => key!("resource-priorities"),76			Self::NextResourceId => key!("next-resource-id"),77			Self::ResourceId(id) => key!(RESOURCE_ID_PREFIX, id.to_le_bytes()),78			Self::PendingNftAccept => key!("pending-nft-accept"),79			Self::PendingChildren => key!("pending-children"),80			Self::Parts => key!("parts"),81			Self::Base => key!("base"),82			Self::Src => key!("src"),83			Self::EquippedNft => key!("equipped-nft"),84			Self::BaseType => key!("base-type"),85			Self::ExternalPartId => key!("ext-part-id"),86			Self::EquippableList => key!("equippable-list"),87			Self::ZIndex => key!("z-index"),88			Self::ThemeName => key!("theme-name"),89			Self::ThemeInherit => key!("theme-inherit"),90			Self::UserProperty(name) => key!(USER_PROPERTY_PREFIX, name),91		}92	}93}9495pub fn strip_key_prefix(key: &PropertyKey, prefix: &str) -> Option<PropertyKey> {96	let key_prefix = PropertyKey::try_from(prefix.as_bytes().to_vec()).ok()?;97	let key_prefix = PropertyScope::Rmrk.apply(key_prefix).ok()?;9899	key.as_slice().strip_prefix(key_prefix.as_slice())?100		.to_vec().try_into().ok()101}102103pub fn is_valid_key_prefix(key: &PropertyKey, prefix: &str) -> bool {104	strip_key_prefix(key, prefix).is_some()105}
modifiedpallets/proxy-rmrk-core/src/rpc.rsdiffbeforeafterboth
--- a/pallets/proxy-rmrk-core/src/rpc.rs
+++ b/pallets/proxy-rmrk-core/src/rpc.rs
@@ -132,17 +132,6 @@
 	Ok(
 		pallet_nonfungible::TokenChildren::<T>::iter_prefix((collection_id, nft_id))
 			.filter_map(|((child_collection, child_token), _)| {
-				let is_pending = <Pallet<T>>::get_nft_property_decoded(
-					child_collection,
-					child_token,
-					RmrkProperty::PendingNftAccept,
-				)
-				.ok()?;
-
-				if is_pending {
-					return None;
-				}
-
 				let rmrk_child_collection =
 					<Pallet<T>>::rmrk_collection_id(child_collection).ok()?;
 
@@ -151,6 +140,13 @@
 					nft_id: child_token.0,
 				})
 			})
+			.chain(
+				<Pallet<T>>::iterate_pending_children(collection_id, nft_id)?
+					.map(|(child_collection, child_nft_id)| RmrkNftChild {
+						collection_id: child_collection,
+						nft_id: child_nft_id,
+					})
+			)
 			.collect(),
 	)
 }
@@ -224,7 +220,11 @@
 		nft_id,
 		PropertyScope::Rmrk,
 	)
-	.filter_map(|(_, value)| {
+	.filter_map(|(key, value)| {
+		if !is_valid_key_prefix(&key, RESOURCE_ID_PREFIX) {
+			return None;
+		}
+
 		let resource_info: RmrkResourceInfo = <Pallet<T>>::decode_property(&value).ok()?;
 
 		Some(resource_info)