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_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
/// The author-noting hook to react to container chains authoring.
93
pub trait AuthorNotingHook<AccountId> {
94
    /// This hook is called partway through the `set_latest_author_data` inherent in author-noting.
95
    ///
96
    /// The hook should never panic and is required to return the weight consumed.
97
    fn on_container_author_noted(
98
        author: &AccountId,
99
        block_number: BlockNumber,
100
        para_id: ParaId,
101
    ) -> Weight;
102
}
103

            
104
#[impl_trait_for_tuples::impl_for_tuples(5)]
105
impl<AccountId> AuthorNotingHook<AccountId> for Tuple {
106
20226
    fn on_container_author_noted(a: &AccountId, b: BlockNumber, p: ParaId) -> Weight {
107
20226
        let mut weight: Weight = Default::default();
108
20226
        for_tuples!( #( weight.saturating_accrue(Tuple::on_container_author_noted(a, b, p)); )* );
109
20226
        weight
110
20226
    }
111
}
112

            
113
pub trait DistributeRewards<AccountId, Imbalance> {
114
    fn distribute_rewards(rewarded: AccountId, amount: Imbalance) -> DispatchResultWithPostInfo;
115
}
116

            
117
impl<AccountId, Imbalance> DistributeRewards<AccountId, Imbalance> for () {
118
26
    fn distribute_rewards(_rewarded: AccountId, _amount: Imbalance) -> DispatchResultWithPostInfo {
119
26
        Ok(().into())
120
26
    }
121
}
122

            
123
/// Get the current list of container chains parachain ids.
124
pub trait GetCurrentContainerChains {
125
    type MaxContainerChains: Get<u32>;
126

            
127
    fn current_container_chains() -> BoundedVec<ParaId, Self::MaxContainerChains>;
128

            
129
    #[cfg(feature = "runtime-benchmarks")]
130
    fn set_current_container_chains(container_chains: &[ParaId]);
131
}
132

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

            
159
impl SlotFrequency {
160
677
    pub fn should_parathread_buy_core(
161
677
        &self,
162
677
        current_slot: Slot,
163
677
        max_slot_required_to_complete_purchase: Slot,
164
677
        last_block_slot: Slot,
165
677
    ) -> bool {
166
677
        current_slot
167
677
            >= last_block_slot
168
677
                .saturating_add(Slot::from(u64::from(self.min)))
169
677
                .saturating_sub(max_slot_required_to_complete_purchase)
170
677
    }
171

            
172
    pub fn should_parathread_author_block(
173
        &self,
174
        current_slot: Slot,
175
        last_block_slot: Slot,
176
    ) -> bool {
177
        current_slot >= last_block_slot.saturating_add(Slot::from(u64::from(self.min)))
178
    }
179
}
180

            
181
impl Default for SlotFrequency {
182
114
    fn default() -> Self {
183
114
        Self { min: 1, max: 1 }
184
114
    }
185
}
186

            
187
#[derive(
188
    Clone,
189
    Debug,
190
    Encode,
191
    Decode,
192
1261
    scale_info::TypeInfo,
193
    PartialEq,
194
    Eq,
195
    Serialize,
196
    Deserialize,
197
    MaxEncodedLen,
198
)]
199
pub struct ParathreadParams {
200
    pub slot_frequency: SlotFrequency,
201
}
202

            
203
#[derive(Clone, Debug, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq)]
204
pub struct SessionContainerChains {
205
    pub parachains: Vec<ParaId>,
206
    pub parathreads: Vec<(ParaId, ParathreadParams)>,
207
}
208

            
209
/// Get the list of container chains parachain ids at given
210
/// session index.
211
pub trait GetSessionContainerChains<SessionIndex> {
212
    fn session_container_chains(session_index: SessionIndex) -> SessionContainerChains;
213
    #[cfg(feature = "runtime-benchmarks")]
214
    fn set_session_container_chains(session_index: SessionIndex, container_chains: &[ParaId]);
215
}
216

            
217
/// Returns author for a parachain id for the given slot.
218
pub trait GetContainerChainAuthor<AccountId> {
219
    fn author_for_slot(slot: Slot, para_id: ParaId) -> Option<AccountId>;
220
    #[cfg(feature = "runtime-benchmarks")]
221
    fn set_authors_for_para_id(para_id: ParaId, authors: Vec<AccountId>);
222
}
223

            
224
/// Returns the host configuration composed of the amount of collators assigned
225
/// to the orchestrator chain, and how many collators are assigned per container chain.
226
pub trait GetHostConfiguration<SessionIndex> {
227
    fn max_collators(session_index: SessionIndex) -> u32;
228
    fn min_collators_for_orchestrator(session_index: SessionIndex) -> u32;
229
    fn max_collators_for_orchestrator(session_index: SessionIndex) -> u32;
230
    fn collators_per_container(session_index: SessionIndex) -> u32;
231
    fn collators_per_parathread(session_index: SessionIndex) -> u32;
232
    fn target_container_chain_fullness(session_index: SessionIndex) -> Perbill;
233
    fn max_parachain_cores_percentage(session_index: SessionIndex) -> Option<Perbill>;
234
    #[cfg(feature = "runtime-benchmarks")]
235
    fn set_host_configuration(_session_index: SessionIndex) {}
236
}
237

            
238
/// Returns current session index.
239
pub trait GetSessionIndex<SessionIndex> {
240
    fn session_index() -> SessionIndex;
241
}
242

            
243
/// Should pallet_collator_assignment trigger a full rotation on this session?
244
pub trait ShouldRotateAllCollators<SessionIndex> {
245
    fn should_rotate_all_collators(session_index: SessionIndex) -> bool;
246
}
247

            
248
impl<SessionIndex> ShouldRotateAllCollators<SessionIndex> for () {
249
    fn should_rotate_all_collators(_session_index: SessionIndex) -> bool {
250
        false
251
    }
252
}
253

            
254
/// Helper trait for pallet_collator_assignment to be able to give priority to invulnerables
255
pub trait RemoveInvulnerables<AccountId> {
256
    /// Remove the first n invulnerables from the list of collators. The order should be respected.
257
    fn remove_invulnerables(
258
        collators: &mut Vec<AccountId>,
259
        num_invulnerables: usize,
260
    ) -> Vec<AccountId>;
261
}
262

            
263
impl<AccountId: Clone> RemoveInvulnerables<AccountId> for () {
264
385
    fn remove_invulnerables(
265
385
        _collators: &mut Vec<AccountId>,
266
385
        _num_invulnerables: usize,
267
385
    ) -> Vec<AccountId> {
268
385
        // Default impl: no collators are invulnerables
269
385
        vec![]
270
385
    }
271
}
272

            
273
/// Helper trait for pallet_collator_assignment to be able to not assign collators to container chains with no credits
274
/// in pallet_services_payment
275
pub trait RemoveParaIdsWithNoCredits {
276
    /// Remove para ids with not enough credits. The resulting order will affect priority: the first para id in the list
277
    /// will be the first one to get collators.
278
    fn remove_para_ids_with_no_credits(
279
        para_ids: &mut Vec<ParaId>,
280
        currently_assigned: &BTreeSet<ParaId>,
281
    );
282

            
283
    /// Make those para ids valid by giving them enough credits, for benchmarking.
284
    #[cfg(feature = "runtime-benchmarks")]
285
    fn make_valid_para_ids(para_ids: &[ParaId]);
286
}
287

            
288
impl RemoveParaIdsWithNoCredits for () {
289
    fn remove_para_ids_with_no_credits(
290
        _para_ids: &mut Vec<ParaId>,
291
        _currently_assigned: &BTreeSet<ParaId>,
292
    ) {
293
    }
294

            
295
    #[cfg(feature = "runtime-benchmarks")]
296
    fn make_valid_para_ids(_para_ids: &[ParaId]) {}
297
}
298

            
299
pub trait RelayStorageRootProvider {
300
    fn get_relay_storage_root(relay_block_number: u32) -> Option<H256>;
301

            
302
    #[cfg(feature = "runtime-benchmarks")]
303
    fn set_relay_storage_root(relay_block_number: u32, storage_root: Option<H256>);
304
}
305

            
306
impl RelayStorageRootProvider for () {
307
    fn get_relay_storage_root(_relay_block_number: u32) -> Option<H256> {
308
        None
309
    }
310

            
311
    #[cfg(feature = "runtime-benchmarks")]
312
    fn set_relay_storage_root(_relay_block_number: u32, _storage_root: Option<H256>) {}
313
}
314

            
315
/// Information extracted from the latest container chain header
316
#[derive(
317
    Default,
318
    Clone,
319
    Encode,
320
    Decode,
321
    PartialEq,
322
    sp_core::RuntimeDebug,
323
1746
    scale_info::TypeInfo,
324
    MaxEncodedLen,
325
    Serialize,
326
    Deserialize,
327
)]
328
pub struct ContainerChainBlockInfo<AccountId> {
329
    pub block_number: BlockNumber,
330
    pub author: AccountId,
331
    pub latest_slot_number: Slot,
332
}
333

            
334
pub trait LatestAuthorInfoFetcher<AccountId> {
335
    fn get_latest_author_info(para_id: ParaId) -> Option<ContainerChainBlockInfo<AccountId>>;
336
}
337

            
338
pub trait StorageDeposit<Data, Balance> {
339
    fn compute_deposit(data: &Data) -> Result<Balance, DispatchErrorWithPostInfo>;
340
}
341

            
342
pub struct BytesDeposit<BaseCost, ByteCost>(PhantomData<(BaseCost, ByteCost)>);
343
impl<Data, Balance, BaseCost, ByteCost> StorageDeposit<Data, Balance>
344
    for BytesDeposit<BaseCost, ByteCost>
345
where
346
    Data: Encode,
347
    Balance: TryFrom<usize> + CheckedAdd + CheckedMul,
348
    BaseCost: Get<Balance>,
349
    ByteCost: Get<Balance>,
350
{
351
163
    fn compute_deposit(data: &Data) -> Result<Balance, DispatchErrorWithPostInfo> {
352
163
        let base = BaseCost::get();
353
163
        let byte = ByteCost::get();
354
163
        let size: Balance = data
355
163
            .encoded_size()
356
163
            .try_into()
357
163
            .map_err(|_| ArithmeticError::Overflow)?;
358

            
359
163
        let deposit = byte
360
163
            .checked_mul(&size)
361
163
            .ok_or(ArithmeticError::Overflow)?
362
163
            .checked_add(&base)
363
163
            .ok_or(ArithmeticError::Overflow)?;
364

            
365
163
        Ok(deposit)
366
163
    }
367
}
368

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

            
375
impl GenericStorageReader for GenericStateProof<cumulus_primitives_core::relay_chain::Block> {
376
41433
    fn read_entry<T: Decode>(&self, key: &[u8], fallback: Option<T>) -> Result<T, ReadEntryErr> {
377
41433
        GenericStateProof::read_entry(self, key, fallback)
378
41433
    }
379
}
380

            
381
/// Solo chain impl, read directly from storage
382
pub struct NativeStorageReader;
383
impl GenericStorageReader for NativeStorageReader {
384
11
    fn read_entry<T: Decode>(&self, key: &[u8], fallback: Option<T>) -> Result<T, ReadEntryErr> {
385
11
        match frame_support::storage::unhashed::get(key).or(fallback) {
386
11
            Some(x) => Ok(x),
387
            None => Err(ReadEntryErr::Absent),
388
        }
389
11
    }
390
}
391

            
392
/// Trait to handle registrar-related operations in a relay-chain context.
393
/// Mostly used to wire Tanssi's and Polkadot's registrars, for them to
394
/// work together in a solo-chain environment.
395
pub trait RegistrarHandler<AccountId> {
396
    fn register(
397
        who: AccountId,
398
        id: ParaId,
399
        genesis_storage: &[ContainerChainGenesisDataItem],
400
        head_data: Option<HeadData>,
401
    ) -> DispatchResult;
402

            
403
    fn schedule_para_upgrade(id: ParaId) -> DispatchResult;
404
    fn schedule_para_downgrade(id: ParaId) -> DispatchResult;
405
    fn deregister(id: ParaId);
406
    fn deregister_weight() -> Weight;
407

            
408
    #[cfg(feature = "runtime-benchmarks")]
409
    fn bench_head_data() -> Option<HeadData> {
410
        None
411
    }
412
    #[cfg(feature = "runtime-benchmarks")]
413
    fn add_trusted_validation_code(_code: Vec<u8>) {}
414
    #[cfg(feature = "runtime-benchmarks")]
415
    fn registrar_new_session(_session: u32) {}
416
}
417

            
418
impl<AccountId> RegistrarHandler<AccountId> for () {
419
135
    fn register(
420
135
        _who: AccountId,
421
135
        _id: ParaId,
422
135
        _genesis_storage: &[ContainerChainGenesisDataItem],
423
135
        _head_data: Option<HeadData>,
424
135
    ) -> DispatchResult {
425
135
        Ok(())
426
135
    }
427

            
428
64
    fn schedule_para_upgrade(_id: ParaId) -> DispatchResult {
429
64
        Ok(())
430
64
    }
431

            
432
52
    fn schedule_para_downgrade(_id: ParaId) -> DispatchResult {
433
52
        Ok(())
434
52
    }
435

            
436
24
    fn deregister(_id: ParaId) {}
437

            
438
24
    fn deregister_weight() -> Weight {
439
24
        Weight::default()
440
24
    }
441
}
442

            
443
/// Trait to retrieve the orchestrator block author (if any).
444
/// In a relay-chain context we will return None.
445
pub trait MaybeSelfChainBlockAuthor<AccountId> {
446
    fn get_block_author() -> Option<AccountId>;
447
}
448

            
449
impl<AccountId> MaybeSelfChainBlockAuthor<AccountId> for () {
450
4066
    fn get_block_author() -> Option<AccountId> {
451
4066
        None
452
4066
    }
453
}
454

            
455
/// Information regarding the active era (era in used in session).
456
#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
457
pub struct ActiveEraInfo {
458
    /// Index of era.
459
    pub index: EraIndex,
460
    /// Moment of start expressed as millisecond from `$UNIX_EPOCH`.
461
    ///
462
    /// Start can be none if start hasn't been set for the era yet,
463
    /// Start is set on the first on_finalize of the era to guarantee usage of `Time`.
464
    pub start: Option<u64>,
465
}
466

            
467
/// Counter for the number of eras that have passed.
468
pub type EraIndex = u32;
469

            
470
pub trait EraIndexProvider {
471
    fn active_era() -> ActiveEraInfo;
472
    fn era_to_session_start(era_index: EraIndex) -> Option<u32>;
473
}
474

            
475
pub trait ValidatorProvider<ValidatorId> {
476
    fn validators() -> Vec<ValidatorId>;
477
}
478

            
479
pub trait InvulnerablesProvider<ValidatorId> {
480
    fn invulnerables() -> Vec<ValidatorId>;
481
}
482

            
483
pub trait OnEraStart {
484
    fn on_era_start(_era_index: EraIndex, _session_start: u32) {}
485
}
486

            
487
#[impl_trait_for_tuples::impl_for_tuples(5)]
488
impl OnEraStart for Tuple {
489
173
    fn on_era_start(era_index: EraIndex, session_start: u32) {
490
173
        for_tuples!( #( Tuple::on_era_start(era_index, session_start); )* );
491
173
    }
492
}
493

            
494
pub trait OnEraEnd {
495
    fn on_era_end(_era_index: EraIndex) {}
496
}
497

            
498
#[impl_trait_for_tuples::impl_for_tuples(5)]
499
impl OnEraEnd for Tuple {
500
511
    fn on_era_end(era_index: EraIndex) {
501
511
        for_tuples!( #( Tuple::on_era_end(era_index); )* );
502
511
    }
503
}