Skip to content
2 changes: 1 addition & 1 deletion cumulus/test/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.
#![recursion_limit = "256"]

// Make the WASM binary available.
// Make the WASM binaries available.
#[cfg(feature = "std")]
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));

Expand Down
59 changes: 59 additions & 0 deletions cumulus/zombienet/zombienet-sdk-helpers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use zombienet_sdk::subxt::{
dynamic::Value,
events::Events,
ext::scale_value::value,
metadata::Metadata,
tx::{signer::Signer, DynamicPayload, TxStatus},
utils::H256,
OnlineClient, PolkadotConfig,
Expand All @@ -28,6 +29,25 @@ use zombienet_sdk::subxt::{
// If it does not arrive for whatever reason, we should not wait forever.
const WAIT_MAX_BLOCKS_FOR_SESSION: u32 = 50;

/// Format a `sp_runtime::DispatchError` using runtime metadata for human-readable output.
///
/// For module errors this resolves the pallet index and error index to their names
/// (e.g. `ParachainSystem::TooBig`) instead of showing raw bytes.
fn format_dispatch_error(err: &sp_runtime::DispatchError, metadata: &Metadata) -> String {
match err {
sp_runtime::DispatchError::Module(module_err) => {
let pallet = metadata.pallet_by_index(module_err.index);
let pallet_name = pallet.as_ref().map(|p| p.name()).unwrap_or("UnknownPallet");
let error_name = pallet
.and_then(|p| p.error_variant_by_index(module_err.error[0]))
.map(|v| v.name.as_str())
.unwrap_or("UnknownError");
format!("{pallet_name}::{error_name}")
},
other => format!("{other:?}"),
}
}

/// Find an event in subxt `Events` and attempt to decode the fields of the event.
fn find_event_and_decode_fields<T: Decode>(
events: &Events<PolkadotConfig>,
Expand Down Expand Up @@ -569,6 +589,45 @@ pub fn create_runtime_upgrade_call(wasm: &[u8]) -> DynamicPayload {
)
}

/// Submit a runtime upgrade via sudo and verify it was scheduled.
///
/// This submits a `Sudo::sudo_unchecked_weight(System::set_code(wasm))` extrinsic,
/// waits for finalization, then checks the `Sudid` event to verify the inner dispatch
/// succeeded. Returns the hash of the finalized block containing the upgrade extrinsic.
pub async fn submit_sudo_runtime_upgrade<S: Signer<PolkadotConfig>>(
client: &OnlineClient<PolkadotConfig>,
wasm: &[u8],
signer: &S,
) -> Result<H256, anyhow::Error> {
log::info!("Submitting sudo runtime upgrade, wasm size: {} bytes", wasm.len());
let call = create_runtime_upgrade_call(wasm);
let block_hash =
submit_extrinsic_and_wait_for_finalization_success(client, &call, signer).await?;

// Verify the inner sudo dispatch succeeded by checking the Sudid event.
// sudo_unchecked_weight always returns Ok at the extrinsic level, even if the
// inner call fails — the actual result is only in the Sudid event.
let block = client.blocks().at(block_hash).await?;
let events = block.events().await?;
let sudid_results: Vec<Result<(), sp_runtime::DispatchError>> =
find_event_and_decode_fields(&events, "Sudo", "Sudid")?;

match sudid_results.first() {
Some(Ok(())) => {
log::info!("Sudo runtime upgrade dispatched successfully in block {block_hash:?}")
},
Some(Err(e)) => {
return Err(anyhow!(
"Sudo runtime upgrade inner dispatch failed in block {block_hash:?}: {}",
format_dispatch_error(e, &client.metadata()),
))
},
None => return Err(anyhow!("Sudid event not found in block {block_hash:?}")),
}

Ok(block_hash)
}

/// Wait until a runtime upgrade has happened.
///
/// This checks all finalized blocks until it finds a block that sets the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ use serde_json::json;
use crate::utils::initialize_network;

use cumulus_test_runtime::{
elastic_scaling::WASM_BINARY_BLOATY as WASM_ELASTIC_SCALING,
elastic_scaling_12s_slot::WASM_BINARY_BLOATY as WASM_ELASTIC_SCALING_12S_SLOT,
elastic_scaling::WASM_BINARY as WASM_ELASTIC_SCALING,
elastic_scaling_12s_slot::WASM_BINARY as WASM_ELASTIC_SCALING_12S_SLOT,
};
use cumulus_zombienet_sdk_helpers::{
assert_para_throughput, assign_cores, create_runtime_upgrade_call,
submit_extrinsic_and_wait_for_finalization_success, wait_for_runtime_upgrade,
assert_para_throughput, assign_cores, submit_sudo_runtime_upgrade, wait_for_runtime_upgrade,
};
use polkadot_primitives::Id as ParaId;
use rstest::rstest;
Expand Down Expand Up @@ -71,9 +70,7 @@ async fn elastic_scaling_upgrade_to_3_cores(
};

log::info!("Performing runtime upgrade");
let call = create_runtime_upgrade_call(wasm);
submit_extrinsic_and_wait_for_finalization_success(&collator0_client, &call, &dev::alice())
.await?;
submit_sudo_runtime_upgrade(&collator0_client, wasm, &dev::alice()).await?;

let collator1 = network.get_node("collator1")?;
let collator1_client: OnlineClient<PolkadotConfig> = collator1.wait_client().await?;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@

use crate::utils::initialize_network;
use anyhow::anyhow;
use cumulus_test_runtime::slot_duration_18s::WASM_BINARY_BLOATY as WASM_WITH_SLOT_DURATION_18S;
use cumulus_test_runtime::slot_duration_18s::WASM_BINARY as WASM_WITH_SLOT_DURATION_18S;
use cumulus_zombienet_sdk_helpers::{
assert_blocks_are_being_finalized, assert_para_throughput, create_runtime_upgrade_call,
submit_extrinsic_and_wait_for_finalization_success, wait_for_runtime_upgrade,
assert_blocks_are_being_finalized, assert_para_throughput, submit_sudo_runtime_upgrade,
wait_for_runtime_upgrade,
};
use futures::StreamExt;
use polkadot_primitives::Id as ParaId;
Expand Down Expand Up @@ -45,9 +45,7 @@ async fn parachain_runtime_upgrade_slot_duration_18s() -> Result<(), anyhow::Err
let initial_slot_duration = get_slot_duration(&collator_client).await?;

log::info!("Performing runtime upgrade for parachain {}", PARA_ID);
let call = create_runtime_upgrade_call(wasm);
submit_extrinsic_and_wait_for_finalization_success(&collator_client, &call, &dev::alice())
.await?;
submit_sudo_runtime_upgrade(&collator_client, wasm, &dev::alice()).await?;

let block_hash_of_upgrade = wait_for_runtime_upgrade(&collator_client).await?;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@

use crate::utils::initialize_network;
use anyhow::anyhow;
use cumulus_test_runtime::wasm_spec_version_incremented::WASM_BINARY_BLOATY as WASM_RUNTIME_UPGRADE;
use cumulus_zombienet_sdk_helpers::{
create_runtime_upgrade_call, submit_extrinsic_and_wait_for_finalization_success,
wait_for_runtime_upgrade,
};
use cumulus_test_runtime::wasm_spec_version_incremented::WASM_BINARY as WASM_RUNTIME_UPGRADE;
use cumulus_zombienet_sdk_helpers::{submit_sudo_runtime_upgrade, wait_for_runtime_upgrade};
use zombienet_sdk::{
subxt::{OnlineClient, PolkadotConfig},
subxt_signer::sr25519::dev,
Expand Down Expand Up @@ -36,10 +33,12 @@ async fn runtime_upgrade() -> Result<(), anyhow::Error> {
log::info!("Current runtime spec version {current_spec_version}");

log::info!("Performing runtime upgrade");

let call = create_runtime_upgrade_call(WASM_RUNTIME_UPGRADE.expect("Wasm runtime not build"));
submit_extrinsic_and_wait_for_finalization_success(&charlie_client, &call, &dev::alice())
.await?;
submit_sudo_runtime_upgrade(
&charlie_client,
WASM_RUNTIME_UPGRADE.expect("Wasm runtime not built"),
&dev::alice(),
)
.await?;

let dave = network.get_node("dave")?;
let dave_client: OnlineClient<PolkadotConfig> = dave.wait_client().await?;
Expand Down
23 changes: 23 additions & 0 deletions prdoc/pr_11641.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
title: 'Fix runtime upgrade zombienet tests exceeding max_code_size'
doc:
- audience: Runtime Dev
description: |-
The wasm-builder's compaction and compression decision now uses the actual
blob build profile rather than the outer cargo profile. Since debug builds
already compile WASM blobs with the Release profile, they now also get
compacted and compressed, producing a properly sized `WASM_BINARY`.
Previously `WASM_BINARY` in debug builds was identical to `WASM_BINARY_BLOATY`.
- audience: Node Dev
description: |-
Zombienet runtime upgrade tests now use compact (`WASM_BINARY`) runtimes
instead of `WASM_BINARY_BLOATY`, which had grown beyond the relay chain's
`max_code_size` limit. A new `submit_sudo_runtime_upgrade` helper verifies
that the inner sudo dispatch succeeded and renders any dispatch errors using
runtime metadata (e.g. `ParachainSystem::TooBig` instead of raw byte indices).
crates:
- name: substrate-wasm-builder
bump: patch
- name: cumulus-zombienet-sdk-helpers
bump: minor
- name: cumulus-zombienet-sdk-tests
bump: none
19 changes: 6 additions & 13 deletions substrate/utils/wasm-builder/src/wasm_project.rs
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of these changes should be required. I don't get why you are touching this file, when you are trying to use compressed files in zombienet. The zombienet test are compiled any way in release mode? Please explain why this is required.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct fix here: #11765

Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ fn maybe_compact_and_compress_wasm(
build_config: &BuildConfiguration,
bloaty_changed: bool,
) -> (Option<WasmBinary>, WasmBinaryBloaty, bool) {
let needs_compact = build_config.outer_build_profile.wants_compact();
let needs_compact = build_config.blob_build_profile.wants_compact();
let compact_path = blob_paths.compact();
let compressed_path = blob_paths.compact_compressed();
let compact_or_compressed_exists = compact_path.exists() || compressed_path.exists();
Expand Down Expand Up @@ -810,9 +810,7 @@ impl Profile {
/// The build configuration for this build.
#[derive(Debug)]
struct BuildConfiguration {
/// The profile that is used to build the outer project.
pub outer_build_profile: Profile,
/// The profile to use to build the runtime blob.
/// The profile to use to build the runtime blob and decide whether to compact/compress.
pub blob_build_profile: Profile,
}

Expand All @@ -828,9 +826,7 @@ impl BuildConfiguration {
/// When not overridden by a env variable we always default to building wasm with the `Release`
/// profile even when the main build uses the debug build. This is because wasm built with the
/// `Debug` profile is too slow for normal development activities and almost never intended.
///
/// When cargo is building in `--profile dev`, user likely intends to compile fast, so we don't
/// bother producing compact or compressed blobs.
/// Compaction and compression are also decided by this profile.
///
/// # Note
///
Expand Down Expand Up @@ -860,8 +856,8 @@ impl BuildConfiguration {
.to_string();
(name, false)
};
let outer_build_profile = Profile::iter().find(|p| p.directory() == name);
let blob_build_profile = match (outer_build_profile.clone(), overridden) {
let detected_profile = Profile::iter().find(|p| p.directory() == name);
let blob_build_profile = match (detected_profile, overridden) {
// When not overridden by a env variable we default to using the `Release` profile
// for the wasm build even when the main build uses the debug build. This
// is because the `Debug` profile is too slow for normal development activities.
Expand Down Expand Up @@ -889,10 +885,7 @@ impl BuildConfiguration {
process::exit(1);
},
};
BuildConfiguration {
outer_build_profile: outer_build_profile.unwrap_or(Profile::Release),
blob_build_profile,
}
BuildConfiguration { blob_build_profile }
}
}

Expand Down
Loading