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 mocks;
29
#[cfg(test)]
30
mod tests;
31

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

            
61
use {sp_consensus_slots::Slot, sp_core::crypto::Pair};
62

            
63
const LOG_TARGET: &str = "aura::tanssi";
64

            
65
type AuthorityId<P> = <P as Pair>::Public;
66

            
67
/// Get slot author for given block along with authorities.
68
7810
pub(crate) fn slot_author<P: Pair>(
69
7810
    slot: Slot,
70
7810
    authorities: &[AuthorityId<P>],
71
7810
) -> Option<&AuthorityId<P>> {
72
7810
    if authorities.is_empty() {
73
        return None;
74
7810
    }
75
7810

            
76
7810
    let idx = *slot % (authorities.len() as u64);
77
7810
    assert!(
78
7810
        idx <= usize::MAX as u64,
79
        "It is impossible to have a vector with length beyond the address space; qed",
80
    );
81

            
82
7810
    let current_author = authorities.get(idx as usize).expect(
83
7810
        "authorities not empty; index constrained to list length;this is a valid index; qed",
84
7810
    );
85
7810

            
86
7810
    Some(current_author)
87
7810
}
88

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

            
107
    let authorities = runtime_api
108
        .para_id_authorities(*parent_hash, para_id)
109
        .ok()?;
110
    log::info!(
111
        "Authorities found for para {:?} are {:?}",
112
        para_id,
113
        authorities
114
    );
115
    authorities
116
}
117

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

            
136
    let slot_frequency = runtime_api
137
        .parathread_slot_frequency(*parent_hash, para_id)
138
        .ok()?;
139
    log::debug!("slot_freq for para {:?} is {:?}", para_id, slot_frequency);
140
    slot_frequency
141
}
142

            
143
use {
144
    nimbus_primitives::{NimbusId, NimbusPair, NIMBUS_KEY_ID},
145
    tp_traits::SlotFrequency,
146
};
147

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

            
168
    // Print a more helpful message than "not eligible" when there are no keys at all.
169
    if available_keys.is_empty() {
170
        log::warn!(
171
            target: LOG_TARGET,
172
            "🔏 No Nimbus keys available. We will not be able to author."
173
        );
174
        return None;
175
    }
176

            
177
    // Iterate keys until we find an eligible one, or run out of candidates.
178
    for type_public_pair in available_keys {
179
        if let Ok(nimbus_id) = NimbusId::from_slice(&type_public_pair) {
180
            // If we dont find any parachain that we are assigned to, return none
181

            
182
            if let Ok(Some(para_id)) = client
183
                .check_para_id_assignment(*parent_hash, nimbus_id.clone())
184
                .await
185
            {
186
                log::debug!("Para id found for assignment {:?}", para_id);
187

            
188
                return Some((nimbus_id.into(), para_id));
189
            } else {
190
                log::debug!("No Para id found for assignment {:?}", nimbus_id);
191
            }
192
        } else {
193
            log::debug!("Invalid nimbus id: {:?}", type_public_pair);
194
        }
195
    }
196

            
197
    None
198
}
199

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

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

            
229
    // Iterate keys until we find an eligible one, or run out of candidates.
230
    for type_public_pair in available_keys {
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)) = client
235
                .check_para_id_assignment_next_session(*parent_hash, nimbus_id.clone())
236
                .await
237
            {
238
                log::debug!("Para id found for assignment {:?}", para_id);
239

            
240
                return Some((nimbus_id.into(), para_id));
241
            } else {
242
                log::debug!("No Para id found for assignment {:?}", nimbus_id);
243
            }
244
        } else {
245
            log::debug!("Invalid nimbus id: {:?}", type_public_pair);
246
        }
247
    }
248

            
249
    None
250
}