git.delta.rocks / unique-network / refs/commits / 7bcde70a2ca7

difftreelog

NFTPAR-118. Per Collection Limits

str-mv2020-12-11parent: #68e6834.patch.diff
in: master

3 files changed

modifiedREADME.mddiffbeforeafterboth
--- a/README.md
+++ b/README.md
@@ -241,6 +241,12 @@
       "ReFungible": "CreateReFungibleData"
     }
   }
+  "CollectionLimits": {
+    "AccountTokenOwnershipLimit": "u32",
+    "SponsoredDataSize": "u32",
+    "TokenLimit": "u32",
+    "SponsorTransferTimeout": "u32"
+  }
 }
 
 ```
\ No newline at end of file
modifiednode/src/chain_spec.rsdiffbeforeafterboth
before · node/src/chain_spec.rs
1// use nft_runtime::{2//     AccountId, AuraConfig, BalancesConfig, GenesisConfig, GrandpaConfig, Signature, SudoConfig,3//     SystemConfig, WASM_BINARY,4// };5// use nft_runtime::{ContractsConfig, ContractsSchedule, NftConfig, CollectionType};6use nft_runtime::*;7use sc_service::ChainType;8use sp_consensus_aura::sr25519::AuthorityId as AuraId;9use sp_core::{sr25519, Pair, Public};10use sp_finality_grandpa::AuthorityId as GrandpaId;11use sp_runtime::traits::{IdentifyAccount, Verify};1213// Note this is the URL for the telemetry server14//const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";1516/// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type.17pub type ChainSpec = sc_service::GenericChainSpec<GenesisConfig>;1819/// Helper function to generate a crypto pair from seed20pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {21    TPublic::Pair::from_string(&format!("//{}", seed), None)22        .expect("static values are valid; qed")23        .public()24}2526type AccountPublic = <Signature as Verify>::Signer;2728/// Helper function to generate an account ID from seed29pub fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId30where31    AccountPublic: From<<TPublic::Pair as Pair>::Public>,32{33    AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account()34}3536/// Helper function to generate an authority key for Aura37pub fn authority_keys_from_seed(s: &str) -> (AuraId, GrandpaId) {38    (get_from_seed::<AuraId>(s), get_from_seed::<GrandpaId>(s))39}4041pub fn development_config() -> Result<ChainSpec, String> {42	let wasm_binary = WASM_BINARY.ok_or("Development wasm binary not available".to_string())?;4344	Ok(ChainSpec::from_genesis(45		// Name46		"Development",47		// ID48		"dev",49		ChainType::Development,50		move || testnet_genesis(51			wasm_binary,52			// Initial PoA authorities53			vec![54				authority_keys_from_seed("Alice"),55			],56			// Sudo account57			get_account_id_from_seed::<sr25519::Public>("Alice"),58			// Pre-funded accounts59			vec![60				get_account_id_from_seed::<sr25519::Public>("Alice"),61				get_account_id_from_seed::<sr25519::Public>("Bob"),62				get_account_id_from_seed::<sr25519::Public>("Alice//stash"),63				get_account_id_from_seed::<sr25519::Public>("Bob//stash"),64			],65			true,66		),67		// Bootnodes68		vec![],69		// Telemetry70		None,71		// Protocol ID72		None,73		// Properties74		None,75		// Extensions76		None,77	))78}7980pub fn local_testnet_config() -> Result<ChainSpec, String> {81	let wasm_binary = WASM_BINARY.ok_or("Development wasm binary not available".to_string())?;8283	Ok(ChainSpec::from_genesis(84		// Name85		"Local Testnet",86		// ID87		"local_testnet",88		ChainType::Local,89		move || testnet_genesis(90			wasm_binary,91			// Initial PoA authorities92			vec![93				authority_keys_from_seed("Alice"),94				authority_keys_from_seed("Bob"),95			],96			// Sudo account97			get_account_id_from_seed::<sr25519::Public>("Alice"),98			// Pre-funded accounts99			vec![100				get_account_id_from_seed::<sr25519::Public>("Alice"),101				get_account_id_from_seed::<sr25519::Public>("Bob"),102				get_account_id_from_seed::<sr25519::Public>("Charlie"),103				get_account_id_from_seed::<sr25519::Public>("Dave"),104				get_account_id_from_seed::<sr25519::Public>("Eve"),105				get_account_id_from_seed::<sr25519::Public>("Ferdie"),106				get_account_id_from_seed::<sr25519::Public>("Alice//stash"),107				get_account_id_from_seed::<sr25519::Public>("Bob//stash"),108				get_account_id_from_seed::<sr25519::Public>("Charlie//stash"),109				get_account_id_from_seed::<sr25519::Public>("Dave//stash"),110				get_account_id_from_seed::<sr25519::Public>("Eve//stash"),111				get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),112			],113			true,114		),115		// Bootnodes116		vec![],117		// Telemetry118		None,119		// Protocol ID120		None,121		// Properties122		None,123		// Extensions124		None,125	))126}127128fn testnet_genesis(129    wasm_binary: &[u8],130    initial_authorities: Vec<(AuraId, GrandpaId)>,131    root_key: AccountId,132    endowed_accounts: Vec<AccountId>,133    enable_println: bool,134) -> GenesisConfig {135    GenesisConfig {136        system: Some(SystemConfig {137            code: wasm_binary.to_vec(),138            changes_trie_config: Default::default(),139        }),140        pallet_balances: Some(BalancesConfig {141            balances: endowed_accounts142                .iter()143                .cloned()144                .map(|k| (k, 1 << 100))145                .collect(),146        }),147        pallet_aura: Some(AuraConfig {148            authorities: initial_authorities.iter().map(|x| (x.0.clone())).collect(),149        }),150		pallet_grandpa: Some(GrandpaConfig {151            authorities: initial_authorities152                .iter()153                .map(|x| (x.1.clone(), 1))154                .collect(),155		}),156		pallet_treasury: Some(Default::default()),157        pallet_sudo: Some(SudoConfig { key: root_key }),158        pallet_nft: Some(NftConfig {159            collection: vec![(160                1,161                CollectionType {162                    owner: get_account_id_from_seed::<sr25519::Public>("Alice"),163                    mode: CollectionMode::NFT,164                    access: AccessMode::Normal,165                    decimal_points: 0,166                    name: vec![],167                    description: vec![],168                    token_prefix: vec![],169                    mint_mode: false,170                    offchain_schema: vec![],171                    sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),172                    unconfirmed_sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),173                    const_on_chain_schema: vec![],174                    variable_on_chain_schema: vec![]175                },176            )],177            nft_item_id: vec![],178            fungible_item_id: vec![],179            refungible_item_id: vec![],180            chain_limit: ChainLimits {181                collection_numbers_limit: 10,182                account_token_ownership_limit: 10,183                collections_admins_limit: 5,184                custom_data_limit: 2048,185                nft_sponsor_transfer_timeout: 15,186                fungible_sponsor_transfer_timeout: 15,187                refungible_sponsor_transfer_timeout: 15,188            },189        }),190        pallet_contracts: Some(ContractsConfig {191            current_schedule: ContractsSchedule {192                enable_println,193                ..Default::default()194            },195        }),196    }197}
after · node/src/chain_spec.rs
1// use nft_runtime::{2//     AccountId, AuraConfig, BalancesConfig, GenesisConfig, GrandpaConfig, Signature, SudoConfig,3//     SystemConfig, WASM_BINARY,4// };5// use nft_runtime::{ContractsConfig, ContractsSchedule, NftConfig, CollectionType};6use nft_runtime::*;7use sc_service::ChainType;8use sp_consensus_aura::sr25519::AuthorityId as AuraId;9use sp_core::{sr25519, Pair, Public};10use sp_finality_grandpa::AuthorityId as GrandpaId;11use sp_runtime::traits::{IdentifyAccount, Verify};1213// Note this is the URL for the telemetry server14//const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";1516/// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type.17pub type ChainSpec = sc_service::GenericChainSpec<GenesisConfig>;1819/// Helper function to generate a crypto pair from seed20pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {21    TPublic::Pair::from_string(&format!("//{}", seed), None)22        .expect("static values are valid; qed")23        .public()24}2526type AccountPublic = <Signature as Verify>::Signer;2728/// Helper function to generate an account ID from seed29pub fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId30where31    AccountPublic: From<<TPublic::Pair as Pair>::Public>,32{33    AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account()34}3536/// Helper function to generate an authority key for Aura37pub fn authority_keys_from_seed(s: &str) -> (AuraId, GrandpaId) {38    (get_from_seed::<AuraId>(s), get_from_seed::<GrandpaId>(s))39}4041pub fn development_config() -> Result<ChainSpec, String> {42	let wasm_binary = WASM_BINARY.ok_or("Development wasm binary not available".to_string())?;4344	Ok(ChainSpec::from_genesis(45		// Name46		"Development",47		// ID48		"dev",49		ChainType::Development,50		move || testnet_genesis(51			wasm_binary,52			// Initial PoA authorities53			vec![54				authority_keys_from_seed("Alice"),55			],56			// Sudo account57			get_account_id_from_seed::<sr25519::Public>("Alice"),58			// Pre-funded accounts59			vec![60				get_account_id_from_seed::<sr25519::Public>("Alice"),61				get_account_id_from_seed::<sr25519::Public>("Bob"),62				get_account_id_from_seed::<sr25519::Public>("Alice//stash"),63				get_account_id_from_seed::<sr25519::Public>("Bob//stash"),64			],65			true,66		),67		// Bootnodes68		vec![],69		// Telemetry70		None,71		// Protocol ID72		None,73		// Properties74		None,75		// Extensions76		None,77	))78}7980pub fn local_testnet_config() -> Result<ChainSpec, String> {81	let wasm_binary = WASM_BINARY.ok_or("Development wasm binary not available".to_string())?;8283	Ok(ChainSpec::from_genesis(84		// Name85		"Local Testnet",86		// ID87		"local_testnet",88		ChainType::Local,89		move || testnet_genesis(90			wasm_binary,91			// Initial PoA authorities92			vec![93				authority_keys_from_seed("Alice"),94				authority_keys_from_seed("Bob"),95			],96			// Sudo account97			get_account_id_from_seed::<sr25519::Public>("Alice"),98			// Pre-funded accounts99			vec![100				get_account_id_from_seed::<sr25519::Public>("Alice"),101				get_account_id_from_seed::<sr25519::Public>("Bob"),102				get_account_id_from_seed::<sr25519::Public>("Charlie"),103				get_account_id_from_seed::<sr25519::Public>("Dave"),104				get_account_id_from_seed::<sr25519::Public>("Eve"),105				get_account_id_from_seed::<sr25519::Public>("Ferdie"),106				get_account_id_from_seed::<sr25519::Public>("Alice//stash"),107				get_account_id_from_seed::<sr25519::Public>("Bob//stash"),108				get_account_id_from_seed::<sr25519::Public>("Charlie//stash"),109				get_account_id_from_seed::<sr25519::Public>("Dave//stash"),110				get_account_id_from_seed::<sr25519::Public>("Eve//stash"),111				get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),112			],113			true,114		),115		// Bootnodes116		vec![],117		// Telemetry118		None,119		// Protocol ID120		None,121		// Properties122		None,123		// Extensions124		None,125	))126}127128fn testnet_genesis(129    wasm_binary: &[u8],130    initial_authorities: Vec<(AuraId, GrandpaId)>,131    root_key: AccountId,132    endowed_accounts: Vec<AccountId>,133    enable_println: bool,134) -> GenesisConfig {135    GenesisConfig {136        system: Some(SystemConfig {137            code: wasm_binary.to_vec(),138            changes_trie_config: Default::default(),139        }),140        pallet_balances: Some(BalancesConfig {141            balances: endowed_accounts142                .iter()143                .cloned()144                .map(|k| (k, 1 << 100))145                .collect(),146        }),147        pallet_aura: Some(AuraConfig {148            authorities: initial_authorities.iter().map(|x| (x.0.clone())).collect(),149        }),150		pallet_grandpa: Some(GrandpaConfig {151            authorities: initial_authorities152                .iter()153                .map(|x| (x.1.clone(), 1))154                .collect(),155		}),156		pallet_treasury: Some(Default::default()),157        pallet_sudo: Some(SudoConfig { key: root_key }),158        pallet_nft: Some(NftConfig {159            collection: vec![(160                1,161                CollectionType {162                    owner: get_account_id_from_seed::<sr25519::Public>("Alice"),163                    mode: CollectionMode::NFT,164                    access: AccessMode::Normal,165                    decimal_points: 0,166                    name: vec![],167                    description: vec![],168                    token_prefix: vec![],169                    mint_mode: false,170                    offchain_schema: vec![],171                    sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),172                    unconfirmed_sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),173                    const_on_chain_schema: vec![],174					variable_on_chain_schema: vec![],175					limits: CollectionLimits::default()176                },177            )],178            nft_item_id: vec![],179            fungible_item_id: vec![],180            refungible_item_id: vec![],181            chain_limit: ChainLimits {182                collection_numbers_limit: 10,183                account_token_ownership_limit: 10,184                collections_admins_limit: 5,185                custom_data_limit: 2048,186                nft_sponsor_transfer_timeout: 15,187                fungible_sponsor_transfer_timeout: 15,188                refungible_sponsor_transfer_timeout: 15,189            },190        }),191        pallet_contracts: Some(ContractsConfig {192            current_schedule: ContractsSchedule {193                enable_println,194                ..Default::default()195            },196        }),197    }198}
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -109,6 +109,7 @@
     pub offchain_schema: Vec<u8>,
     pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender
     pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship
+    pub limits: CollectionLimits, // Collection private restrictions 
     pub variable_on_chain_schema: Vec<u8>, //
     pub const_on_chain_schema: Vec<u8>, //
 }
@@ -164,6 +165,27 @@
     pub start_block: BlockNumber,
 }
 
+#[derive(Encode, Decode, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct CollectionLimits {
+    pub account_token_ownership_limit: u32,
+    pub sponsored_data_size: u32,
+    pub token_limit: u32,
+
+    // Timeouts for item types in passed blocks
+    pub sponsor_transfer_timeout: u32,
+}
+
+impl Default for CollectionLimits {
+    fn default() -> CollectionLimits {
+        CollectionLimits { 
+            account_token_ownership_limit: 0, 
+            token_limit: 0,
+            sponsored_data_size: 0, 
+            sponsor_transfer_timeout: 0 }
+    }
+}
+
 #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 pub struct ChainLimits {
@@ -321,7 +343,11 @@
         /// Unexpected collection type.
         UnexpectedCollectionType,
         /// Can't store metadata in fungible tokens.
-        CantStoreMetadataInFungibleTokens
+        CantStoreMetadataInFungibleTokens,
+        /// Collection token limit exceeded
+        CollectionTokenLimitExceeded,
+        /// Account token limit exceeded per collection
+        AccountTokenLimitExceeded
 	}
 }
 
@@ -537,6 +563,7 @@
                 unconfirmed_sponsor: T::AccountId::default(),
                 variable_on_chain_schema: Vec::new(),
                 const_on_chain_schema: Vec::new(),
+                limits: CollectionLimits::default(),
             };
 
             // Add new collection to map
@@ -1380,17 +1407,21 @@
             Ok(())
         }
 
-        // #[cfg(feature = "runtime-benchmarks")]
-        // #[weight = 0]
-        // pub fn add_contract_sponsoring_debug(
-        //     origin,
-        //     contract_address: T::AccountId, 
-        //     owner: T::AccountId) -> DispatchResult {
-        //     let sender = ensure_signed(origin)?;
-        //     <ContractOwner<T>>::insert(contract_address.clone(), owner);
-        //     Ok(())
-        // }
-    
+        #[weight = 0]
+        pub fn set_collection_limits(
+            origin,
+            collection_id: u64,
+            limits: CollectionLimits,
+        ) -> DispatchResult {
+            let sender = ensure_signed(origin)?;
+            Self::check_owner_permissions(collection_id, sender.clone())?;
+
+            let mut target_collection = <Collection<T>>::get(collection_id);
+            target_collection.limits = limits;
+            <Collection<T>>::insert(collection_id, target_collection);
+
+            Ok(())
+        } 
     }
 }
 
@@ -1399,6 +1430,12 @@
     fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {
 
         if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {
+
+            // check token limit and account token limit
+            let total_items: u64 = ItemListIndex::get(collection_id);
+            let account_items: u32 = <AddressTokens<T>>::get(collection_id, sender.clone()).len() as u32;
+            ensure!(collection.limits.token_limit as u64 > total_items,  Error::<T>::CollectionTokenLimitExceeded);
+            ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);
             ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);
             Self::check_white_list(collection_id, owner)?;
             Self::check_white_list(collection_id, sender)?;
@@ -1478,7 +1515,6 @@
                 Self::add_refungible_item(item)?;
             }
         };
-
 
         // call event
         Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));
@@ -2211,18 +2247,35 @@
         // Determine who is paying transaction fee based on ecnomic model
         // Parse call to extract collection ID and access collection sponsor
         let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {
-            Some(Call::create_item(collection_id, _properties, _owner)) => {
-                <Collection<T>>::get(collection_id).sponsor
+            Some(Call::create_item(collection_id, _owner, _properties)) => {
+
+                // check free create limit
+                if <Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)
+                {
+                    <Collection<T>>::get(collection_id).sponsor
+                } else {
+                    T::AccountId::default()
+                }
             }
             Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {
+                
+                let _collection_limits = <Collection<T>>::get(collection_id).limits;
                 let _collection_mode = <Collection<T>>::get(collection_id).mode;
 
                 // sponsor timeout
                 let sponsor_transfer = match _collection_mode {
                     CollectionMode::NFT => {
+
+                        // get correct limit
+                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {
+                            _collection_limits.sponsor_transfer_timeout
+                        } else {
+                            ChainLimit::get().nft_sponsor_transfer_timeout
+                        };
+
                         let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);
                         let block_number = <system::Module<T>>::block_number() as T::BlockNumber;
-                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();
+                        let limit_time = basket + limit.into();
                         if block_number >= limit_time {
                             <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);
                             true
@@ -2232,12 +2285,20 @@
                         }
                     }
                     CollectionMode::Fungible(_) => {
+
+                        // get correct limit
+                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {
+                            _collection_limits.sponsor_transfer_timeout
+                        } else {
+                            ChainLimit::get().fungible_sponsor_transfer_timeout
+                        };
+
                         let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);
                         let block_number = <system::Module<T>>::block_number() as T::BlockNumber;
                         if basket.iter().any(|i| i.address == _new_owner.clone())
                         {
                             let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();
-                            let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();
+                            let limit_time = item.start_block + limit.into();
                             if block_number >= limit_time {
                                 basket.retain(|x| x.address == item.address);
                                 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });
@@ -2254,9 +2315,17 @@
                         }
                     }
                     CollectionMode::ReFungible(_) => {
+
+                        // get correct limit
+                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {
+                            _collection_limits.sponsor_transfer_timeout
+                        } else {
+                            ChainLimit::get().refungible_sponsor_transfer_timeout
+                        };
+
                         let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);
                         let block_number = <system::Module<T>>::block_number() as T::BlockNumber;
-                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();
+                        let limit_time = basket + limit.into();
                         if block_number >= limit_time {
                             <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);
                             true