From 6fd9af7e103eac2c1c772f10631c896c804c1ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Tue, 11 Nov 2025 22:06:04 +0100 Subject: [PATCH 1/5] BACKPORT-CONFLICT --- cumulus/pallets/parachain-system/src/lib.rs | 4 +- .../src/validate_block/implementation.rs | 9 ++ .../src/validate_block/tests.rs | 91 ++++++++++++++++++- cumulus/test/runtime/src/lib.rs | 1 + prdoc/pr_10280.prdoc | 8 ++ 5 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 prdoc/pr_10280.prdoc diff --git a/cumulus/pallets/parachain-system/src/lib.rs b/cumulus/pallets/parachain-system/src/lib.rs index b1d4c36d03144..4a95f7dc0651b 100644 --- a/cumulus/pallets/parachain-system/src/lib.rs +++ b/cumulus/pallets/parachain-system/src/lib.rs @@ -579,7 +579,7 @@ pub mod pallet { // TODO: This is more than zero, but will need benchmarking to figure out what. let mut total_weight = Weight::zero(); - // NOTE: the inherent data is expected to be unique, even if this block is built + // NOTE: the inherent data is expected to be unique, even if this block is build // in the context of the same relay parent as the previous one. In particular, // the inherent shouldn't contain messages that were already processed by any of the // ancestors. @@ -637,7 +637,7 @@ pub mod pallet { ), ); - // initialization logic: we know that this runs exactly once every block, + // Initialization logic: we know that this runs exactly once every block, // which means we can put the initialization logic here to remove the // sequencing problem. let upgrade_go_ahead_signal = relay_state_proof diff --git a/cumulus/pallets/parachain-system/src/validate_block/implementation.rs b/cumulus/pallets/parachain-system/src/validate_block/implementation.rs index 443528c1a98ee..2c3448b54aafa 100644 --- a/cumulus/pallets/parachain-system/src/validate_block/implementation.rs +++ b/cumulus/pallets/parachain-system/src/validate_block/implementation.rs @@ -31,10 +31,15 @@ use frame_support::{ traits::{ExecuteBlock, Get, IsSubType}, BoundedVec, }; +<<<<<<< HEAD use polkadot_parachain_primitives::primitives::{ HeadData, RelayChainBlockNumber, ValidationResult, }; use sp_core::storage::{ChildInfo, StateVersion}; +======= +use polkadot_parachain_primitives::primitives::{HeadData, ValidationResult}; +use sp_core::storage::{well_known_keys, ChildInfo, StateVersion}; +>>>>>>> 4e1d9638 (Accept only one block in `validate_block` when upgrading a runtime (#10280)) use sp_externalities::{set_and_run_with_externalities, Externalities}; use sp_io::{hashing::blake2_128, KillStorageResult}; use sp_runtime::traits::{ @@ -269,6 +274,10 @@ where }, ); + if overlay.storage(well_known_keys::CODE).is_some() && num_blocks > 1 { + panic!("When applying a runtime upgrade, only one block per PoV is allowed. Received {num_blocks}.") + } + run_with_externalities_and_recorder::( &backend, &mut Default::default(), diff --git a/cumulus/pallets/parachain-system/src/validate_block/tests.rs b/cumulus/pallets/parachain-system/src/validate_block/tests.rs index daab7d04c4e2b..c34323f841888 100644 --- a/cumulus/pallets/parachain-system/src/validate_block/tests.rs +++ b/cumulus/pallets/parachain-system/src/validate_block/tests.rs @@ -16,11 +16,12 @@ use crate::{validate_block::MemoryOptimizedValidationParams, *}; use codec::{Decode, DecodeAll, Encode}; -use cumulus_primitives_core::{ParachainBlockData, PersistedValidationData}; +use cumulus_primitives_core::{relay_chain, ParachainBlockData, PersistedValidationData}; use cumulus_test_client::{ generate_extrinsic, generate_extrinsic_with_pair, runtime::{ - self as test_runtime, Block, Hash, Header, TestPalletCall, UncheckedExtrinsic, WASM_BINARY, + self as test_runtime, Block, Hash, Header, SudoCall, SystemCall, TestPalletCall, + UncheckedExtrinsic, WASM_BINARY, }, seal_block, transfer, BlockData, BlockOrigin, BuildParachainBlockData, Client, DefaultTestClientBuilderExt, HeadData, InitBlockBuilder, @@ -692,3 +693,89 @@ fn ensure_we_only_like_blockchains() { .contains("Not a valid chain of blocks :(")); } } + +#[test] +fn rejects_multiple_blocks_per_pov_when_applying_runtime_upgrade() { + sp_tracing::try_init_simple(); + + if env::var("RUN_TEST").is_ok() { + let (client, genesis_head) = create_elastic_scaling_test_client(); + + let code = test_runtime::elastic_scaling_500ms::WASM_BINARY + .expect("You need to build the WASM binaries to run the tests!") + .to_vec(); + let code_len = code.len() as u32; + + let mut proof_builder = + RelayStateSproofBuilder { current_slot: 1.into(), ..Default::default() }; + proof_builder.host_config.max_code_size = code_len * 2; + + // Build the block that send the runtime upgrade. + let TestBlockData { block: initial_block_data, .. } = build_block_with_witness( + &client, + vec![generate_extrinsic_with_pair( + &client, + Alice.into(), + SudoCall::sudo { + call: Box::new(SystemCall::set_code_without_checks { code }.into()), + }, + Some(0), + )], + genesis_head.clone(), + proof_builder, + Vec::new(), + ); + + let initial_block = initial_block_data.blocks()[0].clone(); + let (mut header, extrinsics) = initial_block.clone().deconstruct(); + let seal = header.digest.pop().unwrap(); + + let mut import = BlockImportParams::new(BlockOrigin::Own, header.clone()); + import.body = Some(extrinsics); + import.post_digests.push(seal); + import.fork_choice = Some(ForkChoiceStrategy::Custom(true)); + + futures::executor::block_on(BlockImport::import_block(&client, import)).unwrap(); + let initial_block_header = initial_block.header().clone(); + + let mut proof_builder = RelayStateSproofBuilder { + current_slot: 2.into(), + upgrade_go_ahead: Some(relay_chain::UpgradeGoAhead::GoAhead), + ..Default::default() + }; + proof_builder.host_config.max_code_size = code_len * 2; + + // 2. Build a PoV that consists of multiple blocks. + let TestBlockData { block: pov_block_data, validation_data: pov_validation_data } = + build_multiple_blocks_with_witness( + &client, + initial_block_header.clone(), // Start building PoV from the initial block's header + proof_builder, + 4, + |_| Vec::new(), + ); + + // 3. Validate the PoV. + call_validate_block_elastic_scaling( + initial_block_header, // The parent is the head of the initial block before the PoV + pov_block_data, + pov_validation_data.relay_parent_storage_root, + ) + .unwrap_err(); + } else { + let output = Command::new(env::current_exe().unwrap()) + .args([ + "rejects_multiple_blocks_per_pov_when_applying_runtime_upgrade", + "--", + "--nocapture", + ]) + .env("RUN_TEST", "1") + .output() + .expect("Runs the test"); + + assert!(output.status.success()); + + assert!(dbg!(String::from_utf8(output.stderr).unwrap()) + .contains("only one block per PoV is allowed")); + } +} diff --git a/cumulus/test/runtime/src/lib.rs b/cumulus/test/runtime/src/lib.rs index 40226df701903..ecb0f26187beb 100644 --- a/cumulus/test/runtime/src/lib.rs +++ b/cumulus/test/runtime/src/lib.rs @@ -94,6 +94,7 @@ pub use frame_support::{ }, StorageValue, }; +pub use frame_system::Call as SystemCall; use frame_system::{ limits::{BlockLength, BlockWeights}, EnsureRoot, diff --git a/prdoc/pr_10280.prdoc b/prdoc/pr_10280.prdoc new file mode 100644 index 0000000000000..8ddc11439a2fa --- /dev/null +++ b/prdoc/pr_10280.prdoc @@ -0,0 +1,8 @@ +title: Accept only one block in `validate_block` when upgrading a runtime +doc: +- audience: Node Dev + description: |- + As the validation is running the entire time using the same validation code, we can not accept any other blocks after a runtime upgrade was applied. +crates: +- name: cumulus-pallet-parachain-system + bump: patch From 533f508cb4eeed07ade48d5ec052c1634e26c0f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Wed, 12 Nov 2025 10:30:49 +0100 Subject: [PATCH 2/5] Refactor imports in implementation.rs Removed unused imports and cleaned up code structure. --- .../src/validate_block/implementation.rs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/cumulus/pallets/parachain-system/src/validate_block/implementation.rs b/cumulus/pallets/parachain-system/src/validate_block/implementation.rs index 2c3448b54aafa..01e20cd8f3f91 100644 --- a/cumulus/pallets/parachain-system/src/validate_block/implementation.rs +++ b/cumulus/pallets/parachain-system/src/validate_block/implementation.rs @@ -30,16 +30,9 @@ use cumulus_primitives_core::{ use frame_support::{ traits::{ExecuteBlock, Get, IsSubType}, BoundedVec, -}; -<<<<<<< HEAD -use polkadot_parachain_primitives::primitives::{ - HeadData, RelayChainBlockNumber, ValidationResult, -}; -use sp_core::storage::{ChildInfo, StateVersion}; -======= -use polkadot_parachain_primitives::primitives::{HeadData, ValidationResult}; -use sp_core::storage::{well_known_keys, ChildInfo, StateVersion}; ->>>>>>> 4e1d9638 (Accept only one block in `validate_block` when upgrading a runtime (#10280)) +} +use polkadot_parachain_primitives::primitives::{HeadData, RelayChainBlockNumber, ValidationResult}; +use sp_core::storage::{well_known_keys, ChildInfo, StateVersion} use sp_externalities::{set_and_run_with_externalities, Externalities}; use sp_io::{hashing::blake2_128, KillStorageResult}; use sp_runtime::traits::{ From 7025239ae112795cc96b52e40c886670e4591e59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Wed, 12 Nov 2025 11:26:56 +0100 Subject: [PATCH 3/5] Apply suggestions from code review --- .../parachain-system/src/validate_block/implementation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cumulus/pallets/parachain-system/src/validate_block/implementation.rs b/cumulus/pallets/parachain-system/src/validate_block/implementation.rs index 01e20cd8f3f91..649b263fb5cbb 100644 --- a/cumulus/pallets/parachain-system/src/validate_block/implementation.rs +++ b/cumulus/pallets/parachain-system/src/validate_block/implementation.rs @@ -30,7 +30,7 @@ use cumulus_primitives_core::{ use frame_support::{ traits::{ExecuteBlock, Get, IsSubType}, BoundedVec, -} +}; use polkadot_parachain_primitives::primitives::{HeadData, RelayChainBlockNumber, ValidationResult}; use sp_core::storage::{well_known_keys, ChildInfo, StateVersion} use sp_externalities::{set_and_run_with_externalities, Externalities}; From cdc46bc02aca55faf138a99d086b738c6b274c2b Mon Sep 17 00:00:00 2001 From: Egor_P Date: Wed, 12 Nov 2025 13:18:56 +0100 Subject: [PATCH 4/5] Add missing ";" --- .../parachain-system/src/validate_block/implementation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cumulus/pallets/parachain-system/src/validate_block/implementation.rs b/cumulus/pallets/parachain-system/src/validate_block/implementation.rs index 649b263fb5cbb..7a5139554199c 100644 --- a/cumulus/pallets/parachain-system/src/validate_block/implementation.rs +++ b/cumulus/pallets/parachain-system/src/validate_block/implementation.rs @@ -32,7 +32,7 @@ use frame_support::{ BoundedVec, }; use polkadot_parachain_primitives::primitives::{HeadData, RelayChainBlockNumber, ValidationResult}; -use sp_core::storage::{well_known_keys, ChildInfo, StateVersion} +use sp_core::storage::{well_known_keys, ChildInfo, StateVersion}; use sp_externalities::{set_and_run_with_externalities, Externalities}; use sp_io::{hashing::blake2_128, KillStorageResult}; use sp_runtime::traits::{ From de71bf1537e832684cc689627c64b68304a72441 Mon Sep 17 00:00:00 2001 From: "cmd[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 12 Nov 2025 12:31:26 +0000 Subject: [PATCH 5/5] Update from github-actions[bot] running command 'fmt' --- .../parachain-system/src/validate_block/implementation.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cumulus/pallets/parachain-system/src/validate_block/implementation.rs b/cumulus/pallets/parachain-system/src/validate_block/implementation.rs index 7a5139554199c..25300d632e4a6 100644 --- a/cumulus/pallets/parachain-system/src/validate_block/implementation.rs +++ b/cumulus/pallets/parachain-system/src/validate_block/implementation.rs @@ -31,7 +31,9 @@ use frame_support::{ traits::{ExecuteBlock, Get, IsSubType}, BoundedVec, }; -use polkadot_parachain_primitives::primitives::{HeadData, RelayChainBlockNumber, ValidationResult}; +use polkadot_parachain_primitives::primitives::{ + HeadData, RelayChainBlockNumber, ValidationResult, +}; use sp_core::storage::{well_known_keys, ChildInfo, StateVersion}; use sp_externalities::{set_and_run_with_externalities, Externalities}; use sp_io::{hashing::blake2_128, KillStorageResult};