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::{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#[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>(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#[derive(Debug, thiserror::Error)]
464pub enum Error<B: BlockT> {
465 #[error("Multiple AuRa pre-runtime digests")]
467 MultiplePreDigests,
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::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 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 assert!(client.header(block.hash()).unwrap().is_some());
872 }
873}