pub mod migration;
use frame_support::{
	dispatch::DispatchResult,
	ensure,
	pallet_prelude::Weight,
	traits::{Currency, Get, ReservableCurrency},
};
use frame_system::{self, ensure_root, ensure_signed};
use primitives::{HeadData, Id as ParaId, ValidationCode, LOWEST_PUBLIC_ID, MIN_CODE_SIZE};
use runtime_parachains::{
	configuration, ensure_parachain,
	paras::{self, ParaGenesisArgs, UpgradeStrategy},
	Origin, ParaLifecycle,
};
use sp_std::{prelude::*, result};
use crate::traits::{OnSwap, Registrar};
pub use pallet::*;
use parity_scale_codec::{Decode, Encode};
use runtime_parachains::paras::{OnNewHead, ParaKind};
use scale_info::TypeInfo;
use sp_runtime::{
	traits::{CheckedSub, Saturating},
	RuntimeDebug,
};
#[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, TypeInfo)]
pub struct ParaInfo<Account, Balance> {
	pub(crate) manager: Account,
	deposit: Balance,
	locked: Option<bool>,
}
impl<Account, Balance> ParaInfo<Account, Balance> {
	pub fn is_locked(&self) -> bool {
		self.locked.unwrap_or(false)
	}
}
type BalanceOf<T> =
	<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
pub trait WeightInfo {
	fn reserve() -> Weight;
	fn register() -> Weight;
	fn force_register() -> Weight;
	fn deregister() -> Weight;
	fn swap() -> Weight;
	fn schedule_code_upgrade(b: u32) -> Weight;
	fn set_current_head(b: u32) -> Weight;
}
pub struct TestWeightInfo;
impl WeightInfo for TestWeightInfo {
	fn reserve() -> Weight {
		Weight::zero()
	}
	fn register() -> Weight {
		Weight::zero()
	}
	fn force_register() -> Weight {
		Weight::zero()
	}
	fn deregister() -> Weight {
		Weight::zero()
	}
	fn swap() -> Weight {
		Weight::zero()
	}
	fn schedule_code_upgrade(_b: u32) -> Weight {
		Weight::zero()
	}
	fn set_current_head(_b: u32) -> Weight {
		Weight::zero()
	}
}
#[frame_support::pallet]
pub mod pallet {
	use super::*;
	use frame_support::pallet_prelude::*;
	use frame_system::pallet_prelude::*;
	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
	#[pallet::pallet]
	#[pallet::without_storage_info]
	#[pallet::storage_version(STORAGE_VERSION)]
	pub struct Pallet<T>(_);
	#[pallet::config]
	#[pallet::disable_frame_system_supertrait_check]
	pub trait Config: configuration::Config + paras::Config {
		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
		type RuntimeOrigin: From<<Self as frame_system::Config>::RuntimeOrigin>
			+ Into<result::Result<Origin, <Self as Config>::RuntimeOrigin>>;
		type Currency: ReservableCurrency<Self::AccountId>;
		type OnSwap: crate::traits::OnSwap;
		#[pallet::constant]
		type ParaDeposit: Get<BalanceOf<Self>>;
		#[pallet::constant]
		type DataDepositPerByte: Get<BalanceOf<Self>>;
		type WeightInfo: WeightInfo;
	}
	#[pallet::event]
	#[pallet::generate_deposit(pub(super) fn deposit_event)]
	pub enum Event<T: Config> {
		Registered { para_id: ParaId, manager: T::AccountId },
		Deregistered { para_id: ParaId },
		Reserved { para_id: ParaId, who: T::AccountId },
		Swapped { para_id: ParaId, other_id: ParaId },
	}
	#[pallet::error]
	pub enum Error<T> {
		NotRegistered,
		AlreadyRegistered,
		NotOwner,
		CodeTooLarge,
		HeadDataTooLarge,
		NotParachain,
		NotParathread,
		CannotDeregister,
		CannotDowngrade,
		CannotUpgrade,
		ParaLocked,
		NotReserved,
		InvalidCode,
		CannotSwap,
	}
	#[pallet::storage]
	pub(super) type PendingSwap<T> = StorageMap<_, Twox64Concat, ParaId, ParaId>;
	#[pallet::storage]
	pub type Paras<T: Config> =
		StorageMap<_, Twox64Concat, ParaId, ParaInfo<T::AccountId, BalanceOf<T>>>;
	#[pallet::storage]
	pub type NextFreeParaId<T> = StorageValue<_, ParaId, ValueQuery>;
	#[pallet::genesis_config]
	pub struct GenesisConfig<T: Config> {
		#[serde(skip)]
		pub _config: sp_std::marker::PhantomData<T>,
		pub next_free_para_id: ParaId,
	}
	impl<T: Config> Default for GenesisConfig<T> {
		fn default() -> Self {
			GenesisConfig { next_free_para_id: LOWEST_PUBLIC_ID, _config: Default::default() }
		}
	}
	#[pallet::genesis_build]
	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
		fn build(&self) {
			NextFreeParaId::<T>::put(self.next_free_para_id);
		}
	}
	#[pallet::hooks]
	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
	#[pallet::call]
	impl<T: Config> Pallet<T> {
		#[pallet::call_index(0)]
		#[pallet::weight(<T as Config>::WeightInfo::register())]
		pub fn register(
			origin: OriginFor<T>,
			id: ParaId,
			genesis_head: HeadData,
			validation_code: ValidationCode,
		) -> DispatchResult {
			let who = ensure_signed(origin)?;
			Self::do_register(who, None, id, genesis_head, validation_code, true)?;
			Ok(())
		}
		#[pallet::call_index(1)]
		#[pallet::weight(<T as Config>::WeightInfo::force_register())]
		pub fn force_register(
			origin: OriginFor<T>,
			who: T::AccountId,
			deposit: BalanceOf<T>,
			id: ParaId,
			genesis_head: HeadData,
			validation_code: ValidationCode,
		) -> DispatchResult {
			ensure_root(origin)?;
			Self::do_register(who, Some(deposit), id, genesis_head, validation_code, false)
		}
		#[pallet::call_index(2)]
		#[pallet::weight(<T as Config>::WeightInfo::deregister())]
		pub fn deregister(origin: OriginFor<T>, id: ParaId) -> DispatchResult {
			Self::ensure_root_para_or_owner(origin, id)?;
			Self::do_deregister(id)
		}
		#[pallet::call_index(3)]
		#[pallet::weight(<T as Config>::WeightInfo::swap())]
		pub fn swap(origin: OriginFor<T>, id: ParaId, other: ParaId) -> DispatchResult {
			Self::ensure_root_para_or_owner(origin, id)?;
			if id == other {
				PendingSwap::<T>::remove(id);
				return Ok(())
			}
			let id_lifecycle =
				paras::Pallet::<T>::lifecycle(id).ok_or(Error::<T>::NotRegistered)?;
			if PendingSwap::<T>::get(other) == Some(id) {
				let other_lifecycle =
					paras::Pallet::<T>::lifecycle(other).ok_or(Error::<T>::NotRegistered)?;
				if id_lifecycle == ParaLifecycle::Parachain &&
					other_lifecycle == ParaLifecycle::Parathread
				{
					Self::do_thread_and_chain_swap(id, other);
				} else if id_lifecycle == ParaLifecycle::Parathread &&
					other_lifecycle == ParaLifecycle::Parachain
				{
					Self::do_thread_and_chain_swap(other, id);
				} else if id_lifecycle == ParaLifecycle::Parachain &&
					other_lifecycle == ParaLifecycle::Parachain
				{
					T::OnSwap::on_swap(id, other);
				} else {
					return Err(Error::<T>::CannotSwap.into())
				}
				Self::deposit_event(Event::<T>::Swapped { para_id: id, other_id: other });
				PendingSwap::<T>::remove(other);
			} else {
				PendingSwap::<T>::insert(id, other);
			}
			Ok(())
		}
		#[pallet::call_index(4)]
		#[pallet::weight(T::DbWeight::get().reads_writes(1, 1))]
		pub fn remove_lock(origin: OriginFor<T>, para: ParaId) -> DispatchResult {
			Self::ensure_root_or_para(origin, para)?;
			<Self as Registrar>::remove_lock(para);
			Ok(())
		}
		#[pallet::call_index(5)]
		#[pallet::weight(<T as Config>::WeightInfo::reserve())]
		pub fn reserve(origin: OriginFor<T>) -> DispatchResult {
			let who = ensure_signed(origin)?;
			let id = NextFreeParaId::<T>::get().max(LOWEST_PUBLIC_ID);
			Self::do_reserve(who, None, id)?;
			NextFreeParaId::<T>::set(id + 1);
			Ok(())
		}
		#[pallet::call_index(6)]
		#[pallet::weight(T::DbWeight::get().reads_writes(1, 1))]
		pub fn add_lock(origin: OriginFor<T>, para: ParaId) -> DispatchResult {
			Self::ensure_root_para_or_owner(origin, para)?;
			<Self as Registrar>::apply_lock(para);
			Ok(())
		}
		#[pallet::call_index(7)]
		#[pallet::weight(<T as Config>::WeightInfo::schedule_code_upgrade(new_code.0.len() as u32))]
		pub fn schedule_code_upgrade(
			origin: OriginFor<T>,
			para: ParaId,
			new_code: ValidationCode,
		) -> DispatchResult {
			Self::ensure_root_para_or_owner(origin, para)?;
			runtime_parachains::schedule_code_upgrade::<T>(
				para,
				new_code,
				UpgradeStrategy::ApplyAtExpectedBlock,
			)?;
			Ok(())
		}
		#[pallet::call_index(8)]
		#[pallet::weight(<T as Config>::WeightInfo::set_current_head(new_head.0.len() as u32))]
		pub fn set_current_head(
			origin: OriginFor<T>,
			para: ParaId,
			new_head: HeadData,
		) -> DispatchResult {
			Self::ensure_root_para_or_owner(origin, para)?;
			runtime_parachains::set_current_head::<T>(para, new_head);
			Ok(())
		}
	}
}
impl<T: Config> Registrar for Pallet<T> {
	type AccountId = T::AccountId;
	fn manager_of(id: ParaId) -> Option<T::AccountId> {
		Some(Paras::<T>::get(id)?.manager)
	}
	fn parachains() -> Vec<ParaId> {
		paras::Parachains::<T>::get()
	}
	fn is_parathread(id: ParaId) -> bool {
		paras::Pallet::<T>::is_parathread(id)
	}
	fn is_parachain(id: ParaId) -> bool {
		paras::Pallet::<T>::is_parachain(id)
	}
	fn apply_lock(id: ParaId) {
		Paras::<T>::mutate(id, |x| x.as_mut().map(|info| info.locked = Some(true)));
	}
	fn remove_lock(id: ParaId) {
		Paras::<T>::mutate(id, |x| x.as_mut().map(|info| info.locked = Some(false)));
	}
	fn register(
		manager: T::AccountId,
		id: ParaId,
		genesis_head: HeadData,
		validation_code: ValidationCode,
	) -> DispatchResult {
		Self::do_register(manager, None, id, genesis_head, validation_code, false)
	}
	fn deregister(id: ParaId) -> DispatchResult {
		Self::do_deregister(id)
	}
	fn make_parachain(id: ParaId) -> DispatchResult {
		ensure!(
			paras::Pallet::<T>::lifecycle(id) == Some(ParaLifecycle::Parathread),
			Error::<T>::NotParathread
		);
		runtime_parachains::schedule_parathread_upgrade::<T>(id)
			.map_err(|_| Error::<T>::CannotUpgrade)?;
		Ok(())
	}
	fn make_parathread(id: ParaId) -> DispatchResult {
		ensure!(
			paras::Pallet::<T>::lifecycle(id) == Some(ParaLifecycle::Parachain),
			Error::<T>::NotParachain
		);
		runtime_parachains::schedule_parachain_downgrade::<T>(id)
			.map_err(|_| Error::<T>::CannotDowngrade)?;
		Ok(())
	}
	#[cfg(any(feature = "runtime-benchmarks", test))]
	fn worst_head_data() -> HeadData {
		let max_head_size = configuration::ActiveConfig::<T>::get().max_head_data_size;
		assert!(max_head_size > 0, "max_head_data can't be zero for generating worst head data.");
		vec![0u8; max_head_size as usize].into()
	}
	#[cfg(any(feature = "runtime-benchmarks", test))]
	fn worst_validation_code() -> ValidationCode {
		let max_code_size = configuration::ActiveConfig::<T>::get().max_code_size;
		assert!(max_code_size > 0, "max_code_size can't be zero for generating worst code data.");
		let validation_code = vec![0u8; max_code_size as usize];
		validation_code.into()
	}
	#[cfg(any(feature = "runtime-benchmarks", test))]
	fn execute_pending_transitions() {
		use runtime_parachains::shared;
		shared::Pallet::<T>::set_session_index(shared::Pallet::<T>::scheduled_session());
		paras::Pallet::<T>::test_on_new_session();
	}
}
impl<T: Config> Pallet<T> {
	fn ensure_root_para_or_owner(
		origin: <T as frame_system::Config>::RuntimeOrigin,
		id: ParaId,
	) -> DispatchResult {
		ensure_signed(origin.clone())
			.map_err(|e| e.into())
			.and_then(|who| -> DispatchResult {
				let para_info = Paras::<T>::get(id).ok_or(Error::<T>::NotRegistered)?;
				ensure!(!para_info.is_locked(), Error::<T>::ParaLocked);
				ensure!(para_info.manager == who, Error::<T>::NotOwner);
				Ok(())
			})
			.or_else(|_| -> DispatchResult { Self::ensure_root_or_para(origin, id) })
	}
	fn ensure_root_or_para(
		origin: <T as frame_system::Config>::RuntimeOrigin,
		id: ParaId,
	) -> DispatchResult {
		if let Ok(caller_id) = ensure_parachain(<T as Config>::RuntimeOrigin::from(origin.clone()))
		{
			ensure!(caller_id == id, Error::<T>::NotOwner);
		} else {
			ensure_root(origin.clone())?;
		}
		Ok(())
	}
	fn do_reserve(
		who: T::AccountId,
		deposit_override: Option<BalanceOf<T>>,
		id: ParaId,
	) -> DispatchResult {
		ensure!(!Paras::<T>::contains_key(id), Error::<T>::AlreadyRegistered);
		ensure!(paras::Pallet::<T>::lifecycle(id).is_none(), Error::<T>::AlreadyRegistered);
		let deposit = deposit_override.unwrap_or_else(T::ParaDeposit::get);
		<T as Config>::Currency::reserve(&who, deposit)?;
		let info = ParaInfo { manager: who.clone(), deposit, locked: None };
		Paras::<T>::insert(id, info);
		Self::deposit_event(Event::<T>::Reserved { para_id: id, who });
		Ok(())
	}
	fn do_register(
		who: T::AccountId,
		deposit_override: Option<BalanceOf<T>>,
		id: ParaId,
		genesis_head: HeadData,
		validation_code: ValidationCode,
		ensure_reserved: bool,
	) -> DispatchResult {
		let deposited = if let Some(para_data) = Paras::<T>::get(id) {
			ensure!(para_data.manager == who, Error::<T>::NotOwner);
			ensure!(!para_data.is_locked(), Error::<T>::ParaLocked);
			para_data.deposit
		} else {
			ensure!(!ensure_reserved, Error::<T>::NotReserved);
			Default::default()
		};
		ensure!(paras::Pallet::<T>::lifecycle(id).is_none(), Error::<T>::AlreadyRegistered);
		let (genesis, deposit) =
			Self::validate_onboarding_data(genesis_head, validation_code, ParaKind::Parathread)?;
		let deposit = deposit_override.unwrap_or(deposit);
		if let Some(additional) = deposit.checked_sub(&deposited) {
			<T as Config>::Currency::reserve(&who, additional)?;
		} else if let Some(rebate) = deposited.checked_sub(&deposit) {
			<T as Config>::Currency::unreserve(&who, rebate);
		};
		let info = ParaInfo { manager: who.clone(), deposit, locked: None };
		Paras::<T>::insert(id, info);
		let res = runtime_parachains::schedule_para_initialize::<T>(id, genesis);
		debug_assert!(res.is_ok());
		Self::deposit_event(Event::<T>::Registered { para_id: id, manager: who });
		Ok(())
	}
	fn do_deregister(id: ParaId) -> DispatchResult {
		match paras::Pallet::<T>::lifecycle(id) {
			Some(ParaLifecycle::Parathread) | None => {},
			_ => return Err(Error::<T>::NotParathread.into()),
		}
		runtime_parachains::schedule_para_cleanup::<T>(id)
			.map_err(|_| Error::<T>::CannotDeregister)?;
		if let Some(info) = Paras::<T>::take(&id) {
			<T as Config>::Currency::unreserve(&info.manager, info.deposit);
		}
		PendingSwap::<T>::remove(id);
		Self::deposit_event(Event::<T>::Deregistered { para_id: id });
		Ok(())
	}
	fn validate_onboarding_data(
		genesis_head: HeadData,
		validation_code: ValidationCode,
		para_kind: ParaKind,
	) -> Result<(ParaGenesisArgs, BalanceOf<T>), sp_runtime::DispatchError> {
		let config = configuration::ActiveConfig::<T>::get();
		ensure!(validation_code.0.len() >= MIN_CODE_SIZE as usize, Error::<T>::InvalidCode);
		ensure!(validation_code.0.len() <= config.max_code_size as usize, Error::<T>::CodeTooLarge);
		ensure!(
			genesis_head.0.len() <= config.max_head_data_size as usize,
			Error::<T>::HeadDataTooLarge
		);
		let per_byte_fee = T::DataDepositPerByte::get();
		let deposit = T::ParaDeposit::get()
			.saturating_add(per_byte_fee.saturating_mul((genesis_head.0.len() as u32).into()))
			.saturating_add(per_byte_fee.saturating_mul(config.max_code_size.into()));
		Ok((ParaGenesisArgs { genesis_head, validation_code, para_kind }, deposit))
	}
	fn do_thread_and_chain_swap(to_downgrade: ParaId, to_upgrade: ParaId) {
		let res1 = runtime_parachains::schedule_parachain_downgrade::<T>(to_downgrade);
		debug_assert!(res1.is_ok());
		let res2 = runtime_parachains::schedule_parathread_upgrade::<T>(to_upgrade);
		debug_assert!(res2.is_ok());
		T::OnSwap::on_swap(to_upgrade, to_downgrade);
	}
}
impl<T: Config> OnNewHead for Pallet<T> {
	fn on_new_head(id: ParaId, _head: &HeadData) -> Weight {
		let mut writes = 0;
		if let Some(mut info) = Paras::<T>::get(id) {
			if info.locked.is_none() {
				info.locked = Some(true);
				Paras::<T>::insert(id, info);
				writes += 1;
			}
		}
		T::DbWeight::get().reads_writes(1, writes)
	}
}
#[cfg(test)]
mod tests {
	use super::*;
	use crate::{
		mock::conclude_pvf_checking, paras_registrar, traits::Registrar as RegistrarTrait,
	};
	use frame_support::{
		assert_noop, assert_ok, derive_impl,
		error::BadOrigin,
		parameter_types,
		traits::{ConstU32, OnFinalize, OnInitialize},
	};
	use frame_system::limits;
	use pallet_balances::Error as BalancesError;
	use primitives::{Balance, BlockNumber, SessionIndex, MAX_CODE_SIZE};
	use runtime_parachains::{configuration, origin, shared};
	use sp_core::H256;
	use sp_io::TestExternalities;
	use sp_keyring::Sr25519Keyring;
	use sp_runtime::{
		traits::{BlakeTwo256, IdentityLookup},
		transaction_validity::TransactionPriority,
		BuildStorage, Perbill,
	};
	use sp_std::collections::btree_map::BTreeMap;
	type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
	type Block = frame_system::mocking::MockBlockU32<Test>;
	frame_support::construct_runtime!(
		pub enum Test
		{
			System: frame_system,
			Balances: pallet_balances,
			Configuration: configuration,
			Parachains: paras,
			ParasShared: shared,
			Registrar: paras_registrar,
			ParachainsOrigin: origin,
		}
	);
	impl<C> frame_system::offchain::SendTransactionTypes<C> for Test
	where
		RuntimeCall: From<C>,
	{
		type Extrinsic = UncheckedExtrinsic;
		type OverarchingCall = RuntimeCall;
	}
	const NORMAL_RATIO: Perbill = Perbill::from_percent(75);
	parameter_types! {
		pub const BlockHashCount: u32 = 250;
		pub BlockWeights: limits::BlockWeights =
			frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1024, u64::MAX));
		pub BlockLength: limits::BlockLength =
			limits::BlockLength::max_with_normal_ratio(4 * 1024 * 1024, NORMAL_RATIO);
	}
	#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
	impl frame_system::Config for Test {
		type BaseCallFilter = frame_support::traits::Everything;
		type RuntimeOrigin = RuntimeOrigin;
		type RuntimeCall = RuntimeCall;
		type Nonce = u64;
		type Hash = H256;
		type Hashing = BlakeTwo256;
		type AccountId = u64;
		type Lookup = IdentityLookup<u64>;
		type Block = Block;
		type RuntimeEvent = RuntimeEvent;
		type BlockHashCount = BlockHashCount;
		type DbWeight = ();
		type BlockWeights = BlockWeights;
		type BlockLength = BlockLength;
		type Version = ();
		type PalletInfo = PalletInfo;
		type AccountData = pallet_balances::AccountData<u128>;
		type OnNewAccount = ();
		type OnKilledAccount = ();
		type SystemWeightInfo = ();
		type SS58Prefix = ();
		type OnSetCode = ();
		type MaxConsumers = frame_support::traits::ConstU32<16>;
	}
	parameter_types! {
		pub const ExistentialDeposit: Balance = 1;
	}
	impl pallet_balances::Config for Test {
		type Balance = u128;
		type DustRemoval = ();
		type RuntimeEvent = RuntimeEvent;
		type ExistentialDeposit = ExistentialDeposit;
		type AccountStore = System;
		type MaxLocks = ();
		type MaxReserves = ();
		type ReserveIdentifier = [u8; 8];
		type WeightInfo = ();
		type RuntimeHoldReason = RuntimeHoldReason;
		type RuntimeFreezeReason = RuntimeFreezeReason;
		type FreezeIdentifier = ();
		type MaxFreezes = ConstU32<1>;
	}
	impl shared::Config for Test {
		type DisabledValidators = ();
	}
	impl origin::Config for Test {}
	parameter_types! {
		pub const ParasUnsignedPriority: TransactionPriority = TransactionPriority::max_value();
	}
	impl paras::Config for Test {
		type RuntimeEvent = RuntimeEvent;
		type WeightInfo = paras::TestWeightInfo;
		type UnsignedPriority = ParasUnsignedPriority;
		type QueueFootprinter = ();
		type NextSessionRotation = crate::mock::TestNextSessionRotation;
		type OnNewHead = ();
		type AssignCoretime = ();
	}
	impl configuration::Config for Test {
		type WeightInfo = configuration::TestWeightInfo;
	}
	parameter_types! {
		pub const ParaDeposit: Balance = 10;
		pub const DataDepositPerByte: Balance = 1;
		pub const MaxRetries: u32 = 3;
	}
	impl Config for Test {
		type RuntimeOrigin = RuntimeOrigin;
		type RuntimeEvent = RuntimeEvent;
		type Currency = Balances;
		type OnSwap = MockSwap;
		type ParaDeposit = ParaDeposit;
		type DataDepositPerByte = DataDepositPerByte;
		type WeightInfo = TestWeightInfo;
	}
	pub fn new_test_ext() -> TestExternalities {
		let mut t = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
		configuration::GenesisConfig::<Test> {
			config: configuration::HostConfiguration {
				max_code_size: MAX_CODE_SIZE,
				max_head_data_size: 1 * 1024 * 1024, ..Default::default()
			},
		}
		.assimilate_storage(&mut t)
		.unwrap();
		pallet_balances::GenesisConfig::<Test> {
			balances: vec![(1, 10_000_000), (2, 10_000_000), (3, 10_000_000)],
		}
		.assimilate_storage(&mut t)
		.unwrap();
		t.into()
	}
	parameter_types! {
		pub static SwapData: BTreeMap<ParaId, u64> = BTreeMap::new();
	}
	pub struct MockSwap;
	impl OnSwap for MockSwap {
		fn on_swap(one: ParaId, other: ParaId) {
			let mut swap_data = SwapData::get();
			let one_data = swap_data.remove(&one).unwrap_or_default();
			let other_data = swap_data.remove(&other).unwrap_or_default();
			swap_data.insert(one, other_data);
			swap_data.insert(other, one_data);
			SwapData::set(swap_data);
		}
	}
	const BLOCKS_PER_SESSION: u32 = 3;
	const VALIDATORS: &[Sr25519Keyring] = &[
		Sr25519Keyring::Alice,
		Sr25519Keyring::Bob,
		Sr25519Keyring::Charlie,
		Sr25519Keyring::Dave,
		Sr25519Keyring::Ferdie,
	];
	fn run_to_block(n: BlockNumber) {
		assert!(System::block_number() < n);
		while System::block_number() < n {
			let b = System::block_number();
			if System::block_number() > 1 {
				System::on_finalize(System::block_number());
			}
			if (b + 1) % BLOCKS_PER_SESSION == 0 {
				let session_index = shared::CurrentSessionIndex::<Test>::get() + 1;
				let validators_pub_keys = VALIDATORS.iter().map(|v| v.public().into()).collect();
				shared::Pallet::<Test>::set_session_index(session_index);
				shared::Pallet::<Test>::set_active_validators_ascending(validators_pub_keys);
				Parachains::test_on_new_session();
			}
			System::set_block_number(b + 1);
			System::on_initialize(System::block_number());
		}
	}
	fn run_to_session(n: BlockNumber) {
		let block_number = n * BLOCKS_PER_SESSION;
		run_to_block(block_number);
	}
	fn test_genesis_head(size: usize) -> HeadData {
		HeadData(vec![0u8; size])
	}
	fn test_validation_code(size: usize) -> ValidationCode {
		let validation_code = vec![0u8; size as usize];
		ValidationCode(validation_code)
	}
	fn para_origin(id: ParaId) -> RuntimeOrigin {
		runtime_parachains::Origin::Parachain(id).into()
	}
	fn max_code_size() -> u32 {
		configuration::ActiveConfig::<Test>::get().max_code_size
	}
	fn max_head_size() -> u32 {
		configuration::ActiveConfig::<Test>::get().max_head_data_size
	}
	#[test]
	fn basic_setup_works() {
		new_test_ext().execute_with(|| {
			assert_eq!(PendingSwap::<Test>::get(&ParaId::from(0u32)), None);
			assert_eq!(Paras::<Test>::get(&ParaId::from(0u32)), None);
		});
	}
	#[test]
	fn end_to_end_scenario_works() {
		new_test_ext().execute_with(|| {
			let para_id = LOWEST_PUBLIC_ID;
			const START_SESSION_INDEX: SessionIndex = 1;
			run_to_session(START_SESSION_INDEX);
			assert!(!Parachains::is_parathread(para_id));
			let validation_code = test_validation_code(32);
			assert_ok!(Registrar::reserve(RuntimeOrigin::signed(1)));
			assert_ok!(Registrar::register(
				RuntimeOrigin::signed(1),
				para_id,
				test_genesis_head(32),
				validation_code.clone(),
			));
			conclude_pvf_checking::<Test>(&validation_code, VALIDATORS, START_SESSION_INDEX);
			run_to_session(START_SESSION_INDEX + 2);
			assert!(Parachains::is_parathread(para_id));
			assert!(!Parachains::is_parachain(para_id));
			assert_ok!(Registrar::make_parachain(para_id));
			run_to_session(START_SESSION_INDEX + 4);
			assert!(!Parachains::is_parathread(para_id));
			assert!(Parachains::is_parachain(para_id));
			assert_ok!(Registrar::make_parathread(para_id));
			run_to_session(START_SESSION_INDEX + 6);
			assert!(Parachains::is_parathread(para_id));
			assert!(!Parachains::is_parachain(para_id));
			assert_ok!(Registrar::deregister(RuntimeOrigin::root(), para_id,));
			run_to_session(START_SESSION_INDEX + 8);
			assert!(!Parachains::is_parathread(para_id));
			assert!(!Parachains::is_parachain(para_id));
		});
	}
	#[test]
	fn register_works() {
		new_test_ext().execute_with(|| {
			const START_SESSION_INDEX: SessionIndex = 1;
			run_to_session(START_SESSION_INDEX);
			let para_id = LOWEST_PUBLIC_ID;
			assert!(!Parachains::is_parathread(para_id));
			let validation_code = test_validation_code(32);
			assert_ok!(Registrar::reserve(RuntimeOrigin::signed(1)));
			assert_eq!(Balances::reserved_balance(&1), <Test as Config>::ParaDeposit::get());
			assert_ok!(Registrar::register(
				RuntimeOrigin::signed(1),
				para_id,
				test_genesis_head(32),
				validation_code.clone(),
			));
			conclude_pvf_checking::<Test>(&validation_code, VALIDATORS, START_SESSION_INDEX);
			run_to_session(START_SESSION_INDEX + 2);
			assert!(Parachains::is_parathread(para_id));
			let validation_code_deposit =
				max_code_size() as BalanceOf<Test> * <Test as Config>::DataDepositPerByte::get();
			let head_deposit = 32 * <Test as Config>::DataDepositPerByte::get();
			assert_eq!(
				Balances::reserved_balance(&1),
				<Test as Config>::ParaDeposit::get() + head_deposit + validation_code_deposit
			);
		});
	}
	#[test]
	fn schedule_code_upgrade_validates_code() {
		new_test_ext().execute_with(|| {
			const START_SESSION_INDEX: SessionIndex = 1;
			run_to_session(START_SESSION_INDEX);
			let para_id = LOWEST_PUBLIC_ID;
			assert!(!Parachains::is_parathread(para_id));
			let validation_code = test_validation_code(32);
			assert_ok!(Registrar::reserve(RuntimeOrigin::signed(1)));
			assert_eq!(Balances::reserved_balance(&1), <Test as Config>::ParaDeposit::get());
			assert_ok!(Registrar::register(
				RuntimeOrigin::signed(1),
				para_id,
				test_genesis_head(32),
				validation_code.clone(),
			));
			conclude_pvf_checking::<Test>(&validation_code, VALIDATORS, START_SESSION_INDEX);
			run_to_session(START_SESSION_INDEX + 2);
			assert!(Parachains::is_parathread(para_id));
			let new_code = test_validation_code(0);
			assert_noop!(
				Registrar::schedule_code_upgrade(
					RuntimeOrigin::signed(1),
					para_id,
					new_code.clone(),
				),
				paras::Error::<Test>::InvalidCode
			);
			let new_code = test_validation_code(max_code_size() as usize + 1);
			assert_noop!(
				Registrar::schedule_code_upgrade(
					RuntimeOrigin::signed(1),
					para_id,
					new_code.clone(),
				),
				paras::Error::<Test>::InvalidCode
			);
		});
	}
	#[test]
	fn register_handles_basic_errors() {
		new_test_ext().execute_with(|| {
			let para_id = LOWEST_PUBLIC_ID;
			assert_noop!(
				Registrar::register(
					RuntimeOrigin::signed(1),
					para_id,
					test_genesis_head(max_head_size() as usize),
					test_validation_code(max_code_size() as usize),
				),
				Error::<Test>::NotReserved
			);
			assert_ok!(Registrar::reserve(RuntimeOrigin::signed(1)));
			assert_noop!(
				Registrar::register(
					RuntimeOrigin::signed(2),
					para_id,
					test_genesis_head(max_head_size() as usize),
					test_validation_code(max_code_size() as usize),
				),
				Error::<Test>::NotOwner
			);
			assert_ok!(Registrar::register(
				RuntimeOrigin::signed(1),
				para_id,
				test_genesis_head(max_head_size() as usize),
				test_validation_code(max_code_size() as usize),
			));
			run_to_session(2);
			assert_ok!(Registrar::deregister(RuntimeOrigin::root(), para_id));
			assert_noop!(
				Registrar::register(
					RuntimeOrigin::signed(1),
					para_id,
					test_genesis_head(max_head_size() as usize),
					test_validation_code(max_code_size() as usize),
				),
				Error::<Test>::NotReserved
			);
			assert_ok!(Registrar::reserve(RuntimeOrigin::signed(2)));
			assert_noop!(
				Registrar::register(
					RuntimeOrigin::signed(2),
					para_id + 1,
					test_genesis_head((max_head_size() + 1) as usize),
					test_validation_code(max_code_size() as usize),
				),
				Error::<Test>::HeadDataTooLarge
			);
			assert_noop!(
				Registrar::register(
					RuntimeOrigin::signed(2),
					para_id + 1,
					test_genesis_head(max_head_size() as usize),
					test_validation_code((max_code_size() + 1) as usize),
				),
				Error::<Test>::CodeTooLarge
			);
			assert_noop!(
				Registrar::reserve(RuntimeOrigin::signed(1337)),
				BalancesError::<Test, _>::InsufficientBalance
			);
		});
	}
	#[test]
	fn deregister_works() {
		new_test_ext().execute_with(|| {
			const START_SESSION_INDEX: SessionIndex = 1;
			run_to_session(START_SESSION_INDEX);
			let para_id = LOWEST_PUBLIC_ID;
			assert!(!Parachains::is_parathread(para_id));
			let validation_code = test_validation_code(32);
			assert_ok!(Registrar::reserve(RuntimeOrigin::signed(1)));
			assert_ok!(Registrar::register(
				RuntimeOrigin::signed(1),
				para_id,
				test_genesis_head(32),
				validation_code.clone(),
			));
			conclude_pvf_checking::<Test>(&validation_code, VALIDATORS, START_SESSION_INDEX);
			run_to_session(START_SESSION_INDEX + 2);
			assert!(Parachains::is_parathread(para_id));
			assert_ok!(Registrar::deregister(RuntimeOrigin::root(), para_id,));
			run_to_session(START_SESSION_INDEX + 4);
			assert!(paras::Pallet::<Test>::lifecycle(para_id).is_none());
			assert_eq!(Balances::reserved_balance(&1), 0);
		});
	}
	#[test]
	fn deregister_handles_basic_errors() {
		new_test_ext().execute_with(|| {
			const START_SESSION_INDEX: SessionIndex = 1;
			run_to_session(START_SESSION_INDEX);
			let para_id = LOWEST_PUBLIC_ID;
			assert!(!Parachains::is_parathread(para_id));
			let validation_code = test_validation_code(32);
			assert_ok!(Registrar::reserve(RuntimeOrigin::signed(1)));
			assert_ok!(Registrar::register(
				RuntimeOrigin::signed(1),
				para_id,
				test_genesis_head(32),
				validation_code.clone(),
			));
			conclude_pvf_checking::<Test>(&validation_code, VALIDATORS, START_SESSION_INDEX);
			run_to_session(START_SESSION_INDEX + 2);
			assert!(Parachains::is_parathread(para_id));
			assert_noop!(Registrar::deregister(RuntimeOrigin::signed(2), para_id,), BadOrigin);
			assert_ok!(Registrar::make_parachain(para_id));
			run_to_session(START_SESSION_INDEX + 4);
			assert_noop!(
				Registrar::deregister(RuntimeOrigin::root(), para_id,),
				Error::<Test>::NotParathread
			);
		});
	}
	#[test]
	fn swap_works() {
		new_test_ext().execute_with(|| {
			const START_SESSION_INDEX: SessionIndex = 1;
			run_to_session(START_SESSION_INDEX);
			let para_1 = LOWEST_PUBLIC_ID;
			let para_2 = LOWEST_PUBLIC_ID + 1;
			let validation_code = test_validation_code(max_code_size() as usize);
			assert_ok!(Registrar::reserve(RuntimeOrigin::signed(1)));
			assert_ok!(Registrar::register(
				RuntimeOrigin::signed(1),
				para_1,
				test_genesis_head(max_head_size() as usize),
				validation_code.clone(),
			));
			assert_ok!(Registrar::reserve(RuntimeOrigin::signed(2)));
			assert_ok!(Registrar::register(
				RuntimeOrigin::signed(2),
				para_2,
				test_genesis_head(max_head_size() as usize),
				validation_code.clone(),
			));
			conclude_pvf_checking::<Test>(&validation_code, VALIDATORS, START_SESSION_INDEX);
			run_to_session(START_SESSION_INDEX + 2);
			assert_ok!(Registrar::make_parachain(para_1));
			let mut swap_data = SwapData::get();
			swap_data.insert(para_1, 69);
			swap_data.insert(para_2, 1337);
			SwapData::set(swap_data);
			run_to_session(START_SESSION_INDEX + 4);
			assert!(Parachains::is_parachain(para_1));
			assert!(!Parachains::is_parathread(para_1));
			assert!(!Parachains::is_parachain(para_2));
			assert!(Parachains::is_parathread(para_2));
			assert_ok!(Registrar::swap(para_origin(para_1), para_1, para_2,));
			assert_ok!(Registrar::swap(para_origin(para_2), para_2, para_1,));
			System::assert_last_event(RuntimeEvent::Registrar(paras_registrar::Event::Swapped {
				para_id: para_2,
				other_id: para_1,
			}));
			run_to_session(START_SESSION_INDEX + 6);
			assert!(!Parachains::is_parachain(para_1));
			assert!(Parachains::is_parathread(para_1));
			assert!(Parachains::is_parachain(para_2));
			assert!(!Parachains::is_parathread(para_2));
			assert_eq!(SwapData::get().get(¶_1).unwrap(), &1337);
			assert_eq!(SwapData::get().get(¶_2).unwrap(), &69);
			assert_ok!(Registrar::swap(para_origin(para_1), para_1, para_2,));
			assert_ok!(Registrar::swap(para_origin(para_2), para_2, para_1,));
			System::assert_last_event(RuntimeEvent::Registrar(paras_registrar::Event::Swapped {
				para_id: para_2,
				other_id: para_1,
			}));
			assert_eq!(SwapData::get().get(¶_1).unwrap(), &69);
			assert_eq!(SwapData::get().get(¶_2).unwrap(), &1337);
			let para_3 = LOWEST_PUBLIC_ID + 2;
			let validation_code = test_validation_code(max_code_size() as usize);
			assert_ok!(Registrar::reserve(RuntimeOrigin::signed(3)));
			assert_ok!(Registrar::register(
				RuntimeOrigin::signed(3),
				para_3,
				test_genesis_head(max_head_size() as usize),
				validation_code.clone(),
			));
			conclude_pvf_checking::<Test>(&validation_code, VALIDATORS, START_SESSION_INDEX + 6);
			run_to_session(START_SESSION_INDEX + 8);
			assert_ok!(Registrar::make_parachain(para_3));
			let mut swap_data = SwapData::get();
			swap_data.insert(para_3, 777);
			SwapData::set(swap_data);
			run_to_session(START_SESSION_INDEX + 10);
			assert!(Parachains::is_parachain(para_3));
			assert!(!Parachains::is_parathread(para_3));
			assert!(Parachains::is_parachain(para_1));
			assert!(!Parachains::is_parathread(para_1));
			assert_ok!(Registrar::swap(para_origin(para_1), para_1, para_3,));
			assert_ok!(Registrar::swap(para_origin(para_3), para_3, para_1,));
			System::assert_last_event(RuntimeEvent::Registrar(paras_registrar::Event::Swapped {
				para_id: para_3,
				other_id: para_1,
			}));
			assert_eq!(SwapData::get().get(¶_3).unwrap(), &69);
			assert_eq!(SwapData::get().get(¶_1).unwrap(), &777);
		});
	}
	#[test]
	fn para_lock_works() {
		new_test_ext().execute_with(|| {
			run_to_block(1);
			assert_ok!(Registrar::reserve(RuntimeOrigin::signed(1)));
			let para_id = LOWEST_PUBLIC_ID;
			assert_ok!(Registrar::register(
				RuntimeOrigin::signed(1),
				para_id,
				vec![1; 3].into(),
				test_validation_code(32)
			));
			assert_noop!(Registrar::add_lock(RuntimeOrigin::signed(2), para_id), BadOrigin);
			Registrar::on_new_head(para_id, &Default::default());
			assert_noop!(
				Registrar::ensure_root_para_or_owner(RuntimeOrigin::signed(1), para_id),
				BadOrigin
			);
			assert_noop!(Registrar::remove_lock(RuntimeOrigin::signed(1), para_id), BadOrigin);
			assert_ok!(Registrar::remove_lock(para_origin(para_id), para_id));
			assert_ok!(Registrar::ensure_root_para_or_owner(RuntimeOrigin::signed(1), para_id));
			Registrar::on_new_head(para_id, &Default::default());
			assert_ok!(Registrar::ensure_root_para_or_owner(RuntimeOrigin::signed(1), para_id));
		});
	}
	#[test]
	fn swap_handles_bad_states() {
		new_test_ext().execute_with(|| {
			const START_SESSION_INDEX: SessionIndex = 1;
			run_to_session(START_SESSION_INDEX);
			let para_1 = LOWEST_PUBLIC_ID;
			let para_2 = LOWEST_PUBLIC_ID + 1;
			assert!(!Parachains::is_parathread(para_1));
			assert!(!Parachains::is_parathread(para_2));
			assert_noop!(
				Registrar::swap(RuntimeOrigin::root(), para_1, para_2),
				Error::<Test>::NotRegistered
			);
			let validation_code = test_validation_code(32);
			assert_ok!(Registrar::reserve(RuntimeOrigin::signed(1)));
			assert_ok!(Registrar::reserve(RuntimeOrigin::signed(2)));
			assert_ok!(Registrar::register(
				RuntimeOrigin::signed(1),
				para_1,
				test_genesis_head(32),
				validation_code.clone(),
			));
			assert_ok!(Registrar::register(
				RuntimeOrigin::signed(2),
				para_2,
				test_genesis_head(32),
				validation_code.clone(),
			));
			conclude_pvf_checking::<Test>(&validation_code, VALIDATORS, START_SESSION_INDEX);
			assert_ok!(Registrar::swap(RuntimeOrigin::root(), para_1, para_2));
			assert_noop!(
				Registrar::swap(RuntimeOrigin::root(), para_2, para_1),
				Error::<Test>::CannotSwap
			);
			run_to_session(START_SESSION_INDEX + 2);
			assert!(Parachains::is_parathread(para_1));
			assert!(Parachains::is_parathread(para_2));
			assert_ok!(Registrar::swap(RuntimeOrigin::root(), para_1, para_2));
			assert_noop!(
				Registrar::swap(RuntimeOrigin::root(), para_2, para_1),
				Error::<Test>::CannotSwap
			);
			assert_ok!(Registrar::make_parachain(para_1));
			assert_ok!(Registrar::swap(RuntimeOrigin::root(), para_1, para_2));
			assert_noop!(
				Registrar::swap(RuntimeOrigin::root(), para_2, para_1),
				Error::<Test>::CannotSwap
			);
			run_to_session(START_SESSION_INDEX + 3);
			assert_ok!(Registrar::swap(RuntimeOrigin::root(), para_1, para_2));
			assert_noop!(
				Registrar::swap(RuntimeOrigin::root(), para_2, para_1),
				Error::<Test>::CannotSwap
			);
			run_to_session(START_SESSION_INDEX + 4);
			assert!(Parachains::is_parachain(para_1));
			assert!(Parachains::is_parathread(para_2));
			assert_ok!(Registrar::swap(RuntimeOrigin::root(), para_1, para_2));
			assert_ok!(Registrar::swap(RuntimeOrigin::root(), para_2, para_1));
			assert!(System::events().iter().any(|r| matches!(
				r.event,
				RuntimeEvent::Registrar(paras_registrar::Event::Swapped { .. })
			)));
			run_to_session(START_SESSION_INDEX + 5);
			assert_ok!(Registrar::swap(RuntimeOrigin::root(), para_1, para_2));
			assert_noop!(
				Registrar::swap(RuntimeOrigin::root(), para_2, para_1),
				Error::<Test>::CannotSwap
			);
			run_to_session(START_SESSION_INDEX + 6);
			assert!(Parachains::is_parachain(para_2));
			assert!(Parachains::is_parathread(para_1));
			assert!(System::events().iter().any(|r| matches!(
				r.event,
				RuntimeEvent::Registrar(paras_registrar::Event::Swapped { .. })
			)));
			assert_ok!(Registrar::make_parathread(para_2));
			run_to_session(START_SESSION_INDEX + 7);
			assert_ok!(Registrar::swap(RuntimeOrigin::root(), para_1, para_2));
			assert_noop!(
				Registrar::swap(RuntimeOrigin::root(), para_2, para_1),
				Error::<Test>::CannotSwap
			);
			run_to_session(START_SESSION_INDEX + 8);
			assert!(Parachains::is_parathread(para_1));
			assert!(Parachains::is_parathread(para_2));
		});
	}
}
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking {
	use super::{Pallet as Registrar, *};
	use crate::traits::Registrar as RegistrarT;
	use frame_support::assert_ok;
	use frame_system::RawOrigin;
	use primitives::{MAX_CODE_SIZE, MAX_HEAD_DATA_SIZE, MIN_CODE_SIZE};
	use runtime_parachains::{paras, shared, Origin as ParaOrigin};
	use sp_runtime::traits::Bounded;
	use frame_benchmarking::{account, benchmarks, whitelisted_caller};
	fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
		let events = frame_system::Pallet::<T>::events();
		let system_event: <T as frame_system::Config>::RuntimeEvent = generic_event.into();
		let frame_system::EventRecord { event, .. } = &events[events.len() - 1];
		assert_eq!(event, &system_event);
	}
	fn register_para<T: Config>(id: u32) -> ParaId {
		let para = ParaId::from(id);
		let genesis_head = Registrar::<T>::worst_head_data();
		let validation_code = Registrar::<T>::worst_validation_code();
		let caller: T::AccountId = whitelisted_caller();
		T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
		assert_ok!(Registrar::<T>::reserve(RawOrigin::Signed(caller.clone()).into()));
		assert_ok!(Registrar::<T>::register(
			RawOrigin::Signed(caller).into(),
			para,
			genesis_head,
			validation_code.clone()
		));
		assert_ok!(runtime_parachains::paras::Pallet::<T>::add_trusted_validation_code(
			frame_system::Origin::<T>::Root.into(),
			validation_code,
		));
		return para
	}
	fn para_origin(id: u32) -> ParaOrigin {
		ParaOrigin::Parachain(id.into())
	}
	fn next_scheduled_session<T: Config>() {
		shared::Pallet::<T>::set_session_index(shared::Pallet::<T>::scheduled_session());
		paras::Pallet::<T>::test_on_new_session();
	}
	benchmarks! {
		where_clause { where ParaOrigin: Into<<T as frame_system::Config>::RuntimeOrigin> }
		reserve {
			let caller: T::AccountId = whitelisted_caller();
			T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
		}: _(RawOrigin::Signed(caller.clone()))
		verify {
			assert_last_event::<T>(Event::<T>::Reserved { para_id: LOWEST_PUBLIC_ID, who: caller }.into());
			assert!(Paras::<T>::get(LOWEST_PUBLIC_ID).is_some());
			assert_eq!(paras::Pallet::<T>::lifecycle(LOWEST_PUBLIC_ID), None);
		}
		register {
			let para = LOWEST_PUBLIC_ID;
			let genesis_head = Registrar::<T>::worst_head_data();
			let validation_code = Registrar::<T>::worst_validation_code();
			let caller: T::AccountId = whitelisted_caller();
			T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
			assert_ok!(Registrar::<T>::reserve(RawOrigin::Signed(caller.clone()).into()));
		}: _(RawOrigin::Signed(caller.clone()), para, genesis_head, validation_code.clone())
		verify {
			assert_last_event::<T>(Event::<T>::Registered{ para_id: para, manager: caller }.into());
			assert_eq!(paras::Pallet::<T>::lifecycle(para), Some(ParaLifecycle::Onboarding));
			assert_ok!(runtime_parachains::paras::Pallet::<T>::add_trusted_validation_code(
				frame_system::Origin::<T>::Root.into(),
				validation_code,
			));
			next_scheduled_session::<T>();
			assert_eq!(paras::Pallet::<T>::lifecycle(para), Some(ParaLifecycle::Parathread));
		}
		force_register {
			let manager: T::AccountId = account("manager", 0, 0);
			let deposit = 0u32.into();
			let para = ParaId::from(69);
			let genesis_head = Registrar::<T>::worst_head_data();
			let validation_code = Registrar::<T>::worst_validation_code();
		}: _(RawOrigin::Root, manager.clone(), deposit, para, genesis_head, validation_code.clone())
		verify {
			assert_last_event::<T>(Event::<T>::Registered { para_id: para, manager }.into());
			assert_eq!(paras::Pallet::<T>::lifecycle(para), Some(ParaLifecycle::Onboarding));
			assert_ok!(runtime_parachains::paras::Pallet::<T>::add_trusted_validation_code(
				frame_system::Origin::<T>::Root.into(),
				validation_code,
			));
			next_scheduled_session::<T>();
			assert_eq!(paras::Pallet::<T>::lifecycle(para), Some(ParaLifecycle::Parathread));
		}
		deregister {
			let para = register_para::<T>(LOWEST_PUBLIC_ID.into());
			next_scheduled_session::<T>();
			let caller: T::AccountId = whitelisted_caller();
		}: _(RawOrigin::Signed(caller), para)
		verify {
			assert_last_event::<T>(Event::<T>::Deregistered { para_id: para }.into());
		}
		swap {
			let parathread = register_para::<T>(LOWEST_PUBLIC_ID.into());
			let parachain = register_para::<T>((LOWEST_PUBLIC_ID + 1).into());
			let parachain_origin = para_origin(parachain.into());
			next_scheduled_session::<T>();
			Registrar::<T>::make_parachain(parachain)?;
			next_scheduled_session::<T>();
			assert_eq!(paras::Pallet::<T>::lifecycle(parachain), Some(ParaLifecycle::Parachain));
			assert_eq!(paras::Pallet::<T>::lifecycle(parathread), Some(ParaLifecycle::Parathread));
			let caller: T::AccountId = whitelisted_caller();
			Registrar::<T>::swap(parachain_origin.into(), parachain, parathread)?;
		}: _(RawOrigin::Signed(caller.clone()), parathread, parachain)
		verify {
			next_scheduled_session::<T>();
			assert_eq!(paras::Pallet::<T>::lifecycle(parachain), Some(ParaLifecycle::Parathread));
			assert_eq!(paras::Pallet::<T>::lifecycle(parathread), Some(ParaLifecycle::Parachain));
		}
		schedule_code_upgrade {
			let b in MIN_CODE_SIZE .. MAX_CODE_SIZE;
			let new_code = ValidationCode(vec![0; b as usize]);
			let para_id = ParaId::from(1000);
		}: _(RawOrigin::Root, para_id, new_code)
		set_current_head {
			let b in 1 .. MAX_HEAD_DATA_SIZE;
			let new_head = HeadData(vec![0; b as usize]);
			let para_id = ParaId::from(1000);
		}: _(RawOrigin::Root, para_id, new_head)
		impl_benchmark_test_suite!(
			Registrar,
			crate::integration_tests::new_test_ext(),
			crate::integration_tests::Test,
		);
	}
}