From ec332a0b04f62f7c330bf60d1aa17f0ca6b11b16 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Thu, 04 Aug 2022 14:01:05 +0000 Subject: [PATCH] refactor(pallet-refungible): disallow invalid bulk mints `create_multiple_items_ex` was allowing invalid (that will be always rejected at runtime level) refungible mint extrinsics, by passing multiple users into `RefungibleMultipleItems` call. --- --- a/pallets/refungible/Cargo.toml +++ b/pallets/refungible/Cargo.toml @@ -28,6 +28,7 @@ sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24" } sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24" } sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24" } +derivative = { version = "2.2.0", features = ["use_core"] } [features] default = ["std"] --- a/pallets/refungible/src/common.rs +++ b/pallets/refungible/src/common.rs @@ -19,8 +19,9 @@ use sp_std::collections::btree_map::BTreeMap; use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight, traits::Get}; use up_data_structs::{ - CollectionId, TokenId, CreateItemExData, CreateRefungibleExData, budget::Budget, Property, - PropertyKey, PropertyValue, PropertyKeyPermission, CreateItemData, CollectionPropertiesVec, + CollectionId, TokenId, CreateItemExData, budget::Budget, Property, PropertyKey, PropertyValue, + PropertyKeyPermission, CollectionPropertiesVec, CreateRefungibleExMultipleOwners, + CreateRefungibleExSingleOwner, }; use pallet_common::{ CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions, with_weight, @@ -32,7 +33,7 @@ use crate::{ AccountBalance, Allowance, Balance, Config, Error, Owned, Pallet, RefungibleHandle, - SelfWeightOf, TokenData, weights::WeightInfo, TokensMinted, TotalSupply, + SelfWeightOf, TokenData, weights::WeightInfo, TokensMinted, TotalSupply, CreateItemData, }; macro_rules! max_weight_of { @@ -58,11 +59,11 @@ >::create_item() } - fn create_multiple_items(data: &[CreateItemData]) -> Weight { + fn create_multiple_items(data: &[up_data_structs::CreateItemData]) -> Weight { >::create_multiple_items(data.len() as u32).saturating_add( data.iter() .map(|data| match data { - CreateItemData::ReFungible(rft_data) => { + up_data_structs::CreateItemData::ReFungible(rft_data) => { properties_weight::(&rft_data.properties) } _ => 0, @@ -152,9 +153,9 @@ fn map_create_data( data: up_data_structs::CreateItemData, to: &T::CrossAccountId, -) -> Result, DispatchError> { +) -> Result, DispatchError> { match data { - up_data_structs::CreateItemData::ReFungible(data) => Ok(CreateRefungibleExData { + up_data_structs::CreateItemData::ReFungible(data) => Ok(CreateItemData { users: { let mut out = BTreeMap::new(); out.insert(to.clone(), data.pieces); @@ -214,12 +215,26 @@ ) -> DispatchResultWithPostInfo { let weight = >::create_multiple_items_ex(&data); let data = match data { - CreateItemExData::RefungibleMultipleOwners(r) => vec![r], - CreateItemExData::RefungibleMultipleItems(r) - if r.iter().all(|i| i.users.len() == 1) => - { - r.into_inner() - } + CreateItemExData::RefungibleMultipleOwners(CreateRefungibleExMultipleOwners { + users, + properties, + }) => vec![CreateItemData { users, properties }], + CreateItemExData::RefungibleMultipleItems(r) => r + .into_inner() + .into_iter() + .map( + |CreateRefungibleExSingleOwner { + user, + pieces, + properties, + }| CreateItemData { + users: BTreeMap::from([(user, pieces)]) + .try_into() + .expect("limit >= 1"), + properties, + }, + ) + .collect(), _ => fail!(>::NotRefungibleDataUsedToMintFungibleCollectionToken), }; --- a/pallets/refungible/src/erc.rs +++ b/pallets/refungible/src/erc.rs @@ -494,7 +494,7 @@ >::create_item( self, &caller, - CreateItemData:: { + CreateItemData:: { users, properties: CollectionPropertiesVec::default(), }, @@ -560,7 +560,7 @@ >::create_item( self, &caller, - CreateItemData:: { users, properties }, + CreateItemData:: { users, properties }, &budget, ) .map_err(dispatch_to_evm::)?; @@ -717,7 +717,7 @@ .collect::>() .try_into() .unwrap(); - let create_item_data = CreateItemData:: { + let create_item_data = CreateItemData:: { users, properties: CollectionPropertiesVec::default(), }; @@ -777,7 +777,7 @@ }) .map_err(|e| Error::Revert(alloc::format!("Can't add property: {:?}", e)))?; - let create_item_data = CreateItemData:: { + let create_item_data = CreateItemData:: { users: users.clone(), properties, }; --- a/pallets/refungible/src/lib.rs +++ b/pallets/refungible/src/lib.rs @@ -93,7 +93,9 @@ use codec::{Encode, Decode, MaxEncodedLen}; use core::ops::Deref; use evm_coder::ToLog; -use frame_support::{BoundedVec, ensure, fail, storage::with_transaction, transactional}; +use frame_support::{ + BoundedVec, ensure, fail, storage::with_transaction, transactional, pallet_prelude::ConstU32, +}; use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm}; use pallet_evm_coder_substrate::WithRecorder; use pallet_common::{ @@ -106,11 +108,13 @@ use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome}; use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap}; use up_data_structs::{ - AccessMode, budget::Budget, CollectionId, CreateCollectionData, CreateRefungibleExData, - CustomDataLimit, mapping::TokenAddressMapping, MAX_REFUNGIBLE_PIECES, TokenId, Property, + AccessMode, budget::Budget, CollectionId, CreateCollectionData, CustomDataLimit, + mapping::TokenAddressMapping, MAX_REFUNGIBLE_PIECES, MAX_ITEMS_PER_BATCH, TokenId, Property, PropertyKey, PropertyKeyPermission, PropertyPermission, PropertyScope, PropertyValue, - TrySetProperty, + TrySetProperty, CollectionPropertiesVec, }; +use frame_support::BoundedBTreeMap; +use derivative::Derivative; pub use pallet::*; #[cfg(feature = "runtime-benchmarks")] @@ -120,8 +124,13 @@ pub mod erc_token; pub mod weights; -pub type CreateItemData = - CreateRefungibleExData<::CrossAccountId>; +#[derive(Derivative, Clone)] +pub struct CreateItemData { + #[derivative(Debug(format_with = "bounded::map_debug"))] + pub users: BoundedBTreeMap>, + #[derivative(Debug(format_with = "bounded::vec_debug"))] + pub properties: CollectionPropertiesVec, +} pub(crate) type SelfWeightOf = ::WeightInfo; /// Token data, stored independently from other data used to describe it @@ -873,7 +882,7 @@ pub fn create_multiple_items( collection: &RefungibleHandle, sender: &T::CrossAccountId, - data: Vec>, + data: Vec>, nesting_budget: &dyn Budget, ) -> DispatchResult { if !collection.is_owner_or_admin(sender) { @@ -1213,7 +1222,7 @@ pub fn create_item( collection: &RefungibleHandle, sender: &T::CrossAccountId, - data: CreateItemData, + data: CreateItemData, nesting_budget: &dyn Budget, ) -> DispatchResult { Self::create_multiple_items(collection, sender, vec![data], nesting_budget) --- a/primitives/data-structs/src/lib.rs +++ b/primitives/data-structs/src/lib.rs @@ -826,13 +826,23 @@ /// Extended data for create ReFungible item. #[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)] #[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))] -pub struct CreateRefungibleExData { +pub struct CreateRefungibleExMultipleOwners { #[derivative(Debug(format_with = "bounded::map_debug"))] pub users: BoundedBTreeMap>, #[derivative(Debug(format_with = "bounded::vec_debug"))] pub properties: CollectionPropertiesVec, } +/// Extended data for create ReFungible item. +#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)] +#[derivative(Debug(bound = "CrossAccountId: fmt::Debug"))] +pub struct CreateRefungibleExSingleOwner { + pub user: CrossAccountId, + pub pieces: u128, + #[derivative(Debug(format_with = "bounded::vec_debug"))] + pub properties: CollectionPropertiesVec, +} + /// Unified extended data for creating item. #[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)] #[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))] @@ -853,12 +863,12 @@ /// many tokens, each may have only one owner RefungibleMultipleItems( #[derivative(Debug(format_with = "bounded::vec_debug"))] - BoundedVec, ConstU32>, + BoundedVec, ConstU32>, ), /// Extended data for create ReFungible item in case of /// single token, which may have many owners - RefungibleMultipleOwners(CreateRefungibleExData), + RefungibleMultipleOwners(CreateRefungibleExMultipleOwners), } impl From for CreateItemData { -- gitstuff