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