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
//! The Tanssi AuRa consensus algorithm for orchestrator chain and container chain collators.    
18
//! This file contains those functions that are used by consensus_orchestrator.rs structs and
19
//! and traits
20
//! slot_author returns the author based on the slot number and authorities provided (aura-like)
21
//! authorities retrieves the current set of authorities based on the first eligible key found in the keystore
22

            
23
pub mod collators;
24
mod consensus_orchestrator;
25
mod manual_seal;
26

            
27
#[cfg(test)]
28
mod tests;
29

            
30
pub use {
31
    crate::consensus_orchestrator::OrchestratorAuraWorkerAuxData,
32
    cumulus_primitives_core::ParaId,
33
    dp_consensus::TanssiAuthorityAssignmentApi,
34
    manual_seal::{
35
        get_aura_id_from_seed, ContainerManualSealAuraConsensusDataProvider,
36
        OrchestratorManualSealAuraConsensusDataProvider,
37
    },
38
    pallet_registrar_runtime_api::OnDemandBlockProductionApi,
39
    parity_scale_codec::{Decode, Encode},
40
    sc_consensus_aura::{
41
        find_pre_digest, slot_duration, AuraVerifier, BuildAuraWorkerParams, CompatibilityMode,
42
        SlotProportion,
43
    },
44
    sc_consensus_slots::InherentDataProviderExt,
45
    sp_api::{Core, ProvideRuntimeApi},
46
    sp_application_crypto::AppPublic,
47
    sp_consensus::Error as ConsensusError,
48
    sp_core::crypto::{ByteArray, Public},
49
    sp_keystore::{Keystore, KeystorePtr},
50
    sp_runtime::traits::{Block as BlockT, Header as HeaderT, Member, NumberFor},
51
    std::hash::Hash,
52
};
53

            
54
use {sp_consensus_slots::Slot, sp_core::crypto::Pair};
55

            
56
const LOG_TARGET: &str = "aura::tanssi";
57

            
58
type AuthorityId<P> = <P as Pair>::Public;
59

            
60
/// Get slot author for given block along with authorities.
61
6825
pub(crate) fn slot_author<P: Pair>(
62
6825
    slot: Slot,
63
6825
    authorities: &[AuthorityId<P>],
64
6825
) -> Option<&AuthorityId<P>> {
65
6825
    if authorities.is_empty() {
66
        return None;
67
6825
    }
68
6825

            
69
6825
    let idx = *slot % (authorities.len() as u64);
70
6825
    assert!(
71
6825
        idx <= usize::MAX as u64,
72
        "It is impossible to have a vector with length beyond the address space; qed",
73
    );
74

            
75
6825
    let current_author = authorities.get(idx as usize).expect(
76
6825
        "authorities not empty; index constrained to list length;this is a valid index; qed",
77
6825
    );
78
6825

            
79
6825
    Some(current_author)
80
6825
}
81

            
82
/// Return the set of authorities assigned to the paraId where
83
/// the first eligible key from the keystore is collating
84
1
pub fn authorities<B, C, P>(
85
1
    client: &C,
86
1
    parent_hash: &B::Hash,
87
1
    para_id: ParaId,
88
1
) -> Option<Vec<AuthorityId<P>>>
89
1
where
90
1
    P: Pair + Send + Sync,
91
1
    P::Public: AppPublic + Hash + Member + Encode + Decode,
92
1
    P::Signature: TryFrom<Vec<u8>> + Hash + Member + Encode + Decode,
93
1
    B: BlockT,
94
1
    C: ProvideRuntimeApi<B>,
95
1
    C::Api: TanssiAuthorityAssignmentApi<B, AuthorityId<P>>,
96
1
    AuthorityId<P>: From<<NimbusPair as sp_application_crypto::Pair>::Public>,
97
1
{
98
1
    let runtime_api = client.runtime_api();
99

            
100
1
    let authorities = runtime_api
101
1
        .para_id_authorities(*parent_hash, para_id)
102
1
        .ok()?;
103
1
    log::info!(
104
        "Authorities found for para {:?} are {:?}",
105
        para_id,
106
        authorities
107
    );
108
1
    authorities
109
1
}
110

            
111
/// Return the set of authorities assigned to the paraId where
112
/// the first eligible key from the keystore is collating
113
pub fn min_slot_freq<B, C, P>(
114
    client: &C,
115
    parent_hash: &B::Hash,
116
    para_id: ParaId,
117
) -> Option<SlotFrequency>
118
where
119
    P: Pair + Send + Sync + 'static,
120
    P::Public: AppPublic + Hash + Member + Encode + Decode,
121
    P::Signature: TryFrom<Vec<u8>> + Hash + Member + Encode + Decode,
122
    B: BlockT,
123
    C: ProvideRuntimeApi<B>,
124
    C::Api: OnDemandBlockProductionApi<B, ParaId, Slot>,
125
    AuthorityId<P>: From<<NimbusPair as sp_application_crypto::Pair>::Public>,
126
{
127
    let runtime_api = client.runtime_api();
128

            
129
    let slot_frequency = runtime_api
130
        .parathread_slot_frequency(*parent_hash, para_id)
131
        .ok()?;
132
    log::debug!("slot_freq for para {:?} is {:?}", para_id, slot_frequency);
133
    slot_frequency
134
}
135

            
136
use nimbus_primitives::{NimbusId, NimbusPair, NIMBUS_KEY_ID};
137
use tp_traits::SlotFrequency;
138

            
139
/// Grab the first eligible nimbus key from the keystore
140
/// If multiple keys are eligible this function still only returns one
141
/// and makes no guarantees which one as that depends on the keystore's iterator behavior.
142
/// This is the standard way of determining which key to author with.
143
/// It also returns its ParaId assignment
144
pub fn first_eligible_key<B: BlockT, C, P>(
145
    client: &C,
146
    parent_hash: &B::Hash,
147
    keystore: KeystorePtr,
148
) -> Option<(AuthorityId<P>, ParaId)>
149
where
150
    C: ProvideRuntimeApi<B>,
151
    C::Api: TanssiAuthorityAssignmentApi<B, AuthorityId<P>>,
152
    P: Pair + Send + Sync,
153
    P::Public: AppPublic + Hash + Member + Encode + Decode,
154
    P::Signature: TryFrom<Vec<u8>> + Hash + Member + Encode + Decode,
155
    AuthorityId<P>: From<<NimbusPair as sp_application_crypto::Pair>::Public>,
156
{
157
    // Get all the available keys
158
    let available_keys = Keystore::keys(&*keystore, NIMBUS_KEY_ID).ok()?;
159

            
160
    // Print a more helpful message than "not eligible" when there are no keys at all.
161
    if available_keys.is_empty() {
162
        log::warn!(
163
            target: LOG_TARGET,
164
            "🔏 No Nimbus keys available. We will not be able to author."
165
        );
166
        return None;
167
    }
168

            
169
    let runtime_api = client.runtime_api();
170

            
171
    // Iterate keys until we find an eligible one, or run out of candidates.
172
    // If we are skipping prediction, then we author with the first key we find.
173
    // prediction skipping only really makes sense when there is a single key in the keystore.
174
    available_keys.into_iter().find_map(|type_public_pair| {
175
        if let Ok(nimbus_id) = NimbusId::from_slice(&type_public_pair) {
176
            // If we dont find any parachain that we are assigned to, return none
177

            
178
            if let Ok(Some(para_id)) =
179
                runtime_api.check_para_id_assignment(*parent_hash, nimbus_id.clone().into())
180
            {
181
                log::debug!("Para id found for assignment {:?}", para_id);
182

            
183
                Some((nimbus_id.into(), para_id))
184
            } else {
185
                log::debug!("No Para id found for assignment {:?}", nimbus_id);
186

            
187
                None
188
            }
189
        } else {
190
            None
191
        }
192
    })
193
}
194

            
195
/// Grab the first eligible nimbus key from the keystore
196
/// If multiple keys are eligible this function still only returns one
197
/// and makes no guarantees which one as that depends on the keystore's iterator behavior.
198
/// This is the standard way of determining which key to author with.
199
/// It also returns its ParaId assignment
200
pub fn first_eligible_key_next_session<B: BlockT, C, P>(
201
    client: &C,
202
    parent_hash: &B::Hash,
203
    keystore: KeystorePtr,
204
) -> Option<(AuthorityId<P>, ParaId)>
205
where
206
    C: ProvideRuntimeApi<B>,
207
    C::Api: TanssiAuthorityAssignmentApi<B, AuthorityId<P>>,
208
    P: Pair + Send + Sync,
209
    P::Public: AppPublic + Hash + Member + Encode + Decode,
210
    P::Signature: TryFrom<Vec<u8>> + Hash + Member + Encode + Decode,
211
    AuthorityId<P>: From<<NimbusPair as sp_application_crypto::Pair>::Public>,
212
{
213
    // Get all the available keys
214
    let available_keys = Keystore::keys(&*keystore, NIMBUS_KEY_ID).ok()?;
215

            
216
    // Print a more helpful message than "not eligible" when there are no keys at all.
217
    if available_keys.is_empty() {
218
        log::warn!(
219
            target: LOG_TARGET,
220
            "🔏 No Nimbus keys available. We will not be able to author."
221
        );
222
        return None;
223
    }
224

            
225
    let runtime_api = client.runtime_api();
226

            
227
    // Iterate keys until we find an eligible one, or run out of candidates.
228
    // If we are skipping prediction, then we author with the first key we find.
229
    // prediction skipping only really makes sense when there is a single key in the keystore.
230
    available_keys.into_iter().find_map(|type_public_pair| {
231
        if let Ok(nimbus_id) = NimbusId::from_slice(&type_public_pair) {
232
            // If we dont find any parachain that we are assigned to, return none
233

            
234
            if let Ok(Some(para_id)) = runtime_api
235
                .check_para_id_assignment_next_session(*parent_hash, nimbus_id.clone().into())
236
            {
237
                log::debug!("Para id found for assignment {:?}", para_id);
238

            
239
                Some((nimbus_id.into(), para_id))
240
            } else {
241
                log::debug!("No Para id found for assignment {:?}", nimbus_id);
242

            
243
                None
244
            }
245
        } else {
246
            None
247
        }
248
    })
249
}