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 use cumulus_primitives_core::{
23
    relay_chain::{BlockNumber, Slot},
24
    ParaId,
25
};
26
use {
27
    core::marker::PhantomData,
28
    frame_support::{
29
        dispatch::DispatchErrorWithPostInfo,
30
        pallet_prelude::{Decode, DispatchResultWithPostInfo, Encode, Get, MaxEncodedLen, Weight},
31
        BoundedVec,
32
    },
33
    sp_core::H256,
34
    sp_runtime::{
35
        app_crypto::sp_core,
36
        traits::{CheckedAdd, CheckedMul},
37
        ArithmeticError,
38
    },
39
    sp_std::{collections::btree_set::BTreeSet, vec::Vec},
40
};
41

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

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

            
67
/// Container chains collator assignment tip prioritization on congestion.
68
/// Tips paras are willing to pay for collator assignment in case of collators demand
69
/// surpasses the offer.
70
pub trait CollatorAssignmentTip<Balance> {
71
    fn get_para_tip(a: ParaId) -> Option<Balance>;
72
}
73

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

            
91
#[impl_trait_for_tuples::impl_for_tuples(5)]
92
impl<AccountId> AuthorNotingHook<AccountId> for Tuple {
93
6584
    fn on_container_author_noted(a: &AccountId, b: BlockNumber, p: ParaId) -> Weight {
94
6584
        let mut weight: Weight = Default::default();
95
6584
        for_tuples!( #( weight.saturating_accrue(Tuple::on_container_author_noted(a, b, p)); )* );
96
6584
        weight
97
6584
    }
98
}
99

            
100
pub trait DistributeRewards<AccountId, Imbalance> {
101
    fn distribute_rewards(rewarded: AccountId, amount: Imbalance) -> DispatchResultWithPostInfo;
102
}
103

            
104
impl<AccountId, Imbalance> DistributeRewards<AccountId, Imbalance> for () {
105
26
    fn distribute_rewards(_rewarded: AccountId, _amount: Imbalance) -> DispatchResultWithPostInfo {
106
26
        Ok(().into())
107
26
    }
108
}
109

            
110
/// Get the current list of container chains parachain ids.
111
pub trait GetCurrentContainerChains {
112
    type MaxContainerChains: Get<u32>;
113

            
114
    fn current_container_chains() -> BoundedVec<ParaId, Self::MaxContainerChains>;
115

            
116
    #[cfg(feature = "runtime-benchmarks")]
117
    fn set_current_container_chains(container_chains: &[ParaId]);
118
}
119

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

            
136
impl SlotFrequency {
137
277
    pub fn should_parathread_buy_core(
138
277
        &self,
139
277
        current_slot: Slot,
140
277
        max_slot_required_to_complete_purchase: Slot,
141
277
        last_block_slot: Slot,
142
277
    ) -> bool {
143
277
        current_slot
144
277
            >= last_block_slot
145
277
                .saturating_add(Slot::from(u64::from(self.min)))
146
277
                .saturating_sub(max_slot_required_to_complete_purchase)
147
277
    }
148
}
149

            
150
impl Default for SlotFrequency {
151
48
    fn default() -> Self {
152
48
        Self { min: 1, max: 1 }
153
48
    }
154
}
155

            
156
352
#[derive(Clone, Debug, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq)]
157
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
158
pub struct ParathreadParams {
159
    pub slot_frequency: SlotFrequency,
160
}
161

            
162
#[derive(Clone, Debug, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq)]
163
pub struct SessionContainerChains {
164
    pub parachains: Vec<ParaId>,
165
    pub parathreads: Vec<(ParaId, ParathreadParams)>,
166
}
167

            
168
/// Get the list of container chains parachain ids at given
169
/// session index.
170
pub trait GetSessionContainerChains<SessionIndex> {
171
    fn session_container_chains(session_index: SessionIndex) -> SessionContainerChains;
172
    #[cfg(feature = "runtime-benchmarks")]
173
    fn set_session_container_chains(session_index: SessionIndex, container_chains: &[ParaId]);
174
}
175

            
176
/// Returns author for a parachain id for the given slot.
177
pub trait GetContainerChainAuthor<AccountId> {
178
    fn author_for_slot(slot: Slot, para_id: ParaId) -> Option<AccountId>;
179
    #[cfg(feature = "runtime-benchmarks")]
180
    fn set_authors_for_para_id(para_id: ParaId, authors: Vec<AccountId>);
181
}
182

            
183
/// Returns the host configuration composed of the amount of collators assigned
184
/// to the orchestrator chain, and how many collators are assigned per container chain.
185
pub trait GetHostConfiguration<SessionIndex> {
186
    fn max_collators(session_index: SessionIndex) -> u32;
187
    fn min_collators_for_orchestrator(session_index: SessionIndex) -> u32;
188
    fn max_collators_for_orchestrator(session_index: SessionIndex) -> u32;
189
    fn collators_per_container(session_index: SessionIndex) -> u32;
190
    fn collators_per_parathread(session_index: SessionIndex) -> u32;
191
    #[cfg(feature = "runtime-benchmarks")]
192
    fn set_host_configuration(_session_index: SessionIndex) {}
193
}
194

            
195
/// Returns current session index.
196
pub trait GetSessionIndex<SessionIndex> {
197
    fn session_index() -> SessionIndex;
198
}
199

            
200
/// Should pallet_collator_assignment trigger a full rotation on this session?
201
pub trait ShouldRotateAllCollators<SessionIndex> {
202
    fn should_rotate_all_collators(session_index: SessionIndex) -> bool;
203
}
204

            
205
/// Helper trait for pallet_collator_assignment to be able to give priority to invulnerables
206
pub trait RemoveInvulnerables<AccountId> {
207
    /// Remove the first n invulnerables from the list of collators. The order should be respected.
208
    fn remove_invulnerables(
209
        collators: &mut Vec<AccountId>,
210
        num_invulnerables: usize,
211
    ) -> Vec<AccountId>;
212
}
213

            
214
/// Helper trait for pallet_collator_assignment to be able to not assign collators to container chains with no credits
215
/// in pallet_services_payment
216
pub trait RemoveParaIdsWithNoCredits {
217
    /// Remove para ids with not enough credits. The resulting order will affect priority: the first para id in the list
218
    /// will be the first one to get collators.
219
    fn remove_para_ids_with_no_credits(
220
        para_ids: &mut Vec<ParaId>,
221
        currently_assigned: &BTreeSet<ParaId>,
222
    );
223

            
224
    /// Make those para ids valid by giving them enough credits, for benchmarking.
225
    #[cfg(feature = "runtime-benchmarks")]
226
    fn make_valid_para_ids(para_ids: &[ParaId]);
227
}
228

            
229
pub trait RelayStorageRootProvider {
230
    fn get_relay_storage_root(relay_block_number: u32) -> Option<H256>;
231

            
232
    #[cfg(feature = "runtime-benchmarks")]
233
    fn set_relay_storage_root(relay_block_number: u32, storage_root: Option<H256>);
234
}
235

            
236
/// Information extracted from the latest container chain header
237
#[derive(
238
    Default,
239
    Clone,
240
    Encode,
241
    Decode,
242
    PartialEq,
243
    sp_core::RuntimeDebug,
244
704
    scale_info::TypeInfo,
245
    MaxEncodedLen,
246
)]
247
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
248
pub struct ContainerChainBlockInfo<AccountId> {
249
    pub block_number: BlockNumber,
250
    pub author: AccountId,
251
    pub latest_slot_number: Slot,
252
}
253

            
254
pub trait LatestAuthorInfoFetcher<AccountId> {
255
    fn get_latest_author_info(para_id: ParaId) -> Option<ContainerChainBlockInfo<AccountId>>;
256
}
257

            
258
pub trait StorageDeposit<Data, Balance> {
259
    fn compute_deposit(data: &Data) -> Result<Balance, DispatchErrorWithPostInfo>;
260
}
261

            
262
pub struct BytesDeposit<BaseCost, ByteCost>(PhantomData<(BaseCost, ByteCost)>);
263
impl<Data, Balance, BaseCost, ByteCost> StorageDeposit<Data, Balance>
264
    for BytesDeposit<BaseCost, ByteCost>
265
where
266
    Data: Encode,
267
    Balance: TryFrom<usize> + CheckedAdd + CheckedMul,
268
    BaseCost: Get<Balance>,
269
    ByteCost: Get<Balance>,
270
{
271
90
    fn compute_deposit(data: &Data) -> Result<Balance, DispatchErrorWithPostInfo> {
272
90
        let base = BaseCost::get();
273
90
        let byte = ByteCost::get();
274
90
        let size: Balance = data
275
90
            .encoded_size()
276
90
            .try_into()
277
90
            .map_err(|_| ArithmeticError::Overflow)?;
278

            
279
90
        let deposit = byte
280
90
            .checked_mul(&size)
281
90
            .ok_or(ArithmeticError::Overflow)?
282
90
            .checked_add(&base)
283
90
            .ok_or(ArithmeticError::Overflow)?;
284

            
285
90
        Ok(deposit)
286
90
    }
287
}