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
        GetSessionContainerChains, GetSessionIndex, ParaId, ParathreadParams as ParathreadParamsTy,
70
        RegistrarHandler, RelayStorageRootProvider, SessionContainerChains, SlotFrequency,
71
    },
72
};
73

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

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

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

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

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

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

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

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

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

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

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

            
158
        type RelayStorageRootProvider: RelayStorageRootProvider;
159

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

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

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

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

            
170
        type RuntimeHoldReason: From<HoldReason>;
171

            
172
        type RegistrarHooks: RegistrarHooks;
173

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

            
182
        type WeightInfo: WeightInfo;
183

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
374
8658
            let buffered_paras = BufferedParasToDeregister::<T>::take();
375

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

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

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

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

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

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

            
483
            Ok(())
484
        }
485
    }
486

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

            
502
161
            Ok(())
503
        }
504

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

            
516
53
            Self::do_deregister(para_id)?;
517

            
518
51
            Ok(())
519
        }
520

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

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

            
529
128
            Ok(())
530
        }
531

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

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

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

            
561
10
            Ok(())
562
        }
563

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

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

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

            
592
1
            Ok(())
593
        }
594

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

            
612
16
            Ok(())
613
        }
614

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

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

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

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

            
633
3
            Ok(())
634
        }
635

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

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

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

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

            
658
4
            Ok(())
659
        }
660

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

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

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

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

            
708
1
            Ok(())
709
        }
710

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

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

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

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

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

            
766
2
            Ok(())
767
        }
768

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

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

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

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

            
790
5
                let current = info.deposit;
791

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
911
            deposit_info.creator
912
        }
913

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

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

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

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

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

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

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

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

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

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

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

            
982
178
            Ok(())
983
188
        }
984

            
985
55
        fn do_deregister(para_id: ParaId) -> DispatchResult {
986
            // Check if the para id is in "PendingVerification".
987
            // This is a special case because then we can remove it immediately, instead of waiting 2 sessions.
988
55
            let is_pending_verification = PendingVerification::<T>::take(para_id).is_some();
989
55
            if is_pending_verification {
990
9
                Self::deposit_event(Event::ParaIdDeregistered { para_id });
991
                // Cleanup immediately
992
9
                Self::cleanup_deregistered_para_id(para_id);
993
9
                BufferedParasToDeregister::<T>::try_append(para_id).map_err(|_e| {
994
                    DispatchError::Other(
995
                        "Failed to add paraId to deregistration list: buffer is full",
996
                    )
997
                })?;
998
            } else {
999
46
                Self::schedule_paused_parachain_change(|para_ids, paused| {
                    // We have to find out where, in the sorted vec the para id is, if anywhere.
46
                    match para_ids.binary_search(&para_id) {
41
                        Ok(index) => {
41
                            para_ids.remove(index);
41
                        }
                        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());
                                }
                            }
                        }
                    }
44
                    Ok(())
46
                })?;
                // Mark this para id for cleanup later
44
                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.
44
                if ParathreadParams::<T>::get(para_id).is_none() {
42
                    T::InnerRegistrar::schedule_para_downgrade(para_id)?;
2
                }
44
                Self::deposit_event(Event::ParaIdDeregistered { para_id });
            }
53
            Ok(())
55
        }
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
        }
62
        fn schedule_paused_parachain_change(
62
            updater: impl FnOnce(
62
                &mut BoundedVec<ParaId, T::MaxLengthParaIds>,
62
                &mut BoundedVec<ParaId, T::MaxLengthParaIds>,
62
            ) -> DispatchResult,
62
        ) -> DispatchResult {
62
            let mut pending_paras = PendingParaIds::<T>::get();
62
            let mut pending_paused = PendingPaused::<T>::get();
            // First, we need to decide what we should use as the base paras.
62
            let mut base_paras = pending_paras
62
                .last()
62
                .map(|(_, paras)| paras.clone())
62
                .unwrap_or_else(Self::registered_para_ids);
62
            let mut base_paused = pending_paused
62
                .last()
62
                .map(|(_, paras)| paras.clone())
62
                .unwrap_or_else(Self::paused);
62
            let old_base_paras = base_paras.clone();
62
            let old_base_paused = base_paused.clone();
62
            updater(&mut base_paras, &mut base_paused)?;
55
            if base_paras != old_base_paras {
52
                let new_paras = base_paras;
52
                let scheduled_session = Self::scheduled_session();
52
                if let Some(&mut (_, ref mut paras)) = pending_paras
52
                    .iter_mut()
52
                    .find(|&&mut (apply_at_session, _)| apply_at_session >= scheduled_session)
15
                {
15
                    *paras = new_paras;
41
                } else {
37
                    // We are scheduling a new parachains change for the scheduled session.
37
                    pending_paras.push((scheduled_session, new_paras));
37
                }
52
                <PendingParaIds<T>>::put(pending_paras);
3
            }
55
            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);
41
            }
55
            Ok(())
62
        }
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.
247
        fn scheduled_session() -> T::SessionIndex {
247
            T::CurrentSessionIndex::session_index().saturating_add(T::SessionDelay::get())
247
        }
        /// 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.
6533
        pub fn initializer_on_new_session(
6533
            session_index: &T::SessionIndex,
6533
        ) -> SessionChangeOutcome<T> {
6533
            let pending_paras = <PendingParaIds<T>>::get();
6533
            let prev_paras = RegisteredParaIds::<T>::get();
6533
            let new_paras = if !pending_paras.is_empty() {
843
                let (mut past_and_present, future) = pending_paras
843
                    .into_iter()
856
                    .partition::<Vec<_>, _>(|&(apply_at_session, _)| {
856
                        apply_at_session <= *session_index
856
                    });
843
                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",
                    );
843
                }
843
                let new_paras = past_and_present.pop().map(|(_, paras)| paras);
843
                if let Some(ref new_paras) = new_paras {
428
                    // Apply the new parachain list.
428
                    RegisteredParaIds::<T>::put(new_paras);
428
                    <PendingParaIds<T>>::put(future);
428
                }
843
                new_paras
            } else {
                // pending_paras.is_empty, so parachain list did not change
5690
                None
            };
6533
            let pending_paused = <PendingPaused<T>>::get();
6533
            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
                }
6522
            }
6533
            let pending_parathread_params = <PendingParathreadParams<T>>::get();
6533
            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
                }
6527
            }
6533
            let pending_to_remove = <PendingToRemove<T>>::get();
6533
            if !pending_to_remove.is_empty() {
167
                let (past_and_present, future) =
167
                    pending_to_remove.into_iter().partition::<Vec<_>, _>(
168
                        |&(apply_at_session, _)| apply_at_session <= *session_index,
                    );
167
                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.
84
                    let mut removed_para_ids = BTreeSet::new();
168
                    for (_, new_paras) in &past_and_present {
171
                        for para_id in new_paras {
87
                            Self::cleanup_deregistered_para_id(*para_id);
87
                            removed_para_ids.insert(*para_id);
87
                            if let Err(id) = BufferedParasToDeregister::<T>::try_append(*para_id) {
                                log::error!(
                                    target: LOG_TARGET,
                                    "Failed to add paraId {:?} to deregistration list",
                                    id
                                );
87
                            }
                        }
                    }
                    // Also need to remove PendingParams to avoid setting params for a para id that does not exist
84
                    let mut pending_parathread_params = <PendingParathreadParams<T>>::get();
85
                    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
                        });
                    }
84
                    <PendingParathreadParams<T>>::put(pending_parathread_params);
84
                    <PendingToRemove<T>>::put(future);
83
                }
6366
            }
6533
            SessionChangeOutcome {
6533
                prev_paras,
6533
                new_paras,
6533
            }
6533
        }
        /// Remove all para id storage in this pallet,
        /// and execute para_deregistered hook to clean up other pallets as well
96
        fn cleanup_deregistered_para_id(para_id: ParaId) {
96
            ParaGenesisData::<T>::remove(para_id);
96
            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
96
            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
                );
74
            }
96
            ParaManager::<T>::remove(para_id);
96
            T::RegistrarHooks::para_deregistered(para_id);
96
        }
44
        fn schedule_parachain_cleanup(para_id: ParaId) -> DispatchResult {
44
            let scheduled_session = Self::scheduled_session();
44
            let mut pending_paras = PendingToRemove::<T>::get();
            // First, we need to decide what we should use as the base paras.
44
            let base_paras = match pending_paras
44
                .binary_search_by_key(&scheduled_session, |(session, _paras)| *session)
            {
3
                Ok(i) => &mut pending_paras[i].1,
41
                Err(i) => {
41
                    pending_paras.insert(i, (scheduled_session, Default::default()));
41
                    &mut pending_paras[i].1
                }
            };
            // Add the para_id to the entry for the scheduled session.
44
            match base_paras.binary_search(&para_id) {
                // This Ok is unreachable
                Ok(_) => return Err(Error::<T>::ParaIdAlreadyDeregistered.into()),
44
                Err(index) => {
44
                    base_paras
44
                        .try_insert(index, para_id)
44
                        .map_err(|_e| Error::<T>::ParaIdListFull)?;
                }
            }
            // Save the updated list of pending parachains for removal.
44
            <PendingToRemove<T>>::put(pending_paras);
44
            Ok(())
44
        }
9455
        pub fn registered_para_ids() -> BoundedVec<ParaId, T::MaxLengthParaIds> {
9455
            RegisteredParaIds::<T>::get()
9455
        }
9656
        pub fn pending_registered_para_ids(
9656
        ) -> Vec<(T::SessionIndex, BoundedVec<ParaId, T::MaxLengthParaIds>)> {
9656
            PendingParaIds::<T>::get()
9656
        }
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)
        }
64
        pub fn paused() -> BoundedVec<ParaId, T::MaxLengthParaIds> {
64
            Paused::<T>::get()
64
        }
        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> GetSessionContainerChains<T::SessionIndex> for Pallet<T> {
9623
        fn session_container_chains(session_index: T::SessionIndex) -> SessionContainerChains {
9623
            let (past_and_present, _) = Pallet::<T>::pending_registered_para_ids()
9623
                .into_iter()
9623
                .partition::<Vec<_>, _>(|&(apply_at_session, _)| apply_at_session <= session_index);
9623
            let paras = if let Some(last) = past_and_present.last() {
400
                last.1.clone()
            } else {
9223
                Pallet::<T>::registered_para_ids()
            };
9623
            let mut parachains = vec![];
9623
            let mut parathreads = vec![];
19731
            for para_id in paras {
                // TODO: sweet O(n) db reads
10108
                if let Some(parathread_params) = ParathreadParams::<T>::get(para_id) {
429
                    parathreads.push((para_id, parathread_params));
9679
                } else {
9679
                    parachains.push(para_id);
9679
                }
            }
9623
            SessionContainerChains {
9623
                parachains,
9623
                parathreads,
9623
            }
9623
        }
        #[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;
222
    fn try_origin(
222
        o: T::RuntimeOrigin,
222
        para_id: &ParaId,
222
    ) -> Result<Self::Success, T::RuntimeOrigin> {
156
        let signed_account =
222
            <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)
222
    }
    #[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())
    }
}