difftreelog
refactor(pallet-refungible) disallow invalid bulk mints
in: master
`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.
5 files changed
pallets/refungible/Cargo.tomldiffbeforeafterboth--- 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"]
pallets/refungible/src/common.rsdiffbeforeafterboth--- 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 @@
<SelfWeightOf<T>>::create_item()
}
- fn create_multiple_items(data: &[CreateItemData]) -> Weight {
+ fn create_multiple_items(data: &[up_data_structs::CreateItemData]) -> Weight {
<SelfWeightOf<T>>::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::<T>(&rft_data.properties)
}
_ => 0,
@@ -152,9 +153,9 @@
fn map_create_data<T: Config>(
data: up_data_structs::CreateItemData,
to: &T::CrossAccountId,
-) -> Result<CreateRefungibleExData<T::CrossAccountId>, DispatchError> {
+) -> Result<CreateItemData<T::CrossAccountId>, 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 = <CommonWeights<T>>::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!(<Error<T>>::NotRefungibleDataUsedToMintFungibleCollectionToken),
};
pallets/refungible/src/erc.rsdiffbeforeafterboth494 <Pallet<T>>::create_item(494 <Pallet<T>>::create_item(495 self,495 self,496 &caller,496 &caller,497 CreateItemData::<T> {497 CreateItemData::<T::CrossAccountId> {498 users,498 users,499 properties: CollectionPropertiesVec::default(),499 properties: CollectionPropertiesVec::default(),500 },500 },560 <Pallet<T>>::create_item(560 <Pallet<T>>::create_item(561 self,561 self,562 &caller,562 &caller,563 CreateItemData::<T> { users, properties },563 CreateItemData::<T::CrossAccountId> { users, properties },564 &budget,564 &budget,565 )565 )566 .map_err(dispatch_to_evm::<T>)?;566 .map_err(dispatch_to_evm::<T>)?;717 .collect::<BTreeMap<_, _>>()717 .collect::<BTreeMap<_, _>>()718 .try_into()718 .try_into()719 .unwrap();719 .unwrap();720 let create_item_data = CreateItemData::<T> {720 let create_item_data = CreateItemData::<T::CrossAccountId> {721 users,721 users,722 properties: CollectionPropertiesVec::default(),722 properties: CollectionPropertiesVec::default(),723 };723 };777 })777 })778 .map_err(|e| Error::Revert(alloc::format!("Can't add property: {:?}", e)))?;778 .map_err(|e| Error::Revert(alloc::format!("Can't add property: {:?}", e)))?;779779780 let create_item_data = CreateItemData::<T> {780 let create_item_data = CreateItemData::<T::CrossAccountId> {781 users: users.clone(),781 users: users.clone(),782 properties,782 properties,783 };783 };pallets/refungible/src/lib.rsdiffbeforeafterboth--- 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<T> =
- CreateRefungibleExData<<T as pallet_evm::account::Config>::CrossAccountId>;
+#[derive(Derivative, Clone)]
+pub struct CreateItemData<CrossAccountId> {
+ #[derivative(Debug(format_with = "bounded::map_debug"))]
+ pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,
+ #[derivative(Debug(format_with = "bounded::vec_debug"))]
+ pub properties: CollectionPropertiesVec,
+}
pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;
/// Token data, stored independently from other data used to describe it
@@ -873,7 +882,7 @@
pub fn create_multiple_items(
collection: &RefungibleHandle<T>,
sender: &T::CrossAccountId,
- data: Vec<CreateItemData<T>>,
+ data: Vec<CreateItemData<T::CrossAccountId>>,
nesting_budget: &dyn Budget,
) -> DispatchResult {
if !collection.is_owner_or_admin(sender) {
@@ -1213,7 +1222,7 @@
pub fn create_item(
collection: &RefungibleHandle<T>,
sender: &T::CrossAccountId,
- data: CreateItemData<T>,
+ data: CreateItemData<T::CrossAccountId>,
nesting_budget: &dyn Budget,
) -> DispatchResult {
Self::create_multiple_items(collection, sender, vec![data], nesting_budget)
primitives/data-structs/src/lib.rsdiffbeforeafterboth--- 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<CrossAccountId> {
+pub struct CreateRefungibleExMultipleOwners<CrossAccountId> {
#[derivative(Debug(format_with = "bounded::map_debug"))]
pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,
#[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<CrossAccountId> {
+ 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<CreateRefungibleExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,
+ BoundedVec<CreateRefungibleExSingleOwner<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,
),
/// Extended data for create ReFungible item in case of
/// single token, which may have many owners
- RefungibleMultipleOwners(CreateRefungibleExData<CrossAccountId>),
+ RefungibleMultipleOwners(CreateRefungibleExMultipleOwners<CrossAccountId>),
}
impl From<CreateNftData> for CreateItemData {