referrerpolicy=no-referrer-when-downgrade

sc_consensus_slots/
slots.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//! Utility stream for yielding slots in a loop.
20//!
21//! This is used instead of `futures_timer::Interval` because it was unreliable.
22
23use super::{InherentDataProviderExt, Slot, LOG_TARGET};
24use sp_consensus::{SelectChain, SyncOracle};
25use sp_inherents::{CreateInherentDataProviders, InherentDataProvider};
26use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
27
28use futures_timer::Delay;
29use std::time::{Duration, Instant};
30
31/// Returns current duration since unix epoch.
32pub fn duration_now() -> Duration {
33	use std::time::SystemTime;
34	let now = SystemTime::now();
35	now.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_else(|e| {
36		panic!("Current time {:?} is before unix epoch. Something is wrong: {:?}", now, e)
37	})
38}
39
40/// Returns the duration until the next slot from now.
41pub fn time_until_next_slot(slot_duration: Duration) -> Duration {
42	let now = duration_now().as_millis();
43
44	let next_slot = (now + slot_duration.as_millis()) / slot_duration.as_millis();
45	let remaining_millis = next_slot * slot_duration.as_millis() - now;
46	Duration::from_millis(remaining_millis as u64)
47}
48
49/// Information about a slot.
50pub struct SlotInfo<B: BlockT> {
51	/// The slot number as found in the inherent data.
52	pub slot: Slot,
53	/// The instant at which the slot ends.
54	pub ends_at: Instant,
55	/// The inherent data provider.
56	pub create_inherent_data: Box<dyn InherentDataProvider>,
57	/// Slot duration.
58	pub duration: Duration,
59	/// The chain header this slot is based on.
60	pub chain_head: B::Header,
61	/// Some potential block size limit for the block to be authored at this slot.
62	///
63	/// For more information see [`Proposer::propose`](sp_consensus::Proposer::propose).
64	pub block_size_limit: Option<usize>,
65}
66
67impl<B: BlockT> SlotInfo<B> {
68	/// Create a new [`SlotInfo`].
69	///
70	/// `ends_at` is calculated using `timestamp` and `duration`.
71	pub fn new(
72		slot: Slot,
73		create_inherent_data: Box<dyn InherentDataProvider>,
74		duration: Duration,
75		chain_head: B::Header,
76		block_size_limit: Option<usize>,
77	) -> Self {
78		Self {
79			slot,
80			create_inherent_data,
81			duration,
82			chain_head,
83			block_size_limit,
84			ends_at: Instant::now() + time_until_next_slot(duration),
85		}
86	}
87}
88
89/// A stream that returns every time there is a new slot.
90pub(crate) struct Slots<Block, SC, IDP, SO> {
91	last_slot: Slot,
92	slot_duration: Duration,
93	until_next_slot: Option<Delay>,
94	create_inherent_data_providers: IDP,
95	select_chain: SC,
96	sync_oracle: SO,
97	_phantom: std::marker::PhantomData<Block>,
98}
99
100impl<Block, SC, IDP, SO> Slots<Block, SC, IDP, SO> {
101	/// Create a new `Slots` stream.
102	pub fn new(
103		slot_duration: Duration,
104		create_inherent_data_providers: IDP,
105		select_chain: SC,
106		sync_oracle: SO,
107	) -> Self {
108		Slots {
109			last_slot: 0.into(),
110			slot_duration,
111			until_next_slot: None,
112			create_inherent_data_providers,
113			select_chain,
114			sync_oracle,
115			_phantom: Default::default(),
116		}
117	}
118}
119
120impl<Block, SC, IDP, SO> Slots<Block, SC, IDP, SO>
121where
122	Block: BlockT,
123	SC: SelectChain<Block>,
124	IDP: CreateInherentDataProviders<Block, ()> + 'static,
125	IDP::InherentDataProviders: crate::InherentDataProviderExt,
126	SO: SyncOracle,
127{
128	/// Returns a future that fires when the next slot starts.
129	pub async fn next_slot(&mut self) -> SlotInfo<Block> {
130		loop {
131			// Wait for slot timeout
132			self.until_next_slot
133				.take()
134				.unwrap_or_else(|| {
135					// Schedule first timeout.
136					let wait_dur = time_until_next_slot(self.slot_duration);
137					Delay::new(wait_dur)
138				})
139				.await;
140
141			// Schedule delay for next slot.
142			let wait_dur = time_until_next_slot(self.slot_duration);
143			self.until_next_slot = Some(Delay::new(wait_dur));
144
145			if self.sync_oracle.is_major_syncing() {
146				log::debug!(target: LOG_TARGET, "Skipping slot: major sync is in progress.");
147				continue;
148			}
149
150			let chain_head = match self.select_chain.best_chain().await {
151				Ok(x) => x,
152				Err(e) => {
153					log::warn!(
154						target: LOG_TARGET,
155						"Unable to author block in slot. No best block header: {}",
156						e,
157					);
158					// Let's retry at the next slot.
159					continue
160				},
161			};
162
163			let inherent_data_providers = match self
164				.create_inherent_data_providers
165				.create_inherent_data_providers(chain_head.hash(), ())
166				.await
167			{
168				Ok(x) => x,
169				Err(e) => {
170					log::warn!(
171						target: LOG_TARGET,
172						"Unable to author block in slot. Failure creating inherent data provider: {}",
173						e,
174					);
175					// Let's retry at the next slot.
176					continue
177				},
178			};
179
180			let slot = inherent_data_providers.slot();
181
182			// Never yield the same slot twice.
183			if slot > self.last_slot {
184				self.last_slot = slot;
185
186				break SlotInfo::new(
187					slot,
188					Box::new(inherent_data_providers),
189					self.slot_duration,
190					chain_head,
191					None,
192				)
193			}
194		}
195	}
196}