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, Slot},
28
        ParaId,
29
    },
30
};
31
use {
32
    core::marker::PhantomData,
33
    frame_support::{
34
        dispatch::DispatchErrorWithPostInfo,
35
        pallet_prelude::{Decode, DispatchResultWithPostInfo, Encode, Get, MaxEncodedLen, Weight},
36
        BoundedVec,
37
    },
38
    serde::{Deserialize, Serialize},
39
    sp_core::H256,
40
    sp_runtime::{
41
        app_crypto::sp_core,
42
        traits::{CheckedAdd, CheckedMul},
43
        ArithmeticError,
44
    },
45
    sp_std::{collections::btree_set::BTreeSet, vec, vec::Vec},
46
};
47

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

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

            
73
/// Container chains collator assignment tip prioritization on congestion.
74
/// Tips paras are willing to pay for collator assignment in case of collators demand
75
/// surpasses the offer.
76
pub trait CollatorAssignmentTip<Balance> {
77
    fn get_para_tip(a: ParaId) -> Option<Balance>;
78
}
79

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

            
97
#[impl_trait_for_tuples::impl_for_tuples(5)]
98
impl<AccountId> AuthorNotingHook<AccountId> for Tuple {
99
6584
    fn on_container_author_noted(a: &AccountId, b: BlockNumber, p: ParaId) -> Weight {
100
6584
        let mut weight: Weight = Default::default();
101
6584
        for_tuples!( #( weight.saturating_accrue(Tuple::on_container_author_noted(a, b, p)); )* );
102
6584
        weight
103
6584
    }
104
}
105

            
106
pub trait DistributeRewards<AccountId, Imbalance> {
107
    fn distribute_rewards(rewarded: AccountId, amount: Imbalance) -> DispatchResultWithPostInfo;
108
}
109

            
110
impl<AccountId, Imbalance> DistributeRewards<AccountId, Imbalance> for () {
111
26
    fn distribute_rewards(_rewarded: AccountId, _amount: Imbalance) -> DispatchResultWithPostInfo {
112
26
        Ok(().into())
113
26
    }
114
}
115

            
116
/// Get the current list of container chains parachain ids.
117
pub trait GetCurrentContainerChains {
118
    type MaxContainerChains: Get<u32>;
119

            
120
    fn current_container_chains() -> BoundedVec<ParaId, Self::MaxContainerChains>;
121

            
122
    #[cfg(feature = "runtime-benchmarks")]
123
    fn set_current_container_chains(container_chains: &[ParaId]);
124
}
125

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

            
143
impl SlotFrequency {
144
480
    pub fn should_parathread_buy_core(
145
480
        &self,
146
480
        current_slot: Slot,
147
480
        max_slot_required_to_complete_purchase: Slot,
148
480
        last_block_slot: Slot,
149
480
    ) -> bool {
150
480
        current_slot
151
480
            >= last_block_slot
152
480
                .saturating_add(Slot::from(u64::from(self.min)))
153
480
                .saturating_sub(max_slot_required_to_complete_purchase)
154
480
    }
155
}
156

            
157
impl Default for SlotFrequency {
158
48
    fn default() -> Self {
159
48
        Self { min: 1, max: 1 }
160
48
    }
161
}
162

            
163
#[derive(
164
1584
    Clone, Debug, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq, Serialize, Deserialize,
165
)]
166
pub struct ParathreadParams {
167
    pub slot_frequency: SlotFrequency,
168
}
169

            
170
#[derive(Clone, Debug, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq)]
171
pub struct SessionContainerChains {
172
    pub parachains: Vec<ParaId>,
173
    pub parathreads: Vec<(ParaId, ParathreadParams)>,
174
}
175

            
176
/// Get the list of container chains parachain ids at given
177
/// session index.
178
pub trait GetSessionContainerChains<SessionIndex> {
179
    fn session_container_chains(session_index: SessionIndex) -> SessionContainerChains;
180
    #[cfg(feature = "runtime-benchmarks")]
181
    fn set_session_container_chains(session_index: SessionIndex, container_chains: &[ParaId]);
182
}
183

            
184
/// Returns author for a parachain id for the given slot.
185
pub trait GetContainerChainAuthor<AccountId> {
186
    fn author_for_slot(slot: Slot, para_id: ParaId) -> Option<AccountId>;
187
    #[cfg(feature = "runtime-benchmarks")]
188
    fn set_authors_for_para_id(para_id: ParaId, authors: Vec<AccountId>);
189
}
190

            
191
/// Returns the host configuration composed of the amount of collators assigned
192
/// to the orchestrator chain, and how many collators are assigned per container chain.
193
pub trait GetHostConfiguration<SessionIndex> {
194
    fn max_collators(session_index: SessionIndex) -> u32;
195
    fn min_collators_for_orchestrator(session_index: SessionIndex) -> u32;
196
    fn max_collators_for_orchestrator(session_index: SessionIndex) -> u32;
197
    fn collators_per_container(session_index: SessionIndex) -> u32;
198
    fn collators_per_parathread(session_index: SessionIndex) -> u32;
199
    #[cfg(feature = "runtime-benchmarks")]
200
    fn set_host_configuration(_session_index: SessionIndex) {}
201
}
202

            
203
/// Returns current session index.
204
pub trait GetSessionIndex<SessionIndex> {
205
    fn session_index() -> SessionIndex;
206
}
207

            
208
/// Should pallet_collator_assignment trigger a full rotation on this session?
209
pub trait ShouldRotateAllCollators<SessionIndex> {
210
    fn should_rotate_all_collators(session_index: SessionIndex) -> bool;
211
}
212

            
213
impl<SessionIndex> ShouldRotateAllCollators<SessionIndex> for () {
214
249
    fn should_rotate_all_collators(_session_index: SessionIndex) -> bool {
215
249
        false
216
249
    }
217
}
218

            
219
/// Helper trait for pallet_collator_assignment to be able to give priority to invulnerables
220
pub trait RemoveInvulnerables<AccountId> {
221
    /// Remove the first n invulnerables from the list of collators. The order should be respected.
222
    fn remove_invulnerables(
223
        collators: &mut Vec<AccountId>,
224
        num_invulnerables: usize,
225
    ) -> Vec<AccountId>;
226
}
227

            
228
impl<AccountId: Clone> RemoveInvulnerables<AccountId> for () {
229
120
    fn remove_invulnerables(
230
120
        _collators: &mut Vec<AccountId>,
231
120
        _num_invulnerables: usize,
232
120
    ) -> Vec<AccountId> {
233
120
        // Default impl: no collators are invulnerables
234
120
        vec![]
235
120
    }
236
}
237

            
238
/// Helper trait for pallet_collator_assignment to be able to not assign collators to container chains with no credits
239
/// in pallet_services_payment
240
pub trait RemoveParaIdsWithNoCredits {
241
    /// Remove para ids with not enough credits. The resulting order will affect priority: the first para id in the list
242
    /// will be the first one to get collators.
243
    fn remove_para_ids_with_no_credits(
244
        para_ids: &mut Vec<ParaId>,
245
        currently_assigned: &BTreeSet<ParaId>,
246
    );
247

            
248
    /// Make those para ids valid by giving them enough credits, for benchmarking.
249
    #[cfg(feature = "runtime-benchmarks")]
250
    fn make_valid_para_ids(para_ids: &[ParaId]);
251
}
252

            
253
impl RemoveParaIdsWithNoCredits for () {
254
576
    fn remove_para_ids_with_no_credits(
255
576
        _para_ids: &mut Vec<ParaId>,
256
576
        _currently_assigned: &BTreeSet<ParaId>,
257
576
    ) {
258
576
    }
259

            
260
    #[cfg(feature = "runtime-benchmarks")]
261
    fn make_valid_para_ids(_para_ids: &[ParaId]) {}
262
}
263

            
264
pub trait RelayStorageRootProvider {
265
    fn get_relay_storage_root(relay_block_number: u32) -> Option<H256>;
266

            
267
    #[cfg(feature = "runtime-benchmarks")]
268
    fn set_relay_storage_root(relay_block_number: u32, storage_root: Option<H256>);
269
}
270

            
271
impl RelayStorageRootProvider for () {
272
    fn get_relay_storage_root(_relay_block_number: u32) -> Option<H256> {
273
        None
274
    }
275

            
276
    #[cfg(feature = "runtime-benchmarks")]
277
    fn set_relay_storage_root(relay_block_number: u32, storage_root: Option<H256>) {}
278
}
279

            
280
/// Information extracted from the latest container chain header
281
#[derive(
282
    Default,
283
    Clone,
284
    Encode,
285
    Decode,
286
    PartialEq,
287
    sp_core::RuntimeDebug,
288
704
    scale_info::TypeInfo,
289
    MaxEncodedLen,
290
    Serialize,
291
    Deserialize,
292
)]
293
pub struct ContainerChainBlockInfo<AccountId> {
294
    pub block_number: BlockNumber,
295
    pub author: AccountId,
296
    pub latest_slot_number: Slot,
297
}
298

            
299
pub trait LatestAuthorInfoFetcher<AccountId> {
300
    fn get_latest_author_info(para_id: ParaId) -> Option<ContainerChainBlockInfo<AccountId>>;
301
}
302

            
303
pub trait StorageDeposit<Data, Balance> {
304
    fn compute_deposit(data: &Data) -> Result<Balance, DispatchErrorWithPostInfo>;
305
}
306

            
307
pub struct BytesDeposit<BaseCost, ByteCost>(PhantomData<(BaseCost, ByteCost)>);
308
impl<Data, Balance, BaseCost, ByteCost> StorageDeposit<Data, Balance>
309
    for BytesDeposit<BaseCost, ByteCost>
310
where
311
    Data: Encode,
312
    Balance: TryFrom<usize> + CheckedAdd + CheckedMul,
313
    BaseCost: Get<Balance>,
314
    ByteCost: Get<Balance>,
315
{
316
90
    fn compute_deposit(data: &Data) -> Result<Balance, DispatchErrorWithPostInfo> {
317
90
        let base = BaseCost::get();
318
90
        let byte = ByteCost::get();
319
90
        let size: Balance = data
320
90
            .encoded_size()
321
90
            .try_into()
322
90
            .map_err(|_| ArithmeticError::Overflow)?;
323

            
324
90
        let deposit = byte
325
90
            .checked_mul(&size)
326
90
            .ok_or(ArithmeticError::Overflow)?
327
90
            .checked_add(&base)
328
90
            .ok_or(ArithmeticError::Overflow)?;
329

            
330
90
        Ok(deposit)
331
90
    }
332
}