#![cfg_attr(not(feature = "std"), no_std)]
use codec::{Decode, Encode};
use sp_inherents::{InherentData, InherentIdentifier, IsFatalError};
use sp_std::time::Duration;
pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"timstap0";
pub type InherentType = Timestamp;
#[derive(Debug, Encode, Decode, Eq, Clone, Copy, Default, Ord)]
pub struct Timestamp(u64);
impl Timestamp {
pub const fn new(inner: u64) -> Self {
Self(inner)
}
pub const fn as_duration(self) -> Duration {
Duration::from_millis(self.0)
}
pub const fn as_millis(&self) -> u64 {
self.0
}
pub fn checked_sub(self, other: Self) -> Option<Self> {
self.0.checked_sub(other.0).map(Self)
}
#[cfg(feature = "std")]
pub fn current() -> Self {
use std::time::SystemTime;
let now = SystemTime::now();
now.duration_since(SystemTime::UNIX_EPOCH)
.expect("Current time is always after unix epoch; qed")
.into()
}
}
impl sp_std::ops::Deref for Timestamp {
type Target = u64;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl core::ops::Add for Timestamp {
type Output = Self;
fn add(self, other: Self) -> Self {
Self(self.0 + other.0)
}
}
impl core::ops::Add<u64> for Timestamp {
type Output = Self;
fn add(self, other: u64) -> Self {
Self(self.0 + other)
}
}
impl<T: Into<u64> + Copy> core::cmp::PartialEq<T> for Timestamp {
fn eq(&self, eq: &T) -> bool {
self.0 == (*eq).into()
}
}
impl<T: Into<u64> + Copy> core::cmp::PartialOrd<T> for Timestamp {
fn partial_cmp(&self, other: &T) -> Option<core::cmp::Ordering> {
self.0.partial_cmp(&(*other).into())
}
}
#[cfg(feature = "std")]
impl std::fmt::Display for Timestamp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<u64> for Timestamp {
fn from(timestamp: u64) -> Self {
Timestamp(timestamp)
}
}
impl From<Timestamp> for u64 {
fn from(timestamp: Timestamp) -> u64 {
timestamp.0
}
}
impl From<Duration> for Timestamp {
fn from(duration: Duration) -> Self {
Timestamp(duration.as_millis() as u64)
}
}
#[derive(Encode, sp_runtime::RuntimeDebug)]
#[cfg_attr(feature = "std", derive(Decode, thiserror::Error))]
pub enum InherentError {
#[cfg_attr(
feature = "std",
error("The time since the last timestamp is lower than the minimum period.")
)]
TooEarly,
#[cfg_attr(feature = "std", error("The timestamp of the block is too far in the future."))]
TooFarInFuture,
}
impl IsFatalError for InherentError {
fn is_fatal_error(&self) -> bool {
match self {
InherentError::TooEarly => true,
InherentError::TooFarInFuture => true,
}
}
}
impl InherentError {
#[cfg(feature = "std")]
pub fn try_from(id: &InherentIdentifier, mut data: &[u8]) -> Option<Self> {
if id == &INHERENT_IDENTIFIER {
<InherentError as codec::Decode>::decode(&mut data).ok()
} else {
None
}
}
}
pub trait TimestampInherentData {
fn timestamp_inherent_data(&self) -> Result<Option<InherentType>, sp_inherents::Error>;
}
impl TimestampInherentData for InherentData {
fn timestamp_inherent_data(&self) -> Result<Option<InherentType>, sp_inherents::Error> {
self.get_data(&INHERENT_IDENTIFIER)
}
}
#[cfg(feature = "std")]
pub struct InherentDataProvider {
max_drift: InherentType,
timestamp: InherentType,
}
#[cfg(feature = "std")]
impl InherentDataProvider {
pub fn from_system_time() -> Self {
Self {
max_drift: std::time::Duration::from_secs(60).into(),
timestamp: Timestamp::current(),
}
}
pub fn new(timestamp: InherentType) -> Self {
Self { max_drift: std::time::Duration::from_secs(60).into(), timestamp }
}
pub fn with_max_drift(mut self, max_drift: std::time::Duration) -> Self {
self.max_drift = max_drift.into();
self
}
pub fn timestamp(&self) -> InherentType {
self.timestamp
}
}
#[cfg(feature = "std")]
impl sp_std::ops::Deref for InherentDataProvider {
type Target = InherentType;
fn deref(&self) -> &Self::Target {
&self.timestamp
}
}
#[cfg(feature = "std")]
#[async_trait::async_trait]
impl sp_inherents::InherentDataProvider for InherentDataProvider {
async fn provide_inherent_data(
&self,
inherent_data: &mut InherentData,
) -> Result<(), sp_inherents::Error> {
inherent_data.put_data(INHERENT_IDENTIFIER, &self.timestamp)
}
async fn try_handle_error(
&self,
identifier: &InherentIdentifier,
error: &[u8],
) -> Option<Result<(), sp_inherents::Error>> {
Some(Err(sp_inherents::Error::Application(Box::from(InherentError::try_from(
identifier, error,
)?))))
}
}