From 84565ec03e945151fe3973365aaedcd25c09497f Mon Sep 17 00:00:00 2001
From: Chris Sosnin <48099298+slumber@users.noreply.github.com>
Date: Tue, 8 Aug 2023 09:32:42 +0300
Subject: [PATCH] (async backing) restore `CheckInherents` for
backwards-compatibility (#2977)
* bring back timestamp
* Restore CheckInherents
* revert to empty CheckInherents
* make CheckInherents optional
---
Cargo.lock | 12 ++++
Cargo.toml | 1 +
.../parachain-system/proc-macro/src/lib.rs | 29 +++++---
pallets/parachain-system/src/lib.rs | 34 ++++++++-
.../src/validate_block/implementation.rs | 33 ++++++++-
primitives/timestamp/Cargo.toml | 27 +++++++
primitives/timestamp/src/lib.rs | 70 +++++++++++++++++++
7 files changed, 195 insertions(+), 11 deletions(-)
create mode 100644 primitives/timestamp/Cargo.toml
create mode 100644 primitives/timestamp/src/lib.rs
diff --git a/Cargo.lock b/Cargo.lock
index 0d953383686..e5ab529a67a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2877,6 +2877,18 @@ dependencies = [
"tracing",
]
+[[package]]
+name = "cumulus-primitives-timestamp"
+version = "0.1.0"
+dependencies = [
+ "cumulus-primitives-core",
+ "futures",
+ "parity-scale-codec 3.6.4",
+ "sp-inherents",
+ "sp-std",
+ "sp-timestamp",
+]
+
[[package]]
name = "cumulus-primitives-utility"
version = "0.1.0"
diff --git a/Cargo.toml b/Cargo.toml
index 7981dc816cd..b0899a03bc2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -32,6 +32,7 @@ members = [
"parachain-template/runtime",
"primitives/core",
"primitives/parachain-inherent",
+ "primitives/timestamp",
"primitives/utility",
"polkadot-parachain",
"parachains/common",
diff --git a/pallets/parachain-system/proc-macro/src/lib.rs b/pallets/parachain-system/proc-macro/src/lib.rs
index 3c6fb21e5f3..70c6857120c 100644
--- a/pallets/parachain-system/proc-macro/src/lib.rs
+++ b/pallets/parachain-system/proc-macro/src/lib.rs
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Cumulus. If not, see .
-use proc_macro2::{Span, TokenStream};
+use proc_macro2::Span;
use proc_macro_crate::{crate_name, FoundCrate};
use syn::{
parse::{Parse, ParseStream},
@@ -25,17 +25,20 @@ use syn::{
mod keywords {
syn::custom_keyword!(Runtime);
syn::custom_keyword!(BlockExecutor);
+ syn::custom_keyword!(CheckInherents);
}
struct Input {
runtime: Path,
block_executor: Path,
+ check_inherents: Option,
}
impl Parse for Input {
fn parse(input: ParseStream) -> Result {
let mut runtime = None;
let mut block_executor = None;
+ let mut check_inherents = None;
fn parse_inner(
input: ParseStream,
@@ -56,26 +59,24 @@ impl Parse for Input {
}
}
- while runtime.is_none() || block_executor.is_none() {
+ while !input.is_empty() || runtime.is_none() || block_executor.is_none() {
let lookahead = input.lookahead1();
if lookahead.peek(keywords::Runtime) {
parse_inner::(input, &mut runtime)?;
} else if lookahead.peek(keywords::BlockExecutor) {
parse_inner::(input, &mut block_executor)?;
+ } else if lookahead.peek(keywords::CheckInherents) {
+ parse_inner::(input, &mut check_inherents)?;
} else {
return Err(lookahead.error())
}
}
- let rest = input.parse::()?;
- if !rest.is_empty() {
- return Err(Error::new(rest.span(), "Unexpected input data"))
- }
-
Ok(Self {
runtime: runtime.expect("Everything is parsed before; qed"),
block_executor: block_executor.expect("Everything is parsed before; qed"),
+ check_inherents,
})
}
}
@@ -91,7 +92,7 @@ fn crate_() -> Result {
#[proc_macro]
pub fn register_validate_block(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
- let Input { runtime, block_executor } = match syn::parse(input) {
+ let Input { runtime, block_executor, check_inherents } = match syn::parse(input) {
Ok(t) => t,
Err(e) => return e.into_compile_error().into(),
};
@@ -101,6 +102,17 @@ pub fn register_validate_block(input: proc_macro::TokenStream) -> proc_macro::To
Err(e) => return e.into_compile_error().into(),
};
+ let check_inherents = match check_inherents {
+ Some(_check_inherents) => {
+ quote::quote! { #_check_inherents }
+ },
+ None => {
+ quote::quote! {
+ #crate_::DummyCheckInherents<<#runtime as #crate_::validate_block::GetRuntimeBlockType>::RuntimeBlock>
+ }
+ },
+ };
+
if cfg!(not(feature = "std")) {
quote::quote! {
#[doc(hidden)]
@@ -127,6 +139,7 @@ pub fn register_validate_block(input: proc_macro::TokenStream) -> proc_macro::To
<#runtime as #crate_::validate_block::GetRuntimeBlockType>::RuntimeBlock,
#block_executor,
#runtime,
+ #check_inherents,
>(params);
#crate_::validate_block::polkadot_parachain::write_result(&res)
diff --git a/pallets/parachain-system/src/lib.rs b/pallets/parachain-system/src/lib.rs
index 83954995678..648e6e90cd4 100644
--- a/pallets/parachain-system/src/lib.rs
+++ b/pallets/parachain-system/src/lib.rs
@@ -48,7 +48,7 @@ use frame_system::{ensure_none, ensure_root, pallet_prelude::HeaderFor};
use polkadot_parachain::primitives::RelayChainBlockNumber;
use scale_info::TypeInfo;
use sp_runtime::{
- traits::{BlockNumberProvider, Hash},
+ traits::{Block as BlockT, BlockNumberProvider, Hash},
transaction_validity::{
InvalidTransaction, TransactionLongevity, TransactionSource, TransactionValidity,
ValidTransaction,
@@ -86,10 +86,12 @@ pub use consensus_hook::{ConsensusHook, ExpectParentIncluded};
/// ```
/// struct BlockExecutor;
/// struct Runtime;
+/// struct CheckInherents;
///
/// cumulus_pallet_parachain_system::register_validate_block! {
/// Runtime = Runtime,
/// BlockExecutor = Executive,
+/// CheckInherents = CheckInherents,
/// }
///
/// # fn main() {}
@@ -1497,6 +1499,36 @@ impl UpwardMessageSender for Pallet {
}
}
+/// Something that can check the inherents of a block.
+#[cfg_attr(
+ feature = "parameterized-consensus-hook",
+ deprecated = "consider switching to `cumulus-pallet-parachain-system::ConsensusHook`"
+)]
+pub trait CheckInherents {
+ /// Check all inherents of the block.
+ ///
+ /// This function gets passed all the extrinsics of the block, so it is up to the callee to
+ /// identify the inherents. The `validation_data` can be used to access the
+ fn check_inherents(
+ block: &Block,
+ validation_data: &RelayChainStateProof,
+ ) -> frame_support::inherent::CheckInherentsResult;
+}
+
+/// Struct that always returns `Ok` on inherents check, needed for backwards-compatibility.
+#[doc(hidden)]
+pub struct DummyCheckInherents(sp_std::marker::PhantomData);
+
+#[allow(deprecated)]
+impl CheckInherents for DummyCheckInherents {
+ fn check_inherents(
+ _: &Block,
+ _: &RelayChainStateProof,
+ ) -> frame_support::inherent::CheckInherentsResult {
+ sp_inherents::CheckInherentsResult::new()
+ }
+}
+
/// Something that should be informed about system related events.
///
/// This includes events like [`on_validation_data`](Self::on_validation_data) that is being
diff --git a/pallets/parachain-system/src/validate_block/implementation.rs b/pallets/parachain-system/src/validate_block/implementation.rs
index 6945362394f..4f07f2021c3 100644
--- a/pallets/parachain-system/src/validate_block/implementation.rs
+++ b/pallets/parachain-system/src/validate_block/implementation.rs
@@ -65,12 +65,20 @@ fn with_externalities R, R>(f: F) -> R {
/// we have the in-memory database that contains all the values from the state of the parachain
/// that we require to verify the block.
///
-/// 5. The last step is to execute the entire block in the machinery we just have setup. Executing
+/// 5. We are going to run `check_inherents`. This is important to check stuff like the timestamp
+/// matching the real world time.
+///
+/// 6. The last step is to execute the entire block in the machinery we just have setup. Executing
/// the blocks include running all transactions in the block against our in-memory database and
/// ensuring that the final storage root matches the storage root in the header of the block. In the
/// end we return back the [`ValidationResult`] with all the required information for the validator.
#[doc(hidden)]
-pub fn validate_block, PSC: crate::Config>(
+pub fn validate_block<
+ B: BlockT,
+ E: ExecuteBlock,
+ PSC: crate::Config,
+ CI: crate::CheckInherents,
+>(
MemoryOptimizedValidationParams {
block_data,
parent_head,
@@ -159,6 +167,27 @@ where
sp_io::offchain_index::host_clear.replace_implementation(host_offchain_index_clear),
);
+ run_with_externalities::(&backend, || {
+ let relay_chain_proof = crate::RelayChainStateProof::new(
+ PSC::SelfParaId::get(),
+ inherent_data.validation_data.relay_parent_storage_root,
+ inherent_data.relay_chain_state.clone(),
+ )
+ .expect("Invalid relay chain state proof");
+
+ let res = CI::check_inherents(&block, &relay_chain_proof);
+
+ if !res.ok() {
+ if log::log_enabled!(log::Level::Error) {
+ res.into_errors().for_each(|e| {
+ log::error!("Checking inherent with identifier `{:?}` failed", e.0)
+ });
+ }
+
+ panic!("Checking inherents failed");
+ }
+ });
+
run_with_externalities::(&backend, || {
let head_data = HeadData(block.header().encode());
diff --git a/primitives/timestamp/Cargo.toml b/primitives/timestamp/Cargo.toml
new file mode 100644
index 00000000000..176f22fe5d3
--- /dev/null
+++ b/primitives/timestamp/Cargo.toml
@@ -0,0 +1,27 @@
+[package]
+name = "cumulus-primitives-timestamp"
+version = "0.1.0"
+authors = ["Parity Technologies "]
+edition = "2021"
+description = "Provides timestamp related functionality for parachains."
+
+[dependencies]
+codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ "derive" ] }
+futures = "0.3.28"
+
+# Substrate
+sp-inherents = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+sp-timestamp = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+
+# Cumulus
+cumulus-primitives-core = { path = "../core", default-features = false }
+
+[features]
+default = [ "std" ]
+std = [
+ "sp-inherents/std",
+ "sp-std/std",
+ "sp-timestamp/std",
+ "cumulus-primitives-core/std",
+]
diff --git a/primitives/timestamp/src/lib.rs b/primitives/timestamp/src/lib.rs
new file mode 100644
index 00000000000..5af3ddf21dc
--- /dev/null
+++ b/primitives/timestamp/src/lib.rs
@@ -0,0 +1,70 @@
+// Copyright 2021 Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus 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.
+
+// Cumulus 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 Cumulus. If not, see .
+
+//! Cumulus timestamp related primitives.
+//!
+//! Provides a [`InherentDataProvider`] that should be used in the validation phase of the parachain.
+//! It will be used to create the inherent data and that will be used to check the inherents inside
+//! the parachain block (in this case the timestamp inherent). As we don't have access to any clock
+//! from the runtime the timestamp is always passed as an inherent into the runtime. To check this
+//! inherent when validating the block, we will use the relay chain slot. As the relay chain slot
+//! is derived from a timestamp, we can easily convert it back to a timestamp by muliplying it with
+//! the slot duration. By comparing the relay chain slot derived timestamp with the timestamp we can
+//! ensure that the parachain timestamp is reasonable.
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+use cumulus_primitives_core::relay_chain::Slot;
+use sp_inherents::{Error, InherentData};
+use sp_std::time::Duration;
+
+pub use sp_timestamp::{InherentType, INHERENT_IDENTIFIER};
+
+/// The inherent data provider for the timestamp.
+///
+/// This should be used in the runtime when checking the inherents in the validation phase of the
+/// parachain.
+pub struct InherentDataProvider {
+ relay_chain_slot: Slot,
+ relay_chain_slot_duration: Duration,
+}
+
+impl InherentDataProvider {
+ /// Create `Self` from the given relay chain slot and slot duration.
+ pub fn from_relay_chain_slot_and_duration(
+ relay_chain_slot: Slot,
+ relay_chain_slot_duration: Duration,
+ ) -> Self {
+ Self { relay_chain_slot, relay_chain_slot_duration }
+ }
+
+ /// Create the inherent data.
+ pub fn create_inherent_data(&self) -> Result {
+ let mut inherent_data = InherentData::new();
+ self.provide_inherent_data(&mut inherent_data).map(|_| inherent_data)
+ }
+
+ /// Provide the inherent data into the given `inherent_data`.
+ pub fn provide_inherent_data(&self, inherent_data: &mut InherentData) -> Result<(), Error> {
+ // As the parachain starts building at around `relay_chain_slot + 1` we use that slot to
+ // calculate the timestamp.
+ let data: InherentType = ((*self.relay_chain_slot + 1) *
+ self.relay_chain_slot_duration.as_millis() as u64)
+ .into();
+
+ inherent_data.put_data(INHERENT_IDENTIFIER, &data)
+ }
+}