diff --git a/.github/workflows/check-licenses.yml b/.github/workflows/check-licenses.yml
index 3bc95305f7467..b77135805409d 100644
--- a/.github/workflows/check-licenses.yml
+++ b/.github/workflows/check-licenses.yml
@@ -17,7 +17,7 @@ jobs:
steps:
- name: Checkout sources
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- - uses: actions/setup-node@v4.0.1
+ - uses: actions/setup-node@v4.0.3
with:
node-version: "18.x"
registry-url: "https://npm.pkg.github.com"
diff --git a/.github/workflows/checks-quick.yml b/.github/workflows/checks-quick.yml
index 1fcec5c80fd49..678bfb6f2058b 100644
--- a/.github/workflows/checks-quick.yml
+++ b/.github/workflows/checks-quick.yml
@@ -109,7 +109,7 @@ jobs:
- name: Checkout sources
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Setup Node.js
- uses: actions/setup-node@v4.0.1
+ uses: actions/setup-node@v4.0.3
with:
node-version: "18.x"
registry-url: "https://npm.pkg.github.com"
diff --git a/Cargo.lock b/Cargo.lock
index 8a75d096bf142..75a056a25c3c1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -262,9 +262,9 @@ dependencies = [
[[package]]
name = "anyhow"
-version = "1.0.81"
+version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
+checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
[[package]]
name = "approx"
@@ -3258,6 +3258,36 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "coretime-rococo-emulated-chain"
+version = "0.0.0"
+dependencies = [
+ "coretime-rococo-runtime",
+ "cumulus-primitives-core",
+ "emulated-integration-tests-common",
+ "frame-support",
+ "parachains-common",
+ "sp-core",
+ "testnet-parachains-constants",
+]
+
+[[package]]
+name = "coretime-rococo-integration-tests"
+version = "0.1.0"
+dependencies = [
+ "emulated-integration-tests-common",
+ "frame-support",
+ "pallet-balances",
+ "pallet-identity",
+ "pallet-message-queue",
+ "polkadot-runtime-common",
+ "rococo-runtime-constants",
+ "rococo-system-emulated-network",
+ "sp-runtime",
+ "staging-xcm",
+ "staging-xcm-executor",
+]
+
[[package]]
name = "coretime-rococo-runtime"
version = "0.1.0"
@@ -3324,6 +3354,36 @@ dependencies = [
"xcm-runtime-apis",
]
+[[package]]
+name = "coretime-westend-emulated-chain"
+version = "0.0.0"
+dependencies = [
+ "coretime-westend-runtime",
+ "cumulus-primitives-core",
+ "emulated-integration-tests-common",
+ "frame-support",
+ "parachains-common",
+ "sp-core",
+ "testnet-parachains-constants",
+]
+
+[[package]]
+name = "coretime-westend-integration-tests"
+version = "0.1.0"
+dependencies = [
+ "emulated-integration-tests-common",
+ "frame-support",
+ "pallet-balances",
+ "pallet-identity",
+ "pallet-message-queue",
+ "polkadot-runtime-common",
+ "sp-runtime",
+ "staging-xcm",
+ "staging-xcm-executor",
+ "westend-runtime-constants",
+ "westend-system-emulated-network",
+]
+
[[package]]
name = "coretime-westend-runtime"
version = "0.1.0"
@@ -4149,6 +4209,24 @@ dependencies = [
"staging-xcm",
]
+[[package]]
+name = "cumulus-pov-validator"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "clap 4.5.9",
+ "parity-scale-codec",
+ "polkadot-node-primitives",
+ "polkadot-parachain-primitives",
+ "polkadot-primitives",
+ "sc-executor",
+ "sp-core",
+ "sp-io",
+ "sp-maybe-compressed-blob",
+ "tracing",
+ "tracing-subscriber 0.3.18",
+]
+
[[package]]
name = "cumulus-primitives-aura"
version = "0.7.0"
@@ -14016,6 +14094,7 @@ dependencies = [
"pallet-balances",
"pallet-broker",
"pallet-message-queue",
+ "pallet-mmr",
"pallet-session",
"pallet-staking",
"pallet-timestamp",
@@ -14045,6 +14124,7 @@ dependencies = [
"sp-runtime",
"sp-session",
"sp-staking",
+ "sp-std 14.0.0",
"sp-tracing 16.0.0",
"staging-xcm",
"staging-xcm-executor",
@@ -16460,6 +16540,7 @@ version = "0.0.0"
dependencies = [
"asset-hub-rococo-emulated-chain",
"bridge-hub-rococo-emulated-chain",
+ "coretime-rococo-emulated-chain",
"emulated-integration-tests-common",
"penpal-emulated-chain",
"people-rococo-emulated-chain",
@@ -20836,6 +20917,7 @@ dependencies = [
"parity-scale-codec",
"platforms",
"polkadot-sdk",
+ "pretty_assertions",
"rand",
"regex",
"sc-service-test",
@@ -20902,6 +20984,7 @@ dependencies = [
"schemars",
"serde",
"sp-io",
+ "sp-runtime",
"sp-weights",
"xcm-procedural",
]
@@ -23526,6 +23609,7 @@ dependencies = [
"asset-hub-westend-emulated-chain",
"bridge-hub-westend-emulated-chain",
"collectives-westend-emulated-chain",
+ "coretime-westend-emulated-chain",
"emulated-integration-tests-common",
"penpal-emulated-chain",
"people-westend-emulated-chain",
diff --git a/Cargo.toml b/Cargo.toml
index c6f57d0c940c1..d74f0c08241ce 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -61,6 +61,7 @@ members = [
"bridges/snowbridge/primitives/router",
"bridges/snowbridge/runtime/runtime-common",
"bridges/snowbridge/runtime/test-common",
+ "cumulus/bin/pov-validator",
"cumulus/client/cli",
"cumulus/client/collator",
"cumulus/client/consensus/aura",
@@ -90,6 +91,8 @@ members = [
"cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo",
"cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend",
"cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend",
+ "cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-rococo",
+ "cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-westend",
"cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-rococo",
"cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-westend",
"cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal",
@@ -104,6 +107,8 @@ members = [
"cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo",
"cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend",
"cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend",
+ "cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo",
+ "cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend",
"cumulus/parachains/integration-tests/emulated/tests/people/people-rococo",
"cumulus/parachains/integration-tests/emulated/tests/people/people-westend",
"cumulus/parachains/pallets/collective-content",
@@ -666,7 +671,9 @@ colored = { version = "2.0.4" }
comfy-table = { version = "7.1.0", default-features = false }
console = { version = "0.15.8" }
contracts-rococo-runtime = { path = "cumulus/parachains/runtimes/contracts/contracts-rococo" }
+coretime-rococo-emulated-chain = { path = "cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-rococo" }
coretime-rococo-runtime = { path = "cumulus/parachains/runtimes/coretime/coretime-rococo" }
+coretime-westend-emulated-chain = { path = "cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-westend" }
coretime-westend-runtime = { path = "cumulus/parachains/runtimes/coretime/coretime-westend" }
cpu-time = { version = "1.0.0" }
criterion = { version = "0.5.1", default-features = false }
diff --git a/cumulus/bin/pov-validator/Cargo.toml b/cumulus/bin/pov-validator/Cargo.toml
new file mode 100644
index 0000000000000..9be92960ad772
--- /dev/null
+++ b/cumulus/bin/pov-validator/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+name = "cumulus-pov-validator"
+version = "0.1.0"
+authors.workspace = true
+edition.workspace = true
+repository.workspace = true
+license.workspace = true
+homepage.workspace = true
+description = "A tool for validating PoVs locally"
+
+[dependencies]
+codec.workspace = true
+clap = { workspace = true, features = ["derive"] }
+sc-executor.workspace = true
+sp-io.workspace = true
+sp-core.workspace = true
+sp-maybe-compressed-blob.workspace = true
+polkadot-node-primitives.workspace = true
+polkadot-parachain-primitives.workspace = true
+polkadot-primitives.workspace = true
+anyhow.workspace = true
+tracing.workspace = true
+tracing-subscriber.workspace = true
+
+[lints]
+workspace = true
diff --git a/cumulus/bin/pov-validator/src/main.rs b/cumulus/bin/pov-validator/src/main.rs
new file mode 100644
index 0000000000000..1c08f218f6b8a
--- /dev/null
+++ b/cumulus/bin/pov-validator/src/main.rs
@@ -0,0 +1,154 @@
+// This file is part of Cumulus.
+
+// Copyright (C) Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+
+// This program 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.
+
+// This program 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 this program. If not, see .
+
+use clap::Parser;
+use codec::{Decode, Encode};
+use polkadot_node_primitives::{BlockData, PoV, POV_BOMB_LIMIT, VALIDATION_CODE_BOMB_LIMIT};
+use polkadot_parachain_primitives::primitives::ValidationParams;
+use polkadot_primitives::{BlockNumber as RBlockNumber, Hash as RHash, HeadData};
+use sc_executor::WasmExecutor;
+use sp_core::traits::{CallContext, CodeExecutor, RuntimeCode, WrappedRuntimeCode};
+use std::{fs, path::PathBuf, time::Instant};
+use tracing::level_filters::LevelFilter;
+
+/// Tool for validating a `PoV` locally.
+#[derive(Parser)]
+struct Cli {
+ /// The path to the validation code that should be used to validate the `PoV`.
+ ///
+ /// The validation code can either be downloaded from the relay chain that the parachain is
+ /// connected to or by building the runtime manually to obtain the WASM binary.
+ #[arg(long)]
+ validation_code: PathBuf,
+
+ /// The path to the `PoV` to validate.
+ ///
+ /// The `PoV`'s can be obtained by running `polkadot-parachains --collator --chain YOUR_CHAIN
+ /// --export-pov-to-path PATH_TO_EXPORT` and then choose one of the exported `PoV`'s.
+ #[arg(long)]
+ pov: PathBuf,
+}
+
+fn main() -> anyhow::Result<()> {
+ let _ = tracing_subscriber::fmt()
+ .with_env_filter(
+ tracing_subscriber::EnvFilter::from_default_env()
+ .add_directive(LevelFilter::INFO.into()),
+ )
+ .with_writer(std::io::stderr)
+ .try_init();
+
+ let cli = Cli::parse();
+
+ let validation_code = fs::read(&cli.validation_code).map_err(|error| {
+ tracing::error!(%error, path = %cli.validation_code.display(), "Failed to read validation code");
+ anyhow::anyhow!("Failed to read validation code")
+ })?;
+
+ let validation_code =
+ sp_maybe_compressed_blob::decompress(&validation_code, VALIDATION_CODE_BOMB_LIMIT)
+ .map_err(|error| {
+ tracing::error!(%error, "Failed to decompress validation code");
+ anyhow::anyhow!("Failed to decompress validation code")
+ })?;
+
+ let pov_file = fs::read(&cli.pov).map_err(|error| {
+ tracing::error!(%error, path = %cli.pov.display(), "Failed to read PoV");
+ anyhow::anyhow!("Failed to read PoV")
+ })?;
+
+ let executor = WasmExecutor::::builder()
+ .with_allow_missing_host_functions(true)
+ .build();
+
+ let runtime_code = RuntimeCode {
+ code_fetcher: &WrappedRuntimeCode(validation_code.into()),
+ heap_pages: None,
+ // The hash is used for caching, which we need here, but we only use one wasm file. So, the
+ // actual hash is not that important.
+ hash: vec![1, 2, 3],
+ };
+
+ // We are calling `Core_version` to get the wasm file compiled. We don't care about the result.
+ let _ = executor
+ .call(
+ &mut sp_io::TestExternalities::default().ext(),
+ &runtime_code,
+ "Core_version",
+ &[],
+ CallContext::Offchain,
+ )
+ .0;
+
+ let pov_file_ptr = &mut &pov_file[..];
+ let pov = PoV::decode(pov_file_ptr).map_err(|error| {
+ tracing::error!(%error, "Failed to decode `PoV`");
+ anyhow::anyhow!("Failed to decode `PoV`")
+ })?;
+ let head_data = HeadData::decode(pov_file_ptr).map_err(|error| {
+ tracing::error!(%error, "Failed to `HeadData`");
+ anyhow::anyhow!("Failed to decode `HeadData`")
+ })?;
+ let relay_parent_storage_root = RHash::decode(pov_file_ptr).map_err(|error| {
+ tracing::error!(%error, "Failed to relay storage root");
+ anyhow::anyhow!("Failed to decode relay storage root")
+ })?;
+ let relay_parent_number = RBlockNumber::decode(pov_file_ptr).map_err(|error| {
+ tracing::error!(%error, "Failed to relay block number");
+ anyhow::anyhow!("Failed to decode relay block number")
+ })?;
+
+ let pov = sp_maybe_compressed_blob::decompress(&pov.block_data.0, POV_BOMB_LIMIT).map_err(
+ |error| {
+ tracing::error!(%error, "Failed to decompress `PoV`");
+ anyhow::anyhow!("Failed to decompress `PoV`")
+ },
+ )?;
+
+ let validation_params = ValidationParams {
+ relay_parent_number,
+ relay_parent_storage_root,
+ parent_head: head_data,
+ block_data: BlockData(pov.into()),
+ };
+
+ tracing::info!("Starting validation");
+
+ let start = Instant::now();
+
+ let res = executor
+ .call(
+ &mut sp_io::TestExternalities::default().ext(),
+ &runtime_code,
+ "validate_block",
+ &validation_params.encode(),
+ CallContext::Offchain,
+ )
+ .0;
+
+ let duration = start.elapsed();
+
+ match res {
+ Ok(_) => tracing::info!("Validation was successful"),
+ Err(error) => tracing::error!(%error, "Validation failed"),
+ }
+
+ tracing::info!("Validation took {}ms", duration.as_millis());
+
+ Ok(())
+}
diff --git a/cumulus/client/consensus/aura/src/collators/lookahead.rs b/cumulus/client/consensus/aura/src/collators/lookahead.rs
index 749b131123949..02d60538a7323 100644
--- a/cumulus/client/consensus/aura/src/collators/lookahead.rs
+++ b/cumulus/client/consensus/aura/src/collators/lookahead.rs
@@ -39,10 +39,13 @@ use cumulus_primitives_aura::AuraUnincludedSegmentApi;
use cumulus_primitives_core::{CollectCollationInfo, PersistedValidationData};
use cumulus_relay_chain_interface::RelayChainInterface;
-use polkadot_node_primitives::SubmitCollationParams;
+use polkadot_node_primitives::{PoV, SubmitCollationParams};
use polkadot_node_subsystem::messages::CollationGenerationMessage;
use polkadot_overseer::Handle as OverseerHandle;
-use polkadot_primitives::{CollatorPair, Id as ParaId, OccupiedCoreAssumption};
+use polkadot_primitives::{
+ BlockNumber as RBlockNumber, CollatorPair, Hash as RHash, HeadData, Id as ParaId,
+ OccupiedCoreAssumption,
+};
use futures::prelude::*;
use sc_client_api::{backend::AuxStore, BlockBackend, BlockOf};
@@ -54,10 +57,49 @@ use sp_consensus_aura::{AuraApi, Slot};
use sp_core::crypto::Pair;
use sp_inherents::CreateInherentDataProviders;
use sp_keystore::KeystorePtr;
-use sp_runtime::traits::{Block as BlockT, Header as HeaderT, Member};
-use std::{sync::Arc, time::Duration};
+use sp_runtime::traits::{Block as BlockT, Header as HeaderT, Member, NumberFor};
+use std::{
+ fs::{self, File},
+ path::PathBuf,
+ sync::Arc,
+ time::Duration,
+};
-use crate::collator::{self as collator_util};
+use crate::{collator as collator_util, LOG_TARGET};
+
+/// Export the given `pov` to the file system at `path`.
+///
+/// The file will be named `block_hash_block_number.pov`.
+///
+/// The `parent_header`, `relay_parent_storage_root` and `relay_parent_number` will also be
+/// stored in the file alongside the `pov`. This enables stateless validation of the `pov`.
+fn export_pov_to_path(
+ path: PathBuf,
+ pov: PoV,
+ block_hash: Block::Hash,
+ block_number: NumberFor,
+ parent_header: Block::Header,
+ relay_parent_storage_root: RHash,
+ relay_parent_number: RBlockNumber,
+) {
+ if let Err(error) = fs::create_dir_all(&path) {
+ tracing::error!(target: LOG_TARGET, %error, path = %path.display(), "Failed to create PoV export directory");
+ return
+ }
+
+ let mut file = match File::create(path.join(format!("{block_hash:?}_{block_number}.pov"))) {
+ Ok(f) => f,
+ Err(error) => {
+ tracing::error!(target: LOG_TARGET, %error, "Failed to export PoV.");
+ return
+ },
+ };
+
+ pov.encode_to(&mut file);
+ HeadData(parent_header.encode()).encode_to(&mut file);
+ relay_parent_storage_root.encode_to(&mut file);
+ relay_parent_number.encode_to(&mut file);
+}
/// Parameters for [`run`].
pub struct Params {
@@ -97,7 +139,58 @@ pub struct Params {
/// Run async-backing-friendly Aura.
pub fn run(
- mut params: Params,
+ params: Params,
+) -> impl Future