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},
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
        Ok(weight)
70
    }
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
    fn get_para_tip(_: ParaId) -> Option<Balance> {
82
        None
83
    }
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
198
    pub fn should_parathread_author_block(
157
198
        &self,
158
198
        current_slot: Slot,
159
198
        last_block_slot: Slot,
160
198
    ) -> bool {
161
198
        current_slot >= last_block_slot.saturating_add(Slot::from(u64::from(self.min)))
162
198
    }
163
}
164

            
165
impl Default for SlotFrequency {
166
48
    fn default() -> Self {
167
48
        Self { min: 1, max: 1 }
168
48
    }
169
}
170

            
171
#[derive(
172
1584
    Clone, Debug, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq, Serialize, Deserialize,
173
)]
174
pub struct ParathreadParams {
175
    pub slot_frequency: SlotFrequency,
176
}
177

            
178
#[derive(Clone, Debug, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq)]
179
pub struct SessionContainerChains {
180
    pub parachains: Vec<ParaId>,
181
    pub parathreads: Vec<(ParaId, ParathreadParams)>,
182
}
183

            
184
/// Get the list of container chains parachain ids at given
185
/// session index.
186
pub trait GetSessionContainerChains<SessionIndex> {
187
    fn session_container_chains(session_index: SessionIndex) -> SessionContainerChains;
188
    #[cfg(feature = "runtime-benchmarks")]
189
    fn set_session_container_chains(session_index: SessionIndex, container_chains: &[ParaId]);
190
}
191

            
192
/// Returns author for a parachain id for the given slot.
193
pub trait GetContainerChainAuthor<AccountId> {
194
    fn author_for_slot(slot: Slot, para_id: ParaId) -> Option<AccountId>;
195
    #[cfg(feature = "runtime-benchmarks")]
196
    fn set_authors_for_para_id(para_id: ParaId, authors: Vec<AccountId>);
197
}
198

            
199
/// Returns the host configuration composed of the amount of collators assigned
200
/// to the orchestrator chain, and how many collators are assigned per container chain.
201
pub trait GetHostConfiguration<SessionIndex> {
202
    fn max_collators(session_index: SessionIndex) -> u32;
203
    fn min_collators_for_orchestrator(session_index: SessionIndex) -> u32;
204
    fn max_collators_for_orchestrator(session_index: SessionIndex) -> u32;
205
    fn collators_per_container(session_index: SessionIndex) -> u32;
206
    fn collators_per_parathread(session_index: SessionIndex) -> u32;
207
    #[cfg(feature = "runtime-benchmarks")]
208
    fn set_host_configuration(_session_index: SessionIndex) {}
209
}
210

            
211
/// Returns current session index.
212
pub trait GetSessionIndex<SessionIndex> {
213
    fn session_index() -> SessionIndex;
214
}
215

            
216
/// Should pallet_collator_assignment trigger a full rotation on this session?
217
pub trait ShouldRotateAllCollators<SessionIndex> {
218
    fn should_rotate_all_collators(session_index: SessionIndex) -> bool;
219
}
220

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

            
230
/// Helper trait for pallet_collator_assignment to be able to not assign collators to container chains with no credits
231
/// in pallet_services_payment
232
pub trait RemoveParaIdsWithNoCredits {
233
    /// Remove para ids with not enough credits. The resulting order will affect priority: the first para id in the list
234
    /// will be the first one to get collators.
235
    fn remove_para_ids_with_no_credits(
236
        para_ids: &mut Vec<ParaId>,
237
        currently_assigned: &BTreeSet<ParaId>,
238
    );
239

            
240
    /// Make those para ids valid by giving them enough credits, for benchmarking.
241
    #[cfg(feature = "runtime-benchmarks")]
242
    fn make_valid_para_ids(para_ids: &[ParaId]);
243
}
244

            
245
pub trait RelayStorageRootProvider {
246
    fn get_relay_storage_root(relay_block_number: u32) -> Option<H256>;
247

            
248
    #[cfg(feature = "runtime-benchmarks")]
249
    fn set_relay_storage_root(relay_block_number: u32, storage_root: Option<H256>);
250
}
251

            
252
/// Information extracted from the latest container chain header
253
#[derive(
254
    Default,
255
    Clone,
256
    Encode,
257
    Decode,
258
    PartialEq,
259
    sp_core::RuntimeDebug,
260
704
    scale_info::TypeInfo,
261
    MaxEncodedLen,
262
    Serialize,
263
    Deserialize,
264
)]
265
pub struct ContainerChainBlockInfo<AccountId> {
266
    pub block_number: BlockNumber,
267
    pub author: AccountId,
268
    pub latest_slot_number: Slot,
269
}
270

            
271
pub trait LatestAuthorInfoFetcher<AccountId> {
272
    fn get_latest_author_info(para_id: ParaId) -> Option<ContainerChainBlockInfo<AccountId>>;
273
}
274

            
275
pub trait StorageDeposit<Data, Balance> {
276
    fn compute_deposit(data: &Data) -> Result<Balance, DispatchErrorWithPostInfo>;
277
}
278

            
279
pub struct BytesDeposit<BaseCost, ByteCost>(PhantomData<(BaseCost, ByteCost)>);
280
impl<Data, Balance, BaseCost, ByteCost> StorageDeposit<Data, Balance>
281
    for BytesDeposit<BaseCost, ByteCost>
282
where
283
    Data: Encode,
284
    Balance: TryFrom<usize> + CheckedAdd + CheckedMul,
285
    BaseCost: Get<Balance>,
286
    ByteCost: Get<Balance>,
287
{
288
90
    fn compute_deposit(data: &Data) -> Result<Balance, DispatchErrorWithPostInfo> {
289
90
        let base = BaseCost::get();
290
90
        let byte = ByteCost::get();
291
90
        let size: Balance = data
292
90
            .encoded_size()
293
90
            .try_into()
294
90
            .map_err(|_| ArithmeticError::Overflow)?;
295

            
296
90
        let deposit = byte
297
90
            .checked_mul(&size)
298
90
            .ok_or(ArithmeticError::Overflow)?
299
90
            .checked_add(&base)
300
90
            .ok_or(ArithmeticError::Overflow)?;
301

            
302
90
        Ok(deposit)
303
90
    }
304
}