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::{ApiExt, 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>(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>(&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 digests
466	#[error("Multiple AuRa pre-runtime digests")]
467	MultiplePreDigests,
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::MultiplePreDigests => {
501				Error::MultiplePreDigests
502			},
503			crate::standalone::PreDigestLookupError::NoDigestFound => Error::NoDigestFound,
504		}
505	}
506}
507
508fn fetch_authorities_from_runtime<A, B, C>(
509	client: &C,
510	parent_hash: B::Hash,
511	context_block_number: NumberFor<B>,
512	compatibility_mode: &CompatibilityMode<NumberFor<B>>,
513) -> Result<Vec<A>, ConsensusError>
514where
515	A: Codec + Debug,
516	B: BlockT,
517	C: ProvideRuntimeApi<B>,
518	C::Api: AuraApi<B, A>,
519{
520	let mut runtime_api = client.runtime_api();
521
522	match compatibility_mode {
523		CompatibilityMode::None => {},
524		// Use `initialize_block` until we hit the block that should disable the mode.
525		CompatibilityMode::UseInitializeBlock { until } => {
526			if *until > context_block_number {
527				runtime_api
528					.initialize_block(
529						parent_hash,
530						&B::Header::new(
531							context_block_number,
532							Default::default(),
533							Default::default(),
534							parent_hash,
535							Default::default(),
536						),
537					)
538					.map_err(|_| ConsensusError::InvalidAuthoritiesSet)?;
539			}
540		},
541	}
542
543	runtime_api.set_call_context(sp_core::traits::CallContext::Onchain { import: false });
544	runtime_api
545		.authorities(parent_hash)
546		.ok()
547		.ok_or(ConsensusError::InvalidAuthoritiesSet)
548}
549
550#[cfg(test)]
551mod tests {
552	use super::*;
553	use parking_lot::Mutex;
554	use sc_block_builder::BlockBuilderBuilder;
555	use sc_client_api::BlockchainEvents;
556	use sc_consensus::BoxJustificationImport;
557	use sc_consensus_slots::{BackoffAuthoringOnFinalizedHeadLagging, SimpleSlotWorker};
558	use sc_keystore::LocalKeystore;
559	use sc_network_test::{Block as TestBlock, *};
560	use sp_application_crypto::{key_types::AURA, AppCrypto};
561	use sp_consensus::{NoNetwork as DummyOracle, Proposal, ProposeArgs};
562	use sp_consensus_aura::sr25519::AuthorityPair;
563	use sp_keyring::sr25519::Keyring;
564	use sp_keystore::Keystore;
565	use sp_runtime::traits::{Block as BlockT, Header as _};
566	use sp_timestamp::Timestamp;
567	use std::{
568		task::Poll,
569		time::{Duration, Instant},
570	};
571	use substrate_test_runtime_client::{
572		runtime::{Header, H256},
573		TestClient,
574	};
575
576	const SLOT_DURATION_MS: u64 = 1000;
577
578	type Error = sp_blockchain::Error;
579
580	struct DummyFactory(Arc<TestClient>);
581	struct DummyProposer(Arc<TestClient>);
582
583	impl Environment<TestBlock> for DummyFactory {
584		type Proposer = DummyProposer;
585		type CreateProposer = futures::future::Ready<Result<DummyProposer, Error>>;
586		type Error = Error;
587
588		fn init(&mut self, _: &<TestBlock as BlockT>::Header) -> Self::CreateProposer {
589			futures::future::ready(Ok(DummyProposer(self.0.clone())))
590		}
591	}
592
593	impl Proposer<TestBlock> for DummyProposer {
594		type Error = Error;
595		type Proposal = future::Ready<Result<Proposal<TestBlock>, Error>>;
596
597		fn propose(self, args: ProposeArgs<TestBlock>) -> Self::Proposal {
598			let r = BlockBuilderBuilder::new(&*self.0)
599				.on_parent_block(self.0.chain_info().best_hash)
600				.fetch_parent_block_number(&*self.0)
601				.unwrap()
602				.with_inherent_digests(args.inherent_digests)
603				.build()
604				.unwrap()
605				.build();
606
607			future::ready(
608				r.map(|b| Proposal { block: b.block, storage_changes: b.storage_changes }),
609			)
610		}
611	}
612
613	type AuraVerifier = import_queue::AuraVerifier<
614		PeersFullClient,
615		AuthorityPair,
616		Box<
617			dyn CreateInherentDataProviders<
618				TestBlock,
619				(),
620				InherentDataProviders = (InherentDataProvider,),
621			>,
622		>,
623		TestBlock,
624	>;
625	type AuraPeer = Peer<(), PeersClient>;
626
627	#[derive(Default)]
628	pub struct AuraTestNet {
629		peers: Vec<AuraPeer>,
630	}
631
632	impl TestNetFactory for AuraTestNet {
633		type Verifier = AuraVerifier;
634		type PeerData = ();
635		type BlockImport = PeersClient;
636
637		fn make_verifier(&self, client: PeersClient, _peer_data: &()) -> Self::Verifier {
638			let client = client.as_client();
639			let slot_duration = slot_duration(&*client).expect("slot duration available");
640
641			assert_eq!(slot_duration.as_millis() as u64, SLOT_DURATION_MS);
642			import_queue::AuraVerifier::new(
643				client,
644				Box::new(|_, _| async {
645					let slot = InherentDataProvider::from_timestamp_and_slot_duration(
646						Timestamp::current(),
647						SlotDuration::from_millis(SLOT_DURATION_MS),
648					);
649					Ok((slot,))
650				}),
651				CheckForEquivocation::Yes,
652				None,
653				CompatibilityMode::None,
654			)
655		}
656
657		fn make_block_import(
658			&self,
659			client: PeersClient,
660		) -> (
661			BlockImportAdapter<Self::BlockImport>,
662			Option<BoxJustificationImport<Block>>,
663			Self::PeerData,
664		) {
665			(client.as_block_import(), None, ())
666		}
667
668		fn peer(&mut self, i: usize) -> &mut AuraPeer {
669			&mut self.peers[i]
670		}
671
672		fn peers(&self) -> &Vec<AuraPeer> {
673			&self.peers
674		}
675
676		fn peers_mut(&mut self) -> &mut Vec<AuraPeer> {
677			&mut self.peers
678		}
679
680		fn mut_peers<F: FnOnce(&mut Vec<AuraPeer>)>(&mut self, closure: F) {
681			closure(&mut self.peers);
682		}
683	}
684
685	#[tokio::test]
686	async fn authoring_blocks() {
687		sp_tracing::try_init_simple();
688		let net = AuraTestNet::new(3);
689
690		let peers = &[(0, Keyring::Alice), (1, Keyring::Bob), (2, Keyring::Charlie)];
691
692		let net = Arc::new(Mutex::new(net));
693		let mut import_notifications = Vec::new();
694		let mut aura_futures = Vec::new();
695
696		let mut keystore_paths = Vec::new();
697		for (peer_id, key) in peers {
698			let mut net = net.lock();
699			let peer = net.peer(*peer_id);
700			let client = peer.client().as_client();
701			let select_chain = peer.select_chain().expect("full client has a select chain");
702			let keystore_path = tempfile::tempdir().expect("Creates keystore path");
703			let keystore = Arc::new(
704				LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore."),
705			);
706
707			keystore
708				.sr25519_generate_new(AURA, Some(&key.to_seed()))
709				.expect("Creates authority key");
710			keystore_paths.push(keystore_path);
711
712			let environ = DummyFactory(client.clone());
713			import_notifications.push(
714				client
715					.import_notification_stream()
716					.take_while(|n| {
717						future::ready(!(n.origin != BlockOrigin::Own && n.header.number() < &5))
718					})
719					.for_each(move |_| future::ready(())),
720			);
721
722			let slot_duration = slot_duration(&*client).expect("slot duration available");
723
724			aura_futures.push(
725				start_aura::<AuthorityPair, _, _, _, _, _, _, _, _, _, _>(StartAuraParams {
726					slot_duration,
727					block_import: client.clone(),
728					select_chain,
729					client,
730					proposer_factory: environ,
731					sync_oracle: DummyOracle,
732					justification_sync_link: (),
733					create_inherent_data_providers: |_, _| async {
734						let slot = InherentDataProvider::from_timestamp_and_slot_duration(
735							Timestamp::current(),
736							SlotDuration::from_millis(SLOT_DURATION_MS),
737						);
738
739						Ok((slot,))
740					},
741					force_authoring: false,
742					backoff_authoring_blocks: Some(
743						BackoffAuthoringOnFinalizedHeadLagging::default(),
744					),
745					keystore,
746					block_proposal_slot_portion: SlotProportion::new(0.5),
747					max_block_proposal_slot_portion: None,
748					telemetry: None,
749					compatibility_mode: CompatibilityMode::None,
750				})
751				.expect("Starts aura"),
752			);
753		}
754
755		future::select(
756			future::poll_fn(move |cx| {
757				net.lock().poll(cx);
758				Poll::<()>::Pending
759			}),
760			future::select(future::join_all(aura_futures), future::join_all(import_notifications)),
761		)
762		.await;
763	}
764
765	#[tokio::test]
766	async fn current_node_authority_should_claim_slot() {
767		let net = AuraTestNet::new(4);
768
769		let mut authorities = vec![
770			Keyring::Alice.public().into(),
771			Keyring::Bob.public().into(),
772			Keyring::Charlie.public().into(),
773		];
774
775		let keystore_path = tempfile::tempdir().expect("Creates keystore path");
776		let keystore = LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore.");
777		let public = keystore
778			.sr25519_generate_new(AuthorityPair::ID, None)
779			.expect("Key should be created");
780		authorities.push(public.into());
781
782		let net = Arc::new(Mutex::new(net));
783
784		let mut net = net.lock();
785		let peer = net.peer(3);
786		let client = peer.client().as_client();
787		let environ = DummyFactory(client.clone());
788
789		let mut worker = AuraWorker {
790			client: client.clone(),
791			block_import: client,
792			env: environ,
793			keystore: keystore.into(),
794			sync_oracle: DummyOracle,
795			justification_sync_link: (),
796			force_authoring: false,
797			backoff_authoring_blocks: Some(BackoffAuthoringOnFinalizedHeadLagging::default()),
798			telemetry: None,
799			block_proposal_slot_portion: SlotProportion::new(0.5),
800			max_block_proposal_slot_portion: None,
801			compatibility_mode: Default::default(),
802			_phantom: PhantomData::<fn() -> AuthorityPair>,
803		};
804
805		let head = Header::new(
806			1,
807			H256::from_low_u64_be(0),
808			H256::from_low_u64_be(0),
809			Default::default(),
810			Default::default(),
811		);
812		assert!(worker.claim_slot(&head, 0.into(), &authorities).await.is_none());
813		assert!(worker.claim_slot(&head, 1.into(), &authorities).await.is_none());
814		assert!(worker.claim_slot(&head, 2.into(), &authorities).await.is_none());
815		assert!(worker.claim_slot(&head, 3.into(), &authorities).await.is_some());
816		assert!(worker.claim_slot(&head, 4.into(), &authorities).await.is_none());
817		assert!(worker.claim_slot(&head, 5.into(), &authorities).await.is_none());
818		assert!(worker.claim_slot(&head, 6.into(), &authorities).await.is_none());
819		assert!(worker.claim_slot(&head, 7.into(), &authorities).await.is_some());
820	}
821
822	#[tokio::test]
823	async fn on_slot_returns_correct_block() {
824		let net = AuraTestNet::new(4);
825
826		let keystore_path = tempfile::tempdir().expect("Creates keystore path");
827		let keystore = LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore.");
828		keystore
829			.sr25519_generate_new(AuthorityPair::ID, Some(&Keyring::Alice.to_seed()))
830			.expect("Key should be created");
831
832		let net = Arc::new(Mutex::new(net));
833
834		let mut net = net.lock();
835		let peer = net.peer(3);
836		let client = peer.client().as_client();
837		let environ = DummyFactory(client.clone());
838
839		let mut worker = AuraWorker {
840			client: client.clone(),
841			block_import: client.clone(),
842			env: environ,
843			keystore: keystore.into(),
844			sync_oracle: DummyOracle,
845			justification_sync_link: (),
846			force_authoring: false,
847			backoff_authoring_blocks: Option::<()>::None,
848			telemetry: None,
849			block_proposal_slot_portion: SlotProportion::new(0.5),
850			max_block_proposal_slot_portion: None,
851			compatibility_mode: Default::default(),
852			_phantom: PhantomData::<fn() -> AuthorityPair>,
853		};
854
855		let head = client.expect_header(client.info().genesis_hash).unwrap();
856
857		let block = worker
858			.on_slot(SlotInfo {
859				slot: 0.into(),
860				ends_at: Instant::now() + Duration::from_secs(100),
861				create_inherent_data: Box::new(()),
862				duration: Duration::from_millis(1000),
863				chain_head: head,
864				block_size_limit: None,
865				storage_proof_recorder: None,
866			})
867			.await
868			.unwrap();
869
870		// The returned block should be imported and we should be able to get its header by now.
871		assert!(client.header(block.hash()).unwrap().is_some());
872	}
873}