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
    serde::{Deserialize, Serialize},
41
    sp_core::H256,
42
    sp_runtime::{
43
        app_crypto::sp_core,
44
        traits::{CheckedAdd, CheckedMul},
45
        ArithmeticError, DispatchResult, Perbill,
46
    },
47
    sp_std::{collections::btree_set::BTreeSet, vec::Vec},
48
};
49

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

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

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

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

            
86
impl<Balance> CollatorAssignmentTip<Balance> for () {
87
    fn get_para_tip(_: ParaId) -> Option<Balance> {
88
        None
89
    }
90
}
91
/// The author-noting hook to react to container chains authoring.
92
pub trait AuthorNotingHook<AccountId> {
93
    /// This hook is called partway through the `set_latest_author_data` inherent in author-noting.
94
    ///
95
    /// The hook should never panic and is required to return the weight consumed.
96
    fn on_container_author_noted(
97
        author: &AccountId,
98
        block_number: BlockNumber,
99
        para_id: ParaId,
100
    ) -> Weight;
101
}
102

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
364
129
        Ok(deposit)
365
129
    }
366
}
367

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

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

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

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

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

            
408
impl<AccountId> RegistrarHandler<AccountId> for () {
409
111
    fn register(
410
111
        _who: AccountId,
411
111
        _id: ParaId,
412
111
        _genesis_storage: &[ContainerChainGenesisDataItem],
413
111
        _head_data: Option<HeadData>,
414
111
    ) -> DispatchResult {
415
111
        Ok(())
416
111
    }
417

            
418
56
    fn schedule_para_upgrade(_id: ParaId) -> DispatchResult {
419
56
        Ok(())
420
56
    }
421

            
422
38
    fn schedule_para_downgrade(_id: ParaId) -> DispatchResult {
423
38
        Ok(())
424
38
    }
425

            
426
16
    fn deregister(_id: ParaId) {}
427

            
428
16
    fn deregister_weight() -> Weight {
429
16
        Weight::default()
430
16
    }
431
}
432

            
433
/// Trait to retrieve the orchestrator block author (if any).
434
/// In a relay-chain context we will return None.
435
pub trait MaybeSelfChainBlockAuthor<AccountId> {
436
    fn get_block_author() -> Option<AccountId>;
437
}
438

            
439
impl<AccountId> MaybeSelfChainBlockAuthor<AccountId> for () {
440
3317
    fn get_block_author() -> Option<AccountId> {
441
3317
        None
442
3317
    }
443
}