referrerpolicy=no-referrer-when-downgrade

sc_consensus_manual_seal/consensus/
timestamp.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//! Mocked timestamp inherent, allows for manual seal to create blocks for runtimes
20//! that expect this inherent.
21
22use crate::Error;
23use sc_client_api::{AuxStore, UsageProvider};
24use sp_api::ProvideRuntimeApi;
25use sp_blockchain::HeaderBackend;
26use sp_consensus_aura::{
27	sr25519::{AuthorityId, AuthoritySignature},
28	AuraApi,
29};
30use sp_consensus_babe::BabeApi;
31use sp_consensus_slots::{Slot, SlotDuration};
32use sp_inherents::{InherentData, InherentDataProvider, InherentIdentifier};
33use sp_runtime::traits::{Block as BlockT, Zero};
34use sp_timestamp::{InherentType, INHERENT_IDENTIFIER};
35use std::{
36	sync::{atomic, Arc},
37	time::SystemTime,
38};
39
40/// Provide duration since unix epoch in millisecond for timestamp inherent.
41/// Mocks the timestamp inherent to always produce a valid timestamp for the next slot.
42///
43/// This works by either fetching the `slot_number` from the most recent header and dividing
44/// that value by `slot_duration` in order to fork chains that expect this inherent.
45///
46/// It produces timestamp inherents that are increased by `slot_duration` whenever
47/// `provide_inherent_data` is called.
48pub struct SlotTimestampProvider {
49	// holds the unix millisecond timestamp for the most recent block
50	unix_millis: atomic::AtomicU64,
51	// configured slot_duration in the runtime
52	slot_duration: SlotDuration,
53}
54
55impl SlotTimestampProvider {
56	/// Create a new mocked time stamp provider, for babe.
57	pub fn new_babe<B, C>(client: Arc<C>) -> Result<Self, Error>
58	where
59		B: BlockT,
60		C: AuxStore + HeaderBackend<B> + ProvideRuntimeApi<B> + UsageProvider<B>,
61		C::Api: BabeApi<B>,
62	{
63		let slot_duration = sc_consensus_babe::configuration(&*client)?.slot_duration();
64
65		let time = Self::with_header(&client, slot_duration, |header| {
66			let slot_number = *sc_consensus_babe::find_pre_digest::<B>(&header)
67				.map_err(|err| format!("{}", err))?
68				.slot();
69			Ok(slot_number)
70		})?;
71
72		Ok(Self { unix_millis: atomic::AtomicU64::new(time), slot_duration })
73	}
74
75	/// Create a new mocked time stamp provider, for aura
76	pub fn new_aura<B, C>(client: Arc<C>) -> Result<Self, Error>
77	where
78		B: BlockT,
79		C: AuxStore + HeaderBackend<B> + ProvideRuntimeApi<B> + UsageProvider<B>,
80		C::Api: AuraApi<B, AuthorityId>,
81	{
82		let slot_duration = sc_consensus_aura::slot_duration(&*client)?;
83
84		let time = Self::with_header(&client, slot_duration, |header| {
85			let slot_number = *sc_consensus_aura::find_pre_digest::<B, AuthoritySignature>(&header)
86				.map_err(|err| format!("{}", err))?;
87			Ok(slot_number)
88		})?;
89
90		Ok(Self { unix_millis: atomic::AtomicU64::new(time), slot_duration })
91	}
92
93	fn with_header<F, C, B>(
94		client: &Arc<C>,
95		slot_duration: SlotDuration,
96		func: F,
97	) -> Result<u64, Error>
98	where
99		B: BlockT,
100		C: AuxStore + HeaderBackend<B> + UsageProvider<B>,
101		F: Fn(B::Header) -> Result<u64, Error>,
102	{
103		let info = client.info();
104
105		// looks like this isn't the first block, rehydrate the fake time.
106		// otherwise we'd be producing blocks for older slots.
107		let time = if info.best_number != Zero::zero() {
108			let header = client
109				.header(info.best_hash)?
110				.ok_or_else(|| "best header not found in the db!".to_string())?;
111			let slot = func(header)?;
112			// add the slot duration so there's no collision of slots
113			(slot * slot_duration.as_millis() as u64) + slot_duration.as_millis() as u64
114		} else {
115			// this is the first block, use the correct time.
116			let now = SystemTime::now();
117			now.duration_since(SystemTime::UNIX_EPOCH)
118				.map_err(|err| Error::StringError(format!("{}", err)))?
119				.as_millis() as u64
120		};
121
122		Ok(time)
123	}
124
125	/// Get the current slot number
126	pub fn slot(&self) -> Slot {
127		Slot::from_timestamp(
128			self.unix_millis.load(atomic::Ordering::SeqCst).into(),
129			self.slot_duration,
130		)
131	}
132
133	/// Gets the current time stamp.
134	pub fn timestamp(&self) -> sp_timestamp::Timestamp {
135		sp_timestamp::Timestamp::new(self.unix_millis.load(atomic::Ordering::SeqCst))
136	}
137}
138
139#[async_trait::async_trait]
140impl InherentDataProvider for SlotTimestampProvider {
141	async fn provide_inherent_data(
142		&self,
143		inherent_data: &mut InherentData,
144	) -> Result<(), sp_inherents::Error> {
145		// we update the time here.
146		let new_time: InherentType = self
147			.unix_millis
148			.fetch_add(self.slot_duration.as_millis() as u64, atomic::Ordering::SeqCst)
149			.into();
150		inherent_data.put_data(INHERENT_IDENTIFIER, &new_time)?;
151		Ok(())
152	}
153
154	async fn try_handle_error(
155		&self,
156		_: &InherentIdentifier,
157		_: &[u8],
158	) -> Option<Result<(), sp_inherents::Error>> {
159		None
160	}
161}