--- a/node/cli/src/chain_spec.rs +++ b/node/cli/src/chain_spec.rs @@ -196,6 +196,7 @@ const_on_chain_schema: vec![], variable_on_chain_schema: vec![], limits: CollectionLimits::default(), + meta_update_permission: MetaUpdatePermission::ItemOwner, }, )], nft_item_id: vec![], --- a/pallets/inflation/src/tests.rs +++ b/pallets/inflation/src/tests.rs @@ -31,6 +31,8 @@ type ExistentialDeposit = ExistentialDeposit; type WeightInfo = (); type MaxLocks = MaxLocks; + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; } frame_support::construct_runtime!( --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -41,7 +41,7 @@ MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES, AccessMode, ChainLimits, Collection, CreateItemData, CollectionLimits, CollectionId, CollectionMode, TokenId, SchemaVersion, SponsorshipState, Ownership, NftItemType, - FungibleItemType, ReFungibleItemType, + FungibleItemType, ReFungibleItemType, MetaUpdatePermission, }; use pallet_ethereum::EthereumTransactionSender; @@ -175,6 +175,10 @@ BadCreateRefungibleCall, /// Gas limit exceeded OutOfGas, + /// Metadata update denied by collection settings + MetadataUpdateDenied, + /// Metadata update flag become unmutable with None option + MetadataFlagFrozen, } } @@ -533,6 +537,7 @@ variable_on_chain_schema: Vec::new(), const_on_chain_schema: Vec::new(), limits, + meta_update_permission: MetaUpdatePermission::default(), }; // Add new collection to map @@ -922,6 +927,37 @@ Ok(()) } + // TODO! transaction weight + /// Set meta_update_permission value for particular collection + /// + /// # Permissions + /// + /// * Collection Owner. + /// + /// # Arguments + /// + /// * collection_id: ID of the collection. + /// + /// * value: New flag value. + #[weight = ::WeightInfo::burn_item()] + #[transactional] + pub fn set_meta_update_permission_flag(origin, collection_id: CollectionId, value: MetaUpdatePermission) -> DispatchResult { + + let sender = ensure_signed(origin)?; + let mut target_collection = Self::get_collection(collection_id)?; + + ensure!( + target_collection.meta_update_permission != MetaUpdatePermission::None, + Error::::MetadataFlagFrozen + ); + Self::check_owner_permissions(&target_collection, &sender)?; + + target_collection.meta_update_permission = value; + Self::save_collection(target_collection); + + Ok(()) + } + /// Destroys a concrete instance of NFT. /// /// # Permissions @@ -1481,11 +1517,7 @@ ); // Modify permissions check - ensure!( - Self::is_item_owner(sender, collection, item_id) - || Self::is_owner_or_admin_permissions(collection, sender), - Error::::NoPermission - ); + Self::meta_update_check(sender, collection, item_id)?; match collection.mode { CollectionMode::NFT => Self::set_nft_variable_data(collection, item_id, data)?, @@ -1499,6 +1531,32 @@ Ok(()) } + pub fn meta_update_check( + sender: &T::CrossAccountId, + collection: &CollectionHandle, + item_id: TokenId) -> DispatchResult { + + match collection.meta_update_permission { + MetaUpdatePermission::ItemOwner => { + ensure!( + Self::is_item_owner(sender, collection, item_id), + Error::::NoPermission + ); + }, + MetaUpdatePermission::Admin => { + ensure!( + Self::is_owner_or_admin_permissions(collection, sender), + Error::::NoPermission + ); + }, + MetaUpdatePermission::None => { + fail!(Error::::MetadataUpdateDenied); + }, + } + + Ok(()) + } + pub fn create_multiple_items_internal( sender: &T::CrossAccountId, collection: &CollectionHandle, --- a/pallets/nft/src/mock.rs +++ b/pallets/nft/src/mock.rs @@ -74,6 +74,8 @@ type ExistentialDeposit = ExistentialDeposit; type WeightInfo = (); type MaxLocks = MaxLocks; + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; } parameter_types! { @@ -99,41 +101,6 @@ type Timestamp = pallet_timestamp::Pallet; type Randomness = pallet_randomness_collective_flip::Pallet; - -parameter_types! { - pub const TombstoneDeposit: u64 = 1; - pub const DepositPerContract: u64 = 1; - pub const DepositPerStorageByte: u64 = 1; - pub const DepositPerStorageItem: u64 = 1; - pub RentFraction: Perbill = Perbill::from_rational(1u32, 30 * 24 * 60 * 10); - pub const SurchargeReward: u64 = 1; - pub const SignedClaimHandicap: u32 = 2; - pub DeletionWeightLimit: u64 = u64::MAX;//Perbill::from_percent(10); - pub DeletionQueueDepth: u32 = 10; - pub Schedule: pallet_contracts::Schedule = Default::default(); -} - -impl pallet_contracts::Config for Test { - type Time = Timestamp; - type Randomness = Randomness; - type Currency = pallet_balances::Pallet; - type Event = (); - type RentPayment = (); - type SignedClaimHandicap = SignedClaimHandicap; - type TombstoneDeposit = TombstoneDeposit; - type DepositPerContract = DepositPerContract; - type DepositPerStorageByte = DepositPerStorageByte; - type DepositPerStorageItem = DepositPerStorageItem; - type RentFraction = RentFraction; - type SurchargeReward = SurchargeReward; - type DeletionWeightLimit = DeletionWeightLimit; - type DeletionQueueDepth = DeletionQueueDepth; - type ChainExtension = (); - type WeightPrice = (); - type WeightInfo = pallet_contracts::weights::SubstrateWeight; - type Schedule = Schedule; - type CallStack = [pallet_contracts::Frame; 31]; -} parameter_types! { pub const CollectionCreationPrice: u32 = 0; --- a/pallets/nft/src/tests.rs +++ b/pallets/nft/src/tests.rs @@ -2351,3 +2351,200 @@ ); }); } + +#[test] +fn set_variable_meta_data_on_nft_with_item_owner_permission_flag() { + new_test_ext().execute_with(|| { + default_limits(); + + let collection_id = create_test_collection(&CollectionMode::NFT, 1); + + let origin1 = Origin::signed(1); + + let data = default_nft_data(); + create_test_item(1, &data.into()); + + TemplateModule::set_meta_update_permission_flag(origin1.clone(), collection_id, MetaUpdatePermission::ItemOwner); + + let variable_data = b"test set_variable_meta_data method.".to_vec(); + assert_ok!(TemplateModule::set_variable_meta_data( + origin1, + collection_id, + 1, + variable_data.clone() + )); + + assert_eq!( + TemplateModule::nft_item_id(collection_id, 1) + .unwrap() + .variable_data, + variable_data + ); + }); +} + +#[test] +fn set_variable_meta_data_on_nft_with_item_owner_permission_flag_neg() { + new_test_ext().execute_with(|| { + default_limits(); + + let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 2, 1); + + let origin1 = Origin::signed(1); + let origin2 = Origin::signed(2); + + assert_ok!(TemplateModule::set_mint_permission( + origin2.clone(), + collection_id, + true + )); + assert_ok!(TemplateModule::add_to_white_list( + origin2.clone(), + collection_id, + account(1) + )); + + let data = default_nft_data(); + create_test_item(1, &data.into()); + + TemplateModule::set_meta_update_permission_flag(origin2.clone(), collection_id, MetaUpdatePermission::ItemOwner); + + let variable_data = b"test set_variable_meta_data method.".to_vec(); + assert_noop!(TemplateModule::set_variable_meta_data( + origin2, + collection_id, + 1, + variable_data.clone()), + Error::::NoPermission + ); + }); +} + +#[test] +fn set_variable_meta_data_on_nft_with_admin_flag() { + new_test_ext().execute_with(|| { + default_limits(); + + let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 2, 1); + + let origin1 = Origin::signed(1); + let origin2 = Origin::signed(2); + + assert_ok!(TemplateModule::set_mint_permission( + origin2.clone(), + collection_id, + true + )); + assert_ok!(TemplateModule::add_to_white_list( + origin2.clone(), + collection_id, + account(1) + )); + + assert_ok!(TemplateModule::add_collection_admin( + origin2.clone(), + collection_id, + account(1) + )); + + let data = default_nft_data(); + create_test_item(1, &data.into()); + + TemplateModule::set_meta_update_permission_flag(origin2.clone(), collection_id, MetaUpdatePermission::Admin); + + let variable_data = b"test set_variable_meta_data method.".to_vec(); + assert_ok!(TemplateModule::set_variable_meta_data( + origin1, + collection_id, + 1, + variable_data.clone() + )); + + assert_eq!( + TemplateModule::nft_item_id(collection_id, 1) + .unwrap() + .variable_data, + variable_data + ); + }); +} + +#[test] +fn set_variable_meta_data_on_nft_with_admin_flag_neg() { + new_test_ext().execute_with(|| { + default_limits(); + + let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 2, 1); + + let origin1 = Origin::signed(1); + let origin2 = Origin::signed(2); + + assert_ok!(TemplateModule::set_mint_permission( + origin2.clone(), + collection_id, + true + )); + assert_ok!(TemplateModule::add_to_white_list( + origin2.clone(), + collection_id, + account(1) + )); + + let data = default_nft_data(); + create_test_item(1, &data.into()); + + TemplateModule::set_meta_update_permission_flag(origin2.clone(), collection_id, MetaUpdatePermission::Admin); + + let variable_data = b"test set_variable_meta_data method.".to_vec(); + assert_noop!(TemplateModule::set_variable_meta_data( + origin1, + collection_id, + 1, + variable_data.clone()), + Error::::NoPermission + ); + }); +} + +#[test] +fn set_variable_meta_flag_after_freeze() { + new_test_ext().execute_with(|| { + default_limits(); + + let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 2, 1); + + let origin2 = Origin::signed(2); + + TemplateModule::set_meta_update_permission_flag(origin2.clone(), collection_id, MetaUpdatePermission::None); + assert_noop!(TemplateModule::set_meta_update_permission_flag( + origin2.clone(), + collection_id, + MetaUpdatePermission::Admin), + Error::::MetadataFlagFrozen + ); + }); +} + +#[test] +fn set_variable_meta_data_on_nft_with_none_flag_neg() { + new_test_ext().execute_with(|| { + default_limits(); + + let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 1, 1); + let origin1 = Origin::signed(1); + + let data = default_nft_data(); + create_test_item(1, &data.into()); + + TemplateModule::set_meta_update_permission_flag(origin1.clone(), collection_id, MetaUpdatePermission::None); + + let variable_data = b"test set_variable_meta_data method.".to_vec(); + assert_noop!(TemplateModule::set_variable_meta_data( + origin1.clone(), + collection_id, + 1, + variable_data.clone()), + Error::::MetadataUpdateDenied + ); + }); +} \ No newline at end of file --- a/pallets/scheduler/src/lib.rs +++ b/pallets/scheduler/src/lib.rs @@ -947,900 +947,4 @@ fn root() -> OriginCaller { system::RawOrigin::Root.into() } - - #[test] - fn basic_scheduling_works() { - new_test_ext().execute_with(|| { - let call = Call::Logger(logger::Call::log(42, 1000)); - assert!(!::BaseCallFilter::filter( - &call - )); - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(4), - None, - 127, - root(), - call - )); - run_to_block(3); - assert!(logger::log().is_empty()); - run_to_block(4); - assert_eq!(logger::log(), vec![(root(), 42u32)]); - run_to_block(100); - assert_eq!(logger::log(), vec![(root(), 42u32)]); - }); - } - - #[test] - fn schedule_after_works() { - new_test_ext().execute_with(|| { - run_to_block(2); - let call = Call::Logger(logger::Call::log(42, 1000)); - assert!(!::BaseCallFilter::filter( - &call - )); - // This will schedule the call 3 blocks after the next block... so block 3 + 3 = 6 - assert_ok!(Scheduler::do_schedule( - DispatchTime::After(3), - None, - 127, - root(), - call - )); - run_to_block(5); - assert!(logger::log().is_empty()); - run_to_block(6); - assert_eq!(logger::log(), vec![(root(), 42u32)]); - run_to_block(100); - assert_eq!(logger::log(), vec![(root(), 42u32)]); - }); - } - - #[test] - fn schedule_after_zero_works() { - new_test_ext().execute_with(|| { - run_to_block(2); - let call = Call::Logger(logger::Call::log(42, 1000)); - assert!(!::BaseCallFilter::filter( - &call - )); - assert_ok!(Scheduler::do_schedule( - DispatchTime::After(0), - None, - 127, - root(), - call - )); - // Will trigger on the next block. - run_to_block(3); - assert_eq!(logger::log(), vec![(root(), 42u32)]); - run_to_block(100); - assert_eq!(logger::log(), vec![(root(), 42u32)]); - }); - } - - #[test] - fn periodic_scheduling_works() { - new_test_ext().execute_with(|| { - // at #4, every 3 blocks, 3 times. - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(4), - Some((3, 3)), - 127, - root(), - Call::Logger(logger::Call::log(42, 1000)) - )); - run_to_block(3); - assert!(logger::log().is_empty()); - run_to_block(4); - assert_eq!(logger::log(), vec![(root(), 42u32)]); - run_to_block(6); - assert_eq!(logger::log(), vec![(root(), 42u32)]); - run_to_block(7); - assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]); - run_to_block(9); - assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]); - run_to_block(10); - assert_eq!( - logger::log(), - vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)] - ); - run_to_block(100); - assert_eq!( - logger::log(), - vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)] - ); - }); - } - - #[test] - fn reschedule_works() { - new_test_ext().execute_with(|| { - let call = Call::Logger(logger::Call::log(42, 1000)); - assert!(!::BaseCallFilter::filter( - &call - )); - assert_eq!( - Scheduler::do_schedule(DispatchTime::At(4), None, 127, root(), call).unwrap(), - (4, 0) - ); - - run_to_block(3); - assert!(logger::log().is_empty()); - - assert_eq!( - Scheduler::do_reschedule((4, 0), DispatchTime::At(6)).unwrap(), - (6, 0) - ); - - assert_noop!( - Scheduler::do_reschedule((6, 0), DispatchTime::At(6)), - Error::::RescheduleNoChange - ); - - run_to_block(4); - assert!(logger::log().is_empty()); - - run_to_block(6); - assert_eq!(logger::log(), vec![(root(), 42u32)]); - - run_to_block(100); - assert_eq!(logger::log(), vec![(root(), 42u32)]); - }); - } - - #[test] - fn reschedule_named_works() { - new_test_ext().execute_with(|| { - let call = Call::Logger(logger::Call::log(42, 1000)); - assert!(!::BaseCallFilter::filter( - &call - )); - assert_eq!( - Scheduler::do_schedule_named( - 1u32.encode(), - DispatchTime::At(4), - None, - 127, - root(), - call - ) - .unwrap(), - (4, 0) - ); - - run_to_block(3); - assert!(logger::log().is_empty()); - - assert_eq!( - Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(6)).unwrap(), - (6, 0) - ); - - assert_noop!( - Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(6)), - Error::::RescheduleNoChange - ); - - run_to_block(4); - assert!(logger::log().is_empty()); - - run_to_block(6); - assert_eq!(logger::log(), vec![(root(), 42u32)]); - - run_to_block(100); - assert_eq!(logger::log(), vec![(root(), 42u32)]); - }); - } - - #[test] - fn reschedule_named_perodic_works() { - new_test_ext().execute_with(|| { - let call = Call::Logger(logger::Call::log(42, 1000)); - assert!(!::BaseCallFilter::filter( - &call - )); - assert_eq!( - Scheduler::do_schedule_named( - 1u32.encode(), - DispatchTime::At(4), - Some((3, 3)), - 127, - root(), - call - ) - .unwrap(), - (4, 0) - ); - - run_to_block(3); - assert!(logger::log().is_empty()); - - assert_eq!( - Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(5)).unwrap(), - (5, 0) - ); - assert_eq!( - Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(6)).unwrap(), - (6, 0) - ); - - run_to_block(5); - assert!(logger::log().is_empty()); - - run_to_block(6); - assert_eq!(logger::log(), vec![(root(), 42u32)]); - - assert_eq!( - Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(10)).unwrap(), - (10, 0) - ); - - run_to_block(9); - assert_eq!(logger::log(), vec![(root(), 42u32)]); - - run_to_block(10); - assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]); - - run_to_block(13); - assert_eq!( - logger::log(), - vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)] - ); - - run_to_block(100); - assert_eq!( - logger::log(), - vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)] - ); - }); - } - - #[test] - fn cancel_named_scheduling_works_with_normal_cancel() { - new_test_ext().execute_with(|| { - // at #4. - Scheduler::do_schedule_named( - 1u32.encode(), - DispatchTime::At(4), - None, - 127, - root(), - Call::Logger(logger::Call::log(69, 1000)), - ) - .unwrap(); - let i = Scheduler::do_schedule( - DispatchTime::At(4), - None, - 127, - root(), - Call::Logger(logger::Call::log(42, 1000)), - ) - .unwrap(); - run_to_block(3); - assert!(logger::log().is_empty()); - assert_ok!(Scheduler::do_cancel_named(None, 1u32.encode())); - assert_ok!(Scheduler::do_cancel(None, i)); - run_to_block(100); - assert!(logger::log().is_empty()); - }); - } - - #[test] - fn cancel_named_periodic_scheduling_works() { - new_test_ext().execute_with(|| { - // at #4, every 3 blocks, 3 times. - Scheduler::do_schedule_named( - 1u32.encode(), - DispatchTime::At(4), - Some((3, 3)), - 127, - root(), - Call::Logger(logger::Call::log(42, 1000)), - ) - .unwrap(); - // same id results in error. - assert!(Scheduler::do_schedule_named( - 1u32.encode(), - DispatchTime::At(4), - None, - 127, - root(), - Call::Logger(logger::Call::log(69, 1000)) - ) - .is_err()); - // different id is ok. - Scheduler::do_schedule_named( - 2u32.encode(), - DispatchTime::At(8), - None, - 127, - root(), - Call::Logger(logger::Call::log(69, 1000)), - ) - .unwrap(); - run_to_block(3); - assert!(logger::log().is_empty()); - run_to_block(4); - assert_eq!(logger::log(), vec![(root(), 42u32)]); - run_to_block(6); - assert_ok!(Scheduler::do_cancel_named(None, 1u32.encode())); - run_to_block(100); - assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 69u32)]); - }); - } - - #[test] - fn scheduler_respects_weight_limits() { - new_test_ext().execute_with(|| { - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(4), - None, - 127, - root(), - Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2)) - )); - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(4), - None, - 127, - root(), - Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2)) - )); - // 69 and 42 do not fit together - run_to_block(4); - assert_eq!(logger::log(), vec![(root(), 42u32)]); - run_to_block(5); - assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 69u32)]); - }); - } - - #[test] - fn scheduler_respects_hard_deadlines_more() { - new_test_ext().execute_with(|| { - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(4), - None, - 0, - root(), - Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2)) - )); - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(4), - None, - 0, - root(), - Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2)) - )); - // With base weights, 69 and 42 should not fit together, but do because of hard deadlines - run_to_block(4); - assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 69u32)]); - }); - } - - #[test] - fn scheduler_respects_priority_ordering() { - new_test_ext().execute_with(|| { - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(4), - None, - 1, - root(), - Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2)) - )); - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(4), - None, - 0, - root(), - Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2)) - )); - run_to_block(4); - assert_eq!(logger::log(), vec![(root(), 69u32), (root(), 42u32)]); - }); - } - - #[test] - fn scheduler_respects_priority_ordering_with_soft_deadlines() { - new_test_ext().execute_with(|| { - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(4), - None, - 255, - root(), - Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 3)) - )); - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(4), - None, - 127, - root(), - Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2)) - )); - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(4), - None, - 126, - root(), - Call::Logger(logger::Call::log(2600, MaximumSchedulerWeight::get() / 2)) - )); - - // 2600 does not fit with 69 or 42, but has higher priority, so will go through - run_to_block(4); - assert_eq!(logger::log(), vec![(root(), 2600u32)]); - // 69 and 42 fit together - run_to_block(5); - assert_eq!( - logger::log(), - vec![(root(), 2600u32), (root(), 69u32), (root(), 42u32)] - ); - }); - } - - #[test] - fn on_initialize_weight_is_correct() { - new_test_ext().execute_with(|| { - let base_weight: Weight = - ::DbWeight::get().reads_writes(1, 2); - let base_multiplier = 0; - let named_multiplier = ::DbWeight::get().writes(1); - let periodic_multiplier = - ::DbWeight::get().reads_writes(1, 1); - - // Named - assert_ok!(Scheduler::do_schedule_named( - 1u32.encode(), - DispatchTime::At(1), - None, - 255, - root(), - Call::Logger(logger::Call::log(3, MaximumSchedulerWeight::get() / 3)) - )); - // Anon Periodic - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(1), - Some((1000, 3)), - 128, - root(), - Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 3)) - )); - // Anon - assert_ok!(Scheduler::do_schedule( - DispatchTime::At(1), - None, - 127, - root(), - Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2)) - )); - // Named Periodic - assert_ok!(Scheduler::do_schedule_named( - 2u32.encode(), - DispatchTime::At(1), - Some((1000, 3)), - 126, - root(), - Call::Logger(logger::Call::log(2600, MaximumSchedulerWeight::get() / 2)) - )); - - // Will include the named periodic only - let actual_weight = Scheduler::on_initialize(1); - let call_weight = MaximumSchedulerWeight::get() / 2; - assert_eq!( - actual_weight, - call_weight - + base_weight + base_multiplier - + named_multiplier + periodic_multiplier - ); - assert_eq!(logger::log(), vec![(root(), 2600u32)]); - - // Will include anon and anon periodic - let actual_weight = Scheduler::on_initialize(2); - let call_weight = MaximumSchedulerWeight::get() / 2 + MaximumSchedulerWeight::get() / 3; - assert_eq!( - actual_weight, - call_weight + base_weight + base_multiplier * 2 + periodic_multiplier - ); - assert_eq!( - logger::log(), - vec![(root(), 2600u32), (root(), 69u32), (root(), 42u32)] - ); - - // Will include named only - let actual_weight = Scheduler::on_initialize(3); - let call_weight = MaximumSchedulerWeight::get() / 3; - assert_eq!( - actual_weight, - call_weight + base_weight + base_multiplier + named_multiplier - ); - assert_eq!( - logger::log(), - vec![ - (root(), 2600u32), - (root(), 69u32), - (root(), 42u32), - (root(), 3u32) - ] - ); - - // Will contain none - let actual_weight = Scheduler::on_initialize(4); - assert_eq!(actual_weight, 0); - }); - } - - #[test] - fn root_calls_works() { - new_test_ext().execute_with(|| { - let call = Box::new(Call::Logger(logger::Call::log(69, 1000))); - let call2 = Box::new(Call::Logger(logger::Call::log(42, 1000))); - assert_ok!(Scheduler::schedule_named( - Origin::root(), - 1u32.encode(), - 4, - None, - 127, - call - )); - assert_ok!(Scheduler::schedule(Origin::root(), 4, None, 127, call2)); - run_to_block(3); - // Scheduled calls are in the agenda. - assert_eq!(Agenda::::get(4).len(), 2); - assert!(logger::log().is_empty()); - assert_ok!(Scheduler::cancel_named(Origin::root(), 1u32.encode())); - assert_ok!(Scheduler::cancel(Origin::root(), 4, 1)); - // Scheduled calls are made NONE, so should not effect state - run_to_block(100); - assert!(logger::log().is_empty()); - }); - } - - #[test] - fn fails_to_schedule_task_in_the_past() { - new_test_ext().execute_with(|| { - run_to_block(3); - - let call = Box::new(Call::Logger(logger::Call::log(69, 1000))); - let call2 = Box::new(Call::Logger(logger::Call::log(42, 1000))); - - assert_err!( - Scheduler::schedule_named(Origin::root(), 1u32.encode(), 2, None, 127, call), - Error::::TargetBlockNumberInPast, - ); - - assert_err!( - Scheduler::schedule(Origin::root(), 2, None, 127, call2.clone()), - Error::::TargetBlockNumberInPast, - ); - - assert_err!( - Scheduler::schedule(Origin::root(), 3, None, 127, call2), - Error::::TargetBlockNumberInPast, - ); - }); - } - - #[test] - fn should_use_orign() { - new_test_ext().execute_with(|| { - let call = Box::new(Call::Logger(logger::Call::log(69, 1000))); - let call2 = Box::new(Call::Logger(logger::Call::log(42, 1000))); - assert_ok!(Scheduler::schedule_named( - system::RawOrigin::Signed(1).into(), - 1u32.encode(), - 4, - None, - 127, - call - )); - assert_ok!(Scheduler::schedule( - system::RawOrigin::Signed(1).into(), - 4, - None, - 127, - call2 - )); - run_to_block(3); - // Scheduled calls are in the agenda. - assert_eq!(Agenda::::get(4).len(), 2); - assert!(logger::log().is_empty()); - assert_ok!(Scheduler::cancel_named( - system::RawOrigin::Signed(1).into(), - 1u32.encode() - )); - assert_ok!(Scheduler::cancel(system::RawOrigin::Signed(1).into(), 4, 1)); - // Scheduled calls are made NONE, so should not effect state - run_to_block(100); - assert!(logger::log().is_empty()); - }); - } - - #[test] - fn should_check_orign() { - new_test_ext().execute_with(|| { - let call = Box::new(Call::Logger(logger::Call::log(69, 1000))); - let call2 = Box::new(Call::Logger(logger::Call::log(42, 1000))); - assert_noop!( - Scheduler::schedule_named( - system::RawOrigin::Signed(2).into(), - 1u32.encode(), - 4, - None, - 127, - call - ), - BadOrigin - ); - assert_noop!( - Scheduler::schedule(system::RawOrigin::Signed(2).into(), 4, None, 127, call2), - BadOrigin - ); - }); - } - - #[test] - fn should_check_orign_for_cancel() { - new_test_ext().execute_with(|| { - let call = Box::new(Call::Logger(logger::Call::log_without_filter(69, 1000))); - let call2 = Box::new(Call::Logger(logger::Call::log_without_filter(42, 1000))); - assert_ok!(Scheduler::schedule_named( - system::RawOrigin::Signed(1).into(), - 1u32.encode(), - 4, - None, - 127, - call - )); - assert_ok!(Scheduler::schedule( - system::RawOrigin::Signed(1).into(), - 4, - None, - 127, - call2 - )); - run_to_block(3); - // Scheduled calls are in the agenda. - assert_eq!(Agenda::::get(4).len(), 2); - assert!(logger::log().is_empty()); - assert_noop!( - Scheduler::cancel_named(system::RawOrigin::Signed(2).into(), 1u32.encode()), - BadOrigin - ); - assert_noop!( - Scheduler::cancel(system::RawOrigin::Signed(2).into(), 4, 1), - BadOrigin - ); - assert_noop!( - Scheduler::cancel_named(system::RawOrigin::Root.into(), 1u32.encode()), - BadOrigin - ); - assert_noop!( - Scheduler::cancel(system::RawOrigin::Root.into(), 4, 1), - BadOrigin - ); - run_to_block(5); - assert_eq!( - logger::log(), - vec![ - (system::RawOrigin::Signed(1).into(), 69u32), - (system::RawOrigin::Signed(1).into(), 42u32) - ] - ); - }); - } - - #[test] - fn migration_to_v2_works() { - new_test_ext().execute_with(|| { - for i in 0..3u64 { - let k = i.twox_64_concat(); - let old = vec![ - Some(ScheduledV1 { - maybe_id: None, - priority: i as u8 + 10, - call: Call::Logger(logger::Call::log(96, 100)), - maybe_periodic: None, - }), - None, - Some(ScheduledV1 { - maybe_id: Some(b"test".to_vec()), - priority: 123, - call: Call::Logger(logger::Call::log(69, 1000)), - maybe_periodic: Some((456u64, 10)), - }), - ]; - frame_support::migration::put_storage_value(b"Scheduler", b"Agenda", &k, old); - } - - assert_eq!(StorageVersion::get(), Releases::V1); - - assert!(Scheduler::migrate_v1_to_t2()); - - assert_eq_uvec!( - Agenda::::iter().collect::>(), - vec![ - ( - 0, - vec![ - Some(ScheduledV2 { - maybe_id: None, - priority: 10, - call: Call::Logger(logger::Call::log(96, 100)), - maybe_periodic: None, - origin: root(), - _phantom: PhantomData::::default(), - }), - None, - Some(ScheduledV2 { - maybe_id: Some(b"test".to_vec()), - priority: 123, - call: Call::Logger(logger::Call::log(69, 1000)), - maybe_periodic: Some((456u64, 10)), - origin: root(), - _phantom: PhantomData::::default(), - }), - ] - ), - ( - 1, - vec![ - Some(ScheduledV2 { - maybe_id: None, - priority: 11, - call: Call::Logger(logger::Call::log(96, 100)), - maybe_periodic: None, - origin: root(), - _phantom: PhantomData::::default(), - }), - None, - Some(ScheduledV2 { - maybe_id: Some(b"test".to_vec()), - priority: 123, - call: Call::Logger(logger::Call::log(69, 1000)), - maybe_periodic: Some((456u64, 10)), - origin: root(), - _phantom: PhantomData::::default(), - }), - ] - ), - ( - 2, - vec![ - Some(ScheduledV2 { - maybe_id: None, - priority: 12, - call: Call::Logger(logger::Call::log(96, 100)), - maybe_periodic: None, - origin: root(), - _phantom: PhantomData::::default(), - }), - None, - Some(ScheduledV2 { - maybe_id: Some(b"test".to_vec()), - priority: 123, - call: Call::Logger(logger::Call::log(69, 1000)), - maybe_periodic: Some((456u64, 10)), - origin: root(), - _phantom: PhantomData::::default(), - }), - ] - ) - ] - ); - - assert_eq!(StorageVersion::get(), Releases::V2); - }); - } - - #[test] - fn test_migrate_origin() { - new_test_ext().execute_with(|| { - for i in 0..3u64 { - let k = i.twox_64_concat(); - let old: Vec>> = vec![ - Some(Scheduled { - maybe_id: None, - priority: i as u8 + 10, - call: Call::Logger(logger::Call::log(96, 100)), - origin: 3u32, - maybe_periodic: None, - _phantom: Default::default(), - }), - None, - Some(Scheduled { - maybe_id: Some(b"test".to_vec()), - priority: 123, - origin: 2u32, - call: Call::Logger(logger::Call::log(69, 1000)), - maybe_periodic: Some((456u64, 10)), - _phantom: Default::default(), - }), - ]; - frame_support::migration::put_storage_value(b"Scheduler", b"Agenda", &k, old); - } - - impl From for OriginCaller { - fn from(value: u32) -> Self { - match value { - 3 => system::RawOrigin::Root.into(), - 2 => system::RawOrigin::None.into(), - _ => unimplemented!(), - } - } - } - - Scheduler::migrate_origin::(); - - assert_eq_uvec!( - Agenda::::iter().collect::>(), - vec![ - ( - 0, - vec![ - Some(ScheduledV2::<_, _, OriginCaller, u64> { - maybe_id: None, - priority: 10, - call: Call::Logger(logger::Call::log(96, 100)), - maybe_periodic: None, - origin: system::RawOrigin::Root.into(), - _phantom: PhantomData::::default(), - }), - None, - Some(ScheduledV2 { - maybe_id: Some(b"test".to_vec()), - priority: 123, - call: Call::Logger(logger::Call::log(69, 1000)), - maybe_periodic: Some((456u64, 10)), - origin: system::RawOrigin::None.into(), - _phantom: PhantomData::::default(), - }), - ] - ), - ( - 1, - vec![ - Some(ScheduledV2 { - maybe_id: None, - priority: 11, - call: Call::Logger(logger::Call::log(96, 100)), - maybe_periodic: None, - origin: system::RawOrigin::Root.into(), - _phantom: PhantomData::::default(), - }), - None, - Some(ScheduledV2 { - maybe_id: Some(b"test".to_vec()), - priority: 123, - call: Call::Logger(logger::Call::log(69, 1000)), - maybe_periodic: Some((456u64, 10)), - origin: system::RawOrigin::None.into(), - _phantom: PhantomData::::default(), - }), - ] - ), - ( - 2, - vec![ - Some(ScheduledV2 { - maybe_id: None, - priority: 12, - call: Call::Logger(logger::Call::log(96, 100)), - maybe_periodic: None, - origin: system::RawOrigin::Root.into(), - _phantom: PhantomData::::default(), - }), - None, - Some(ScheduledV2 { - maybe_id: Some(b"test".to_vec()), - priority: 123, - call: Call::Logger(logger::Call::log(69, 1000)), - maybe_periodic: Some((456u64, 10)), - origin: system::RawOrigin::None.into(), - _phantom: PhantomData::::default(), - }), - ] - ) - ] - ); - }); - } } --- a/primitives/nft/src/lib.rs +++ b/primitives/nft/src/lib.rs @@ -144,6 +144,7 @@ pub limits: CollectionLimits, // Collection private restrictions pub variable_on_chain_schema: Vec, // pub const_on_chain_schema: Vec, // + pub meta_update_permission: MetaUpdatePermission, } #[derive(Encode, Decode, Debug, Clone, PartialEq)] @@ -241,6 +242,20 @@ #[derive(Encode, Decode, Debug, Clone, PartialEq)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub enum MetaUpdatePermission { + ItemOwner, + Admin, + None, +} + +impl Default for MetaUpdatePermission { + fn default() -> Self { + Self::ItemOwner + } +} + +#[derive(Encode, Decode, Debug, Clone, PartialEq)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum CreateItemData { NFT(CreateNftData), Fungible(CreateFungibleData),