referrerpolicy=no-referrer-when-downgrade

sc_consensus_aura/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! Aura (Authority-round) consensus in substrate.
20//!
21//! Aura works by having a list of authorities A who are expected to roughly
22//! agree on the current time. Time is divided up into discrete slots of t
23//! seconds each. For each slot s, the author of that slot is A[s % |A|].
24//!
25//! The author is allowed to issue one block but not more during that slot,
26//! and it will be built upon the longest valid chain that has been seen.
27//!
28//! Blocks from future steps will be either deferred or rejected depending on how
29//! far in the future they are.
30//!
31//! NOTE: Aura itself is designed to be generic over the crypto used.
32#![forbid(missing_docs, unsafe_code)]
33use std::{fmt::Debug, marker::PhantomData, pin::Pin, sync::Arc};
34
35use codec::Codec;
36use futures::prelude::*;
37
38use sc_client_api::{backend::AuxStore, BlockOf};
39use sc_consensus::{BlockImport, BlockImportParams, ForkChoiceStrategy, StateAction};
40use sc_consensus_slots::{
41	BackoffAuthoringBlocksStrategy, InherentDataProviderExt, SimpleSlotWorkerToSlotWorker,
42	SlotInfo, StorageChanges,
43};
44use sc_telemetry::TelemetryHandle;
45use sp_api::{Core, ProvideRuntimeApi};
46use sp_application_crypto::AppPublic;
47use sp_blockchain::HeaderBackend;
48use sp_consensus::{BlockOrigin, Environment, Error as ConsensusError, Proposer, SelectChain};
49use sp_consensus_slots::Slot;
50use sp_core::crypto::Pair;
51use sp_inherents::CreateInherentDataProviders;
52use sp_keystore::KeystorePtr;
53use sp_runtime::traits::{Block as BlockT, Header, Member, NumberFor};
54
55mod authorities_tracker;
56mod import_queue;
57pub mod standalone;
58
59pub use crate::standalone::{find_pre_digest, slot_duration};
60pub use authorities_tracker::AuthoritiesTracker;
61pub use import_queue::{
62	build_verifier, import_queue, AuraVerifier, BuildVerifierParams, CheckForEquivocation,
63	ImportQueueParams,
64};
65pub use sc_consensus_slots::SlotProportion;
66pub use sp_consensus::SyncOracle;
67pub use sp_consensus_aura::{
68	digests::CompatibleDigestItem,
69	inherents::{InherentDataProvider, InherentType as AuraInherent, INHERENT_IDENTIFIER},
70	AuraApi, ConsensusLog, SlotDuration, AURA_ENGINE_ID,
71};
72
73const LOG_TARGET: &str = "aura";
74
75type AuthorityId<P> = <P as Pair>::Public;
76
77/// Run `AURA` in a compatibility mode.
78///
79/// This is required for when the chain was launched and later there
80/// was a consensus breaking change.
81#[derive(Debug, Clone)]
82pub enum CompatibilityMode<N> {
83	/// Don't use any compatibility mode.
84	None,
85	/// Call `initialize_block` before doing any runtime calls.
86	///
87	/// Previously the node would execute `initialize_block` before fetching the authorities
88	/// from the runtime. This behaviour changed in: <https://github.com/paritytech/substrate/pull/9132>
89	///
90	/// By calling `initialize_block` before fetching the authorities, on a block that
91	/// would enact a new validator set, the block would already be build/sealed by an
92	/// authority of the new set. With this mode disabled (the default) a block that enacts a new
93	/// set isn't sealed/built by an authority of the new set, however to make new nodes be able to
94	/// sync old chains this compatibility mode exists.
95	UseInitializeBlock {
96		/// The block number until this compatibility mode should be executed. The first runtime
97		/// call in the context of the `until` block (importing it/building it) will disable the
98		/// compatibility mode (i.e. at `until` the default rules will apply). When enabling this
99		/// compatibility mode the `until` block should be a future block on which all nodes will
100		/// have upgraded to a release that includes the updated compatibility mode configuration.
101		/// At `until` block there will be a hard fork when the authority set changes, between the
102		/// old nodes (running with `initialize_block`, i.e. without the compatibility mode
103		/// configuration) and the new nodes.
104		until: N,
105	},
106}
107
108impl<N> Default for CompatibilityMode<N> {
109	fn default() -> Self {
110		Self::None
111	}
112}
113
114/// Parameters of [`start_aura`].
115pub struct StartAuraParams<C, SC, I, PF, SO, L, CIDP, BS, N> {
116	/// The duration of a slot.
117	pub slot_duration: SlotDuration,
118	/// The client to interact with the chain.
119	pub client: Arc<C>,
120	/// A select chain implementation to select the best block.
121	pub select_chain: SC,
122	/// The block import.
123	pub block_import: I,
124	/// The proposer factory to build proposer instances.
125	pub proposer_factory: PF,
126	/// The sync oracle that can give us the current sync status.
127	pub sync_oracle: SO,
128	/// Hook into the sync module to control the justification sync process.
129	pub justification_sync_link: L,
130	/// Something that can create the inherent data providers.
131	pub create_inherent_data_providers: CIDP,
132	/// Should we force the authoring of blocks?
133	pub force_authoring: bool,
134	/// The backoff strategy when we miss slots.
135	pub backoff_authoring_blocks: Option<BS>,
136	/// The keystore used by the node.
137	pub keystore: KeystorePtr,
138	/// The proportion of the slot dedicated to proposing.
139	///
140	/// The block proposing will be limited to this proportion of the slot from the starting of the
141	/// slot. However, the proposing can still take longer when there is some lenience factor
142	/// applied, because there were no blocks produced for some slots.
143	pub block_proposal_slot_portion: SlotProportion,
144	/// The maximum proportion of the slot dedicated to proposing with any lenience factor applied
145	/// due to no blocks being produced.
146	pub max_block_proposal_slot_portion: Option<SlotProportion>,
147	/// Telemetry instance used to report telemetry metrics.
148	pub telemetry: Option<TelemetryHandle>,
149	/// Compatibility mode that should be used.
150	///
151	/// If in doubt, use `Default::default()`.
152	pub compatibility_mode: CompatibilityMode<N>,
153}
154
155/// Start the aura worker. The returned future should be run in a futures executor.
156pub fn start_aura<P, B, C, SC, I, PF, SO, L, CIDP, BS, Error>(
157	StartAuraParams {
158		slot_duration,
159		client,
160		select_chain,
161		block_import,
162		proposer_factory,
163		sync_oracle,
164		justification_sync_link,
165		create_inherent_data_providers,
166		force_authoring,
167		backoff_authoring_blocks,
168		keystore,
169		block_proposal_slot_portion,
170		max_block_proposal_slot_portion,
171		telemetry,
172		compatibility_mode,
173	}: StartAuraParams<C, SC, I, PF, SO, L, CIDP, BS, NumberFor<B>>,
174) -> Result<impl Future<Output = ()>, ConsensusError>
175where
176	P: Pair,
177	P::Public: AppPublic + Member,
178	P::Signature: TryFrom<Vec<u8>> + Member + Codec,
179	B: BlockT,
180	C: ProvideRuntimeApi<B> + BlockOf + AuxStore + HeaderBackend<B> + Send + Sync,
181	C::Api: AuraApi<B, AuthorityId<P>>,
182	SC: SelectChain<B>,
183	I: BlockImport<B> + Send + Sync + 'static,
184	PF: Environment<B, Error = Error> + Send + Sync + 'static,
185	PF::Proposer: Proposer<B, Error = Error>,
186	SO: SyncOracle + Send + Sync + Clone,
187	L: sc_consensus::JustificationSyncLink<B>,
188	CIDP: CreateInherentDataProviders<B, ()> + Send + 'static,
189	CIDP::InherentDataProviders: InherentDataProviderExt + Send,
190	BS: BackoffAuthoringBlocksStrategy<NumberFor<B>> + Send + Sync + 'static,
191	Error: std::error::Error + Send + From<ConsensusError> + 'static,
192{
193	let worker = build_aura_worker::<P, _, _, _, _, _, _, _, _>(BuildAuraWorkerParams {
194		client,
195		block_import,
196		proposer_factory,
197		keystore,
198		sync_oracle: sync_oracle.clone(),
199		justification_sync_link,
200		force_authoring,
201		backoff_authoring_blocks,
202		telemetry,
203		block_proposal_slot_portion,
204		max_block_proposal_slot_portion,
205		compatibility_mode,
206	});
207
208	Ok(sc_consensus_slots::start_slot_worker(
209		slot_duration,
210		select_chain,
211		SimpleSlotWorkerToSlotWorker(worker),
212		sync_oracle,
213		create_inherent_data_providers,
214	))
215}
216
217/// Parameters of [`build_aura_worker`].
218pub struct BuildAuraWorkerParams<C, I, PF, SO, L, BS, N> {
219	/// The client to interact with the chain.
220	pub client: Arc<C>,
221	/// The block import.
222	pub block_import: I,
223	/// The proposer factory to build proposer instances.
224	pub proposer_factory: PF,
225	/// The sync oracle that can give us the current sync status.
226	pub sync_oracle: SO,
227	/// Hook into the sync module to control the justification sync process.
228	pub justification_sync_link: L,
229	/// Should we force the authoring of blocks?
230	pub force_authoring: bool,
231	/// The backoff strategy when we miss slots.
232	pub backoff_authoring_blocks: Option<BS>,
233	/// The keystore used by the node.
234	pub keystore: KeystorePtr,
235	/// The proportion of the slot dedicated to proposing.
236	///
237	/// The block proposing will be limited to this proportion of the slot from the starting of the
238	/// slot. However, the proposing can still take longer when there is some lenience factor
239	/// applied, because there were no blocks produced for some slots.
240	pub block_proposal_slot_portion: SlotProportion,
241	/// The maximum proportion of the slot dedicated to proposing with any lenience factor applied
242	/// due to no blocks being produced.
243	pub max_block_proposal_slot_portion: Option<SlotProportion>,
244	/// Telemetry instance used to report telemetry metrics.
245	pub telemetry: Option<TelemetryHandle>,
246	/// Compatibility mode that should be used.
247	///
248	/// If in doubt, use `Default::default()`.
249	pub compatibility_mode: CompatibilityMode<N>,
250}
251
252/// Build the aura worker.
253///
254/// The caller is responsible for running this worker, otherwise it will do nothing.
255pub fn build_aura_worker<P, B, C, PF, I, SO, L, BS, Error>(
256	BuildAuraWorkerParams {
257		client,
258		block_import,
259		proposer_factory,
260		sync_oracle,
261		justification_sync_link,
262		backoff_authoring_blocks,
263		keystore,
264		block_proposal_slot_portion,
265		max_block_proposal_slot_portion,
266		telemetry,
267		force_authoring,
268		compatibility_mode,
269	}: BuildAuraWorkerParams<C, I, PF, SO, L, BS, NumberFor<B>>,
270) -> impl sc_consensus_slots::SimpleSlotWorker<
271	B,
272	Proposer = PF::Proposer,
273	BlockImport = I,
274	SyncOracle = SO,
275	JustificationSyncLink = L,
276	Claim = P::Public,
277	AuxData = Vec<AuthorityId<P>>,
278>
279where
280	B: BlockT,
281	C: ProvideRuntimeApi<B> + BlockOf + AuxStore + HeaderBackend<B> + Send + Sync,
282	C::Api: AuraApi<B, AuthorityId<P>>,
283	PF: Environment<B, Error = Error> + Send + Sync + 'static,
284	PF::Proposer: Proposer<B, Error = Error>,
285	P: Pair,
286	P::Public: AppPublic + Member,
287	P::Signature: TryFrom<Vec<u8>> + Member + Codec,
288	I: BlockImport<B> + Send + Sync + 'static,
289	Error: std::error::Error + Send + From<ConsensusError> + 'static,
290	SO: SyncOracle + Send + Sync + Clone,
291	L: sc_consensus::JustificationSyncLink<B>,
292	BS: BackoffAuthoringBlocksStrategy<NumberFor<B>> + Send + Sync + 'static,
293{
294	AuraWorker {
295		client,
296		block_import,
297		env: proposer_factory,
298		keystore,
299		sync_oracle,
300		justification_sync_link,
301		force_authoring,
302		backoff_authoring_blocks,
303		telemetry,
304		block_proposal_slot_portion,
305		max_block_proposal_slot_portion,
306		compatibility_mode,
307		_phantom: PhantomData::<fn() -> P>,
308	}
309}
310
311struct AuraWorker<C, E, I, P, SO, L, BS, N> {
312	client: Arc<C>,
313	block_import: I,
314	env: E,
315	keystore: KeystorePtr,
316	sync_oracle: SO,
317	justification_sync_link: L,
318	force_authoring: bool,
319	backoff_authoring_blocks: Option<BS>,
320	block_proposal_slot_portion: SlotProportion,
321	max_block_proposal_slot_portion: Option<SlotProportion>,
322	telemetry: Option<TelemetryHandle>,
323	compatibility_mode: CompatibilityMode<N>,
324	_phantom: PhantomData<fn() -> P>,
325}
326
327#[async_trait::async_trait]
328impl<B, C, E, I, P, Error, SO, L, BS> sc_consensus_slots::SimpleSlotWorker<B>
329	for AuraWorker<C, E, I, P, SO, L, BS, NumberFor<B>>
330where
331	B: BlockT,
332	C: ProvideRuntimeApi<B> + BlockOf + HeaderBackend<B> + Sync,
333	C::Api: AuraApi<B, AuthorityId<P>>,
334	E: Environment<B, Error = Error> + Send + Sync,
335	E::Proposer: Proposer<B, Error = Error>,
336	I: BlockImport<B> + Send + Sync + 'static,
337	P: Pair,
338	P::Public: AppPublic + Member,
339	P::Signature: TryFrom<Vec<u8>> + Member + Codec,
340	SO: SyncOracle + Send + Clone + Sync,
341	L: sc_consensus::JustificationSyncLink<B>,
342	BS: BackoffAuthoringBlocksStrategy<NumberFor<B>> + Send + Sync + 'static,
343	Error: std::error::Error + Send + From<ConsensusError> + 'static,
344{
345	type BlockImport = I;
346	type SyncOracle = SO;
347	type JustificationSyncLink = L;
348	type CreateProposer =
349		Pin<Box<dyn Future<Output = Result<E::Proposer, ConsensusError>> + Send + 'static>>;
350	type Proposer = E::Proposer;
351	type Claim = P::Public;
352	type AuxData = Vec<AuthorityId<P>>;
353
354	fn logging_target(&self) -> &'static str {
355		"aura"
356	}
357
358	fn block_import(&mut self) -> &mut Self::BlockImport {
359		&mut self.block_import
360	}
361
362	fn aux_data(&self, header: &B::Header, _slot: Slot) -> Result<Self::AuxData, ConsensusError> {
363		fetch_authorities_from_runtime(
364			self.client.as_ref(),
365			header.hash(),
366			*header.number() + 1u32.into(),
367			&self.compatibility_mode,
368		)
369	}
370
371	fn authorities_len(&self, authorities: &Self::AuxData) -> Option<usize> {
372		Some(authorities.len())
373	}
374
375	async fn claim_slot(
376		&mut self,
377		_header: &B::Header,
378		slot: Slot,
379		authorities: &Self::AuxData,
380	) -> Option<Self::Claim> {
381		crate::standalone::claim_slot::<P>(slot, authorities, &self.keystore).await
382	}
383
384	fn pre_digest_data(&self, slot: Slot, _claim: &Self::Claim) -> Vec<sp_runtime::DigestItem> {
385		vec![crate::standalone::pre_digest::<P>(slot)]
386	}
387
388	async fn block_import_params(
389		&self,
390		header: B::Header,
391		header_hash: &B::Hash,
392		body: Vec<B::Extrinsic>,
393		storage_changes: StorageChanges<B>,
394		public: Self::Claim,
395		_authorities: Self::AuxData,
396	) -> Result<sc_consensus::BlockImportParams<B>, ConsensusError> {
397		let signature_digest_item =
398			crate::standalone::seal::<_, P>(header_hash, &public, &self.keystore)?;
399
400		let mut import_block = BlockImportParams::new(BlockOrigin::Own, header);
401		import_block.post_digests.push(signature_digest_item);
402		import_block.body = Some(body);
403		import_block.state_action =
404			StateAction::ApplyChanges(sc_consensus::StorageChanges::Changes(storage_changes));
405		import_block.fork_choice = Some(ForkChoiceStrategy::LongestChain);
406
407		Ok(import_block)
408	}
409
410	fn force_authoring(&self) -> bool {
411		self.force_authoring
412	}
413
414	fn should_backoff(&self, slot: Slot, chain_head: &B::Header) -> bool {
415		if let Some(ref strategy) = self.backoff_authoring_blocks {
416			if let Ok(chain_head_slot) = find_pre_digest::<B, P::Signature>(chain_head) {
417				return strategy.should_backoff(
418					*chain_head.number(),
419					chain_head_slot,
420					self.client.info().finalized_number,
421					slot,
422					self.logging_target(),
423				)
424			}
425		}
426		false
427	}
428
429	fn sync_oracle(&mut self) -> &mut Self::SyncOracle {
430		&mut self.sync_oracle
431	}
432
433	fn justification_sync_link(&mut self) -> &mut Self::JustificationSyncLink {
434		&mut self.justification_sync_link
435	}
436
437	fn proposer(&mut self, block: &B::Header) -> Self::CreateProposer {
438		self.env
439			.init(block)
440			.map_err(|e| ConsensusError::ClientImport(format!("{:?}", e)))
441			.boxed()
442	}
443
444	fn telemetry(&self) -> Option<TelemetryHandle> {
445		self.telemetry.clone()
446	}
447
448	fn proposing_remaining_duration(&self, slot_info: &SlotInfo<B>) -> std::time::Duration {
449		let parent_slot = find_pre_digest::<B, P::Signature>(&slot_info.chain_head).ok();
450
451		sc_consensus_slots::proposing_remaining_duration(
452			parent_slot,
453			slot_info,
454			&self.block_proposal_slot_portion,
455			self.max_block_proposal_slot_portion.as_ref(),
456			sc_consensus_slots::SlotLenienceType::Exponential,
457			self.logging_target(),
458		)
459	}
460}
461
462/// Aura Errors
463#[derive(Debug, thiserror::Error)]
464pub enum Error<B: BlockT> {
465	/// Multiple Aura pre-runtime headers
466	#[error("Multiple Aura pre-runtime headers")]
467	MultipleHeaders,
468	/// No Aura pre-runtime digest found
469	#[error("No Aura pre-runtime digest found")]
470	NoDigestFound,
471	/// Header is unsealed
472	#[error("Header {0:?} is unsealed")]
473	HeaderUnsealed(B::Hash),
474	/// Header has a bad seal
475	#[error("Header {0:?} has a bad seal")]
476	HeaderBadSeal(B::Hash),
477	/// Slot Author not found
478	#[error("Slot Author not found")]
479	SlotAuthorNotFound,
480	/// Bad signature
481	#[error("Bad signature on {0:?}")]
482	BadSignature(B::Hash),
483	/// Client Error
484	#[error(transparent)]
485	Client(sp_blockchain::Error),
486	/// Inherents Error
487	#[error("Inherent error: {0}")]
488	Inherent(sp_inherents::Error),
489}
490
491impl<B: BlockT> From<Error<B>> for String {
492	fn from(error: Error<B>) -> String {
493		error.to_string()
494	}
495}
496
497impl<B: BlockT> From<crate::standalone::PreDigestLookupError> for Error<B> {
498	fn from(e: crate::standalone::PreDigestLookupError) -> Self {
499		match e {
500			crate::standalone::PreDigestLookupError::MultipleHeaders => Error::MultipleHeaders,
501			crate::standalone::PreDigestLookupError::NoDigestFound => Error::NoDigestFound,
502		}
503	}
504}
505
506fn fetch_authorities_from_runtime<A, B, C>(
507	client: &C,
508	parent_hash: B::Hash,
509	context_block_number: NumberFor<B>,
510	compatibility_mode: &CompatibilityMode<NumberFor<B>>,
511) -> Result<Vec<A>, ConsensusError>
512where
513	A: Codec + Debug,
514	B: BlockT,
515	C: ProvideRuntimeApi<B>,
516	C::Api: AuraApi<B, A>,
517{
518	let runtime_api = client.runtime_api();
519
520	match compatibility_mode {
521		CompatibilityMode::None => {},
522		// Use `initialize_block` until we hit the block that should disable the mode.
523		CompatibilityMode::UseInitializeBlock { until } =>
524			if *until > context_block_number {
525				runtime_api
526					.initialize_block(
527						parent_hash,
528						&B::Header::new(
529							context_block_number,
530							Default::default(),
531							Default::default(),
532							parent_hash,
533							Default::default(),
534						),
535					)
536					.map_err(|_| ConsensusError::InvalidAuthoritiesSet)?;
537			},
538	}
539
540	runtime_api
541		.authorities(parent_hash)
542		.ok()
543		.ok_or(ConsensusError::InvalidAuthoritiesSet)
544}
545
546#[cfg(test)]
547mod tests {
548	use super::*;
549	use parking_lot::Mutex;
550	use sc_block_builder::BlockBuilderBuilder;
551	use sc_client_api::BlockchainEvents;
552	use sc_consensus::BoxJustificationImport;
553	use sc_consensus_slots::{BackoffAuthoringOnFinalizedHeadLagging, SimpleSlotWorker};
554	use sc_keystore::LocalKeystore;
555	use sc_network_test::{Block as TestBlock, *};
556	use sp_application_crypto::{key_types::AURA, AppCrypto};
557	use sp_consensus::{NoNetwork as DummyOracle, Proposal, ProposeArgs};
558	use sp_consensus_aura::sr25519::AuthorityPair;
559	use sp_keyring::sr25519::Keyring;
560	use sp_keystore::Keystore;
561	use sp_runtime::traits::{Block as BlockT, Header as _};
562	use sp_timestamp::Timestamp;
563	use std::{
564		task::Poll,
565		time::{Duration, Instant},
566	};
567	use substrate_test_runtime_client::{
568		runtime::{Header, H256},
569		TestClient,
570	};
571
572	const SLOT_DURATION_MS: u64 = 1000;
573
574	type Error = sp_blockchain::Error;
575
576	struct DummyFactory(Arc<TestClient>);
577	struct DummyProposer(Arc<TestClient>);
578
579	impl Environment<TestBlock> for DummyFactory {
580		type Proposer = DummyProposer;
581		type CreateProposer = futures::future::Ready<Result<DummyProposer, Error>>;
582		type Error = Error;
583
584		fn init(&mut self, _: &<TestBlock as BlockT>::Header) -> Self::CreateProposer {
585			futures::future::ready(Ok(DummyProposer(self.0.clone())))
586		}
587	}
588
589	impl Proposer<TestBlock> for DummyProposer {
590		type Error = Error;
591		type Proposal = future::Ready<Result<Proposal<TestBlock>, Error>>;
592
593		fn propose(self, args: ProposeArgs<TestBlock>) -> Self::Proposal {
594			let r = BlockBuilderBuilder::new(&*self.0)
595				.on_parent_block(self.0.chain_info().best_hash)
596				.fetch_parent_block_number(&*self.0)
597				.unwrap()
598				.with_inherent_digests(args.inherent_digests)
599				.build()
600				.unwrap()
601				.build();
602
603			future::ready(
604				r.map(|b| Proposal { block: b.block, storage_changes: b.storage_changes }),
605			)
606		}
607	}
608
609	type AuraVerifier = import_queue::AuraVerifier<
610		PeersFullClient,
611		AuthorityPair,
612		Box<
613			dyn CreateInherentDataProviders<
614				TestBlock,
615				(),
616				InherentDataProviders = (InherentDataProvider,),
617			>,
618		>,
619		TestBlock,
620	>;
621	type AuraPeer = Peer<(), PeersClient>;
622
623	#[derive(Default)]
624	pub struct AuraTestNet {
625		peers: Vec<AuraPeer>,
626	}
627
628	impl TestNetFactory for AuraTestNet {
629		type Verifier = AuraVerifier;
630		type PeerData = ();
631		type BlockImport = PeersClient;
632
633		fn make_verifier(&self, client: PeersClient, _peer_data: &()) -> Self::Verifier {
634			let client = client.as_client();
635			let slot_duration = slot_duration(&*client).expect("slot duration available");
636
637			assert_eq!(slot_duration.as_millis() as u64, SLOT_DURATION_MS);
638			import_queue::AuraVerifier::new(
639				client,
640				Box::new(|_, _| async {
641					let slot = InherentDataProvider::from_timestamp_and_slot_duration(
642						Timestamp::current(),
643						SlotDuration::from_millis(SLOT_DURATION_MS),
644					);
645					Ok((slot,))
646				}),
647				CheckForEquivocation::Yes,
648				None,
649				CompatibilityMode::None,
650			)
651		}
652
653		fn make_block_import(
654			&self,
655			client: PeersClient,
656		) -> (
657			BlockImportAdapter<Self::BlockImport>,
658			Option<BoxJustificationImport<Block>>,
659			Self::PeerData,
660		) {
661			(client.as_block_import(), None, ())
662		}
663
664		fn peer(&mut self, i: usize) -> &mut AuraPeer {
665			&mut self.peers[i]
666		}
667
668		fn peers(&self) -> &Vec<AuraPeer> {
669			&self.peers
670		}
671
672		fn peers_mut(&mut self) -> &mut Vec<AuraPeer> {
673			&mut self.peers
674		}
675
676		fn mut_peers<F: FnOnce(&mut Vec<AuraPeer>)>(&mut self, closure: F) {
677			closure(&mut self.peers);
678		}
679	}
680
681	#[tokio::test]
682	async fn authoring_blocks() {
683		sp_tracing::try_init_simple();
684		let net = AuraTestNet::new(3);
685
686		let peers = &[(0, Keyring::Alice), (1, Keyring::Bob), (2, Keyring::Charlie)];
687
688		let net = Arc::new(Mutex::new(net));
689		let mut import_notifications = Vec::new();
690		let mut aura_futures = Vec::new();
691
692		let mut keystore_paths = Vec::new();
693		for (peer_id, key) in peers {
694			let mut net = net.lock();
695			let peer = net.peer(*peer_id);
696			let client = peer.client().as_client();
697			let select_chain = peer.select_chain().expect("full client has a select chain");
698			let keystore_path = tempfile::tempdir().expect("Creates keystore path");
699			let keystore = Arc::new(
700				LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore."),
701			);
702
703			keystore
704				.sr25519_generate_new(AURA, Some(&key.to_seed()))
705				.expect("Creates authority key");
706			keystore_paths.push(keystore_path);
707
708			let environ = DummyFactory(client.clone());
709			import_notifications.push(
710				client
711					.import_notification_stream()
712					.take_while(|n| {
713						future::ready(!(n.origin != BlockOrigin::Own && n.header.number() < &5))
714					})
715					.for_each(move |_| future::ready(())),
716			);
717
718			let slot_duration = slot_duration(&*client).expect("slot duration available");
719
720			aura_futures.push(
721				start_aura::<AuthorityPair, _, _, _, _, _, _, _, _, _, _>(StartAuraParams {
722					slot_duration,
723					block_import: client.clone(),
724					select_chain,
725					client,
726					proposer_factory: environ,
727					sync_oracle: DummyOracle,
728					justification_sync_link: (),
729					create_inherent_data_providers: |_, _| async {
730						let slot = InherentDataProvider::from_timestamp_and_slot_duration(
731							Timestamp::current(),
732							SlotDuration::from_millis(SLOT_DURATION_MS),
733						);
734
735						Ok((slot,))
736					},
737					force_authoring: false,
738					backoff_authoring_blocks: Some(
739						BackoffAuthoringOnFinalizedHeadLagging::default(),
740					),
741					keystore,
742					block_proposal_slot_portion: SlotProportion::new(0.5),
743					max_block_proposal_slot_portion: None,
744					telemetry: None,
745					compatibility_mode: CompatibilityMode::None,
746				})
747				.expect("Starts aura"),
748			);
749		}
750
751		future::select(
752			future::poll_fn(move |cx| {
753				net.lock().poll(cx);
754				Poll::<()>::Pending
755			}),
756			future::select(future::join_all(aura_futures), future::join_all(import_notifications)),
757		)
758		.await;
759	}
760
761	#[tokio::test]
762	async fn current_node_authority_should_claim_slot() {
763		let net = AuraTestNet::new(4);
764
765		let mut authorities = vec![
766			Keyring::Alice.public().into(),
767			Keyring::Bob.public().into(),
768			Keyring::Charlie.public().into(),
769		];
770
771		let keystore_path = tempfile::tempdir().expect("Creates keystore path");
772		let keystore = LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore.");
773		let public = keystore
774			.sr25519_generate_new(AuthorityPair::ID, None)
775			.expect("Key should be created");
776		authorities.push(public.into());
777
778		let net = Arc::new(Mutex::new(net));
779
780		let mut net = net.lock();
781		let peer = net.peer(3);
782		let client = peer.client().as_client();
783		let environ = DummyFactory(client.clone());
784
785		let mut worker = AuraWorker {
786			client: client.clone(),
787			block_import: client,
788			env: environ,
789			keystore: keystore.into(),
790			sync_oracle: DummyOracle,
791			justification_sync_link: (),
792			force_authoring: false,
793			backoff_authoring_blocks: Some(BackoffAuthoringOnFinalizedHeadLagging::default()),
794			telemetry: None,
795			block_proposal_slot_portion: SlotProportion::new(0.5),
796			max_block_proposal_slot_portion: None,
797			compatibility_mode: Default::default(),
798			_phantom: PhantomData::<fn() -> AuthorityPair>,
799		};
800
801		let head = Header::new(
802			1,
803			H256::from_low_u64_be(0),
804			H256::from_low_u64_be(0),
805			Default::default(),
806			Default::default(),
807		);
808		assert!(worker.claim_slot(&head, 0.into(), &authorities).await.is_none());
809		assert!(worker.claim_slot(&head, 1.into(), &authorities).await.is_none());
810		assert!(worker.claim_slot(&head, 2.into(), &authorities).await.is_none());
811		assert!(worker.claim_slot(&head, 3.into(), &authorities).await.is_some());
812		assert!(worker.claim_slot(&head, 4.into(), &authorities).await.is_none());
813		assert!(worker.claim_slot(&head, 5.into(), &authorities).await.is_none());
814		assert!(worker.claim_slot(&head, 6.into(), &authorities).await.is_none());
815		assert!(worker.claim_slot(&head, 7.into(), &authorities).await.is_some());
816	}
817
818	#[tokio::test]
819	async fn on_slot_returns_correct_block() {
820		let net = AuraTestNet::new(4);
821
822		let keystore_path = tempfile::tempdir().expect("Creates keystore path");
823		let keystore = LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore.");
824		keystore
825			.sr25519_generate_new(AuthorityPair::ID, Some(&Keyring::Alice.to_seed()))
826			.expect("Key should be created");
827
828		let net = Arc::new(Mutex::new(net));
829
830		let mut net = net.lock();
831		let peer = net.peer(3);
832		let client = peer.client().as_client();
833		let environ = DummyFactory(client.clone());
834
835		let mut worker = AuraWorker {
836			client: client.clone(),
837			block_import: client.clone(),
838			env: environ,
839			keystore: keystore.into(),
840			sync_oracle: DummyOracle,
841			justification_sync_link: (),
842			force_authoring: false,
843			backoff_authoring_blocks: Option::<()>::None,
844			telemetry: None,
845			block_proposal_slot_portion: SlotProportion::new(0.5),
846			max_block_proposal_slot_portion: None,
847			compatibility_mode: Default::default(),
848			_phantom: PhantomData::<fn() -> AuthorityPair>,
849		};
850
851		let head = client.expect_header(client.info().genesis_hash).unwrap();
852
853		let block = worker
854			.on_slot(SlotInfo {
855				slot: 0.into(),
856				ends_at: Instant::now() + Duration::from_secs(100),
857				create_inherent_data: Box::new(()),
858				duration: Duration::from_millis(1000),
859				chain_head: head,
860				block_size_limit: None,
861				storage_proof_recorder: None,
862			})
863			.await
864			.unwrap();
865
866		// The returned block should be imported and we should be able to get its header by now.
867		assert!(client.header(block.hash()).unwrap().is_some());
868	}
869}