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::{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 assert!(client.header(res.block.hash()).unwrap().is_some());
881 }
882}