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
//! Crate containing various traits used by moondance crates allowing to connect pallet
18
//! with each other or with mocks.
19

            
20
#![cfg_attr(not(feature = "std"), no_std)]
21

            
22
pub mod alias;
23

            
24
pub use {
25
    alias::*,
26
    cumulus_primitives_core::{
27
        relay_chain::{BlockNumber, HeadData, Slot, ValidationCode},
28
        ParaId,
29
    },
30
    dp_chain_state_snapshot::{GenericStateProof, ReadEntryErr},
31
    dp_container_chain_genesis_data::ContainerChainGenesisDataItem,
32
};
33
use {
34
    core::marker::PhantomData,
35
    frame_support::{
36
        dispatch::DispatchErrorWithPostInfo,
37
        pallet_prelude::{Decode, DispatchResultWithPostInfo, Encode, Get, MaxEncodedLen, Weight},
38
        BoundedVec,
39
    },
40
    scale_info::TypeInfo,
41
    serde::{Deserialize, Serialize},
42
    sp_core::H256,
43
    sp_runtime::{
44
        app_crypto::sp_core,
45
        traits::{CheckedAdd, CheckedMul},
46
        ArithmeticError, DispatchResult, Perbill, RuntimeDebug,
47
    },
48
    sp_std::{collections::btree_map::BTreeMap, collections::btree_set::BTreeSet, vec::Vec},
49
};
50

            
51
// Separate import as rustfmt wrongly change it to `sp_std::vec::self`, which is the module instead
52
// of the macro.
53
use sp_std::vec;
54

            
55
/// The collator-assignment hook to react to collators being assigned to container chains.
56
pub trait CollatorAssignmentHook<Balance> {
57
    /// This hook is called when collators are assigned to a container
58
    ///
59
    /// The hook should never panic and is required to return the weight consumed.
60
    fn on_collators_assigned(
61
        para_id: ParaId,
62
        maybe_tip: Option<&Balance>,
63
        is_parathread: bool,
64
    ) -> Result<Weight, sp_runtime::DispatchError>;
65
}
66

            
67
#[impl_trait_for_tuples::impl_for_tuples(5)]
68
impl<Balance> CollatorAssignmentHook<Balance> for Tuple {
69
    fn on_collators_assigned(
70
        p: ParaId,
71
        t: Option<&Balance>,
72
        ip: bool,
73
    ) -> Result<Weight, sp_runtime::DispatchError> {
74
        let mut weight: Weight = Default::default();
75
        for_tuples!( #( weight.saturating_accrue(Tuple::on_collators_assigned(p, t, ip)?); )* );
76
        Ok(weight)
77
    }
78
}
79

            
80
/// Container chains collator assignment tip prioritization on congestion.
81
/// Tips paras are willing to pay for collator assignment in case of collators demand
82
/// surpasses the offer.
83
pub trait CollatorAssignmentTip<Balance> {
84
    fn get_para_tip(a: ParaId) -> Option<Balance>;
85
}
86

            
87
impl<Balance> CollatorAssignmentTip<Balance> for () {
88
    fn get_para_tip(_: ParaId) -> Option<Balance> {
89
        None
90
    }
91
}
92

            
93
pub struct AuthorNotingInfo<AccountId> {
94
    pub author: AccountId,
95
    pub block_number: BlockNumber,
96
    pub para_id: ParaId,
97
}
98

            
99
/// The author-noting hook to react to container chains authoring.
100
pub trait AuthorNotingHook<AccountId> {
101
    /// This hook is called partway through the `set_latest_author_data` inherent in author-noting.
102
    ///
103
    /// The hook should never panic and is required to return the weight consumed.
104
    fn on_container_authors_noted(info: &[AuthorNotingInfo<AccountId>]) -> Weight;
105

            
106
    #[cfg(feature = "runtime-benchmarks")]
107
    fn prepare_worst_case_for_bench(author: &AccountId, block_number: BlockNumber, para_id: ParaId);
108
}
109

            
110
#[impl_trait_for_tuples::impl_for_tuples(5)]
111
impl<AccountId> AuthorNotingHook<AccountId> for Tuple {
112
21843
    fn on_container_authors_noted(info: &[AuthorNotingInfo<AccountId>]) -> Weight {
113
21843
        let mut weight: Weight = Default::default();
114
21843
        for_tuples!( #( weight.saturating_accrue(Tuple::on_container_authors_noted(info)); )* );
115
21843
        weight
116
21843
    }
117

            
118
    #[cfg(feature = "runtime-benchmarks")]
119
    fn prepare_worst_case_for_bench(a: &AccountId, b: BlockNumber, p: ParaId) {
120
        for_tuples!( #( Tuple::prepare_worst_case_for_bench(a, b, p); )* );
121
    }
122
}
123

            
124
pub trait DistributeRewards<AccountId, Imbalance> {
125
    fn distribute_rewards(rewarded: AccountId, amount: Imbalance) -> DispatchResultWithPostInfo;
126
}
127

            
128
impl<AccountId, Imbalance> DistributeRewards<AccountId, Imbalance> for () {
129
26
    fn distribute_rewards(_rewarded: AccountId, _amount: Imbalance) -> DispatchResultWithPostInfo {
130
26
        Ok(().into())
131
26
    }
132
}
133

            
134
/// Get the current list of container chains parachain ids.
135
pub trait GetCurrentContainerChains {
136
    type MaxContainerChains: Get<u32>;
137

            
138
    fn current_container_chains() -> BoundedVec<ParaId, Self::MaxContainerChains>;
139

            
140
    #[cfg(feature = "runtime-benchmarks")]
141
    fn set_current_container_chains(container_chains: &[ParaId]);
142
}
143

            
144
/// How often should a parathread collator propose blocks. The units are "1 out of n slots", where the slot time is the
145
/// tanssi slot time, 6 seconds.
146
// TODO: this is currently ignored
147
#[derive(
148
    Clone,
149
    Debug,
150
    Encode,
151
    Decode,
152
2574
    scale_info::TypeInfo,
153
    PartialEq,
154
    Eq,
155
    Serialize,
156
    Deserialize,
157
    MaxEncodedLen,
158
)]
159
pub struct SlotFrequency {
160
    /// The parathread will produce at most 1 block every x slots. min=10 means that collators can produce 1 block
161
    /// every `x >= 10` slots, but they are not enforced to. If collators produce a block after less than 10
162
    /// slots, they will not be rewarded by tanssi.
163
    pub min: u32,
164
    /// The parathread will produce at least 1 block every x slots. max=10 means that collators are forced to
165
    /// produce 1 block every `x <= 10` slots. Collators can produce a block sooner than that if the `min` allows it, but
166
    /// waiting more than 10 slots will make them lose the block reward.
167
    pub max: u32,
168
}
169

            
170
impl SlotFrequency {
171
677
    pub fn should_parathread_buy_core(
172
677
        &self,
173
677
        current_slot: Slot,
174
677
        max_slot_required_to_complete_purchase: Slot,
175
677
        last_block_slot: Slot,
176
677
    ) -> bool {
177
677
        current_slot
178
677
            >= last_block_slot
179
677
                .saturating_add(Slot::from(u64::from(self.min)))
180
677
                .saturating_sub(max_slot_required_to_complete_purchase)
181
677
    }
182

            
183
    pub fn should_parathread_author_block(
184
        &self,
185
        current_slot: Slot,
186
        last_block_slot: Slot,
187
    ) -> bool {
188
        current_slot >= last_block_slot.saturating_add(Slot::from(u64::from(self.min)))
189
    }
190
}
191

            
192
impl Default for SlotFrequency {
193
357
    fn default() -> Self {
194
357
        Self { min: 1, max: 1 }
195
357
    }
196
}
197

            
198
#[derive(
199
    Clone,
200
    Debug,
201
    Encode,
202
    Decode,
203
1287
    scale_info::TypeInfo,
204
    PartialEq,
205
    Eq,
206
    Serialize,
207
    Deserialize,
208
    MaxEncodedLen,
209
)]
210
pub struct ParathreadParams {
211
    pub slot_frequency: SlotFrequency,
212
}
213

            
214
#[derive(Clone, Debug, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq)]
215
pub struct SessionContainerChains {
216
    pub parachains: Vec<ParaId>,
217
    pub parathreads: Vec<(ParaId, ParathreadParams)>,
218
}
219

            
220
/// Get the list of container chains parachain ids at given
221
/// session index.
222
pub trait GetSessionContainerChains<SessionIndex> {
223
    fn session_container_chains(session_index: SessionIndex) -> SessionContainerChains;
224
    #[cfg(feature = "runtime-benchmarks")]
225
    fn set_session_container_chains(session_index: SessionIndex, container_chains: &[ParaId]);
226
}
227

            
228
/// Returns author for a parachain id for the given slot.
229
pub trait GetContainerChainAuthor<AccountId> {
230
    fn author_for_slot(slot: Slot, para_id: ParaId) -> Option<AccountId>;
231
    #[cfg(feature = "runtime-benchmarks")]
232
    fn set_authors_for_para_id(para_id: ParaId, authors: Vec<AccountId>);
233
}
234

            
235
/// Returns the host configuration composed of the amount of collators assigned
236
/// to the orchestrator chain, and how many collators are assigned per container chain.
237
pub trait GetHostConfiguration<SessionIndex> {
238
    fn max_collators(session_index: SessionIndex) -> u32;
239
    fn min_collators_for_orchestrator(session_index: SessionIndex) -> u32;
240
    fn max_collators_for_orchestrator(session_index: SessionIndex) -> u32;
241
    fn collators_per_container(session_index: SessionIndex) -> u32;
242
    fn collators_per_parathread(session_index: SessionIndex) -> u32;
243
    fn target_container_chain_fullness(session_index: SessionIndex) -> Perbill;
244
    fn max_parachain_cores_percentage(session_index: SessionIndex) -> Option<Perbill>;
245
    fn full_rotation_mode(session_index: SessionIndex) -> FullRotationModes;
246
    #[cfg(feature = "runtime-benchmarks")]
247
    fn set_host_configuration(_session_index: SessionIndex) {}
248
}
249

            
250
/// Returns current session index.
251
pub trait GetSessionIndex<SessionIndex> {
252
    fn session_index() -> SessionIndex;
253
}
254

            
255
/// Should pallet_collator_assignment trigger a full rotation on this session?
256
pub trait ShouldRotateAllCollators<SessionIndex> {
257
    fn should_rotate_all_collators(session_index: SessionIndex) -> bool;
258
}
259

            
260
impl<SessionIndex> ShouldRotateAllCollators<SessionIndex> for () {
261
    fn should_rotate_all_collators(_session_index: SessionIndex) -> bool {
262
        false
263
    }
264
}
265

            
266
/// Helper trait for pallet_collator_assignment to be able to give priority to invulnerables
267
pub trait RemoveInvulnerables<AccountId> {
268
    /// Remove the first n invulnerables from the list of collators. The order should be respected.
269
    fn remove_invulnerables(
270
        collators: &mut Vec<AccountId>,
271
        num_invulnerables: usize,
272
    ) -> Vec<AccountId>;
273
}
274

            
275
impl<AccountId: Clone> RemoveInvulnerables<AccountId> for () {
276
497
    fn remove_invulnerables(
277
497
        _collators: &mut Vec<AccountId>,
278
497
        _num_invulnerables: usize,
279
497
    ) -> Vec<AccountId> {
280
497
        // Default impl: no collators are invulnerables
281
497
        vec![]
282
497
    }
283
}
284

            
285
/// Helper trait for pallet_collator_assignment to be able to not assign collators to container chains with no credits
286
/// in pallet_services_payment
287
pub trait ParaIdAssignmentHooks<B, AC> {
288
    /// Remove para ids with not enough credits. The resulting order will affect priority: the first para id in the list
289
    /// will be the first one to get collators.
290
    fn pre_assignment(para_ids: &mut Vec<ParaId>, old_assigned: &BTreeSet<ParaId>);
291
    fn post_assignment(
292
        current_assigned: &BTreeSet<ParaId>,
293
        new_assigned: &mut BTreeMap<ParaId, Vec<AC>>,
294
        maybe_tip: &Option<B>,
295
    ) -> Weight;
296

            
297
    /// Make those para ids valid by giving them enough credits, for benchmarking.
298
    #[cfg(feature = "runtime-benchmarks")]
299
    fn make_valid_para_ids(para_ids: &[ParaId]);
300
}
301

            
302
impl<B, AC> ParaIdAssignmentHooks<B, AC> for () {
303
    fn pre_assignment(_para_ids: &mut Vec<ParaId>, _currently_assigned: &BTreeSet<ParaId>) {}
304

            
305
    fn post_assignment(
306
        _current_assigned: &BTreeSet<ParaId>,
307
        _new_assigned: &mut BTreeMap<ParaId, Vec<AC>>,
308
        _maybe_tip: &Option<B>,
309
    ) -> Weight {
310
        Default::default()
311
    }
312

            
313
    #[cfg(feature = "runtime-benchmarks")]
314
    fn make_valid_para_ids(_para_ids: &[ParaId]) {}
315
}
316

            
317
pub trait RelayStorageRootProvider {
318
    fn get_relay_storage_root(relay_block_number: u32) -> Option<H256>;
319

            
320
    #[cfg(feature = "runtime-benchmarks")]
321
    fn set_relay_storage_root(relay_block_number: u32, storage_root: Option<H256>);
322
}
323

            
324
impl RelayStorageRootProvider for () {
325
    fn get_relay_storage_root(_relay_block_number: u32) -> Option<H256> {
326
        None
327
    }
328

            
329
    #[cfg(feature = "runtime-benchmarks")]
330
    fn set_relay_storage_root(_relay_block_number: u32, _storage_root: Option<H256>) {}
331
}
332

            
333
/// Information extracted from the latest container chain header
334
#[derive(
335
    Default,
336
    Clone,
337
    Encode,
338
    Decode,
339
    PartialEq,
340
    sp_core::RuntimeDebug,
341
1782
    scale_info::TypeInfo,
342
    MaxEncodedLen,
343
    Serialize,
344
    Deserialize,
345
)]
346
pub struct ContainerChainBlockInfo<AccountId> {
347
    pub block_number: BlockNumber,
348
    pub author: AccountId,
349
    pub latest_slot_number: Slot,
350
}
351

            
352
pub trait LatestAuthorInfoFetcher<AccountId> {
353
    fn get_latest_author_info(para_id: ParaId) -> Option<ContainerChainBlockInfo<AccountId>>;
354
}
355

            
356
pub trait StorageDeposit<Data, Balance> {
357
    fn compute_deposit(data: &Data) -> Result<Balance, DispatchErrorWithPostInfo>;
358
}
359

            
360
pub struct BytesDeposit<BaseCost, ByteCost>(PhantomData<(BaseCost, ByteCost)>);
361
impl<Data, Balance, BaseCost, ByteCost> StorageDeposit<Data, Balance>
362
    for BytesDeposit<BaseCost, ByteCost>
363
where
364
    Data: Encode,
365
    Balance: TryFrom<usize> + CheckedAdd + CheckedMul,
366
    BaseCost: Get<Balance>,
367
    ByteCost: Get<Balance>,
368
{
369
175
    fn compute_deposit(data: &Data) -> Result<Balance, DispatchErrorWithPostInfo> {
370
175
        let base = BaseCost::get();
371
175
        let byte = ByteCost::get();
372
175
        let size: Balance = data
373
175
            .encoded_size()
374
175
            .try_into()
375
175
            .map_err(|_| ArithmeticError::Overflow)?;
376

            
377
175
        let deposit = byte
378
175
            .checked_mul(&size)
379
175
            .ok_or(ArithmeticError::Overflow)?
380
175
            .checked_add(&base)
381
175
            .ok_or(ArithmeticError::Overflow)?;
382

            
383
175
        Ok(deposit)
384
175
    }
385
}
386

            
387
/// Trait to abstract away relay storage proofs, and allow the same logic to work on both parachains and solochains.
388
/// Parachains should use relay storage proofs, while solochains should read from storage directly.
389
pub trait GenericStorageReader {
390
    fn read_entry<T: Decode>(&self, key: &[u8], fallback: Option<T>) -> Result<T, ReadEntryErr>;
391
}
392

            
393
impl GenericStorageReader for GenericStateProof<cumulus_primitives_core::relay_chain::Block> {
394
42999
    fn read_entry<T: Decode>(&self, key: &[u8], fallback: Option<T>) -> Result<T, ReadEntryErr> {
395
42999
        GenericStateProof::read_entry(self, key, fallback)
396
42999
    }
397
}
398

            
399
/// Solo chain impl, read directly from storage
400
pub struct NativeStorageReader;
401
impl GenericStorageReader for NativeStorageReader {
402
11
    fn read_entry<T: Decode>(&self, key: &[u8], fallback: Option<T>) -> Result<T, ReadEntryErr> {
403
11
        match frame_support::storage::unhashed::get(key).or(fallback) {
404
11
            Some(x) => Ok(x),
405
            None => Err(ReadEntryErr::Absent),
406
        }
407
11
    }
408
}
409

            
410
/// Trait to handle registrar-related operations in a relay-chain context.
411
/// Mostly used to wire Tanssi's and Polkadot's registrars, for them to
412
/// work together in a solo-chain environment.
413
pub trait RegistrarHandler<AccountId> {
414
    fn register(
415
        who: AccountId,
416
        id: ParaId,
417
        genesis_storage: &[ContainerChainGenesisDataItem],
418
        head_data: Option<HeadData>,
419
    ) -> DispatchResult;
420

            
421
    fn schedule_para_upgrade(id: ParaId) -> DispatchResult;
422
    fn schedule_para_downgrade(id: ParaId) -> DispatchResult;
423
    fn deregister(id: ParaId);
424
    fn deregister_weight() -> Weight;
425

            
426
    #[cfg(feature = "runtime-benchmarks")]
427
    fn bench_head_data() -> Option<HeadData> {
428
        None
429
    }
430
    #[cfg(feature = "runtime-benchmarks")]
431
    fn add_trusted_validation_code(_code: Vec<u8>) {}
432
    #[cfg(feature = "runtime-benchmarks")]
433
    fn registrar_new_session(_session: u32) {}
434
    #[cfg(feature = "runtime-benchmarks")]
435
    fn prepare_chain_registration(_id: ParaId, _who: AccountId) {}
436
}
437

            
438
impl<AccountId> RegistrarHandler<AccountId> for () {
439
147
    fn register(
440
147
        _who: AccountId,
441
147
        _id: ParaId,
442
147
        _genesis_storage: &[ContainerChainGenesisDataItem],
443
147
        _head_data: Option<HeadData>,
444
147
    ) -> DispatchResult {
445
147
        Ok(())
446
147
    }
447

            
448
64
    fn schedule_para_upgrade(_id: ParaId) -> DispatchResult {
449
64
        Ok(())
450
64
    }
451

            
452
70
    fn schedule_para_downgrade(_id: ParaId) -> DispatchResult {
453
70
        Ok(())
454
70
    }
455

            
456
42
    fn deregister(_id: ParaId) {}
457

            
458
42
    fn deregister_weight() -> Weight {
459
42
        Weight::default()
460
42
    }
461
}
462

            
463
/// Trait to retrieve the orchestrator block author (if any).
464
/// In a relay-chain context we will return None.
465
pub trait MaybeSelfChainBlockAuthor<AccountId> {
466
    fn get_block_author() -> Option<AccountId>;
467
}
468

            
469
impl<AccountId> MaybeSelfChainBlockAuthor<AccountId> for () {
470
5037
    fn get_block_author() -> Option<AccountId> {
471
5037
        None
472
5037
    }
473
}
474

            
475
/// Information regarding the active era (era in used in session).
476
#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
477
pub struct ActiveEraInfo {
478
    /// Index of era.
479
    pub index: EraIndex,
480
    /// Moment of start expressed as millisecond from `$UNIX_EPOCH`.
481
    ///
482
    /// Start can be none if start hasn't been set for the era yet,
483
    /// Start is set on the first on_finalize of the era to guarantee usage of `Time`.
484
    pub start: Option<u64>,
485
}
486

            
487
/// Counter for the number of eras that have passed.
488
pub type EraIndex = u32;
489

            
490
pub trait EraIndexProvider {
491
    fn active_era() -> ActiveEraInfo;
492
    fn era_to_session_start(era_index: EraIndex) -> Option<u32>;
493
}
494

            
495
pub trait ValidatorProvider<ValidatorId> {
496
    fn validators() -> Vec<ValidatorId>;
497
}
498

            
499
pub trait InvulnerablesProvider<ValidatorId> {
500
    fn invulnerables() -> Vec<ValidatorId>;
501
}
502

            
503
pub trait OnEraStart {
504
    fn on_era_start(_era_index: EraIndex, _session_start: u32, _external_idx: u64) {}
505
}
506

            
507
#[impl_trait_for_tuples::impl_for_tuples(5)]
508
impl OnEraStart for Tuple {
509
243
    fn on_era_start(era_index: EraIndex, session_start: u32, external_idx: u64) {
510
243
        for_tuples!( #( Tuple::on_era_start(era_index, session_start, external_idx); )* );
511
243
    }
512
}
513

            
514
pub trait OnEraEnd {
515
    fn on_era_end(_era_index: EraIndex) {}
516
}
517

            
518
#[impl_trait_for_tuples::impl_for_tuples(5)]
519
impl OnEraEnd for Tuple {
520
    fn on_era_end(era_index: EraIndex) {
521
        for_tuples!( #( Tuple::on_era_end(era_index); )* );
522
    }
523
}
524

            
525
/// Strategy to use when rotating collators. Default: rotate all of them. Allows to rotate only a random subset.
526
#[derive(
527
    Clone,
528
    Debug,
529
    Default,
530
    Encode,
531
    Decode,
532
5148
    scale_info::TypeInfo,
533
    PartialEq,
534
    Eq,
535
    Serialize,
536
    Deserialize,
537
    MaxEncodedLen,
538
)]
539
pub enum FullRotationMode {
540
97819
    #[default]
541
    RotateAll,
542
672
    KeepAll,
543
463
    /// Keep this many collators
544
    KeepCollators {
545
        keep: u32,
546
    },
547
469
    /// Keep a ratio of collators wrt to max collators.
548
    /// If max collators changes, the number of collators kept also changes.
549
    KeepPerbill {
550
        percentage: Perbill,
551
    },
552
}
553

            
554
/// Allow to set a different [FullRotationMode] for each kind of chain. Default: rotate all.
555
#[derive(
556
    Clone,
557
    Debug,
558
    Default,
559
    Encode,
560
    Decode,
561
3861
    scale_info::TypeInfo,
562
    PartialEq,
563
    Eq,
564
    Serialize,
565
    Deserialize,
566
    MaxEncodedLen,
567
)]
568
pub struct FullRotationModes {
569
    pub orchestrator: FullRotationMode,
570
    pub parachain: FullRotationMode,
571
    pub parathread: FullRotationMode,
572
}
573

            
574
impl FullRotationModes {
575
    /// Keep all collators assigned to their current chain if possible. This is equivalent to disabling rotation.
576
20375
    pub fn keep_all() -> Self {
577
20375
        Self {
578
20375
            orchestrator: FullRotationMode::KeepAll,
579
20375
            parachain: FullRotationMode::KeepAll,
580
20375
            parathread: FullRotationMode::KeepAll,
581
20375
        }
582
20375
    }
583
}
584

            
585
// A trait to retrieve the external index provider identifying some set of data
586
// In starlight, used to retrieve the external index associated to validators
587
pub trait ExternalIndexProvider {
588
    fn get_external_index() -> u64;
589
}