use crate::Error;
use sc_client_api::{AuxStore, UsageProvider};
use sp_api::ProvideRuntimeApi;
use sp_blockchain::HeaderBackend;
use sp_consensus_aura::{
sr25519::{AuthorityId, AuthoritySignature},
AuraApi,
};
use sp_consensus_babe::BabeApi;
use sp_consensus_slots::{Slot, SlotDuration};
use sp_inherents::{InherentData, InherentDataProvider, InherentIdentifier};
use sp_runtime::traits::{Block as BlockT, Zero};
use sp_timestamp::{InherentType, INHERENT_IDENTIFIER};
use std::{
sync::{atomic, Arc},
time::SystemTime,
};
pub struct SlotTimestampProvider {
unix_millis: atomic::AtomicU64,
slot_duration: SlotDuration,
}
impl SlotTimestampProvider {
pub fn new_babe<B, C>(client: Arc<C>) -> Result<Self, Error>
where
B: BlockT,
C: AuxStore + HeaderBackend<B> + ProvideRuntimeApi<B> + UsageProvider<B>,
C::Api: BabeApi<B>,
{
let slot_duration = sc_consensus_babe::configuration(&*client)?.slot_duration();
let time = Self::with_header(&client, slot_duration, |header| {
let slot_number = *sc_consensus_babe::find_pre_digest::<B>(&header)
.map_err(|err| format!("{}", err))?
.slot();
Ok(slot_number)
})?;
Ok(Self { unix_millis: atomic::AtomicU64::new(time), slot_duration })
}
pub fn new_aura<B, C>(client: Arc<C>) -> Result<Self, Error>
where
B: BlockT,
C: AuxStore + HeaderBackend<B> + ProvideRuntimeApi<B> + UsageProvider<B>,
C::Api: AuraApi<B, AuthorityId>,
{
let slot_duration = sc_consensus_aura::slot_duration(&*client)?;
let time = Self::with_header(&client, slot_duration, |header| {
let slot_number = *sc_consensus_aura::find_pre_digest::<B, AuthoritySignature>(&header)
.map_err(|err| format!("{}", err))?;
Ok(slot_number)
})?;
Ok(Self { unix_millis: atomic::AtomicU64::new(time), slot_duration })
}
fn with_header<F, C, B>(
client: &Arc<C>,
slot_duration: SlotDuration,
func: F,
) -> Result<u64, Error>
where
B: BlockT,
C: AuxStore + HeaderBackend<B> + UsageProvider<B>,
F: Fn(B::Header) -> Result<u64, Error>,
{
let info = client.info();
let time = if info.best_number != Zero::zero() {
let header = client
.header(info.best_hash)?
.ok_or_else(|| "best header not found in the db!".to_string())?;
let slot = func(header)?;
(slot * slot_duration.as_millis() as u64) + slot_duration.as_millis() as u64
} else {
let now = SystemTime::now();
now.duration_since(SystemTime::UNIX_EPOCH)
.map_err(|err| Error::StringError(format!("{}", err)))?
.as_millis() as u64
};
Ok(time)
}
pub fn slot(&self) -> Slot {
Slot::from_timestamp(
self.unix_millis.load(atomic::Ordering::SeqCst).into(),
self.slot_duration,
)
}
pub fn timestamp(&self) -> sp_timestamp::Timestamp {
sp_timestamp::Timestamp::new(self.unix_millis.load(atomic::Ordering::SeqCst))
}
}
#[async_trait::async_trait]
impl InherentDataProvider for SlotTimestampProvider {
async fn provide_inherent_data(
&self,
inherent_data: &mut InherentData,
) -> Result<(), sp_inherents::Error> {
let new_time: InherentType = self
.unix_millis
.fetch_add(self.slot_duration.as_millis() as u64, atomic::Ordering::SeqCst)
.into();
inherent_data.put_data(INHERENT_IDENTIFIER, &new_time)?;
Ok(())
}
async fn try_handle_error(
&self,
_: &InherentIdentifier,
_: &[u8],
) -> Option<Result<(), sp_inherents::Error>> {
None
}
}