1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
// Copyright 2019-2022 PureStake Inc.
// This file is part of Moonbeam.

// Moonbeam is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Moonbeam is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.

//! A way to get a relyable timestamp

use cumulus_pallet_parachain_system::{
	consensus_hook::UnincludedSegmentCapacity,
	relay_state_snapshot::{self, ReadEntryErr},
	ConsensusHook, RelayChainStateProof,
};
use frame_support::pallet_prelude::*;
use frame_support::storage::types::{StorageValue, ValueQuery};
use frame_support::traits::{StorageInstance, Time};
pub use moonbeam_core_primitives::well_known_relay_keys;

/// Get the relay timestamp.
/// Noe that the relay timestamp is populated at the parachain system inherent.
/// If you fetch the timestamp before, you will get the timestamp of the parent block.
pub struct RelayTimestamp;
impl Time for RelayTimestamp {
	type Moment = u64;

	fn now() -> Self::Moment {
		RelayTimestampNow::get()
	}
}

/// A wrapper around the consensus hook to get the relay timestamp from the relay storage proof
pub struct ConsensusHookWrapperForRelayTimestamp<Runtime, Inner>(
	core::marker::PhantomData<(Runtime, Inner)>,
);
impl<Runtime, Inner> ConsensusHook for ConsensusHookWrapperForRelayTimestamp<Runtime, Inner>
where
	Runtime: frame_system::Config,
	Inner: ConsensusHook,
{
	fn on_state_proof(state_proof: &RelayChainStateProof) -> (Weight, UnincludedSegmentCapacity) {
		let relay_timestamp: u64 =
			match state_proof.read_entry(well_known_relay_keys::TIMESTAMP_NOW, None) {
				Ok(relay_timestamp) => relay_timestamp,
				// Log the read entry error
				Err(relay_state_snapshot::Error::ReadEntry(ReadEntryErr::Proof)) => {
					log::error!("Invalid relay storage proof: fail to read key TIMESTAMP_NOW");
					panic!("Invalid realy storage proof: fail to read key TIMESTAMP_NOW");
				}
				Err(relay_state_snapshot::Error::ReadEntry(ReadEntryErr::Decode)) => {
					log::error!("Corrupted relay storage: fail to decode value TIMESTAMP_NOW");
					panic!("Corrupted relay storage: fail to decode value TIMESTAMP_NOW");
				}
				Err(relay_state_snapshot::Error::ReadEntry(ReadEntryErr::Absent)) => {
					log::error!("Corrupted relay storage: value TIMESTAMP_NOW is absent!");
					panic!("Corrupted relay storage: value TIMESTAMP_NOW is absent!");
				}
				// Can't return another kind of error, the blokc is invalid anyway, so we should panic
				_ => unreachable!(),
			};

		let wrapper_weight = <Runtime as frame_system::Config>::DbWeight::get().writes(1);

		RelayTimestampNow::put(relay_timestamp);

		let (weight, capacity) = Inner::on_state_proof(state_proof);

		(weight.saturating_add(wrapper_weight), capacity)
	}
}

// Prefix for storage value RelayTimestampNow
struct RelayTimestampNowPrefix;
impl StorageInstance for RelayTimestampNowPrefix {
	const STORAGE_PREFIX: &'static str = "RelayTimestampNow";

	fn pallet_prefix() -> &'static str {
		"runtime"
	}
}

// Storage type used to store the last current relay timestamp
type RelayTimestampNow = StorageValue<RelayTimestampNowPrefix, u64, ValueQuery>;