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::{Currency, EnsureOriginWithArg, ReservableCurrency},
50
        DefaultNoBound, Hashable, LOG_TARGET,
51
    },
52
    frame_system::pallet_prelude::*,
53
    parity_scale_codec::{Decode, Encode},
54
    sp_core::H256,
55
    sp_runtime::{
56
        traits::{AtLeast32BitUnsigned, Verify},
57
        Saturating,
58
    },
59
    sp_std::{collections::btree_set::BTreeSet, prelude::*},
60
    tp_container_chain_genesis_data::ContainerChainGenesisData,
61
    tp_traits::{
62
        GetCurrentContainerChains, GetSessionContainerChains, GetSessionIndex, ParaId,
63
        ParathreadParams as ParathreadParamsTy, RelayStorageRootProvider, SessionContainerChains,
64
        SlotFrequency,
65
    },
66
};
67

            
68
1
#[frame_support::pallet]
69
pub mod pallet {
70
    use super::*;
71

            
72
188
    #[pallet::pallet]
73
    #[pallet::without_storage_info]
74
    pub struct Pallet<T>(_);
75

            
76
    #[pallet::genesis_config]
77
    #[derive(DefaultNoBound)]
78
    pub struct GenesisConfig<T: Config> {
79
        /// Para ids
80
        pub para_ids: Vec<(ParaId, ContainerChainGenesisData<T::MaxLengthTokenSymbol>)>,
81
    }
82

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

            
98
253
            let mut bounded_para_ids = BoundedVec::default();
99

            
100
443
            for (para_id, genesis_data) in para_ids {
101
192
                bounded_para_ids
102
192
                    .try_push(*para_id)
103
192
                    .expect("too many para ids in genesis: bounded vec full");
104
192

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

            
117
251
            <RegisteredParaIds<T>>::put(bounded_para_ids);
118
251
        }
119
    }
120

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

            
127
        /// Origin that is allowed to call register and deregister
128
        type RegistrarOrigin: EnsureOrigin<Self::RuntimeOrigin>;
129

            
130
        /// Origin that is allowed to call mark_valid_for_collating
131
        type MarkValidForCollatingOrigin: EnsureOrigin<Self::RuntimeOrigin>;
132

            
133
        /// Max length of para id list
134
        #[pallet::constant]
135
        type MaxLengthParaIds: Get<u32>;
136

            
137
        /// Max length of encoded genesis data
138
        #[pallet::constant]
139
        type MaxGenesisDataSize: Get<u32>;
140

            
141
        #[pallet::constant]
142
        type MaxLengthTokenSymbol: Get<u32>;
143

            
144
        type RegisterWithRelayProofOrigin: EnsureOrigin<
145
            Self::RuntimeOrigin,
146
            Success = Self::AccountId,
147
        >;
148

            
149
        type RelayStorageRootProvider: RelayStorageRootProvider;
150

            
151
        type SessionIndex: parity_scale_codec::FullCodec + TypeInfo + Copy + AtLeast32BitUnsigned;
152

            
153
        #[pallet::constant]
154
        type SessionDelay: Get<Self::SessionIndex>;
155

            
156
        type CurrentSessionIndex: GetSessionIndex<Self::SessionIndex>;
157

            
158
        type Currency: ReservableCurrency<Self::AccountId>;
159

            
160
        #[pallet::constant]
161
        type DepositAmount: Get<<Self::Currency as Currency<Self::AccountId>>::Balance>;
162

            
163
        type RegistrarHooks: RegistrarHooks;
164

            
165
        type WeightInfo: WeightInfo;
166
    }
167

            
168
55660
    #[pallet::storage]
169
    pub type RegisteredParaIds<T: Config> =
170
        StorageValue<_, BoundedVec<ParaId, T::MaxLengthParaIds>, ValueQuery>;
171

            
172
22070
    #[pallet::storage]
173
    pub type PendingParaIds<T: Config> = StorageValue<
174
        _,
175
        Vec<(T::SessionIndex, BoundedVec<ParaId, T::MaxLengthParaIds>)>,
176
        ValueQuery,
177
    >;
178

            
179
636
    #[pallet::storage]
180
    pub type ParaGenesisData<T: Config> = StorageMap<
181
        _,
182
        Blake2_128Concat,
183
        ParaId,
184
        ContainerChainGenesisData<T::MaxLengthTokenSymbol>,
185
        OptionQuery,
186
    >;
187

            
188
398
    #[pallet::storage]
189
    pub type PendingVerification<T: Config> =
190
        StorageMap<_, Blake2_128Concat, ParaId, (), OptionQuery>;
191

            
192
154
    #[pallet::storage]
193
    pub type Paused<T: Config> =
194
        StorageValue<_, BoundedVec<ParaId, T::MaxLengthParaIds>, ValueQuery>;
195

            
196
4072
    #[pallet::storage]
197
    pub type PendingPaused<T: Config> = StorageValue<
198
        _,
199
        Vec<(T::SessionIndex, BoundedVec<ParaId, T::MaxLengthParaIds>)>,
200
        ValueQuery,
201
    >;
202

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

            
210
16201
    #[pallet::storage]
211
    pub type ParathreadParams<T: Config> =
212
        StorageMap<_, Blake2_128Concat, ParaId, ParathreadParamsTy, OptionQuery>;
213

            
214
4200
    #[pallet::storage]
215
    pub type PendingParathreadParams<T: Config> = StorageValue<
216
        _,
217
        Vec<(
218
            T::SessionIndex,
219
            BoundedVec<(ParaId, ParathreadParamsTy), T::MaxLengthParaIds>,
220
        )>,
221
        ValueQuery,
222
    >;
223

            
224
    pub type DepositBalanceOf<T> =
225
        <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
226

            
227
528
    #[derive(Default, Clone, Encode, Decode, RuntimeDebug, PartialEq, scale_info::TypeInfo)]
228
    #[scale_info(skip_type_params(T))]
229
    pub struct DepositInfo<T: Config> {
230
        pub creator: T::AccountId,
231
        pub deposit: DepositBalanceOf<T>,
232
    }
233

            
234
    /// Registrar deposits, a mapping from paraId to a struct
235
    /// holding the creator (from which the deposit was reserved) and
236
    /// the deposit amount
237
216
    #[pallet::storage]
238
    pub type RegistrarDeposit<T: Config> = StorageMap<_, Blake2_128Concat, ParaId, DepositInfo<T>>;
239

            
240
270
    #[pallet::storage]
241
    pub type ParaManager<T: Config> =
242
        StorageMap<_, Blake2_128Concat, ParaId, T::AccountId, OptionQuery>;
243

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

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

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

            
320
            // All para ids have an entry in `ParaGenesisData`
321
            for para_id in &para_ids {
322
                assert!(
323
                    ParaGenesisData::<T>::contains_key(para_id),
324
                    "Para id {} missing genesis data",
325
                    u32::from(*para_id)
326
                );
327
            }
328

            
329
            // All entries in `RegistrarDeposit` and `ParaGenesisData` are in one of the other lists
330
            let mut para_id_set = BTreeSet::from_iter(para_ids.iter().cloned());
331
            // Also add the Pending lists here
332
            para_id_set.extend(
333
                PendingParaIds::<T>::get()
334
                    .into_iter()
335
                    .flat_map(|(_session_index, x)| x),
336
            );
337
            para_id_set.extend(
338
                PendingPaused::<T>::get()
339
                    .into_iter()
340
                    .flat_map(|(_session_index, x)| x),
341
            );
342
            para_id_set.extend(
343
                PendingToRemove::<T>::get()
344
                    .into_iter()
345
                    .flat_map(|(_session_index, x)| x),
346
            );
347
            let entries: Vec<_> = RegistrarDeposit::<T>::iter().map(|(k, _v)| k).collect();
348
            for para_id in entries {
349
                assert!(
350
                    para_id_set.contains(&para_id),
351
                    "Found RegistrarDeposit for unknown para id: {}",
352
                    u32::from(para_id)
353
                );
354
            }
355
            let entries: Vec<_> = ParaGenesisData::<T>::iter().map(|(k, _v)| k).collect();
356
            for para_id in entries {
357
                assert!(
358
                    para_id_set.contains(&para_id),
359
                    "Found ParaGenesisData for unknown para id: {}",
360
                    u32::from(para_id)
361
                );
362
            }
363

            
364
            // Sorted storage items are sorted
365
            fn assert_is_sorted_and_unique<T: Ord>(x: &[T], name: &str) {
366
                assert!(
367
                    x.windows(2).all(|w| w[0] < w[1]),
368
                    "sorted list not sorted or not unique: {}",
369
                    name,
370
                );
371
            }
372
            assert_is_sorted_and_unique(&RegisteredParaIds::<T>::get(), "RegisteredParaIds");
373
            assert_is_sorted_and_unique(&Paused::<T>::get(), "Paused");
374
            for (i, (_session_index, x)) in PendingParaIds::<T>::get().into_iter().enumerate() {
375
                assert_is_sorted_and_unique(&x, &format!("PendingParaIds[{}]", i));
376
            }
377
            for (i, (_session_index, x)) in PendingPaused::<T>::get().into_iter().enumerate() {
378
                assert_is_sorted_and_unique(&x, &format!("PendingPaused[{}]", i));
379
            }
380
            for (i, (_session_index, x)) in PendingToRemove::<T>::get().into_iter().enumerate() {
381
                assert_is_sorted_and_unique(&x, &format!("PendingToRemove[{}]", i));
382
            }
383

            
384
            // Pending storage items are sorted and session index is unique
385
            let pending: Vec<_> = PendingParaIds::<T>::get()
386
                .into_iter()
387
                .map(|(session_index, _x)| session_index)
388
                .collect();
389
            assert_is_sorted_and_unique(&pending, "PendingParaIds");
390
            let pending: Vec<_> = PendingPaused::<T>::get()
391
                .into_iter()
392
                .map(|(session_index, _x)| session_index)
393
                .collect();
394
            assert_is_sorted_and_unique(&pending, "PendingPaused");
395
            let pending: Vec<_> = PendingToRemove::<T>::get()
396
                .into_iter()
397
                .map(|(session_index, _x)| session_index)
398
                .collect();
399
            assert_is_sorted_and_unique(&pending, "PendingToRemove");
400

            
401
            Ok(())
402
        }
403
    }
404

            
405
323
    #[pallet::call]
406
    impl<T: Config> Pallet<T> {
407
        /// Register container-chain
408
        #[pallet::call_index(0)]
409
        #[pallet::weight(T::WeightInfo::register(genesis_data.encoded_size() as u32, genesis_data.storage.len() as u32))]
410
        pub fn register(
411
            origin: OriginFor<T>,
412
            para_id: ParaId,
413
            genesis_data: ContainerChainGenesisData<T::MaxLengthTokenSymbol>,
414
101
        ) -> DispatchResult {
415
101
            let account = ensure_signed(origin)?;
416
100
            Self::do_register(account, para_id, genesis_data)?;
417
94
            Self::deposit_event(Event::ParaIdRegistered { para_id });
418
94

            
419
94
            Ok(())
420
        }
421

            
422
        /// Deregister container-chain.
423
        ///
424
        /// If a container-chain is registered but not marked as valid_for_collating, this will remove it
425
        /// from `PendingVerification` as well.
426
        #[pallet::call_index(1)]
427
        #[pallet::weight(T::WeightInfo::deregister_immediate(
428
        ).max(T::WeightInfo::deregister_scheduled(
429
        )))]
430
49
        pub fn deregister(origin: OriginFor<T>, para_id: ParaId) -> DispatchResult {
431
49
            T::RegistrarOrigin::ensure_origin(origin)?;
432

            
433
48
            Self::do_deregister(para_id)?;
434

            
435
46
            Ok(())
436
        }
437

            
438
        /// Mark container-chain valid for collating
439
        #[pallet::call_index(2)]
440
        #[pallet::weight(T::WeightInfo::mark_valid_for_collating())]
441
97
        pub fn mark_valid_for_collating(origin: OriginFor<T>, para_id: ParaId) -> DispatchResult {
442
97
            T::MarkValidForCollatingOrigin::ensure_origin(origin)?;
443

            
444
96
            Self::do_mark_valid_for_collating(para_id)?;
445

            
446
91
            Ok(())
447
        }
448

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

            
456
10
            Self::schedule_paused_parachain_change(|para_ids, paused| {
457
10
                match paused.binary_search(&para_id) {
458
1
                    Ok(_) => return Err(Error::<T>::ParaIdAlreadyPaused.into()),
459
9
                    Err(index) => {
460
9
                        paused
461
9
                            .try_insert(index, para_id)
462
9
                            .map_err(|_e| Error::<T>::ParaIdListFull)?;
463
                    }
464
                }
465
9
                match para_ids.binary_search(&para_id) {
466
8
                    Ok(index) => {
467
8
                        para_ids.remove(index);
468
8
                    }
469
                    // We can only pause para ids that are marked as valid,
470
                    // otherwise unpausing them later would cause problems
471
1
                    Err(_) => return Err(Error::<T>::ParaIdNotRegistered.into()),
472
                }
473
8
                Self::deposit_event(Event::ParaIdPaused { para_id });
474
8

            
475
8
                Ok(())
476
10
            })?;
477

            
478
8
            Ok(())
479
        }
480

            
481
        /// Unpause container-chain.
482
        /// Only container-chains that have been paused can be unpaused.
483
        #[pallet::call_index(5)]
484
        #[pallet::weight(T::WeightInfo::unpause_container_chain())]
485
6
        pub fn unpause_container_chain(origin: OriginFor<T>, para_id: ParaId) -> DispatchResult {
486
6
            T::RegistrarOrigin::ensure_origin(origin)?;
487

            
488
6
            Self::schedule_paused_parachain_change(|para_ids, paused| {
489
6
                match paused.binary_search(&para_id) {
490
3
                    Ok(index) => {
491
3
                        paused.remove(index);
492
3
                    }
493
3
                    Err(_) => return Err(Error::<T>::ParaIdNotPaused.into()),
494
                }
495
3
                match para_ids.binary_search(&para_id) {
496
                    // This Ok is unreachable, a para id cannot be in "RegisteredParaIds" and "Paused" at the same time
497
                    Ok(_) => return Err(Error::<T>::ParaIdAlreadyRegistered.into()),
498
3
                    Err(index) => {
499
3
                        para_ids
500
3
                            .try_insert(index, para_id)
501
3
                            .map_err(|_e| Error::<T>::ParaIdListFull)?;
502
                    }
503
                }
504
3
                Self::deposit_event(Event::ParaIdUnpaused { para_id });
505
3

            
506
3
                Ok(())
507
6
            })?;
508

            
509
3
            Ok(())
510
        }
511

            
512
        /// Register parathread
513
        #[pallet::call_index(6)]
514
        #[pallet::weight(T::WeightInfo::register_parathread(genesis_data.encoded_size() as u32, genesis_data.storage.len() as u32))]
515
        pub fn register_parathread(
516
            origin: OriginFor<T>,
517
            para_id: ParaId,
518
            slot_frequency: SlotFrequency,
519
            genesis_data: ContainerChainGenesisData<T::MaxLengthTokenSymbol>,
520
23
        ) -> DispatchResult {
521
23
            let account = ensure_signed(origin)?;
522
23
            Self::do_register(account, para_id, genesis_data)?;
523
            // Insert parathread params
524
23
            let params = ParathreadParamsTy { slot_frequency };
525
23
            ParathreadParams::<T>::insert(para_id, params);
526
23
            Self::deposit_event(Event::ParaIdRegistered { para_id });
527
23

            
528
23
            Ok(())
529
        }
530

            
531
        /// Change parathread params
532
        #[pallet::call_index(7)]
533
        #[pallet::weight(T::WeightInfo::set_parathread_params())]
534
        pub fn set_parathread_params(
535
            origin: OriginFor<T>,
536
            para_id: ParaId,
537
            slot_frequency: SlotFrequency,
538
6
        ) -> DispatchResult {
539
6
            T::RegistrarOrigin::ensure_origin(origin)?;
540

            
541
6
            Self::schedule_parathread_params_change(para_id, |params| {
542
5
                params.slot_frequency = slot_frequency;
543
5

            
544
5
                Self::deposit_event(Event::ParathreadParamsChanged { para_id });
545
5

            
546
5
                Ok(())
547
6
            })?;
548

            
549
5
            Ok(())
550
        }
551

            
552
        #[pallet::call_index(8)]
553
        #[pallet::weight(T::WeightInfo::set_para_manager())]
554
        pub fn set_para_manager(
555
            origin: OriginFor<T>,
556
            para_id: ParaId,
557
            manager_address: T::AccountId,
558
2
        ) -> DispatchResult {
559
2
            let origin = ensure_signed(origin)?;
560

            
561
2
            let creator =
562
2
                RegistrarDeposit::<T>::get(para_id).map(|deposit_info| deposit_info.creator);
563
2

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

            
566
2
            ParaManager::<T>::insert(para_id, manager_address.clone());
567
2

            
568
2
            Self::deposit_event(Event::<T>::ParaManagerChanged {
569
2
                para_id,
570
2
                manager_address,
571
2
            });
572
2

            
573
2
            Ok(())
574
        }
575

            
576
        /// Register parachain or parathread
577
        #[pallet::call_index(9)]
578
        #[pallet::weight(T::WeightInfo::register_with_relay_proof(genesis_data.encoded_size() as u32, genesis_data.storage.len() as u32))]
579
        pub fn register_with_relay_proof(
580
            origin: OriginFor<T>,
581
            para_id: ParaId,
582
            parathread_params: Option<ParathreadParamsTy>,
583
            relay_proof_block_number: u32,
584
            relay_storage_proof: sp_trie::StorageProof,
585
            manager_signature: cumulus_primitives_core::relay_chain::Signature,
586
            genesis_data: ContainerChainGenesisData<T::MaxLengthTokenSymbol>,
587
9
        ) -> DispatchResult {
588
9
            let account = T::RegisterWithRelayProofOrigin::ensure_origin(origin)?;
589
8
            let relay_storage_root =
590
9
                T::RelayStorageRootProvider::get_relay_storage_root(relay_proof_block_number)
591
9
                    .ok_or(Error::<T>::RelayStorageRootNotFound)?;
592
7
            let relay_state_proof =
593
8
                GenericStateProof::<cumulus_primitives_core::relay_chain::Block>::new(
594
8
                    relay_storage_root,
595
8
                    relay_storage_proof,
596
8
                )
597
8
                .map_err(|_| Error::<T>::InvalidRelayStorageProof)?;
598

            
599
7
            let bytes = para_id.twox_64_concat();
600
7
            let key = [REGISTRAR_PARAS_INDEX, bytes.as_slice()].concat();
601
7
            let relay_para_info = relay_state_proof
602
7
                .read_entry::<ParaInfo<
603
7
                    cumulus_primitives_core::relay_chain::AccountId,
604
7
                    cumulus_primitives_core::relay_chain::Balance,
605
7
                >>(key.as_slice(), None)
606
7
                .map_err(|_| Error::<T>::InvalidRelayStorageProof)?;
607
5
            let relay_manager = relay_para_info.manager;
608
5

            
609
5
            // Verify manager signature
610
5
            let signature_msg = Self::relay_signature_msg(para_id, &account, relay_storage_root);
611
5
            if !manager_signature.verify(&*signature_msg, &relay_manager) {
612
2
                return Err(Error::<T>::InvalidRelayManagerSignature.into());
613
3
            }
614
3

            
615
3
            Self::do_register(account, para_id, genesis_data)?;
616
            // Insert parathread params
617
3
            if let Some(parathread_params) = parathread_params {
618
                ParathreadParams::<T>::insert(para_id, parathread_params);
619
3
            }
620
3
            Self::deposit_event(Event::ParaIdRegistered { para_id });
621
3

            
622
3
            Ok(())
623
        }
624

            
625
        /// Deregister a parachain that no longer exists in the relay chain. The origin of this
626
        /// extrinsic will be rewarded with the parachain deposit.
627
        #[pallet::call_index(10)]
628
        #[pallet::weight(T::WeightInfo::deregister_with_relay_proof_immediate(
629
        ).max(T::WeightInfo::deregister_with_relay_proof_scheduled(
630
        )))]
631
        pub fn deregister_with_relay_proof(
632
            origin: OriginFor<T>,
633
            para_id: ParaId,
634
            relay_proof_block_number: u32,
635
            relay_storage_proof: sp_trie::StorageProof,
636
7
        ) -> DispatchResult {
637
7
            let account = T::RegisterWithRelayProofOrigin::ensure_origin(origin)?;
638

            
639
6
            let relay_storage_root =
640
7
                T::RelayStorageRootProvider::get_relay_storage_root(relay_proof_block_number)
641
7
                    .ok_or(Error::<T>::RelayStorageRootNotFound)?;
642
5
            let relay_state_proof =
643
6
                GenericStateProof::<cumulus_primitives_core::relay_chain::Block>::new(
644
6
                    relay_storage_root,
645
6
                    relay_storage_proof,
646
6
                )
647
6
                .map_err(|_| Error::<T>::InvalidRelayStorageProof)?;
648

            
649
5
            let bytes = para_id.twox_64_concat();
650
5
            let key = [REGISTRAR_PARAS_INDEX, bytes.as_slice()].concat();
651
            // TODO: we don't even need to decode the value, only check if it exists
652
            // Need to add exists_storage method to dancekit
653
5
            let relay_para_info = relay_state_proof
654
5
                .read_optional_entry::<ParaInfo<
655
5
                    cumulus_primitives_core::relay_chain::AccountId,
656
5
                    cumulus_primitives_core::relay_chain::Balance,
657
5
                >>(key.as_slice())
658
5
                .map_err(|_| Error::<T>::InvalidRelayStorageProof)?;
659
5
            if relay_para_info.is_some() {
660
1
                return Err(Error::<T>::ParaStillExistsInRelay.into());
661
4
            }
662

            
663
            // Take the deposit immediately and give it to origin account
664
4
            if let Some(asset_info) = RegistrarDeposit::<T>::take(para_id) {
665
4
                // Slash deposit from parachain creator
666
4
                let (slashed_deposit, _not_slashed_deposit) =
667
4
                    T::Currency::slash_reserved(&asset_info.creator, asset_info.deposit);
668
4
                // _not_slashed_deposit can be greater than 0 if the account does not have enough reserved balance
669
4
                // Should never happen but if it does, we just drop it
670
4
                // TODO: is that the same as burning? Or as doing nothing? Either seems fine.
671
4

            
672
4
                // Give deposit to origin account
673
4
                // TODO: error handling
674
4
                let _ = T::Currency::resolve_into_existing(&account, slashed_deposit);
675
4
            }
676

            
677
4
            Self::do_deregister(para_id)?;
678

            
679
4
            Ok(())
680
        }
681
    }
682

            
683
    pub struct SessionChangeOutcome<T: Config> {
684
        /// Previously active parachains.
685
        pub prev_paras: BoundedVec<ParaId, T::MaxLengthParaIds>,
686
        /// If new parachains have been applied in the new session, this is the new  list.
687
        pub new_paras: Option<BoundedVec<ParaId, T::MaxLengthParaIds>>,
688
    }
689

            
690
    impl<T: Config> Pallet<T> {
691
72
        pub fn is_para_manager(para_id: &ParaId, account: &T::AccountId) -> bool {
692
            // This check will only pass if both are true:
693
            // * The para_id has a deposit in pallet_registrar
694
            // * The signed_account is the para manager (or creator if None)
695
72
            if let Some(manager) = ParaManager::<T>::get(para_id) {
696
72
                manager == *account
697
            } else {
698
                RegistrarDeposit::<T>::get(para_id)
699
                    .map(|deposit_info| deposit_info.creator)
700
                    .as_ref()
701
                    == Some(account)
702
            }
703
72
        }
704

            
705
        #[cfg(feature = "runtime-benchmarks")]
706
        pub fn benchmarks_get_or_create_para_manager(para_id: &ParaId) -> T::AccountId {
707
            use {
708
                frame_benchmarking::account,
709
                frame_support::{assert_ok, dispatch::RawOrigin, traits::Currency},
710
            };
711
            // Return container chain manager, or register container chain as ALICE if it does not exist
712
            if !ParaGenesisData::<T>::contains_key(para_id) {
713
                // Register as a new user
714

            
715
                /// Create a funded user.
716
                /// Used for generating the necessary amount for registering
717
                fn create_funded_user<T: Config>(
718
                    string: &'static str,
719
                    n: u32,
720
                    total: DepositBalanceOf<T>,
721
                ) -> (T::AccountId, DepositBalanceOf<T>) {
722
                    const SEED: u32 = 0;
723
                    let user = account(string, n, SEED);
724
                    T::Currency::make_free_balance_be(&user, total);
725
                    let _ = T::Currency::issue(total);
726
                    (user, total)
727
                }
728
                let new_balance =
729
                    (T::Currency::minimum_balance() + T::DepositAmount::get()) * 2u32.into();
730
                let account = create_funded_user::<T>("caller", 1000, new_balance).0;
731
                let origin = RawOrigin::Signed(account);
732
                assert_ok!(Self::register(origin.into(), *para_id, Default::default()));
733
            }
734

            
735
            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");
736

            
737
            // Fund deposit creator, just in case it is not a new account
738
            let new_balance =
739
                (T::Currency::minimum_balance() + T::DepositAmount::get()) * 2u32.into();
740
            T::Currency::make_free_balance_be(&deposit_info.creator, new_balance);
741
            let _ = T::Currency::issue(new_balance);
742

            
743
            deposit_info.creator
744
        }
745

            
746
126
        fn do_register(
747
126
            account: T::AccountId,
748
126
            para_id: ParaId,
749
126
            genesis_data: ContainerChainGenesisData<T::MaxLengthTokenSymbol>,
750
126
        ) -> DispatchResult {
751
126
            let deposit = T::DepositAmount::get();
752
126

            
753
126
            // Verify we can reserve
754
126
            T::Currency::can_reserve(&account, deposit)
755
126
                .then_some(true)
756
126
                .ok_or(Error::<T>::NotSufficientDeposit)?;
757

            
758
            // Check if the para id is already registered by looking at the genesis data
759
126
            if ParaGenesisData::<T>::contains_key(para_id) {
760
5
                return Err(Error::<T>::ParaIdAlreadyRegistered.into());
761
121
            }
762
121

            
763
121
            // Check if the para id is already in PendingVerification (unreachable)
764
121
            let is_pending_verification = PendingVerification::<T>::take(para_id).is_some();
765
121
            if is_pending_verification {
766
                return Err(Error::<T>::ParaIdAlreadyRegistered.into());
767
121
            }
768
121

            
769
121
            // Insert para id into PendingVerification
770
121
            PendingVerification::<T>::insert(para_id, ());
771
121

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

            
786
120
            // Reserve the deposit, we verified we can do this
787
120
            T::Currency::reserve(&account, deposit)?;
788

            
789
            // Update DepositInfo
790
120
            RegistrarDeposit::<T>::insert(
791
120
                para_id,
792
120
                DepositInfo {
793
120
                    creator: account.clone(),
794
120
                    deposit,
795
120
                },
796
120
            );
797
120
            ParaGenesisData::<T>::insert(para_id, genesis_data);
798
120

            
799
120
            ParaManager::<T>::insert(para_id, account);
800
120

            
801
120
            Ok(())
802
126
        }
803

            
804
52
        fn do_deregister(para_id: ParaId) -> DispatchResult {
805
52
            // Check if the para id is in "PendingVerification".
806
52
            // This is a special case because then we can remove it immediately, instead of waiting 2 sessions.
807
52
            let is_pending_verification = PendingVerification::<T>::take(para_id).is_some();
808
52
            if is_pending_verification {
809
5
                Self::deposit_event(Event::ParaIdDeregistered { para_id });
810
5
                // Cleanup immediately
811
5
                Self::cleanup_deregistered_para_id(para_id);
812
5
            } else {
813
47
                Self::schedule_paused_parachain_change(|para_ids, paused| {
814
47
                    // We have to find out where, in the sorted vec the para id is, if anywhere.
815
47

            
816
47
                    match para_ids.binary_search(&para_id) {
817
42
                        Ok(index) => {
818
42
                            para_ids.remove(index);
819
42
                        }
820
                        Err(_) => {
821
                            // If the para id is not registered, it may be paused. In that case, remove it from there
822
5
                            match paused.binary_search(&para_id) {
823
3
                                Ok(index) => {
824
3
                                    paused.remove(index);
825
3
                                }
826
                                Err(_) => {
827
2
                                    return Err(Error::<T>::ParaIdNotRegistered.into());
828
                                }
829
                            }
830
                        }
831
                    }
832

            
833
45
                    Ok(())
834
47
                })?;
835
                // Mark this para id for cleanup later
836
45
                Self::schedule_parachain_cleanup(para_id)?;
837
45
                Self::deposit_event(Event::ParaIdDeregistered { para_id });
838
            }
839

            
840
50
            Ok(())
841
52
        }
842

            
843
96
        fn do_mark_valid_for_collating(para_id: ParaId) -> DispatchResult {
844
96
            let is_pending_verification = PendingVerification::<T>::take(para_id).is_some();
845
96
            if !is_pending_verification {
846
3
                return Err(Error::<T>::ParaIdNotInPendingVerification.into());
847
93
            }
848
93

            
849
93
            Self::schedule_parachain_change(|para_ids| {
850
93
                // We don't want to add duplicate para ids, so we check whether the potential new
851
93
                // para id is already present in the list. Because the list is always ordered, we can
852
93
                // leverage the binary search which makes this check O(log n).
853
93

            
854
93
                match para_ids.binary_search(&para_id) {
855
                    // This Ok is unreachable
856
                    Ok(_) => return Err(Error::<T>::ParaIdAlreadyRegistered.into()),
857
93
                    Err(index) => {
858
93
                        para_ids
859
93
                            .try_insert(index, para_id)
860
93
                            .map_err(|_e| Error::<T>::ParaIdListFull)?;
861
                    }
862
                }
863

            
864
93
                Ok(())
865
93
            })?;
866

            
867
93
            T::RegistrarHooks::check_valid_for_collating(para_id)?;
868

            
869
91
            Self::deposit_event(Event::ParaIdValidForCollating { para_id });
870
91

            
871
91
            T::RegistrarHooks::para_marked_valid_for_collating(para_id);
872
91

            
873
91
            Ok(())
874
96
        }
875

            
876
        /// Relay parachain manager signature message. Includes:
877
        /// * para_id, in case the manager has more than 1 para in the relay
878
        /// * accountid in tanssi, to ensure that the creator role is assigned to the desired account
879
        /// * relay_storage_root, to make the signature network-specific, and also make it expire
880
        ///     when the relay storage root expires.
881
12
        pub fn relay_signature_msg(
882
12
            para_id: ParaId,
883
12
            tanssi_account: &T::AccountId,
884
12
            relay_storage_root: H256,
885
12
        ) -> Vec<u8> {
886
12
            (para_id, tanssi_account, relay_storage_root).encode()
887
12
        }
888

            
889
93
        fn schedule_parachain_change(
890
93
            updater: impl FnOnce(&mut BoundedVec<ParaId, T::MaxLengthParaIds>) -> DispatchResult,
891
93
        ) -> DispatchResult {
892
93
            let mut pending_paras = PendingParaIds::<T>::get();
893
93
            // First, we need to decide what we should use as the base paras.
894
93
            let mut base_paras = pending_paras
895
93
                .last()
896
93
                .map(|(_, paras)| paras.clone())
897
93
                .unwrap_or_else(Self::registered_para_ids);
898
93

            
899
93
            updater(&mut base_paras)?;
900
93
            let new_paras = base_paras;
901
93

            
902
93
            let scheduled_session = Self::scheduled_session();
903

            
904
93
            if let Some(&mut (_, ref mut paras)) = pending_paras
905
93
                .iter_mut()
906
93
                .find(|&&mut (apply_at_session, _)| apply_at_session >= scheduled_session)
907
6
            {
908
6
                *paras = new_paras;
909
87
            } else {
910
87
                // We are scheduling a new parachains change for the scheduled session.
911
87
                pending_paras.push((scheduled_session, new_paras));
912
87
            }
913

            
914
93
            <PendingParaIds<T>>::put(pending_paras);
915
93

            
916
93
            Ok(())
917
93
        }
918

            
919
63
        fn schedule_paused_parachain_change(
920
63
            updater: impl FnOnce(
921
63
                &mut BoundedVec<ParaId, T::MaxLengthParaIds>,
922
63
                &mut BoundedVec<ParaId, T::MaxLengthParaIds>,
923
63
            ) -> DispatchResult,
924
63
        ) -> DispatchResult {
925
63
            let mut pending_paras = PendingParaIds::<T>::get();
926
63
            let mut pending_paused = PendingPaused::<T>::get();
927
63
            // First, we need to decide what we should use as the base paras.
928
63
            let mut base_paras = pending_paras
929
63
                .last()
930
63
                .map(|(_, paras)| paras.clone())
931
63
                .unwrap_or_else(Self::registered_para_ids);
932
63
            let mut base_paused = pending_paused
933
63
                .last()
934
63
                .map(|(_, paras)| paras.clone())
935
63
                .unwrap_or_else(Self::paused);
936
63
            let old_base_paras = base_paras.clone();
937
63
            let old_base_paused = base_paused.clone();
938
63

            
939
63
            updater(&mut base_paras, &mut base_paused)?;
940

            
941
56
            if base_paras != old_base_paras {
942
53
                let new_paras = base_paras;
943
53
                let scheduled_session = Self::scheduled_session();
944

            
945
53
                if let Some(&mut (_, ref mut paras)) = pending_paras
946
53
                    .iter_mut()
947
53
                    .find(|&&mut (apply_at_session, _)| apply_at_session >= scheduled_session)
948
17
                {
949
17
                    *paras = new_paras;
950
40
                } else {
951
36
                    // We are scheduling a new parachains change for the scheduled session.
952
36
                    pending_paras.push((scheduled_session, new_paras));
953
36
                }
954

            
955
53
                <PendingParaIds<T>>::put(pending_paras);
956
3
            }
957

            
958
56
            if base_paused != old_base_paused {
959
14
                let new_paused = base_paused;
960
14
                let scheduled_session = Self::scheduled_session();
961

            
962
14
                if let Some(&mut (_, ref mut paras)) = pending_paused
963
14
                    .iter_mut()
964
14
                    .find(|&&mut (apply_at_session, _)| apply_at_session >= scheduled_session)
965
2
                {
966
2
                    *paras = new_paused;
967
12
                } else {
968
12
                    // We are scheduling a new parachains change for the scheduled session.
969
12
                    pending_paused.push((scheduled_session, new_paused));
970
12
                }
971

            
972
14
                <PendingPaused<T>>::put(pending_paused);
973
42
            }
974

            
975
56
            Ok(())
976
63
        }
977

            
978
6
        fn schedule_parathread_params_change(
979
6
            para_id: ParaId,
980
6
            updater: impl FnOnce(&mut ParathreadParamsTy) -> DispatchResult,
981
6
        ) -> DispatchResult {
982
            // Check that the para id is a parathread by reading the old params
983
6
            let params = match ParathreadParams::<T>::get(para_id) {
984
5
                Some(x) => x,
985
                None => {
986
1
                    return Err(Error::<T>::NotAParathread.into());
987
                }
988
            };
989

            
990
5
            let mut pending_params = PendingParathreadParams::<T>::get();
991
5
            // First, we need to decide what we should use as the base params.
992
5
            let mut base_params = pending_params
993
5
                .last()
994
5
                .and_then(|(_, para_id_params)| {
995
                    match para_id_params
996
                        .binary_search_by_key(&para_id, |(para_id, _params)| *para_id)
997
                    {
998
                        Ok(idx) => {
999
                            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.
210
        fn scheduled_session() -> T::SessionIndex {
210
            T::CurrentSessionIndex::session_index().saturating_add(T::SessionDelay::get())
210
        }
        /// 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.
1949
        pub fn initializer_on_new_session(
1949
            session_index: &T::SessionIndex,
1949
        ) -> SessionChangeOutcome<T> {
1949
            let pending_paras = <PendingParaIds<T>>::get();
1949
            let prev_paras = RegisteredParaIds::<T>::get();
1949
            let new_paras = if !pending_paras.is_empty() {
336
                let (mut past_and_present, future) = pending_paras
336
                    .into_iter()
342
                    .partition::<Vec<_>, _>(|&(apply_at_session, _)| {
342
                        apply_at_session <= *session_index
342
                    });
336

            
336
                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",
                    );
336
                }
336
                let new_paras = past_and_present.pop().map(|(_, paras)| paras);
336
                if let Some(ref new_paras) = new_paras {
171
                    // Apply the new parachain list.
171
                    RegisteredParaIds::<T>::put(new_paras);
171
                    <PendingParaIds<T>>::put(future);
171
                }
336
                new_paras
            } else {
                // pending_paras.is_empty, so parachain list did not change
1613
                None
            };
1949
            let pending_paused = <PendingPaused<T>>::get();
1949
            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
                }
1930
            }
1949
            let pending_parathread_params = <PendingParathreadParams<T>>::get();
1949
            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
                }
1939
            }
1949
            let pending_to_remove = <PendingToRemove<T>>::get();
1949
            if !pending_to_remove.is_empty() {
135
                let (past_and_present, future) =
135
                    pending_to_remove.into_iter().partition::<Vec<_>, _>(
136
                        |&(apply_at_session, _)| apply_at_session <= *session_index,
135
                    );
135

            
135
                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.
68
                    let mut removed_para_ids = BTreeSet::new();
136
                    for (_, new_paras) in &past_and_present {
139
                        for para_id in new_paras {
71
                            Self::cleanup_deregistered_para_id(*para_id);
71
                            removed_para_ids.insert(*para_id);
71
                        }
                    }
                    // Also need to remove PendingParams to avoid setting params for a para id that does not exist
68
                    let mut pending_parathread_params = <PendingParathreadParams<T>>::get();
69
                    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
                    }
68
                    <PendingParathreadParams<T>>::put(pending_parathread_params);
68
                    <PendingToRemove<T>>::put(future);
67
                }
1814
            }
1949
            SessionChangeOutcome {
1949
                prev_paras,
1949
                new_paras,
1949
            }
1949
        }
        /// Remove all para id storage in this pallet,
        /// and execute para_deregistered hook to clean up other pallets as well
76
        fn cleanup_deregistered_para_id(para_id: ParaId) {
76
            ParaGenesisData::<T>::remove(para_id);
76
            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
76
            if let Some(asset_info) = RegistrarDeposit::<T>::take(para_id) {
24
                // Unreserve deposit
24
                T::Currency::unreserve(&asset_info.creator, asset_info.deposit);
71
            }
76
            ParaManager::<T>::remove(para_id);
76

            
76
            T::RegistrarHooks::para_deregistered(para_id);
76
        }
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
        }
25457
        pub fn registered_para_ids() -> BoundedVec<ParaId, T::MaxLengthParaIds> {
25457
            RegisteredParaIds::<T>::get()
25457
        }
8611
        pub fn pending_registered_para_ids(
8611
        ) -> Vec<(T::SessionIndex, BoundedVec<ParaId, T::MaxLengthParaIds>)> {
8611
            PendingParaIds::<T>::get()
8611
        }
124
        pub fn para_genesis_data(
124
            para_id: ParaId,
124
        ) -> Option<ContainerChainGenesisData<T::MaxLengthTokenSymbol>> {
124
            ParaGenesisData::<T>::get(para_id)
124
        }
        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()
        }
23
        pub fn parathread_params(para_id: ParaId) -> Option<ParathreadParamsTy> {
23
            ParathreadParams::<T>::get(para_id)
23
        }
        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;
16842
        fn current_container_chains() -> BoundedVec<ParaId, Self::MaxContainerChains> {
16842
            Self::registered_para_ids()
16842
        }
        #[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> {
8581
        fn session_container_chains(session_index: T::SessionIndex) -> SessionContainerChains {
8581
            let (past_and_present, _) = Pallet::<T>::pending_registered_para_ids()
8581
                .into_iter()
8581
                .partition::<Vec<_>, _>(|&(apply_at_session, _)| apply_at_session <= session_index);
8581
            let paras = if let Some(last) = past_and_present.last() {
148
                last.1.clone()
            } else {
8433
                Pallet::<T>::registered_para_ids()
            };
8581
            let mut parachains = vec![];
8581
            let mut parathreads = vec![];
24650
            for para_id in paras {
                // TODO: sweet O(n) db reads
16069
                if let Some(parathread_params) = ParathreadParams::<T>::get(para_id) {
288
                    parathreads.push((para_id, parathread_params));
15781
                } else {
15781
                    parachains.push(para_id);
15781
                }
            }
8581
            SessionContainerChains {
8581
                parachains,
8581
                parathreads,
8581
            }
8581
        }
        #[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;
91
    fn try_origin(
91
        o: T::RuntimeOrigin,
91
        para_id: &ParaId,
91
    ) -> Result<Self::Success, T::RuntimeOrigin> {
72
        let signed_account =
91
            <frame_system::EnsureSigned<_> as EnsureOrigin<_>>::try_origin(o.clone())?;
72
        if !Pallet::<T>::is_para_manager(para_id, &signed_account) {
2
            return Err(frame_system::RawOrigin::Signed(signed_account).into());
70
        }
70

            
70
        Ok(signed_account)
91
    }
    #[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>,
}