1#![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#[derive(Debug, Clone)]
82pub enum CompatibilityMode<N> {
83 None,
85 UseInitializeBlock {
96 until: N,
105 },
106}
107
108impl<N> Default for CompatibilityMode<N> {
109 fn default() -> Self {
110 Self::None
111 }
112}
113
114pub struct StartAuraParams<C, SC, I, PF, SO, L, CIDP, BS, N> {
116 pub slot_duration: SlotDuration,
118 pub client: Arc<C>,
120 pub select_chain: SC,
122 pub block_import: I,
124 pub proposer_factory: PF,
126 pub sync_oracle: SO,
128 pub justification_sync_link: L,
130 pub create_inherent_data_providers: CIDP,
132 pub force_authoring: bool,
134 pub backoff_authoring_blocks: Option<BS>,
136 pub keystore: KeystorePtr,
138 pub block_proposal_slot_portion: SlotProportion,
144 pub max_block_proposal_slot_portion: Option<SlotProportion>,
147 pub telemetry: Option<TelemetryHandle>,
149 pub compatibility_mode: CompatibilityMode<N>,
153}
154
155pub 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
217pub struct BuildAuraWorkerParams<C, I, PF, SO, L, BS, N> {
219 pub client: Arc<C>,
221 pub block_import: I,
223 pub proposer_factory: PF,
225 pub sync_oracle: SO,
227 pub justification_sync_link: L,
229 pub force_authoring: bool,
231 pub backoff_authoring_blocks: Option<BS>,
233 pub keystore: KeystorePtr,
235 pub block_proposal_slot_portion: SlotProportion,
241 pub max_block_proposal_slot_portion: Option<SlotProportion>,
244 pub telemetry: Option<TelemetryHandle>,
246 pub compatibility_mode: CompatibilityMode<N>,
250}
251
252pub 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#[derive(Debug, thiserror::Error)]
464pub enum Error<B: BlockT> {
465 #[error("Multiple Aura pre-runtime headers")]
467 MultipleHeaders,
468 #[error("No Aura pre-runtime digest found")]
470 NoDigestFound,
471 #[error("Header {0:?} is unsealed")]
473 HeaderUnsealed(B::Hash),
474 #[error("Header {0:?} has a bad seal")]
476 HeaderBadSeal(B::Hash),
477 #[error("Slot Author not found")]
479 SlotAuthorNotFound,
480 #[error("Bad signature on {0:?}")]
482 BadSignature(B::Hash),
483 #[error(transparent)]
485 Client(sp_blockchain::Error),
486 #[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 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 assert!(client.header(block.hash()).unwrap().is_some());
868 }
869}