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