1
// Copyright (C) Moondance Labs Ltd.
2
// This file is part of Tanssi.
3

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

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

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

            
17
//! # Registrar Pallet
18
//!
19
//! This pallet is in charge of registering containerChains (identified by their Id)
20
//! that have to be served by the orchestrator chain. Parachains registrations and de-
21
//! registrations are not immediately applied, but rather they take T::SessionDelay sessions
22
//! to be applied.
23
//!
24
//! Registered container chains are stored in the PendingParaIds storage item until the session
25
//! in which they can be onboarded arrives, in which case they are added to the RegisteredParaIds
26
//! storage item.
27

            
28
#![cfg_attr(not(feature = "std"), no_std)]
29

            
30
#[cfg(test)]
31
mod mock;
32

            
33
#[cfg(test)]
34
mod tests;
35

            
36
#[cfg(any(test, feature = "runtime-benchmarks"))]
37
mod benchmark_blob;
38
#[cfg(any(test, feature = "runtime-benchmarks"))]
39
mod benchmarks;
40
pub mod weights;
41
pub use weights::WeightInfo;
42

            
43
pub use pallet::*;
44

            
45
use {
46
    dp_chain_state_snapshot::GenericStateProof,
47
    frame_support::{
48
        pallet_prelude::*,
49
        traits::{
50
            fungible::{Inspect, InspectHold, Mutate, MutateHold},
51
            tokens::{Fortitude, Precision, Restriction},
52
            EnsureOriginWithArg,
53
        },
54
        DefaultNoBound, Hashable, LOG_TARGET,
55
    },
56
    frame_system::pallet_prelude::*,
57
    parity_scale_codec::{Decode, Encode},
58
    sp_core::H256,
59
    sp_runtime::{
60
        traits::{AtLeast32BitUnsigned, Verify},
61
        Saturating,
62
    },
63
    sp_std::{collections::btree_set::BTreeSet, prelude::*},
64
    tp_container_chain_genesis_data::ContainerChainGenesisData,
65
    tp_traits::{
66
        GetCurrentContainerChains, GetSessionContainerChains, GetSessionIndex, ParaId,
67
        ParathreadParams as ParathreadParamsTy, RelayStorageRootProvider, SessionContainerChains,
68
        SlotFrequency,
69
    },
70
};
71

            
72
1
#[frame_support::pallet]
73
pub mod pallet {
74
    use super::*;
75

            
76
198
    #[pallet::pallet]
77
    #[pallet::without_storage_info]
78
    pub struct Pallet<T>(_);
79

            
80
    #[pallet::genesis_config]
81
    #[derive(DefaultNoBound)]
82
    pub struct GenesisConfig<T: Config> {
83
        /// Para ids
84
        pub para_ids: Vec<(ParaId, ContainerChainGenesisData<T::MaxLengthTokenSymbol>)>,
85
    }
86

            
87
536
    #[pallet::genesis_build]
88
    impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
89
276
        fn build(&self) {
90
276
            // Sort para ids and detect duplicates, but do it using a vector of
91
276
            // references to avoid cloning the genesis data, which may be big.
92
276
            let mut para_ids: Vec<&_> = self.para_ids.iter().collect();
93
285
            para_ids.sort_by(|a, b| a.0.cmp(&b.0));
94
278
            para_ids.dedup_by(|a, b| {
95
102
                if a.0 == b.0 {
96
1
                    panic!("Duplicate para_id: {}", u32::from(a.0));
97
                } else {
98
101
                    false
99
101
                }
100
277
            });
101
276

            
102
276
            let mut bounded_para_ids = BoundedVec::default();
103

            
104
469
            for (para_id, genesis_data) in para_ids {
105
195
                bounded_para_ids
106
195
                    .try_push(*para_id)
107
195
                    .expect("too many para ids in genesis: bounded vec full");
108
195

            
109
195
                let genesis_data_size = genesis_data.encoded_size();
110
195
                if genesis_data_size > T::MaxGenesisDataSize::get() as usize {
111
2
                    panic!(
112
2
                        "genesis data for para_id {:?} is too large: {} bytes (limit is {})",
113
2
                        u32::from(*para_id),
114
2
                        genesis_data_size,
115
2
                        T::MaxGenesisDataSize::get()
116
2
                    );
117
193
                }
118
193
                <ParaGenesisData<T>>::insert(para_id, genesis_data);
119
            }
120

            
121
274
            <RegisteredParaIds<T>>::put(bounded_para_ids);
122
274
        }
123
    }
124

            
125
    /// Configure the pallet by specifying the parameters and types on which it depends.
126
    #[pallet::config]
127
    pub trait Config: frame_system::Config {
128
        /// Because this pallet emits events, it depends on the runtime's definition of an event.
129
        type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
130

            
131
        /// Origin that is allowed to call register and deregister
132
        type RegistrarOrigin: EnsureOrigin<Self::RuntimeOrigin>;
133

            
134
        /// Origin that is allowed to call mark_valid_for_collating
135
        type MarkValidForCollatingOrigin: EnsureOrigin<Self::RuntimeOrigin>;
136

            
137
        /// Max length of para id list
138
        #[pallet::constant]
139
        type MaxLengthParaIds: Get<u32>;
140

            
141
        /// Max length of encoded genesis data
142
        #[pallet::constant]
143
        type MaxGenesisDataSize: Get<u32>;
144

            
145
        #[pallet::constant]
146
        type MaxLengthTokenSymbol: Get<u32>;
147

            
148
        type RegisterWithRelayProofOrigin: EnsureOrigin<
149
            Self::RuntimeOrigin,
150
            Success = Self::AccountId,
151
        >;
152

            
153
        type RelayStorageRootProvider: RelayStorageRootProvider;
154

            
155
        type SessionIndex: parity_scale_codec::FullCodec + TypeInfo + Copy + AtLeast32BitUnsigned;
156

            
157
        #[pallet::constant]
158
        type SessionDelay: Get<Self::SessionIndex>;
159

            
160
        type CurrentSessionIndex: GetSessionIndex<Self::SessionIndex>;
161

            
162
        type Currency: Mutate<Self::AccountId>
163
            + MutateHold<Self::AccountId, Reason = Self::RuntimeHoldReason>;
164

            
165
        type RuntimeHoldReason: From<HoldReason>;
166

            
167
        #[pallet::constant]
168
        type DepositAmount: Get<<Self::Currency as Inspect<Self::AccountId>>::Balance>;
169

            
170
        type RegistrarHooks: RegistrarHooks;
171

            
172
        type WeightInfo: WeightInfo;
173
    }
174

            
175
58080
    #[pallet::storage]
176
    pub type RegisteredParaIds<T: Config> =
177
        StorageValue<_, BoundedVec<ParaId, T::MaxLengthParaIds>, ValueQuery>;
178

            
179
24376
    #[pallet::storage]
180
    pub type PendingParaIds<T: Config> = StorageValue<
181
        _,
182
        Vec<(T::SessionIndex, BoundedVec<ParaId, T::MaxLengthParaIds>)>,
183
        ValueQuery,
184
    >;
185

            
186
705
    #[pallet::storage]
187
    pub type ParaGenesisData<T: Config> = StorageMap<
188
        _,
189
        Blake2_128Concat,
190
        ParaId,
191
        ContainerChainGenesisData<T::MaxLengthTokenSymbol>,
192
        OptionQuery,
193
    >;
194

            
195
422
    #[pallet::storage]
196
    pub type PendingVerification<T: Config> =
197
        StorageMap<_, Blake2_128Concat, ParaId, (), OptionQuery>;
198

            
199
154
    #[pallet::storage]
200
    pub type Paused<T: Config> =
201
        StorageValue<_, BoundedVec<ParaId, T::MaxLengthParaIds>, ValueQuery>;
202

            
203
5032
    #[pallet::storage]
204
    pub type PendingPaused<T: Config> = StorageValue<
205
        _,
206
        Vec<(T::SessionIndex, BoundedVec<ParaId, T::MaxLengthParaIds>)>,
207
        ValueQuery,
208
    >;
209

            
210
5204
    #[pallet::storage]
211
    pub type PendingToRemove<T: Config> = StorageValue<
212
        _,
213
        Vec<(T::SessionIndex, BoundedVec<ParaId, T::MaxLengthParaIds>)>,
214
        ValueQuery,
215
    >;
216

            
217
16627
    #[pallet::storage]
218
    pub type ParathreadParams<T: Config> =
219
        StorageMap<_, Blake2_128Concat, ParaId, ParathreadParamsTy, OptionQuery>;
220

            
221
5220
    #[pallet::storage]
222
    pub type PendingParathreadParams<T: Config> = StorageValue<
223
        _,
224
        Vec<(
225
            T::SessionIndex,
226
            BoundedVec<(ParaId, ParathreadParamsTy), T::MaxLengthParaIds>,
227
        )>,
228
        ValueQuery,
229
    >;
230

            
231
    pub type DepositBalanceOf<T> =
232
        <<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
233

            
234
558
    #[derive(Default, Clone, Encode, Decode, RuntimeDebug, PartialEq, scale_info::TypeInfo)]
235
    #[scale_info(skip_type_params(T))]
236
    pub struct DepositInfo<T: Config> {
237
        pub creator: T::AccountId,
238
        pub deposit: DepositBalanceOf<T>,
239
    }
240

            
241
    /// Registrar deposits, a mapping from paraId to a struct
242
    /// holding the creator (from which the deposit was reserved) and
243
    /// the deposit amount
244
245
    #[pallet::storage]
245
    pub type RegistrarDeposit<T: Config> = StorageMap<_, Blake2_128Concat, ParaId, DepositInfo<T>>;
246

            
247
299
    #[pallet::storage]
248
    pub type ParaManager<T: Config> =
249
        StorageMap<_, Blake2_128Concat, ParaId, T::AccountId, OptionQuery>;
250

            
251
    #[pallet::event]
252
295
    #[pallet::generate_deposit(pub(super) fn deposit_event)]
253
    pub enum Event<T: Config> {
254
10
        /// A new para id has been registered. [para_id]
255
        ParaIdRegistered { para_id: ParaId },
256
11
        /// A para id has been deregistered. [para_id]
257
        ParaIdDeregistered { para_id: ParaId },
258
4
        /// A new para id is now valid for collating. [para_id]
259
        ParaIdValidForCollating { para_id: ParaId },
260
2
        /// A para id has been paused from collating.
261
        ParaIdPaused { para_id: ParaId },
262
        /// A para id has been unpaused.
263
        ParaIdUnpaused { para_id: ParaId },
264
        /// Parathread params changed
265
        ParathreadParamsChanged { para_id: ParaId },
266
        /// Para manager has changed
267
        ParaManagerChanged {
268
            para_id: ParaId,
269
            manager_address: T::AccountId,
270
        },
271
    }
272

            
273
50
    #[pallet::error]
274
    pub enum Error<T> {
275
        /// Attempted to register a ParaId that was already registered
276
        ParaIdAlreadyRegistered,
277
        /// Attempted to deregister a ParaId that is not registered
278
        ParaIdNotRegistered,
279
        /// Attempted to deregister a ParaId that is already being deregistered
280
        ParaIdAlreadyDeregistered,
281
        /// Attempted to pause a ParaId that was already paused
282
        ParaIdAlreadyPaused,
283
        /// Attempted to unpause a ParaId that was not paused
284
        ParaIdNotPaused,
285
        /// The bounded list of ParaIds has reached its limit
286
        ParaIdListFull,
287
        /// Attempted to register a ParaId with a genesis data size greater than the limit
288
        GenesisDataTooBig,
289
        /// Tried to mark_valid_for_collating a ParaId that is not in PendingVerification
290
        ParaIdNotInPendingVerification,
291
        /// Tried to register a ParaId with an account that did not have enough balance for the deposit
292
        NotSufficientDeposit,
293
        /// Tried to change parathread params for a para id that is not a registered parathread
294
        NotAParathread,
295
        /// Attempted to execute an extrinsic meant only for the para creator
296
        NotParaCreator,
297
        /// The relay storage root for the corresponding block number could not be retrieved
298
        RelayStorageRootNotFound,
299
        /// The provided relay storage proof is not valid
300
        InvalidRelayStorageProof,
301
        /// The provided signature from the parachain manager in the relay is not valid
302
        InvalidRelayManagerSignature,
303
        /// Tried to deregister a parachain that was not deregistered from the relay chain
304
        ParaStillExistsInRelay,
305
    }
306

            
307
    #[pallet::composite_enum]
308
    pub enum HoldReason {
309
125
        RegistrarDeposit,
310
    }
311

            
312
13618
    #[pallet::hooks]
313
    impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
314
        #[cfg(feature = "try-runtime")]
315
        fn try_state(_n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
316
            use {scale_info::prelude::format, sp_std::collections::btree_set::BTreeSet};
317
            // A para id can only be in 1 of [`RegisteredParaIds`, `PendingVerification`, `Paused`]
318
            // Get all those para ids and check for duplicates
319
            let mut para_ids: Vec<ParaId> = vec![];
320
            para_ids.extend(RegisteredParaIds::<T>::get());
321
            para_ids.extend(PendingVerification::<T>::iter_keys());
322
            para_ids.extend(Paused::<T>::get());
323
            para_ids.sort();
324
            para_ids.dedup_by(|a, b| {
325
                if a == b {
326
                    panic!("Duplicate para id: {}", u32::from(*a));
327
                } else {
328
                    false
329
                }
330
            });
331

            
332
            // All para ids have an entry in `ParaGenesisData`
333
            for para_id in &para_ids {
334
                assert!(
335
                    ParaGenesisData::<T>::contains_key(para_id),
336
                    "Para id {} missing genesis data",
337
                    u32::from(*para_id)
338
                );
339
            }
340

            
341
            // All entries in `RegistrarDeposit` and `ParaGenesisData` are in one of the other lists
342
            let mut para_id_set = BTreeSet::from_iter(para_ids.iter().cloned());
343
            // Also add the Pending lists here
344
            para_id_set.extend(
345
                PendingParaIds::<T>::get()
346
                    .into_iter()
347
                    .flat_map(|(_session_index, x)| x),
348
            );
349
            para_id_set.extend(
350
                PendingPaused::<T>::get()
351
                    .into_iter()
352
                    .flat_map(|(_session_index, x)| x),
353
            );
354
            para_id_set.extend(
355
                PendingToRemove::<T>::get()
356
                    .into_iter()
357
                    .flat_map(|(_session_index, x)| x),
358
            );
359
            let entries: Vec<_> = RegistrarDeposit::<T>::iter().map(|(k, _v)| k).collect();
360
            for para_id in entries {
361
                assert!(
362
                    para_id_set.contains(&para_id),
363
                    "Found RegistrarDeposit for unknown para id: {}",
364
                    u32::from(para_id)
365
                );
366
            }
367
            let entries: Vec<_> = ParaGenesisData::<T>::iter().map(|(k, _v)| k).collect();
368
            for para_id in entries {
369
                assert!(
370
                    para_id_set.contains(&para_id),
371
                    "Found ParaGenesisData for unknown para id: {}",
372
                    u32::from(para_id)
373
                );
374
            }
375

            
376
            // Sorted storage items are sorted
377
            fn assert_is_sorted_and_unique<T: Ord>(x: &[T], name: &str) {
378
                assert!(
379
                    x.windows(2).all(|w| w[0] < w[1]),
380
                    "sorted list not sorted or not unique: {}",
381
                    name,
382
                );
383
            }
384
            assert_is_sorted_and_unique(&RegisteredParaIds::<T>::get(), "RegisteredParaIds");
385
            assert_is_sorted_and_unique(&Paused::<T>::get(), "Paused");
386
            for (i, (_session_index, x)) in PendingParaIds::<T>::get().into_iter().enumerate() {
387
                assert_is_sorted_and_unique(&x, &format!("PendingParaIds[{}]", i));
388
            }
389
            for (i, (_session_index, x)) in PendingPaused::<T>::get().into_iter().enumerate() {
390
                assert_is_sorted_and_unique(&x, &format!("PendingPaused[{}]", i));
391
            }
392
            for (i, (_session_index, x)) in PendingToRemove::<T>::get().into_iter().enumerate() {
393
                assert_is_sorted_and_unique(&x, &format!("PendingToRemove[{}]", i));
394
            }
395

            
396
            // Pending storage items are sorted and session index is unique
397
            let pending: Vec<_> = PendingParaIds::<T>::get()
398
                .into_iter()
399
                .map(|(session_index, _x)| session_index)
400
                .collect();
401
            assert_is_sorted_and_unique(&pending, "PendingParaIds");
402
            let pending: Vec<_> = PendingPaused::<T>::get()
403
                .into_iter()
404
                .map(|(session_index, _x)| session_index)
405
                .collect();
406
            assert_is_sorted_and_unique(&pending, "PendingPaused");
407
            let pending: Vec<_> = PendingToRemove::<T>::get()
408
                .into_iter()
409
                .map(|(session_index, _x)| session_index)
410
                .collect();
411
            assert_is_sorted_and_unique(&pending, "PendingToRemove");
412

            
413
            Ok(())
414
        }
415
    }
416

            
417
323
    #[pallet::call]
418
    impl<T: Config> Pallet<T> {
419
        /// Register container-chain
420
        #[pallet::call_index(0)]
421
        #[pallet::weight(T::WeightInfo::register(genesis_data.encoded_size() as u32, genesis_data.storage.len() as u32))]
422
        pub fn register(
423
            origin: OriginFor<T>,
424
            para_id: ParaId,
425
            genesis_data: ContainerChainGenesisData<T::MaxLengthTokenSymbol>,
426
103
        ) -> DispatchResult {
427
103
            let account = ensure_signed(origin)?;
428
102
            Self::do_register(account, para_id, genesis_data)?;
429
96
            Self::deposit_event(Event::ParaIdRegistered { para_id });
430
96

            
431
96
            Ok(())
432
        }
433

            
434
        /// Deregister container-chain.
435
        ///
436
        /// If a container-chain is registered but not marked as valid_for_collating, this will remove it
437
        /// from `PendingVerification` as well.
438
        #[pallet::call_index(1)]
439
        #[pallet::weight(T::WeightInfo::deregister_immediate(
440
        ).max(T::WeightInfo::deregister_scheduled(
441
        )))]
442
49
        pub fn deregister(origin: OriginFor<T>, para_id: ParaId) -> DispatchResult {
443
49
            T::RegistrarOrigin::ensure_origin(origin)?;
444

            
445
48
            Self::do_deregister(para_id)?;
446

            
447
46
            Ok(())
448
        }
449

            
450
        /// Mark container-chain valid for collating
451
        #[pallet::call_index(2)]
452
        #[pallet::weight(T::WeightInfo::mark_valid_for_collating())]
453
105
        pub fn mark_valid_for_collating(origin: OriginFor<T>, para_id: ParaId) -> DispatchResult {
454
105
            T::MarkValidForCollatingOrigin::ensure_origin(origin)?;
455

            
456
104
            Self::do_mark_valid_for_collating(para_id)?;
457

            
458
99
            Ok(())
459
        }
460

            
461
        /// Pause container-chain from collating. Does not remove its boot nodes nor its genesis config.
462
        /// Only container-chains that have been marked as valid_for_collating can be paused.
463
        #[pallet::call_index(4)]
464
        #[pallet::weight(T::WeightInfo::pause_container_chain())]
465
11
        pub fn pause_container_chain(origin: OriginFor<T>, para_id: ParaId) -> DispatchResult {
466
11
            T::RegistrarOrigin::ensure_origin(origin)?;
467

            
468
10
            Self::schedule_paused_parachain_change(|para_ids, paused| {
469
10
                match paused.binary_search(&para_id) {
470
1
                    Ok(_) => return Err(Error::<T>::ParaIdAlreadyPaused.into()),
471
9
                    Err(index) => {
472
9
                        paused
473
9
                            .try_insert(index, para_id)
474
9
                            .map_err(|_e| Error::<T>::ParaIdListFull)?;
475
                    }
476
                }
477
9
                match para_ids.binary_search(&para_id) {
478
8
                    Ok(index) => {
479
8
                        para_ids.remove(index);
480
8
                    }
481
                    // We can only pause para ids that are marked as valid,
482
                    // otherwise unpausing them later would cause problems
483
1
                    Err(_) => return Err(Error::<T>::ParaIdNotRegistered.into()),
484
                }
485
8
                Self::deposit_event(Event::ParaIdPaused { para_id });
486
8

            
487
8
                Ok(())
488
10
            })?;
489

            
490
8
            Ok(())
491
        }
492

            
493
        /// Unpause container-chain.
494
        /// Only container-chains that have been paused can be unpaused.
495
        #[pallet::call_index(5)]
496
        #[pallet::weight(T::WeightInfo::unpause_container_chain())]
497
6
        pub fn unpause_container_chain(origin: OriginFor<T>, para_id: ParaId) -> DispatchResult {
498
6
            T::RegistrarOrigin::ensure_origin(origin)?;
499

            
500
6
            Self::schedule_paused_parachain_change(|para_ids, paused| {
501
6
                match paused.binary_search(&para_id) {
502
3
                    Ok(index) => {
503
3
                        paused.remove(index);
504
3
                    }
505
3
                    Err(_) => return Err(Error::<T>::ParaIdNotPaused.into()),
506
                }
507
3
                match para_ids.binary_search(&para_id) {
508
                    // This Ok is unreachable, a para id cannot be in "RegisteredParaIds" and "Paused" at the same time
509
                    Ok(_) => return Err(Error::<T>::ParaIdAlreadyRegistered.into()),
510
3
                    Err(index) => {
511
3
                        para_ids
512
3
                            .try_insert(index, para_id)
513
3
                            .map_err(|_e| Error::<T>::ParaIdListFull)?;
514
                    }
515
                }
516
3
                Self::deposit_event(Event::ParaIdUnpaused { para_id });
517
3

            
518
3
                Ok(())
519
6
            })?;
520

            
521
3
            Ok(())
522
        }
523

            
524
        /// Register parathread
525
        #[pallet::call_index(6)]
526
        #[pallet::weight(T::WeightInfo::register_parathread(genesis_data.encoded_size() as u32, genesis_data.storage.len() as u32))]
527
        pub fn register_parathread(
528
            origin: OriginFor<T>,
529
            para_id: ParaId,
530
            slot_frequency: SlotFrequency,
531
            genesis_data: ContainerChainGenesisData<T::MaxLengthTokenSymbol>,
532
29
        ) -> DispatchResult {
533
29
            let account = ensure_signed(origin)?;
534
29
            Self::do_register(account, para_id, genesis_data)?;
535
            // Insert parathread params
536
29
            let params = ParathreadParamsTy { slot_frequency };
537
29
            ParathreadParams::<T>::insert(para_id, params);
538
29
            Self::deposit_event(Event::ParaIdRegistered { para_id });
539
29

            
540
29
            Ok(())
541
        }
542

            
543
        /// Change parathread params
544
        #[pallet::call_index(7)]
545
        #[pallet::weight(T::WeightInfo::set_parathread_params())]
546
        pub fn set_parathread_params(
547
            origin: OriginFor<T>,
548
            para_id: ParaId,
549
            slot_frequency: SlotFrequency,
550
6
        ) -> DispatchResult {
551
6
            T::RegistrarOrigin::ensure_origin(origin)?;
552

            
553
6
            Self::schedule_parathread_params_change(para_id, |params| {
554
5
                params.slot_frequency = slot_frequency;
555
5

            
556
5
                Self::deposit_event(Event::ParathreadParamsChanged { para_id });
557
5

            
558
5
                Ok(())
559
6
            })?;
560

            
561
5
            Ok(())
562
        }
563

            
564
        #[pallet::call_index(8)]
565
        #[pallet::weight(T::WeightInfo::set_para_manager())]
566
        pub fn set_para_manager(
567
            origin: OriginFor<T>,
568
            para_id: ParaId,
569
            manager_address: T::AccountId,
570
2
        ) -> DispatchResult {
571
2
            let origin = ensure_signed(origin)?;
572

            
573
2
            let creator =
574
2
                RegistrarDeposit::<T>::get(para_id).map(|deposit_info| deposit_info.creator);
575
2

            
576
2
            ensure!(Some(origin) == creator, Error::<T>::NotParaCreator);
577

            
578
2
            ParaManager::<T>::insert(para_id, manager_address.clone());
579
2

            
580
2
            Self::deposit_event(Event::<T>::ParaManagerChanged {
581
2
                para_id,
582
2
                manager_address,
583
2
            });
584
2

            
585
2
            Ok(())
586
        }
587

            
588
        /// Register parachain or parathread
589
        #[pallet::call_index(9)]
590
        #[pallet::weight(T::WeightInfo::register_with_relay_proof(genesis_data.encoded_size() as u32, genesis_data.storage.len() as u32))]
591
        pub fn register_with_relay_proof(
592
            origin: OriginFor<T>,
593
            para_id: ParaId,
594
            parathread_params: Option<ParathreadParamsTy>,
595
            relay_proof_block_number: u32,
596
            relay_storage_proof: sp_trie::StorageProof,
597
            manager_signature: cumulus_primitives_core::relay_chain::Signature,
598
            genesis_data: ContainerChainGenesisData<T::MaxLengthTokenSymbol>,
599
9
        ) -> DispatchResult {
600
9
            let account = T::RegisterWithRelayProofOrigin::ensure_origin(origin)?;
601
8
            let relay_storage_root =
602
9
                T::RelayStorageRootProvider::get_relay_storage_root(relay_proof_block_number)
603
9
                    .ok_or(Error::<T>::RelayStorageRootNotFound)?;
604
7
            let relay_state_proof =
605
8
                GenericStateProof::<cumulus_primitives_core::relay_chain::Block>::new(
606
8
                    relay_storage_root,
607
8
                    relay_storage_proof,
608
8
                )
609
8
                .map_err(|_| Error::<T>::InvalidRelayStorageProof)?;
610

            
611
7
            let bytes = para_id.twox_64_concat();
612
7
            let key = [REGISTRAR_PARAS_INDEX, bytes.as_slice()].concat();
613
7
            let relay_para_info = relay_state_proof
614
7
                .read_entry::<ParaInfo<
615
7
                    cumulus_primitives_core::relay_chain::AccountId,
616
7
                    cumulus_primitives_core::relay_chain::Balance,
617
7
                >>(key.as_slice(), None)
618
7
                .map_err(|_| Error::<T>::InvalidRelayStorageProof)?;
619
5
            let relay_manager = relay_para_info.manager;
620
5

            
621
5
            // Verify manager signature
622
5
            let signature_msg = Self::relay_signature_msg(para_id, &account, relay_storage_root);
623
5
            if !manager_signature.verify(&*signature_msg, &relay_manager) {
624
2
                return Err(Error::<T>::InvalidRelayManagerSignature.into());
625
3
            }
626
3

            
627
3
            Self::do_register(account, para_id, genesis_data)?;
628
            // Insert parathread params
629
3
            if let Some(parathread_params) = parathread_params {
630
                ParathreadParams::<T>::insert(para_id, parathread_params);
631
3
            }
632
3
            Self::deposit_event(Event::ParaIdRegistered { para_id });
633
3

            
634
3
            Ok(())
635
        }
636

            
637
        /// Deregister a parachain that no longer exists in the relay chain. The origin of this
638
        /// extrinsic will be rewarded with the parachain deposit.
639
        #[pallet::call_index(10)]
640
        #[pallet::weight(T::WeightInfo::deregister_with_relay_proof_immediate(
641
        ).max(T::WeightInfo::deregister_with_relay_proof_scheduled(
642
        )))]
643
        pub fn deregister_with_relay_proof(
644
            origin: OriginFor<T>,
645
            para_id: ParaId,
646
            relay_proof_block_number: u32,
647
            relay_storage_proof: sp_trie::StorageProof,
648
7
        ) -> DispatchResult {
649
7
            let account = T::RegisterWithRelayProofOrigin::ensure_origin(origin)?;
650

            
651
6
            let relay_storage_root =
652
7
                T::RelayStorageRootProvider::get_relay_storage_root(relay_proof_block_number)
653
7
                    .ok_or(Error::<T>::RelayStorageRootNotFound)?;
654
5
            let relay_state_proof =
655
6
                GenericStateProof::<cumulus_primitives_core::relay_chain::Block>::new(
656
6
                    relay_storage_root,
657
6
                    relay_storage_proof,
658
6
                )
659
6
                .map_err(|_| Error::<T>::InvalidRelayStorageProof)?;
660

            
661
5
            let bytes = para_id.twox_64_concat();
662
5
            let key = [REGISTRAR_PARAS_INDEX, bytes.as_slice()].concat();
663
            // TODO: we don't even need to decode the value, only check if it exists
664
            // Need to add exists_storage method to dancekit
665
5
            let relay_para_info = relay_state_proof
666
5
                .read_optional_entry::<ParaInfo<
667
5
                    cumulus_primitives_core::relay_chain::AccountId,
668
5
                    cumulus_primitives_core::relay_chain::Balance,
669
5
                >>(key.as_slice())
670
5
                .map_err(|_| Error::<T>::InvalidRelayStorageProof)?;
671
5
            if relay_para_info.is_some() {
672
1
                return Err(Error::<T>::ParaStillExistsInRelay.into());
673
4
            }
674

            
675
            // Take the deposit immediately and give it to origin account
676
4
            if let Some(asset_info) = RegistrarDeposit::<T>::take(para_id) {
677
4
                // Slash deposit from parachain creator
678
4
                // TODO: error handling
679
4
                let _ = T::Currency::transfer_on_hold(
680
4
                    &HoldReason::RegistrarDeposit.into(),
681
4
                    &asset_info.creator,
682
4
                    &account,
683
4
                    asset_info.deposit,
684
4
                    Precision::Exact,
685
4
                    Restriction::Free,
686
4
                    Fortitude::Force,
687
4
                );
688
4
            }
689

            
690
4
            Self::do_deregister(para_id)?;
691

            
692
4
            Ok(())
693
        }
694
    }
695

            
696
    pub struct SessionChangeOutcome<T: Config> {
697
        /// Previously active parachains.
698
        pub prev_paras: BoundedVec<ParaId, T::MaxLengthParaIds>,
699
        /// If new parachains have been applied in the new session, this is the new  list.
700
        pub new_paras: Option<BoundedVec<ParaId, T::MaxLengthParaIds>>,
701
    }
702

            
703
    impl<T: Config> Pallet<T> {
704
78
        pub fn is_para_manager(para_id: &ParaId, account: &T::AccountId) -> bool {
705
            // This check will only pass if both are true:
706
            // * The para_id has a deposit in pallet_registrar
707
            // * The signed_account is the para manager (or creator if None)
708
78
            if let Some(manager) = ParaManager::<T>::get(para_id) {
709
78
                manager == *account
710
            } else {
711
                RegistrarDeposit::<T>::get(para_id)
712
                    .map(|deposit_info| deposit_info.creator)
713
                    .as_ref()
714
                    == Some(account)
715
            }
716
78
        }
717

            
718
        #[cfg(feature = "runtime-benchmarks")]
719
        pub fn benchmarks_get_or_create_para_manager(para_id: &ParaId) -> T::AccountId {
720
            use {
721
                frame_benchmarking::account,
722
                frame_support::{assert_ok, dispatch::RawOrigin},
723
            };
724
            // Return container chain manager, or register container chain as ALICE if it does not exist
725
            if !ParaGenesisData::<T>::contains_key(para_id) {
726
                // Register as a new user
727

            
728
                /// Create a funded user.
729
                /// Used for generating the necessary amount for registering
730
                fn create_funded_user<T: Config>(
731
                    string: &'static str,
732
                    n: u32,
733
                    total: DepositBalanceOf<T>,
734
                ) -> (T::AccountId, DepositBalanceOf<T>) {
735
                    const SEED: u32 = 0;
736
                    let user = account(string, n, SEED);
737
                    assert_ok!(T::Currency::mint_into(&user, total));
738
                    (user, total)
739
                }
740
                let new_balance =
741
                    (T::Currency::minimum_balance() + T::DepositAmount::get()) * 2u32.into();
742
                let account = create_funded_user::<T>("caller", 1000, new_balance).0;
743
                let origin = RawOrigin::Signed(account);
744
                assert_ok!(Self::register(origin.into(), *para_id, Default::default()));
745
            }
746

            
747
            let deposit_info = RegistrarDeposit::<T>::get(para_id).expect("Cannot return signed origin for a container chain that was registered by root. Try using a different para id");
748

            
749
            // Fund deposit creator, just in case it is not a new account
750
            let new_balance =
751
                (T::Currency::minimum_balance() + T::DepositAmount::get()) * 2u32.into();
752
            assert_ok!(T::Currency::mint_into(&deposit_info.creator, new_balance));
753

            
754
            deposit_info.creator
755
        }
756

            
757
134
        fn do_register(
758
134
            account: T::AccountId,
759
134
            para_id: ParaId,
760
134
            genesis_data: ContainerChainGenesisData<T::MaxLengthTokenSymbol>,
761
134
        ) -> DispatchResult {
762
134
            let deposit = T::DepositAmount::get();
763
134
            // Verify we can hold
764
134
            if !T::Currency::can_hold(&HoldReason::RegistrarDeposit.into(), &account, deposit) {
765
                return Err(Error::<T>::NotSufficientDeposit.into());
766
134
            }
767
134

            
768
134
            // Check if the para id is already registered by looking at the genesis data
769
134
            if ParaGenesisData::<T>::contains_key(para_id) {
770
5
                return Err(Error::<T>::ParaIdAlreadyRegistered.into());
771
129
            }
772
129

            
773
129
            // Check if the para id is already in PendingVerification (unreachable)
774
129
            let is_pending_verification = PendingVerification::<T>::take(para_id).is_some();
775
129
            if is_pending_verification {
776
                return Err(Error::<T>::ParaIdAlreadyRegistered.into());
777
129
            }
778
129

            
779
129
            // Insert para id into PendingVerification
780
129
            PendingVerification::<T>::insert(para_id, ());
781
129

            
782
129
            // The actual registration takes place 2 sessions after the call to
783
129
            // `mark_valid_for_collating`, but the genesis data is inserted now.
784
129
            // This is because collators should be able to start syncing the new container chain
785
129
            // before the first block is mined. However, we could store the genesis data in a
786
129
            // different key, like PendingParaGenesisData.
787
129
            // TODO: for benchmarks, this call to .encoded_size is O(n) with respect to the number
788
129
            // of key-values in `genesis_data.storage`, even if those key-values are empty. And we
789
129
            // won't detect that the size is too big until after iterating over all of them, so the
790
129
            // limit in that case would be the transaction size.
791
129
            let genesis_data_size = genesis_data.encoded_size();
792
129
            if genesis_data_size > T::MaxGenesisDataSize::get() as usize {
793
1
                return Err(Error::<T>::GenesisDataTooBig.into());
794
128
            }
795
128

            
796
128
            // Hold the deposit, we verified we can do this
797
128
            T::Currency::hold(&HoldReason::RegistrarDeposit.into(), &account, deposit)?;
798

            
799
            // Update DepositInfo
800
128
            RegistrarDeposit::<T>::insert(
801
128
                para_id,
802
128
                DepositInfo {
803
128
                    creator: account.clone(),
804
128
                    deposit,
805
128
                },
806
128
            );
807
128
            ParaGenesisData::<T>::insert(para_id, genesis_data);
808
128

            
809
128
            ParaManager::<T>::insert(para_id, account);
810
128

            
811
128
            Ok(())
812
134
        }
813

            
814
52
        fn do_deregister(para_id: ParaId) -> DispatchResult {
815
52
            // Check if the para id is in "PendingVerification".
816
52
            // This is a special case because then we can remove it immediately, instead of waiting 2 sessions.
817
52
            let is_pending_verification = PendingVerification::<T>::take(para_id).is_some();
818
52
            if is_pending_verification {
819
5
                Self::deposit_event(Event::ParaIdDeregistered { para_id });
820
5
                // Cleanup immediately
821
5
                Self::cleanup_deregistered_para_id(para_id);
822
5
            } else {
823
47
                Self::schedule_paused_parachain_change(|para_ids, paused| {
824
47
                    // We have to find out where, in the sorted vec the para id is, if anywhere.
825
47

            
826
47
                    match para_ids.binary_search(&para_id) {
827
42
                        Ok(index) => {
828
42
                            para_ids.remove(index);
829
42
                        }
830
                        Err(_) => {
831
                            // If the para id is not registered, it may be paused. In that case, remove it from there
832
5
                            match paused.binary_search(&para_id) {
833
3
                                Ok(index) => {
834
3
                                    paused.remove(index);
835
3
                                }
836
                                Err(_) => {
837
2
                                    return Err(Error::<T>::ParaIdNotRegistered.into());
838
                                }
839
                            }
840
                        }
841
                    }
842

            
843
45
                    Ok(())
844
47
                })?;
845
                // Mark this para id for cleanup later
846
45
                Self::schedule_parachain_cleanup(para_id)?;
847
45
                Self::deposit_event(Event::ParaIdDeregistered { para_id });
848
            }
849

            
850
50
            Ok(())
851
52
        }
852

            
853
104
        fn do_mark_valid_for_collating(para_id: ParaId) -> DispatchResult {
854
104
            let is_pending_verification = PendingVerification::<T>::take(para_id).is_some();
855
104
            if !is_pending_verification {
856
3
                return Err(Error::<T>::ParaIdNotInPendingVerification.into());
857
101
            }
858
101

            
859
101
            Self::schedule_parachain_change(|para_ids| {
860
101
                // We don't want to add duplicate para ids, so we check whether the potential new
861
101
                // para id is already present in the list. Because the list is always ordered, we can
862
101
                // leverage the binary search which makes this check O(log n).
863
101

            
864
101
                match para_ids.binary_search(&para_id) {
865
                    // This Ok is unreachable
866
                    Ok(_) => return Err(Error::<T>::ParaIdAlreadyRegistered.into()),
867
101
                    Err(index) => {
868
101
                        para_ids
869
101
                            .try_insert(index, para_id)
870
101
                            .map_err(|_e| Error::<T>::ParaIdListFull)?;
871
                    }
872
                }
873

            
874
101
                Ok(())
875
101
            })?;
876

            
877
101
            T::RegistrarHooks::check_valid_for_collating(para_id)?;
878

            
879
99
            Self::deposit_event(Event::ParaIdValidForCollating { para_id });
880
99

            
881
99
            T::RegistrarHooks::para_marked_valid_for_collating(para_id);
882
99

            
883
99
            Ok(())
884
104
        }
885

            
886
        /// Relay parachain manager signature message. Includes:
887
        /// * para_id, in case the manager has more than 1 para in the relay
888
        /// * accountid in tanssi, to ensure that the creator role is assigned to the desired account
889
        /// * relay_storage_root, to make the signature network-specific, and also make it expire
890
        ///     when the relay storage root expires.
891
12
        pub fn relay_signature_msg(
892
12
            para_id: ParaId,
893
12
            tanssi_account: &T::AccountId,
894
12
            relay_storage_root: H256,
895
12
        ) -> Vec<u8> {
896
12
            (para_id, tanssi_account, relay_storage_root).encode()
897
12
        }
898

            
899
101
        fn schedule_parachain_change(
900
101
            updater: impl FnOnce(&mut BoundedVec<ParaId, T::MaxLengthParaIds>) -> DispatchResult,
901
101
        ) -> DispatchResult {
902
101
            let mut pending_paras = PendingParaIds::<T>::get();
903
101
            // First, we need to decide what we should use as the base paras.
904
101
            let mut base_paras = pending_paras
905
101
                .last()
906
101
                .map(|(_, paras)| paras.clone())
907
101
                .unwrap_or_else(Self::registered_para_ids);
908
101

            
909
101
            updater(&mut base_paras)?;
910
101
            let new_paras = base_paras;
911
101

            
912
101
            let scheduled_session = Self::scheduled_session();
913

            
914
101
            if let Some(&mut (_, ref mut paras)) = pending_paras
915
101
                .iter_mut()
916
101
                .find(|&&mut (apply_at_session, _)| apply_at_session >= scheduled_session)
917
7
            {
918
7
                *paras = new_paras;
919
94
            } else {
920
94
                // We are scheduling a new parachains change for the scheduled session.
921
94
                pending_paras.push((scheduled_session, new_paras));
922
94
            }
923

            
924
101
            <PendingParaIds<T>>::put(pending_paras);
925
101

            
926
101
            Ok(())
927
101
        }
928

            
929
63
        fn schedule_paused_parachain_change(
930
63
            updater: impl FnOnce(
931
63
                &mut BoundedVec<ParaId, T::MaxLengthParaIds>,
932
63
                &mut BoundedVec<ParaId, T::MaxLengthParaIds>,
933
63
            ) -> DispatchResult,
934
63
        ) -> DispatchResult {
935
63
            let mut pending_paras = PendingParaIds::<T>::get();
936
63
            let mut pending_paused = PendingPaused::<T>::get();
937
63
            // First, we need to decide what we should use as the base paras.
938
63
            let mut base_paras = pending_paras
939
63
                .last()
940
63
                .map(|(_, paras)| paras.clone())
941
63
                .unwrap_or_else(Self::registered_para_ids);
942
63
            let mut base_paused = pending_paused
943
63
                .last()
944
63
                .map(|(_, paras)| paras.clone())
945
63
                .unwrap_or_else(Self::paused);
946
63
            let old_base_paras = base_paras.clone();
947
63
            let old_base_paused = base_paused.clone();
948
63

            
949
63
            updater(&mut base_paras, &mut base_paused)?;
950

            
951
56
            if base_paras != old_base_paras {
952
53
                let new_paras = base_paras;
953
53
                let scheduled_session = Self::scheduled_session();
954

            
955
53
                if let Some(&mut (_, ref mut paras)) = pending_paras
956
53
                    .iter_mut()
957
53
                    .find(|&&mut (apply_at_session, _)| apply_at_session >= scheduled_session)
958
17
                {
959
17
                    *paras = new_paras;
960
40
                } else {
961
36
                    // We are scheduling a new parachains change for the scheduled session.
962
36
                    pending_paras.push((scheduled_session, new_paras));
963
36
                }
964

            
965
53
                <PendingParaIds<T>>::put(pending_paras);
966
3
            }
967

            
968
56
            if base_paused != old_base_paused {
969
14
                let new_paused = base_paused;
970
14
                let scheduled_session = Self::scheduled_session();
971

            
972
14
                if let Some(&mut (_, ref mut paras)) = pending_paused
973
14
                    .iter_mut()
974
14
                    .find(|&&mut (apply_at_session, _)| apply_at_session >= scheduled_session)
975
2
                {
976
2
                    *paras = new_paused;
977
12
                } else {
978
12
                    // We are scheduling a new parachains change for the scheduled session.
979
12
                    pending_paused.push((scheduled_session, new_paused));
980
12
                }
981

            
982
14
                <PendingPaused<T>>::put(pending_paused);
983
42
            }
984

            
985
56
            Ok(())
986
63
        }
987

            
988
6
        fn schedule_parathread_params_change(
989
6
            para_id: ParaId,
990
6
            updater: impl FnOnce(&mut ParathreadParamsTy) -> DispatchResult,
991
6
        ) -> DispatchResult {
992
            // Check that the para id is a parathread by reading the old params
993
6
            let params = match ParathreadParams::<T>::get(para_id) {
994
5
                Some(x) => x,
995
                None => {
996
1
                    return Err(Error::<T>::NotAParathread.into());
997
                }
998
            };
999

            
5
            let mut pending_params = PendingParathreadParams::<T>::get();
5
            // First, we need to decide what we should use as the base params.
5
            let mut base_params = pending_params
5
                .last()
5
                .and_then(|(_, para_id_params)| {
                    match para_id_params
                        .binary_search_by_key(&para_id, |(para_id, _params)| *para_id)
                    {
                        Ok(idx) => {
                            let (_para_id, params) = &para_id_params[idx];
                            Some(params.clone())
                        }
                        Err(_idx) => None,
                    }
5
                })
5
                .unwrap_or(params);
5

            
5
            updater(&mut base_params)?;
5
            let new_params = base_params;
5

            
5
            let scheduled_session = Self::scheduled_session();
5
            if let Some(&mut (_, ref mut para_id_params)) = pending_params
5
                .iter_mut()
5
                .find(|&&mut (apply_at_session, _)| apply_at_session >= scheduled_session)
            {
                match para_id_params.binary_search_by_key(&para_id, |(para_id, _params)| *para_id) {
                    Ok(idx) => {
                        let (_para_id, params) = &mut para_id_params[idx];
                        *params = new_params;
                    }
                    Err(idx) => {
                        para_id_params
                            .try_insert(idx, (para_id, new_params))
                            .map_err(|_e| Error::<T>::ParaIdListFull)?;
                    }
                }
5
            } else {
5
                // We are scheduling a new parathread params change for the scheduled session.
5
                pending_params.push((
5
                    scheduled_session,
5
                    BoundedVec::truncate_from(vec![(para_id, new_params)]),
5
                ));
5
            }
5
            <PendingParathreadParams<T>>::put(pending_params);
5

            
5
            Ok(())
6
        }
        /// Return the session index that should be used for any future scheduled changes.
218
        fn scheduled_session() -> T::SessionIndex {
218
            T::CurrentSessionIndex::session_index().saturating_add(T::SessionDelay::get())
218
        }
        /// Called by the initializer to note that a new session has started.
        ///
        /// Returns the parachain list that was actual before the session change and the parachain list
        /// that became active after the session change. If there were no scheduled changes, both will
        /// be the same.
2429
        pub fn initializer_on_new_session(
2429
            session_index: &T::SessionIndex,
2429
        ) -> SessionChangeOutcome<T> {
2429
            let pending_paras = <PendingParaIds<T>>::get();
2429
            let prev_paras = RegisteredParaIds::<T>::get();
2429
            let new_paras = if !pending_paras.is_empty() {
414
                let (mut past_and_present, future) = pending_paras
414
                    .into_iter()
420
                    .partition::<Vec<_>, _>(|&(apply_at_session, _)| {
420
                        apply_at_session <= *session_index
420
                    });
414

            
414
                if past_and_present.len() > 1 {
                    // This should never happen since we schedule parachain changes only into the future
                    // sessions and this handler called for each session change.
                    log::error!(
                        target: LOG_TARGET,
                        "Skipping applying parachain changes scheduled sessions in the past",
                    );
414
                }
414
                let new_paras = past_and_present.pop().map(|(_, paras)| paras);
414
                if let Some(ref new_paras) = new_paras {
210
                    // Apply the new parachain list.
210
                    RegisteredParaIds::<T>::put(new_paras);
210
                    <PendingParaIds<T>>::put(future);
210
                }
414
                new_paras
            } else {
                // pending_paras.is_empty, so parachain list did not change
2015
                None
            };
2429
            let pending_paused = <PendingPaused<T>>::get();
2429
            if !pending_paused.is_empty() {
19
                let (mut past_and_present, future) = pending_paused
19
                    .into_iter()
20
                    .partition::<Vec<_>, _>(|&(apply_at_session, _)| {
20
                        apply_at_session <= *session_index
20
                    });
19

            
19
                if past_and_present.len() > 1 {
                    // This should never happen since we schedule parachain changes only into the future
                    // sessions and this handler called for each session change.
                    log::error!(
                        target: LOG_TARGET,
                        "Skipping applying paused parachain changes scheduled sessions in the past",
                    );
19
                }
19
                let new_paused = past_and_present.pop().map(|(_, paras)| paras);
19
                if let Some(ref new_paused) = new_paused {
10
                    // Apply the new parachain list.
10
                    Paused::<T>::put(new_paused);
10
                    <PendingPaused<T>>::put(future);
10
                }
2410
            }
2429
            let pending_parathread_params = <PendingParathreadParams<T>>::get();
2429
            if !pending_parathread_params.is_empty() {
10
                let (mut past_and_present, future) = pending_parathread_params
10
                    .into_iter()
10
                    .partition::<Vec<_>, _>(|&(apply_at_session, _)| {
10
                        apply_at_session <= *session_index
10
                    });
10

            
10
                if past_and_present.len() > 1 {
                    // This should never happen since we schedule parachain changes only into the future
                    // sessions and this handler called for each session change.
                    log::error!(
                        target: LOG_TARGET,
                        "Skipping applying parathread params changes scheduled sessions in the past",
                    );
10
                }
10
                let new_params = past_and_present.pop().map(|(_, params)| params);
10
                if let Some(ref new_params) = new_params {
9
                    for (para_id, params) in new_params {
4
                        <ParathreadParams<T>>::insert(para_id, params);
4
                    }
5
                    <PendingParathreadParams<T>>::put(future);
5
                }
2419
            }
2429
            let pending_to_remove = <PendingToRemove<T>>::get();
2429
            if !pending_to_remove.is_empty() {
165
                let (past_and_present, future) =
165
                    pending_to_remove.into_iter().partition::<Vec<_>, _>(
166
                        |&(apply_at_session, _)| apply_at_session <= *session_index,
165
                    );
165

            
165
                if !past_and_present.is_empty() {
                    // Unlike `PendingParaIds`, this cannot skip items because we must cleanup all parachains.
                    // But this will only happen if `initializer_on_new_session` is not called for a big range of
                    // sessions, and many parachains are deregistered in the meantime.
83
                    let mut removed_para_ids = BTreeSet::new();
166
                    for (_, new_paras) in &past_and_present {
169
                        for para_id in new_paras {
86
                            Self::cleanup_deregistered_para_id(*para_id);
86
                            removed_para_ids.insert(*para_id);
86
                        }
                    }
                    // Also need to remove PendingParams to avoid setting params for a para id that does not exist
83
                    let mut pending_parathread_params = <PendingParathreadParams<T>>::get();
84
                    for (_, new_params) in &mut pending_parathread_params {
1
                        new_params.retain(|(para_id, _params)| {
1
                            // Retain para ids that are not in the list of removed para ids
1
                            !removed_para_ids.contains(para_id)
1
                        });
1
                    }
83
                    <PendingParathreadParams<T>>::put(pending_parathread_params);
83
                    <PendingToRemove<T>>::put(future);
82
                }
2264
            }
2429
            SessionChangeOutcome {
2429
                prev_paras,
2429
                new_paras,
2429
            }
2429
        }
        /// Remove all para id storage in this pallet,
        /// and execute para_deregistered hook to clean up other pallets as well
91
        fn cleanup_deregistered_para_id(para_id: ParaId) {
91
            ParaGenesisData::<T>::remove(para_id);
91
            ParathreadParams::<T>::remove(para_id);
            // Get asset creator and deposit amount
            // Deposit may not exist, for example if the para id was registered on genesis
91
            if let Some(asset_info) = RegistrarDeposit::<T>::take(para_id) {
24
                // Release hold
24
                let _ = T::Currency::release(
24
                    &HoldReason::RegistrarDeposit.into(),
24
                    &asset_info.creator,
24
                    asset_info.deposit,
24
                    Precision::Exact,
24
                );
86
            }
91
            ParaManager::<T>::remove(para_id);
91

            
91
            T::RegistrarHooks::para_deregistered(para_id);
91
        }
45
        fn schedule_parachain_cleanup(para_id: ParaId) -> DispatchResult {
45
            let scheduled_session = Self::scheduled_session();
45
            let mut pending_paras = PendingToRemove::<T>::get();
            // First, we need to decide what we should use as the base paras.
45
            let base_paras = match pending_paras
45
                .binary_search_by_key(&scheduled_session, |(session, _paras)| *session)
            {
3
                Ok(i) => &mut pending_paras[i].1,
42
                Err(i) => {
42
                    pending_paras.insert(i, (scheduled_session, Default::default()));
42

            
42
                    &mut pending_paras[i].1
                }
            };
            // Add the para_id to the entry for the scheduled session.
45
            match base_paras.binary_search(&para_id) {
                // This Ok is unreachable
                Ok(_) => return Err(Error::<T>::ParaIdAlreadyDeregistered.into()),
45
                Err(index) => {
45
                    base_paras
45
                        .try_insert(index, para_id)
45
                        .map_err(|_e| Error::<T>::ParaIdListFull)?;
                }
            }
            // Save the updated list of pending parachains for removal.
45
            <PendingToRemove<T>>::put(pending_paras);
45

            
45
            Ok(())
45
        }
26125
        pub fn registered_para_ids() -> BoundedVec<ParaId, T::MaxLengthParaIds> {
26125
            RegisteredParaIds::<T>::get()
26125
        }
9229
        pub fn pending_registered_para_ids(
9229
        ) -> Vec<(T::SessionIndex, BoundedVec<ParaId, T::MaxLengthParaIds>)> {
9229
            PendingParaIds::<T>::get()
9229
        }
159
        pub fn para_genesis_data(
159
            para_id: ParaId,
159
        ) -> Option<ContainerChainGenesisData<T::MaxLengthTokenSymbol>> {
159
            ParaGenesisData::<T>::get(para_id)
159
        }
        pub fn pending_verification(para_id: ParaId) -> Option<()> {
            PendingVerification::<T>::get(para_id)
        }
65
        pub fn paused() -> BoundedVec<ParaId, T::MaxLengthParaIds> {
65
            Paused::<T>::get()
65
        }
        pub fn pending_paused() -> Vec<(T::SessionIndex, BoundedVec<ParaId, T::MaxLengthParaIds>)> {
            PendingPaused::<T>::get()
        }
        pub fn pending_to_remove() -> Vec<(T::SessionIndex, BoundedVec<ParaId, T::MaxLengthParaIds>)>
        {
            PendingToRemove::<T>::get()
        }
35
        pub fn parathread_params(para_id: ParaId) -> Option<ParathreadParamsTy> {
35
            ParathreadParams::<T>::get(para_id)
35
        }
        pub fn pending_parathread_params() -> Vec<(
            T::SessionIndex,
            BoundedVec<(ParaId, ParathreadParamsTy), T::MaxLengthParaIds>,
        )> {
            PendingParathreadParams::<T>::get()
        }
14
        pub fn registrar_deposit(para_id: ParaId) -> Option<DepositInfo<T>> {
14
            RegistrarDeposit::<T>::get(para_id)
14
        }
    }
    impl<T: Config> GetCurrentContainerChains for Pallet<T> {
        type MaxContainerChains = T::MaxLengthParaIds;
16945
        fn current_container_chains() -> BoundedVec<ParaId, Self::MaxContainerChains> {
16945
            Self::registered_para_ids()
16945
        }
        #[cfg(feature = "runtime-benchmarks")]
        fn set_current_container_chains(container_chains: &[ParaId]) {
            let paras: BoundedVec<ParaId, T::MaxLengthParaIds> =
                container_chains.to_vec().try_into().unwrap();
            RegisteredParaIds::<T>::put(paras);
        }
    }
    impl<T: Config> GetSessionContainerChains<T::SessionIndex> for Pallet<T> {
9199
        fn session_container_chains(session_index: T::SessionIndex) -> SessionContainerChains {
9199
            let (past_and_present, _) = Pallet::<T>::pending_registered_para_ids()
9199
                .into_iter()
9199
                .partition::<Vec<_>, _>(|&(apply_at_session, _)| apply_at_session <= session_index);
9199
            let paras = if let Some(last) = past_and_present.last() {
208
                last.1.clone()
            } else {
8991
                Pallet::<T>::registered_para_ids()
            };
9199
            let mut parachains = vec![];
9199
            let mut parathreads = vec![];
25661
            for para_id in paras {
                // TODO: sweet O(n) db reads
16462
                if let Some(parathread_params) = ParathreadParams::<T>::get(para_id) {
312
                    parathreads.push((para_id, parathread_params));
16150
                } else {
16150
                    parachains.push(para_id);
16150
                }
            }
9199
            SessionContainerChains {
9199
                parachains,
9199
                parathreads,
9199
            }
9199
        }
        #[cfg(feature = "runtime-benchmarks")]
        fn set_session_container_chains(
            _session_index: T::SessionIndex,
            container_chains: &[ParaId],
        ) {
            // TODO: this assumes session_index == current
            let paras: BoundedVec<ParaId, T::MaxLengthParaIds> =
                container_chains.to_vec().try_into().unwrap();
            RegisteredParaIds::<T>::put(paras);
        }
    }
}
pub trait RegistrarHooks {
    fn para_marked_valid_for_collating(_para_id: ParaId) -> Weight {
        Weight::default()
    }
    fn para_deregistered(_para_id: ParaId) -> Weight {
        Weight::default()
    }
    fn check_valid_for_collating(_para_id: ParaId) -> DispatchResult {
        Ok(())
    }
    #[cfg(feature = "runtime-benchmarks")]
    fn benchmarks_ensure_valid_for_collating(_para_id: ParaId) {}
}
impl RegistrarHooks for () {}
pub struct EnsureSignedByManager<T>(sp_std::marker::PhantomData<T>);
impl<T> EnsureOriginWithArg<T::RuntimeOrigin, ParaId> for EnsureSignedByManager<T>
where
    T: Config,
{
    type Success = T::AccountId;
98
    fn try_origin(
98
        o: T::RuntimeOrigin,
98
        para_id: &ParaId,
98
    ) -> Result<Self::Success, T::RuntimeOrigin> {
78
        let signed_account =
98
            <frame_system::EnsureSigned<_> as EnsureOrigin<_>>::try_origin(o.clone())?;
78
        if !Pallet::<T>::is_para_manager(para_id, &signed_account) {
2
            return Err(frame_system::RawOrigin::Signed(signed_account).into());
76
        }
76

            
76
        Ok(signed_account)
98
    }
    #[cfg(feature = "runtime-benchmarks")]
    fn try_successful_origin(para_id: &ParaId) -> Result<T::RuntimeOrigin, ()> {
        let manager = Pallet::<T>::benchmarks_get_or_create_para_manager(para_id);
        Ok(frame_system::RawOrigin::Signed(manager).into())
    }
}
// TODO: import this from dancekit
pub const REGISTRAR_PARAS_INDEX: &[u8] =
    &hex_literal::hex!["3fba98689ebed1138735e0e7a5a790abcd710b30bd2eab0352ddcc26417aa194"];
// Need to copy ParaInfo from
// polkadot-sdk/polkadot/runtime/common/src/paras_registrar/mod.rs
// Because its fields are not public...
// TODO: import this from dancekit
#[derive(Encode, Decode, Clone, PartialEq, Eq, Default, TypeInfo)]
pub struct ParaInfo<Account, Balance> {
    /// The account that has placed a deposit for registering this para.
    manager: Account,
    /// The amount reserved by the `manager` account for the registration.
    deposit: Balance,
    /// Whether the para registration should be locked from being controlled by the manager.
    /// None means the lock had not been explicitly set, and should be treated as false.
    locked: Option<bool>,
}