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
extern crate alloc;
30

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

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

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

            
44
pub use pallet::*;
45

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

            
75
35568
#[frame_support::pallet]
76
pub mod pallet {
77
    use super::*;
78

            
79
2820
    #[pallet::pallet]
80
    pub struct Pallet<T>(_);
81

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

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

            
110
1322
            let mut bounded_para_ids = BoundedVec::default();
111

            
112
1798
            for (para_id, genesis_data, parathread_params) in para_ids {
113
478
                bounded_para_ids
114
478
                    .try_push(*para_id)
115
478
                    .expect("too many para ids in genesis: bounded vec full");
116
478

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

            
128
477
                if let Some(parathread_params) = parathread_params {
129
40
                    <ParathreadParams<T>>::insert(para_id, parathread_params);
130
436
                }
131
            }
132

            
133
1320
            <RegisteredParaIds<T>>::put(bounded_para_ids);
134
1320
        }
135
    }
136

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

            
143
        /// Origin that is allowed to call maintenance extrinsics for container owner
144
        type RegistrarOrigin: EnsureOriginWithArg<Self::RuntimeOrigin, ParaId>;
145

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

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

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

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

            
162
        type RelayStorageRootProvider: RelayStorageRootProvider;
163

            
164
        type SessionIndex: parity_scale_codec::FullCodec + TypeInfo + Copy + AtLeast32BitUnsigned;
165

            
166
        #[pallet::constant]
167
        type SessionDelay: Get<Self::SessionIndex>;
168

            
169
        type CurrentSessionIndex: GetSessionIndex<Self::SessionIndex>;
170

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

            
174
        type RuntimeHoldReason: From<HoldReason>;
175

            
176
        type RegistrarHooks: RegistrarHooks;
177

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

            
186
        type WeightInfo: WeightInfo;
187

            
188
        #[pallet::constant]
189
        type DataDepositPerByte: Get<<Self::Currency as Inspect<Self::AccountId>>::Balance>;
190
    }
191

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
352
    #[pallet::composite_enum]
353
    pub enum HoldReason {
354
347
        RegistrarDeposit,
355
    }
356

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

            
362
31650
            let buffered_paras = BufferedParasToDeregister::<T>::take();
363

            
364
31715
            for para_id in buffered_paras {
365
65
                weight.saturating_accrue(T::InnerRegistrar::deregister_weight());
366
65
                // Deregister (in the relay context) each paraId present inside the buffer
367
65
                T::InnerRegistrar::deregister(para_id);
368
65
            }
369
31650
            weight
370
31650
        }
371

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

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

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

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

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

            
471
            Ok(())
472
        }
473
    }
474

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

            
490
214
            Ok(())
491
        }
492

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

            
504
111
            Self::do_deregister(para_id)?;
505

            
506
109
            Ok(())
507
        }
508

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

            
515
185
            Self::do_mark_valid_for_collating(para_id)?;
516

            
517
176
            Ok(())
518
        }
519

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

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

            
546
22
                Ok(())
547
24
            })?;
548

            
549
22
            Ok(())
550
        }
551

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

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

            
577
7
                Ok(())
578
10
            })?;
579

            
580
7
            Ok(())
581
        }
582

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

            
600
40
            Ok(())
601
        }
602

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

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

            
616
9
                Self::deposit_event(Event::ParathreadParamsChanged { para_id });
617
9

            
618
9
                Ok(())
619
10
            })?;
620

            
621
9
            Ok(())
622
        }
623

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

            
636
6
                ensure!(Some(origin) == creator, Error::<T>::NotParaCreator);
637
10
            }
638

            
639
16
            ParaManager::<T>::insert(para_id, manager_address.clone());
640
16

            
641
16
            Self::deposit_event(Event::<T>::ParaManagerChanged {
642
16
                para_id,
643
16
                manager_address,
644
16
            });
645
16

            
646
16
            Ok(())
647
        }
648

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

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

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

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

            
696
7
            Ok(())
697
        }
698

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

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

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

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

            
752
8
            Self::do_deregister(para_id)?;
753

            
754
8
            Ok(())
755
        }
756
    }
757

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

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

            
780
        #[cfg(feature = "runtime-benchmarks")]
781
        pub fn benchmarks_get_or_create_para_manager(para_id: &ParaId) -> T::AccountId {
782
            use {
783
                frame_benchmarking::account,
784
                frame_support::{assert_ok, dispatch::RawOrigin},
785
            };
786

            
787
            let mut storage = BoundedVec::try_from(vec![]).unwrap();
788
            storage
789
                .try_push((b":code".to_vec(), vec![1; 10]).into())
790
                .unwrap();
791
            let genesis_data = ContainerChainGenesisData {
792
                storage,
793
                name: Default::default(),
794
                id: Default::default(),
795
                fork_id: Default::default(),
796
                extensions: Default::default(),
797
                properties: Default::default(),
798
            };
799

            
800
            // Return container chain manager, or register container chain as ALICE if it does not exist
801
            if !ParaGenesisData::<T>::contains_key(para_id) {
802
                // Register as a new user
803

            
804
                /// Create a funded user.
805
                /// Used for generating the necessary amount for registering
806
                fn create_funded_user<T: Config>(
807
                    string: &'static str,
808
                    n: u32,
809
                    total: DepositBalanceOf<T>,
810
                ) -> (T::AccountId, DepositBalanceOf<T>) {
811
                    const SEED: u32 = 0;
812
                    let user = account(string, n, SEED);
813
                    assert_ok!(T::Currency::mint_into(&user, total));
814
                    (user, total)
815
                }
816

            
817
                let deposit = Self::get_genesis_cost(genesis_data.encoded_size());
818
                let new_balance = T::Currency::minimum_balance()
819
                    .saturating_mul(100_000_000u32.into())
820
                    .saturating_add(deposit);
821
                let account = create_funded_user::<T>("caller", 1000, new_balance).0;
822
                T::InnerRegistrar::prepare_chain_registration(*para_id, account.clone());
823
                let origin = RawOrigin::Signed(account);
824

            
825
                assert_ok!(Self::register(
826
                    origin.into(),
827
                    *para_id,
828
                    genesis_data.clone(),
829
                    T::InnerRegistrar::bench_head_data(),
830
                ));
831
            }
832

            
833
            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");
834

            
835
            let deposit = Self::get_genesis_cost(genesis_data.encoded_size());
836
            // Fund deposit creator, just in case it is not a new account
837
            let new_balance = (T::Currency::minimum_balance().saturating_add(deposit))
838
                .saturating_mul(2u32.into());
839
            assert_ok!(T::Currency::mint_into(&deposit_info.creator, new_balance));
840

            
841
            deposit_info.creator
842
        }
843

            
844
277
        pub fn get_genesis_cost(size: usize) -> <T::Currency as Inspect<T::AccountId>>::Balance {
845
277
            T::DataDepositPerByte::get().saturating_mul((size as u32).into())
846
277
        }
847

            
848
277
        fn do_register(
849
277
            account: T::AccountId,
850
277
            para_id: ParaId,
851
277
            genesis_data: ContainerChainGenesisData,
852
277
            head_data: Option<HeadData>,
853
277
        ) -> DispatchResult {
854
277
            // The actual registration takes place 2 sessions after the call to
855
277
            // `mark_valid_for_collating`, but the genesis data is inserted now.
856
277
            // This is because collators should be able to start syncing the new container chain
857
277
            // before the first block is mined. However, we could store the genesis data in a
858
277
            // different key, like PendingParaGenesisData.
859
277
            // TODO: for benchmarks, this call to .encoded_size is O(n) with respect to the number
860
277
            // of key-values in `genesis_data.storage`, even if those key-values are empty. And we
861
277
            // won't detect that the size is too big until after iterating over all of them, so the
862
277
            // limit in that case would be the transaction size.
863
277
            let genesis_data_size = genesis_data.encoded_size();
864
277

            
865
277
            let deposit = Self::get_genesis_cost(genesis_data_size);
866
277
            // Verify we can hold
867
277
            if !T::Currency::can_hold(&HoldReason::RegistrarDeposit.into(), &account, deposit) {
868
                return Err(Error::<T>::NotSufficientDeposit.into());
869
277
            }
870
277

            
871
277
            // Check if the para id is already registered by looking at the genesis data
872
277
            if ParaGenesisData::<T>::contains_key(para_id) {
873
11
                return Err(Error::<T>::ParaIdAlreadyRegistered.into());
874
266
            }
875
266

            
876
266
            // Check if the para id is already in PendingVerification (unreachable)
877
266
            let is_pending_verification = PendingVerification::<T>::take(para_id).is_some();
878
266
            if is_pending_verification {
879
                return Err(Error::<T>::ParaIdAlreadyRegistered.into());
880
266
            }
881
266

            
882
266
            // Insert para id into PendingVerification
883
266
            PendingVerification::<T>::insert(para_id, ());
884
266

            
885
266
            if genesis_data_size > T::MaxGenesisDataSize::get() as usize {
886
1
                return Err(Error::<T>::GenesisDataTooBig.into());
887
265
            }
888
265

            
889
265
            // Hold the deposit, we verified we can do this
890
265
            T::Currency::hold(&HoldReason::RegistrarDeposit.into(), &account, deposit)?;
891

            
892
            // Register the paraId also in the relay context (if any).
893
265
            T::InnerRegistrar::register(
894
265
                account.clone(),
895
265
                para_id,
896
265
                &genesis_data.storage,
897
265
                head_data,
898
265
            )?;
899

            
900
            // Update DepositInfo
901
261
            RegistrarDeposit::<T>::insert(
902
261
                para_id,
903
261
                DepositInfo {
904
261
                    creator: account.clone(),
905
261
                    deposit,
906
261
                },
907
261
            );
908
261
            ParaGenesisData::<T>::insert(para_id, genesis_data);
909
261

            
910
261
            ParaManager::<T>::insert(para_id, account);
911
261

            
912
261
            Ok(())
913
277
        }
914

            
915
119
        fn do_deregister(para_id: ParaId) -> DispatchResult {
916
119
            // Check if the para id is in "PendingVerification".
917
119
            // This is a special case because then we can remove it immediately, instead of waiting 2 sessions.
918
119
            let is_pending_verification = PendingVerification::<T>::take(para_id).is_some();
919
119
            if is_pending_verification {
920
15
                Self::deposit_event(Event::ParaIdDeregistered { para_id });
921
15
                // Cleanup immediately
922
15
                Self::cleanup_deregistered_para_id(para_id);
923
15
                BufferedParasToDeregister::<T>::try_mutate(|v| v.try_push(para_id)).map_err(
924
15
                    |_e| {
925
                        DispatchError::Other(
926
                            "Failed to add paraId to deregistration list: buffer is full",
927
                        )
928
15
                    },
929
15
                )?;
930
            } else {
931
104
                Self::schedule_paused_parachain_change(|para_ids, paused| {
932
104
                    // We have to find out where, in the sorted vec the para id is, if anywhere.
933
104

            
934
104
                    match para_ids.binary_search(&para_id) {
935
99
                        Ok(index) => {
936
99
                            para_ids.remove(index);
937
99
                        }
938
                        Err(_) => {
939
                            // If the para id is not registered, it may be paused. In that case, remove it from there
940
5
                            match paused.binary_search(&para_id) {
941
3
                                Ok(index) => {
942
3
                                    paused.remove(index);
943
3
                                }
944
                                Err(_) => {
945
2
                                    return Err(Error::<T>::ParaIdNotRegistered.into());
946
                                }
947
                            }
948
                        }
949
                    }
950

            
951
102
                    Ok(())
952
104
                })?;
953
                // Mark this para id for cleanup later
954
102
                Self::schedule_parachain_cleanup(para_id)?;
955

            
956
                // If we have InnerRegistrar set to a relay context (like Dancelight),
957
                // we first need to downgrade the paraId (if it was a parachain before)
958
                // and convert it to a parathread before deregistering it. Otherwise
959
                // the deregistration process will fail in the scheduled session.
960
                //
961
                // We only downgrade if the paraId is a parachain in the context of
962
                // this pallet.
963
102
                if ParathreadParams::<T>::get(para_id).is_none() {
964
100
                    T::InnerRegistrar::schedule_para_downgrade(para_id)?;
965
2
                }
966

            
967
102
                Self::deposit_event(Event::ParaIdDeregistered { para_id });
968
            }
969

            
970
117
            Ok(())
971
119
        }
972

            
973
185
        fn do_mark_valid_for_collating(para_id: ParaId) -> DispatchResult {
974
185
            let is_pending_verification = PendingVerification::<T>::take(para_id).is_some();
975
185
            if !is_pending_verification {
976
3
                return Err(Error::<T>::ParaIdNotInPendingVerification.into());
977
182
            }
978
182

            
979
182
            Self::schedule_parachain_change(|para_ids| {
980
182
                // We don't want to add duplicate para ids, so we check whether the potential new
981
182
                // para id is already present in the list. Because the list is always ordered, we can
982
182
                // leverage the binary search which makes this check O(log n).
983
182

            
984
182
                match para_ids.binary_search(&para_id) {
985
                    // This Ok is unreachable
986
                    Ok(_) => return Err(Error::<T>::ParaIdAlreadyRegistered.into()),
987
182
                    Err(index) => {
988
182
                        para_ids
989
182
                            .try_insert(index, para_id)
990
182
                            .map_err(|_e| Error::<T>::ParaIdListFull)?;
991
                    }
992
                }
993

            
994
182
                Ok(())
995
182
            })?;
996

            
997
182
            T::RegistrarHooks::check_valid_for_collating(para_id)?;
998

            
999
178
            Self::deposit_event(Event::ParaIdValidForCollating { para_id });
178

            
178
            T::RegistrarHooks::para_marked_valid_for_collating(para_id);
178

            
178
            // If we execute mark_valid_for_collating, we automatically upgrade
178
            // the paraId to a parachain (in the relay context) at the end of the execution.
178
            //
178
            // We only upgrade if the paraId is a parachain in the context of
178
            // this pallet.
178
            if ParathreadParams::<T>::get(para_id).is_none() {
138
                T::InnerRegistrar::schedule_para_upgrade(para_id)?;
40
            }
176
            Ok(())
185
        }
        /// Relay parachain manager signature message. Includes:
        /// * 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
        }
182
        fn schedule_parachain_change(
182
            updater: impl FnOnce(&mut BoundedVec<ParaId, T::MaxLengthParaIds>) -> DispatchResult,
182
        ) -> DispatchResult {
182
            let mut pending_paras = PendingParaIds::<T>::get();
182
            // First, we need to decide what we should use as the base paras.
182
            let mut base_paras = pending_paras
182
                .last()
182
                .map(|(_, paras)| paras.clone())
182
                .unwrap_or_else(Self::registered_para_ids);
182

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

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

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

            
138
            updater(&mut base_paras, &mut base_paused)?;
131
            if base_paras != old_base_paras {
128
                let new_paras = base_paras;
128
                let scheduled_session = Self::scheduled_session();
128
                if let Some(&mut (_, ref mut paras)) = pending_paras
128
                    .iter_mut()
128
                    .find(|&&mut (apply_at_session, _)| apply_at_session >= scheduled_session)
33
                {
33
                    *paras = new_paras;
99
                } else {
95
                    // We are scheduling a new parachains change for the scheduled session.
95
                    pending_paras.push((scheduled_session, new_paras));
95
                }
128
                <PendingParaIds<T>>::put(pending_paras);
3
            }
131
            if base_paused != old_base_paused {
32
                let new_paused = base_paused;
32
                let scheduled_session = Self::scheduled_session();
32
                if let Some(&mut (_, ref mut paras)) = pending_paused
32
                    .iter_mut()
32
                    .find(|&&mut (apply_at_session, _)| apply_at_session >= scheduled_session)
2
                {
2
                    *paras = new_paused;
30
                } else {
30
                    // We are scheduling a new parachains change for the scheduled session.
30
                    pending_paused.push((scheduled_session, new_paused));
30
                }
32
                <PendingPaused<T>>::put(pending_paused);
99
            }
131
            Ok(())
138
        }
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.
453
        fn scheduled_session() -> T::SessionIndex {
453
            T::CurrentSessionIndex::session_index().saturating_add(T::SessionDelay::get())
453
        }
        /// 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.
4865
        pub fn initializer_on_new_session(
4865
            session_index: &T::SessionIndex,
4865
        ) -> SessionChangeOutcome<T> {
4865
            let pending_paras = <PendingParaIds<T>>::get();
4865
            let prev_paras = RegisteredParaIds::<T>::get();
4865
            let new_paras = if !pending_paras.is_empty() {
578
                let (mut past_and_present, future) = pending_paras
578
                    .into_iter()
582
                    .partition::<Vec<_>, _>(|&(apply_at_session, _)| {
582
                        apply_at_session <= *session_index
582
                    });
578

            
578
                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",
                    );
578
                }
578
                let new_paras = past_and_present.pop().map(|(_, paras)| paras);
578
                if let Some(ref new_paras) = new_paras {
291
                    // Apply the new parachain list.
291
                    RegisteredParaIds::<T>::put(new_paras);
291
                    <PendingParaIds<T>>::put(future);
291
                }
578
                new_paras
            } else {
                // pending_paras.is_empty, so parachain list did not change
4287
                None
            };
4865
            let pending_paused = <PendingPaused<T>>::get();
4865
            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
                }
4830
            }
4865
            let pending_parathread_params = <PendingParathreadParams<T>>::get();
4865
            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
                }
4847
            }
4865
            let pending_to_remove = <PendingToRemove<T>>::get();
4865
            if !pending_to_remove.is_empty() {
169
                let (past_and_present, future) =
169
                    pending_to_remove.into_iter().partition::<Vec<_>, _>(
170
                        |&(apply_at_session, _)| apply_at_session <= *session_index,
169
                    );
169

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

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

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

            
102
            Ok(())
102
        }
66611
        pub fn registered_para_ids() -> BoundedVec<ParaId, T::MaxLengthParaIds> {
66611
            RegisteredParaIds::<T>::get()
66611
        }
31817
        pub fn pending_registered_para_ids(
31817
        ) -> Vec<(T::SessionIndex, BoundedVec<ParaId, T::MaxLengthParaIds>)> {
31817
            PendingParaIds::<T>::get()
31817
        }
97
        pub fn para_genesis_data(para_id: ParaId) -> Option<ContainerChainGenesisData> {
97
            ParaGenesisData::<T>::get(para_id)
97
        }
        pub fn pending_verification(para_id: ParaId) -> Option<()> {
            PendingVerification::<T>::get(para_id)
        }
140
        pub fn paused() -> BoundedVec<ParaId, T::MaxLengthParaIds> {
140
            Paused::<T>::get()
140
        }
        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()
        }
103
        pub fn parathread_params(para_id: ParaId) -> Option<ParathreadParamsTy> {
103
            ParathreadParams::<T>::get(para_id)
103
        }
        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;
34854
        fn current_container_chains() -> BoundedVec<ParaId, Self::MaxContainerChains> {
34854
            Self::registered_para_ids()
34854
        }
        #[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> {
31784
        fn session_container_chains(session_index: T::SessionIndex) -> SessionContainerChains {
31784
            let (past_and_present, _) = Pallet::<T>::pending_registered_para_ids()
31784
                .into_iter()
31784
                .partition::<Vec<_>, _>(|&(apply_at_session, _)| apply_at_session <= session_index);
31784
            let paras = if let Some(last) = past_and_present.last() {
353
                last.1.clone()
            } else {
31431
                Pallet::<T>::registered_para_ids()
            };
31784
            let mut parachains = vec![];
31784
            let mut parathreads = vec![];
90157
            for para_id in paras {
                // TODO: sweet O(n) db reads
58373
                if let Some(parathread_params) = ParathreadParams::<T>::get(para_id) {
1658
                    parathreads.push((para_id, parathread_params));
56883
                } else {
56715
                    parachains.push(para_id);
56715
                }
            }
31784
            SessionContainerChains {
31784
                parachains,
31784
                parathreads,
31784
            }
31784
        }
        #[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>(core::marker::PhantomData<T>);
impl<T> EnsureOriginWithArg<T::RuntimeOrigin, ParaId> for EnsureSignedByManager<T>
where
    T: Config,
{
    type Success = T::AccountId;
393
    fn try_origin(
393
        o: T::RuntimeOrigin,
393
        para_id: &ParaId,
393
    ) -> Result<Self::Success, T::RuntimeOrigin> {
225
        let signed_account =
393
            <frame_system::EnsureSigned<_> as EnsureOrigin<_>>::try_origin(o.clone())?;
225
        if !Pallet::<T>::is_para_manager(para_id, &signed_account) {
18
            return Err(frame_system::RawOrigin::Signed(signed_account).into());
207
        }
207

            
207
        Ok(signed_account)
393
    }
    #[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())
    }
}