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

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

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

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

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

            
109
1306
            let mut bounded_para_ids = BoundedVec::default();
110

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

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

            
127
463
                if let Some(parathread_params) = parathread_params {
128
40
                    <ParathreadParams<T>>::insert(para_id, parathread_params);
129
422
                }
130
            }
131

            
132
1304
            <RegisteredParaIds<T>>::put(bounded_para_ids);
133
1304
        }
134
    }
135

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

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

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

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

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

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

            
161
        type RelayStorageRootProvider: RelayStorageRootProvider;
162

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

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

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

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

            
173
        type RuntimeHoldReason: From<HoldReason>;
174

            
175
        type RegistrarHooks: RegistrarHooks;
176

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

            
185
        type WeightInfo: WeightInfo;
186

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
361
30956
            let buffered_paras = BufferedParasToDeregister::<T>::take();
362

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

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

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

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

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

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

            
470
            Ok(())
471
        }
472
    }
473

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

            
489
213
            Ok(())
490
        }
491

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

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

            
505
109
            Ok(())
506
        }
507

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

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

            
516
176
            Ok(())
517
        }
518

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

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

            
545
21
                Ok(())
546
23
            })?;
547

            
548
21
            Ok(())
549
        }
550

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

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

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

            
579
7
            Ok(())
580
        }
581

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

            
599
40
            Ok(())
600
        }
601

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

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

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

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

            
620
9
            Ok(())
621
        }
622

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

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

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

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

            
645
16
            Ok(())
646
        }
647

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

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

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

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

            
695
7
            Ok(())
696
        }
697

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
840
            deposit_info.creator
841
        }
842

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

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

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

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

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

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

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

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

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

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

            
909
260
            ParaManager::<T>::insert(para_id, account);
910
260

            
911
260
            Ok(())
912
276
        }
913

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

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

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

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

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

            
969
117
            Ok(())
970
119
        }
971

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

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

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

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

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

            
998
178
            Self::deposit_event(Event::ParaIdValidForCollating { para_id });
999
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
        }
137
        fn schedule_paused_parachain_change(
137
            updater: impl FnOnce(
137
                &mut BoundedVec<ParaId, T::MaxLengthParaIds>,
137
                &mut BoundedVec<ParaId, T::MaxLengthParaIds>,
137
            ) -> DispatchResult,
137
        ) -> DispatchResult {
137
            let mut pending_paras = PendingParaIds::<T>::get();
137
            let mut pending_paused = PendingPaused::<T>::get();
137
            // First, we need to decide what we should use as the base paras.
137
            let mut base_paras = pending_paras
137
                .last()
137
                .map(|(_, paras)| paras.clone())
137
                .unwrap_or_else(Self::registered_para_ids);
137
            let mut base_paused = pending_paused
137
                .last()
137
                .map(|(_, paras)| paras.clone())
137
                .unwrap_or_else(Self::paused);
137
            let old_base_paras = base_paras.clone();
137
            let old_base_paused = base_paused.clone();
137

            
137
            updater(&mut base_paras, &mut base_paused)?;
130
            if base_paras != old_base_paras {
127
                let new_paras = base_paras;
127
                let scheduled_session = Self::scheduled_session();
127
                if let Some(&mut (_, ref mut paras)) = pending_paras
127
                    .iter_mut()
127
                    .find(|&&mut (apply_at_session, _)| apply_at_session >= scheduled_session)
33
                {
33
                    *paras = new_paras;
98
                } else {
94
                    // We are scheduling a new parachains change for the scheduled session.
94
                    pending_paras.push((scheduled_session, new_paras));
94
                }
127
                <PendingParaIds<T>>::put(pending_paras);
3
            }
130
            if base_paused != old_base_paused {
31
                let new_paused = base_paused;
31
                let scheduled_session = Self::scheduled_session();
31
                if let Some(&mut (_, ref mut paras)) = pending_paused
31
                    .iter_mut()
31
                    .find(|&&mut (apply_at_session, _)| apply_at_session >= scheduled_session)
2
                {
2
                    *paras = new_paused;
29
                } else {
29
                    // We are scheduling a new parachains change for the scheduled session.
29
                    pending_paused.push((scheduled_session, new_paused));
29
                }
31
                <PendingPaused<T>>::put(pending_paused);
99
            }
130
            Ok(())
137
        }
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.
451
        fn scheduled_session() -> T::SessionIndex {
451
            T::CurrentSessionIndex::session_index().saturating_add(T::SessionDelay::get())
451
        }
        /// 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.
4801
        pub fn initializer_on_new_session(
4801
            session_index: &T::SessionIndex,
4801
        ) -> SessionChangeOutcome<T> {
4801
            let pending_paras = <PendingParaIds<T>>::get();
4801
            let prev_paras = RegisteredParaIds::<T>::get();
4801
            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
4223
                None
            };
4801
            let pending_paused = <PendingPaused<T>>::get();
4801
            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
                }
4766
            }
4801
            let pending_parathread_params = <PendingParathreadParams<T>>::get();
4801
            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
                }
4783
            }
4801
            let pending_to_remove = <PendingToRemove<T>>::get();
4801
            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
                }
4632
            }
4801
            SessionChangeOutcome {
4801
                prev_paras,
4801
                new_paras,
4801
            }
4801
        }
        /// 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
        }
65420
        pub fn registered_para_ids() -> BoundedVec<ParaId, T::MaxLengthParaIds> {
65420
            RegisteredParaIds::<T>::get()
65420
        }
31129
        pub fn pending_registered_para_ids(
31129
        ) -> Vec<(T::SessionIndex, BoundedVec<ParaId, T::MaxLengthParaIds>)> {
31129
            PendingParaIds::<T>::get()
31129
        }
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)
        }
139
        pub fn paused() -> BoundedVec<ParaId, T::MaxLengthParaIds> {
139
            Paused::<T>::get()
139
        }
        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;
34352
        fn current_container_chains() -> BoundedVec<ParaId, Self::MaxContainerChains> {
34352
            Self::registered_para_ids()
34352
        }
        #[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> {
31096
        fn session_container_chains(session_index: T::SessionIndex) -> SessionContainerChains {
31096
            let (past_and_present, _) = Pallet::<T>::pending_registered_para_ids()
31096
                .into_iter()
31096
                .partition::<Vec<_>, _>(|&(apply_at_session, _)| apply_at_session <= session_index);
31096
            let paras = if let Some(last) = past_and_present.last() {
353
                last.1.clone()
            } else {
30743
                Pallet::<T>::registered_para_ids()
            };
31096
            let mut parachains = vec![];
31096
            let mut parathreads = vec![];
88480
            for para_id in paras {
                // TODO: sweet O(n) db reads
57384
                if let Some(parathread_params) = ParathreadParams::<T>::get(para_id) {
1623
                    parathreads.push((para_id, parathread_params));
55929
                } else {
55761
                    parachains.push(para_id);
55761
                }
            }
31096
            SessionContainerChains {
31096
                parachains,
31096
                parathreads,
31096
            }
31096
        }
        #[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;
383
    fn try_origin(
383
        o: T::RuntimeOrigin,
383
        para_id: &ParaId,
383
    ) -> Result<Self::Success, T::RuntimeOrigin> {
223
        let signed_account =
383
            <frame_system::EnsureSigned<_> as EnsureOrigin<_>>::try_origin(o.clone())?;
223
        if !Pallet::<T>::is_para_manager(para_id, &signed_account) {
17
            return Err(frame_system::RawOrigin::Signed(signed_account).into());
206
        }
206

            
206
        Ok(signed_account)
383
    }
    #[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())
    }
}