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
#[frame_support::pallet]
69
pub mod pallet {
70
    use super::*;
71

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

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

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

            
105
184
                let genesis_data_size = genesis_data.encoded_size();
106
184
                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
182
                }
114
182
                <ParaGenesisData<T>>::insert(para_id, genesis_data);
115
            }
116

            
117
256
            <RegisteredParaIds<T>>::put(bounded_para_ids);
118
256
        }
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
81270
    #[pallet::storage]
169
    #[pallet::getter(fn registered_para_ids)]
170
    pub type RegisteredParaIds<T: Config> =
171
        StorageValue<_, BoundedVec<ParaId, T::MaxLengthParaIds>, ValueQuery>;
172

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

            
181
643
    #[pallet::storage]
182
    #[pallet::getter(fn para_genesis_data)]
183
    pub type ParaGenesisData<T: Config> = StorageMap<
184
        _,
185
        Blake2_128Concat,
186
        ParaId,
187
        ContainerChainGenesisData<T::MaxLengthTokenSymbol>,
188
        OptionQuery,
189
    >;
190

            
191
411
    #[pallet::storage]
192
    #[pallet::getter(fn pending_verification)]
193
    pub type PendingVerification<T: Config> =
194
        StorageMap<_, Blake2_128Concat, ParaId, (), OptionQuery>;
195

            
196
210
    #[pallet::storage]
197
    #[pallet::getter(fn paused)]
198
    pub type Paused<T: Config> =
199
        StorageValue<_, BoundedVec<ParaId, T::MaxLengthParaIds>, ValueQuery>;
200

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

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

            
217
16194
    #[pallet::storage]
218
    #[pallet::getter(fn parathread_params)]
219
    pub type ParathreadParams<T: Config> =
220
        StorageMap<_, Blake2_128Concat, ParaId, ParathreadParamsTy, OptionQuery>;
221

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

            
233
    pub type DepositBalanceOf<T> =
234
        <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
235

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

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

            
250
260
    #[pallet::storage]
251
    pub type ParaManager<T: Config> =
252
        StorageMap<_, Blake2_128Concat, ParaId, T::AccountId, OptionQuery>;
253

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

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

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

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

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

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

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

            
411
            Ok(())
412
        }
413
    }
414

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

            
429
93
            Ok(())
430
        }
431

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

            
443
45
            Self::do_deregister(para_id)?;
444

            
445
43
            Ok(())
446
        }
447

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

            
454
102
            Self::do_mark_valid_for_collating(para_id)?;
455

            
456
97
            Ok(())
457
        }
458

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

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

            
485
8
                Ok(())
486
10
            })?;
487

            
488
8
            Ok(())
489
        }
490

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

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

            
516
3
                Ok(())
517
6
            })?;
518

            
519
3
            Ok(())
520
        }
521

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

            
538
29
            Ok(())
539
        }
540

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

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

            
554
5
                Self::deposit_event(Event::ParathreadParamsChanged { para_id });
555
5

            
556
5
                Ok(())
557
6
            })?;
558

            
559
5
            Ok(())
560
        }
561

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

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

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

            
576
2
            ParaManager::<T>::insert(para_id, manager_address.clone());
577
2

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

            
583
2
            Ok(())
584
        }
585

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

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

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

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

            
632
3
            Ok(())
633
        }
634

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

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

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

            
673
            // Take the deposit immediately and give it to origin account
674
4
            if let Some(asset_info) = RegistrarDeposit::<T>::take(para_id) {
675
4
                // Slash deposit from parachain creator
676
4
                let (slashed_deposit, _not_slashed_deposit) =
677
4
                    T::Currency::slash_reserved(&asset_info.creator, asset_info.deposit);
678
4
                // _not_slashed_deposit can be greater than 0 if the account does not have enough reserved balance
679
4
                // Should never happen but if it does, we just drop it
680
4
                // TODO: is that the same as burning? Or as doing nothing? Either seems fine.
681
4

            
682
4
                // Give deposit to origin account
683
4
                // TODO: error handling
684
4
                let _ = T::Currency::resolve_into_existing(&account, slashed_deposit);
685
4
            }
686

            
687
4
            Self::do_deregister(para_id)?;
688

            
689
4
            Ok(())
690
        }
691
    }
692

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

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

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

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

            
745
            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");
746

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

            
753
            deposit_info.creator
754
        }
755

            
756
131
        fn do_register(
757
131
            account: T::AccountId,
758
131
            para_id: ParaId,
759
131
            genesis_data: ContainerChainGenesisData<T::MaxLengthTokenSymbol>,
760
131
        ) -> DispatchResult {
761
131
            let deposit = T::DepositAmount::get();
762
131

            
763
131
            // Verify we can reserve
764
131
            T::Currency::can_reserve(&account, deposit)
765
131
                .then_some(true)
766
131
                .ok_or(Error::<T>::NotSufficientDeposit)?;
767

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

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

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

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

            
796
125
            // Reserve the deposit, we verified we can do this
797
125
            T::Currency::reserve(&account, deposit)?;
798

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

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

            
811
125
            Ok(())
812
131
        }
813

            
814
49
        fn do_deregister(para_id: ParaId) -> DispatchResult {
815
49
            // Check if the para id is in "PendingVerification".
816
49
            // This is a special case because then we can remove it immediately, instead of waiting 2 sessions.
817
49
            let is_pending_verification = PendingVerification::<T>::take(para_id).is_some();
818
49
            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
44
                Self::schedule_paused_parachain_change(|para_ids, paused| {
824
44
                    // We have to find out where, in the sorted vec the para id is, if anywhere.
825
44

            
826
44
                    match para_ids.binary_search(&para_id) {
827
39
                        Ok(index) => {
828
39
                            para_ids.remove(index);
829
39
                        }
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
42
                    Ok(())
844
44
                })?;
845
                // Mark this para id for cleanup later
846
42
                Self::schedule_parachain_cleanup(para_id)?;
847
42
                Self::deposit_event(Event::ParaIdDeregistered { para_id });
848
            }
849

            
850
47
            Ok(())
851
49
        }
852

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

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

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

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

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

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

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

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

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

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

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

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

            
926
99
            Ok(())
927
99
        }
928

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

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

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

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

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

            
968
53
            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
39
            }
984

            
985
53
            Ok(())
986
60
        }
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.
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.
1884
        pub fn initializer_on_new_session(
1884
            session_index: &T::SessionIndex,
1884
        ) -> SessionChangeOutcome<T> {
1884
            let pending_paras = <PendingParaIds<T>>::get();
1884
            let prev_paras = RegisteredParaIds::<T>::get();
1884
            let new_paras = if !pending_paras.is_empty() {
318
                let (mut past_and_present, future) = pending_paras
318
                    .into_iter()
324
                    .partition::<Vec<_>, _>(|&(apply_at_session, _)| {
324
                        apply_at_session <= *session_index
324
                    });
318

            
318
                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",
                    );
318
                }
318
                let new_paras = past_and_present.pop().map(|(_, paras)| paras);
318
                if let Some(ref new_paras) = new_paras {
162
                    // Apply the new parachain list.
162
                    RegisteredParaIds::<T>::put(new_paras);
162
                    <PendingParaIds<T>>::put(future);
162
                }
318
                new_paras
            } else {
                // pending_paras.is_empty, so parachain list did not change
1566
                None
            };
1884
            let pending_paused = <PendingPaused<T>>::get();
1884
            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
                }
1865
            }
1884
            let pending_parathread_params = <PendingParathreadParams<T>>::get();
1884
            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
                }
1874
            }
1884
            let pending_to_remove = <PendingToRemove<T>>::get();
1884
            if !pending_to_remove.is_empty() {
93
                let (past_and_present, future) =
93
                    pending_to_remove.into_iter().partition::<Vec<_>, _>(
94
                        |&(apply_at_session, _)| apply_at_session <= *session_index,
93
                    );
93

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

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

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

            
42
            Ok(())
42
        }
    }
    impl<T: Config> GetCurrentContainerChains for Pallet<T> {
        type MaxContainerChains = T::MaxLengthParaIds;
16943
        fn current_container_chains() -> BoundedVec<ParaId, Self::MaxContainerChains> {
16943
            Self::registered_para_ids()
16943
        }
        #[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> {
8594
        fn session_container_chains(session_index: T::SessionIndex) -> SessionContainerChains {
8594
            let (past_and_present, _) = Pallet::<T>::pending_registered_para_ids()
8594
                .into_iter()
8594
                .partition::<Vec<_>, _>(|&(apply_at_session, _)| apply_at_session <= session_index);
8594
            let paras = if let Some(last) = past_and_present.last() {
160
                last.1.clone()
            } else {
8434
                Pallet::<T>::registered_para_ids()
            };
8594
            let mut parachains = vec![];
8594
            let mut parathreads = vec![];
24624
            for para_id in paras {
                // TODO: sweet O(n) db reads
16030
                if let Some(parathread_params) = ParathreadParams::<T>::get(para_id) {
312
                    parathreads.push((para_id, parathread_params));
15718
                } else {
15718
                    parachains.push(para_id);
15718
                }
            }
8594
            SessionContainerChains {
8594
                parachains,
8594
                parathreads,
8594
            }
8594
        }
        #[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>,
}