paras_registrar.rs 36.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Copyright 2017-2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.

// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.

17
//! Pallet to handle parathread/parachain registration and related fund management.
18
19
20
21
//! In essence this is a simple wrapper around `paras`.

use frame_support::{
	dispatch::DispatchResult,
Shawn Tabrizi's avatar
Shawn Tabrizi committed
22
	ensure,
23
	pallet_prelude::Weight,
Shawn Tabrizi's avatar
Shawn Tabrizi committed
24
	traits::{Currency, Get, ReservableCurrency},
25
26
};
use frame_system::{self, ensure_root, ensure_signed};
Shawn Tabrizi's avatar
Shawn Tabrizi committed
27
use primitives::v1::{HeadData, Id as ParaId, ValidationCode, LOWEST_PUBLIC_ID};
28
use runtime_parachains::{
Shawn Tabrizi's avatar
Shawn Tabrizi committed
29
30
	configuration, ensure_parachain,
	paras::{self, ParaGenesisArgs},
31
	Origin, ParaLifecycle,
32
};
Shawn Tabrizi's avatar
Shawn Tabrizi committed
33
use sp_std::{prelude::*, result};
34

Shawn Tabrizi's avatar
Shawn Tabrizi committed
35
use crate::traits::{OnSwap, Registrar};
36
pub use pallet::*;
Shawn Tabrizi's avatar
Shawn Tabrizi committed
37
use parity_scale_codec::{Decode, Encode};
38
use scale_info::TypeInfo;
Shawn Tabrizi's avatar
Shawn Tabrizi committed
39
40
41
42
use sp_runtime::{
	traits::{CheckedSub, Saturating},
	RuntimeDebug,
};
43

44
#[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, TypeInfo)]
45
pub struct ParaInfo<Account, Balance> {
46
	/// The account that has placed a deposit for registering this para.
47
	pub(crate) manager: Account,
48
	/// The amount reserved by the `manager` account for the registration.
49
	deposit: Balance,
50
51
	/// Whether the para registration should be locked from being controlled by the manager.
	locked: bool,
52
53
}

54
type BalanceOf<T> =
55
	<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
56

57
pub trait WeightInfo {
58
	fn reserve() -> Weight;
59
	fn register() -> Weight;
60
	fn force_register() -> Weight;
61
62
63
64
65
66
	fn deregister() -> Weight;
	fn swap() -> Weight;
}

pub struct TestWeightInfo;
impl WeightInfo for TestWeightInfo {
Shawn Tabrizi's avatar
Shawn Tabrizi committed
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
	fn reserve() -> Weight {
		0
	}
	fn register() -> Weight {
		0
	}
	fn force_register() -> Weight {
		0
	}
	fn deregister() -> Weight {
		0
	}
	fn swap() -> Weight {
		0
	}
82
83
}

84
85
#[frame_support::pallet]
pub mod pallet {
Shawn Tabrizi's avatar
Shawn Tabrizi committed
86
	use super::*;
87
88
	use frame_support::pallet_prelude::*;
	use frame_system::pallet_prelude::*;
89

90
91
92
	#[pallet::pallet]
	#[pallet::generate_store(pub(super) trait Store)]
	pub struct Pallet<T>(_);
93

94
95
	#[pallet::config]
	#[pallet::disable_frame_system_supertrait_check]
96
	pub trait Config: configuration::Config + paras::Config {
97
98
		/// The overarching event type.
		type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
99

100
101
102
103
104
105
		/// The aggregated origin type must support the `parachains` origin. We require that we can
		/// infallibly convert between this origin and the system origin, but in reality, they're the
		/// same type, we just can't express that to the Rust type system without writing a `where`
		/// clause everywhere.
		type Origin: From<<Self as frame_system::Config>::Origin>
			+ Into<result::Result<Origin, <Self as Config>::Origin>>;
106

107
108
		/// The system's currency for parathread payment.
		type Currency: ReservableCurrency<Self::AccountId>;
109

110
111
		/// Runtime hook for when a parachain and parathread swap.
		type OnSwap: crate::traits::OnSwap;
112

113
114
115
116
		/// The deposit to be paid to run a parathread.
		/// This should include the cost for storing the genesis head and validation code.
		#[pallet::constant]
		type ParaDeposit: Get<BalanceOf<Self>>;
117

118
119
120
		/// The deposit to be paid per byte stored on chain.
		#[pallet::constant]
		type DataDepositPerByte: Get<BalanceOf<Self>>;
121

122
123
		/// Weight Information for the Extrinsics in the Pallet
		type WeightInfo: WeightInfo;
124
	}
125

126
127
128
129
	#[pallet::event]
	#[pallet::generate_deposit(pub(super) fn deposit_event)]
	pub enum Event<T: Config> {
		Registered(ParaId, T::AccountId),
130
		Deregistered(ParaId),
131
		Reserved(ParaId, T::AccountId),
132
133
	}

134
135
	#[pallet::error]
	pub enum Error<T> {
136
137
138
139
140
141
		/// The ID is not registered.
		NotRegistered,
		/// The ID is already registered.
		AlreadyRegistered,
		/// The caller is not the owner of this Id.
		NotOwner,
142
143
144
145
		/// Invalid para code size.
		CodeTooLarge,
		/// Invalid para head data size.
		HeadDataTooLarge,
146
147
148
149
		/// Para is not a Parachain.
		NotParachain,
		/// Para is not a Parathread.
		NotParathread,
150
151
		/// Cannot deregister para
		CannotDeregister,
152
153
154
155
		/// Cannot schedule downgrade of parachain to parathread
		CannotDowngrade,
		/// Cannot schedule upgrade of parathread to parachain
		CannotUpgrade,
156
157
		/// Para is locked from manipulation by the manager. Must use parachain or relay chain governance.
		ParaLocked,
158
159
		/// The ID given for registration has not been reserved.
		NotReserved,
160
161
	}

162
163
164
165
166
167
168
169
170
	/// Pending swap operations.
	#[pallet::storage]
	pub(super) type PendingSwap<T> = StorageMap<_, Twox64Concat, ParaId, ParaId>;

	/// Amount held on deposit for each para and the original depositor.
	///
	/// The given account ID is responsible for registering the code and initial head data, but may only do
	/// so if it isn't yet registered. (After that, it's up to governance to do so.)
	#[pallet::storage]
Shawn Tabrizi's avatar
Shawn Tabrizi committed
171
172
	pub type Paras<T: Config> =
		StorageMap<_, Twox64Concat, ParaId, ParaInfo<T::AccountId, BalanceOf<T>>>;
173

174
175
176
	/// The next free `ParaId`.
	#[pallet::storage]
	pub type NextFreeParaId<T> = StorageValue<_, ParaId, ValueQuery>;
177

178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
	#[pallet::genesis_config]
	pub struct GenesisConfig {
		pub next_free_para_id: ParaId,
	}

	#[cfg(feature = "std")]
	impl Default for GenesisConfig {
		fn default() -> Self {
			GenesisConfig { next_free_para_id: LOWEST_PUBLIC_ID }
		}
	}

	#[pallet::genesis_build]
	impl<T: Config> GenesisBuild<T> for GenesisConfig {
		fn build(&self) {
			NextFreeParaId::<T>::put(self.next_free_para_id);
		}
	}

197
198
	#[pallet::hooks]
	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
199

200
201
	#[pallet::call]
	impl<T: Config> Pallet<T> {
202
		/// Register head data and validation code for a reserved Para Id.
203
		///
204
205
206
207
208
		/// ## Arguments
		/// - `origin`: Must be called by a `Signed` origin.
		/// - `id`: The para ID. Must be owned/managed by the `origin` signing account.
		/// - `genesis_head`: The genesis head data of the parachain/thread.
		/// - `validation_code`: The initial validation code of the parachain/thread.
209
		///
210
211
212
		/// ## Deposits/Fees
		/// The origin signed account must reserve a corresponding deposit for the registration. Anything already
		/// reserved previously for this para ID is accounted for.
213
		///
214
215
		/// ## Events
		/// The `Registered` event is emitted in case of success.
216
		#[pallet::weight(<T as Config>::WeightInfo::register())]
217
		pub fn register(
218
			origin: OriginFor<T>,
219
220
221
222
223
			id: ParaId,
			genesis_head: HeadData,
			validation_code: ValidationCode,
		) -> DispatchResult {
			let who = ensure_signed(origin)?;
224
			Self::do_register(who, None, id, genesis_head, validation_code, true)?;
225
			Ok(())
226
227
228
229
230
231
		}

		/// Force the registration of a Para Id on the relay chain.
		///
		/// This function must be called by a Root origin.
		///
Denis_P's avatar
Denis_P committed
232
		/// The deposit taken can be specified for this registration. Any `ParaId`
233
		/// can be registered, including sub-1000 IDs which are System Parachains.
234
		#[pallet::weight(<T as Config>::WeightInfo::force_register())]
235
		pub fn force_register(
236
			origin: OriginFor<T>,
237
238
239
240
241
242
243
			who: T::AccountId,
			deposit: BalanceOf<T>,
			id: ParaId,
			genesis_head: HeadData,
			validation_code: ValidationCode,
		) -> DispatchResult {
			ensure_root(origin)?;
244
			Self::do_register(who, Some(deposit), id, genesis_head, validation_code, false)
245
246
		}

247
		/// Deregister a Para Id, freeing all data and returning any deposit.
248
		///
249
		/// The caller must be Root, the `para` owner, or the `para` itself. The para must be a parathread.
250
		#[pallet::weight(<T as Config>::WeightInfo::deregister())]
251
		pub fn deregister(origin: OriginFor<T>, id: ParaId) -> DispatchResult {
252
			Self::ensure_root_para_or_owner(origin, id)?;
253
			Self::do_deregister(id)
254
255
		}

256
257
258
259
		/// Swap a parachain with another parachain or parathread.
		///
		/// The origin must be Root, the `para` owner, or the `para` itself.
		///
260
261
262
263
264
265
266
		/// The swap will happen only if there is already an opposite swap pending. If there is not,
		/// the swap will be stored in the pending swaps map, ready for a later confirmatory swap.
		///
		/// The `ParaId`s remain mapped to the same head data and code so external code can rely on
		/// `ParaId` to be a long-term identifier of a notional "parachain". However, their
		/// scheduling info (i.e. whether they're a parathread or parachain), auction information
		/// and the auction deposit are switched.
267
		#[pallet::weight(<T as Config>::WeightInfo::swap())]
268
		pub fn swap(origin: OriginFor<T>, id: ParaId, other: ParaId) -> DispatchResult {
269
270
			Self::ensure_root_para_or_owner(origin, id)?;

271
272
273
			if PendingSwap::<T>::get(other) == Some(id) {
				if let Some(other_lifecycle) = paras::Pallet::<T>::lifecycle(other) {
					if let Some(id_lifecycle) = paras::Pallet::<T>::lifecycle(id) {
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
						// identify which is a parachain and which is a parathread
						if id_lifecycle.is_parachain() && other_lifecycle.is_parathread() {
							// We check that both paras are in an appropriate lifecycle for a swap,
							// so these should never fail.
							let res1 = runtime_parachains::schedule_parachain_downgrade::<T>(id);
							debug_assert!(res1.is_ok());
							let res2 = runtime_parachains::schedule_parathread_upgrade::<T>(other);
							debug_assert!(res2.is_ok());
							T::OnSwap::on_swap(id, other);
						} else if id_lifecycle.is_parathread() && other_lifecycle.is_parachain() {
							// We check that both paras are in an appropriate lifecycle for a swap,
							// so these should never fail.
							let res1 = runtime_parachains::schedule_parachain_downgrade::<T>(other);
							debug_assert!(res1.is_ok());
							let res2 = runtime_parachains::schedule_parathread_upgrade::<T>(id);
							debug_assert!(res2.is_ok());
							T::OnSwap::on_swap(id, other);
						}

293
						PendingSwap::<T>::remove(other);
294
295
					}
				}
296
			} else {
297
				PendingSwap::<T>::insert(id, other);
298
			}
299
300

			Ok(())
301
		}
302
303
304
305
306

		/// Remove a manager lock from a para. This will allow the manager of a
		/// previously locked para to deregister or swap a para without using governance.
		///
		/// Can only be called by the Root origin.
307
		#[pallet::weight(T::DbWeight::get().reads_writes(1, 1))]
308
		pub fn force_remove_lock(origin: OriginFor<T>, para: ParaId) -> DispatchResult {
309
310
			ensure_root(origin)?;
			Self::remove_lock(para);
311
			Ok(())
312
		}
313

314
		/// Reserve a Para Id on the relay chain.
315
		///
316
317
318
		/// This function will reserve a new Para Id to be owned/managed by the origin account.
		/// The origin account is able to register head data and validation code using `register` to create
		/// a parathread. Using the Slots pallet, a parathread can then be upgraded to get a parachain slot.
319
		///
320
321
		/// ## Arguments
		/// - `origin`: Must be called by a `Signed` origin. Becomes the manager/owner of the new para ID.
322
		///
323
324
325
326
327
		/// ## Deposits/Fees
		/// The origin must reserve a deposit of `ParaDeposit` for the registration.
		///
		/// ## Events
		/// The `Reserved` event is emitted in case of success, which provides the ID reserved for use.
328
		#[pallet::weight(<T as Config>::WeightInfo::reserve())]
329
		pub fn reserve(origin: OriginFor<T>) -> DispatchResult {
330
			let who = ensure_signed(origin)?;
331
			let id = NextFreeParaId::<T>::get().max(LOWEST_PUBLIC_ID);
332
			Self::do_reserve(who, None, id)?;
333
			NextFreeParaId::<T>::set(id + 1);
334
335
			Ok(())
		}
336
337
338
	}
}

339
impl<T: Config> Registrar for Pallet<T> {
340
341
342
343
344
345
346
347
348
	type AccountId = T::AccountId;

	/// Return the manager `AccountId` of a para if one exists.
	fn manager_of(id: ParaId) -> Option<T::AccountId> {
		Some(Paras::<T>::get(id)?.manager)
	}

	// All parachains. Ordered ascending by ParaId. Parathreads are not included.
	fn parachains() -> Vec<ParaId> {
349
		paras::Pallet::<T>::parachains()
350
351
352
353
	}

	// Return if a para is a parathread
	fn is_parathread(id: ParaId) -> bool {
354
		paras::Pallet::<T>::is_parathread(id)
355
356
357
358
	}

	// Return if a para is a parachain
	fn is_parachain(id: ParaId) -> bool {
359
		paras::Pallet::<T>::is_parachain(id)
360
361
	}

362
363
364
365
366
367
368
369
370
371
	// Apply a lock to the parachain.
	fn apply_lock(id: ParaId) {
		Paras::<T>::mutate(id, |x| x.as_mut().map(|mut info| info.locked = true));
	}

	// Apply a lock to the parachain.
	fn remove_lock(id: ParaId) {
		Paras::<T>::mutate(id, |x| x.as_mut().map(|mut info| info.locked = false));
	}

372
	// Register a Para ID under control of `manager`.
373
	//
Denis_P's avatar
Denis_P committed
374
	// Note this is a backend registration API, so verification of ParaId
375
	// is not done here to prevent.
376
377
	fn register(
		manager: T::AccountId,
378
379
380
381
		id: ParaId,
		genesis_head: HeadData,
		validation_code: ValidationCode,
	) -> DispatchResult {
382
		Self::do_register(manager, None, id, genesis_head, validation_code, false)
383
	}
384

385
386
387
388
	// Deregister a Para ID, free any data, and return any deposits.
	fn deregister(id: ParaId) -> DispatchResult {
		Self::do_deregister(id)
	}
389

390
391
392
	// Upgrade a registered parathread into a parachain.
	fn make_parachain(id: ParaId) -> DispatchResult {
		// Para backend should think this is a parathread...
Shawn Tabrizi's avatar
Shawn Tabrizi committed
393
394
395
396
397
398
		ensure!(
			paras::Pallet::<T>::lifecycle(id) == Some(ParaLifecycle::Parathread),
			Error::<T>::NotParathread
		);
		runtime_parachains::schedule_parathread_upgrade::<T>(id)
			.map_err(|_| Error::<T>::CannotUpgrade)?;
399
400
401
		// Once a para has upgraded to a parachain, it can no longer be managed by the owner.
		// Intentionally, the flag stays with the para even after downgrade.
		Self::apply_lock(id);
402
403
		Ok(())
	}
404

405
406
407
	// Downgrade a registered para into a parathread.
	fn make_parathread(id: ParaId) -> DispatchResult {
		// Para backend should think this is a parachain...
Shawn Tabrizi's avatar
Shawn Tabrizi committed
408
409
410
411
412
413
		ensure!(
			paras::Pallet::<T>::lifecycle(id) == Some(ParaLifecycle::Parachain),
			Error::<T>::NotParachain
		);
		runtime_parachains::schedule_parachain_downgrade::<T>(id)
			.map_err(|_| Error::<T>::CannotDowngrade)?;
414
415
416
		Ok(())
	}

417
418
	#[cfg(any(feature = "runtime-benchmarks", test))]
	fn worst_head_data() -> HeadData {
419
420
		let max_head_size = configuration::Pallet::<T>::config().max_head_data_size;
		assert!(max_head_size > 0, "max_head_data can't be zero for generating worst head data.");
421
422
		vec![0u8; max_head_size as usize].into()
	}
423

424
425
	#[cfg(any(feature = "runtime-benchmarks", test))]
	fn worst_validation_code() -> ValidationCode {
426
427
		let max_code_size = configuration::Pallet::<T>::config().max_code_size;
		assert!(max_code_size > 0, "max_code_size can't be zero for generating worst code data.");
asynchronous rob's avatar
asynchronous rob committed
428
		let validation_code = vec![0u8; max_code_size as usize];
429
430
		validation_code.into()
	}
431

432
433
434
	#[cfg(any(feature = "runtime-benchmarks", test))]
	fn execute_pending_transitions() {
		use runtime_parachains::shared;
Shawn Tabrizi's avatar
Shawn Tabrizi committed
435
		shared::Pallet::<T>::set_session_index(shared::Pallet::<T>::scheduled_session());
436
		paras::Pallet::<T>::test_on_new_session();
437
438
439
	}
}

440
impl<T: Config> Pallet<T> {
441
442
	/// Ensure the origin is one of Root, the `para` owner, or the `para` itself.
	/// If the origin is the `para` owner, the `para` must be unlocked.
Shawn Tabrizi's avatar
Shawn Tabrizi committed
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
	fn ensure_root_para_or_owner(
		origin: <T as frame_system::Config>::Origin,
		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.locked, Error::<T>::ParaLocked);
				ensure!(para_info.manager == who, Error::<T>::NotOwner);
				Ok(())
			})
			.or_else(|_| -> DispatchResult {
				// Else check if para origin...
				let caller_id = ensure_parachain(<T as Config>::Origin::from(origin.clone()))?;
				ensure!(caller_id == id, Error::<T>::NotOwner);
				Ok(())
			})
			.or_else(|_| -> DispatchResult {
				// Check if root...
				ensure_root(origin.clone()).map_err(|e| e.into())
			})
465
466
	}

467
468
469
470
471
472
	fn do_reserve(
		who: T::AccountId,
		deposit_override: Option<BalanceOf<T>>,
		id: ParaId,
	) -> DispatchResult {
		ensure!(!Paras::<T>::contains_key(id), Error::<T>::AlreadyRegistered);
473
		ensure!(paras::Pallet::<T>::lifecycle(id).is_none(), Error::<T>::AlreadyRegistered);
474
475
476

		let deposit = deposit_override.unwrap_or_else(T::ParaDeposit::get);
		<T as Config>::Currency::reserve(&who, deposit)?;
Shawn Tabrizi's avatar
Shawn Tabrizi committed
477
		let info = ParaInfo { manager: who.clone(), deposit, locked: false };
478
479

		Paras::<T>::insert(id, info);
480
		Self::deposit_event(Event::<T>::Reserved(id, who));
481
482
483
		Ok(())
	}

484
485
486
487
	/// Attempt to register a new Para Id under management of `who` in the
	/// system with the given information.
	fn do_register(
		who: T::AccountId,
488
		deposit_override: Option<BalanceOf<T>>,
489
490
491
		id: ParaId,
		genesis_head: HeadData,
		validation_code: ValidationCode,
492
		ensure_reserved: bool,
493
	) -> DispatchResult {
494
495
496
497
498
499
500
501
		let deposited = if let Some(para_data) = Paras::<T>::get(id) {
			ensure!(para_data.manager == who, Error::<T>::NotOwner);
			ensure!(!para_data.locked, Error::<T>::ParaLocked);
			para_data.deposit
		} else {
			ensure!(!ensure_reserved, Error::<T>::NotReserved);
			Default::default()
		};
502
		ensure!(paras::Pallet::<T>::lifecycle(id).is_none(), Error::<T>::AlreadyRegistered);
Shawn Tabrizi's avatar
Shawn Tabrizi committed
503
504
		let (genesis, deposit) =
			Self::validate_onboarding_data(genesis_head, validation_code, false)?;
505
		let deposit = deposit_override.unwrap_or(deposit);
506
507
508
509
510
511

		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);
		};
Shawn Tabrizi's avatar
Shawn Tabrizi committed
512
		let info = ParaInfo { manager: who.clone(), deposit, locked: false };
513
514
515
516
517

		Paras::<T>::insert(id, info);
		// We check above that para has no lifecycle, so this should not fail.
		let res = runtime_parachains::schedule_para_initialize::<T>(id, genesis);
		debug_assert!(res.is_ok());
518
		Self::deposit_event(Event::<T>::Registered(id, who));
519
520
521
522
523
		Ok(())
	}

	/// Deregister a Para Id, freeing all data returning any deposit.
	fn do_deregister(id: ParaId) -> DispatchResult {
524
		match paras::Pallet::<T>::lifecycle(id) {
525
526
			// Para must be a parathread, or not exist at all.
			Some(ParaLifecycle::Parathread) | None => {},
Shawn Tabrizi's avatar
Shawn Tabrizi committed
527
			_ => return Err(Error::<T>::NotParathread.into()),
528
		}
Shawn Tabrizi's avatar
Shawn Tabrizi committed
529
530
		runtime_parachains::schedule_para_cleanup::<T>(id)
			.map_err(|_| Error::<T>::CannotDeregister)?;
531

532
533
534
535
		if let Some(info) = Paras::<T>::take(&id) {
			<T as Config>::Currency::unreserve(&info.manager, info.deposit);
		}

536
537
		PendingSwap::<T>::remove(id);
		Self::deposit_event(Event::<T>::Deregistered(id));
538
539
		Ok(())
	}
540
541
542
543
544
545
546
547
548

	/// Verifies the onboarding data is valid for a para.
	///
	/// Returns `ParaGenesisArgs` and the deposit needed for the data.
	fn validate_onboarding_data(
		genesis_head: HeadData,
		validation_code: ValidationCode,
		parachain: bool,
	) -> Result<(ParaGenesisArgs, BalanceOf<T>), sp_runtime::DispatchError> {
549
550
		let config = configuration::Pallet::<T>::config();
		ensure!(validation_code.0.len() <= config.max_code_size as usize, Error::<T>::CodeTooLarge);
Shawn Tabrizi's avatar
Shawn Tabrizi committed
551
552
553
554
		ensure!(
			genesis_head.0.len() <= config.max_head_data_size as usize,
			Error::<T>::HeadDataTooLarge
		);
555
556
557

		let per_byte_fee = T::DataDepositPerByte::get();
		let deposit = T::ParaDeposit::get()
Shawn Tabrizi's avatar
Shawn Tabrizi committed
558
559
			.saturating_add(per_byte_fee.saturating_mul((genesis_head.0.len() as u32).into()))
			.saturating_add(per_byte_fee.saturating_mul((validation_code.0.len() as u32).into()));
560

Shawn Tabrizi's avatar
Shawn Tabrizi committed
561
		Ok((ParaGenesisArgs { genesis_head, validation_code, parachain }, deposit))
562
	}
563
564
565
566
567
}

#[cfg(test)]
mod tests {
	use super::*;
Shawn Tabrizi's avatar
Shawn Tabrizi committed
568
	use crate::{paras_registrar, traits::Registrar as RegistrarTrait};
569
	use frame_support::{
Shawn Tabrizi's avatar
Shawn Tabrizi committed
570
		assert_noop, assert_ok,
571
		error::BadOrigin,
Shawn Tabrizi's avatar
Shawn Tabrizi committed
572
573
		parameter_types,
		traits::{GenesisBuild, OnFinalize, OnInitialize},
574
	};
Shawn Tabrizi's avatar
Shawn Tabrizi committed
575
	use frame_system::limits;
576
	use pallet_balances::Error as BalancesError;
Shawn Tabrizi's avatar
Shawn Tabrizi committed
577
578
579
580
581
582
583
584
	use primitives::v1::{Balance, BlockNumber, Header};
	use runtime_parachains::{configuration, shared};
	use sp_core::H256;
	use sp_io::TestExternalities;
	use sp_runtime::{
		traits::{BlakeTwo256, IdentityLookup},
		Perbill,
	};
585
586
587
588
589
590
591
592
593
594

	type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
	type Block = frame_system::mocking::MockBlock<Test>;

	frame_support::construct_runtime!(
		pub enum Test where
			Block = Block,
			NodeBlock = Block,
			UncheckedExtrinsic = UncheckedExtrinsic,
		{
595
596
			System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
			Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
597
			Configuration: configuration::{Pallet, Call, Storage, Config<T>},
ferrell-code's avatar
ferrell-code committed
598
			Parachains: paras::{Pallet, Origin, Call, Storage, Config, Event},
599
			ParasShared: shared::{Pallet, Call, Storage},
600
			Registrar: paras_registrar::{Pallet, Call, Storage, Event<T>},
601
		}
602
	);
603

604
	const NORMAL_RATIO: Perbill = Perbill::from_percent(75);
605
606
	parameter_types! {
		pub const BlockHashCount: u32 = 250;
607
		pub BlockWeights: limits::BlockWeights =
608
			frame_system::limits::BlockWeights::simple_max(1024);
609
610
		pub BlockLength: limits::BlockLength =
			limits::BlockLength::max_with_normal_ratio(4 * 1024 * 1024, NORMAL_RATIO);
611
612
	}

613
	impl frame_system::Config for Test {
614
		type BaseCallFilter = frame_support::traits::Everything;
615
616
617
618
619
620
621
622
623
		type Origin = Origin;
		type Call = Call;
		type Index = u64;
		type BlockNumber = BlockNumber;
		type Hash = H256;
		type Hashing = BlakeTwo256;
		type AccountId = u64;
		type Lookup = IdentityLookup<u64>;
		type Header = Header;
624
		type Event = Event;
625
626
		type BlockHashCount = BlockHashCount;
		type DbWeight = ();
627
628
		type BlockWeights = BlockWeights;
		type BlockLength = BlockLength;
629
		type Version = ();
630
		type PalletInfo = PalletInfo;
631
632
		type AccountData = pallet_balances::AccountData<u128>;
		type OnNewAccount = ();
633
		type OnKilledAccount = ();
634
		type SystemWeightInfo = ();
635
		type SS58Prefix = ();
636
		type OnSetCode = ();
637
638
639
640
641
642
	}

	parameter_types! {
		pub const ExistentialDeposit: Balance = 1;
	}

643
	impl pallet_balances::Config for Test {
644
645
		type Balance = u128;
		type DustRemoval = ();
646
		type Event = Event;
647
648
649
		type ExistentialDeposit = ExistentialDeposit;
		type AccountStore = System;
		type MaxLocks = ();
Gavin Wood's avatar
Gavin Wood committed
650
651
		type MaxReserves = ();
		type ReserveIdentifier = [u8; 8];
652
653
654
		type WeightInfo = ();
	}

655
656
	impl shared::Config for Test {}

657
	impl paras::Config for Test {
658
		type Origin = Origin;
659
		type Event = Event;
660
661
	}

662
663
664
	impl configuration::Config for Test {
		type WeightInfo = configuration::weights::WeightInfo<Test>;
	}
665
666

	parameter_types! {
667
668
		pub const ParaDeposit: Balance = 10;
		pub const DataDepositPerByte: Balance = 1;
669
670
671
		pub const MaxRetries: u32 = 3;
	}

672
	impl Config for Test {
673
		type Event = Event;
674
		type Origin = Origin;
675
676
677
678
679
		type Currency = Balances;
		type OnSwap = ();
		type ParaDeposit = ParaDeposit;
		type DataDepositPerByte = DataDepositPerByte;
		type WeightInfo = TestWeightInfo;
680
681
	}

682
	pub fn new_test_ext() -> TestExternalities {
683
684
		let mut t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();

685
686
687
		GenesisBuild::<Test>::assimilate_storage(
			&configuration::GenesisConfig {
				config: configuration::HostConfiguration {
Shawn Tabrizi's avatar
Shawn Tabrizi committed
688
					max_code_size: 2 * 1024 * 1024,      // 2 MB
689
690
					max_head_data_size: 1 * 1024 * 1024, // 1 MB
					..Default::default()
Shawn Tabrizi's avatar
Shawn Tabrizi committed
691
				},
692
			},
Shawn Tabrizi's avatar
Shawn Tabrizi committed
693
694
695
			&mut t,
		)
		.unwrap();
696

Shawn Tabrizi's avatar
Shawn Tabrizi committed
697
698
699
		pallet_balances::GenesisConfig::<Test> { balances: vec![(1, 10_000_000), (2, 10_000_000)] }
			.assimilate_storage(&mut t)
			.unwrap();
700
701
702
703

		t.into()
	}

704
705
	const BLOCKS_PER_SESSION: u32 = 3;

706
	fn run_to_block(n: BlockNumber) {
707
		// NOTE that this function only simulates modules of interest. Depending on new pallet may
708
		// require adding it here.
709
		assert!(System::block_number() < n);
710
711
712
713
714
715
716
		while System::block_number() < n {
			let b = System::block_number();

			if System::block_number() > 1 {
				System::on_finalize(System::block_number());
			}
			// Session change every 3 blocks.
717
			if (b + 1) % BLOCKS_PER_SESSION == 0 {
718
				shared::Pallet::<Test>::set_session_index(
Shawn Tabrizi's avatar
Shawn Tabrizi committed
719
					shared::Pallet::<Test>::session_index() + 1,
720
				);
721
				Parachains::test_on_new_session();
722
723
			}
			System::set_block_number(b + 1);
724
			System::on_initialize(System::block_number());
725
726
727
		}
	}

728
729
730
731
732
733
734
735
736
737
	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 {
asynchronous rob's avatar
asynchronous rob committed
738
		let validation_code = vec![0u8; size as usize];
739
740
741
742
743
744
745
		ValidationCode(validation_code)
	}

	fn para_origin(id: ParaId) -> Origin {
		runtime_parachains::Origin::Parachain(id).into()
	}

746
	fn max_code_size() -> u32 {
747
		Configuration::config().max_code_size
748
749
750
	}

	fn max_head_size() -> u32 {
751
		Configuration::config().max_head_data_size
752
753
	}

754
755
756
	#[test]
	fn basic_setup_works() {
		new_test_ext().execute_with(|| {
757
			assert_eq!(PendingSwap::<Test>::get(&ParaId::from(0u32)), None);
758
			assert_eq!(Paras::<Test>::get(&ParaId::from(0u32)), None);
759
760
761
762
		});
	}

	#[test]
763
	fn end_to_end_scenario_works() {
764
		new_test_ext().execute_with(|| {
765
			let para_id = LOWEST_PUBLIC_ID;
766
			run_to_block(1);
767
768
			// first para is not yet registered
			assert!(!Parachains::is_parathread(para_id));
769
			// We register the Para ID
770
			assert_ok!(Registrar::reserve(Origin::signed(1)));
771
772
			assert_ok!(Registrar::register(
				Origin::signed(1),
773
				para_id,
774
775
776
777
778
				test_genesis_head(32),
				test_validation_code(32),
			));
			run_to_session(2);
			// It is now a parathread.
779
780
			assert!(Parachains::is_parathread(para_id));
			assert!(!Parachains::is_parachain(para_id));
781
			// Some other external process will elevate parathread to parachain
782
			assert_ok!(Registrar::make_parachain(para_id));
783
784
			run_to_session(4);
			// It is now a parachain.
785
786
			assert!(!Parachains::is_parathread(para_id));
			assert!(Parachains::is_parachain(para_id));
787
			// Turn it back into a parathread
788
			assert_ok!(Registrar::make_parathread(para_id));
789
			run_to_session(6);
790
791
			assert!(Parachains::is_parathread(para_id));
			assert!(!Parachains::is_parachain(para_id));
792
			// Deregister it
Shawn Tabrizi's avatar
Shawn Tabrizi committed
793
			assert_ok!(Registrar::deregister(Origin::root(), para_id,));
794
795
			run_to_session(8);
			// It is nothing
796
797
			assert!(!Parachains::is_parathread(para_id));
			assert!(!Parachains::is_parachain(para_id));
798
799
		});
	}
800

801
802
803
804
	#[test]
	fn register_works() {
		new_test_ext().execute_with(|| {
			run_to_block(1);
805
806
			let para_id = LOWEST_PUBLIC_ID;
			assert!(!Parachains::is_parathread(para_id));
807
808
			assert_ok!(Registrar::reserve(Origin::signed(1)));
			assert_eq!(Balances::reserved_balance(&1), <Test as Config>::ParaDeposit::get());
809
810
			assert_ok!(Registrar::register(
				Origin::signed(1),
811
				para_id,
812
813
				test_genesis_head(32),
				test_validation_code(32),
814
			));
815
			run_to_session(2);
816
			assert!(Parachains::is_parathread(para_id));
817
818
			assert_eq!(
				Balances::reserved_balance(&1),
Shawn Tabrizi's avatar
Shawn Tabrizi committed
819
820
				<Test as Config>::ParaDeposit::get() +
					64 * <Test as Config>::DataDepositPerByte::get()
821
822
823
			);
		});
	}
824

825
826
827
	#[test]
	fn register_handles_basic_errors() {
		new_test_ext().execute_with(|| {
828
829
			let para_id = LOWEST_PUBLIC_ID;

Shawn Tabrizi's avatar
Shawn Tabrizi committed
830
831
832
833
834
835
836
837
838
			assert_noop!(
				Registrar::register(
					Origin::signed(1),
					para_id,
					test_genesis_head(max_head_size() as usize),
					test_validation_code(max_code_size() as usize),
				),
				Error::<Test>::NotReserved
			);
839

840
			// Successfully register para
841
842
			assert_ok!(Registrar::reserve(Origin::signed(1)));

Shawn Tabrizi's avatar
Shawn Tabrizi committed
843
844
845
846
847
848
849
850
851
			assert_noop!(
				Registrar::register(
					Origin::signed(2),
					para_id,
					test_genesis_head(max_head_size() as usize),
					test_validation_code(max_code_size() as usize),
				),
				Error::<Test>::NotOwner
			);
852

853
854
			assert_ok!(Registrar::register(
				Origin::signed(1),
855
				para_id,
856
857
				test_genesis_head(max_head_size() as usize),
				test_validation_code(max_code_size() as usize),
858
859
			));

860
			run_to_session(2);
861

862
			assert_ok!(Registrar::deregister(Origin::root(), para_id));
863

864
			// Can't do it again
Shawn Tabrizi's avatar
Shawn Tabrizi committed
865
866
867
868
869
870
871
872
873
			assert_noop!(
				Registrar::register(
					Origin::signed(1),
					para_id,
					test_genesis_head(max_head_size() as usize),
					test_validation_code(max_code_size() as usize),
				),
				Error::<Test>::NotReserved
			);
874
875

			// Head Size Check
876
			assert_ok!(Registrar::reserve(Origin::signed(2)));
Shawn Tabrizi's avatar
Shawn Tabrizi committed
877
878
879
880
881
882
883
884
885
			assert_noop!(
				Registrar::register(
					Origin::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
			);
886
887

			// Code Size Check
Shawn Tabrizi's avatar
Shawn Tabrizi committed
888
889
890
891
892
893
894
895
896
			assert_noop!(
				Registrar::register(
					Origin::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
			);
897
898

			// Needs enough funds for deposit
Shawn Tabrizi's avatar
Shawn Tabrizi committed
899
900
901
902
			assert_noop!(
				Registrar::reserve(Origin::signed(1337)),
				BalancesError::<Test, _>::InsufficientBalance
			);
903
904
905
906
		});
	}

	#[test]
907
	fn deregister_works() {
908
909
		new_test_ext().execute_with(|| {
			run_to_block(1);
910
911
			let para_id = LOWEST_PUBLIC_ID;
			assert!(!Parachains::is_parathread(para_id));
912
			assert_ok!(Registrar::reserve(Origin::signed(1)));
913
914
			assert_ok!(Registrar::register(
				Origin::signed(1),
915
				para_id,
916
917
918
919
				test_genesis_head(32),
				test_validation_code(32),
			));
			run_to_session(2);
920
			assert!(Parachains::is_parathread(para_id));
Shawn Tabrizi's avatar
Shawn Tabrizi committed
921
			assert_ok!(Registrar::deregister(Origin::root(), para_id,));
922
			run_to_session(4);
923
			assert!(paras::Pallet::<Test>::lifecycle(para_id).is_none());
924
925
926
			assert_eq!(Balances::reserved_balance(&1), 0);
		});
	}
927

928
929
930
931
	#[test]
	fn deregister_handles_basic_errors() {
		new_test_ext().execute_with(|| {
			run_to_block(1);
932
933
			let para_id = LOWEST_PUBLIC_ID;
			assert!(!Parachains::is_parathread(para_id));
934
			assert_ok!(Registrar::reserve(Origin::signed(1)));
935
			assert_ok!(Registrar::register(
936
				Origin::signed(1),
937
				para_id,
938
939
				test_genesis_head(32),
				test_validation_code(32),
940
			));
941
			run_to_session(2);
942
			assert!(Parachains::is_parathread(para_id));
943
			// Owner check
Shawn Tabrizi's avatar
Shawn Tabrizi committed
944
			assert_noop!(Registrar::deregister(Origin::signed(2), para_id,), BadOrigin);
945
			assert_ok!(Registrar::make_parachain(para_id));
946
947
			run_to_session(4);
			// Cant directly deregister parachain
Shawn Tabrizi's avatar
Shawn Tabrizi committed
948
949
950
951
			assert_noop!(
				Registrar::deregister(Origin::root(), para_id,),
				Error::<Test>::NotParathread
			);
952
953
		});
	}
954

955
956
957
	#[test]
	fn swap_works() {
		new_test_ext().execute_with(|| {
958
959
960
			// Successfully register first two parachains
			let para_1 = LOWEST_PUBLIC_ID;
			let para_2 = LOWEST_PUBLIC_ID + 1;
961
			assert_ok!(Registrar::reserve(Origin::signed(1)));
962
963
			assert_ok!(Registrar::register(
				Origin::signed(1),
964
				para_1,
965
966
				test_genesis_head(max_head_size() as usize),
				test_validation_code(max_code_size() as usize),
967
			));
968
			assert_ok!(Registrar::reserve(Origin::signed(2)));
969
970
			assert_ok!(Registrar::register(
				Origin::signed(2),
971
				para_2,
972
973
				test_genesis_head(max_head_size() as usize),
				test_validation_code(max_code_size() as usize),