1use std::fmt::Debug;
22
23use log::trace;
24
25use codec::Codec;
26
27use sc_client_api::UsageProvider;
28use sp_api::{ApiExt, Core, ProvideRuntimeApi};
29use sp_application_crypto::{AppCrypto, AppPublic};
30use sp_blockchain::Result as CResult;
31use sp_consensus::Error as ConsensusError;
32use sp_consensus_slots::Slot;
33use sp_core::crypto::{ByteArray, Pair};
34use sp_keystore::KeystorePtr;
35use sp_runtime::{
36 traits::{Block as BlockT, Header, NumberFor, Zero},
37 DigestItem,
38};
39
40pub use sc_consensus_slots::check_equivocation;
41
42use super::{
43 AuraApi, AuthorityId, CompatibilityMode, CompatibleDigestItem, SlotDuration, LOG_TARGET,
44};
45
46pub fn slot_duration<A, B, C>(client: &C) -> CResult<SlotDuration>
48where
49 A: Codec,
50 B: BlockT,
51 C: ProvideRuntimeApi<B> + UsageProvider<B>,
52 C::Api: AuraApi<B, A>,
53{
54 slot_duration_at(client, client.usage_info().chain.best_hash)
55}
56
57pub fn slot_duration_at<A, B, C>(client: &C, block_hash: B::Hash) -> CResult<SlotDuration>
59where
60 A: Codec,
61 B: BlockT,
62 C: ProvideRuntimeApi<B>,
63 C::Api: AuraApi<B, A>,
64{
65 let mut runtime_api = client.runtime_api();
66 runtime_api.set_call_context(sp_core::traits::CallContext::Onchain { import: false });
67 runtime_api.slot_duration(block_hash).map_err(|err| err.into())
68}
69
70pub fn slot_author<P: Pair>(slot: Slot, authorities: &[AuthorityId<P>]) -> Option<&AuthorityId<P>> {
72 if authorities.is_empty() {
73 return None;
74 }
75
76 let idx = *slot % (authorities.len() as u64);
77 assert!(
78 idx <= usize::MAX as u64,
79 "It is impossible to have a vector with length beyond the address space; qed",
80 );
81
82 let current_author = authorities.get(idx as usize).expect(
83 "authorities not empty; index constrained to list length;this is a valid index; qed",
84 );
85
86 Some(current_author)
87}
88
89pub async fn claim_slot<P: Pair>(
94 slot: Slot,
95 authorities: &[AuthorityId<P>],
96 keystore: &KeystorePtr,
97) -> Option<P::Public> {
98 let expected_author = slot_author::<P>(slot, authorities);
99 expected_author.and_then(|p| {
100 if keystore.has_keys(&[(p.to_raw_vec(), sp_application_crypto::key_types::AURA)]) {
101 Some(p.clone())
102 } else {
103 None
104 }
105 })
106}
107
108pub fn pre_digest<P: Pair>(slot: Slot) -> sp_runtime::DigestItem
113where
114 P::Signature: Codec,
115{
116 <DigestItem as CompatibleDigestItem<P::Signature>>::aura_pre_digest(slot)
117}
118
119pub fn seal<Hash, P>(
123 header_hash: &Hash,
124 public: &P::Public,
125 keystore: &KeystorePtr,
126) -> Result<sp_runtime::DigestItem, ConsensusError>
127where
128 Hash: AsRef<[u8]>,
129 P: Pair,
130 P::Signature: Codec + TryFrom<Vec<u8>>,
131 P::Public: AppPublic,
132{
133 let signature = keystore
134 .sign_with(
135 <AuthorityId<P> as AppCrypto>::ID,
136 <AuthorityId<P> as AppCrypto>::CRYPTO_ID,
137 public.as_slice(),
138 header_hash.as_ref(),
139 )
140 .map_err(|e| ConsensusError::CannotSign(format!("{}. Key: {:?}", e, public)))?
141 .ok_or_else(|| {
142 ConsensusError::CannotSign(format!("Could not find key in keystore. Key: {:?}", public))
143 })?;
144
145 let signature = signature
146 .as_slice()
147 .try_into()
148 .map_err(|_| ConsensusError::InvalidSignature(signature, public.to_raw_vec()))?;
149
150 let signature_digest_item =
151 <DigestItem as CompatibleDigestItem<P::Signature>>::aura_seal(signature);
152
153 Ok(signature_digest_item)
154}
155
156#[derive(Debug, thiserror::Error)]
158pub enum PreDigestLookupError {
159 #[error("Multiple Aura pre-runtime headers")]
161 MultipleHeaders,
162 #[error("No Aura pre-runtime digest found")]
164 NoDigestFound,
165}
166
167pub fn find_pre_digest<B: BlockT, Signature: Codec>(
173 header: &B::Header,
174) -> Result<Slot, PreDigestLookupError> {
175 if header.number().is_zero() {
176 return Ok(0.into());
177 }
178
179 let mut pre_digest: Option<Slot> = None;
180 for log in header.digest().logs() {
181 trace!(target: LOG_TARGET, "Checking log {:?}", log);
182 match (CompatibleDigestItem::<Signature>::as_aura_pre_digest(log), pre_digest.is_some()) {
183 (Some(_), true) => return Err(PreDigestLookupError::MultipleHeaders),
184 (None, _) => trace!(target: LOG_TARGET, "Ignoring digest not meant for us"),
185 (s, false) => pre_digest = s,
186 }
187 }
188 pre_digest.ok_or_else(|| PreDigestLookupError::NoDigestFound)
189}
190
191pub fn fetch_authorities_with_compatibility_mode<A, B, C>(
197 client: &C,
198 parent_hash: B::Hash,
199 context_block_number: NumberFor<B>,
200 compatibility_mode: &CompatibilityMode<NumberFor<B>>,
201) -> Result<Vec<A>, ConsensusError>
202where
203 A: Codec + Debug,
204 B: BlockT,
205 C: ProvideRuntimeApi<B>,
206 C::Api: AuraApi<B, A>,
207{
208 let mut runtime_api = client.runtime_api();
209
210 match compatibility_mode {
211 CompatibilityMode::None => {},
212 CompatibilityMode::UseInitializeBlock { until } => {
214 if *until > context_block_number {
215 runtime_api
216 .initialize_block(
217 parent_hash,
218 &B::Header::new(
219 context_block_number,
220 Default::default(),
221 Default::default(),
222 parent_hash,
223 Default::default(),
224 ),
225 )
226 .map_err(|_| ConsensusError::InvalidAuthoritiesSet)?;
227 }
228 },
229 }
230
231 runtime_api.set_call_context(sp_core::traits::CallContext::Onchain { import: false });
232 runtime_api
233 .authorities(parent_hash)
234 .ok()
235 .ok_or(ConsensusError::InvalidAuthoritiesSet)
236}
237
238pub fn fetch_authorities<A, B, C>(
240 client: &C,
241 parent_hash: B::Hash,
242) -> Result<Vec<A>, ConsensusError>
243where
244 A: Codec + Debug,
245 B: BlockT,
246 C: ProvideRuntimeApi<B>,
247 C::Api: AuraApi<B, A>,
248{
249 let mut runtime_api = client.runtime_api();
250 runtime_api.set_call_context(sp_core::traits::CallContext::Onchain { import: false });
251 runtime_api
252 .authorities(parent_hash)
253 .ok()
254 .ok_or(ConsensusError::InvalidAuthoritiesSet)
255}
256
257#[derive(Debug, thiserror::Error)]
259pub enum SealVerificationError<Header> {
260 #[error("Header slot is in the future")]
262 Deferred(Header, Slot),
263
264 #[error("Header is unsealed.")]
266 Unsealed,
267
268 #[error("Header has a malformed seal")]
270 BadSeal,
271
272 #[error("Header has a bad signature")]
274 BadSignature,
275
276 #[error("No slot author for provided slot")]
278 SlotAuthorNotFound,
279
280 #[error("Header has no valid slot pre-digest")]
282 InvalidPreDigest(PreDigestLookupError),
283}
284
285pub fn check_header_slot_and_seal<B: BlockT, P: Pair>(
294 slot_now: Slot,
295 mut header: B::Header,
296 authorities: &[AuthorityId<P>],
297) -> Result<(B::Header, Slot, DigestItem), SealVerificationError<B::Header>>
298where
299 P::Signature: Codec,
300 P::Public: Codec + PartialEq + Clone,
301{
302 let seal = header.digest_mut().pop().ok_or(SealVerificationError::Unsealed)?;
303
304 let sig = seal.as_aura_seal().ok_or(SealVerificationError::BadSeal)?;
305
306 let slot = find_pre_digest::<B, P::Signature>(&header)
307 .map_err(SealVerificationError::InvalidPreDigest)?;
308
309 if slot > slot_now {
310 header.digest_mut().push(seal);
311 return Err(SealVerificationError::Deferred(header, slot));
312 } else {
313 let expected_author =
316 slot_author::<P>(slot, authorities).ok_or(SealVerificationError::SlotAuthorNotFound)?;
317
318 let pre_hash = header.hash();
319
320 if P::verify(&sig, pre_hash.as_ref(), expected_author) {
321 Ok((header, slot, seal))
322 } else {
323 Err(SealVerificationError::BadSignature)
324 }
325 }
326}
327
328#[cfg(test)]
329mod tests {
330 use super::*;
331 use sp_keyring::sr25519::Keyring;
332
333 #[test]
334 fn authorities_call_works() {
335 let client = substrate_test_runtime_client::new();
336
337 assert_eq!(client.chain_info().best_number, 0);
338 assert_eq!(
339 fetch_authorities_with_compatibility_mode(
340 &client,
341 client.chain_info().best_hash,
342 1,
343 &CompatibilityMode::None
344 )
345 .unwrap(),
346 vec![
347 Keyring::Alice.public().into(),
348 Keyring::Bob.public().into(),
349 Keyring::Charlie.public().into()
350 ]
351 );
352
353 assert_eq!(
354 fetch_authorities(&client, client.chain_info().best_hash).unwrap(),
355 vec![
356 Keyring::Alice.public().into(),
357 Keyring::Bob.public().into(),
358 Keyring::Charlie.public().into()
359 ]
360 );
361 }
362}