// This file is part of Bizinikiwi. // Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see . //! Utility stream for yielding slots in a loop. //! //! This is used instead of `futures_timer::Interval` because it was unreliable. use super::{InherentDataProviderExt, Slot, LOG_TARGET}; use pezsp_consensus::{SelectChain, SyncOracle}; use pezsp_inherents::{CreateInherentDataProviders, InherentDataProvider}; use pezsp_runtime::traits::{Block as BlockT, Header as HeaderT}; use futures_timer::Delay; use std::time::{Duration, Instant}; /// Returns current duration since unix epoch. pub fn duration_now() -> Duration { use std::time::SystemTime; let now = SystemTime::now(); now.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_else(|e| { panic!("Current time {:?} is before unix epoch. Something is wrong: {:?}", now, e) }) } /// Returns the duration until the next slot from now. pub fn time_until_next_slot(slot_duration: Duration) -> Duration { let now = duration_now().as_millis(); let next_slot = (now + slot_duration.as_millis()) / slot_duration.as_millis(); let remaining_millis = next_slot * slot_duration.as_millis() - now; Duration::from_millis(remaining_millis as u64) } /// Information about a slot. pub struct SlotInfo { /// The slot number as found in the inherent data. pub slot: Slot, /// The instant at which the slot ends. pub ends_at: Instant, /// The inherent data provider. pub create_inherent_data: Box, /// Slot duration. pub duration: Duration, /// The chain header this slot is based on. pub chain_head: B::Header, /// Some potential block size limit for the block to be authored at this slot. /// /// For more information see [`Proposer::propose`](pezsp_consensus::Proposer::propose). pub block_size_limit: Option, } impl SlotInfo { /// Create a new [`SlotInfo`]. /// /// `ends_at` is calculated using `timestamp` and `duration`. pub fn new( slot: Slot, create_inherent_data: Box, duration: Duration, chain_head: B::Header, block_size_limit: Option, ) -> Self { Self { slot, create_inherent_data, duration, chain_head, block_size_limit, ends_at: Instant::now() + time_until_next_slot(duration), } } } /// A stream that returns every time there is a new slot. pub(crate) struct Slots { last_slot: Slot, slot_duration: Duration, until_next_slot: Option, create_inherent_data_providers: IDP, select_chain: SC, sync_oracle: SO, _phantom: std::marker::PhantomData, } impl Slots { /// Create a new `Slots` stream. pub fn new( slot_duration: Duration, create_inherent_data_providers: IDP, select_chain: SC, sync_oracle: SO, ) -> Self { Slots { last_slot: 0.into(), slot_duration, until_next_slot: None, create_inherent_data_providers, select_chain, sync_oracle, _phantom: Default::default(), } } } impl Slots where Block: BlockT, SC: SelectChain, IDP: CreateInherentDataProviders + 'static, IDP::InherentDataProviders: crate::InherentDataProviderExt, SO: SyncOracle, { /// Returns a future that fires when the next slot starts. pub async fn next_slot(&mut self) -> SlotInfo { loop { // Wait for slot timeout self.until_next_slot .take() .unwrap_or_else(|| { // Schedule first timeout. let wait_dur = time_until_next_slot(self.slot_duration); Delay::new(wait_dur) }) .await; // Schedule delay for next slot. let wait_dur = time_until_next_slot(self.slot_duration); self.until_next_slot = Some(Delay::new(wait_dur)); if self.sync_oracle.is_major_syncing() { log::debug!(target: LOG_TARGET, "Skipping slot: major sync is in progress."); continue; } let chain_head = match self.select_chain.best_chain().await { Ok(x) => x, Err(e) => { log::warn!( target: LOG_TARGET, "Unable to author block in slot. No best block header: {}", e, ); // Let's retry at the next slot. continue; }, }; let inherent_data_providers = match self .create_inherent_data_providers .create_inherent_data_providers(chain_head.hash(), ()) .await { Ok(x) => x, Err(e) => { log::warn!( target: LOG_TARGET, "Unable to author block in slot. Failure creating inherent data provider: {}", e, ); // Let's retry at the next slot. continue; }, }; let slot = inherent_data_providers.slot(); // Never yield the same slot twice. if slot > self.last_slot { self.last_slot = slot; break SlotInfo::new( slot, Box::new(inherent_data_providers), self.slot_duration, chain_head, None, ); } } } }