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

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

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

            
80
    #[pallet::genesis_config]
81
    #[derive(DefaultNoBound)]
82
    pub struct GenesisConfig<T: Config> {
83
        /// Para ids
84
        pub para_ids: Vec<(
85
            ParaId,
86
            ContainerChainGenesisData,
87
            Option<ParathreadParamsTy>,
88
        )>,
89
        #[serde(skip)]
90
        pub phantom: PhantomData<T>,
91
    }
92

            
93
357
    #[pallet::genesis_build]
94
    impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
95
365
        fn build(&self) {
96
365
            // Sort para ids and detect duplicates, but do it using a vector of
97
365
            // references to avoid cloning the genesis data, which may be big.
98
365
            let mut para_ids: Vec<&_> = self.para_ids.iter().collect();
99
374
            para_ids.sort_by(|a, b| a.0.cmp(&b.0));
100
367
            para_ids.dedup_by(|a, b| {
101
152
                if a.0 == b.0 {
102
1
                    panic!("Duplicate para_id: {}", u32::from(a.0));
103
                } else {
104
151
                    false
105
151
                }
106
366
            });
107
365

            
108
365
            let mut bounded_para_ids = BoundedVec::default();
109

            
110
639
            for (para_id, genesis_data, parathread_params) in para_ids {
111
276
                bounded_para_ids
112
276
                    .try_push(*para_id)
113
276
                    .expect("too many para ids in genesis: bounded vec full");
114
276

            
115
276
                let genesis_data_size = genesis_data.encoded_size();
116
276
                if genesis_data_size > T::MaxGenesisDataSize::get() as usize {
117
1
                    panic!(
118
1
                        "genesis data for para_id {:?} is too large: {} bytes (limit is {})",
119
1
                        u32::from(*para_id),
120
1
                        genesis_data_size,
121
1
                        T::MaxGenesisDataSize::get()
122
1
                    );
123
275
                }
124
275
                <ParaGenesisData<T>>::insert(para_id, genesis_data);
125

            
126
275
                if let Some(parathread_params) = parathread_params {
127
20
                    <ParathreadParams<T>>::insert(para_id, parathread_params);
128
254
                }
129
            }
130

            
131
363
            <RegisteredParaIds<T>>::put(bounded_para_ids);
132
363
        }
133
    }
134

            
135
    /// Configure the pallet by specifying the parameters and types on which it depends.
136
    #[pallet::config]
137
    pub trait Config: frame_system::Config {
138
        /// Because this pallet emits events, it depends on the runtime's definition of an event.
139
        type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
140

            
141
        /// Origin that is allowed to call register and deregister
142
        type RegistrarOrigin: EnsureOrigin<Self::RuntimeOrigin>;
143

            
144
        /// Origin that is allowed to call mark_valid_for_collating
145
        type MarkValidForCollatingOrigin: EnsureOrigin<Self::RuntimeOrigin>;
146

            
147
        /// Max length of para id list
148
        #[pallet::constant]
149
        type MaxLengthParaIds: Get<u32>;
150

            
151
        /// Max length of encoded genesis data
152
        #[pallet::constant]
153
        type MaxGenesisDataSize: Get<u32>;
154

            
155
        type RegisterWithRelayProofOrigin: EnsureOrigin<
156
            Self::RuntimeOrigin,
157
            Success = Self::AccountId,
158
        >;
159

            
160
        type RelayStorageRootProvider: RelayStorageRootProvider;
161

            
162
        type SessionIndex: parity_scale_codec::FullCodec + TypeInfo + Copy + AtLeast32BitUnsigned;
163

            
164
        #[pallet::constant]
165
        type SessionDelay: Get<Self::SessionIndex>;
166

            
167
        type CurrentSessionIndex: GetSessionIndex<Self::SessionIndex>;
168

            
169
        type Currency: Mutate<Self::AccountId>
170
            + MutateHold<Self::AccountId, Reason = Self::RuntimeHoldReason>;
171

            
172
        type RuntimeHoldReason: From<HoldReason>;
173

            
174
        #[pallet::constant]
175
        type DepositAmount: Get<<Self::Currency as Inspect<Self::AccountId>>::Balance>;
176

            
177
        type RegistrarHooks: RegistrarHooks;
178

            
179
        /// External manager that takes care of executing specific operations
180
        /// when register-like functions of this pallet are called.
181
        ///
182
        /// Mostly used when we are in a relay-chain configuration context (Dancelight)
183
        /// to also register, deregister and upgrading paraIds in polkadot's
184
        /// paras_registrar pallet.
185
        type InnerRegistrar: RegistrarHandler<Self::AccountId>;
186

            
187
        type WeightInfo: WeightInfo;
188
    }
189

            
190
151660
    #[pallet::storage]
191
    pub type RegisteredParaIds<T: Config> =
192
        StorageValue<_, BoundedVec<ParaId, T::MaxLengthParaIds>, ValueQuery>;
193

            
194
55410
    #[pallet::storage]
195
    #[pallet::unbounded]
196
    pub type PendingParaIds<T: Config> = StorageValue<
197
        _,
198
        Vec<(T::SessionIndex, BoundedVec<ParaId, T::MaxLengthParaIds>)>,
199
        ValueQuery,
200
    >;
201

            
202
865
    #[pallet::storage]
203
    // TODO: this is not unbounded because we check the encoded size in register
204
    #[pallet::unbounded]
205
    pub type ParaGenesisData<T: Config> =
206
        StorageMap<_, Blake2_128Concat, ParaId, ContainerChainGenesisData, OptionQuery>;
207

            
208
666
    #[pallet::storage]
209
    pub type PendingVerification<T: Config> =
210
        StorageMap<_, Blake2_128Concat, ParaId, (), OptionQuery>;
211

            
212
250
    #[pallet::storage]
213
    pub type Paused<T: Config> =
214
        StorageValue<_, BoundedVec<ParaId, T::MaxLengthParaIds>, ValueQuery>;
215

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

            
224
6566
    #[pallet::storage]
225
    #[pallet::unbounded]
226
    pub type PendingToRemove<T: Config> = StorageValue<
227
        _,
228
        Vec<(T::SessionIndex, BoundedVec<ParaId, T::MaxLengthParaIds>)>,
229
        ValueQuery,
230
    >;
231

            
232
46760
    #[pallet::storage]
233
    pub type ParathreadParams<T: Config> =
234
        StorageMap<_, Blake2_128Concat, ParaId, ParathreadParamsTy, OptionQuery>;
235

            
236
6446
    #[pallet::storage]
237
    #[pallet::unbounded]
238
    pub type PendingParathreadParams<T: Config> = StorageValue<
239
        _,
240
        Vec<(
241
            T::SessionIndex,
242
            BoundedVec<(ParaId, ParathreadParamsTy), T::MaxLengthParaIds>,
243
        )>,
244
        ValueQuery,
245
    >;
246

            
247
    /// This storage aims to act as a 'buffer' for paraIds that must be deregistered at the
248
    /// end of the block execution by calling 'T::InnerRegistrar::deregister()' implementation.
249
    ///
250
    /// We need this buffer because when we are using this pallet on a relay-chain environment
251
    /// like Dancelight (where 'T::InnerRegistrar' implementation is usually the
252
    /// 'paras_registrar' pallet) we need to deregister (via 'paras_registrar::deregister')
253
    /// the same paraIds we have in 'PendingToRemove<T>', and we need to do this deregistration
254
    /// process inside 'on_finalize' hook.
255
    ///
256
    /// It can be the case that some paraIds need to be downgraded to a parathread before
257
    /// deregistering on 'paras_registrar'. This process usually takes 2 sessions,
258
    /// and the actual downgrade happens when the block finalizes.
259
    ///
260
    /// Therefore, if we tried to perform this relay deregistration process at the beginning
261
    /// of the session/block inside ('on_initialize') initializer_on_new_session() as we do
262
    /// for this pallet, it would fail due to the downgrade process could have not taken
263
    /// place yet.  
264
48506
    #[pallet::storage]
265
    pub type BufferedParasToDeregister<T: Config> =
266
        StorageValue<_, BoundedVec<ParaId, T::MaxLengthParaIds>, ValueQuery>;
267

            
268
    pub type DepositBalanceOf<T> =
269
        <<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
270

            
271
    #[derive(
272
1164
        Default, Clone, Encode, Decode, RuntimeDebug, PartialEq, scale_info::TypeInfo, MaxEncodedLen,
273
    )]
274
    #[scale_info(skip_type_params(T))]
275
    pub struct DepositInfo<T: Config> {
276
        pub creator: T::AccountId,
277
        pub deposit: DepositBalanceOf<T>,
278
    }
279

            
280
    /// Registrar deposits, a mapping from paraId to a struct
281
    /// holding the creator (from which the deposit was reserved) and
282
    /// the deposit amount
283
328
    #[pallet::storage]
284
    pub type RegistrarDeposit<T: Config> = StorageMap<_, Blake2_128Concat, ParaId, DepositInfo<T>>;
285

            
286
457
    #[pallet::storage]
287
    pub type ParaManager<T: Config> =
288
        StorageMap<_, Blake2_128Concat, ParaId, T::AccountId, OptionQuery>;
289

            
290
    #[pallet::event]
291
473
    #[pallet::generate_deposit(pub(super) fn deposit_event)]
292
    pub enum Event<T: Config> {
293
10
        /// A new para id has been registered. [para_id]
294
        ParaIdRegistered { para_id: ParaId },
295
15
        /// A para id has been deregistered. [para_id]
296
        ParaIdDeregistered { para_id: ParaId },
297
4
        /// A new para id is now valid for collating. [para_id]
298
        ParaIdValidForCollating { para_id: ParaId },
299
2
        /// A para id has been paused from collating.
300
        ParaIdPaused { para_id: ParaId },
301
        /// A para id has been unpaused.
302
        ParaIdUnpaused { para_id: ParaId },
303
        /// Parathread params changed
304
        ParathreadParamsChanged { para_id: ParaId },
305
        /// Para manager has changed
306
        ParaManagerChanged {
307
            para_id: ParaId,
308
            manager_address: T::AccountId,
309
        },
310
    }
311

            
312
112
    #[pallet::error]
313
    pub enum Error<T> {
314
        /// Attempted to register a ParaId that was already registered
315
        ParaIdAlreadyRegistered,
316
        /// Attempted to deregister a ParaId that is not registered
317
        ParaIdNotRegistered,
318
        /// Attempted to deregister a ParaId that is already being deregistered
319
        ParaIdAlreadyDeregistered,
320
        /// Attempted to pause a ParaId that was already paused
321
        ParaIdAlreadyPaused,
322
        /// Attempted to unpause a ParaId that was not paused
323
        ParaIdNotPaused,
324
        /// The bounded list of ParaIds has reached its limit
325
        ParaIdListFull,
326
        /// Attempted to register a ParaId with a genesis data size greater than the limit
327
        GenesisDataTooBig,
328
        /// Tried to mark_valid_for_collating a ParaId that is not in PendingVerification
329
        ParaIdNotInPendingVerification,
330
        /// Tried to register a ParaId with an account that did not have enough balance for the deposit
331
        NotSufficientDeposit,
332
        /// Tried to change parathread params for a para id that is not a registered parathread
333
        NotAParathread,
334
        /// Attempted to execute an extrinsic meant only for the para creator
335
        NotParaCreator,
336
        /// The relay storage root for the corresponding block number could not be retrieved
337
        RelayStorageRootNotFound,
338
        /// The provided relay storage proof is not valid
339
        InvalidRelayStorageProof,
340
        /// The provided signature from the parachain manager in the relay is not valid
341
        InvalidRelayManagerSignature,
342
        /// Tried to deregister a parachain that was not deregistered from the relay chain
343
        ParaStillExistsInRelay,
344
        /// Tried to register a paraId in a relay context without specifying a proper HeadData.
345
        HeadDataNecessary,
346
        /// Tried to register a paraId in a relay context without specifying a wasm chain code.
347
        WasmCodeNecessary,
348
    }
349

            
350
    #[pallet::composite_enum]
351
    pub enum HoldReason {
352
246
        RegistrarDeposit,
353
    }
354

            
355
47946
    #[pallet::hooks]
356
    impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
357
24075
        fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
358
24075
            let mut weight = Weight::zero().saturating_add(T::DbWeight::get().reads_writes(1, 1));
359
24075

            
360
24075
            let buffered_paras = BufferedParasToDeregister::<T>::take();
361

            
362
24119
            for para_id in buffered_paras {
363
44
                weight += T::InnerRegistrar::deregister_weight();
364
44
                // Deregister (in the relay context) each paraId present inside the buffer
365
44
                T::InnerRegistrar::deregister(para_id);
366
44
            }
367
24075
            weight
368
24075
        }
369

            
370
        #[cfg(feature = "try-runtime")]
371
        fn try_state(_n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
372
            use {scale_info::prelude::format, sp_std::collections::btree_set::BTreeSet};
373
            // A para id can only be in 1 of [`RegisteredParaIds`, `PendingVerification`, `Paused`]
374
            // Get all those para ids and check for duplicates
375
            let mut para_ids: Vec<ParaId> = vec![];
376
            para_ids.extend(RegisteredParaIds::<T>::get());
377
            para_ids.extend(PendingVerification::<T>::iter_keys());
378
            para_ids.extend(Paused::<T>::get());
379
            para_ids.sort();
380
            para_ids.dedup_by(|a, b| {
381
                if a == b {
382
                    panic!("Duplicate para id: {}", u32::from(*a));
383
                } else {
384
                    false
385
                }
386
            });
387

            
388
            // All para ids have an entry in `ParaGenesisData`
389
            for para_id in &para_ids {
390
                assert!(
391
                    ParaGenesisData::<T>::contains_key(para_id),
392
                    "Para id {} missing genesis data",
393
                    u32::from(*para_id)
394
                );
395
            }
396

            
397
            // All entries in `RegistrarDeposit` and `ParaGenesisData` are in one of the other lists
398
            let mut para_id_set = BTreeSet::from_iter(para_ids.iter().cloned());
399
            // Also add the Pending lists here
400
            para_id_set.extend(
401
                PendingParaIds::<T>::get()
402
                    .into_iter()
403
                    .flat_map(|(_session_index, x)| x),
404
            );
405
            para_id_set.extend(
406
                PendingPaused::<T>::get()
407
                    .into_iter()
408
                    .flat_map(|(_session_index, x)| x),
409
            );
410
            para_id_set.extend(
411
                PendingToRemove::<T>::get()
412
                    .into_iter()
413
                    .flat_map(|(_session_index, x)| x),
414
            );
415
            let entries: Vec<_> = RegistrarDeposit::<T>::iter().map(|(k, _v)| k).collect();
416
            for para_id in entries {
417
                assert!(
418
                    para_id_set.contains(&para_id),
419
                    "Found RegistrarDeposit for unknown para id: {}",
420
                    u32::from(para_id)
421
                );
422
            }
423
            let entries: Vec<_> = ParaGenesisData::<T>::iter().map(|(k, _v)| k).collect();
424
            for para_id in entries {
425
                assert!(
426
                    para_id_set.contains(&para_id),
427
                    "Found ParaGenesisData for unknown para id: {}",
428
                    u32::from(para_id)
429
                );
430
            }
431

            
432
            // Sorted storage items are sorted
433
            fn assert_is_sorted_and_unique<T: Ord>(x: &[T], name: &str) {
434
                assert!(
435
                    x.windows(2).all(|w| w[0] < w[1]),
436
                    "sorted list not sorted or not unique: {}",
437
                    name,
438
                );
439
            }
440
            assert_is_sorted_and_unique(&RegisteredParaIds::<T>::get(), "RegisteredParaIds");
441
            assert_is_sorted_and_unique(&Paused::<T>::get(), "Paused");
442
            for (i, (_session_index, x)) in PendingParaIds::<T>::get().into_iter().enumerate() {
443
                assert_is_sorted_and_unique(&x, &format!("PendingParaIds[{}]", i));
444
            }
445
            for (i, (_session_index, x)) in PendingPaused::<T>::get().into_iter().enumerate() {
446
                assert_is_sorted_and_unique(&x, &format!("PendingPaused[{}]", i));
447
            }
448
            for (i, (_session_index, x)) in PendingToRemove::<T>::get().into_iter().enumerate() {
449
                assert_is_sorted_and_unique(&x, &format!("PendingToRemove[{}]", i));
450
            }
451

            
452
            // Pending storage items are sorted and session index is unique
453
            let pending: Vec<_> = PendingParaIds::<T>::get()
454
                .into_iter()
455
                .map(|(session_index, _x)| session_index)
456
                .collect();
457
            assert_is_sorted_and_unique(&pending, "PendingParaIds");
458
            let pending: Vec<_> = PendingPaused::<T>::get()
459
                .into_iter()
460
                .map(|(session_index, _x)| session_index)
461
                .collect();
462
            assert_is_sorted_and_unique(&pending, "PendingPaused");
463
            let pending: Vec<_> = PendingToRemove::<T>::get()
464
                .into_iter()
465
                .map(|(session_index, _x)| session_index)
466
                .collect();
467
            assert_is_sorted_and_unique(&pending, "PendingToRemove");
468

            
469
            Ok(())
470
        }
471
    }
472

            
473
1211
    #[pallet::call]
474
    impl<T: Config> Pallet<T> {
475
        /// Register container-chain
476
        #[pallet::call_index(0)]
477
        #[pallet::weight(T::WeightInfo::register(genesis_data.encoded_size() as u32, genesis_data.storage.len() as u32))]
478
        pub fn register(
479
            origin: OriginFor<T>,
480
            para_id: ParaId,
481
            genesis_data: ContainerChainGenesisData,
482
            head_data: Option<HeadData>,
483
184
        ) -> DispatchResult {
484
184
            let account = ensure_signed(origin)?;
485
183
            Self::do_register(account, para_id, genesis_data, head_data)?;
486
170
            Self::deposit_event(Event::ParaIdRegistered { para_id });
487
170

            
488
170
            Ok(())
489
        }
490

            
491
        /// Deregister container-chain.
492
        ///
493
        /// If a container-chain is registered but not marked as valid_for_collating, this will remove it
494
        /// from `PendingVerification` as well.
495
        #[pallet::call_index(1)]
496
        #[pallet::weight(T::WeightInfo::deregister_immediate(
497
        ).max(T::WeightInfo::deregister_scheduled(
498
        )))]
499
86
        pub fn deregister(origin: OriginFor<T>, para_id: ParaId) -> DispatchResult {
500
86
            T::RegistrarOrigin::ensure_origin(origin)?;
501

            
502
85
            Self::do_deregister(para_id)?;
503

            
504
83
            Ok(())
505
        }
506

            
507
        /// Mark container-chain valid for collating
508
        #[pallet::call_index(2)]
509
        #[pallet::weight(T::WeightInfo::mark_valid_for_collating())]
510
150
        pub fn mark_valid_for_collating(origin: OriginFor<T>, para_id: ParaId) -> DispatchResult {
511
150
            T::MarkValidForCollatingOrigin::ensure_origin(origin)?;
512

            
513
149
            Self::do_mark_valid_for_collating(para_id)?;
514

            
515
142
            Ok(())
516
        }
517

            
518
        /// Pause container-chain from collating. Does not remove its boot nodes nor its genesis config.
519
        /// Only container-chains that have been marked as valid_for_collating can be paused.
520
        #[pallet::call_index(4)]
521
        #[pallet::weight(T::WeightInfo::pause_container_chain())]
522
15
        pub fn pause_container_chain(origin: OriginFor<T>, para_id: ParaId) -> DispatchResult {
523
15
            T::RegistrarOrigin::ensure_origin(origin)?;
524

            
525
14
            Self::schedule_paused_parachain_change(|para_ids, paused| {
526
14
                match paused.binary_search(&para_id) {
527
1
                    Ok(_) => return Err(Error::<T>::ParaIdAlreadyPaused.into()),
528
13
                    Err(index) => {
529
13
                        paused
530
13
                            .try_insert(index, para_id)
531
13
                            .map_err(|_e| Error::<T>::ParaIdListFull)?;
532
                    }
533
                }
534
13
                match para_ids.binary_search(&para_id) {
535
12
                    Ok(index) => {
536
12
                        para_ids.remove(index);
537
12
                    }
538
                    // We can only pause para ids that are marked as valid,
539
                    // otherwise unpausing them later would cause problems
540
1
                    Err(_) => return Err(Error::<T>::ParaIdNotRegistered.into()),
541
                }
542
12
                Self::deposit_event(Event::ParaIdPaused { para_id });
543
12

            
544
12
                Ok(())
545
14
            })?;
546

            
547
12
            Ok(())
548
        }
549

            
550
        /// Unpause container-chain.
551
        /// Only container-chains that have been paused can be unpaused.
552
        #[pallet::call_index(5)]
553
        #[pallet::weight(T::WeightInfo::unpause_container_chain())]
554
10
        pub fn unpause_container_chain(origin: OriginFor<T>, para_id: ParaId) -> DispatchResult {
555
10
            T::RegistrarOrigin::ensure_origin(origin)?;
556

            
557
10
            Self::schedule_paused_parachain_change(|para_ids, paused| {
558
10
                match paused.binary_search(&para_id) {
559
7
                    Ok(index) => {
560
7
                        paused.remove(index);
561
7
                    }
562
3
                    Err(_) => return Err(Error::<T>::ParaIdNotPaused.into()),
563
                }
564
7
                match para_ids.binary_search(&para_id) {
565
                    // This Ok is unreachable, a para id cannot be in "RegisteredParaIds" and "Paused" at the same time
566
                    Ok(_) => return Err(Error::<T>::ParaIdAlreadyRegistered.into()),
567
7
                    Err(index) => {
568
7
                        para_ids
569
7
                            .try_insert(index, para_id)
570
7
                            .map_err(|_e| Error::<T>::ParaIdListFull)?;
571
                    }
572
                }
573
7
                Self::deposit_event(Event::ParaIdUnpaused { para_id });
574
7

            
575
7
                Ok(())
576
10
            })?;
577

            
578
7
            Ok(())
579
        }
580

            
581
        /// Register parathread
582
        #[pallet::call_index(6)]
583
        #[pallet::weight(T::WeightInfo::register_parathread(genesis_data.encoded_size() as u32, genesis_data.storage.len() as u32))]
584
        pub fn register_parathread(
585
            origin: OriginFor<T>,
586
            para_id: ParaId,
587
            slot_frequency: SlotFrequency,
588
            genesis_data: ContainerChainGenesisData,
589
            head_data: Option<HeadData>,
590
28
        ) -> DispatchResult {
591
28
            let account = ensure_signed(origin)?;
592
28
            Self::do_register(account, para_id, genesis_data, head_data)?;
593
            // Insert parathread params
594
28
            let params = ParathreadParamsTy { slot_frequency };
595
28
            ParathreadParams::<T>::insert(para_id, params);
596
28
            Self::deposit_event(Event::ParaIdRegistered { para_id });
597
28

            
598
28
            Ok(())
599
        }
600

            
601
        /// Change parathread params
602
        #[pallet::call_index(7)]
603
        #[pallet::weight(T::WeightInfo::set_parathread_params())]
604
        pub fn set_parathread_params(
605
            origin: OriginFor<T>,
606
            para_id: ParaId,
607
            slot_frequency: SlotFrequency,
608
10
        ) -> DispatchResult {
609
10
            T::RegistrarOrigin::ensure_origin(origin)?;
610

            
611
10
            Self::schedule_parathread_params_change(para_id, |params| {
612
9
                params.slot_frequency = slot_frequency;
613
9

            
614
9
                Self::deposit_event(Event::ParathreadParamsChanged { para_id });
615
9

            
616
9
                Ok(())
617
10
            })?;
618

            
619
9
            Ok(())
620
        }
621

            
622
        #[pallet::call_index(8)]
623
        #[pallet::weight(T::WeightInfo::set_para_manager())]
624
        pub fn set_para_manager(
625
            origin: OriginFor<T>,
626
            para_id: ParaId,
627
            manager_address: T::AccountId,
628
6
        ) -> DispatchResult {
629
            // Allow root to force set para manager.
630
6
            if let Some(origin) = ensure_signed_or_root(origin)? {
631
6
                let creator =
632
6
                    RegistrarDeposit::<T>::get(para_id).map(|deposit_info| deposit_info.creator);
633
6

            
634
6
                ensure!(Some(origin) == creator, Error::<T>::NotParaCreator);
635
            }
636

            
637
6
            ParaManager::<T>::insert(para_id, manager_address.clone());
638
6

            
639
6
            Self::deposit_event(Event::<T>::ParaManagerChanged {
640
6
                para_id,
641
6
                manager_address,
642
6
            });
643
6

            
644
6
            Ok(())
645
        }
646

            
647
        /// Register parachain or parathread
648
        #[pallet::call_index(9)]
649
        #[pallet::weight(T::WeightInfo::register_with_relay_proof(genesis_data.encoded_size() as u32, genesis_data.storage.len() as u32))]
650
        pub fn register_with_relay_proof(
651
            origin: OriginFor<T>,
652
            para_id: ParaId,
653
            parathread_params: Option<ParathreadParamsTy>,
654
            relay_proof_block_number: u32,
655
            relay_storage_proof: sp_trie::StorageProof,
656
            manager_signature: cumulus_primitives_core::relay_chain::Signature,
657
            genesis_data: ContainerChainGenesisData,
658
            head_data: Option<HeadData>,
659
13
        ) -> DispatchResult {
660
13
            let account = T::RegisterWithRelayProofOrigin::ensure_origin(origin)?;
661
12
            let relay_storage_root =
662
13
                T::RelayStorageRootProvider::get_relay_storage_root(relay_proof_block_number)
663
13
                    .ok_or(Error::<T>::RelayStorageRootNotFound)?;
664
11
            let relay_state_proof =
665
12
                GenericStateProof::<cumulus_primitives_core::relay_chain::Block>::new(
666
12
                    relay_storage_root,
667
12
                    relay_storage_proof,
668
12
                )
669
12
                .map_err(|_| Error::<T>::InvalidRelayStorageProof)?;
670

            
671
11
            let bytes = para_id.twox_64_concat();
672
11
            let key = [REGISTRAR_PARAS_INDEX, bytes.as_slice()].concat();
673
11
            let relay_para_info = relay_state_proof
674
11
                .read_entry::<ParaInfo<
675
11
                    cumulus_primitives_core::relay_chain::AccountId,
676
11
                    cumulus_primitives_core::relay_chain::Balance,
677
11
                >>(key.as_slice(), None)
678
11
                .map_err(|_| Error::<T>::InvalidRelayStorageProof)?;
679
9
            let relay_manager = relay_para_info.manager;
680
9

            
681
9
            // Verify manager signature
682
9
            let signature_msg = Self::relay_signature_msg(para_id, &account, relay_storage_root);
683
9
            if !manager_signature.verify(&*signature_msg, &relay_manager) {
684
2
                return Err(Error::<T>::InvalidRelayManagerSignature.into());
685
7
            }
686
7

            
687
7
            Self::do_register(account, para_id, genesis_data, head_data)?;
688
            // Insert parathread params
689
7
            if let Some(parathread_params) = parathread_params {
690
                ParathreadParams::<T>::insert(para_id, parathread_params);
691
7
            }
692
7
            Self::deposit_event(Event::ParaIdRegistered { para_id });
693
7

            
694
7
            Ok(())
695
        }
696

            
697
        /// Deregister a parachain that no longer exists in the relay chain. The origin of this
698
        /// extrinsic will be rewarded with the parachain deposit.
699
        #[pallet::call_index(10)]
700
        #[pallet::weight(T::WeightInfo::deregister_with_relay_proof_immediate(
701
        ).max(T::WeightInfo::deregister_with_relay_proof_scheduled(
702
        )))]
703
        pub fn deregister_with_relay_proof(
704
            origin: OriginFor<T>,
705
            para_id: ParaId,
706
            relay_proof_block_number: u32,
707
            relay_storage_proof: sp_trie::StorageProof,
708
11
        ) -> DispatchResult {
709
11
            let account = T::RegisterWithRelayProofOrigin::ensure_origin(origin)?;
710

            
711
10
            let relay_storage_root =
712
11
                T::RelayStorageRootProvider::get_relay_storage_root(relay_proof_block_number)
713
11
                    .ok_or(Error::<T>::RelayStorageRootNotFound)?;
714
9
            let relay_state_proof =
715
10
                GenericStateProof::<cumulus_primitives_core::relay_chain::Block>::new(
716
10
                    relay_storage_root,
717
10
                    relay_storage_proof,
718
10
                )
719
10
                .map_err(|_| Error::<T>::InvalidRelayStorageProof)?;
720

            
721
9
            let bytes = para_id.twox_64_concat();
722
9
            let key = [REGISTRAR_PARAS_INDEX, bytes.as_slice()].concat();
723
            // TODO: we don't even need to decode the value, only check if it exists
724
            // Need to add exists_storage method to dancekit
725
9
            let relay_para_info = relay_state_proof
726
9
                .read_optional_entry::<ParaInfo<
727
9
                    cumulus_primitives_core::relay_chain::AccountId,
728
9
                    cumulus_primitives_core::relay_chain::Balance,
729
9
                >>(key.as_slice())
730
9
                .map_err(|_| Error::<T>::InvalidRelayStorageProof)?;
731
9
            if relay_para_info.is_some() {
732
1
                return Err(Error::<T>::ParaStillExistsInRelay.into());
733
8
            }
734

            
735
            // Take the deposit immediately and give it to origin account
736
8
            if let Some(asset_info) = RegistrarDeposit::<T>::take(para_id) {
737
8
                // Slash deposit from parachain creator
738
8
                // TODO: error handling
739
8
                let _ = T::Currency::transfer_on_hold(
740
8
                    &HoldReason::RegistrarDeposit.into(),
741
8
                    &asset_info.creator,
742
8
                    &account,
743
8
                    asset_info.deposit,
744
8
                    Precision::Exact,
745
8
                    Restriction::Free,
746
8
                    Fortitude::Force,
747
8
                );
748
8
            }
749

            
750
8
            Self::do_deregister(para_id)?;
751

            
752
8
            Ok(())
753
        }
754
    }
755

            
756
    pub struct SessionChangeOutcome<T: Config> {
757
        /// Previously active parachains.
758
        pub prev_paras: BoundedVec<ParaId, T::MaxLengthParaIds>,
759
        /// If new parachains have been applied in the new session, this is the new  list.
760
        pub new_paras: Option<BoundedVec<ParaId, T::MaxLengthParaIds>>,
761
    }
762

            
763
    impl<T: Config> Pallet<T> {
764
157
        pub fn is_para_manager(para_id: &ParaId, account: &T::AccountId) -> bool {
765
            // This check will only pass if both are true:
766
            // * The para_id has a deposit in pallet_registrar
767
            // * The signed_account is the para manager (or creator if None)
768
157
            if let Some(manager) = ParaManager::<T>::get(para_id) {
769
157
                manager == *account
770
            } else {
771
                RegistrarDeposit::<T>::get(para_id)
772
                    .map(|deposit_info| deposit_info.creator)
773
                    .as_ref()
774
                    == Some(account)
775
            }
776
157
        }
777

            
778
        #[cfg(feature = "runtime-benchmarks")]
779
        pub fn benchmarks_get_or_create_para_manager(para_id: &ParaId) -> T::AccountId {
780
            use {
781
                frame_benchmarking::account,
782
                frame_support::{assert_ok, dispatch::RawOrigin},
783
            };
784
            // Return container chain manager, or register container chain as ALICE if it does not exist
785
            if !ParaGenesisData::<T>::contains_key(para_id) {
786
                // Register as a new user
787

            
788
                /// Create a funded user.
789
                /// Used for generating the necessary amount for registering
790
                fn create_funded_user<T: Config>(
791
                    string: &'static str,
792
                    n: u32,
793
                    total: DepositBalanceOf<T>,
794
                ) -> (T::AccountId, DepositBalanceOf<T>) {
795
                    const SEED: u32 = 0;
796
                    let user = account(string, n, SEED);
797
                    assert_ok!(T::Currency::mint_into(&user, total));
798
                    (user, total)
799
                }
800
                let new_balance =
801
                    T::Currency::minimum_balance() * 10_000_000u32.into() + T::DepositAmount::get();
802
                let account = create_funded_user::<T>("caller", 1000, new_balance).0;
803
                let origin = RawOrigin::Signed(account);
804
                let mut storage = vec![];
805
                storage.push((b":code".to_vec(), vec![1; 10]).into());
806
                let genesis_data = ContainerChainGenesisData {
807
                    storage,
808
                    name: Default::default(),
809
                    id: Default::default(),
810
                    fork_id: Default::default(),
811
                    extensions: Default::default(),
812
                    properties: Default::default(),
813
                };
814
                assert_ok!(Self::register(
815
                    origin.into(),
816
                    *para_id,
817
                    genesis_data,
818
                    T::InnerRegistrar::bench_head_data(),
819
                ));
820
            }
821

            
822
            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");
823

            
824
            // Fund deposit creator, just in case it is not a new account
825
            let new_balance =
826
                (T::Currency::minimum_balance() + T::DepositAmount::get()) * 2u32.into();
827
            assert_ok!(T::Currency::mint_into(&deposit_info.creator, new_balance));
828

            
829
            deposit_info.creator
830
        }
831

            
832
218
        fn do_register(
833
218
            account: T::AccountId,
834
218
            para_id: ParaId,
835
218
            genesis_data: ContainerChainGenesisData,
836
218
            head_data: Option<HeadData>,
837
218
        ) -> DispatchResult {
838
218
            let deposit = T::DepositAmount::get();
839
218
            // Verify we can hold
840
218
            if !T::Currency::can_hold(&HoldReason::RegistrarDeposit.into(), &account, deposit) {
841
                return Err(Error::<T>::NotSufficientDeposit.into());
842
218
            }
843
218

            
844
218
            // Check if the para id is already registered by looking at the genesis data
845
218
            if ParaGenesisData::<T>::contains_key(para_id) {
846
10
                return Err(Error::<T>::ParaIdAlreadyRegistered.into());
847
208
            }
848
208

            
849
208
            // Check if the para id is already in PendingVerification (unreachable)
850
208
            let is_pending_verification = PendingVerification::<T>::take(para_id).is_some();
851
208
            if is_pending_verification {
852
                return Err(Error::<T>::ParaIdAlreadyRegistered.into());
853
208
            }
854
208

            
855
208
            // Insert para id into PendingVerification
856
208
            PendingVerification::<T>::insert(para_id, ());
857
208

            
858
208
            // The actual registration takes place 2 sessions after the call to
859
208
            // `mark_valid_for_collating`, but the genesis data is inserted now.
860
208
            // This is because collators should be able to start syncing the new container chain
861
208
            // before the first block is mined. However, we could store the genesis data in a
862
208
            // different key, like PendingParaGenesisData.
863
208
            // TODO: for benchmarks, this call to .encoded_size is O(n) with respect to the number
864
208
            // of key-values in `genesis_data.storage`, even if those key-values are empty. And we
865
208
            // won't detect that the size is too big until after iterating over all of them, so the
866
208
            // limit in that case would be the transaction size.
867
208
            let genesis_data_size = genesis_data.encoded_size();
868
208
            if genesis_data_size > T::MaxGenesisDataSize::get() as usize {
869
1
                return Err(Error::<T>::GenesisDataTooBig.into());
870
207
            }
871
207

            
872
207
            // Hold the deposit, we verified we can do this
873
207
            T::Currency::hold(&HoldReason::RegistrarDeposit.into(), &account, deposit)?;
874

            
875
            // Register the paraId also in the relay context (if any).
876
207
            T::InnerRegistrar::register(
877
207
                account.clone(),
878
207
                para_id,
879
207
                &genesis_data.storage,
880
207
                head_data,
881
207
            )?;
882

            
883
            // Update DepositInfo
884
205
            RegistrarDeposit::<T>::insert(
885
205
                para_id,
886
205
                DepositInfo {
887
205
                    creator: account.clone(),
888
205
                    deposit,
889
205
                },
890
205
            );
891
205
            ParaGenesisData::<T>::insert(para_id, genesis_data);
892
205

            
893
205
            ParaManager::<T>::insert(para_id, account);
894
205

            
895
205
            Ok(())
896
218
        }
897

            
898
93
        fn do_deregister(para_id: ParaId) -> DispatchResult {
899
93
            // Check if the para id is in "PendingVerification".
900
93
            // This is a special case because then we can remove it immediately, instead of waiting 2 sessions.
901
93
            let is_pending_verification = PendingVerification::<T>::take(para_id).is_some();
902
93
            if is_pending_verification {
903
14
                Self::deposit_event(Event::ParaIdDeregistered { para_id });
904
14
                // Cleanup immediately
905
14
                Self::cleanup_deregistered_para_id(para_id);
906
14
                BufferedParasToDeregister::<T>::try_mutate(|v| v.try_push(para_id)).map_err(
907
14
                    |_e| {
908
                        DispatchError::Other(
909
                            "Failed to add paraId to deregistration list: buffer is full",
910
                        )
911
14
                    },
912
14
                )?;
913
            } else {
914
79
                Self::schedule_paused_parachain_change(|para_ids, paused| {
915
79
                    // We have to find out where, in the sorted vec the para id is, if anywhere.
916
79

            
917
79
                    match para_ids.binary_search(&para_id) {
918
74
                        Ok(index) => {
919
74
                            para_ids.remove(index);
920
74
                        }
921
                        Err(_) => {
922
                            // If the para id is not registered, it may be paused. In that case, remove it from there
923
5
                            match paused.binary_search(&para_id) {
924
3
                                Ok(index) => {
925
3
                                    paused.remove(index);
926
3
                                }
927
                                Err(_) => {
928
2
                                    return Err(Error::<T>::ParaIdNotRegistered.into());
929
                                }
930
                            }
931
                        }
932
                    }
933

            
934
77
                    Ok(())
935
79
                })?;
936
                // Mark this para id for cleanup later
937
77
                Self::schedule_parachain_cleanup(para_id)?;
938

            
939
                // If we have InnerRegistrar set to a relay context (like Dancelight),
940
                // we first need to downgrade the paraId (if it was a parachain before)
941
                // and convert it to a parathread before deregistering it. Otherwise
942
                // the deregistration process will fail in the scheduled session.
943
                //
944
                // We only downgrade if the paraId is a parachain in the context of
945
                // this pallet.
946
77
                if ParathreadParams::<T>::get(para_id).is_none() {
947
75
                    T::InnerRegistrar::schedule_para_downgrade(para_id)?;
948
2
                }
949

            
950
77
                Self::deposit_event(Event::ParaIdDeregistered { para_id });
951
            }
952

            
953
91
            Ok(())
954
93
        }
955

            
956
149
        fn do_mark_valid_for_collating(para_id: ParaId) -> DispatchResult {
957
149
            let is_pending_verification = PendingVerification::<T>::take(para_id).is_some();
958
149
            if !is_pending_verification {
959
3
                return Err(Error::<T>::ParaIdNotInPendingVerification.into());
960
146
            }
961
146

            
962
146
            Self::schedule_parachain_change(|para_ids| {
963
146
                // We don't want to add duplicate para ids, so we check whether the potential new
964
146
                // para id is already present in the list. Because the list is always ordered, we can
965
146
                // leverage the binary search which makes this check O(log n).
966
146

            
967
146
                match para_ids.binary_search(&para_id) {
968
                    // This Ok is unreachable
969
                    Ok(_) => return Err(Error::<T>::ParaIdAlreadyRegistered.into()),
970
146
                    Err(index) => {
971
146
                        para_ids
972
146
                            .try_insert(index, para_id)
973
146
                            .map_err(|_e| Error::<T>::ParaIdListFull)?;
974
                    }
975
                }
976

            
977
146
                Ok(())
978
146
            })?;
979

            
980
146
            T::RegistrarHooks::check_valid_for_collating(para_id)?;
981

            
982
143
            Self::deposit_event(Event::ParaIdValidForCollating { para_id });
983
143

            
984
143
            T::RegistrarHooks::para_marked_valid_for_collating(para_id);
985
143

            
986
143
            // If we execute mark_valid_for_collating, we automatically upgrade
987
143
            // the paraId to a parachain (in the relay context) at the end of the execution.
988
143
            //
989
143
            // We only upgrade if the paraId is a parachain in the context of
990
143
            // this pallet.
991
143
            if ParathreadParams::<T>::get(para_id).is_none() {
992
115
                T::InnerRegistrar::schedule_para_upgrade(para_id)?;
993
28
            }
994

            
995
142
            Ok(())
996
149
        }
997

            
998
        /// Relay parachain manager signature message. Includes:
999
        /// * para_id, in case the manager has more than 1 para in the relay
        /// * accountid in tanssi, to ensure that the creator role is assigned to the desired account
        /// * relay_storage_root, to make the signature network-specific, and also make it expire
        ///     when the relay storage root expires.
16
        pub fn relay_signature_msg(
16
            para_id: ParaId,
16
            tanssi_account: &T::AccountId,
16
            relay_storage_root: H256,
16
        ) -> Vec<u8> {
16
            (para_id, tanssi_account, relay_storage_root).encode()
16
        }
146
        fn schedule_parachain_change(
146
            updater: impl FnOnce(&mut BoundedVec<ParaId, T::MaxLengthParaIds>) -> DispatchResult,
146
        ) -> DispatchResult {
146
            let mut pending_paras = PendingParaIds::<T>::get();
146
            // First, we need to decide what we should use as the base paras.
146
            let mut base_paras = pending_paras
146
                .last()
146
                .map(|(_, paras)| paras.clone())
146
                .unwrap_or_else(Self::registered_para_ids);
146

            
146
            updater(&mut base_paras)?;
146
            let new_paras = base_paras;
146

            
146
            let scheduled_session = Self::scheduled_session();
146
            if let Some(&mut (_, ref mut paras)) = pending_paras
146
                .iter_mut()
146
                .find(|&&mut (apply_at_session, _)| apply_at_session >= scheduled_session)
7
            {
7
                *paras = new_paras;
139
            } else {
139
                // We are scheduling a new parachains change for the scheduled session.
139
                pending_paras.push((scheduled_session, new_paras));
139
            }
146
            <PendingParaIds<T>>::put(pending_paras);
146

            
146
            Ok(())
146
        }
103
        fn schedule_paused_parachain_change(
103
            updater: impl FnOnce(
103
                &mut BoundedVec<ParaId, T::MaxLengthParaIds>,
103
                &mut BoundedVec<ParaId, T::MaxLengthParaIds>,
103
            ) -> DispatchResult,
103
        ) -> DispatchResult {
103
            let mut pending_paras = PendingParaIds::<T>::get();
103
            let mut pending_paused = PendingPaused::<T>::get();
103
            // First, we need to decide what we should use as the base paras.
103
            let mut base_paras = pending_paras
103
                .last()
103
                .map(|(_, paras)| paras.clone())
103
                .unwrap_or_else(Self::registered_para_ids);
103
            let mut base_paused = pending_paused
103
                .last()
103
                .map(|(_, paras)| paras.clone())
103
                .unwrap_or_else(Self::paused);
103
            let old_base_paras = base_paras.clone();
103
            let old_base_paused = base_paused.clone();
103

            
103
            updater(&mut base_paras, &mut base_paused)?;
96
            if base_paras != old_base_paras {
93
                let new_paras = base_paras;
93
                let scheduled_session = Self::scheduled_session();
93
                if let Some(&mut (_, ref mut paras)) = pending_paras
93
                    .iter_mut()
93
                    .find(|&&mut (apply_at_session, _)| apply_at_session >= scheduled_session)
26
                {
26
                    *paras = new_paras;
71
                } else {
67
                    // We are scheduling a new parachains change for the scheduled session.
67
                    pending_paras.push((scheduled_session, new_paras));
67
                }
93
                <PendingParaIds<T>>::put(pending_paras);
3
            }
96
            if base_paused != old_base_paused {
22
                let new_paused = base_paused;
22
                let scheduled_session = Self::scheduled_session();
22
                if let Some(&mut (_, ref mut paras)) = pending_paused
22
                    .iter_mut()
22
                    .find(|&&mut (apply_at_session, _)| apply_at_session >= scheduled_session)
2
                {
2
                    *paras = new_paused;
20
                } else {
20
                    // We are scheduling a new parachains change for the scheduled session.
20
                    pending_paused.push((scheduled_session, new_paused));
20
                }
22
                <PendingPaused<T>>::put(pending_paused);
74
            }
96
            Ok(())
103
        }
10
        fn schedule_parathread_params_change(
10
            para_id: ParaId,
10
            updater: impl FnOnce(&mut ParathreadParamsTy) -> DispatchResult,
10
        ) -> DispatchResult {
            // Check that the para id is a parathread by reading the old params
10
            let params = match ParathreadParams::<T>::get(para_id) {
9
                Some(x) => x,
                None => {
1
                    return Err(Error::<T>::NotAParathread.into());
                }
            };
9
            let mut pending_params = PendingParathreadParams::<T>::get();
9
            // First, we need to decide what we should use as the base params.
9
            let mut base_params = pending_params
9
                .last()
9
                .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,
                    }
9
                })
9
                .unwrap_or(params);
9

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

            
9
            let scheduled_session = Self::scheduled_session();
9
            if let Some(&mut (_, ref mut para_id_params)) = pending_params
9
                .iter_mut()
9
                .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)?;
                    }
                }
9
            } else {
9
                // We are scheduling a new parathread params change for the scheduled session.
9
                pending_params.push((
9
                    scheduled_session,
9
                    BoundedVec::truncate_from(vec![(para_id, new_params)]),
9
                ));
9
            }
9
            <PendingParathreadParams<T>>::put(pending_params);
9

            
9
            Ok(())
10
        }
        /// Return the session index that should be used for any future scheduled changes.
347
        fn scheduled_session() -> T::SessionIndex {
347
            T::CurrentSessionIndex::session_index().saturating_add(T::SessionDelay::get())
347
        }
        /// 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.
3062
        pub fn initializer_on_new_session(
3062
            session_index: &T::SessionIndex,
3062
        ) -> SessionChangeOutcome<T> {
3062
            let pending_paras = <PendingParaIds<T>>::get();
3062
            let prev_paras = RegisteredParaIds::<T>::get();
3062
            let new_paras = if !pending_paras.is_empty() {
362
                let (mut past_and_present, future) = pending_paras
362
                    .into_iter()
366
                    .partition::<Vec<_>, _>(|&(apply_at_session, _)| {
366
                        apply_at_session <= *session_index
366
                    });
362

            
362
                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",
                    );
362
                }
362
                let new_paras = past_and_present.pop().map(|(_, paras)| paras);
362
                if let Some(ref new_paras) = new_paras {
183
                    // Apply the new parachain list.
183
                    RegisteredParaIds::<T>::put(new_paras);
183
                    <PendingParaIds<T>>::put(future);
183
                }
362
                new_paras
            } else {
                // pending_paras.is_empty, so parachain list did not change
2700
                None
            };
3062
            let pending_paused = <PendingPaused<T>>::get();
3062
            if !pending_paused.is_empty() {
35
                let (mut past_and_present, future) = pending_paused
35
                    .into_iter()
36
                    .partition::<Vec<_>, _>(|&(apply_at_session, _)| {
36
                        apply_at_session <= *session_index
36
                    });
35

            
35
                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",
                    );
35
                }
35
                let new_paused = past_and_present.pop().map(|(_, paras)| paras);
35
                if let Some(ref new_paused) = new_paused {
18
                    // Apply the new parachain list.
18
                    Paused::<T>::put(new_paused);
18
                    <PendingPaused<T>>::put(future);
18
                }
3027
            }
3062
            let pending_parathread_params = <PendingParathreadParams<T>>::get();
3062
            if !pending_parathread_params.is_empty() {
18
                let (mut past_and_present, future) = pending_parathread_params
18
                    .into_iter()
18
                    .partition::<Vec<_>, _>(|&(apply_at_session, _)| {
18
                        apply_at_session <= *session_index
18
                    });
18

            
18
                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",
                    );
18
                }
18
                let new_params = past_and_present.pop().map(|(_, params)| params);
18
                if let Some(ref new_params) = new_params {
17
                    for (para_id, params) in new_params {
8
                        <ParathreadParams<T>>::insert(para_id, params);
8
                    }
9
                    <PendingParathreadParams<T>>::put(future);
9
                }
3044
            }
3062
            let pending_to_remove = <PendingToRemove<T>>::get();
3062
            if !pending_to_remove.is_empty() {
133
                let (past_and_present, future) =
133
                    pending_to_remove.into_iter().partition::<Vec<_>, _>(
134
                        |&(apply_at_session, _)| apply_at_session <= *session_index,
133
                    );
133

            
133
                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.
67
                    let mut removed_para_ids = BTreeSet::new();
134
                    for (_, new_paras) in &past_and_present {
142
                        for para_id in new_paras {
75
                            Self::cleanup_deregistered_para_id(*para_id);
75
                            removed_para_ids.insert(*para_id);
                            if let Err(id) =
75
                                BufferedParasToDeregister::<T>::try_mutate(|v| v.try_push(*para_id))
                            {
                                log::error!(
                                    target: LOG_TARGET,
                                    "Failed to add paraId {:?} to deregistration list",
                                    id
                                );
75
                            }
                        }
                    }
                    // Also need to remove PendingParams to avoid setting params for a para id that does not exist
67
                    let mut pending_parathread_params = <PendingParathreadParams<T>>::get();
68
                    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
                    }
67
                    <PendingParathreadParams<T>>::put(pending_parathread_params);
67
                    <PendingToRemove<T>>::put(future);
66
                }
2929
            }
3062
            SessionChangeOutcome {
3062
                prev_paras,
3062
                new_paras,
3062
            }
3062
        }
        /// Remove all para id storage in this pallet,
        /// and execute para_deregistered hook to clean up other pallets as well
89
        fn cleanup_deregistered_para_id(para_id: ParaId) {
89
            ParaGenesisData::<T>::remove(para_id);
89
            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
89
            if let Some(asset_info) = RegistrarDeposit::<T>::take(para_id) {
34
                // Release hold
34
                let _ = T::Currency::release(
34
                    &HoldReason::RegistrarDeposit.into(),
34
                    &asset_info.creator,
34
                    asset_info.deposit,
34
                    Precision::Exact,
34
                );
74
            }
89
            ParaManager::<T>::remove(para_id);
89

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

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

            
77
            Ok(())
77
        }
72220
        pub fn registered_para_ids() -> BoundedVec<ParaId, T::MaxLengthParaIds> {
72220
            RegisteredParaIds::<T>::get()
72220
        }
23970
        pub fn pending_registered_para_ids(
23970
        ) -> Vec<(T::SessionIndex, BoundedVec<ParaId, T::MaxLengthParaIds>)> {
23970
            PendingParaIds::<T>::get()
23970
        }
79
        pub fn para_genesis_data(para_id: ParaId) -> Option<ContainerChainGenesisData> {
79
            ParaGenesisData::<T>::get(para_id)
79
        }
        pub fn pending_verification(para_id: ParaId) -> Option<()> {
            PendingVerification::<T>::get(para_id)
        }
105
        pub fn paused() -> BoundedVec<ParaId, T::MaxLengthParaIds> {
105
            Paused::<T>::get()
105
        }
        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()
        }
31
        pub fn parathread_params(para_id: ParaId) -> Option<ParathreadParamsTy> {
31
            ParathreadParams::<T>::get(para_id)
31
        }
        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;
48246
        fn current_container_chains() -> BoundedVec<ParaId, Self::MaxContainerChains> {
48246
            Self::registered_para_ids()
48246
        }
        #[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> {
23940
        fn session_container_chains(session_index: T::SessionIndex) -> SessionContainerChains {
23940
            let (past_and_present, _) = Pallet::<T>::pending_registered_para_ids()
23940
                .into_iter()
23940
                .partition::<Vec<_>, _>(|&(apply_at_session, _)| apply_at_session <= session_index);
23940
            let paras = if let Some(last) = past_and_present.last() {
233
                last.1.clone()
            } else {
23707
                Pallet::<T>::registered_para_ids()
            };
23940
            let mut parachains = vec![];
23940
            let mut parathreads = vec![];
70292
            for para_id in paras {
                // TODO: sweet O(n) db reads
46352
                if let Some(parathread_params) = ParathreadParams::<T>::get(para_id) {
849
                    parathreads.push((para_id, parathread_params));
45503
                } else {
45503
                    parachains.push(para_id);
45503
                }
            }
23940
            SessionContainerChains {
23940
                parachains,
23940
                parathreads,
23940
            }
23940
        }
        #[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;
189
    fn try_origin(
189
        o: T::RuntimeOrigin,
189
        para_id: &ParaId,
189
    ) -> Result<Self::Success, T::RuntimeOrigin> {
157
        let signed_account =
189
            <frame_system::EnsureSigned<_> as EnsureOrigin<_>>::try_origin(o.clone())?;
157
        if !Pallet::<T>::is_para_manager(para_id, &signed_account) {
6
            return Err(frame_system::RawOrigin::Signed(signed_account).into());
151
        }
151

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