difftreelog
CORE-37 Metadata update permissions
in: master
7 files changed
node/cli/src/chain_spec.rsdiffbeforeafterboth--- 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![],
pallets/inflation/src/tests.rsdiffbeforeafterboth--- 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!(
pallets/nft/src/lib.rsdiffbeforeafterboth--- 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 = <T as Config>::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::<T>::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::<T>::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<T>,
+ item_id: TokenId) -> DispatchResult {
+
+ match collection.meta_update_permission {
+ MetaUpdatePermission::ItemOwner => {
+ ensure!(
+ Self::is_item_owner(sender, collection, item_id),
+ Error::<T>::NoPermission
+ );
+ },
+ MetaUpdatePermission::Admin => {
+ ensure!(
+ Self::is_owner_or_admin_permissions(collection, sender),
+ Error::<T>::NoPermission
+ );
+ },
+ MetaUpdatePermission::None => {
+ fail!(Error::<T>::MetadataUpdateDenied);
+ },
+ }
+
+ Ok(())
+ }
+
pub fn create_multiple_items_internal(
sender: &T::CrossAccountId,
collection: &CollectionHandle<T>,
pallets/nft/src/mock.rsdiffbeforeafterboth1#![allow(clippy::from_over_into)]23use crate as pallet_template;4use sp_core::H256;5use frame_support::{parameter_types, weights::IdentityFee};6use sp_runtime::{7 traits::{BlakeTwo256, IdentityLookup},8 testing::Header,9 Perbill,10};11use pallet_transaction_payment::{CurrencyAdapter};12use frame_system as system;13use pallet_evm::AddressMapping;14use crate::{EvmBackwardsAddressMapping, CrossAccountId};15use codec::{Encode, Decode};1617type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;18type Block = frame_system::mocking::MockBlock<Test>;1920// Configure a mock runtime to test the pallet.21frame_support::construct_runtime!(22 pub enum Test where23 Block = Block,24 NodeBlock = Block,25 UncheckedExtrinsic = UncheckedExtrinsic,26 {27 System: frame_system::{Pallet, Call, Config, Storage, Event<T>},28 TemplateModule: pallet_template::{Pallet, Call, Storage},29 Balances: pallet_balances::{Pallet, Call, Storage},30 }31);3233parameter_types! {34 pub const BlockHashCount: u64 = 250;35 pub const SS58Prefix: u8 = 42;36}3738impl system::Config for Test {39 type BaseCallFilter = ();40 type BlockWeights = ();41 type BlockLength = ();42 type DbWeight = ();43 type Origin = Origin;44 type Call = Call;45 type Index = u64;46 type BlockNumber = u64;47 type Hash = H256;48 type Hashing = BlakeTwo256;49 type AccountId = u64;50 type Lookup = IdentityLookup<Self::AccountId>;51 type Header = Header;52 type Event = ();53 type BlockHashCount = BlockHashCount;54 type Version = ();55 type PalletInfo = PalletInfo;56 type AccountData = pallet_balances::AccountData<u64>;57 type OnNewAccount = ();58 type OnKilledAccount = ();59 type SystemWeightInfo = ();60 type SS58Prefix = SS58Prefix;61 type OnSetCode = ();62}6364parameter_types! {65 pub const ExistentialDeposit: u64 = 1;66 pub const MaxLocks: u32 = 50;67}68//frame_system::Module<Test>;69impl pallet_balances::Config for Test {70 type AccountStore = System;71 type Balance = u64;72 type DustRemoval = ();73 type Event = ();74 type ExistentialDeposit = ExistentialDeposit;75 type WeightInfo = ();76 type MaxLocks = MaxLocks;77}7879parameter_types! {80 pub const TransactionByteFee: u64 = 1;81}8283impl pallet_transaction_payment::Config for Test {84 type OnChargeTransaction = CurrencyAdapter<pallet_balances::Pallet<Test>, ()>;85 type TransactionByteFee = TransactionByteFee;86 type WeightToFee = IdentityFee<u64>;87 type FeeMultiplierUpdate = ();88}8990parameter_types! {91 pub const MinimumPeriod: u64 = 1;92}93impl pallet_timestamp::Config for Test {94 type Moment = u64;95 type OnTimestampSet = ();96 type MinimumPeriod = MinimumPeriod;97 type WeightInfo = ();98}99100type Timestamp = pallet_timestamp::Pallet<Test>;101type Randomness = pallet_randomness_collective_flip::Pallet<Test>;102103parameter_types! {104 pub const TombstoneDeposit: u64 = 1;105 pub const DepositPerContract: u64 = 1;106 pub const DepositPerStorageByte: u64 = 1;107 pub const DepositPerStorageItem: u64 = 1;108 pub RentFraction: Perbill = Perbill::from_rational(1u32, 30 * 24 * 60 * 10);109 pub const SurchargeReward: u64 = 1;110 pub const SignedClaimHandicap: u32 = 2;111 pub DeletionWeightLimit: u64 = u64::MAX;//Perbill::from_percent(10);112 pub DeletionQueueDepth: u32 = 10;113 pub Schedule: pallet_contracts::Schedule<Test> = Default::default();114}115116impl pallet_contracts::Config for Test {117 type Time = Timestamp;118 type Randomness = Randomness;119 type Currency = pallet_balances::Pallet<Test>;120 type Event = ();121 type RentPayment = ();122 type SignedClaimHandicap = SignedClaimHandicap;123 type TombstoneDeposit = TombstoneDeposit;124 type DepositPerContract = DepositPerContract;125 type DepositPerStorageByte = DepositPerStorageByte;126 type DepositPerStorageItem = DepositPerStorageItem;127 type RentFraction = RentFraction;128 type SurchargeReward = SurchargeReward;129 type DeletionWeightLimit = DeletionWeightLimit;130 type DeletionQueueDepth = DeletionQueueDepth;131 type ChainExtension = ();132 type WeightPrice = ();133 type WeightInfo = pallet_contracts::weights::SubstrateWeight<Self>;134 type Schedule = Schedule;135 type CallStack = [pallet_contracts::Frame<Self>; 31];136}137138parameter_types! {139 pub const CollectionCreationPrice: u32 = 0;140 pub TreasuryAccountId: u64 = 1234;141 pub EthereumChainId: u32 = 1111;142}143144pub struct TestEvmAddressMapping;145impl AddressMapping<u64> for TestEvmAddressMapping {146 fn into_account_id(_addr: sp_core::H160) -> u64 {147 unimplemented!()148 }149}150151pub struct TestEvmBackwardsAddressMapping;152impl EvmBackwardsAddressMapping<u64> for TestEvmBackwardsAddressMapping {153 fn from_account_id(_account_id: u64) -> sp_core::H160 {154 unimplemented!()155 }156}157158#[derive(Encode, Decode, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]159pub struct TestCrossAccountId(u64, sp_core::H160);160impl CrossAccountId<u64> for TestCrossAccountId {161 fn from_sub(sub: u64) -> Self {162 let mut eth = [0; 20];163 eth[12..20].copy_from_slice(&sub.to_be_bytes());164 Self(sub, sp_core::H160(eth))165 }166 fn as_sub(&self) -> &u64 {167 &self.0168 }169 fn from_eth(_eth: sp_core::H160) -> Self {170 unimplemented!()171 }172 fn as_eth(&self) -> &sp_core::H160 {173 &self.1174 }175}176177pub struct TestEtheremTransactionSender;178impl pallet_ethereum::EthereumTransactionSender for TestEtheremTransactionSender {179 fn submit_logs_transaction(180 _tx: pallet_ethereum::Transaction,181 _logs: Vec<pallet_ethereum::Log>,182 ) -> Result<(), sp_runtime::DispatchError> {183 Ok(())184 }185}186187impl pallet_template::Config for Test {188 type Event = ();189 type WeightInfo = ();190 type CollectionCreationPrice = CollectionCreationPrice;191 type Currency = pallet_balances::Pallet<Test>;192 type TreasuryAccountId = TreasuryAccountId;193 type EvmAddressMapping = TestEvmAddressMapping;194 type EvmBackwardsAddressMapping = TestEvmBackwardsAddressMapping;195 type CrossAccountId = TestCrossAccountId;196 type EthereumChainId = EthereumChainId;197 type EthereumTransactionSender = TestEtheremTransactionSender;198}199200// Build genesis storage according to the mock runtime.201pub fn new_test_ext() -> sp_io::TestExternalities {202 system::GenesisConfig::default()203 .build_storage::<Test>()204 .unwrap()205 .into()206}1#![allow(clippy::from_over_into)]23use crate as pallet_template;4use sp_core::H256;5use frame_support::{parameter_types, weights::IdentityFee};6use sp_runtime::{7 traits::{BlakeTwo256, IdentityLookup},8 testing::Header,9 Perbill,10};11use pallet_transaction_payment::{CurrencyAdapter};12use frame_system as system;13use pallet_evm::AddressMapping;14use crate::{EvmBackwardsAddressMapping, CrossAccountId};15use codec::{Encode, Decode};1617type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;18type Block = frame_system::mocking::MockBlock<Test>;1920// Configure a mock runtime to test the pallet.21frame_support::construct_runtime!(22 pub enum Test where23 Block = Block,24 NodeBlock = Block,25 UncheckedExtrinsic = UncheckedExtrinsic,26 {27 System: frame_system::{Pallet, Call, Config, Storage, Event<T>},28 TemplateModule: pallet_template::{Pallet, Call, Storage},29 Balances: pallet_balances::{Pallet, Call, Storage},30 }31);3233parameter_types! {34 pub const BlockHashCount: u64 = 250;35 pub const SS58Prefix: u8 = 42;36}3738impl system::Config for Test {39 type BaseCallFilter = ();40 type BlockWeights = ();41 type BlockLength = ();42 type DbWeight = ();43 type Origin = Origin;44 type Call = Call;45 type Index = u64;46 type BlockNumber = u64;47 type Hash = H256;48 type Hashing = BlakeTwo256;49 type AccountId = u64;50 type Lookup = IdentityLookup<Self::AccountId>;51 type Header = Header;52 type Event = ();53 type BlockHashCount = BlockHashCount;54 type Version = ();55 type PalletInfo = PalletInfo;56 type AccountData = pallet_balances::AccountData<u64>;57 type OnNewAccount = ();58 type OnKilledAccount = ();59 type SystemWeightInfo = ();60 type SS58Prefix = SS58Prefix;61 type OnSetCode = ();62}6364parameter_types! {65 pub const ExistentialDeposit: u64 = 1;66 pub const MaxLocks: u32 = 50;67}68//frame_system::Module<Test>;69impl pallet_balances::Config for Test {70 type AccountStore = System;71 type Balance = u64;72 type DustRemoval = ();73 type Event = ();74 type ExistentialDeposit = ExistentialDeposit;75 type WeightInfo = ();76 type MaxLocks = MaxLocks;77 type MaxReserves = ();78 type ReserveIdentifier = [u8; 8];79}8081parameter_types! {82 pub const TransactionByteFee: u64 = 1;83}8485impl pallet_transaction_payment::Config for Test {86 type OnChargeTransaction = CurrencyAdapter<pallet_balances::Pallet<Test>, ()>;87 type TransactionByteFee = TransactionByteFee;88 type WeightToFee = IdentityFee<u64>;89 type FeeMultiplierUpdate = ();90}9192parameter_types! {93 pub const MinimumPeriod: u64 = 1;94}95impl pallet_timestamp::Config for Test {96 type Moment = u64;97 type OnTimestampSet = ();98 type MinimumPeriod = MinimumPeriod;99 type WeightInfo = ();100}101102type Timestamp = pallet_timestamp::Pallet<Test>;103type Randomness = pallet_randomness_collective_flip::Pallet<Test>;104105parameter_types! {106 pub const CollectionCreationPrice: u32 = 0;107 pub TreasuryAccountId: u64 = 1234;108 pub EthereumChainId: u32 = 1111;109}110111pub struct TestEvmAddressMapping;112impl AddressMapping<u64> for TestEvmAddressMapping {113 fn into_account_id(_addr: sp_core::H160) -> u64 {114 unimplemented!()115 }116}117118pub struct TestEvmBackwardsAddressMapping;119impl EvmBackwardsAddressMapping<u64> for TestEvmBackwardsAddressMapping {120 fn from_account_id(_account_id: u64) -> sp_core::H160 {121 unimplemented!()122 }123}124125#[derive(Encode, Decode, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]126pub struct TestCrossAccountId(u64, sp_core::H160);127impl CrossAccountId<u64> for TestCrossAccountId {128 fn from_sub(sub: u64) -> Self {129 let mut eth = [0; 20];130 eth[12..20].copy_from_slice(&sub.to_be_bytes());131 Self(sub, sp_core::H160(eth))132 }133 fn as_sub(&self) -> &u64 {134 &self.0135 }136 fn from_eth(_eth: sp_core::H160) -> Self {137 unimplemented!()138 }139 fn as_eth(&self) -> &sp_core::H160 {140 &self.1141 }142}143144pub struct TestEtheremTransactionSender;145impl pallet_ethereum::EthereumTransactionSender for TestEtheremTransactionSender {146 fn submit_logs_transaction(147 _tx: pallet_ethereum::Transaction,148 _logs: Vec<pallet_ethereum::Log>,149 ) -> Result<(), sp_runtime::DispatchError> {150 Ok(())151 }152}153154impl pallet_template::Config for Test {155 type Event = ();156 type WeightInfo = ();157 type CollectionCreationPrice = CollectionCreationPrice;158 type Currency = pallet_balances::Pallet<Test>;159 type TreasuryAccountId = TreasuryAccountId;160 type EvmAddressMapping = TestEvmAddressMapping;161 type EvmBackwardsAddressMapping = TestEvmBackwardsAddressMapping;162 type CrossAccountId = TestCrossAccountId;163 type EthereumChainId = EthereumChainId;164 type EthereumTransactionSender = TestEtheremTransactionSender;165}166167// Build genesis storage according to the mock runtime.168pub fn new_test_ext() -> sp_io::TestExternalities {169 system::GenesisConfig::default()170 .build_storage::<Test>()171 .unwrap()172 .into()173}pallets/nft/src/tests.rsdiffbeforeafterboth--- 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::<Test>::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::<Test>::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::<Test>::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::<Test>::MetadataUpdateDenied
+ );
+ });
+}
\ No newline at end of file
pallets/scheduler/src/lib.rsdiffbeforeafterboth--- 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!(!<Test as frame_system::Config>::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!(!<Test as frame_system::Config>::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!(!<Test as frame_system::Config>::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!(!<Test as frame_system::Config>::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::<Test>::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!(!<Test as frame_system::Config>::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::<Test>::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!(!<Test as frame_system::Config>::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 =
- <Test as frame_system::Config>::DbWeight::get().reads_writes(1, 2);
- let base_multiplier = 0;
- let named_multiplier = <Test as frame_system::Config>::DbWeight::get().writes(1);
- let periodic_multiplier =
- <Test as frame_system::Config>::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::<Test>::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::<Test>::TargetBlockNumberInPast,
- );
-
- assert_err!(
- Scheduler::schedule(Origin::root(), 2, None, 127, call2.clone()),
- Error::<Test>::TargetBlockNumberInPast,
- );
-
- assert_err!(
- Scheduler::schedule(Origin::root(), 3, None, 127, call2),
- Error::<Test>::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::<Test>::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::<Test>::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::<Test>::iter().collect::<Vec<_>>(),
- 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::<u64>::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::<u64>::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::<u64>::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::<u64>::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::<u64>::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::<u64>::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<Option<Scheduled<_, _, u32, u64>>> = 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<u32> for OriginCaller {
- fn from(value: u32) -> Self {
- match value {
- 3 => system::RawOrigin::Root.into(),
- 2 => system::RawOrigin::None.into(),
- _ => unimplemented!(),
- }
- }
- }
-
- Scheduler::migrate_origin::<u32>();
-
- assert_eq_uvec!(
- Agenda::<Test>::iter().collect::<Vec<_>>(),
- 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::<u64>::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::<u64>::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::<u64>::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::<u64>::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::<u64>::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::<u64>::default(),
- }),
- ]
- )
- ]
- );
- });
- }
}
primitives/nft/src/lib.rsdiffbeforeafterboth--- a/primitives/nft/src/lib.rs
+++ b/primitives/nft/src/lib.rs
@@ -144,6 +144,7 @@
pub limits: CollectionLimits<T::BlockNumber>, // Collection private restrictions
pub variable_on_chain_schema: Vec<u8>, //
pub const_on_chain_schema: Vec<u8>, //
+ 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),