Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions srml/timestamp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Timestamp Module

The timestamp module provides functionality to get and set the on-chain time.

## Overview

The timestamp module allows the validators to set and validate a timestamp with each block. It uses timestamp data as an inherent which is provided by the block author and validated/verified by other validators.

It is expected that the timestamp is set by the validator in the beginning of each block, typically one of the first extrinsics. The timestamp can be set only once per block and must be set each block.

Note, that there might be a constraint on how much time must pass before setting the new timestamp, specified by the `tim:block_period` storage entry.

The timestamp module is the recommended way to query the on-chain time instead of using an approach based on block numbers. The block numbers based time measurement can cause issues because of cummulative calculation errors and hence it should be avoided.

## Public Interface

### Types

* `Moment` - Represents the current timestamp.

### Storage Items

* `Now`: `Moment` - The current timestamp represented as **total seconds from the unix epoch**.
* `BlockPeriod`: `Moment` - The minimum (and advised) period between blocks.

### Public Immutable functions

#### get()

Get the current time for the current block. If this function is called prior the setting to timestamp, it will return the timestamp of the previous block.

Returns the timestamp as `Moment`.

#### block_period()

Get the block period for the chain. Return the block period as the `Moment` type.

### Public Mutable functions

#### set(origin, now: T::Moment)

Sets the current time. This call should be invoked exactly once per block. It will panic at the finalization phase, if this call hasn't been invoked by that time.

The timestamp should be greater than the previous one by the amount specified by `block_period`.

##### Errors:

* Timestamp must be updated only once in the block
* Timestamp must increment by at least `BlockPeriod` between sequential blocks

### Inherent Data

The timestamp module manages the block timestamp using InherentData. To identify the timestamp inherent, it defines an `InherentIdentifier`.

The timestamp module defines and implements the trait `TimestampInherentData` for `InherentData` to query the timestamp inherent using the timestamp `InherentIdentifier`.

## Usage

The following example shows how to use the timestamp module in your custom module to query the current timestamp.

In your custom module, after importing the `timestamp` module and deriving your module's configuration trait with the timestamp trait, call the timestamp module's `get` function to get the current timestamp,

```
let now = <timestamp::Module<T>>::get();
```

Similarly, the `block_period` function can be called.
103 changes: 85 additions & 18 deletions srml/timestamp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,74 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.

//! Timestamp manager: provides means to find out the current time.
//!
//! It is expected that the timestamp is set by the validator in the
//! beginning of each block, typically one of the first extrinsics. The timestamp
//! can be set only once per block and must be set each block.
//!
//! Note, that there might be a constraint on how much time must pass
//! before setting the new timestamp, specified by the `tim:block_period`
//! storage entry.
//!
//! # Interaction with the system
//!
//! ## Finalization
//!
//! This module should be hooked up to the finalization routine.
//! # Timestamp Module
//!
//! The timestamp module provides functionality to get and set the on-chain time.
//!
//! ## Overview
//!
//! The timestamp module allows the validators to set and validate a timestamp with each block. It uses timestamp data as an inherent which is provided by the block author and validated/verified by other validators.
//!
//! It is expected that the timestamp is set by the validator in the beginning of each block, typically one of the first extrinsics. The timestamp can be set only once per block and must be set each block.
//!
//! Note, that there might be a constraint on how much time must pass before setting the new timestamp, specified by the `tim:block_period` storage entry.
//!
//! The timestamp module is the recommended way to query the on-chain time instead of using an approach based on block numbers. The block numbers based time measurement can cause issues because of cummulative calculation errors and hence it should be avoided.
//!
//! ## Public Interface
//!
//! ### Types
//!
//! * `Moment` - Represents the current timestamp.
//!
//! ### Storage Items
//!
//! * `Now`: `Moment` - The current timestamp represented as **total seconds from the unix epoch**.
//! * `BlockPeriod`: `Moment` - The minimum (and advised) period between blocks.
//!
//! ### Public Immutable functions
//!
//! #### get()
//!
//! Get the current time for the current block. If this function is called prior the setting to timestamp, it will return the timestamp of the previous block.
//!
//! Returns the timestamp as `Moment`.
//!
//! #### block_period()
//!
//! Get the block period for the chain. Return the block period as the `Moment` type.
//!
//! ### Public Mutable functions
//!
//! #### set(origin, now: T::Moment)
//!
//! Sets the current time. This call should be invoked exactly once per block. It will panic at the finalization phase, if this call hasn't been invoked by that time.
//!
//! The timestamp should be greater than the previous one by the amount specified by `block_period`.
//!
//! ##### Errors:
//!
//! * Timestamp must be updated only once in the block
//! * Timestamp must increment by at least `BlockPeriod` between sequential blocks
//!
//! ### Inherent Data
//!
//! The timestamp module manages the block timestamp using InherentData. To identify the timestamp inherent, it defines an `InherentIdentifier`.
//!
//! The timestamp module defines and implements the trait `TimestampInherentData` for `InherentData` to query the timestamp inherent using the timestamp `InherentIdentifier`.
//!
//! ## Usage
//!
//! The following example shows how to use the timestamp module in your custom module to query the current timestamp.
//!
//! In your custom module, after importing the `timestamp` module and deriving your module's configuration trait with the timestamp trait,
//! call the timestamp module's `get` function to get the current timestamp,
//!
//! ```
//! let now = <timestamp::Module<T>>::get();
//! ```
//!
//! Similarly, the `block_period` function can be called.

#![cfg_attr(not(feature = "std"), no_std)]

Expand Down Expand Up @@ -99,10 +152,12 @@ pub struct InherentDataProvider;

#[cfg(feature = "std")]
impl ProvideInherentData for InherentDataProvider {
/// Returns the identifier for timestamp in inherent data
fn inherent_identifier(&self) -> &'static InherentIdentifier {
&INHERENT_IDENTIFIER
}

/// Provides the current system timestamp (in seconds from epoch) as timestamp inherent data
fn provide_inherent_data(&self, inherent_data: &mut InherentData) -> Result<(), RuntimeString> {
use std::time::SystemTime;

Expand All @@ -116,6 +171,7 @@ impl ProvideInherentData for InherentDataProvider {
})
}

/// Tries to convert the `InherentError` into a string
fn error_to_string(&self, error: &[u8]) -> Option<String> {
InherentError::try_from(&INHERENT_IDENTIFIER, error).map(|e| format!("{:?}", e))
}
Expand Down Expand Up @@ -144,11 +200,13 @@ macro_rules! impl_timestamp_set {

for_each_tuple!(impl_timestamp_set);

/// The module configuration trait
pub trait Trait: system::Trait {
/// Type used for expressing timestamp.
type Moment: Parameter + Default + SimpleArithmetic
+ Mul<Self::BlockNumber, Output = Self::Moment>
+ Div<Self::BlockNumber, Output = Self::Moment>;

/// Something which can be notified when the timestamp is set. Set this to `()` if not needed.
type OnTimestampSet: OnTimestampSet<Self::Moment>;
}
Expand All @@ -157,8 +215,6 @@ decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
/// Set the current time.
///
/// Extrinsic with this call should be placed at the specific position in the each block
/// (specified by the Trait::TIMESTAMP_SET_POSITION) typically at the start of the each block.
/// This call should be invoked exactly once per block. It will panic at the finalization phase,
/// if this call hasn't been invoked by that time.
///
Expand Down Expand Up @@ -186,6 +242,7 @@ decl_storage! {
trait Store for Module<T: Trait> as Timestamp {
/// Current time for the current block.
pub Now get(now) build(|_| T::Moment::sa(0)): T::Moment;

/// The minimum (and advised) period between blocks.
pub BlockPeriod get(block_period) config(period): T::Moment = T::Moment::sa(5);

Expand All @@ -198,7 +255,7 @@ impl<T: Trait> Module<T> {

/// Get the current time for the current block.
///
/// NOTE: if this function is called prior the setting the timestamp,
/// NOTE: if this function is called prior to setting the timestamp,
/// it will return the timestamp of the previous block.
pub fn get() -> T::Moment {
Self::now()
Expand All @@ -211,6 +268,7 @@ impl<T: Trait> Module<T> {
}
}

/// Extracts the timestamp inherent from inherent data.
fn extract_inherent_data(data: &InherentData) -> Result<InherentType, RuntimeString> {
data.get_data::<InherentType>(&INHERENT_IDENTIFIER)
.map_err(|_| RuntimeString::from("Invalid timestamp inherent data encoding."))?
Expand All @@ -222,13 +280,22 @@ impl<T: Trait> ProvideInherent for Module<T> {
type Error = InherentError;
const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;

/// Creates inherent data in storage for timestamp inherent
///
/// Extracts timestamp inherent from the inherent data
/// Finds out the max between current timestamp and `now + block period`
/// Sets the new timestamp by calling the `set` dispatchable function
fn create_inherent(data: &InherentData) -> Option<Self::Call> {
let data = extract_inherent_data(data).expect("Gets and decodes timestamp inherent data");

let next_time = cmp::max(As::sa(data), Self::now() + Self::block_period());
Some(Call::set(next_time.into()))
}

/// Allows other validators to validate the inherent data for timestamp
///
/// Checks if the timestamp is not too far in the future
/// or too behind in the past
fn check_inherent(call: &Self::Call, data: &InherentData) -> result::Result<(), Self::Error> {
const MAX_TIMESTAMP_DRIFT: u64 = 60;

Expand Down