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
#[frame_support::pallet]
76
pub mod pallet {
77
    use super::*;
78

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

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

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

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

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

            
133
1915
            <RegisteredParaIds<T>>::put(bounded_para_ids);
134
1915
        }
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
        /// Origin that is allowed to call maintenance extrinsics for container owner
141
        type RegistrarOrigin: EnsureOriginWithArg<Self::RuntimeOrigin, ParaId>;
142

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

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

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

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

            
159
        type RelayStorageRootProvider: RelayStorageRootProvider;
160

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

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

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

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

            
171
        type RuntimeHoldReason: From<HoldReason>;
172

            
173
        type RegistrarHooks: RegistrarHooks;
174

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

            
183
        type WeightInfo: WeightInfo;
184

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
289
    #[pallet::event]
290
    #[pallet::generate_deposit(pub(super) fn deposit_event)]
291
    pub enum Event<T: Config> {
292
        /// A new para id has been registered. [para_id]
293
        ParaIdRegistered {
294
            para_id: ParaId,
295
        },
296
        /// A para id has been deregistered. [para_id]
297
        ParaIdDeregistered {
298
            para_id: ParaId,
299
        },
300
        /// A new para id is now valid for collating. [para_id]
301
        ParaIdValidForCollating {
302
            para_id: ParaId,
303
        },
304
        /// A para id has been paused from collating.
305
        ParaIdPaused {
306
            para_id: ParaId,
307
        },
308
        /// A para id has been unpaused.
309
        ParaIdUnpaused {
310
            para_id: ParaId,
311
        },
312
        /// Parathread params changed
313
        ParathreadParamsChanged {
314
            para_id: ParaId,
315
        },
316
        /// Para manager has changed
317
        ParaManagerChanged {
318
            para_id: ParaId,
319
            manager_address: T::AccountId,
320
        },
321
        // Deposit has updated
322
        DepositUpdated {
323
            para_id: ParaId,
324
        },
325
    }
326

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

            
365
    #[pallet::composite_enum]
366
    pub enum HoldReason {
367
        RegistrarDeposit,
368
    }
369

            
370
    #[pallet::hooks]
371
    impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
372
8103
        fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
373
8103
            let mut weight = Weight::zero().saturating_add(T::DbWeight::get().reads_writes(1, 1));
374

            
375
8103
            let buffered_paras = BufferedParasToDeregister::<T>::take();
376

            
377
8126
            for para_id in buffered_paras {
378
23
                weight.saturating_accrue(T::InnerRegistrar::deregister_weight());
379
23
                // Deregister (in the relay context) each paraId present inside the buffer
380
23
                T::InnerRegistrar::deregister(para_id);
381
23
            }
382
8103
            weight
383
8103
        }
384

            
385
        #[cfg(feature = "try-runtime")]
386
        fn try_state(_n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
387
            use {alloc::collections::btree_set::BTreeSet, scale_info::prelude::format};
388
            // A para id can only be in 1 of [`RegisteredParaIds`, `PendingVerification`, `Paused`]
389
            // Get all those para ids and check for duplicates
390
            let mut para_ids: Vec<ParaId> = vec![];
391
            para_ids.extend(RegisteredParaIds::<T>::get());
392
            para_ids.extend(PendingVerification::<T>::iter_keys());
393
            para_ids.extend(Paused::<T>::get());
394
            para_ids.sort();
395
            para_ids.dedup_by(|a, b| {
396
                if a == b {
397
                    panic!("Duplicate para id: {}", u32::from(*a));
398
                } else {
399
                    false
400
                }
401
            });
402

            
403
            // All para ids have an entry in `ParaGenesisData`
404
            for para_id in &para_ids {
405
                assert!(
406
                    ParaGenesisData::<T>::contains_key(para_id),
407
                    "Para id {} missing genesis data",
408
                    u32::from(*para_id)
409
                );
410
            }
411

            
412
            // All entries in `RegistrarDeposit` and `ParaGenesisData` are in one of the other lists
413
            let mut para_id_set = BTreeSet::from_iter(para_ids.iter().cloned());
414
            // Also add the Pending lists here
415
            para_id_set.extend(
416
                PendingParaIds::<T>::get()
417
                    .into_iter()
418
                    .flat_map(|(_session_index, x)| x),
419
            );
420
            para_id_set.extend(
421
                PendingPaused::<T>::get()
422
                    .into_iter()
423
                    .flat_map(|(_session_index, x)| x),
424
            );
425
            para_id_set.extend(
426
                PendingToRemove::<T>::get()
427
                    .into_iter()
428
                    .flat_map(|(_session_index, x)| x),
429
            );
430
            let entries: Vec<_> = RegistrarDeposit::<T>::iter().map(|(k, _v)| k).collect();
431
            for para_id in entries {
432
                assert!(
433
                    para_id_set.contains(&para_id),
434
                    "Found RegistrarDeposit for unknown para id: {}",
435
                    u32::from(para_id)
436
                );
437
            }
438
            let entries: Vec<_> = ParaGenesisData::<T>::iter().map(|(k, _v)| k).collect();
439
            for para_id in entries {
440
                assert!(
441
                    para_id_set.contains(&para_id),
442
                    "Found ParaGenesisData for unknown para id: {}",
443
                    u32::from(para_id)
444
                );
445
            }
446

            
447
            // Sorted storage items are sorted
448
            fn assert_is_sorted_and_unique<T: Ord>(x: &[T], name: &str) {
449
                assert!(
450
                    x.windows(2).all(|w| w[0] < w[1]),
451
                    "sorted list not sorted or not unique: {}",
452
                    name,
453
                );
454
            }
455
            assert_is_sorted_and_unique(&RegisteredParaIds::<T>::get(), "RegisteredParaIds");
456
            assert_is_sorted_and_unique(&Paused::<T>::get(), "Paused");
457
            for (i, (_session_index, x)) in PendingParaIds::<T>::get().into_iter().enumerate() {
458
                assert_is_sorted_and_unique(&x, &format!("PendingParaIds[{}]", i));
459
            }
460
            for (i, (_session_index, x)) in PendingPaused::<T>::get().into_iter().enumerate() {
461
                assert_is_sorted_and_unique(&x, &format!("PendingPaused[{}]", i));
462
            }
463
            for (i, (_session_index, x)) in PendingToRemove::<T>::get().into_iter().enumerate() {
464
                assert_is_sorted_and_unique(&x, &format!("PendingToRemove[{}]", i));
465
            }
466

            
467
            // Pending storage items are sorted and session index is unique
468
            let pending: Vec<_> = PendingParaIds::<T>::get()
469
                .into_iter()
470
                .map(|(session_index, _x)| session_index)
471
                .collect();
472
            assert_is_sorted_and_unique(&pending, "PendingParaIds");
473
            let pending: Vec<_> = PendingPaused::<T>::get()
474
                .into_iter()
475
                .map(|(session_index, _x)| session_index)
476
                .collect();
477
            assert_is_sorted_and_unique(&pending, "PendingPaused");
478
            let pending: Vec<_> = PendingToRemove::<T>::get()
479
                .into_iter()
480
                .map(|(session_index, _x)| session_index)
481
                .collect();
482
            assert_is_sorted_and_unique(&pending, "PendingToRemove");
483

            
484
            Ok(())
485
        }
486
    }
487

            
488
    #[pallet::call]
489
    impl<T: Config> Pallet<T> {
490
        /// Register container-chain
491
        #[pallet::call_index(0)]
492
        #[pallet::weight(T::WeightInfo::register(genesis_data.encoded_size() as u32, genesis_data.storage.len() as u32))]
493
        pub fn register(
494
            origin: OriginFor<T>,
495
            para_id: ParaId,
496
            genesis_data: ContainerChainGenesisData,
497
            head_data: Option<HeadData>,
498
172
        ) -> DispatchResult {
499
172
            let account = ensure_signed(origin)?;
500
171
            Self::do_register(account, para_id, genesis_data, head_data)?;
501
161
            Self::deposit_event(Event::ParaIdRegistered { para_id });
502

            
503
161
            Ok(())
504
        }
505

            
506
        /// Deregister container-chain.
507
        ///
508
        /// If a container-chain is registered but not marked as valid_for_collating, this will remove it
509
        /// from `PendingVerification` as well.
510
        #[pallet::call_index(1)]
511
        #[pallet::weight(T::WeightInfo::deregister_immediate(
512
        ).max(T::WeightInfo::deregister_scheduled(
513
        )))]
514
52
        pub fn deregister(origin: OriginFor<T>, para_id: ParaId) -> DispatchResult {
515
52
            T::RegistrarOrigin::ensure_origin(origin, &para_id)?;
516

            
517
51
            Self::do_deregister(para_id)?;
518

            
519
49
            Ok(())
520
        }
521

            
522
        /// Mark container-chain valid for collating
523
        #[pallet::call_index(2)]
524
        #[pallet::weight(T::WeightInfo::mark_valid_for_collating())]
525
138
        pub fn mark_valid_for_collating(origin: OriginFor<T>, para_id: ParaId) -> DispatchResult {
526
138
            T::MarkValidForCollatingOrigin::ensure_origin(origin)?;
527

            
528
137
            Self::do_mark_valid_for_collating(para_id)?;
529

            
530
128
            Ok(())
531
        }
532

            
533
        /// Pause container-chain from collating. Does not remove its boot nodes nor its genesis config.
534
        /// Only container-chains that have been marked as valid_for_collating can be paused.
535
        #[pallet::call_index(4)]
536
        #[pallet::weight(T::WeightInfo::pause_container_chain())]
537
17
        pub fn pause_container_chain(origin: OriginFor<T>, para_id: ParaId) -> DispatchResult {
538
17
            T::RegistrarOrigin::ensure_origin(origin, &para_id)?;
539

            
540
12
            Self::schedule_paused_parachain_change(|para_ids, paused| {
541
12
                match paused.binary_search(&para_id) {
542
1
                    Ok(_) => return Err(Error::<T>::ParaIdAlreadyPaused.into()),
543
11
                    Err(index) => {
544
11
                        paused
545
11
                            .try_insert(index, para_id)
546
11
                            .map_err(|_e| Error::<T>::ParaIdListFull)?;
547
                    }
548
                }
549
11
                match para_ids.binary_search(&para_id) {
550
10
                    Ok(index) => {
551
10
                        para_ids.remove(index);
552
10
                    }
553
                    // We can only pause para ids that are marked as valid,
554
                    // otherwise unpausing them later would cause problems
555
1
                    Err(_) => return Err(Error::<T>::ParaIdNotRegistered.into()),
556
                }
557
10
                Self::deposit_event(Event::ParaIdPaused { para_id });
558

            
559
10
                Ok(())
560
12
            })?;
561

            
562
10
            Ok(())
563
        }
564

            
565
        /// Unpause container-chain.
566
        /// Only container-chains that have been paused can be unpaused.
567
        #[pallet::call_index(5)]
568
        #[pallet::weight(T::WeightInfo::unpause_container_chain())]
569
4
        pub fn unpause_container_chain(origin: OriginFor<T>, para_id: ParaId) -> DispatchResult {
570
4
            T::RegistrarOrigin::ensure_origin(origin, &para_id)?;
571

            
572
4
            Self::schedule_paused_parachain_change(|para_ids, paused| {
573
4
                match paused.binary_search(&para_id) {
574
1
                    Ok(index) => {
575
1
                        paused.remove(index);
576
1
                    }
577
3
                    Err(_) => return Err(Error::<T>::ParaIdNotPaused.into()),
578
                }
579
1
                match para_ids.binary_search(&para_id) {
580
                    // This Ok is unreachable, a para id cannot be in "RegisteredParaIds" and "Paused" at the same time
581
                    Ok(_) => return Err(Error::<T>::ParaIdAlreadyRegistered.into()),
582
1
                    Err(index) => {
583
1
                        para_ids
584
1
                            .try_insert(index, para_id)
585
1
                            .map_err(|_e| Error::<T>::ParaIdListFull)?;
586
                    }
587
                }
588
1
                Self::deposit_event(Event::ParaIdUnpaused { para_id });
589

            
590
1
                Ok(())
591
4
            })?;
592

            
593
1
            Ok(())
594
        }
595

            
596
        /// Register parathread
597
        #[pallet::call_index(6)]
598
        #[pallet::weight(T::WeightInfo::register_parathread(genesis_data.encoded_size() as u32, genesis_data.storage.len() as u32))]
599
        pub fn register_parathread(
600
            origin: OriginFor<T>,
601
            para_id: ParaId,
602
            slot_frequency: SlotFrequency,
603
            genesis_data: ContainerChainGenesisData,
604
            head_data: Option<HeadData>,
605
16
        ) -> DispatchResult {
606
16
            let account = ensure_signed(origin)?;
607
16
            Self::do_register(account, para_id, genesis_data, head_data)?;
608
            // Insert parathread params
609
16
            let params = ParathreadParamsTy { slot_frequency };
610
16
            ParathreadParams::<T>::insert(para_id, params);
611
16
            Self::deposit_event(Event::ParaIdRegistered { para_id });
612

            
613
16
            Ok(())
614
        }
615

            
616
        /// Change parathread params
617
        #[pallet::call_index(7)]
618
        #[pallet::weight(T::WeightInfo::set_parathread_params())]
619
        pub fn set_parathread_params(
620
            origin: OriginFor<T>,
621
            para_id: ParaId,
622
            slot_frequency: SlotFrequency,
623
4
        ) -> DispatchResult {
624
4
            T::RegistrarOrigin::ensure_origin(origin, &para_id)?;
625

            
626
4
            Self::schedule_parathread_params_change(para_id, |params| {
627
3
                params.slot_frequency = slot_frequency;
628

            
629
3
                Self::deposit_event(Event::ParathreadParamsChanged { para_id });
630

            
631
3
                Ok(())
632
3
            })?;
633

            
634
3
            Ok(())
635
        }
636

            
637
        #[pallet::call_index(8)]
638
        #[pallet::weight(T::WeightInfo::set_para_manager())]
639
        pub fn set_para_manager(
640
            origin: OriginFor<T>,
641
            para_id: ParaId,
642
            manager_address: T::AccountId,
643
4
        ) -> DispatchResult {
644
            // Allow root to force set para manager.
645
4
            if let Some(origin) = ensure_signed_or_root(origin)? {
646
                let creator =
647
                    RegistrarDeposit::<T>::get(para_id).map(|deposit_info| deposit_info.creator);
648

            
649
                ensure!(Some(origin) == creator, Error::<T>::NotParaCreator);
650
4
            }
651

            
652
4
            ParaManager::<T>::insert(para_id, manager_address.clone());
653

            
654
4
            Self::deposit_event(Event::<T>::ParaManagerChanged {
655
4
                para_id,
656
4
                manager_address,
657
4
            });
658

            
659
4
            Ok(())
660
        }
661

            
662
        /// Register parachain or parathread
663
        #[pallet::call_index(9)]
664
        #[pallet::weight(T::WeightInfo::register_with_relay_proof(genesis_data.encoded_size() as u32, genesis_data.storage.len() as u32))]
665
        pub fn register_with_relay_proof(
666
            origin: OriginFor<T>,
667
            para_id: ParaId,
668
            parathread_params: Option<ParathreadParamsTy>,
669
            relay_proof_block_number: u32,
670
            relay_storage_proof: sp_trie::StorageProof,
671
            manager_signature: cumulus_primitives_core::relay_chain::Signature,
672
            genesis_data: ContainerChainGenesisData,
673
            head_data: Option<HeadData>,
674
7
        ) -> DispatchResult {
675
7
            let account = T::RegisterWithRelayProofOrigin::ensure_origin(origin)?;
676
6
            let relay_storage_root =
677
7
                T::RelayStorageRootProvider::get_relay_storage_root(relay_proof_block_number)
678
7
                    .ok_or(Error::<T>::RelayStorageRootNotFound)?;
679
5
            let relay_state_proof =
680
6
                GenericStateProof::<cumulus_primitives_core::relay_chain::Block>::new(
681
6
                    relay_storage_root,
682
6
                    relay_storage_proof,
683
                )
684
6
                .map_err(|_| Error::<T>::InvalidRelayStorageProof)?;
685

            
686
5
            let bytes = para_id.twox_64_concat();
687
5
            let key = [REGISTRAR_PARAS_INDEX, bytes.as_slice()].concat();
688
5
            let relay_para_info = relay_state_proof
689
5
                .read_entry::<ParaInfo<
690
                    cumulus_primitives_core::relay_chain::AccountId,
691
                    cumulus_primitives_core::relay_chain::Balance,
692
5
                >>(key.as_slice(), None)
693
5
                .map_err(|_| Error::<T>::InvalidRelayStorageProof)?;
694
3
            let relay_manager = relay_para_info.manager;
695

            
696
            // Verify manager signature
697
3
            let signature_msg = Self::relay_signature_msg(para_id, &account, relay_storage_root);
698
3
            if !manager_signature.verify(&*signature_msg, &relay_manager) {
699
2
                return Err(Error::<T>::InvalidRelayManagerSignature.into());
700
1
            }
701

            
702
1
            Self::do_register(account, para_id, genesis_data, head_data)?;
703
            // Insert parathread params
704
1
            if let Some(parathread_params) = parathread_params {
705
                ParathreadParams::<T>::insert(para_id, parathread_params);
706
1
            }
707
1
            Self::deposit_event(Event::ParaIdRegistered { para_id });
708

            
709
1
            Ok(())
710
        }
711

            
712
        /// Deregister a parachain that no longer exists in the relay chain. The origin of this
713
        /// extrinsic will be rewarded with the parachain deposit.
714
        #[pallet::call_index(10)]
715
        #[pallet::weight(T::WeightInfo::deregister_with_relay_proof_immediate(
716
        ).max(T::WeightInfo::deregister_with_relay_proof_scheduled(
717
        )))]
718
        pub fn deregister_with_relay_proof(
719
            origin: OriginFor<T>,
720
            para_id: ParaId,
721
            relay_proof_block_number: u32,
722
            relay_storage_proof: sp_trie::StorageProof,
723
5
        ) -> DispatchResult {
724
5
            let account = T::RegisterWithRelayProofOrigin::ensure_origin(origin)?;
725

            
726
4
            let relay_storage_root =
727
5
                T::RelayStorageRootProvider::get_relay_storage_root(relay_proof_block_number)
728
5
                    .ok_or(Error::<T>::RelayStorageRootNotFound)?;
729
3
            let relay_state_proof =
730
4
                GenericStateProof::<cumulus_primitives_core::relay_chain::Block>::new(
731
4
                    relay_storage_root,
732
4
                    relay_storage_proof,
733
                )
734
4
                .map_err(|_| Error::<T>::InvalidRelayStorageProof)?;
735

            
736
3
            let bytes = para_id.twox_64_concat();
737
3
            let key = [REGISTRAR_PARAS_INDEX, bytes.as_slice()].concat();
738
            // TODO: we don't even need to decode the value, only check if it exists
739
            // Need to add exists_storage method to dancekit
740
3
            let relay_para_info = relay_state_proof
741
3
                .read_optional_entry::<ParaInfo<
742
                    cumulus_primitives_core::relay_chain::AccountId,
743
                    cumulus_primitives_core::relay_chain::Balance,
744
3
                >>(key.as_slice())
745
3
                .map_err(|_| Error::<T>::InvalidRelayStorageProof)?;
746
3
            if relay_para_info.is_some() {
747
1
                return Err(Error::<T>::ParaStillExistsInRelay.into());
748
2
            }
749

            
750
            // Take the deposit immediately and give it to origin account
751
2
            if let Some(asset_info) = RegistrarDeposit::<T>::take(para_id) {
752
2
                // Slash deposit from parachain creator
753
2
                // TODO: error handling
754
2
                let _ = T::Currency::transfer_on_hold(
755
2
                    &HoldReason::RegistrarDeposit.into(),
756
2
                    &asset_info.creator,
757
2
                    &account,
758
2
                    asset_info.deposit,
759
2
                    Precision::Exact,
760
2
                    Restriction::Free,
761
2
                    Fortitude::Force,
762
2
                );
763
2
            }
764

            
765
2
            Self::do_deregister(para_id)?;
766

            
767
2
            Ok(())
768
        }
769

            
770
        /// Recalculate and reconcile the reserved deposit for `para_id`.
771
        ///
772
        /// If the required amount differs from the currently held deposit,
773
        /// this extrinsic increases or releases the difference on the creator's account.
774
        #[pallet::call_index(11)]
775
        #[pallet::weight(T::WeightInfo::poke_deposit())]
776
8
        pub fn poke_deposit(origin: OriginFor<T>, para_id: ParaId) -> DispatchResult {
777
8
            let who = ensure_signed(origin)?;
778

            
779
            // Mutate the deposit entry in-place
780
7
            RegistrarDeposit::<T>::try_mutate_exists(para_id, |maybe_info| -> DispatchResult {
781
7
                let info = maybe_info.as_mut().ok_or(Error::<T>::ParaIdNotRegistered)?;
782

            
783
                // Only the original creator of the para can poke their deposit.
784
6
                ensure!(info.creator == who, Error::<T>::NotParaCreator);
785

            
786
                // Retrieve the genesis data and calculate the cost based on its encoded size.
787
5
                let genesis =
788
5
                    ParaGenesisData::<T>::get(para_id).ok_or(Error::<T>::ParaIdNotRegistered)?;
789
5
                let required = Self::get_genesis_cost(genesis.encoded_size());
790

            
791
5
                let current = info.deposit;
792

            
793
                // If the current deposit already matches the required one, do nothing.
794
5
                if required == current {
795
2
                    return Ok(());
796
3
                }
797

            
798
                // Adjust the held amount depending on whether we need to increase or decrease
799
3
                if required > current {
800
                    // The deposit must increase
801
1
                    let delta = required.saturating_sub(current);
802

            
803
                    // Attempt to hold the additional amount from the creator's balance.
804
1
                    T::Currency::hold(&HoldReason::RegistrarDeposit.into(), &info.creator, delta)
805
1
                        .map_err(|_| Error::<T>::NotSufficientDeposit)?;
806
                } else {
807
                    // The deposit must decrease
808
2
                    let delta = current.saturating_sub(required);
809

            
810
                    // Release the exact delta from the hold.
811
2
                    T::Currency::release(
812
2
                        &HoldReason::RegistrarDeposit.into(),
813
2
                        &info.creator,
814
2
                        delta,
815
2
                        Precision::Exact,
816
                    )?;
817
                }
818

            
819
                // Update the stored deposit value
820
3
                info.deposit = required;
821

            
822
3
                Self::deposit_event(Event::DepositUpdated { para_id });
823

            
824
3
                Ok(())
825
7
            })
826
        }
827
    }
828

            
829
    pub struct SessionChangeOutcome<T: Config> {
830
        /// Previously active parachains.
831
        pub prev_paras: BoundedVec<ParaId, T::MaxLengthParaIds>,
832
        /// If new parachains have been applied in the new session, this is the new  list.
833
        pub new_paras: Option<BoundedVec<ParaId, T::MaxLengthParaIds>>,
834
    }
835

            
836
    impl<T: Config> Pallet<T> {
837
156
        pub fn is_para_manager(para_id: &ParaId, account: &T::AccountId) -> bool {
838
            // This check will only pass if both are true:
839
            // * The para_id has a deposit in pallet_registrar
840
            // * The signed_account is the para manager (or creator if None)
841
156
            if let Some(manager) = ParaManager::<T>::get(para_id) {
842
152
                manager == *account
843
            } else {
844
4
                RegistrarDeposit::<T>::get(para_id)
845
4
                    .map(|deposit_info| deposit_info.creator)
846
4
                    .as_ref()
847
4
                    == Some(account)
848
            }
849
156
        }
850

            
851
        #[cfg(feature = "runtime-benchmarks")]
852
        pub fn benchmarks_get_or_create_para_manager(para_id: &ParaId) -> T::AccountId {
853
            use {
854
                frame_benchmarking::account,
855
                frame_support::{assert_ok, dispatch::RawOrigin},
856
            };
857

            
858
            let mut storage = BoundedVec::try_from(vec![]).unwrap();
859
            storage
860
                .try_push((b":code".to_vec(), vec![1; 10]).into())
861
                .unwrap();
862
            let genesis_data = ContainerChainGenesisData {
863
                storage,
864
                name: Default::default(),
865
                id: Default::default(),
866
                fork_id: Default::default(),
867
                extensions: Default::default(),
868
                properties: Default::default(),
869
            };
870

            
871
            // Return container chain manager, or register container chain as ALICE if it does not exist
872
            if !ParaGenesisData::<T>::contains_key(para_id) {
873
                // Register as a new user
874

            
875
                /// Create a funded user.
876
                /// Used for generating the necessary amount for registering
877
                fn create_funded_user<T: Config>(
878
                    string: &'static str,
879
                    n: u32,
880
                    total: DepositBalanceOf<T>,
881
                ) -> (T::AccountId, DepositBalanceOf<T>) {
882
                    const SEED: u32 = 0;
883
                    let user = account(string, n, SEED);
884
                    assert_ok!(T::Currency::mint_into(&user, total));
885
                    (user, total)
886
                }
887

            
888
                let deposit = Self::get_genesis_cost(genesis_data.encoded_size());
889
                let new_balance = T::Currency::minimum_balance()
890
                    .saturating_mul(100_000_000u32.into())
891
                    .saturating_add(deposit);
892
                let account = create_funded_user::<T>("caller", 1000, new_balance).0;
893
                T::InnerRegistrar::prepare_chain_registration(*para_id, account.clone());
894
                let origin = RawOrigin::Signed(account);
895

            
896
                assert_ok!(Self::register(
897
                    origin.into(),
898
                    *para_id,
899
                    genesis_data.clone(),
900
                    T::InnerRegistrar::bench_head_data(),
901
                ));
902
            }
903

            
904
            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");
905

            
906
            let deposit = Self::get_genesis_cost(genesis_data.encoded_size());
907
            // Fund deposit creator, just in case it is not a new account
908
            let new_balance = (T::Currency::minimum_balance().saturating_add(deposit))
909
                .saturating_mul(2u32.into());
910
            assert_ok!(T::Currency::mint_into(&deposit_info.creator, new_balance));
911

            
912
            deposit_info.creator
913
        }
914

            
915
193
        pub fn get_genesis_cost(size: usize) -> <T::Currency as Inspect<T::AccountId>>::Balance {
916
193
            T::DataDepositPerByte::get().saturating_mul((size as u32).into())
917
193
        }
918

            
919
188
        fn do_register(
920
188
            account: T::AccountId,
921
188
            para_id: ParaId,
922
188
            genesis_data: ContainerChainGenesisData,
923
188
            head_data: Option<HeadData>,
924
188
        ) -> DispatchResult {
925
            // The actual registration takes place 2 sessions after the call to
926
            // `mark_valid_for_collating`, but the genesis data is inserted now.
927
            // This is because collators should be able to start syncing the new container chain
928
            // before the first block is mined. However, we could store the genesis data in a
929
            // different key, like PendingParaGenesisData.
930
            // TODO: for benchmarks, this call to .encoded_size is O(n) with respect to the number
931
            // of key-values in `genesis_data.storage`, even if those key-values are empty. And we
932
            // won't detect that the size is too big until after iterating over all of them, so the
933
            // limit in that case would be the transaction size.
934
188
            let genesis_data_size = genesis_data.encoded_size();
935

            
936
188
            let deposit = Self::get_genesis_cost(genesis_data_size);
937
            // Verify we can hold
938
188
            if !T::Currency::can_hold(&HoldReason::RegistrarDeposit.into(), &account, deposit) {
939
                return Err(Error::<T>::NotSufficientDeposit.into());
940
188
            }
941

            
942
            // Check if the para id is already registered by looking at the genesis data
943
188
            if ParaGenesisData::<T>::contains_key(para_id) {
944
5
                return Err(Error::<T>::ParaIdAlreadyRegistered.into());
945
183
            }
946

            
947
            // Check if the para id is already in PendingVerification (unreachable)
948
183
            let is_pending_verification = PendingVerification::<T>::take(para_id).is_some();
949
183
            if is_pending_verification {
950
                return Err(Error::<T>::ParaIdAlreadyRegistered.into());
951
183
            }
952

            
953
            // Insert para id into PendingVerification
954
183
            PendingVerification::<T>::insert(para_id, ());
955

            
956
183
            if genesis_data_size > T::MaxGenesisDataSize::get() as usize {
957
1
                return Err(Error::<T>::GenesisDataTooBig.into());
958
182
            }
959

            
960
            // Hold the deposit, we verified we can do this
961
182
            T::Currency::hold(&HoldReason::RegistrarDeposit.into(), &account, deposit)?;
962

            
963
            // Register the paraId also in the relay context (if any).
964
182
            T::InnerRegistrar::register(
965
182
                account.clone(),
966
182
                para_id,
967
182
                &genesis_data.storage,
968
182
                head_data,
969
4
            )?;
970

            
971
            // Update DepositInfo
972
178
            RegistrarDeposit::<T>::insert(
973
178
                para_id,
974
178
                DepositInfo {
975
178
                    creator: account.clone(),
976
178
                    deposit,
977
178
                },
978
            );
979
178
            ParaGenesisData::<T>::insert(para_id, genesis_data);
980

            
981
178
            ParaManager::<T>::insert(para_id, account);
982

            
983
178
            Ok(())
984
188
        }
985

            
986
53
        fn do_deregister(para_id: ParaId) -> DispatchResult {
987
            // Check if the para id is in "PendingVerification".
988
            // This is a special case because then we can remove it immediately, instead of waiting 2 sessions.
989
53
            let is_pending_verification = PendingVerification::<T>::take(para_id).is_some();
990
53
            if is_pending_verification {
991
9
                Self::deposit_event(Event::ParaIdDeregistered { para_id });
992
                // Cleanup immediately
993
9
                Self::cleanup_deregistered_para_id(para_id);
994
9
                BufferedParasToDeregister::<T>::try_append(para_id).map_err(|_e| {
995
                    DispatchError::Other(
996
                        "Failed to add paraId to deregistration list: buffer is full",
997
                    )
998
                })?;
999
            } else {
44
                Self::schedule_paused_parachain_change(|para_ids, paused| {
                    // We have to find out where, in the sorted vec the para id is, if anywhere.
44
                    match para_ids.binary_search(&para_id) {
39
                        Ok(index) => {
39
                            para_ids.remove(index);
39
                        }
                        Err(_) => {
                            // If the para id is not registered, it may be paused. In that case, remove it from there
5
                            match paused.binary_search(&para_id) {
3
                                Ok(index) => {
3
                                    paused.remove(index);
3
                                }
                                Err(_) => {
2
                                    return Err(Error::<T>::ParaIdNotRegistered.into());
                                }
                            }
                        }
                    }
42
                    Ok(())
44
                })?;
                // Mark this para id for cleanup later
42
                Self::schedule_parachain_cleanup(para_id)?;
                // If we have InnerRegistrar set to a relay context (like Dancelight),
                // we first need to downgrade the paraId (if it was a parachain before)
                // and convert it to a parathread before deregistering it. Otherwise
                // the deregistration process will fail in the scheduled session.
                //
                // We only downgrade if the paraId is a parachain in the context of
                // this pallet.
42
                if ParathreadParams::<T>::get(para_id).is_none() {
40
                    T::InnerRegistrar::schedule_para_downgrade(para_id)?;
2
                }
42
                Self::deposit_event(Event::ParaIdDeregistered { para_id });
            }
51
            Ok(())
53
        }
137
        fn do_mark_valid_for_collating(para_id: ParaId) -> DispatchResult {
137
            let is_pending_verification = PendingVerification::<T>::take(para_id).is_some();
137
            if !is_pending_verification {
3
                return Err(Error::<T>::ParaIdNotInPendingVerification.into());
134
            }
134
            Self::schedule_parachain_change(|para_ids| {
                // We don't want to add duplicate para ids, so we check whether the potential new
                // para id is already present in the list. Because the list is always ordered, we can
                // leverage the binary search which makes this check O(log n).
134
                match para_ids.binary_search(&para_id) {
                    // This Ok is unreachable
                    Ok(_) => return Err(Error::<T>::ParaIdAlreadyRegistered.into()),
134
                    Err(index) => {
134
                        para_ids
134
                            .try_insert(index, para_id)
134
                            .map_err(|_e| Error::<T>::ParaIdListFull)?;
                    }
                }
134
                Ok(())
134
            })?;
134
            T::RegistrarHooks::check_valid_for_collating(para_id)?;
130
            Self::deposit_event(Event::ParaIdValidForCollating { para_id });
130
            T::RegistrarHooks::para_marked_valid_for_collating(para_id);
            // If we execute mark_valid_for_collating, we automatically upgrade
            // the paraId to a parachain (in the relay context) at the end of the execution.
            //
            // We only upgrade if the paraId is a parachain in the context of
            // this pallet.
130
            if ParathreadParams::<T>::get(para_id).is_none() {
114
                T::InnerRegistrar::schedule_para_upgrade(para_id)?;
16
            }
128
            Ok(())
137
        }
        /// 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.
10
        pub fn relay_signature_msg(
10
            para_id: ParaId,
10
            tanssi_account: &T::AccountId,
10
            relay_storage_root: H256,
10
        ) -> Vec<u8> {
10
            (para_id, tanssi_account, relay_storage_root).encode()
10
        }
134
        fn schedule_parachain_change(
134
            updater: impl FnOnce(&mut BoundedVec<ParaId, T::MaxLengthParaIds>) -> DispatchResult,
134
        ) -> DispatchResult {
134
            let mut pending_paras = PendingParaIds::<T>::get();
            // First, we need to decide what we should use as the base paras.
134
            let mut base_paras = pending_paras
134
                .last()
134
                .map(|(_, paras)| paras.clone())
134
                .unwrap_or_else(Self::registered_para_ids);
134
            updater(&mut base_paras)?;
134
            let new_paras = base_paras;
134
            let scheduled_session = Self::scheduled_session();
134
            if let Some(&mut (_, ref mut paras)) = pending_paras
134
                .iter_mut()
134
                .find(|&&mut (apply_at_session, _)| apply_at_session >= scheduled_session)
8
            {
8
                *paras = new_paras;
126
            } else {
126
                // We are scheduling a new parachains change for the scheduled session.
126
                pending_paras.push((scheduled_session, new_paras));
126
            }
134
            <PendingParaIds<T>>::put(pending_paras);
134
            Ok(())
134
        }
60
        fn schedule_paused_parachain_change(
60
            updater: impl FnOnce(
60
                &mut BoundedVec<ParaId, T::MaxLengthParaIds>,
60
                &mut BoundedVec<ParaId, T::MaxLengthParaIds>,
60
            ) -> DispatchResult,
60
        ) -> DispatchResult {
60
            let mut pending_paras = PendingParaIds::<T>::get();
60
            let mut pending_paused = PendingPaused::<T>::get();
            // First, we need to decide what we should use as the base paras.
60
            let mut base_paras = pending_paras
60
                .last()
60
                .map(|(_, paras)| paras.clone())
60
                .unwrap_or_else(Self::registered_para_ids);
60
            let mut base_paused = pending_paused
60
                .last()
60
                .map(|(_, paras)| paras.clone())
60
                .unwrap_or_else(Self::paused);
60
            let old_base_paras = base_paras.clone();
60
            let old_base_paused = base_paused.clone();
60
            updater(&mut base_paras, &mut base_paused)?;
53
            if base_paras != old_base_paras {
50
                let new_paras = base_paras;
50
                let scheduled_session = Self::scheduled_session();
50
                if let Some(&mut (_, ref mut paras)) = pending_paras
50
                    .iter_mut()
50
                    .find(|&&mut (apply_at_session, _)| apply_at_session >= scheduled_session)
15
                {
15
                    *paras = new_paras;
39
                } else {
35
                    // We are scheduling a new parachains change for the scheduled session.
35
                    pending_paras.push((scheduled_session, new_paras));
35
                }
50
                <PendingParaIds<T>>::put(pending_paras);
3
            }
53
            if base_paused != old_base_paused {
14
                let new_paused = base_paused;
14
                let scheduled_session = Self::scheduled_session();
14
                if let Some(&mut (_, ref mut paras)) = pending_paused
14
                    .iter_mut()
14
                    .find(|&&mut (apply_at_session, _)| apply_at_session >= scheduled_session)
2
                {
2
                    *paras = new_paused;
12
                } else {
12
                    // We are scheduling a new parachains change for the scheduled session.
12
                    pending_paused.push((scheduled_session, new_paused));
12
                }
14
                <PendingPaused<T>>::put(pending_paused);
39
            }
53
            Ok(())
60
        }
4
        fn schedule_parathread_params_change(
4
            para_id: ParaId,
4
            updater: impl FnOnce(&mut ParathreadParamsTy) -> DispatchResult,
4
        ) -> DispatchResult {
            // Check that the para id is a parathread by reading the old params
4
            let params = match ParathreadParams::<T>::get(para_id) {
3
                Some(x) => x,
                None => {
1
                    return Err(Error::<T>::NotAParathread.into());
                }
            };
3
            let mut pending_params = PendingParathreadParams::<T>::get();
            // First, we need to decide what we should use as the base params.
3
            let mut base_params = pending_params
3
                .last()
3
                .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,
                    }
                })
3
                .unwrap_or(params);
3
            updater(&mut base_params)?;
3
            let new_params = base_params;
3
            let scheduled_session = Self::scheduled_session();
3
            if let Some(&mut (_, ref mut para_id_params)) = pending_params
3
                .iter_mut()
3
                .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)?;
                    }
                }
3
            } else {
3
                // We are scheduling a new parathread params change for the scheduled session.
3
                pending_params.push((
3
                    scheduled_session,
3
                    BoundedVec::truncate_from(vec![(para_id, new_params)]),
3
                ));
3
            }
3
            <PendingParathreadParams<T>>::put(pending_params);
3
            Ok(())
4
        }
        /// Return the session index that should be used for any future scheduled changes.
243
        fn scheduled_session() -> T::SessionIndex {
243
            T::CurrentSessionIndex::session_index().saturating_add(T::SessionDelay::get())
243
        }
        /// 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.
6144
        pub fn initializer_on_new_session(
6144
            session_index: &T::SessionIndex,
6144
        ) -> SessionChangeOutcome<T> {
6144
            let pending_paras = <PendingParaIds<T>>::get();
6144
            let prev_paras = RegisteredParaIds::<T>::get();
6144
            let new_paras = if !pending_paras.is_empty() {
839
                let (mut past_and_present, future) = pending_paras
839
                    .into_iter()
852
                    .partition::<Vec<_>, _>(|&(apply_at_session, _)| {
852
                        apply_at_session <= *session_index
852
                    });
839
                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",
                    );
839
                }
839
                let new_paras = past_and_present.pop().map(|(_, paras)| paras);
839
                if let Some(ref new_paras) = new_paras {
426
                    // Apply the new parachain list.
426
                    RegisteredParaIds::<T>::put(new_paras);
426
                    <PendingParaIds<T>>::put(future);
426
                }
839
                new_paras
            } else {
                // pending_paras.is_empty, so parachain list did not change
5305
                None
            };
6144
            let pending_paused = <PendingPaused<T>>::get();
6144
            if !pending_paused.is_empty() {
11
                let (mut past_and_present, future) = pending_paused
11
                    .into_iter()
12
                    .partition::<Vec<_>, _>(|&(apply_at_session, _)| {
12
                        apply_at_session <= *session_index
12
                    });
11
                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",
                    );
11
                }
11
                let new_paused = past_and_present.pop().map(|(_, paras)| paras);
11
                if let Some(ref new_paused) = new_paused {
6
                    // Apply the new parachain list.
6
                    Paused::<T>::put(new_paused);
6
                    <PendingPaused<T>>::put(future);
6
                }
6133
            }
6144
            let pending_parathread_params = <PendingParathreadParams<T>>::get();
6144
            if !pending_parathread_params.is_empty() {
6
                let (mut past_and_present, future) = pending_parathread_params
6
                    .into_iter()
6
                    .partition::<Vec<_>, _>(|&(apply_at_session, _)| {
6
                        apply_at_session <= *session_index
6
                    });
6
                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",
                    );
6
                }
6
                let new_params = past_and_present.pop().map(|(_, params)| params);
6
                if let Some(ref new_params) = new_params {
5
                    for (para_id, params) in new_params {
2
                        <ParathreadParams<T>>::insert(para_id, params);
2
                    }
3
                    <PendingParathreadParams<T>>::put(future);
3
                }
6138
            }
6144
            let pending_to_remove = <PendingToRemove<T>>::get();
6144
            if !pending_to_remove.is_empty() {
163
                let (past_and_present, future) =
163
                    pending_to_remove.into_iter().partition::<Vec<_>, _>(
164
                        |&(apply_at_session, _)| apply_at_session <= *session_index,
                    );
163
                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.
82
                    let mut removed_para_ids = BTreeSet::new();
164
                    for (_, new_paras) in &past_and_present {
167
                        for para_id in new_paras {
85
                            Self::cleanup_deregistered_para_id(*para_id);
85
                            removed_para_ids.insert(*para_id);
85
                            if let Err(id) = BufferedParasToDeregister::<T>::try_append(*para_id) {
                                log::error!(
                                    target: LOG_TARGET,
                                    "Failed to add paraId {:?} to deregistration list",
                                    id
                                );
85
                            }
                        }
                    }
                    // Also need to remove PendingParams to avoid setting params for a para id that does not exist
82
                    let mut pending_parathread_params = <PendingParathreadParams<T>>::get();
83
                    for (_, new_params) in &mut pending_parathread_params {
1
                        new_params.retain(|(para_id, _params)| {
                            // Retain para ids that are not in the list of removed para ids
1
                            !removed_para_ids.contains(para_id)
1
                        });
                    }
82
                    <PendingParathreadParams<T>>::put(pending_parathread_params);
82
                    <PendingToRemove<T>>::put(future);
81
                }
5981
            }
6144
            SessionChangeOutcome {
6144
                prev_paras,
6144
                new_paras,
6144
            }
6144
        }
        /// Remove all para id storage in this pallet,
        /// and execute para_deregistered hook to clean up other pallets as well
94
        fn cleanup_deregistered_para_id(para_id: ParaId) {
94
            ParaGenesisData::<T>::remove(para_id);
94
            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
94
            if let Some(asset_info) = RegistrarDeposit::<T>::take(para_id) {
41
                // Release hold
41
                let _ = T::Currency::release(
41
                    &HoldReason::RegistrarDeposit.into(),
41
                    &asset_info.creator,
41
                    asset_info.deposit,
41
                    Precision::Exact,
41
                );
72
            }
94
            ParaManager::<T>::remove(para_id);
94
            T::RegistrarHooks::para_deregistered(para_id);
94
        }
42
        fn schedule_parachain_cleanup(para_id: ParaId) -> DispatchResult {
42
            let scheduled_session = Self::scheduled_session();
42
            let mut pending_paras = PendingToRemove::<T>::get();
            // First, we need to decide what we should use as the base paras.
42
            let base_paras = match pending_paras
42
                .binary_search_by_key(&scheduled_session, |(session, _paras)| *session)
            {
3
                Ok(i) => &mut pending_paras[i].1,
39
                Err(i) => {
39
                    pending_paras.insert(i, (scheduled_session, Default::default()));
39
                    &mut pending_paras[i].1
                }
            };
            // Add the para_id to the entry for the scheduled session.
42
            match base_paras.binary_search(&para_id) {
                // This Ok is unreachable
                Ok(_) => return Err(Error::<T>::ParaIdAlreadyDeregistered.into()),
42
                Err(index) => {
42
                    base_paras
42
                        .try_insert(index, para_id)
42
                        .map_err(|_e| Error::<T>::ParaIdListFull)?;
                }
            }
            // Save the updated list of pending parachains for removal.
42
            <PendingToRemove<T>>::put(pending_paras);
42
            Ok(())
42
        }
22841
        pub fn registered_para_ids() -> BoundedVec<ParaId, T::MaxLengthParaIds> {
22841
            RegisteredParaIds::<T>::get()
22841
        }
9221
        pub fn pending_registered_para_ids(
9221
        ) -> Vec<(T::SessionIndex, BoundedVec<ParaId, T::MaxLengthParaIds>)> {
9221
            PendingParaIds::<T>::get()
9221
        }
160
        pub fn para_genesis_data(para_id: ParaId) -> Option<ContainerChainGenesisData> {
160
            ParaGenesisData::<T>::get(para_id)
160
        }
        pub fn pending_verification(para_id: ParaId) -> Option<()> {
            PendingVerification::<T>::get(para_id)
        }
62
        pub fn paused() -> BoundedVec<ParaId, T::MaxLengthParaIds> {
62
            Paused::<T>::get()
62
        }
        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()
        }
127
        pub fn parathread_params(para_id: ParaId) -> Option<ParathreadParamsTy> {
127
            ParathreadParams::<T>::get(para_id)
127
        }
        pub fn pending_parathread_params() -> Vec<(
            T::SessionIndex,
            BoundedVec<(ParaId, ParathreadParamsTy), T::MaxLengthParaIds>,
        )> {
            PendingParathreadParams::<T>::get()
        }
18
        pub fn registrar_deposit(para_id: ParaId) -> Option<DepositInfo<T>> {
18
            RegistrarDeposit::<T>::get(para_id)
18
        }
    }
    impl<T: Config> GetCurrentContainerChains for Pallet<T> {
        type MaxContainerChains = T::MaxLengthParaIds;
13821
        fn current_container_chains() -> BoundedVec<ParaId, Self::MaxContainerChains> {
13821
            Self::registered_para_ids()
13821
        }
        #[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> {
9188
        fn session_container_chains(session_index: T::SessionIndex) -> SessionContainerChains {
9188
            let (past_and_present, _) = Pallet::<T>::pending_registered_para_ids()
9188
                .into_iter()
9188
                .partition::<Vec<_>, _>(|&(apply_at_session, _)| apply_at_session <= session_index);
9188
            let paras = if let Some(last) = past_and_present.last() {
398
                last.1.clone()
            } else {
8790
                Pallet::<T>::registered_para_ids()
            };
9188
            let mut parachains = vec![];
9188
            let mut parathreads = vec![];
19276
            for para_id in paras {
                // TODO: sweet O(n) db reads
10088
                if let Some(parathread_params) = ParathreadParams::<T>::get(para_id) {
509
                    parathreads.push((para_id, parathread_params));
9579
                } else {
9579
                    parachains.push(para_id);
9579
                }
            }
9188
            SessionContainerChains {
9188
                parachains,
9188
                parathreads,
9188
            }
9188
        }
        #[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;
228
    fn try_origin(
228
        o: T::RuntimeOrigin,
228
        para_id: &ParaId,
228
    ) -> Result<Self::Success, T::RuntimeOrigin> {
156
        let signed_account =
228
            <frame_system::EnsureSigned<_> as EnsureOrigin<_>>::try_origin(o.clone())?;
156
        if !Pallet::<T>::is_para_manager(para_id, &signed_account) {
6
            return Err(frame_system::RawOrigin::Signed(signed_account).into());
150
        }
150
        Ok(signed_account)
228
    }
    #[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())
    }
}