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